Arduino:如何使用PROGMEM(可读)初始化结构

时间:2015-04-13 15:46:17

标签: arduino progmem

我有这个:

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
  char *mtext;           // What to say, including sprintf placeholders:  eg: "STOP TIMER...% 4.2i MIN"
  byte mdatatype1;       // 0 means this is actual data to print.  1 means go call the supplied function to get the data when needed.
  void *mdata1;          // Where to get any data from for the menu (upto 2 different bits allowed per line)
} menu_item_type;

然后这个: -

struct menu_item_def sub_menu_items[MAX_MENU_LINES] = {
  {1,2,0, "MOTOR RPM.....% 5.1i RPM"  ,0,(void *)8500,0,0},
  {1,2,1, "ALARM MAX....% 5.2f Lt/Hr" ,0,(void *)85,0,0},
  {1,2,0, "ALARM MIN.... %s"          ,2,(void *)"ON/OFF",0,0},
};

所以我稍后可以将菜单传递给这样的函数:

  Menu(sub_menu_items);

如何将所有内容移动到FLASH中?

我已经尝试过一堆东西的组合,但没有什么能减少"全局变量使用688字节(33%)"消息,除了这个凌乱的kludge:

const char string_0[] PROGMEM = "STOP TIMER....% 5.1i MIN"; 
struct  menu_item_def main_menu_items[MAX_MENU_LINES] = {
    { 1,2,0, (char *)string_0  ,0, (void *)83,0,0 },
    { 1,2,1, "CHEM RATE...% 5.2i Lt/Hr" ,1, (void *)DemoData,0,0 },
    { 1,2,0, "CHEM PUMP....... %s"          ,2, (void *)"ON/OFF",0,0 },
};

这会破坏我将可读菜单编码到我的源代码中的能力,并且沿途所有菜单工作都会增加三倍......

我怎样才能以某种方式完成这项工作,同时保持"定义"我的菜单行在我的源代码中是一行的吗?

2 个答案:

答案 0 :(得分:1)

假设字符串长度相似,您需要做的就是使字符串成为固定长度的数组,例如。

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
  char mtext [30];       // What to say, including sprintf placeholders:  eg: "STOP TIMER...% 4.2i MIN"
  byte mdatatype1;       // 0 means this is actual data to print.  1 means go call the supplied function to get the data when needed.
  void *mdata1;          // Where to get any data from for the menu (upto 2 different bits allowed per line)
} menu_item_type;

现在整个结构可以在PROGMEM中,例如

const struct menu_item_def sub_menu_items[MAX_MENU_LINES] PROGMEM = {
  {1,2,0, "MOTOR RPM.....% 5.1i RPM"  ,0,(void *)8500,0,0},
  {1,2,1, "ALARM MAX....% 5.2f Lt/Hr" ,0,(void *)85,0,0},
  {1,2,0, "ALARM MIN.... %s"          ,2,(void *)"ON/OFF",0,0},
};

要访问字符串数据,您需要一个合适的中间函数,例如

// Print a string from Program Memory directly to save RAM 
void printProgStr (const char * str)
{
  char c;
  if (!str) 
    return;
  while ((c = pgm_read_byte(str++)))
    Serial.print (c);
} // end of printProgStr

对于其他值,请视情况使用pgm_read_bytepgm_read_word

参考

Putting constant data into program memory (PROGMEM)

答案 1 :(得分:0)

我能说的最好 - 必须使用宏 - 由于芯片的架构和PROGMEM的工作方式,任何其他方式都是不可能的。

以下说明了我实施的内容,并且或多或少是“答案”。

此代码的结尾是“Readably init structure”部分 - 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
  );