port_to_mode_PGM[] indexing problem

Hello,
I recently encountered the following problem with the port_to_mode_PGM[] array.

The question is why does this code ....

void setup() {
  Serial.begin(9600);
  for(uint8_t i=0; i<4;i++){
    Serial.println(port_to_mode_PGM[i]);
  }
}

void loop(){
  
}

... have different output than this ...

void setup() {
  Serial.begin(9600);
  Serial.println(port_to_mode_PGM[0]);
  Serial.println(port_to_mode_PGM[1]);
  Serial.println(port_to_mode_PGM[2]);
  Serial.println(port_to_mode_PGM[3]);

}

void loop(){
  
}

The first outputs:
0
39064
47288
47288

and the second:
0
33
36
39

I am using the Arduino Mega.

From 'Arduino.h':

extern const uint16_t PROGMEM port_to_mode_PGM[];

'port_to_mode_PGM' is defined to be in PROGMEM. So, you must access it accordingly.

Because the first one uses a variable index, forcing the compiler to generate code to fetch the data. The "_PGM" part of the name means that the array is in PROGMEM but you are not calling the function that would fetch the data from PROGMEM so it is fetched from the same address in RAM. The RAM address does not contain the data you want.

The second one uses constants indexing into a constant array so the compiler knows what the result of the fetch should be and doesn't generate code to fetch the data.

To get the first one to work you would need something like:

    Serial.println(pgm_read_word(&port_to_mode_PGM[i]));

I assume it's a 'word' array rather than a 'byte' array because the values fetched from RAM would not fit in a byte.

johnwasser:
Because the first one uses a variable index, forcing the compiler to generate code to fetch the data. The "_PGM" part of the name means that the array is in PROGMEM but you are not calling the function that would fetch the data from PROGMEM so it is fetched from the same address in RAM. The RAM address does not contain the data you want.

The second one uses constants indexing into a constant array so the compiler knows what the result of the fetch should be and doesn't generate code to fetch the data.

To get the first one to work you would need something like:

    Serial.println(pgm_read_word(&port_to_mode_PGM[i]));

I assume it's a 'word' array rather than a 'byte' array because the values fetched from RAM would not fit in a byte.

Thank you! It works as expected now. But why does "port_to_mode_PGM" return a 16 bit word, when data direction registers are 8 bit. Should i just cast it to a uint8_t ?

Genwolf:
Thank you! It works as expected now. But why does "port_to_mode_PGM" return a 16 bit word, when data direction registers are 8 bit. Should i just cast it to a uint8_t ?

const uint16_t PROGMEM port_to_mode_PGM[] = {
	NOT_A_PORT,
	NOT_A_PORT,
	(uint16_t) &DDRB,
	(uint16_t) &DDRC,
	(uint16_t) &DDRD,
};

Because it is returning the ADDRESS of the Data Direction (pinMode()) Register for the port and addresses on the UNO are 16-bit. To use the address, cast it as a "(uint8_t *)" like *((uint8_t *)DDRaddress) = value;