The sketch below runs fine on a Duemilanove, printing the strings contained in the PROGMEM'd structs on serial port. If I change the loop condition from j<1 to j<2 or anything above that, I get complete garbage, even for j==0. Note that it works for the similar cases above the loop. What's the issue here?
(Using Arduino 1.0.2)
I think the problem is that '&myEs[j]' is being treated as an SRAM address when j is not a compile-time constant. Since the array and the structures it points to are in FLASH it fetches the wrong value when gets the contents of 'name' or 'id'. That wrong address points to unused memory (0xFF) so it displays the ÿÿÿÿÿÿÿ until it reaches a null character.
The fix seems to be to do the fetches from FLASH manually:
Serial.println(F("\r\n\r\nCall in loop:"));
for (j=0; j<3; j++){
MyStruct * myStruct = (MyStruct *) pgm_read_word(&myEs[j]);
char * name = (char *) pgm_read_word(&myStruct->name);
char * id = (char *) pgm_read_word(&myStruct->id);
Serial.print(j, DEC);
Serial.print(F(":\t"));
Serial.println(FS(name));
Serial.print('\t');
Serial.println(FS(id));
}
jlab8:
So why would it be treated as SRAM address?
We all know that the compiler doesn't usually keep track of which pointers are PROGMEM and which are SRAM. Life would be so much easier if it did. I'm surprised the versions that use compile-time constants actually work. I thought that any value in PROGMEM had to be explicitly fetched from PROGMEM with the pgm_read() functions. Apparently this version of the compiler tracks PROGMEM addresses at compile time but not at run time.
A lot of confusion I see in that piece of code. Too many PROGMEM, too many casts, a F() clone... AFAIK, you have to actually fetch every single byte out of PROGMEM explicitly. Look at how Print doest it:
size_t Print::print(const __FlashStringHelper *ifsh)
{
const char PROGMEM *p = (const char PROGMEM *)ifsh;
size_t n = 0;
while (1) {
unsigned char c = pgm_read_byte(p++);
if (c == 0) break;
n += write(c);
}
return n;
}
The __FlashStringHelper* trick is used to have the compiler call the correct print() version based on where the string literal is stored. It's needed because it can't differentiate by itself PROGMEM pointers from SRAM (i.e. normal) pointers and use the correct "data retrieval" method.
johnwasser:
I think the problem is that '&myEs[j]' is being treated as an SRAM address when j is not a compile-time constant.
I get lost in all the indirection, but is it possible that the struct fields need to have the PROGMEM annotation so that the compiler will treat references to them correctly?
johnwasser:
I think the problem is that '&myEs[j]' is being treated as an SRAM address when j is not a compile-time constant.
I get lost in all the indirection, but is it possible that the struct fields need to have the PROGMEM annotation so that the compiler will treat references to them correctly?
tuxduino:
A lot of confusion I see in that piece of code. Too many PROGMEM, too many casts, a F() clone... AFAIK, you have to actually fetch every single byte out of PROGMEM explicitly. Look at how Print doest it:
Well, this certainly is not meant to be a very poetic piece of code, and granted, those casts are unnecessary and some relic of my trying to get things work and then not cleaning up. But I don't understand: Which of the PROGMEM is superfluous? And where did I clone F()?
tuxduino:
A lot of confusion I see in that piece of code. Too many PROGMEM, too many casts, a F() clone... AFAIK, you have to actually fetch every single byte out of PROGMEM explicitly. Look at how Print doest it:
Well, this certainly is not meant to be a very poetic piece of code, and granted, those casts are unnecessary and some relic of my trying to get things work and then not cleaning up. But I don't understand: Which of the PROGMEM is superfluous? And where did I clone F()?
Sorry, looked too quickly at the code, should have been more constructive. Let me try again...
This
#define FS(x) (__FlashStringHelper*)(x)
looked to me like this
class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
from memory, but I didn't remember exactly how F() was defined
You're using myEs as if it was a normal (i.e. SRAM-stored) array, but it's in PROGMEM, so you have to read that in first.
Then you access myEs[j], which again is in PROGMEM, so you have to fetch it in first.
Finally you take the address of the name field, which is in PROGMEM, and you fetch it in with pgm_read (correct).
So you have to either break the dereferencing in single steps and pgm_read each piece, or use PROGMEM for the "leaf" elements only (i.e. mnX, mtX).