Problem reading with PROGMEM: Odd var/const behavior

I have this bit of code which reads out an array of numbers from PROGMEM. This is based off of the example given in the Arduino reference for PROGMEM, except with a string of ints instead of chars.

First, I programmed in a bunch of arrays like this. They are sequences of 33 integers.

PROGMEM prog_uint16_t segment_A[] = {48, 47, 39, 40, 32, 33, 27, 20, 19, 26, 13, 05, 04, 03, 02, 01, 07, 15, 14, 21, 22, 25, 24, 23, 29, 36, 28, 35, 43, 42, 42, 42, 42};         //A
PROGMEM prog_uint16_t segment_B[] = {36, 13, 06, 05, 04, 03, 02, 01, 07, 15, 16, 23, 22, 21, 29, 28, 35, 43, 44, 45, 46, 47, 48, 39, 40, 32, 33, 27, 26, 25, 24, 19, 20};

then loaded stringTable in as an array of the arrays

PROGMEM prog_uint16_t *stringTable[] = 	   
{   
  segment_A,
  segment_B, //...and so on

Now here is where I run into trouble.

//read Segment_B into Segments[]
int newCount = 1; 

for (int i = 0; i < 33; i++)
{
  Segments[i] = pgm_read_word_near(stringTable[newCount] + i);
}

The problem is, it’s not working. It compiles fine, but the data it reads is junk.

However, if I declare newCount as a constant, or type an integer in place of newCount, such as

//read Segment_B into Segments[]
for (int i = 0; i < 33; i++)
{
  Segments[i] = pgm_read_word_near(stringTable[1] + i);
}

It works fine. Declaring newCount as a constant int works as well, but I don’t want that, I want to be able to increment it in a for loop.

What is going on? Why such a difference between using a variable int and a const int?

Just a hunch here. Try declaring newCount as a byte. I'm wondering if using the literal 1 isn't being interpreted by the compiler as a byte (8 bits) instead of an int (16 bits).

Also your PROGMEM arrays, do they really need to be arrays of ints? Would declaring them as prog_uint8_t (i.e. bytes) work, and then fetch them using pgm_read_byte or pgm_read_byte_near?

Remember both arrays are in progmem, so you need two reads from progmem.

//read Segment_B into Segments[]

//Get address of Segment_B
uint16_t *Array = pgm_read_word( &stringTable[newCount]  );

for (int i = 0; i < 33; i++)
{
  Segments[i] = pgm_read_word_near( &Array[ i ] );
}

The reason replacing newCount with a constant worked, is because the compiler inlined the data and did not actually use PROGMEM

PROGMEM prog_uint16_t segment_A[] = {48, 47, 39, 40, 32, 33, 27, 20, 19, 26, 13, 05, 04, 03, 02, 01, 07, 15, 14, 21, 22, 25, 24, 23, 29, 36, 28, 35, 43, 42, 42, 42, 42};         //A

Do you really need to use uint16_t? None of these values are that large.

Thanks for all your help guys. I changed it to bytes, declared the set of arrays as a single matrix instead, and added the & to the pgm_read function. It works now.

What does the & do in Segments[counternew] = pgm_read_byte_near(&(segmentTable[charCount][counternew]));

& is the address-of operator

After trying to use memcpy_PF() I read something in the LibC fine print…

These functions are an attempt to provide some compatibility with header files that come with IAR C, to make porting applications between different compilers easier. This is not 100% compatibility though (GCC does not have full support for multiple address spaces yet).

So I modified a text char demo to handle bytes. Here is copy a block of 8 bytes from flash to RAM:

#include <avr/io.h>
#include <avr/pgmspace.h>

const byte PROGMEM flashData[ ] = 
{ 
  0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 
};


byte ramData[ 8 ];


void setup(void)
{
  Serial.begin(9600);

  for ( byte i = 0; i < 8; i++ )
  {
    ramData[ i ] = pgm_read_byte( flashData + i ); // flashData without the [ ] is a pointer to flash
  }

  Serial.println( );
  for ( byte i = 0; i < 8; i++ )
  {
    Serial.print( "0x" );
    if ( ramData[ i ] < 0x10 )  Serial.print( "0" );
    Serial.print( ramData[ i ], HEX );
    if ( i < 7 )  Serial.print( ", " );
  }
  Serial.println( );
} 

void loop(void)
{
}

Very helpful! Thank you!

This is for a 43-segment display. The numbers are the panels 0-48. Take a look at the results…