[SOLVED] problem with two-dimensional array in progmem

Hi arduino fans,

I'm currently working on a project that uses a graphical display. Up to now I've put my array for the fonts in the RAM-space. However the space is getting full now and I decided to move all my font-data to the progmem space. The problem is that I use a two-dimensional Array (One dimension for the Symbol, the other for the pixel-data) and I have to pass the array to different functions.
A (simplified) example:

const byte Numberss [] [10] = {     
   { 0x3F, 0x65, 0x67, },              // Pixel data for number 0
   { 0x4C, 0x67, 0xE4, },              // Pixel data for number 1
              //...
};

In main loop I call this function:

DrawNumber(Numbers[1]); // Im passing only one dimension of the array

Then the function does:

void DrawNumber(const byte _numb[]) {
 
  for(int bytecount = 0; bytecount < 3, bytecount++) {     //scan through all bytes
    for(int bitcount = 0; bitcount < 8, bitcount++) {         //scan through all bits
      boolean _pixel = bitRead(_numb[bytecount], bitcount);
      // Stuff that draws Pixel on display
    }
  }
}

I've read this post: Multi-dimensional array in PROGMEM (Arduino Noob) - Syntax & Programs - Arduino Forum. So far so good. But how do I use the "pgm_read_byte()"-function In my example, my DrawNumber function gets only one dimension of the font array.

Any guesses are appreciated.

Thanks in advance, Andy

      boolean _pixel = bitRead(pgm_read_byte(&_numb[bytecount]), bitcount);

or maybe

      boolean _pixel = bitRead(pgm_read_byte(_numb+bytecount), bitcount);

Thanks for your reply, johnwasser

But that doesn't work. I'm only getting garbage pixels on my display.
I think I've used the PROGMEM Keyword correctely:

const byte Numberss [] [10] PROGMEM = {

Do I have to modify this line too?

DrawNumber(Numbers[1]); // Im passing only one dimension of the array

A PROGMEM pointer/array acts just like a character pointer/array except the data is in a different address space.

You can use a regular pointer or array directly:

      char array[100];
      char * pointer = array;
      // These all reference the same character:
      array[10];
      pointer[10];
      *(pointer+10);
      *(array+10);

Because the PROGMEM array is in a different address space and the compiler doesn't keep track, you have to use pgm_byte_read() to do any fetches. If you let the compiler generate code to fetch the data it will fetch it from the same address in SRAM:

      char array[100] PROGMEM;
      char * pointer = array;  // No PROGMEM here since we want our pointer in SRAM
      // These all reference the same character:
      pgm_byte_read(&array[10]);
      pgm_byte_read(&pointer[10]);
      pgm_byte_read(pointer+10);
      pgm_byte_read(array+10);

Perhaps if you showed all your code...

andyb:
I've read this post: Multi-dimensional array in PROGMEM (Arduino Noob) - Syntax & Programs - Arduino Forum. So far so good. But how do I use the "pgm_read_byte()"-function In my example, my DrawNumber function gets only one dimension of the font array.

Maybe change the function prototype to DrawNumber( byte number ) and you call it with DrawNumber(1) instead of DrawNumber(Numbers[1]). Of course you need to modify the function by adding something like..

byte pixelsData = pgm_read_byte(&(Numbers[number][bytecount]));
boolean _pixel = bitRead(pixelsData, bitcount);

guix:

andyb:
I've read this post: Multi-dimensional array in PROGMEM (Arduino Noob) - Syntax & Programs - Arduino Forum. So far so good. But how do I use the "pgm_read_byte()"-function In my example, my DrawNumber function gets only one dimension of the font array.

Maybe change the function prototype to DrawNumber( byte number ) and you call it with DrawNumber(1) instead of DrawNumber(Numbers[1]). Of course you need to modify the function by adding something like..

byte pixelsData = pgm_read_byte(&(Numbers[number][bytecount]));

boolean _pixel = bitRead(pixelsData, bitcount);

Thanks guix for your answer, but I can't do this because of this reason:
Beside the Numbers Array I have to pass Also other arrays to the Funktion, like letters[] or Symbols[] and so on.
Why don't I use only one array? Because the arrays have different sizes. By combining them together I would waste a large amount of data.

I'll post my entire code as soon as I'm at home.

andyb:
Why don't I use only one array? Because the arrays have different sizes. By combining them together I would waste a large amount of data.

You could have an array of pointers that points to your other arrays Numbers, Letters etc. Look at the exemple Arrays of strings in PROGMEM - Arduino Reference

guix:

andyb:
Why don't I use only one array? Because the arrays have different sizes. By combining them together I would waste a large amount of data.

You could have an array of pointers that points to your other arrays Numbers, Letters etc. Look at the exemple Arrays of strings in PROGMEM - Arduino Reference

Thanks, I didn't know that. But what would that change in my example?

The whole display library is in the attachments.
Note that I put the array data at the bottom, it is normally the very first thing in my whole program.

Consider that the code worked perfectely with array in RAM.

However, that's the entire function used to draw a character or symbol:
Note that the first byte of my array data tells the width of the character.

Thanks :wink:

/*
?????????????????????????????????????????????????????????????????????????
?  print single Char                                                    ?
?????????????????????????????????????????????????????????????????????????
*/

void Display_printChar(byte _x, byte _y,  const byte _character [], byte _size, int _color, int _background) {
  
  byte length = _character [0];  // first byte indicates width of character
  byte countByte = 1;
  byte countBit = 0;
  
  // Setting the starting pixel
  writeCommand(SSD1331_CMD_SETCOLUMN);
  writeCommand(_x);
  writeCommand(95);

  writeCommand(SSD1331_CMD_SETROW);
  writeCommand(_y);
  writeCommand(63);
  
  
  for (byte row = 0; row < (15- (7 * _size)); row++) {
    
    for (byte column = 0; column < length; column++) {
      
      if ((column < (((length-1)/2)+1)) || (!_size))
      {
        //if (bitRead(_character [countByte], 7 - countBit)) {     // uncomment this line when Array is located in RAM
        if (bitRead(pgm_read_byte(&_character[countByte]), 7 - countBit)) {  // uncomment this line when Array is located in PROGMEM
          writeData(_color);
        }
        else {
          writeData(_background);
        }
      }
      
      countBit = countBit + 1 + _size;
      
      if (countBit == 8)  {
        countBit = 0;
        countByte++;
      }
    }
    
    // next pixel row:
    writeCommand(SSD1331_CMD_SETCOLUMN);
    writeCommand(_x);
    writeCommand(95);
    
    _y++;
    writeCommand(SSD1331_CMD_SETROW);
    writeCommand(_y);
    writeCommand(63);
    
  }
}

Any help is welcome :slight_smile:

My (wild) guess is the bitRead() function isn't able to address PROGMEM. Suggest something like

byte tempHolder;
tempHolder = pgm_read_byte(&_character[countByte]), 7 - countBit);
if (bitRead(tempholder), 7-countbit))

How is your "const byte _character []" declared? I hope it is:

const byte _character [] PROGMEM = {...};

tylernt:
My (wild) guess is the bitRead() function isn't able to address PROGMEM.

Thanks tylernt,
I tried out what you suggested, but it didnt solve the probloem. I'm getting random pixels as before.

johnwasser:
How is your "const byte _character []" declared? I hope it is:

const byte _character [] PROGMEM = {...};

Hi Johnwasser,

I don't really get what you mean :blush:. Should I change my function declaration to this:

void Display_printChar(byte _x, byte _y,  const byte _character [] PROGMEM, byte _size, int _color, int _background) {

However, my entire code is placed as an attachment some posts earlier (I added the declaration of my characters as a comment at the end of the file).

void Display_printChar(byte _x, byte _y,  const byte _character [], byte _size, int _color, int _background) {
  
  byte length = _character [0];  // first byte Indicates width of character

That won't be right will it? You are getting the wrong length.

solved XD
Thanks Nick Gammon,

I controlled my code a couple of times, but I've always overseen this error. Clearly it has to be:

byte length = pgm_read_byte(&_character [0]);  // first byte Indicates width of character

Sometimes only one line of code can temporarily ruin one's ambition. Now the work on my project can go on XD.