Search code examples
c++arraysarduinoavrprogmem

How to make a 1D PROGMEM array (stored in Flash) of 2D PROGMEM arrays on AVR (ex: ATMega) or Arduino microcontrollers


I have several 2D arrays in PROGMEM.

I want to store them into another array, hence having a PROGMEM array of 2d PROGMEM arrays.

Then I want to read the data out.

Here's what I have

void myFunc()
{
  const static byte DATA1[3][2] PROGMEM = 
  {
    -1, 6,
    -3, 6,
    -5, 5
  };
  const static byte DATA2[3][2] PROGMEM = 
  {
    1,  0,
    1,  0,
    1,  0
  };
  const static byte DATA3[6][2] PROGMEM = 
  {
    0,  1,
    1,  3,
    2,  4,
    3,  4,
    4,  3,
    5,  1
  };
  //PROGMEM array of 2d arrays in PROGMEM
  const static byte* const MY_DATA[] PROGMEM = {DATA1, DATA2, DATA3}; 
  
  //read out the data now:
  byte myData = pgm_read_byte(&((MY_DATA[arrayNum])[x][y]));

  //use the data here, etc, etc...
}

My error is:

error: cannot convert 'const byte (*)[2] {aka const 
unsigned char (*)[2]}' to 'const byte* const {aka const
unsigned char* const}' in initialization

How am I supposed to do this? I've been reading around and trying to figure this out for well over an hr. I don't know what I'm doing wrong.

Helpful Reference pages:

  1. http://www.nongnu.org/avr-libc/user-manual/pgmspace.html
  2. http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#ga963f816fc88a5d8479c285ed4c630229
  3. https://www.arduino.cc/en/Reference/PROGMEM

Related:

Update: 6 years later (June 2022 now) and this is still super confusing stuff!

Here are a couple related answers I wrote though which help add further clarity. Using a typedef would significantly improve clarity in the accepted answer here as well.

  1. [my answer] Passing 1D arrays as function parameters in C (and C++)
  2. [my answer] How to use multidimensional (ex: 2D) arrays, and pointers to them, as function parameters in C and C++

Solution

  • The type of elements of MY_DATA is const byte* const, but DATA1, etc. are converted to const byte (*)[2] as the error message suggests, so type mismatch happens. Note that arrays in expression are automatically converted to pointers pointing their first elements except for some exceptions such as operator of unary & or sizeof.

    Use correct type: arrays of pointers to const byte (*)[2] like this:

    const static byte(* const MY_DATA[])[2] PROGMEM = {DATA1, DATA2, DATA3};
    

    This is "static variable MY_DATA as array of const pointer to array 2 of const byte".

    According to cdecl, const char(* const MY_DATA[99])[2]; can be decoded as "MY_DATA as array 99 of const pointer to array 2 of const char". With some modifications, you can get the implemention of the correct type.

    You can decode type declaration without const in reversed order of normal expression evaluation. (Sorry, currently I'm not good at decoding qualifiers) Let me decode byte(*MY_DATA[])[2] in this method.

    Normal expression evaluation:

    1. MY_DATA
    2. MY_DATA[] : some element of MY_DATA
    3. *MY_DATA[] : dereference the element
    4. (*MY_DATA[])[2] : some element of what is dereferenced

    Decoding type declaration (something not yet decoded is represented by @):

    1. byte @ : something having type byte
    2. byte @[2] : 2-element array of byte
    3. byte (*@)[2] : pointer to 2-element array of byte
    4. byte (*@[])[2] : array of pointers to 2-element array of byte
    5. byte (*MY_DATA[])[2] : MY_DATA, which is an array of pointers to 2-element array of byte