Forum: Compiler & IDEs Zugriff auf PROGMEM struct-pointer-array in atmega328p


von Stefan (Gast)


Lesenswert?

Hallo liebes Forum :-)
ich habe ein Problem beim Zugriff auf ein Array aus Pointern auf 
Structs, die im Flash-Memory liegen. Es geht um einen Kommando-Parser, 
den ich schreiben möchte.

Jedes struct, das ein einzelnes Kommando hält ist vom Typ
1
/* to hold one command */
2
typedef struct{
3
4
  char *cmd;
5
  uint8_t numberOfOptions;
6
  uint8_t cmdCode;
7
8
}command;

Um die Kommandos einfach zu erzeugen habe ich mir das Makro
1
/* generate the structs that hold the commands */
2
#define ADD_COMMAND(NUMBER, CMD_NAME, NUMBER_OPT, CMD_CODE)                      \
3
  const char cmd_ ## NUMBER ## _name[] PROGMEM = CMD_NAME;                       \
4
  const command cmd_ ## NUMBER ## _ PROGMEM = { .cmd = cmd_ ## NUMBER ## _name,  \
5
                                                .numberOfOptions = NUMBER_OPT,   \
6
                                                .cmdCode = CMD_CODE              \
7
                                              };

geschrieben, das mir alle Informationen im Flash-Memory ablegt. Als 
nächstes lege ich die Kommandos an mit
1
ADD_COMMAND(0,  "\0" ,           0, 0x80)     /* no command*/
2
ADD_COMMAND(1,  "*RST\0",        0, 0x81)     /* reset */
3
ADD_COMMAND(2,  "*IDN?\0",       0, 0x82)     /* get IDN */
4
ADD_COMMAND(3,  "*IDN\0",        1, 0x83)     /* set IDN */
5
ADD_COMMAND(4,  "SETPIN\0",      2, 0x84)     /* turn pin on/off */
6
ADD_COMMAND(5,  "GETPIN\0",      1, 0x85)     /* get pin status */
7
ADD_COMMAND(6,  "SETPATT\0",     2, 0x86)     /* define pulse pattern */
8
ADD_COMMAND(7,  "STARTPATT\0",   0, 0x87)     /* start pattern */
9
ADD_COMMAND(8,  "STOPPATT\0",    0, 0x88)     /* stop pattern */
10
ADD_COMMAND(9,  "ALIVE?\0",      0, 0x89)     /* are you alive? */
11
ADD_COMMAND(10, "CFGEVENTCNT\0", 2, 0x8A)     /* configure event counter */
12
ADD_COMMAND(11, "GETEVENTCNT\0", 1, 0x8B)     /* get INT event counter value */

usw...da kommen dann noch ein paar mehr dazu.

Die einzelnen structs werden dann in einem Array gehalten, das auch im 
Flash-Speicher liegen soll. Um Compilezeit-Konstanten zu erhalten mache 
ich das als Pointer-Array. Das sieht dann so aus:
1
const command* const commandList[] PROGMEM = {&cmd_0_, &cmd_1_, &cmd_2_,
2
                                              &cmd_3_, &cmd_4_, &cmd_5_,
3
                                              &cmd_6_, &cmd_7_, &cmd_8_,
4
                                              &cmd_9_, &cmd_10_, &cmd_11_
5
                                             };

Jetzt kommts zur eigentlichen Frage:
Ich möchte gerne auf die einzelnen Kommando-Strings im Flash über einen 
Index zugreifen können um sie mit einem (im SRAM liegenden) über RS232 
empfangenen String zu vergleichen. Hierzu mache ich folgendes:
1
uint8_t i;
2
command *cmdPtr;
3
4
for(i=0; i<=TOTAL_NUMBER_OF_COMMANDS; i++){
5
  cmdPtr = (command*)pgm_read_word(commandList[i]);
6
  if(strcmp_P(rxString.buffer, cmdPtr->cmd) == 0){
7
    /* found known command */
8
    /* mache irgendwas tolles :-) */
9
  }
10
}

Das funktioniert jedoch nicht. Wo liegt also mein Denkfehler?
Bisher habe ich mir das so gedacht:

Im Array commandList liegen lauter Adressen auf structs vom Typ command 
im Flash-Memory. Diese Adressen hole ich mit pgm_read_word und weise sie 
dem cmdPtr zu. Nun kann man doch mit strcmp_P() direkt auf Adressen im 
Flash verweisen, was ich mit cmdPtr->cmd versucht habe. Leider klappt 
das nicht...

Habt ihr eine Idee woran das liegen könnte und wie man das beheben kann?

Viele Liebe Grüße,
Stefan

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:

> ich das als Pointer-Array. Das sieht dann so aus:
>
>
1
> const command* const commandList[] PROGMEM = {&cmd_0_, &cmd_1_, &cmd_2_,
2
>                                               &cmd_3_, &cmd_4_, &cmd_5_,
3
>                                               &cmd_6_, &cmd_7_, &cmd_8_,
4
>                                               &cmd_9_, &cmd_10_, 
5
> &cmd_11_
6
>                                              };
7
>


Du hast also das hier gebaut
1
  commandList  PROGMEM         cmd_0_   PROGMEM
2
  +--------------+             +----------+
3
  |  o------------------------>|   o--------------->"\0" (PROGMEM)
4
  +--------------+             |          |
5
  |  o---------------+         +----------+
6
  +--------------+   |
7
  |  o-------------+ |      +-----------+
8
  +--------------+ | +----->|   o------------>"RST" (PROGMEM)
9
  |              | |        |           |
10
  +--              |        +-----------+

auf alle Inhalte, die mit PROGMEM markiert sind, kannst du nicht direkt 
zugreifen, sondenr musst den UMweg über die pgm_xxx Funktionen gehen.

> Im Array commandList liegen lauter Adressen auf structs vom Typ command
> im Flash-Memory.

Ja.
Aber das Array, welches diese Adresse hält liegt ja selbst auch im 
Flash. Daher ja auch ...

>   cmdPtr = (command*)pgm_read_word(commandList[i]);

...
Aber: No.
Hier würdest du ja den Inhalt von commandList[i] an pgm_read_word 
übergeben. Das ist aber nicht das was du willst.
Du willst ja von der Flash Adresse vom i-ten Element von commandList 
lesen. Also
1
   cmdPtr = (command*)pgm_read_word( &commandList[i] );

von Stefan (Gast)


Lesenswert?

Hallo Karl Heinz,
erstmal vielen lieben Dank für deine Hilfe!

Das habe ich jetzt so übernommen. Wenn ich das richtig verstanden haben, 
dann enthält cmdPtr jetzt eine Adresse zu einem struct im Flash, das 
über den Index i ausgewählt wurde. Stimmt das?

Die Funktion sieht dann jetzt so aus (ich habe mal eine geschrieben, die 
mir einfach die Kommandos an den PC schickt...zum einfachen testen).
1
char txBuffer[50];
2
char *cmd;
3
4
for(i=0; i <= TOTAL_NUMBER_OF_COMMANDS; i++){
5
  cmdPtr = (command*)pgm_read_word(&commandList[i]);
6
  cmd = (char*)pgm_read_word( &cmdPtr->cmd );
7
  strcpy_P(txBuffer, cmd);
8
  sendText(txBuffer);
9
}

So funktionierts dann :-)

Also da muss ich mich noch etwas dran gewöhnen, mich durch die PROGMEMs 
zu hangeln, aber das wird klappen :-)

Vielen lieben Dank nochmals! Jetzt kommen die strings wunderbar bei mir 
an :-)

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:
> Hallo Karl Heinz,
> erstmal vielen lieben Dank für deine Hilfe!
>
> Das habe ich jetzt so übernommen. Wenn ich das richtig verstanden haben,
> dann enthält cmdPtr jetzt eine Adresse zu einem struct im Flash, das
> über den Index i ausgewählt wurde. Stimmt das?

Genau


> for(i=0; i <= TOTAL_NUMBER_OF_COMMANDS; i++){
>   cmdPtr = (command*)pgm_read_word(&commandList[i]);

Ja

>   cmd = (char*)pgm_read_word( &cmdPtr->cmd );

Exakt

>   strcpy_P(txBuffer, cmd);

Und auch hier richtig. Das 2.te Argument an strcpy_P ist die Adresse des 
Strings im Flash


> So funktionierts dann :-)
>
> Also da muss ich mich noch etwas dran gewöhnen, mich durch die PROGMEMs
> zu hangeln, aber das wird klappen :-)

Dein Fehler war

pgm_read_word, will die Adresse von der es lesen soll.
commandList[i] ist zwar auch eine Adresse (formal - ist ja 
datantypmässig ein Pointer) aber nicht die Adresse an der der Inhalt von 
commandList[i] steht. Die lautet &commandList[i]

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Kannst du nicht einfach __flash verwenden? Dann braucht es dan ganze 
Geraffel mit den pgm_read_xxx Funktionen erst garnicht.

Das einzige, worum du dich dann kümmern musst, sind richtige 
Deklarationen / Definitionen der entsprechenden Daten und Zeiger.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.