Wrong data retrieved using PROGMEM

  1. I have a code from internet where entire array ASCII was in RAM & it was working. I have tried to move it to FLASH but error starts coming.
  2. LCDCharacter(‘A’), works fine if I remove PROGMEM from ASCII.
  3. But LCDCharacter_2(‘A’) gives error. I have checked problem instead of picking 5 bytes of ‘A’, it picks first byte of ‘A’ to ‘E’.
  4. how to correct that
#include <avr/pgmspace.h>

static const byte ASCII[][5] PROGMEM = {
  {0x00, 0x00, 0x00, 0x00, 0x00} // 20  
  ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 !
  ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 "
  ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 #
  ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $
  ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 %
  ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 &
  ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 '
  ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 (
  ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 )
  ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a *
  ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b +
  ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c ,
  ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d -
  ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e .
  ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f /
  ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0
  ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1
  ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2
  ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3
  ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4
  ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5
  ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6
  ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7
  ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8
  ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9
  ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a :
  ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ;
  ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c <
  ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d =
  ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e >
  ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ?
  ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @
  ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A
  ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B
  ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C
  ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D
  ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E
  ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F
  ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G
  ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H
  ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I
  ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J
  ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K
  ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L
  ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M
  ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N
  ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O
  ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P
  ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q
  ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R
  ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S
  ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T
  ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U
  ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V
  ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W
  ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X
  ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y
  ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z
  ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [
  ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c \
  ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ]
  ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^
  ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _
  ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 `
  ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a
  ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b
  ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c
  ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d
  ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e
  ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f
  ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g
  ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h
  ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i
  ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j 
  ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k
  ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l
  ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m
  ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n
  ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o
  ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p
  ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q
  ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r
  ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s
  ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t
  ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u
  ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v
  ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w
  ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x
  ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y
  ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z
  ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b {
  ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c |
  ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d }
  ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ~
  ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f DEL
};

void LCDCharacter(char character) {
  LCDWrite(LCD_DATA, 0x00); //Blank vertical line padding
  for (int index = 0 ; index < 5 ; index++)  {
    LCDWrite(LCD_DATA, ASCII[character - 0x20][index]);
  } 
    //0x20 is the ASCII character for Space (' '). The font table starts with this character

  LCDWrite(LCD_DATA, 0x00); //Blank vertical line padding
}

void LCDCharacter_2(char character) {
  
  byte temp;

  LCDWrite(LCD_DATA, 0x00); //Blank vertical line padding
  for (int index = 0 ; index < 5 ; index++)  {
    temp = pgm_read_byte_near(ASCII + (character - 0x20) + (index));
    LCDWrite(LCD_DATA, temp);
  } 
    //0x20 is the ASCII character for Space (' '). The font table starts with this character

  LCDWrite(LCD_DATA, 0x00); //Blank vertical line padding
}

The offset (in bytes) is 5 times the character. Try this:

temp = pgm_read_byte_near(ASCII + (5 * (character - 0x20)) + (index));

@Peter_n: Presently, I don't have a way to test this idea, but would it be possible to replace your code with a call to memmove() and if so, what's the impact on code size? Any noticeable impact on speed?

The microcontroller has no access to variables in flash with normal code. That is why the special functions like "pgm_read_byte" need to be used. A pointer to flash memory is no problem, but the microcontroller just can't read from it with normal code.

Some functions have been adapted to be used with PROGMEM data. For example "sprintf_P" and there is even "memcpy_P" or "memcpy_PF" or so. It doesn't matter a lot, since those funtions use internally one of the "pgm_read_byte" functions.

@Peter_n: Thanks!

  1. Didn't work. Again printing random values.

  2. Surprisingly, both statement below gives different results.

temp = pgm_read_byte_near(ASCII + (character - 0x20) + (index)); temp = pgm_read_byte_near(ASCII[character - 0x20][index]);

Didn't work

Can't see your code.

Try temp = pgm_read_byte_near(ASCII[character - 0x20] + index); or temp = pgm_read_byte_near(&ASCII[character - 0x20][index]);

Putting constant data into program memory (PROGMEM)

I didn’t read your first code very well. You already tried the times 5 by using the double index. Sorry :-[

Here is a working sketch:

// For : http://forum.arduino.cc/index.php?topic=344169.0
// Tested on Arduino Uno with Arduino IDE 1.6.5

const byte ASCII[][5] PROGMEM = {
  {0x00, 0x00, 0x00, 0x00, 0x00}, // 20
  {0x00, 0x00, 0x5f, 0x00, 0x00}, // 21 !
  {0x00, 0x07, 0x00, 0x07, 0x00}, // 22 "
  {0x14, 0x7f, 0x14, 0x7f, 0x14}, // 23 #
  {0x24, 0x2a, 0x7f, 0x2a, 0x12}, // 24 $
  {0x23, 0x13, 0x08, 0x64, 0x62}, // 25 %
  {0x36, 0x49, 0x55, 0x22, 0x50}, // 26 &
  {0x00, 0x05, 0x03, 0x00, 0x00}, // 27 '
  {0x00, 0x1c, 0x22, 0x41, 0x00}, // 28 (
  {0x00, 0x41, 0x22, 0x1c, 0x00}, // 29 )
  {0x14, 0x08, 0x3e, 0x08, 0x14}, // 2a *
  {0x08, 0x08, 0x3e, 0x08, 0x08}, // 2b +
  {0x00, 0x50, 0x30, 0x00, 0x00}, // 2c ,
  {0x08, 0x08, 0x08, 0x08, 0x08}, // 2d -
  {0x00, 0x60, 0x60, 0x00, 0x00}, // 2e .
  {0x20, 0x10, 0x08, 0x04, 0x02}, // 2f /
  {0x3e, 0x51, 0x49, 0x45, 0x3e}, // 30 0
  {0x00, 0x42, 0x7f, 0x40, 0x00}, // 31 1
  {0x42, 0x61, 0x51, 0x49, 0x46}, // 32 2
  {0x21, 0x41, 0x45, 0x4b, 0x31}, // 33 3
  {0x18, 0x14, 0x12, 0x7f, 0x10}, // 34 4
  {0x27, 0x45, 0x45, 0x45, 0x39}, // 35 5
  {0x3c, 0x4a, 0x49, 0x49, 0x30}, // 36 6
  {0x01, 0x71, 0x09, 0x05, 0x03}, // 37 7
  {0x36, 0x49, 0x49, 0x49, 0x36}, // 38 8
  {0x06, 0x49, 0x49, 0x29, 0x1e}, // 39 9
  {0x00, 0x36, 0x36, 0x00, 0x00}, // 3a :
  {0x00, 0x56, 0x36, 0x00, 0x00}, // 3b ;
  {0x08, 0x14, 0x22, 0x41, 0x00}, // 3c <
  {0x14, 0x14, 0x14, 0x14, 0x14}, // 3d =
  {0x00, 0x41, 0x22, 0x14, 0x08}, // 3e >
  {0x02, 0x01, 0x51, 0x09, 0x06}, // 3f ?
  {0x32, 0x49, 0x79, 0x41, 0x3e}, // 40 @
  {0x7e, 0x11, 0x11, 0x11, 0x7e}, // 41 A
  {0x7f, 0x49, 0x49, 0x49, 0x36}, // 42 B
  {0x3e, 0x41, 0x41, 0x41, 0x22}, // 43 C
  {0x7f, 0x41, 0x41, 0x22, 0x1c}, // 44 D
  {0x7f, 0x49, 0x49, 0x49, 0x41}, // 45 E
  {0x7f, 0x09, 0x09, 0x09, 0x01}, // 46 F
  {0x3e, 0x41, 0x49, 0x49, 0x7a}, // 47 G
  {0x7f, 0x08, 0x08, 0x08, 0x7f}, // 48 H
  {0x00, 0x41, 0x7f, 0x41, 0x00}, // 49 I
  {0x20, 0x40, 0x41, 0x3f, 0x01}, // 4a J
  {0x7f, 0x08, 0x14, 0x22, 0x41}, // 4b K
  {0x7f, 0x40, 0x40, 0x40, 0x40}, // 4c L
  {0x7f, 0x02, 0x0c, 0x02, 0x7f}, // 4d M
  {0x7f, 0x04, 0x08, 0x10, 0x7f}, // 4e N
  {0x3e, 0x41, 0x41, 0x41, 0x3e}, // 4f O
  {0x7f, 0x09, 0x09, 0x09, 0x06}, // 50 P
  {0x3e, 0x41, 0x51, 0x21, 0x5e}, // 51 Q
  {0x7f, 0x09, 0x19, 0x29, 0x46}, // 52 R
  {0x46, 0x49, 0x49, 0x49, 0x31}, // 53 S
  {0x01, 0x01, 0x7f, 0x01, 0x01}, // 54 T
  {0x3f, 0x40, 0x40, 0x40, 0x3f}, // 55 U
  {0x1f, 0x20, 0x40, 0x20, 0x1f}, // 56 V
  {0x3f, 0x40, 0x38, 0x40, 0x3f}, // 57 W
  {0x63, 0x14, 0x08, 0x14, 0x63}, // 58 X
  {0x07, 0x08, 0x70, 0x08, 0x07}, // 59 Y
  {0x61, 0x51, 0x49, 0x45, 0x43}, // 5a Z
  {0x00, 0x7f, 0x41, 0x41, 0x00}, // 5b [
  {0x02, 0x04, 0x08, 0x10, 0x20}, // 5c \
  {0x00, 0x41, 0x41, 0x7f, 0x00}, // 5d ]
  {0x04, 0x02, 0x01, 0x02, 0x04}, // 5e ^
  {0x40, 0x40, 0x40, 0x40, 0x40}, // 5f _
  {0x00, 0x01, 0x02, 0x04, 0x00}, // 60 `
  {0x20, 0x54, 0x54, 0x54, 0x78}, // 61 a
  {0x7f, 0x48, 0x44, 0x44, 0x38}, // 62 b
  {0x38, 0x44, 0x44, 0x44, 0x20}, // 63 c
  {0x38, 0x44, 0x44, 0x48, 0x7f}, // 64 d
  {0x38, 0x54, 0x54, 0x54, 0x18}, // 65 e
  {0x08, 0x7e, 0x09, 0x01, 0x02}, // 66 f
  {0x0c, 0x52, 0x52, 0x52, 0x3e}, // 67 g
  {0x7f, 0x08, 0x04, 0x04, 0x78}, // 68 h
  {0x00, 0x44, 0x7d, 0x40, 0x00}, // 69 i
  {0x20, 0x40, 0x44, 0x3d, 0x00}, // 6a j
  {0x7f, 0x10, 0x28, 0x44, 0x00}, // 6b k
  {0x00, 0x41, 0x7f, 0x40, 0x00}, // 6c l
  {0x7c, 0x04, 0x18, 0x04, 0x78}, // 6d m
  {0x7c, 0x08, 0x04, 0x04, 0x78}, // 6e n
  {0x38, 0x44, 0x44, 0x44, 0x38}, // 6f o
  {0x7c, 0x14, 0x14, 0x14, 0x08}, // 70 p
  {0x08, 0x14, 0x14, 0x18, 0x7c}, // 71 q
  {0x7c, 0x08, 0x04, 0x04, 0x08}, // 72 r
  {0x48, 0x54, 0x54, 0x54, 0x20}, // 73 s
  {0x04, 0x3f, 0x44, 0x40, 0x20}, // 74 t
  {0x3c, 0x40, 0x40, 0x20, 0x7c}, // 75 u
  {0x1c, 0x20, 0x40, 0x20, 0x1c}, // 76 v
  {0x3c, 0x40, 0x30, 0x40, 0x3c}, // 77 w
  {0x44, 0x28, 0x10, 0x28, 0x44}, // 78 x
  {0x0c, 0x50, 0x50, 0x50, 0x3c}, // 79 y
  {0x44, 0x64, 0x54, 0x4c, 0x44}, // 7a z
  {0x00, 0x08, 0x36, 0x41, 0x00}, // 7b {
  {0x00, 0x00, 0x7f, 0x00, 0x00}, // 7c |
  {0x00, 0x41, 0x36, 0x08, 0x00}, // 7d }
  {0x10, 0x08, 0x08, 0x10, 0x08}, // 7e ~
  {0x78, 0x46, 0x41, 0x46, 0x78}, // 7f DEL
};

void setup() {
  Serial.begin(9600);
  Serial.println(F("\nStarted !"));


  for ( int asc = 0x20; asc <= 0x7F; asc++) {
    Serial.print(F("asc="));
    if( asc < 100)
      Serial.print(F("0"));
    Serial.print(asc);
    Serial.print(F(" : "));
    for ( int index = 0; index < 5; index++) {
      byte temp = pgm_read_byte( &ASCII[asc-0x20][index]);
      Serial.print(F("0x"));
      if ( temp < 0x10)
        Serial.print(F("0"));
      Serial.print( temp, HEX);
      Serial.print(F(", "));
    }
    Serial.println();
  }
}

void loop() {
}

Can you spot the differences ?
I don’t know if the comma is an error, but the table should have the comma at the end of the line, not at the beginning.
The ASCII[1][2] would return the value itself. But the pgm_read…() functions require a pointer. So you need an extra ‘&’ for the pointer to that.
I don’t know if other changes have any influence, I just made it straightforward and working :stuck_out_tongue:

(While I was making the test sketch, oqibidipo already spotted the problem, and Nick Gammon already mentioned his awesome tutorial)

Thanx