如何初始化静态 arduino (C) 数据结构,然后使用它们?

Posted

技术标签:

【中文标题】如何初始化静态 arduino (C) 数据结构,然后使用它们?【英文标题】:How do I initialize static arduino (C) data structures, then use them? 【发布时间】:2015-04-13 06:56:31 【问题描述】:

我正在尝试在 Arduino 中创建多个菜单。每个菜单都有很多行。每行可能有需要显示的变量。我不知道如何:

a) 正确定义我的菜单结构 b) 将起始数据加载到其中 c) 使用它

我认为下面的代码几乎在那里 - 请参阅我的 cmets(查找“// HELP”):

// Example for doing Menus for JB

#define MAX_MENU_LINES 3  // How many lines are on each of your menu screens

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)

  byte mdatatype2;
  void *mdata2;

 menu_item_type;


// I do not want to do this:-
struct MenuT 
  menu_item_type mline;
 MainMenu[MAX_MENU_LINES] = 
  1,2,0, "STOP TIMER....% 5.1i MIN"  ,0,(void *)85,0,0,
  1,2,1, "CHEM RATE....% 5.2f Lt/Hr" ,1,(void *)DemoData,0,0,
  1,2,0, "CHEM PUMP.... %s"          ,0,(void *)"ON/OFF",0,0,
;

// HELP
/* I would prefer to do something like this:-

typedef struct menu_def 
  menu_item_type mline[MAX_MENU_LINES];
 menu_type;

menu_type MainMenu = 
  1,2,0, "STOP TIMER....% 5.1i MIN"  ,0,(void *)85,0,0,
  1,2,1, "CHEM RATE....% 5.2f Lt/Hr" ,0,(void *)85,0,0,
  1,2,0, "CHEM PUMP.... %s"          ,0,(void *)"ON/OFF",0,0,
;

menu_type SubMenu = 
  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"          ,0,(void *)"ON/OFF",0,0,
;


*/

#define MAX_WIDTH 256   // This is the max width of anything you need to print - increase this if your LCD is wider
char buf[MAX_WIDTH];
char *p(char *fmt, ... )  // Helper routine for putting numbers/readings/etc into printable strings
   va_list args; va_start (args, fmt );
   vsnprintf(buf, MAX_WIDTH, fmt, args);
   va_end (args);
   return buf;




void setup() 
  // put your setup code here, to run once:
  Serial.begin(9600); // I have no LCD, so, my output is to serial (Hit Ctrl+Shit+M or go Tools->Serial Monitor to see output). Note: Seral-Monitor will reset board.
  Menu(MainMenu); // HELP - I don't know how to pass whatever got defined above


void loop() 
  // put your main code here, to run repeatedly:

  // Does nothing - the setup outputted the menu already




int Menu( /* // HELP - I don't know what to put here to recevied a passed-in menu */ ) 
  for(int i=0;i<MAX_MENU_LINES;i++) 
//     Serial.println(mymenu[i].mtext);  // Help - I don't know how to reference the bit I need!
  ;
   Serial.print(p("STOP TIMER...% 4.2i MIN",5));


int DemoData() 
  return 85;

很抱歉听起来像一个 n00b - 自从我 30 年前了解到这一点后,我的大脑中的细节被垃圾收集了:-(

【问题讨论】:

85 不是指针,是数字!!! DemoData 是指向函数 DemoData 的指针!!!我看到很多问题!!! 谢谢 Sergio - 是的,忽略我明显的错误(比如缺少“联合”)。这是我正在尝试解决的结构的语法。 (y) ;) 我喜欢你使用我的提示! 【参考方案1】:

你快到了。

您的菜单数据结构是struct MenuT 项的数组。您的 Menu() 函数的函数原型是

int Menu(struct MenuT menu[])

    ...

(您可能希望添加第二个参数以使数组中的有效元素数量在您的函数中可用)。

在您的代码中仍有一些不一致的地方(例如,在声明之前调用的函数)和一些 - 嗯 - 丑陋的部分(函数指针和使用 void 的同一结构成员上的数据,这是工会的意思),但我想你可以自己解决这个问题。

[编辑:将此添加为要求,通过额外的 cmets 更清楚]

如果您需要多个菜单,可以按如下方式设置数据结构:

struct MenuT 
    int num_items;           /* number of items in mline-array */
    menu_item_type *mline;
;

struct menu_item_type main_menu_items[] = 
     1,2,0, "STOP TIMER....% 5.1i MIN"  ,0, (void *) 85,0,0 ,
     1,2,1, "CHEM RATE....% 5.2f Lt/Hr" ,1, (void *) DemoData,0,0 ,
     1,2,0, "CHEM PUMP.... %s"          ,0, (void *) "ON/OFF",0,0 ,
;

struct menu_item_type other_menu_items[] = 
     .... ,
;
struct MenuT main_menu = 
    3,
    main_menu_items
;

struct MenuT other_menu = 
    2,
    other_menu_items
;

引用 Menu() 中的项目将变成

int Menu(struct MenuT *menu) 
    for (int i = 0; i < menu->num_items; i++) 
        Serial.println(menu->items[i].mtext);
    

当从main() 调用时喜欢

Menu(&main_menu);
Menu(&other_menu);

【讨论】:

哇-过去的爆炸-工会,感谢您提醒我正确的方法!你的 decl 工作 - 谢谢!我还在为如何参考而苦苦挣扎? Serial.println(mymenu.mline[i].mtext); ...不是吗...:-( 您不需要嵌套结构定义。只需使用 menu_item_types 数组(重命名为 MenuT)。如果你然后使用我上面给出的参数列表,那将是menu[i].mtext 我想我确实需要额外的定义?我有很多菜单,而不仅仅是一个,所以我需要以某种方式定义菜单结构中的内容,这样我就可以创建许多这样的结构,然后在调用参数等时使用它们。 @cnd:明白。我会调整我的答案。 +1 以获得一些很棒的指针并在那里工作!我尝试按照您的建议添加联合,但我偷偷地怀疑不可能有初始化其中联合的静态结构(似乎没有办法指定数据所属的联合组件的名称到?)。如果我错了 - 请让我知道或帮助我学习如何创建一些东西,其中包括一个函数指针上的 int 联合,以及使用 85 和我的“DemoData”函数地址的静态初始化) .我尝试了很多东西,编译扼流圈试图转换【参考方案2】:

还没有答案,但是,大多数问题现在都已解决。 剩下的 - 是我如何使用联合,或者如果不使用联合,我如何启动对返回数据的函数的调用。这,似乎,不是吗:-

long myint=(*(mymenu[i].mdata1))(); 

这是代码 - 搜索“//帮助”

// Example for doing Menus for JB

#define MAX_MENU_LINES 3  // How many lines are on each of your menu screens

// This is unused - I think it's imposible to static-init using these?
union multi_data 
  int d_int;             // mdatatype 0
  void *d_function;      // 1
  float d_float;         // 2
;

// This is the definition of each line in a menu
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)

  byte mdatatype2;
  void *mdata2;

 menu_item_type;

// All menus are a collection of lines (exactly MAX_MENU_LINES (3 in this example) of them to be exact)
struct menu_item_def main_menu_items[MAX_MENU_LINES] = 
     1,2,0, "STOP TIMER....% 5.1i MIN"  ,0, (void *)85,0,0 ,
     1,2,1, "CHEM RATE....% 5.2f Lt/Hr" ,1, (void *)DemoData,0,0 ,
     1,2,0, "CHEM PUMP.... %s"          ,0, (void *)"ON/OFF",0,0 ,
;

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"          ,0,(void *)"ON/OFF",0,0,
;


#define MAX_WIDTH 256   // This is the max width of anything you need to print - increase this if your LCD is wider
char buf[MAX_WIDTH];
char *p(char *fmt, ... )  // Helper routine for putting numbers/readings/etc into printable strings
   va_list args; va_start (args, fmt );
   vsnprintf(buf, MAX_WIDTH, fmt, args);
   va_end (args);
   return buf;


void setup() 
  // put your setup code here, to run once:
  Serial.begin(9600); // I have no LCD, so, my output is to serial (Hit Ctrl+Shit+M or go Tools->Serial Monitor to see output). Note: Seral-Monitor will reset board.
  Menu(main_menu_items);
  Menu(sub_menu_items);


void loop() 
  // put your main code here, to run repeatedly:
  // Does nothing - the setup outputted the menu already


int Menu( struct menu_item_def *mymenu ) 
  for(int i=0;i<MAX_MENU_LINES;i++) 
    if(mymenu[i].mdatatype1==0)  // an int
      long myint=(long)mymenu[i].mdata1;
      Serial.println(p(mymenu[i].mtext,42));
     else 
      long myint=(*(mymenu[i].mdata1))(); // Help - gives: "JB_Menus.ino:67:37: error: 'void*' is not a pointer-to-object type"
      Serial.println(p(mymenu[i].mtext,myint));
    
  ;
   Serial.print(p("STOP TIMER...% 4.2i MIN",5));
   MenuLine(5,6);



int MenuLine(int x, int y) 
   Serial.print(x);


int DemoData() 
  return 86;

【讨论】:

long myval=(reinterpret_cast)()>(mymenu[i].mdata1))();// 这解决了调用问题 - 所以现在 -只剩下关于工会的问题了。 好的——我能说的最好——联合无法以可读的方式初始化,尤其是在 Arduino 上,当用于零件需要进入闪存而不是 SRAM 的结构时。我想我唯一的答案是使用一个复杂的宏来使我的结构初始化代码易于阅读。【参考方案3】:

这就是我最终的做法。感谢所有提供我需要的线索的人!!!

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

警告: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
  );

要使用这些东西,我只是为简单的数据类型这样做:

long myval=(long)mymenu[i].mdata1;

这是调用我的函数:

long myval=(*reinterpret_cast<long (*)()>(mymenu[i].mdata1))();

【讨论】:

以上是关于如何初始化静态 arduino (C) 数据结构,然后使用它们?的主要内容,如果未能解决你的问题,请参考以下文章

C ++初始化对象的非静态成员数组

如何在 Arduino 类中创建 ISR?

如何使用Arduino的库?

arduino 读取串口数值 ~

arduino 串口如何数据提取变量,请各位高手指点

C # 如何连接到sql 数据库 要详细的代码,为啥显示字段初始值无法引用非静态字段?!