我能说的最好 - 必须使用宏 - 由于芯片的架构和 PROGMEM 的工作方式,任何其他方式都是不可能的。
以下说明了我已实施的内容,或多或少是“答案”。
这段代码的结尾是“可读初始化结构”部分——MAKE_MENU 宏。
代码的开头是所有支持宏的东西。
警告:arduino 中存在各种大小和行尾限制和错误:如果您正在使用它,请经常且频繁地编译 - 调试宏错误是不可能的,因此您不能让错误潜入。
对于任何摸不着头脑的人:PROGMEM 允许您将一些东西放入 FLASH(您有很多)中,而不是将其存储在 SRAM 中(这是非常有限的)。当您使用 arduino 变量耗尽空间时,您需要执行此操作。
#include <avr/pgmspace.h> // This lets us store static variables in FLASH instead of SRAM
#define MAX_MENU_WIDTH 64 // Max number of characters on a single menu line, PLUS 1
#define SERIAL_RATE 115200 // The BAUD rate of the serial port
// This is the definition of each line in a menu. Each line uses 14 bytes of SRAM
typedef struct menu_item_def {
byte x; byte y; // Coordinates of the start for the line of test
byte selected; // set to 1 if the menu buttons have this option selected
byte mdatatype1; // This number explains how to use the following 2 mdata* as output when needed:
char *mtext; // What to say, including sprintf placeholders: eg: "STOP TIMER...% 4.2i MIN"
void *mdata1; // Refer to the "int Menu()" function for details.
void *mdata2;
void *mdata3;
void *mdata4;
} menu_item_type;
#define Pv(a,b) a ## _ ## b
#define Ev(a,b) Pv(a,b)
// The following macro makes menu definitions easy-to-type, and stores menu text in (unchangeable) FLASH and menu data in (precious but small) SRAM
// These numbers should range from 1 to MAX_MENU_LINES. Best not to edit these, or add more than 12: they're already at the absolute max size that an arduino macro can be.
#define FLASH_PART(n, m0,m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11) \
const char Ev(n,0)[] PROGMEM=m0; \
const char Ev(n,1)[] PROGMEM=m1; \
const char Ev(n,2)[] PROGMEM=m2; \
const char Ev(n,3)[] PROGMEM=m3; \
const char Ev(n,4)[] PROGMEM=m4; \
const char Ev(n,5)[] PROGMEM=m5; \
const char Ev(n,6)[] PROGMEM=m6; \
const char Ev(n,7)[] PROGMEM=m7; \
const char Ev(n,8)[] PROGMEM=m8; \
const char Ev(n,9)[] PROGMEM=m9; \
const char Ev(n,10)[] PROGMEM=m10; \
const char Ev(n,11)[] PROGMEM=m11;
#define MAX_MENU_LINES 12 // How many lines are on each of your menu screens
#define MAKE_MENU(n, x0,y0,s0,m0,t0,f0,d0,z0,q0, x1,y1,s1,m1,t1,f1,d1,z1,q1, x2,y2,s2,m2,t2,f2,d2,z2,q2, x3,y3,s3,m3,t3,f3,d3,z3,q3, x4,y4,s4,m4,t4,f4,d4,z4,q4, x5,y5,s5,m5,t5,f5,d5,z5,q5, x6,y6,s6,m6,t6,f6,d6,z6,q6, x7,y7,s7,m7,t7,f7,d7,z7,q7, x8,y8,s8,m8,t8,f8,d8,z8,q8, x9,y9,s9,m9,t9,f9,d9,z9,q9, x10,y10,s10,m10,t10,f10,d10,z10,q10, x11,y11,s11,m11,t11,f11,d11,z11,q11) \
FLASH_PART(n, m0,m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11) \
struct menu_item_def n[MAX_MENU_LINES] = { {x0,y0,s0,t0,(char *)Ev(n,0),f0,d0,z0,q0},{x1,y1,s1,t1,(char *)Ev(n,1),f1,d1,z1,q1},{x2,y2,s2,t2,(char *)Ev(n,2),f2,d2,z2,q2},{x3,y3,s3,t3,(char *)Ev(n,3),f3,d3,z3,q3},{x4,y4,s4,t4,(char *)Ev(n,4),f4,d4,z4,q4},{x5,y5,s5,t5,(char *)Ev(n,5),f5,d5,z5,q5},{x6,y6,s6,t6,(char *)Ev(n,6),f6,d6,z6,q6},{x7,y7,s7,t7,(char *)Ev(n,7),f7,d7,z7,q7},{x8,y8,s8,t8,(char *)Ev(n,8),f8,d8,z8,q8},{x9,y9,s9,t9,(char *)Ev(n,9),f9,d9,z9,q9},{x10,y10,s10,t10,(char *)Ev(n,10),f10,d10,z10,q10},{x11,y11,s11,t11,(char *)Ev(n,11),f11,d11,z11,q11}, };
//FINALLY - Here is the "readable" way to initialize the structures (all the 7,8,9 stuff are placeholders for future expansion):
MAKE_MENU (main_menu_items,
1,1,1, "STOP TIMER.....% 5.1i MIN" ,0, (void *)83, (void *)7, (void *)8, (void *)9,
1,2,1, "CHEM ON/OFF....%s" ,2, (void *)"ON/OFF", (void *)7, (void *)8, (void *)9,
1,3,0, "MOTOR RPM......% s RPM" ,1, (void *)ReadRPM, (void *)7, (void *)8, (void *)9,
1,4,1, "TOT CHEM RATE..% 5.2i L/Hr" ,1, (void *)DemoData, (void *)7, (void *)8, (void *)9,
1,5,0, "TOT CHEM USED..% 5.2i L/Hr" ,1, (void *)DemoData, (void *)7, (void *)8, (void *)9,
1,6,1, "CHEM 1 RATE....% 23.6i L/Hr" ,0, (void *)83, (void *)7, (void *)8, (void *)9,
1,7,1, "CHEM 2 RATE....% 16.3i L/Hr" ,0, (void *)84, (void *)7, (void *)8, (void *)9,
1,8,1, "CHEM 3 RATE....% 34.9i L/Hr" ,0, (void *)85, (void *)7, (void *)8, (void *)9,
1,9,0, "PH READ........% s PH" ,1, (void *)ReadpHVolts, (void *)7, (void *)8, (void *)9,
1,10,1, "PH: Min: %f Max: %f" ,1, (void *)ReadpHMin, (void *)7, (void *)ReadpHMax, (void *)9,
1,11,0, "BATT VOLTS.....% s V" ,1, (void *)ReadBatt, (void *)7, (void *)8, (void *)9,
1,12,0, "Msg:%s" ,1, (void *)DemoData, (void *)7, (void *)8, (void *)9
);