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

时间:2015-04-13 06:56:31

标签: c++ c data-structures struct arduino

我试图在Arduino中做多个菜单。每个菜单都有很多行。每行可能包含需要显示的变量。我无法理解如何:

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

下面我的代码几乎我想 - 看看我的评论(寻找" //帮助"):

// 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年前我学到这个以来的某个地方,这个细节从我的大脑中被垃圾收集: - (

3 个答案:

答案 0 :(得分:1)

你快到了。

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

int Menu(struct MenuT menu[])
{
    ...
}

(您可能想要添加第二个参数,以使函数中的有效元素数量可用)。

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

[编辑:将此添加为需求,其中附加注释更清晰]

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

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);

答案 1 :(得分:0)

还没有答案,但是,现在修复了大部分问题。 剩下的是 - 我如何使用联合,或者如果不使用联合,我如何启动对返回数据的函数的调用。这似乎不是: -

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

这是代码 - 搜索“// Help”

// 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;
}

答案 2 :(得分:0)

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

我能说的最好 - 必须使用宏 - 由于芯片的架构和使用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))();