Pages: [1]   Go Down
Author Topic: Cant get PROGMEM to work  (Read 1129 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 4
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm pretty new to Arduino.  Familiar with but rusty at C.  First post.
I tried to convert a program using an array of strings to use PROGMEM without luck.
Then I tried to simply copy the sample at the end of http://arduino.cc/en/Reference/PROGMEM and run it, and got similar results.  I added a Serial.println("Hello") to the setup() function, and I see the program restarts every 500 mS.  I find I can successfully call pgm_read_word, but calling strcpy_P restarts the program.
Couldn't find a similar issue on the forums.  I'm using a Deumilanove.  The board/mcu is selected correctly in the Tools/Board menu.  Reloading other programs afterward works fine.  Is there something wrong with the sample, or is it my AVR328?
Any debugging hints appreciated.
« Last Edit: April 11, 2009, 03:15:54 pm by vank » Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 4
Posts: 997
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Van,

Can you share your code?

At the risk of seeming self-promoting, may I suggest you look at my "Flash" library, which considerably simplifies the PROGMEM stuff?

Mikal
« Last Edit: April 13, 2009, 11:33:23 pm by mikalhart » Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 135
Posts: 6763
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

FWIW, I copied the same sample code example (the one with the array of strings, right?) into a fresh sketch and it worked fine for me...
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 4
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi, and thanks!

Here's a complete program that does not perform as expected:

Code:
#include <avr/pgmspace.h>
prog_char str[] PROGMEM = "String";
char buffer[64];    // make sure this is large enough for the largest string it must hold

void setup()                    
{
  Serial.begin(9600);
  delay(500);
  Serial.print(millis(), HEX);
  Serial.print(": ");
  Serial.println("Begin");
}


void loop()                    
{
  char* p = (char*)pgm_read_word(&(str));
  delay( 500 );
  Serial.print(millis(), HEX);
  Serial.print(": ");
  Serial.println((long int)p, HEX);
  strcpy_P(buffer, p);
  delay( 500 );
  Serial.print(millis(), HEX);
  Serial.print(": ");
  Serial.println( buffer );
}

When I upload this and run it, the response is:
Code:
1F5: Begin
3F4: 7453
1F5: Begin
3F4: 7453
1F5: Begin
3F4: 7453
1F5: Begin
3F4: 7453
1F5: Begin
3F4: 7453
1F5: Begin
3F4: 7453
...

So the pgm_read_word appears to work,while the strcpy_P appears to cause a reset.
I cannot understand why...
Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 4
Posts: 997
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Van,

The line

Code:
 char* p = (char*)pgm_read_word(&(str));

reads a two byte word at the address in read-only memory referenced by "str".  These are the two bytes 'S' == 0x53 and 't' == 0x74.  p is now an artificially created pointer with value 0x7453.  Now when you call strcpy_P, you are telling it to copy from offset 0x7453, which is way outside the legal flash address space.

What you want is simply:

Code:
strcpy_P(buffer, str);

... or use the Flash library. smiley

Mikal
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 4
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Fascinating; your Flash library works fine, so there's clearly a mistake in my coding.  But I had copied an example tht worked for others as well.  ??!?

This runs as expected:
Code:
#include <Flash.h>
FLASH_STRING(fstr, "String");
char buffer[64];    // make sure this is large enough for the largest string it must hold

void setup()                    
{
  Serial.begin(9600);
  delay(500);
  Serial.print(millis(), HEX);
  Serial.print(": ");
  Serial.println("Begin");
}


void loop()                    
{
  fstr.copy(buffer,7);
  Serial.println(buffer);
  delay(500);
  Serial.print(millis(), HEX);
  Serial.print(": ");
}

...which means I'm unblocked.  But I would love to know what I did wrong.
Thanks for Flash!  You're right, it's harder than it looks.
Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 4
Posts: 997
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey, you were close!  You are far from the first person to stumble over pgm_read_word, etc.

Just FYI, if you want to use Flash to print out a flash-based string, you can avoid having to copy to an intermediate buffer (and save the 64 bytes) if you don't mind this slightly funky syntax:

Code:
FLASH_STRING(fstr, "String");
Serial << fstr << "\r\n";

EDIT: One other thing: If you do want to use the copy method, there is no need to specify the length if you are just copying the whole string.  This will work fine in your example:

Code:
fstr.copy(buffer);

Mikal
« Last Edit: April 15, 2009, 10:12:37 pm by mikalhart » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 16
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Mikal,

I wonder, if build-in Flash memory of ATmega328 is used for bootloader and probably for a program code, then how does your application knows where to start writing data in flash memory (so it does not overwrite memory occupied by others).
Am I right to assume, that reading data from flash should be in reverse order than writing (LIFO)?

Thank you in advance.
Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 4
Posts: 997
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi chupit,

Flash (the library) doesn't really make any decisions about where to write data.  It simply provides a layer on top of already existing technology that makes the reading of flash data simpler.  For example, if you wanted to write a program to print "Hello, world" without the use of my library, you could write something like this:

Code:
#include <avr/pgmspace.h>

const char PROGMEM str[] = "Hello, world!";

void setup()
{
  Serial.begin(9600);
  for (int i=0; ; ++i)
  {
    byte c = pgm_read_byte(str + i);
    if (c == '\0')
      break;
    Serial.print(c, BYTE);
  }
}
void loop(){}

In this short program, the compiler determines where in flash memory to put the code and where to put the "Hello, world!" string.

When you use the Flash library to do the same thing, you aren't doing anything differently.  The same allocation technique is going on "under the covers".  It's just that it's syntatically simpler:

Code:
#include <Flash.h>
FLASH_STRING(str, "Hello, world!");
void setup()
{
  Serial.begin(9600);
  str.print(Serial);
}
void loop(){}

Is that clearer?

Mikal
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 16
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes, thank you.
The question was more not about your library (which I like very much), but about how does it happens internally... who decides where to put and how to write to flash so that bootloader/program is not overwritten by data.
You said compiler does that under cover. That's explains it. Thanks!
Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 135
Posts: 6763
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I believe that your problem is that the example you were copying was using an ARRAY of strings stored in flash, while you removed the array and only had one string (which is an array of characters, of course.)

So when you did the pgm_read_word(), instead of getting the address of a particular string from the array, you were getting the first two bytes of the actual string.  When you tried to use that as an address instead of a string, of course it didn't work.
Code:
1F5: Begin
3F4: 7453
7453 in hex, converted to ASCII, is 't', 'S' (there's a byte swap that happens because of the way ints are stored in  bytewide memory.)  You shouldn't need "p" at all, and I think your program will work fine if you change it to have
Code:
 strcpy_P(buffer,&(str));

Quote
if build-in Flash memory of ATmega328 is used for bootloader and probably for a program code, then how does your application knows where to start writing data in flash memory (so it does not overwrite memory occupied by others).
The commands given to the compiler that Arduino uses (actually, the linker) tell it the "region" of flash memory that it can use.  2k is reserved for the bootloader, and is actually protected from being overwritten by the fuse settings (at least in theory.)  The 'PROGMEM' directive tells the compiler to put the string into the same area as the program instructions, but actually keeping it from colliding with the program is no more difficult than (for example) keeping functions from a library from colliding with functions from your sketch (or another library.)   The compiler essentially says "here's a chunk of 'stuff' that needs to go into flash, and over here is a spot that needs to contain the address of that chunk, once you've finally figured out what that address is going to be.  Then the linker just fills in chunk after chunk, pretty much sequentially, from the known start address, and back-filling the addresses as needed.
Logged

Pages: [1]   Go Up
Jump to: