[TUT] [C] GCC and the PROGMEM Attribute

Go To Last Post
204 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

For an updated version of this tutorial in PDF format, please see this page of my website.

PART I - PROGMEM BASICS

The PROGMEM attribute is always a source of confusion for those beginning with AVR-GCC. The PROGMEM attribute is a powerful one and holds the potential to save a lot of RAM, which is something of a limited commodity on many AVRs. Before you can use the PROGMEM attribute, you must first understand what it does and why it is useful.

When strings are used in a program, they are commonly "hard-coded" into the firmware source code:

LCD_puts("Hardcoded String");

While this seems the most logical way of using strings, it is *not* the most optimal. Common sense and intuition would dictate that the compiler would store the string in the program memory, and read it out byte-by-byte inside the LCD_puts routine. But this is not what happens.

Because the LCD_puts routine (or other string routines) are designed to work with strings in RAM, the compiler is forced to read out the entire string constant from program memory into RAM, and then pass the string's RAM pointer to the routine (in this case LCD_puts). It certainly works but the RAM wastage adds up to significant amounts with each string. Why?

Initial variable values and strings are copied out from program memory into RAM as part of the C startup routines, which execute before your main() function. Those startup routines give your globals their initial values, as well as ensure that all strings are inside RAM so they can be passed to your desired string handling routines. As more strings are added to your program, more data must be copied to RAM at startup and the more RAM is used up by holding static (unchanging) data.

The solution to the problem is forcing strings to stay in program memory and only be read out as they are needed. This is not a simple task and requires string routines specifically designed to handle strings kept solely inside the program memory space.

Using the "const" modifier on your string variables is a natural solution - but a wrong one. A "const" variable can only not be modified by the program code, but it does not explicitly prevent the variable value from being copied out to the AVRs RAM on startup. Some compilers implicitly do this for you, but GCC needs to be explicitly told that the contents must live in program memory for the entire duration of the program's execution.

Entering pgmspace.h

The AVR-LibC library contains a header file, avr/pgmspace.h, which contains all the interfacing information needed to allow you to specify data which is to be kept inside the AVR's flash memory. To use the pgmspace.h functions, you need to include the header at the start of your C file(s):

#include 

To force a string into program memory, we can now use the "PROGMEM" attribute modifier on our string constants. An example of a global string which is stored into program memory and not copied out at execution time is:

char FlashString[] PROGMEM = "This is a string held completely in flash memory.";

Although it is not an absolute requirement, we can remind ourselves - and possibly prevent bugs further down the track - that the string cannot be changed at all during execution by declaring it as a constant:

const char FlashString[] PROGMEM = "This is a string held completely in flash memory.";

Now that the PROGMEM string has a "const" modifier, we cannot try to modify it in our code. The pgmspace.h header also exposes a neat little macro, PSTR, which by some GCC magic allows you to create inline strings:

LCD_puts(PSTR("Program Memory String"));

This stops you from having to clutter your program up with hundreds of variables which hold one-time-used strings. The downside to using the PSTR macro rather than a PROGMEM modified string variable is that you can only use the PSTR string once.

However, we now have a problem - now all your code doesn't work! What's wrong?

The problem is that your string functions are expecting the string to be inside RAM. When you pass a string to a routine, you are in fact just passing a pointer to the start of the string in RAM. The string handling routine then just loads the bytes of the string one-by-one, starting from the pointer's location. But our PROGMEM strings are not in RAM, so the pointer to them is invalid.

A pointer to a string stored in PROGMEM returns the address in flash memory at which the string is stored. It's still a valid pointer, it's just pointing to a different memory space. To use PROGMEM strings in our application, we need to make our string routines PROGMEM-pointer aware.

Again, pgmspace.h comes to our rescue. It contains several functions and macros which deal with PROGMEM-based strings. First, you have all your standard string routines (memcpy, strcmp, etc.) with a "_P" postfix denoting that the function deals with the FLASH memory space. For example, to compare a RAM-based string with a PROGMEM-based string, you would use the strcmp_P function:

strcmp_P("RAM STRING", PSTR("FLASH STRING"));

For a full list of the avaliable string functions, check out the AVRLibC documentation which is installed with your WinAVR installation.

But what if you have your own string function, like a USART-transmitting routine? Lets look at a typical example:

void USART_TxString(const char *data)
{
	while (*data != '\0')
	  USART_Tx(*data++);
}

This relies on the routine USART_Tx, which for the purposes of this example we will assume to be predefined as a function which transmits a single passed character through the AVR's USART. Now, how do we change our routine to use a PROGMEM string?

pgmspace.h exposes a macro which is important for PROGMEM-aware routines; pgm_read_byte. This macro takes a PROGMEM pointer as its argument, and returns the byte located at that pointer value. To mark that our new routine deals in PROGMEM strings, let's append a "_P" to it's name just like the other routines in pgmspace.h:

void USART_TxString_P(const char *data)
{
	while (pgm_read_byte(data) != 0x00)
	  USART_Tx(pgm_read_byte(data++));
}

Now we have our PROGMEM-aware routine, we can use PROGMEM-attributes strings:

USART_TxString_P(PSTR("FLASH STRING"));

Or:

const char TestFlashStr[] PROGMEM = "FLASH STRING";
USART_TxString_P(TestFlashStr);

This should give you a basic idea of how to store strings and keep them in flash memory space. What follows is the second half of this tutorial, for more advanced uses of the PROGMEM attribute.

PART II - More advanced uses of PROGMEM

Ok, so by now you should be able to create and use your own simple strings stored in program memory. But that's not the end of the PROGMEM road - there's still plenty more to learn! In this second tutorial section, I'll cover two slightly more advanced techniques using the PROGMEM attribute. The first part will lead on to the second, so please read both.

Storing data arrays in program memory

It's important to realize that all static data in your program can be kept in program memory without being read out into RAM. While strings are by far the most common reason for using the PROGMEM attribute, you can also store arrays too.

This is especially the case when you are dealing with font arrays for a LCD, or the like. Consider the following (trimmed) code for an AVRButterfly LCD font table:

static unsigned int LCD_SegTable[] PROGMEM =
{
    0xEAA8,     // '*'
    0x2A80,     // '+'
    0x4000,     // ','
    0x0A00,     // '-'
    0x0A51,     // '.' Degree sign
    0x4008,     // '/'
}

Because we've added the PROGMEM attribute, the table is now stored firmly in flash memory. The overall savings for a table this size is negligible, but in a practical application the data to be stored may be many times the size shown here - and without the PROGMEM attribute, all that will be copied into RAM.

First thing to notice here is that the table data is of the type unsigned int. This means that our data is two bytes long (for the AVR), and so out prgm_read_byte macro won't suffice for this instance.

Thankfully, another macro exists; pgm_read_word. A word for the AVR is two bytes long - the same size as an int. Because of this fact, we can now make use of it to read out our table data. If we wanted to grab the fifth element of the array, the following extract would complete this purpose:

pgm_read_word(&LCD_SegTable[4])

Note that we are taking the address of the fourth element of LCD_SegTable, which is an address in flash and not RAM due to the table having the PROGMEM attribute.

PROGMEM Pointers

It's hard to remember that there is a layer of abstraction between flash memory access - unlike RAM variables we can't just reference the contents at will without macros to manage the separate memory space - macros such as prgm_read_byte. But when it all really starts confusing is when you have to deal with pointers to PROGMEM strings which are also held in PROGMEM.

Why could this possibly be useful you ask? Well, i'll start as usual with a short example.

char MenuItem1[] = "Menu Item 1";
char MenuItem2[] = "Menu Item 2";
char MenuItem3[] = "Menu Item 3";

char* MenuItemPointers[] = {MenuItem1, MenuItem2, MenuItem3};

Here we have three strings containing menu function names. We also have an array which points to each of these three items. Let's use our pretend function USART_TxString from part I of this tutorial. Say we want to print out the menu item corresponding with a number the user presses, which we'll assume is returned by an imaginary function named USART_GetNum. Our (pseudo)code might look like this:

#include 

char MenuItem1[] = "Menu Item 1";
char MenuItem2[] = "Menu Item 2";
char MenuItem3[] = "Menu Item 3";

char* MenuItemPointers[] = {MenuItem1, MenuItem2, MenuItem3};

void main (void)
{
   while (1) // Eternal Loop
   {
      char EnteredNum = USART_GetNum();

      USART_TxString(MenuItemPointers[EnteredNum]);
   }
}

Those confident with the basics of C will see no problems with this - it's all non-PROGMEM data and so it all can be interacted with in the manner of which we are accustomed. But what happens if the menu item strings are placed in PROGMEM?

#include 
#include 

const char MenuItem1[] PROGMEM = "Menu Item 1";
const char MenuItem2[] PROGMEM = "Menu Item 2";
const char MenuItem3[] PROGMEM = "Menu Item 3";

char* MenuItemPointers[] = {MenuItem1, MenuItem2, MenuItem3};

void main (void)
{
   while (1) // Eternal Loop
   {
      char EnteredNum = USART_GetNum();

      USART_TxString_P(MenuItemPointers[EnteredNum]);
   }
}

Easy! We add the PROGMEM attribute to our strings and then substitute the RAM-aware USART_TxString routine with the PROGMEM-aware one we delt with in part I, USART_TxString_P. I've also added in the "const" modifier as explained in part I, since it's good practice. But what if we want to save a final part of RAM and store our pointer table in PROGMEM also? This is where it gets slightly more complicated.

Let's try to modify our program to put the pointer array into PROGMEM and see if it works.

#include 
#include 

const char MenuItem1[] PROGMEM = "Menu Item 1";
const char MenuItem2[] PROGMEM = "Menu Item 2";
const char MenuItem3[] PROGMEM = "Menu Item 3";

char* MenuItemPointers[] PROGMEM = {MenuItem1, MenuItem2, MenuItem3};

void main (void)
{
   while (1) // Eternal Loop
   {
      char EnteredNum = USART_GetNum();

      USART_TxString_P(MenuItemPointers[EnteredNum]);
   }
}

Hmm, no luck. The reason is simple - although the pointer table contains valid pointers to strings in PROGMEM, now that it is also in PROGMEM we need to read it via the prgm_read_x macros.

First, it's important to know how pointers in GCC are stored.

Pointers in GCC are usually two bytes long - 16 bits. A 16-bit pointer is the smallest sized integer capable of holding the address of any byte within 64kb of memory. Because of this, our "uint8_t*" typed pointer table's elements are all 16-bits long. The data the pointer is addressing is an unsigned character of 8 bits, but the pointer itself is 16 bits wide.

Ok, so now we know our pointer size. How can we read our 16-bit pointer out of PROGMEM? With the pgm_read_word macro of course!

#include 
#include 

const char MenuItem1[] PROGMEM = "Menu Item 1";
const char MenuItem2[] PROGMEM = "Menu Item 2";
const char MenuItem3[] PROGMEM = "Menu Item 3";

char* MenuItemPointers[] PROGMEM = {MenuItem1, MenuItem2, MenuItem3};

void main (void)
{
   while (1) // Eternal Loop
   {
      char EnteredNum = USART_GetNum();

      USART_TxString_P(pgm_read_word(&MenuItemPointers[EnteredNum]));
   }
}

Almost there! GCC will probably give you warnings with code like this, but will most likely generate the correct code. The problem is the typecast; pgm_read_word is returning a word-sized value while USART_TxString_P is expecting (if you look back at the parameters for the function in part I) a "char" pointer. While the sizes of the two data types are the same (remember, the pointer is 16-bits!) to the compiler it looks like we are trying to do the code equivalent of shoving a square peg in a round hole.

To instruct the compiler that we really do want to use that 16-bit return value of pgm_read_word as a pointer to a const char in flash memory, we need to typecast it to a pointer in the form of char*. Our final program will now look like this:

#include 
#include 

const char MenuItem1[] PROGMEM = "Menu Item 1";
const char MenuItem2[] PROGMEM = "Menu Item 2";
const char MenuItem3[] PROGMEM = "Menu Item 3";

char* MenuItemPointers[] PROGMEM = {MenuItem1, MenuItem2, MenuItem3};

void main (void)
{
   while (1) // Eternal Loop
   {
      char EnteredNum = USART_GetNum();

      USART_TxString_P((char*)pgm_read_word(&MenuItemPointers[EnteredNum]));
   }
}

I recommend looking through the AVR-lib-c documentation (included with the installation of WinAVR in the \docs directory) in the AVR/PgmSpace.h header documentation section for information about the other PROGMEM related library functions avaliable. Other functions (such as pgm_read_dword(), which reads four bytes (double that of a word for the AVR architecture) behave in a similar manner to the functions detailed in this tutorial and might be of use for your end-application.

And so ends the second part of this tutorial. Feedback and corrections welcome!

For an updated version of this tutorial in PDF format, please see this page of my website.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

Last Edited: Sat. Feb 4, 2012 - 02:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

This is a very useful technique since it's easy to write code in which the stack bounces off the variables - and the real gotcha is that all those 'printf' statements you put in to try and trace it make it worse; they're eating the ram space. Indeed, my Coupe ECU monitor had to have a complete rewrite to cope with a change of language from English to Italian; the Italian was a couple of dozen characters longer and that was enough to break it.

Very embarrassing... :o

A minor correction: should

strncmp("RAM STRING, PSTR("FLASH STRING"));

really be

strncmp(RAM STRING, PSTR("FLASH STRING"));

(Oh, and 'solely' rather than 'souley')

Neil

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fixed, and fixed. For the former, I was missing the closing quote on the RAM string. Thanks for the comments (and great story :)).

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Still not quite right.

strncmp("RAM STRING", PSTR("FLASH STRING"));

should be

strncmp_P("RAM STRING", PSTR("FLASH STRING"));

Jim

Your message here - reasonable rates.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Darnit! Fixed. Guess i'm not one to teach after all ;).

Is it worth me posting more advanced techniques like PROGMEM arrays of pointers to PROGMEM strings, or even creating a GCC EEMEM tutorial?

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Don't feel too bad, it took me two goes to get the correction right. :D

As for the other tutorials - yes go ahead. There will always be someone here to pick up the odd typo.

Keep up the good work.

Jim

Your message here - reasonable rates.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for explaining this Dean! I've been lazy to dig up all this information myself and now I found this tutorial and it's all clear. Great job!

The Dark Boxes are coming.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
It's hard to remember that there is a layer of abstraction between flash memory access - unlike RAM variables we can't just manipulate the contents at will.

Well, the layer of abstraction is not that we can't manipulate the contents (flash data can't be manipulated at all), but that we can't use C's normal way or referencing them.

But maybe this is again splitting hairs ... but then it would be newbie's hair, which is much more worth than mine, 'cause it will be gone within some years :lol:

Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fixed. Honestly, I should start putting revision numbers on all my written works :D. Thanks for the comment.

- Dean :twisted

POST REV: 1.0

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It may be worth adding a discussion of the pro's and cons of the two techniques that are available for tables of PROGMEM strings.

You can create a table of pointers, and each pointer points to an individual string, as discussed in the tutorial above.

eg.

char String1[] PROGMEM = "String 1";
char String2[] PROGMEM = "String 2";
char String3[] PROGMEM = "String 3";
char String4[] PROGMEM = "String 4";

PGM_P StringTable[NUMBER_OF_STRINGS] PROGMEM = {
String1,
String2,
String3,
String4
};

Alternatively, you can create a unified two-dimensional array, which is entirely contained in PROGMEM. Each entry in the top-level of the array is a fixed-length array of characters.

eg.

char StringTable[NUMBER_OF_STRINGS][FIXED_STRING_LENGTH] PROGMEM = {
{"String 1"},
{"String 2"},
{"String 3"},
{"String 4"}
};

There are pro's and cons for each approach.
The major drawback of the two-dimensional array versus the table-of-pointers-to-strings, is that the "inner" dimension of the array has to be large enough to accommodate the largest individual string, even if none of the other strings need that much space. In the table-of-pointers approach, each individual string only reserves exactly enough memory to accommodate itself.

The major drawback of the table-of-pointers approach is two-fold: First, you have these extra two bytes associated with each and every string, where the pointers physically reside. The two-dimensional array approach doesn't have to literally store these pointers.

Second, you have to dereference PROGMEM twice in order to access the contents of an individual string: Once to extract the start-address of the string you want from the "top-level" table, and a second time to get at the individual character you're interested in within the target string. With a two-dimensional table, the compiler can compute the starting-addresses for the indiviudal strings deterministically, so you only need to dereference PROGMEM once, at the point where you're actually extracting an individual character from the strings.

The trade-off in storage requirement can be decided rather easily: If most of the individual strings that you want to place in the table are within one or two characters in length, your best bet is to use the two-dimensional table. In that case, the wasted space involved in padding the ends of the shorter strings will be smaller than the space that would be wasted by adding an extra two bytes for pointers to each-and-every string in a look-up table.

If you have a string set where there is a wide discrepancy between the longest and shortest strings, and most strings would have more than one or two bytes of NULL padding at the end to match the length of the longest string, then you'd be better off using the table-of-pointers approach.

[edit 1] Inserted code examples [/edit]
[edit 2] Fixed code tags [/edit]

Last Edited: Thu. Apr 27, 2006 - 12:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rev 2 :wink:

All references to prgm_read_word should be pgm_read_word.

Jim

Your message here - reasonable rates.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JimW52 wrote:
Rev 2 :wink:

All references to prgm_read_word should be pgm_read_word.

Jim

Fixed. Sigh :roll:.

LFMorrison: Excellent points. I'm used to dealing with odd-ball sized strings and I hate the idea of padding, so I usually opt for this approach.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

People might not want to fill this up with lots of "thanks" posts, but I want Dean and all those that helped to know that the time and attention that went into this are VERY MUCH APPRECIATED!! Stuff like this is great and exactly what I needed!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cheers! I thought it was high time the tutorials forum was used for its intended purpose!

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In the examples where the pointer array is still in RAM (if anyone cares about them in this context) you should not have the '&' operator, it will not work. So

      USART_TxString(&MenuItemPointers[EnteredNum]); 

should be

      USART_TxString(MenuItemPointers[EnteredNum]);  

and

      USART_TxString_P(&MenuItemPointers[EnteredNum]); 

should be

      USART_TxString_P(MenuItemPointers[EnteredNum]); 

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You sure? USART_TxString is expecting an address for it's pointer. When I tested out the code:

uint8_t Data[5] = "Test";
	USART_TxString(Data[1]);

Produced a warning while:

	uint8_t Data[5] = "Test";
	USART_TxString(&Data[1]);

Did not. What am I missing?

- Dean :twisted:

PS: Thanks for the feedback guys - it may look like i'm grumbing about all the errors, but I'm secretly pleased that there are people reading it in detail ;)

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I agree with Lajon.
@abcminiuser: in your last post you confuse Data (the string) and MenuItemPointers (the pointers to the strings)!

    USART_TxString() and USART_TxString_P() expects an unsigned char*.
    MenuItemPointers[x] is an unsigned char*.
So you need to pass the menu item (as stated by Lajon) without the '&'.
If you use &MenuItemPointers[x] you will pass the address of the pointer to the string; this is wrong.

Many thanks for your tutorial Dean!

Patrik

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Whoops! You're right Patrick and Lars, sorry. I was indeed confusing the string array with the pointer array. Fixed, and thanks.

Should I combine the two parts at the top so that it's easier to read? I could also attach it in PDF form, if required.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's a good idea to put tutorials + notes from posts in one pdf file!

Another errata:

void USART_TxString_P(const char *data)
{
   while (pgm_read_byte(*FlashData) != 0x00)
     USART_Tx(pgm_read_byte(*FlashData++));
}

Corrige:

void USART_TxString_P(const char *data)
{
   while (pgm_read_byte(data) != 0x00)
     USART_Tx(pgm_read_byte(data++));
}

Patrik

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

minor typo shouldnt #include ? be #include

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Great tutorial, very well written.

One question:

Quote:
Because of this fact, we can now make use of it to read out our table data. If we wanted to grab the fourth element of the array, the following extract would complete this purpose:

Code:
pgm_read_word(&LCD_SegTable[4])


Wouldn't this be the fifth element of the array?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now I know why Smokey's book had so much erratta! If I can't even get a couple of pages right, I shudder to think of what a full book would contain!

You are of course correct. I was using conventional numbering rather than the (true!) zero-indexed way of counting. Fixed.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I recently dealt with the same error as rotamat brought up with USART_TxString_P(). pgm_read_byte() takes a pointer, which is pointed out in your explanation (that's how I figured it out). Dereferencing a pointer as a parameter to this function will give you some very funny output. :)

Might be worth updating this to save some people who don't understand pointers well some trouble, I didn't bother reading the unofficial errata in the rest of the thread. I saw a .pdf there so I instantly trusted it... bad habit.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

GlingON,

I just looked it over again and can't spot the error you speak of - was it due to the PDF being out-dated compared to the thread? I just updated the PDF with the latest content so any errors in it that were corrected in my post should be fixed.

Cheers,
- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah, it was the .pdf. I was looking at that since it's nicer to look at than the forums. Sorry, I should have specified more clearly.

That said I'm still seeing the error in the post and in the .pdf. Let me point it out. It is near the end of part 1.

void USART_TxString_P(const char *data)
{
   while (pgm_read_byte(*data) != 0x00)
     USART_Tx(pgm_read_byte(*data++));
}

The body of the function should read:

while (pgm_read_byte(data) != 0x00)
     USART_Tx(pgm_read_byte(data++));
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why did you say that

LCD_puts(PSTR("Program Memory String"));

only works once??

The documentation say's absolutly nothing about that..

and i managed to use PSTR() a millions times with printf_P()

i used it many times like so..

printf_P(PSTR("Hello world"));
printf_P(PSTR("How are you doing"));

litterally about 100 times, with no issues..

what exactly did you mean by you can only use it once?

I was using a basic hitachi interface LCD (4x20)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry, I meant that individual string can only then be used once. If you make the string a global declaration:

uint8_t SomeString PROGMEM = "Some String";

Then you can use it multiple times throughout your program:

// In some function
printf_P(SomeString);

// In some other function
printf_P(SomeString);

When using PSTR, that string is only available in the instance where it is used - it's unavailable to other statements in your program. You'd have to make duplicates of the string:

// In some function
printf_P(PSTR("Some String"));

// In some other function
printf_P(PSTR("Some String"));

And waste flash (unless the appropriate GCC optimiser flags are set).

Sorry for the confusion.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

abcminiuser wrote:
Sorry, I meant that individual string can only then be used once. If you make the string a global declaration:

uint8_t SomeString PROGMEM = "Some String";

Then you can use it multiple times throughout your program:

// In some function
printf_P(SomeString);

// In some other function
printf_P(SomeString);

When using PSTR, that string is only available in the instance where it is used - it's unavailable to other statements in your program. You'd have to make duplicates of the string:

// In some function
printf_P(PSTR("Some String"));

// In some other function
printf_P(PSTR("Some String"));

And waste flash (unless the appropriate GCC optimiser flags are set).

Sorry for the confusion.

- Dean :twisted:

aahh i see i see.. thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think this thread might be of value here

PROGMEM using structs and arrays
http://www.avrfreaks.net/index.p...

/Bingo

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Awesome guide! After reading it a few times, I think I have a much better understanding about accessing program data in AVR.

Based on what Lars was saying, should

pgm_read_word(&LCD_SegTable[4])

be

pgm_read_word(LCD_SegTable[4])

?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sleekas wrote:
Awesome guide! After reading it a few times, I think I have a much better understanding about accessing program data in AVR.

Based on what Lars was saying, should

pgm_read_word(&LCD_SegTable[4])

be

pgm_read_word(LCD_SegTable[4])

?


No.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh I see, it's because LCD_SegTable contains ints (not int pointers), while MenuItemPointers contains char pointers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A fan-blody-tastic tutorial. *cheers*
Three things come to mind.
1. In the pdf there is an intendation issue on page 3
2. You say nothing about the cost in instructions to get a byte/word from progmem contra ram. Im gessing it's a 5:1 ratio or something.
3. Could be nice to know what the pgm_read_byte actualy does.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cheers d99mhl!

1) Fixed the indentation issue - I assume you were referring to the code with the while loop body non-indented?

2) I should probably add in a footnote. Cost varies depending on the data's flash location and AVR architecture, but yes it's about 5:1 per constant. You're trading a small amount of execution time and flash space for a a (usually) large amount of precious RAM space.

3) Again, depends on the data location and AVR chosen. You can check out the code inside the pgmspace.h header, if you're so inclined. Basically it just loads the data's flash address into the high pointer register pairs (I think the Z pointer, r30/31, is required) then executes a series of LPM instructions. Each LPM loads one byte of flash data, and with the post-pointer-increment feature data of several bytes like a word just has several of these instructions in series.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1. Yes, no worries.
2. Agreed, mostly it's an intresting question when doing somthing verry timeing critical.
3. Will do. Remember "Magic is just a technology that we have yet to understand" and I don't like too much magic in my code. ;)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Dean
Yet another great Tut! But this one I can’t thank you enough for. As it is saving me I’ll bet a week of research and experiment time.
Thanks again,
John
You’re the greatest!!

Resistance is futile…… You will be compiled!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The strncmp_P() call is missing the 'n' parameter.
Prototype in pgmspace.h =
extern int strncmp_P(const char *, PGM_P, size_t) __ATTR_PURE__;
Or just use strcmp_P()
Cheers, Paul

I have coded for about 2 dozen architectures, and they all have their place.
Don't get too religious about them (but AVR is excellent!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks AllN, no problem! Glad it helped.

Paul: You're dead right, I didn't catch that on my proof-reading. Fixed.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Very nice tutorial, thanks to you all.

Gentlemen, I have a quick question: As I understand it, all arrays/structs are passed as pointers when used as a function argument. I am using a font set declared as

 const unsigned char PROGMEM font[128][5] 

Why, then, does

tempChar0 = pgm_read_byte(font[cTemp][0]);

not work but

tempChar0 = pgm_read_byte(&font[cTemp][0]);

does? It seems to me that pgm_read_byte() would be getting a pointer to a pointer. Am I misunderstanding something?

Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Because &font[cTemp][0] is the address of the unsigned char and is a pointer while the 1st is not.
Only because the author of pgm_read_byte wrote it to receive a pointer not the unsigned char it self?

Edited:
And it's the best practice!

Resistance is futile…… You will be compiled!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes arrays are passed by reference but you aren't passing an array, you are passing one elemental element of that array. If on the other hand you tried-

tempChar0 = pgm_read_byte(font[cTemp]);

you'd find it worked, since you are passing the 5 element array (it's still an element in the larger array but it is an array itself).
Of course you could only do that to get the first byte of each character so not that usefull..

Edward

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Explaine pls. how can I access fields of structure located in program memory ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include 
struct MyStruct
{
   char x;
   char y;
   int z;
};

struct MyStruct foo PROGMEM = {'a', 'b', 312};

int main(void)
{
    int MyInt = pgm_read_word(&foo.z);
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include  
struct MyStruct 
{ 
   char x; 
   char y; 
   int z;
   void (*Procedure)();
}; 

struct MyStruct foo PROGMEM =
 {'a', 'b', 312, Read_Procedure}; 

int main(void) 
{ 
    int MyInt = pgm_read_word(&foo.z); 
}

If structure is located in RAM I can call read procedure:

int main(void) 
{ 
    int MyInt = pgm_read_word(&foo.z);
    
    foo.Read_Procedure();
}

Tell me please how can I call read procedure if I locate structure in program memory ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Note that this is off the top of my head so there may be some inconsistent syntax; I have done things similar to this before, but I don't have the source code in front of me as I write this.

typedef void (*function_pointer)();

int main(void)
{
   function_pointer Proc = (function_pointer)pgm_read_word(&foo.Procedure);
   Proc();
}

- Luke

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dean, I just wanted to say thanks for writing this tutorial... It just helped me out a lot!!!!

- James

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, Dean, Thanks for the tute.
You say, in two places:

Quote:
pgm_read_byte. This macro takes a PROGMEM pointer as its argument, and returns the string located at that pointer value.

It should say something like:
Quote:
pgm_read_byte. This macro takes a PROGMEM pointer as its argument, and returns one byte located at that pointer value.

Also maybe add pgm_read_dword() beside pgm_read_word(), something like. "Similarly, for 4-byte values, we can use prgm_read_dword()."

-- Alf

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Alf, I fixed up the one incorrect reference to strings I could find, and added in a footnote about looking at the other avaliable functions. I didn't want to derail the paragraph about pgm_read_word with information about pgm_read_dword as the former was discussing reading out pointers from program space (hard enough as it is) so I went for the footnote.

Cheers!
- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Dean,

Is there any reason that this correction was never made to the tutorial:

Quote:

void USART_TxString_P(const char *data)
{
while (pgm_read_byte(*data) != 0x00)
USART_Tx(pgm_read_byte(*data++));
}

The body of the function should read:

Code:
while (pgm_read_byte(data) != 0x00)
USART_Tx(pgm_read_byte(data++));

Very nice tutorial overall though!

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Is there any reason that this correction was never made to the tutorial:

Yes, an extreamly good one - I forgot! It's now fixed, thanks!

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Dean,

I was working on the IAR to GCC Converter when I found your tutorial which has been a lot of help. I'm nearly finished with the flash part, then I just need to tackle the EEPROM stuff. :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First, thanks for the tutorial. It was helpful in understanding how things are supposed to be done.

Second... YUCK! Trying to define any data structure more complicated than a string or an array of simple data types reminds me of Fortran programs trying to implement 'data structures' with shared common areas and such. Does C++ permit operator overloading enough (ie including pointer dereference/etc) that one could use some C++ magic code to make the use of PROGMEM space invisible to the users?

I've got problems similar to everyone else; menus:

typedef const struct menu_item_ {
    unsigned char menu_key;
    menu_action_t menu_action;	/* function or menu pointer */
} menu_item_t;

const menu_item_t foo[] PROGMEM = {
  {'A', nextmenuA},
  {'B', nextmenuB},
  {0, "menu help string"}
};

And then code, for example, to draw a prompt (on a non-progmem implementation) that looks like:

menu_item_t *menu, *lastitem;
    drawmenu:
	lastitem = menu;
	while ((lastitem->menu_key & 0x7F) != 0)
	    lastitem++;  /* Find menu terminator to get type of draw */
	if (lastitem->menu_action != 0) {
	    char *p = (char *) lastitem->menu_action;
	    while (*p)
		printf("%c", *p++);
	} else {    
	    while (menu->menu_key != 0) {
		printf("%c, ", menu->menu_key & 0x7F);
		menu++;
	    }
	}
	printf("\r\n");
	return;				/* Done! */

and the code is looking really ugly as I try to insert pgm_read calls in the appropriate places...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So how come the definition of PSTR defines the string as

((const PROGMEM char *)(s))

instead of using the typedefs for prog_char:

((const prog_char *)(s))

It seems like the latter would allow C++ code to run different methods for progmem strings rather than ram strings (which would be a good thing.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Dean! Your tutorials helped me a lot.

Nicu reSpawn.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Many thanks for this great tutorial, that helped me to optimize my code and understand better how to use flash to store data. I have one question though: is there a size limit in using flash to store static data ?
While I was switching/improving my strings to flash, it then suddenly did strange things (printing a long list of unreadable cars, as if the string wasn't terminated properly...). I had to go back to RAM for some parts of code.
Is there something I'm not considering ? Maybe a mistake somewhere else, but I don't see it right now.

I'm using an atmega128, and the summary is:
Program: 23934 bytes (18.3% Full)
(.text + .data + .bootloader)

Data: 642 bytes (15.7% Full)
(.data + .bss + .noinit)

Once more, thanks to avrfreaks folks !

Vincent.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are two limits:

1) The hard limit of the amount of FLASH memory in the AVR - this is a physical limit
2) The 64KB limit due to GCC using 16-bit pointers - this can be worked around using special "far pointer" functions and macros

Your code is only 23KB long, so you're hitting neither. Does your code have any large RAM arrays which might be stepping on the stack or string if you're copying it into RAM before use?

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I don't copy anything myself to RAM. I just use large structure to store all states/transitions of the state machine driving the AVR. Something to do with mapping and 64k limit ? On the 64K limit, could you explain further the far pointer trick (is it in a tutorial I would have missed ?) ?

Thanks.
Vincent.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The avr-libc pgmspace.h module contains "far" versions of the existing methods - "pgm_read_byte_far()", for example. To use them on a string located over the 64KB barrier, you'd just OR the string's address (typecast as a long) with 0x10000 to create a psudo-pointer.

char StringLocatedOver64KB[] = "Test";

int main (void)
{
	char a = pgm_read_byte_far(0x10000 | (uint16_t)&StringLocatedOver64KB);
}

You'd need to write your own "far" versions of the other library routines which deal with FLASH memory, such as memcmp_P(), using the far accessor routines.

Alternatively, use Carlos' macro to get the far address of a variable in FLASH space instead:

#define GET_FAR_ADDRESS(var)                          \
({                                                    \
    uint_farptr_t tmp;                                \
                                                      \
    __asm__ __volatile__(                             \
                                                      \
            "ldi    %A0, lo8(%1)"           "\n\t"    \
            "ldi    %B0, hi8(%1)"           "\n\t"    \
            "ldi    %C0, hh8(%1)"           "\n\t"    \
            "clr    %D0"                    "\n\t"    \
        :                                             \
            "=d" (tmp)                                \
        :                                             \
            "p"  (&(var))                             \
    );                                                \
    tmp;                                              \
})

int main (void)
{
	char a = pgm_read_byte_far(GET_FAR_ADDRESS(StringLocatedOver64KB);
}

Which has the distinct advantage that it returns the correct address (albeit a long address) for any FLASH data whether it is over the 64KB boundary or not -- coupled with the far accessor routines and you have bulletproof code.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nice tutorial, but I'm still totally screwed.
I read also the avr-libc FAQ http://www.nongnu.org/avr-libc/u... but I'm still not getting how to make an array of structs into flash.

I want to have a struct of strings for a menu.
like
struct {
char z[2][20]
} mystruct

And I want to make an array of this structs for different languages.
mystruct myarray[n_languages]

So that i can access the strings by something like
this example:
array[French].z[0]
or
array[French].z[1]

And now I want to have my 2.5k of strings in flash using Progmem :lol:
How to do that?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Easy, just add the PROGMEM attribute to the structure itself:

struct {
char z[2][20]
} mystruct PROGMEM;

If you want an array of structures, use the PROGMEM on the array instead.

To access the structure elements you can either read out the structs from FLASH into a RAM structure using pgm_read_block (and then use the RAM strucuture as you would normally), or use the pgm_read_* macros on the structure elements individually as you use them.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for your fast answer.
To clarify my understanding:

When using

struct {
char z[2][20]
} mystruct PROGMEM;

mystruct myarray[n_languages];

I will have the structs in Flash and the array in RAM?
Or do I have to use

struct {
char z[2][20]
} mystruct;

mystruct myarray[n_languages] PROGMEM;

?

And how do I fill my array of structs with strings when it resides in ROM?
In the moment I'm filling it during menu_init() function with this code:

menu_init{
strcpy(my_array[French].z[0], "Salut        ");
strcpy(my_array[French].z[1], "Test        ");
strcpy(my_array[English].z[1], "Hello        ");
.
.
.
}

I know I cannot do it this way anymore, because the ROM stuff is constant so I cannot write to it during runtime. But how to do it know in a smart way?

Regards,
Baldrian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rule of thumb: when in doubt, add more PROGMEM. I think you'd end up with a PROGMEM array of structures, since you've got an actual array of structures, rather than an array of pointers to the structures (which *would* result in a SRAM array of pointers to PROGMEM space).

You cannot change the PROGMEM data at runtime (since it is embedded in the FLASH), but you can initialize it via standard C methods:

mystruct myarray[n_languages] PROGMEM = 
{
 {z: "Salut        "}, // Initializer for myarray[0]
 {z: "Test        "}, // Initializer for myarray[1]
 {z: "Hello        "},  // Initializer for myarray[2]
// etc.
};

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks alot man Very Happy I think I'm on the right way now.

The only thing is, that I cannot test out everything, because I have to wait for my hardware for 2 more weeks. Sad

I made the "readout the strings to my lcd buffer" routine like this:

// disp_buf is NOT in Flash. myarray is in flash.
for (i = 0; i < LCD_H; i++)
    strncpy_P(dispbuf.z[i], myarray[curlanguage].z[i], LCD_W);

It gives no warnings, which is good. I will see if it is working like expected.

Again, thanks a lot for your help.
Regards,
Baldrian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
struct {
char z[menupoints][20]
} mystructX; 

mystructX myarrayX[n_languages] PROGMEM =
{
 {z: "Salut        "}, // Initializer for myarray[0]
 {z: "Test        "}, // Initializer for myarray[1]
 {z: "Hello        "},  // Initializer for myarray[2]
// etc.
};

Now I have this. I have different mystructX foe different menu sizes (so different menupoints values)

I'm building my menu on lcd by copying the myarrayX[language] to LCD buffer using strcpy_P.
This works fine, but now I have for every menu one function where this is done.
What I want to do is to have only one function and give the myarrayX as a parameter to the function. I think I have to realize this using a pointer, because the myarrayX are all different in size as I mentioned before.

I think I can give a pinter to myarrayX to the function and give the size (menupoints) also to it.
But I couldn't figure out how to do this. So, if someone has an idea about that, please share it with me.

Regards,
Baldrian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Baldrian,

Just a mad idea but to pack your strings how about using:

"Salut$Hello$Hola"

Then when you display(HELLO_ID, language) where language is 0=French, 1=English, 2=Spanish. You just first point to the right sting using "HELLO_ID" and then skip 'language' number of '$'s along the string to find the start point to display and then continue displaying characters until NUL or another '$' is encountered. Use something other than '$' if there's a chance it ever needs to be displayed.

In the code you present above, otherwise, you are wasting huge amounts of bytes for ' 's

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That means, delete the structs and just make a list of strings which all have a specific name like

char hello_id PROGMEM = "Salut$Hello$Hola" 

And then I will give some pointers (based on which menu items (strings) have to be displayed) to my menu() function.
Like:

 menu(&hello_ID)

Or better give a struct of pointers to all needed strings to the menu() functions.

I will try it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No I was thinking of an array of strings such as:

char * strings[] = {
 "Salut$Hello$Hola",
 "Aurevoir$Bye$Adios"
 etc.
}

And possibly an enum or some defines:

typedef enum {
 HELLO_ID,
 GOODBYE_ID,
 etc.
} STRINGS;

or

#define HELLO_ID 1
#define GOODBYE_ID 2

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would do it a little differently in this case. (there are many ways to accomplish your goal) Since size is also of concern, I would create individual strings for each phrase in each language, then create an array of pointers (for each language) to the appropriate phrases. And finally create an array of pointers to each of the language arrays. Any other menu data is kept in its own array of structures. To select the current language to display a variable is loaded with the address of the desired language array. This pointer can be global, and then accessed by any display routines, or passed as an additional parameter to them.

typedef enum 
{
 HELLO_ID,
 GOODBYE_ID,
 etc.
} string_id_t;


typedef enum 
{
 ENGLISH_ID,
 FRENCH_ID,
 SPANISH_ID,
 etc.
} language_id_t;

// language-specific phrase strings
char hello_en[] PROGMEM = "Hello";
char goodbye_en[] PROGMEM = "Bye";

char hello_fr[] PROGMEM = "Salut";
char goodbye_fr[] PROGMEM = "Aurevoir";

char hello_es[] PROGMEM = "Hola";
char goodbye_es[] PROGMEM = "Adios";

etc.

// language-specific phrase arrays
const char *prompts_en[] PROGMEM = 
{
 hello_en, 
 goodbye_en,
 etc., 
 NULL
};

const char  *prompts_fr[] PROGMEM = 
{
 hello_fr, 
 goodbye_fr, 
 etc., 
 NULL
};

const char *prompts_es[] PROGMEM = 
{
 hello_es, 
 goodbye_es,
 etc., 
 NULL
};

etc.

// language array
const char **languages[] PROGMEM = 
{
 prompts_en,
 prompts_fr,
 prompts_es,
 etc., 
 NULL
};

// global var to hold the language pointer
const char **current_language = 
     pgm_read_word(&languages[ENGLISH_ID]);


// accessing individual strings
const char *phrase =
     pgm_read_word(¤t_language[string_id]);

while(ch = pgm_read_word(phrase++))
{
  // output code here
}

It may seem tedious, and it is to do by hand. However it can easily be managed with a small generator app that takes text files and generates the necessary data tables. This allows you to edit the resource strings for each language easily.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Since size is also of concern, I would create individual strings for each phrase in each language,
I don't get it glitch, your way to do it is very clean but it does take more space than what clawson has above. You need N_LANGAUGES * 2 + (N_LANGAUGES - 1) * N_STRINGS * 2 bytes extra for all the pointer arrays.
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes Cliffs solution is, slightly, more dense than mine, but it requires more processing time, as you have to search the string for the correct entry.

My comment about size is aimed at the 2 dimensional array approach shown earlier. In which case it only takes more space if all the strings are close to the same length. As long as the average length of a string is more than 2 shorter than the longest string, you will be saving space with my method. (you still may if it is not, but it is not guaranteed)

Note that some of that overhead you show is required for Cliffs solution as well. The solution "as proposed" does not work in progmem.

char * strings[] = {
 "Salut$Hello$Hola",
 "Aurevoir$Bye$Adios"
 etc.
}

The above needs to be either stored as a 2 dimensional array (lots of wasted space, if the amalgamated strings differ in length greatly), or as an array of pointers (same as my method). Thus my method only adds one more level of pointers to the mix. The trade off here is a few hundred bytes, vs execution speed.

Working progmem versions of Cliffs Solution:
1) 2-dimensional Array

char strings[][20] PROGMEM = {
 "Salut$Hello$Hola",
 "Aurevoir$Bye$Adios"
 etc.
}

2) Array of pointers:

char hello_str[] PROGMEM = "Salut$Hello$Hola";
char goodbye_str[] PROGMEM = "Aurevoir$Bye$Adios";

const char *strings[] PROGMEM = {
 hello_str,
 goodbye_str,
 etc.
}

**for the given example strings, option 1 is more compact.

[edit]
one could reduce memory footprint of my method further, by storing all the string pointers in a 2d array.

typedef enum
{
 HELLO_ID,
 GOODBYE_ID,
 etc.
} string_id_t;


typedef enum
{
 ENGLISH_ID,
 FRENCH_ID,
 SPANISH_ID,
 etc.
} language_id_t;

// language-specific phrase strings
char hello_en[] PROGMEM = "Hello";
char goodbye_en[] PROGMEM = "Bye";

char hello_fr[] PROGMEM = "Salut";
char goodbye_fr[] PROGMEM = "Aurevoir";

char hello_es[] PROGMEM = "Hola";
char goodbye_es[] PROGMEM = "Adios";

etc.

// language-specific phrase arrays
const char *languages[N_LANGUAGES][N_WORDS] PROGMEM = {
 {
  hello_en,
  goodbye_en,
  etc.
 },{
  hello_fr,
  goodbye_fr,
  etc.
 },{
  hello_es,
  goodbye_es,
  etc.
 }
};

// global var to hold the language pointer
const char **current_language = languages[ENGLISH_ID];

// accessing individual strings
const char *phrase =
     pgm_read_word(¤t_language[string_id]);

while(ch = pgm_read_word(phrase++))
{
  // output code here
} 

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For the move to PROGMEM it was Glitch's (2) I was thinking of in fact.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually it was (2) I was thinking about when I compared the memory requirement (the size for the single pointer array is the reason for the - 1 in the expression).
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

How can allocate the table in a specific address?

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes (use a named section and set its address using --section-start to the linker) but the question is "why?"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Because I have to save a table in specific flash page. Then I will update this page only when the flash want to make a self update (once a month).

my table is defined as:

char myTable[8][256] PROGMEM = {....}

Then how can I change to ensure it is at the address 0x2000?

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try:

char myTable[8][256] __attribute__ ((section (".mysection"))) = {....}

and in the Makefile add:

LDFLAGS += -Wl,--section-start=.mysection=0x2000

(note that is BYTE address 0x2000 which in Atmel code flash addressing is WORD address 0x1000)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your tip. But then I can access this table with the PROGMEM?

Thanks again ;)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The parameters to pgm_read_byte()/pgm_read_word() don't need to be a variable name - it can just as easily be an absolute address. All you are doing here is calling a function that wraps an LPM instruction.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm also struggling with PROGMEM, in particular passing them to other functions... I can't see why the code below shouldn't work, I assume that the pointer is wrong, but I don't know why...

#include  // PROGMEM

const prog_char PROGMEM stringTest[] = "Test working?";

PGM_P PROGMEM stringTestTable[] = { stringTest };

char buffer[17];

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

void loop() {
	strcpy_P(buffer, (char*)pgm_read_word(&(stringTestTable[0])));
	Serial.print(buffer);
	Serial.print(" : ");
	
	strcpy_P(buffer, (char*)pgm_read_word(&(stringTest)));
	Serial.print(buffer);
	Serial.print(" : ");
	
	Print(stringTestTable[0]);
	Serial.print(" : ");
	
	Print(stringTest);
}

void Print(PGM_P str) {
	strcpy_P(buffer, (char*)pgm_read_word(&(str)));
	Serial.println(buffer);
}

It's arduino code, but I'm sure that won't bother you folks. I've tried const char* and _lots_ of variations instead of the PGM_P, can anyone help?

Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was following this tutorial so I could create a COS table, as the COS function from math.h is taking up too much cpu time. But my program fails if I add in this statement:

cosresult = pgm_read_word(&costable[ICR1L]));

I have included pgmspace.h:

#include 

And I created the table in the first place with a little perl:

        open(TABLE, ">table.c");

	print TABLE "static unsigned int costable[] PROGMEM =\n";
	print TABLE "{\n";

        for ($i=0; $i <= 89; $i++) 
	{
		$cosr = (cos((90/256)*$i)*32765);
		printf TABLE "%.0f,\n", $cosr;
	}	
		$cosr = (cos((90/256)*90)*32765);
		printf TABLE "%.0f\n", $cosr;
 		print TABLE "};";

That way I'm taking advantage of the signed 16-bit space.

My variable were declared as so:

signed short cosresult;

static signed int costable[] PROGMEM =
{
32765,
30761,
24994,
16170,
5367,
-6092,
-16806,
-25464,
-31007,
-32757,
-30500,
-24512,
-15525,
-4639,
6814,
17433,
25921,
31237,
32732,
30223,
24017,
14873,
3910,
-7532,
-18052,
-26364,
-31451,
-32691,
-29931,
-23510,
-14213,
-3178,
8247,
18662,
26795,
31650,
32633,
29624,
22991,
13546,
2444,
-8957,
-19262,
-27212,
-31832,
-32558,
-29302,
-22461,
-12873,
-1709,
9663,
19853,
27615,
31998,
32468,
28965,
21920,
12192,
974,
-10364,
-20434,
-28004,
-32149,
-32361,
-28614,
-21367,
-11506,
-238,
11060,
21004,
28379,
32283,
32237,
28248,
20803,
10814,
-498,
-11750,
-21564,
-28740,
-32400,
-32097,
-27868,
-20229,
-10116,
1234,
12434,
22113,
29086,
32502,
31941
};

Anyone spot a reason why this would not work?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was following this tutorial so I could create a COS table, as the COS function from math.h is taking up too much cpu time. But my program fails at runtime, even though it compiles, if I add in this statement:

cosresult = pgm_read_word(&costable[ICR1L]));

I have included pgmspace.h:

#include 

And I created the table in the first place with a little perl:

        open(TABLE, ">table.c");

	print TABLE "static unsigned int costable[] PROGMEM =\n";
	print TABLE "{\n";

        for ($i=0; $i <= 89; $i++) 
	{
		$cosr = (cos((90/256)*$i)*32765);
		printf TABLE "%.0f,\n", $cosr;
	}	
		$cosr = (cos((90/256)*90)*32765);
		printf TABLE "%.0f\n", $cosr;
 		print TABLE "};";

That way I'm taking advantage of the signed 16-bit space.

My variables were declared as so:

signed short cosresult;

static signed short costable[] PROGMEM =
{
32765,
30761,
24994,
16170,
5367,
-6092,
-16806,
-25464,
-31007,
-32757,
-30500,
-24512,
-15525,
-4639,
6814,
17433,
25921,
31237,
32732,
30223,
24017,
14873,
3910,
-7532,
-18052,
-26364,
-31451,
-32691,
-29931,
-23510,
-14213,
-3178,
8247,
18662,
26795,
31650,
32633,
29624,
22991,
13546,
2444,
-8957,
-19262,
-27212,
-31832,
-32558,
-29302,
-22461,
-12873,
-1709,
9663,
19853,
27615,
31998,
32468,
28965,
21920,
12192,
974,
-10364,
-20434,
-28004,
-32149,
-32361,
-28614,
-21367,
-11506,
-238,
11060,
21004,
28379,
32283,
32237,
28248,
20803,
10814,
-498,
-11750,
-21564,
-28740,
-32400,
-32097,
-27868,
-20229,
-10116,
1234,
12434,
22113,
29086,
32502,
31941
};

Anyone spot a reason why this would not work?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
But my program fails

What does that mean? If you run the code in the simulator do you see the expected cos() values being extracted from the array?

By the way I don't think you want 'static' on that array definition.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ya I'm on linux here, and unfortunately my windoze partition is completely borked. We have simulavr, but I have not had the most luck with it previously, maybe it's time I did some research on that end. By fail, I mean that when I upload it into the AVR I get continuous resets like I'm blowing the stack, which is the reason why I moved this array out to progmem in the first place (only 512 bytes of ram on the atmega168 so I could put the entire array in ram, but 256 16-bit values would be the entire RAM and I'm pretty sure that would not work in any instance I can think of ;). Before trying progmem I was using signed 8-bit values in half my eeprom and accessing like this:

myphase = EEPROM_read(ICR1L);

But after reading up on EEPROM corruption, and the very fact I have so much more progmem space, I'd like to be using progmem instead.

So far as static, I think you're right, so I'm going to switch out static for const (which still gives me continuous resets after I changed it out).

I will definitely get after simulavr in the morning and find out if the data is at least pulling out.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is the PROGMEM compatible with C++?
I got the following problem:

#include 
#include 
#include 
#include 
#include 


#include 

static const char FlashString[] PROGMEM = "Test"; 

int main()
{
	
	DDRC=0xFF;
	for(;;);
	return 0;
}

and get the warning that indeed shows what is happening:
"
../blink.c:27: warning: only initialized variables can be placed into program memory area
"
What the...?
This disagrees with all the theory I've ever read about it.
Now with all my C++ code big and fat, I have problems squeezing strings to PROGMEM desite few days of trials..
???
Compilation:

Build started 19.10.2008 at 02:58:58
avr-g++.exe -I"I:\ATMEGA\..\WinAVR-20080610\avr\include\avr"  -mmcu=atmega324p -Wall -DF_CPU=8000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums  -DATMEGA324  -ffunction-sections  -fno-threadsafe-statics  -ansi  -fno-exceptions  
--pedantic -MD -MP -MT blink.o -MF dep/blink.o.d  -c  ../blink.c

../blink.c:27: warning: only initialized variables can be placed into program memory area
avr-g++.exe -mmcu=atmega324p -Wl,-Map=blink324.map blink.o    -lc -lm  -o blink324.elf
avr-objcopy -O ihex -R .eeprom  blink324.elf blink324.hex
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex blink324.elf blink324.eep || exit 0
avr-objdump -h -S blink324.elf > blink324.lss

AVR Memory Usage
----------------
Device: atmega324p

Program:     196 bytes (0.6% Full)
(.text + .data + .bootloader)

Data:          0 bytes (0.0% Full)
(.data + .bss + .noinit)


Build succeeded with 1 Warnings...

Using WinAVR-20080610

----------
OK sorry found the solution
// Following http://www.avrfreaks.net/index.p...
// in pgmspace.h change:

//ORIG # define PSTR(s) (__extension__({static char __c[] PROGMEM = (s); &__c[0];}))
//FIXED # define PSTR(s) (__extension__({static prog_char __c[] = (s); &__c[0];}))

This is silly trick that internal avrfreaks search engine ignores C++ while external google search can point you back inside the forum.

-------------
still there is something stinking:

	USART_puts("[A string from RAM]");
	USART_putstr_progmem(PSTR("{A string from FLASH}"));
	USART_putendl();
#define USART_putstrp(a) USART_putstr_progmem(PSTR(a))
#define USART_putsp(a) USART_puts_progmem(PSTR(a))
void USART_putstr_progmem(const char * data)
{
	while(pgm_read_byte(data))
	{
		USART_putc(pgm_read_byte(data++));
	}
}

void USART_puts_progmem(const char * data)
{
	USART_putstr_progmem(data);
	USART_putendl();
}

void USART_putstr(const char * data)
{
	for(uint16_t i=0; data[i]!=0; i++)
	{
		USART_putc(data[i]);
	}
}

void USART_puts(const char * data)
{
	USART_putstr(data);
	USART_putendl();
}

I will find the solution since we do not negotiate with terrorists. Still, the output from PROGMEM version is garbage. At least the compiler digested it.
----------
OK now the solution for C++:
changing

# define PSTR(s) (__extension__({static char __c[] PROGMEM = (s); &__c[0];}))

to

# define PSTR(s) (__extension__({static prog_char __c[] = (s); &__c[0];}))

masks the warning but also makes the code not work. strcpy_P, nothing will work.
So you have to live with the flood of warnings.
The only version of function arguments turned out to be this:

#define USART_putstrp(a) USART_putstr_progmem(PSTR(a))
#define USART_putsp(a) USART_puts_progmem(PSTR(a))
void USART_putstr_progmem(PGM_P data)
{
	while(pgm_read_byte(data))
	{
		USART_putc(pgm_read_byte(data++));
	}
}

void USART_puts_progmem(PGM_P data)
{
	USART_putstr_progmem(data);
	USART_putendl();
}

----
at the end, the only thing that is really missing, is sprintf_P that would take format string directly from PROGMEM area.
-----
here is the BUG:
http://www.mail-archive.com/gcc-...
it is not fixed even in the most recent
WinAVR-20081124rc3

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would like to verify that printf_P only changes the format argument to reside in program flash. And that if the format has %s, the following string pointer passed will be in RAM, and not program flash. The doxygen comments in the header seem to say only fmt is in program flash for print_P.

I would also like to ask about the var args functions vsnprintf_P/vsprintf_P, i assume since there is only one va_start() in the avr stdarg.h, that it doesnt care that its used with a program memory pointer or ram.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Correct - the only difference between printf and printf_P is that the format string of the second is stored in FLASH rather than ram. In the format string, you can use %s to denote a SRAM string argument, and %S to denote a FLASH string.

The va_args macros deal with parameters pass on the stack, which are pointers to data or (for the basic primatives) the data themselves. The variable arguments are stored in the SRAM stack, but the pointers may point to SRAM or FLASH memory -- the differentiation is how you use the pointer, not the pointer itself.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, i'm new with the avr microcontroller.

I'm using the usart and the string store int the flash memory.

I've made the next program, but it doesn't work good.

#include  
#include 
#include  

#define F_CPU 4000000
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

const char MenuItem1[] PROGMEM = "Menu Item 1";
const char MenuItem2[] PROGMEM = "Menu Item 2";
const char MenuItem3[] PROGMEM = "Menu Item 3";


char* MenuItemPointers[] PROGMEM = {MenuItem1, MenuItem2, MenuItem3};

//Funciones
//Funcion para un byte
void USART_Tx( unsigned char dato ) {

	while (!(UCSR0A & _BV(UDRE0)))
		/* nada */ ;
	UDR0 = dato;
}
//Funcion para mandar cadenas
void USART_TxString_P(const char *data)
	{
   while (pgm_read_byte(data) != 0x00)//Funcion obtiene dato señala *ptr
     USART_Tx(pgm_read_byte(data++));
	}

int main (void)
{	//Variables
	char indice;
	// Turn on the transmission and reception circuitry:
	UCSR0B |= (1 << RXEN0) | (1 << TXEN0);
	// Use 8-bit character sizes:
	//UCSR0C |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1);
	UCSR0C |= (1 << UCSZ00) | (1 << UCSZ01);
	// Load lower 8-bits of the baud rate value into the low byte
	// of the UBRR register:
	UBRR0L = BAUD_PRESCALE;
	// Load upper 8-bits of the baud rate value into the high byte
	// of the UBRR register:
	UBRR0H = (BAUD_PRESCALE >> 8);
	for (;;) // Loop forever
		{
		// Do nothing until data have been recieved and is ready
		// to be read from the UDR register:
		while ((UCSR0A & (1 << RXC0)) == 0) {};
		// Fetch the recieved byte value into the variable
		// called "ByteReceived":
   		indice = UDR0; // Fetch the recieved byte value into the variable "ByteReceived"
		USART_TxString_P((char*)pgm_read_word(&MenuItemPointers[indice])); 
		}
}

When i press '0',i get "ÿ.ÀHÀGÀFÀEÀDÀCÀBÀAÀ@À?À>À=À<À;À:À9À8À7À6À5À4À3À2À1À0ÀMenu Item 1"
When i press '1', i get "ÿ.ÀHÀGÀFÀEÀDÀCÀBÀAÀ@À?À>À=À<À;À:À9À8À7À6À5À4À3À2À1À0ÀMenu Item 1"
When i press '2', i get "2À1À0ÀMenu Item 1"

Can anyone help me?Does anyone know what is wrong?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you press '0' what arrives at the AVR is not 0x00 but the ASCII code for character '0' which is 0x30. So it'll then try to read the 0x30th (48th) entry in your MenuItemPointers array (which doesn't exist). Try:

         indice = UDR0; // Fetch the recieved byte value into the variable "ByteReceived" 
         indice -= '0'; // convert ASCII to binary
      USART_TxString_P((char*)pgm_read_word(&MenuItemPointers[indice]));

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, now it's work fine. I thought that i was sending the number not its code ascii.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's the thing about UART - if you are just using it as a terminal (rather than sending/receiving files with binary content) that everything going out of the AVR probably needs to be converted from binary to ASCII (printf, itoa, etc.) and everything coming into the AVR probably needs to be converted from ASCII into binary (atoi, scanf, etc.)

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There was mentioning of a PDF. Anybody knows where I can find this PDF?
Thanks,
Leon

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I removed it due to it being old and inaccurate after several revisions. Making new PDFs of all my tutorials is on my todo list, which never seems to be getting any shorter.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

I have used the PROGMEM attribute , but it is not working like it should be.

When I read a byte from flash I can see in the disassembler that the routine starts with the right values (right index , right pointer to the flashlocation), but the result is not right.

61: j = pgm_read_byte(EEPROMerrorCheckString[i]);
+00000180: 01F9 MOVW R30,R18 Copy register pair
+00000181: 5CEC SUBI R30,0xCC Subtract immediate
+00000182: 4FFF SBCI R31,0xFF Subtract immediate with carry
+00000183: 81E0 LDD R30,Z+0 Load indirect with displacement
+00000184: E0F0 LDI R31,0x00 Load immediate
+00000185: 91E4 LPM R30,Z Load program memory

I have no idee why the LDD R30,Z+0 and the LDI R31,0x00 are there. Theye are changing the right value store in the Z register cousing the result of the last instruction to be wrong.

Is there somebody who knows why this happens?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just a guess since all pgm_read_xxxx functions need an address:

pgm_read_byte(&EEPROMerrorCheckString[i]); 

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I read a lot about this amazing pdf tutorial, can't wait to read it. Yes I also read jabius's comment.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JimW52 wrote:
Rev 2 :wink:

All references to prgm_read_word should be pgm_read_word.

Jim

yeah you're right bro.. :twisted:

ArcticSoul
Industrial Electronic Engineering, College Student

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PROGMEM is such a code that i used in my dotmatriks project. :D

Thanks a lot for the tutorial Dean :)

ArcticSoul
Industrial Electronic Engineering, College Student

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is not easy to manipulate data in flash so I made some effort to simplify it.

Solution
I created a function readFlash() which can be used for all different kinds of data types and pointers.

Example
int i PROGMEM = 32767; // write data to program memory
int x = readFlash(i); // read data from program memory.

See here
http://www.arduino.cc/cgi-bin/ya...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I created a function readFlash() which can be used for all different kinds of data types and pointers.

But just to be clear - that function overloading will only work in C++, not C. Those of us who program in C will just need to continue to pick the right pgm_read() function.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,
frist of all thanks for the tutorial ;-)
let's see if I get it:

1)
it's the same to declare

const uint16_t prior0[10] PROGMEM = {100, 360, ..., 120};

and

const prog_uint16_t prior0[10] = {100, 360, ..., 120};

2)
I've got some constant arrays (like the one before) in the prog.mem.

const uint16_t prior0[10] PROGMEM = {100,...}
const uint16_t trans0[100] PROGMEM = {500, 456, ... };
const uint16_t obs0[30] PROGMEM = {500, 456, ... };
const uint16_t prior1[10] PROGMEM = {100,...}
...

this way I hope that all this data and the pointer table are in program space.
now how can I pass this arrays to a function? where I have to use the pgm_read()?

if not in prog space it will be something like:

in file dev.h:

void init(uint16_t pr[], uint16_t tr[], uint16_t ob[])
{
 ...
}

in file main.h:

//array declarations
...

void main(void)
{

init(prior0, trans0, obs0);
...
}

this is not the only way to handle this code, but it fits well in my project structure. I've got several of those arrays (12-15) and I have to pass 3 of them (selected by a comand) to some functions...
thanks again,
cheers!
bo.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1) yes, pgmspace.h just has this:

typedef uint16_t PROGMEM prog_uint16_t;

2) pointers whether they are to RAM or code flash are really just 16 bit numbers and could be passed around as such or you can use the universal get out clause which is "void *" ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First of all this is a great tutorial. Thanks.
I am a little bit confused though. I am having a project in which I intend to use a menu to interface the uC.
Should I use progmem or eemem. Which one is better for LCD, menu
and a Keyboard?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVRs by in large don't have much eemem, so unless you needs are small, there is probably not enough room in eemem. If you do fit in eemem, then it is up to you which you use, but I think that most would choose progmem. Reading from eemem takes longer than reading from progmem.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thanks for the clarification.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey guys,

Is it possible to implement PROGMEM for AVR32 ?

Regards,

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks so much for this tutorial. It explains stuff that other authors miss.
May I suggest that you emphasize that PROGMEM assignments MUST be global. It took me quite a while to figure out the warning (which I think should be an error as it breaks all use of the constant)
Thanks
Kirk

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Utmost puzzling javascript:emoticon(':shock:')

So I followed this tutorial to put some strings into Program memory and then read them with pgm_read_byte(). While the string is reads correctly (when optimization is not at -Os) I can't explain what I see in the debugger!

The .lss file shows that the string is put in program memory right after the interrupt vector table, at address 0x7c. When I single step with the disassembler I can see that the LPM instruction via the Z pointer reads from address 0x7c.

HOWEVER when I bring up a memory window or scroll the disassembler to 0x7c there is no string. Instead I find the string at address 0x38, apparently right in the middle of the vector table, and I find that address 0x7c is inhabited by code. When I add a break point at 0x00007b the debugger stops there, so I'm clearly executing from there.
javascript:emoticon(':twisted:')
What is going on? Why does it appear that my string in memory is not where the .lss file says it should be and why am I executing code where a strings should be. What magic is this ?

Gary

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are mixing up byte and word addresses. The simulator/disassembler presents you everything related to the flash with word addresses, the GNU tools use byte addresses. So 0x7c in the .lss file is 0x3e in the simulator/disassembler.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What about trying to store data in the bootloader section using PROGMEM? Is there a way to change PROGMEM's start location. I can recall strings and variables just fine from FLASH at 0x00000, but not from the bootloader section.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

chrislego88 wrote:
What about trying to store data in the bootloader section using PROGMEM? Is there a way to change PROGMEM's start location. I can recall strings and variables just fine from FLASH at 0x00000, but not from the bootloader section.
If you write a bootloader then you move the whole Flash stuff by relocating the .text section. This also moves the PROGMEM section. So what is you actual problem?

Or don't you write a bootloader but only want to place some data in the bootloader section (whyever)? Then simply don't use the PROGMEM section. Use your own section and place it there.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did relocate the .text section to my boot section. The problem that I am having is trying to recall data from the boot section.

For example,

I run that and it complies but I can never get buf to the init string. So I am not sure why memcpy_P cannot recall the data in FLASH.
const char init[] PROGMEM = "Boot Version";
const char flash[] PROGMEM = "FLASH";

PGM_P table[2] PROGMEM = { init,flash };

int main() {
	char buf[32];
        PGM_P p;
	memcpy_P(&p,&(table[0]),sizeof(PGM_P));
	strcpy_P(buf,p);
}

The code complies but I cannot get buf to hold any information. I am not really sure why. I always get the value of p to be 0xFFFF and then it crashes on strcpy_P command.

Last Edited: Fri. Jun 25, 2010 - 09:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't see any problem. So please be more specific about the context. What mcu? Are you aware that PROGMEM does not work beyond 64k (at least not out-of-the-box)?

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a Atxmega128a1 and the boot section starts at 0x020000.
I know that it doesn't work past 64k but my boot section is only 8k, so if I move the .text section to the boot section address I shouldn't have any problems right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

chrislego88 wrote:
I have a Atxmega128a1 and the boot section starts at 0x020000.
I know that it doesn't work past 64k but my boot section is only 8k, so if I move the .text section to the boot section address I shouldn't have any problems right?
So you don't think that 0x020000 is beyond 0x010000 (64k)?

BTW: I am not familiar with the XMegas. Has an Atxmega128 more than 128k Flash? If not, how then can the boot section start at 0x020000?
EDIT: Forget this. Meanwhile I took a look into the data sheet. ;-)

Stefan Ernst

Last Edited: Fri. Jun 25, 2010 - 10:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No it is beyond 64K but the PROGMEM doesn't travel with the .text section and have it start at 0x020000

If it doesn't then how can I store those strings in my boot section and still be able to recall them?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
but the PROGMEM doesn't travel with the .text section and have it start at 0x020000
Why do you think it does not travel? Your code does not work because PROGMEN has traveled beyond 0x010000.

Quote:
how can I store those strings in my boot section and still be able to recall them?
The first thing you can do is to read at least the thread you are posting in. The 64k border was already a topic here.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Search out Carlos Lamas (I may have spelt the wrong?) utilities called morepgmspace.h

EDIT: take a look at this thread:

http://www.avrfreaks.net/index.p...

It may not be the best thread about morepgmspace.h but it includes a post by Carlos which should allow you to easily find his other posts.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Search out Carlos Lamas (I may have spelt the wrong?) utilities called morepgmspace.h

EDIT: take a look at this thread:

http://www.avrfreaks.net/index.p...

It may not be the best thread about morepgmspace.h but it includes a post by Carlos which should allow you to easily find his other posts.

Thanks a lot those seem to work like a charm.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks a lot for the explanation, it has been very usefull for me!!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for explaining this Dean, ENORMOUSLY helpful.

About the eemem question above (in case your still following the thread) - That portion of memory can tolerate 100000 writes as opposed to 10000 in flash, so ultimately flash seems to me best for constant strings or at strings that rarely change.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello, first of all, thanks for the tutorial!
I didn't read the whole thread but did do some quick search in it.
I was wondering if there was an easy way to directly load a resource file into a program memory destination.

for instance, I have the file res.txt with 800 bytes in it. I would like to copy its contents into program memory at address 0x1000, is there any special instructions in avr studio / avr-gcc to do so?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Handi3 wrote:
I have the file res.txt with 800 bytes in it. I would like to copy its contents into program memory at address 0x1000, is there any special instructions in avr studio / avr-gcc to do so?
I've found this bin2hex perl script to be really useful for this.

See: http://www.chami.com/tips/delphi/052098d.html

This script encodes any data file (binary or text) into a header suitable for perl, C/C++ or Pascal. So its great for including images, binary data or plain text.

To get C compatible output invoke the script like this:

perl bin2hex.pl res.txt 1 > res.h

Then you just include the header file into your program.

#include "res.h"

Since I had a number of files to convert I wrapped bin2hex in this bash script:

#!/bin/sh
for file in input/*; do 
    newfile=$(echo $file | sed 's|input/||')
    echo "Converting $file to $array PROGMEM"
    perl bin2hex.pl $file 1 | sed -e "s/bin_data\[\]/$newfile\[\] PROGMEM/" -e "s/\}/, 0\}/" > output/$newfile.h
done

[edit]The text that starts with "perl" is all one line. Only "done" comes after on a line by itself[/edit]

This converts all of the files in the input/ directory into the output/ directory.

It also changes the name of the array to the name of the file (without extension).

Additionally it adds the PROGMEM attribute to each array, and a NULL termination since I want to deal with the data as a string.

As far as locating the string at a specific address in program memory: Are you sure this is necessary?

Once the header is included you can use the progmem access functions to retreive the data by name.

Hope that helps...

johnea

p.s. I attached the bin2hex.pl script for completeness. Although you'll need to rename bin2hex.txt to bin2hex.pl

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

Super Tutorial... but???

Is it possible to use signed char or signed int with the whole PROGMEM buisness???

Regards

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sure, just use the pgm_read_byte() and pgm_read_word() functions, and cast the result to the appropriate type.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Moderator note: @Decoder, I have spit the fault diagnosis of your program off from this thread and placed it in GCC forum as:

Problem using PROGMEM

THIS thread is not for diagnosing every last program that just happens to use PROGMEM but is for discussing the article in the first post.

Moderator

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Allo! As always a great tutorial Dean!

I was wondering if i could use PROGMEN with some EEMEM data. I have a huge array with EEPROM data pointers and it seems pity to overload my RAM with these.
Wil something like this work?

uint8_t  EEMEM NonVolatileString[10] PROGMEN;
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The EEPROM equivalent of "PROGMEM" is "EEMEM". You'd use:

#include 

...

uint8_t  EEMEM NonVolatileString[10];

there's a separate tutorial about the use of EEPROM in GCC in this forum.

(BTW it's PROGMEM not PROGMEN - it's short for PROGram MEMory)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a quick question, if anyone here can help. I have a program where I have a large configuration struct that houses several different data types. Right now, I have two copies of this struct in EEPROM, which correspond to different configurations. I would like store defaults for each configuration in program memory, so a function like pgm_read_block (analogous to the eeprom_read_block I already use) would be very handy. I saw it mentioned earlier in the thread, but can't find it in pgmspace.h included with WinAVR-20100110. Is it an extension that I could find elsewhere, or will I have to write it myself?

Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Use memcpy_P(), which works just like the regular memcpy() function, but uses a source located in FLASH memory space.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Awesome, that works great. Thanks Dean.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This tutorial was written time ago, but I find it still very useful *today*. So thank you very much, Dean :-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Really great tutorial Dean! thanks very much

I thinking about what is the best solution for language strings. Example: I have device with lcd display. The device supports 3 languages. There's about ~50 strings for each language (various length).

My idea is (I don't know if its correct) to create 3 different language strings for each text in progmem, sth like:

const char txt1_en[] PROGMEM = "text 1 in english";
const char txt1_de[] PROGMEM = "text 1 in german";
const char txt1_pl[] PROGMEM = "text 1 in polish (different length)";
// ...

Additionally 1 table with pointers which point to currently using language strings, e.g.

// english default
char * LanguagePnts[] = {txt1_en, txt2_en, txt3_en}; // ... etc.

Now if user change language in run-time, the pointers should change, and point to elements of diffrent language strings in progmem.

// if user change lang to PL:
LanguagePnts[0] = txt1_pl;
LanguagePnts[1] = txt2_pl;
LanguagePnts[2] = txt3_pl;
//etc.

LCD 'write string' function (progmem version) should be language independent, e.g.

LCD_WriteString_P(LanguagePnts[0]); //txt1
LCD_WriteString_P(LanguagePnts[1]); //txt2
LCD_WriteString_P(LanguagePnts[2]); //txt3
//etc.

I don't know if it is efficiently solution, or even correct one. I'm still beginner and I'll be gratefull to everyone for help.

greetings!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I don't know if it is efficiently solution, or even correct one.

Looks pretty good to me - obviously the array of pointers itself has to be in RAM not PROGMEM if you plan to change language at run time. But you could keep the initialisers for each language themselves in PROGMEM and do something like a memcpy_P() to copy an entire set of language pointers into place in one go.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

But you could keep the initialisers for each language themselves in PROGMEM and do something like a memcpy_P() to copy an entire set of language pointers into place in one go.

great advice, I'll try this, thank you!

edit: I saved over 1.5kB ram only on language strings!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I found that this example (from this thread) works for me, whereas the example in the HOWTO does not work for me.

eg.

char String1[] PROGMEM = "String 1";
char String2[] PROGMEM = "String 2";
char String3[] PROGMEM = "String 3";
char String4[] PROGMEM = "String 4";

PGM_P StringTable[NUMBER_OF_STRINGS] PROGMEM = {
String1,
String2,
String3,
String4
};

I am using avr-gcc 4.3.5 to compile a cpp sourcecode (inherited from my Arduino environment). The example in the HOWTO leaves me with mismatching typeconversion errors or section type conflicts when I use "const".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Questions:
1. Can we change the flash contents by changing the content of the variable using PROGMEM?
2. If question 1 is Yes, then correct me if I'm wrong, the flash sector has to be erased, then store data to it. So how does it work?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Please help!
I have a Atmega M1284P. Data changeable by user and it's used to store in EEPROM, but now decide to store in flash. Here is my Flash memory organize:
App Region: 0 - 0xFFFF
Data Region: 0x10000 to 0x1DFFF
Boot Region: 0x1E000 to 0x1FFFF
I provide 2 commands to user READ_Data and WRITE_Data with 3 parameters: U16_t ui16Address, U16_t ui16Len, U8_t * pui8Buffer
The READ_Data function has no problem, I can use pgm_read_byte_far( 0x10000 + ui16Addr) where ui16Addr is from 0 to 0xDFFF (used to be the EEPROM address).
For the WRITE_Data function, should I assign Data Region Address of the Flash to be 0x1000 to a PROMEM variable? How can I do it?
When initialize, the data is used to read to RAM data structure variables. To save RAM memory, so now I benefit to use Flash instead.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Check out morepgmspace.h by carlos llamas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't think we can change the contents of the flash using these two utilities pgmspace.h and morepgmspace.h. We have to use boot_page_erase, boot_page_fill, boot_page_write in

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I don't think we can change the contents of the flash using these two utilities pgmspace.h and morepgmspace.h. We have to use boot_page_erase, boot_page_fill, boot_page_write in


Correct - this thread is about LPM not SPM.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is there my any chance some sort of "test suite" for the pgmspace.h functions and definitions? I'm working on a compatibility shim for non-AVR processors and prospect of testing everything is daunting...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At first thank you all for this good explanation.

I have compilation error:

variable ‘__c’ must be const in order to be put into read-only section by means of ‘__attribute__((progmem))’

I've googled it and the only thing I have found at the moment is that this error is fixed in AVRLibC 1.7.1.
But upgrading from my 1.6.8 to 1.7.1 changed nothing.
How could it be fixed?

P.S.
Please tell me if I should start new thread.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Please tell me if I should start new thread.


You should - this thread is only for making improvements/corrections to the original article. Start a thread in GCC. Say whether you are using C or C++ in it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you Clawson,
SPM doesn't support or disabled in Application Section, it works only in Bootloader Section. So there is NO WAY to change data in the flash when an app is running, user has to jump to BL mode to write data, then jump back to an app section. How do I read the program counter (PC) to save to EEPROM, so I could jump back to the application?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

SPM doesn't support or disabled in Application Section, it works only in Bootloader Section. So there is NO WAY to change data in the flash when an app is running, user has to jump to BL mode to write data, then jump back to an app section. How do I read the program counter (PC) to save to EEPROM, so I could jump back to the application?

It's easy to write code that updates the flash at run time. As you say it just needs to use SPM and it has to be located in the bootloader section which is why has the BOOTLOADER_SECTION macro. But this is not the correct thread to be discussing this.

NOTE:
This thread is ONLY for discussing the original article about PROGMEM so I'm going to lock this thread now. If anyone has anything to add to correct/improve the original article then PM js, plons or clawson and ask one of us to temporarily unlock this thread to add your contribution.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

abcminiuser wrote:

#include 

const char MenuItem1[] PROGMEM = "Menu Item 1";
const char MenuItem2[] PROGMEM = "Menu Item 2";
const char MenuItem3[] PROGMEM = "Menu Item 3";

char* MenuItemPointers[] PROGMEM = {MenuItem1, MenuItem2, MenuItem3};

Right definition of MenuItemPointers is:
const char * const  MenuItemPointers[] PROGMEM = ...

or otherwise:

error: variable 'MenuItemPointers' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
warning: initialization discards 'const' qualifier from pointer target type [enabled by default]
warning: initialization discards 'const' qualifier from pointer target type [enabled by default]
warning: initialization discards 'const' qualifier from pointer target type [enabled by default]

Quote:
"const" modifier
const is a qualifier

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

at first, thanks for the great tutorial, the explanations and all the comments. I've tried to read and understand everything, but...

I am using AVR STUDIO and the ATmega 162. Within a large program, I would like to strip down my SRAM usage and so I have defined a sort of terminal emulation field in the following way, now:

const char TERMSPEC_ANSI[13][7] PROGMEM = {
    {  27,  91, '2', 'J',   0,   0,   0 },
    {  27,  91, 'A',   0,   0,   0,   3 },
    {  27,  91, 'B',   0,   0,   0,   3 },
    {  27,  91, 'C',   0,   0,   0,   3 },
    {  27,  91, 'D',   0,   0,   0,   3 },
    {  27,  91, 'H',   0,   0,   0,   3 },
    {  27,  91, 'H',   0,   0,   0,   0 },
    {   0,   0,   0,   0,   0,   0,   0 },
    {   0,   0,   0,   0,   0,   0,   0 },
    {   0,   0,   0,   0,   0,   0,   0 },
    {   0,   0,   0,   0,   0,   0,   0 },
    {  27,  91, '7', 'm',   0,   0,   0 },
  };

Compiling runs well, but then I try to read out a character at row position y and column position x at run-time, using:

c = pgm_read_byte( TERMSPEC_ANSI[ y ][ x ] );

But I receive an unexpected character for c . When debugging the memory location, I see that:

(1) The FLASH memory is now (different from the SRAM version) organised as WORDs, not BYTEs. So every address in memory holds two bytes.

(2) The start address of my field TERMSPEC_ANSI (shown in the tooltips) is way off the memory location where my field is now located within the FLASH.

Any suggestions? Any help how to address and retrieve the correct values?

Thanks.
Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can you try

c = pgm_read_byte( & TERMSPEC_ANSI[ y ][ x ] ); 

& sign added. Don't ask me why :-)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pgm_read_byte needs an address to an array element, not the array element itself.

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." - Marcus Aurelius               

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

larryvc wrote:
pgm_read_byte needs an address to an array element, not the array element itself.

Thanks, John and Larry. In the end it was kinda obvious ;-)

The two questions in the last part of my recent posting will be concerned elsewhere, right? It rather has to do with the handling and displaying of FLASH locations within AVR STUDIO, not with the PROGMEM attribute itself.

Thanks.
Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think that it would be better if you asked that in a new thread in one of the Studio forums, whichever Studio you are using.

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." - Marcus Aurelius               

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a question: I have many files in my project and I want to make visible a flash based (PROGMEM) const array as a global variable.

in globals.c I have:

const char global_progmem_const [] PROGMEM = "This is a global PROGMEM const string, visible from all modules!";

in globals.h:

extern const char global_progmem_const [] PROGMEM;

Is this the correct approach?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi @ all,

First I want to thanks for this usefull tutorial, although I figured some of it out myself, with already existing code in my project.

I have now hit a problem with the pointertables:
This:

const char string_normal[] PROGMEM = "normal";

const char* table [NUMBER_OF_LANGUAGES][2] =  {
	{settings00,string_normal},
	{settings10,settingsSpeed11}
};

works perfectly fine (although the pointer table is not in flash itself, which isn't the point here)

But when I implement a pointer to the string, which is then inserted in the pointer table:

const char string_normal[] PROGMEM = "normal";

const char* settings01 = string_normal;

const char* table [NUMBER_OF_LANGUAGES][2] =  {
	{settings00,settings01},
	{settings10,settings11}
};

I get an compiling error:

initializer element is not constant

Whats the correct approach here? I tried every combination of const and PROGMEM attributes but always get this error.

For clarification: I could of course use the first mentioned alternative, but i want to create the pointer tables with a preprocessor macro, because there are a lot of them and it would make implementations of new languages easier.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

excellent, I would put in flash at a 0x1f00 address a bytes of value 5 by the compiler and then read it:

# define fas 0x1f00 / / 5
.
.
pgm_read_byte (fas);

how can I tell the compiler (gcc) to put at 0x1f00 a byte value of 5?

thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is the need of the byte value being written in that specific location?

You should use uint8_t fas PROGMEM = 5;

and then just read it using pgm_read_byte (&fas);

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If there really was some reason you needed something at 0x1F00 then you can do:

const uint8_t foo = 5 __attribute__((section(".myvar")));

then pass this to the linker:

-Wl,-section-start=.myvar=0x1F00

and finally:

var = pgm_read_byte((uint8_t *)0x1F00);

But like Alex says, why would you do this? Why not use PROGMEM properly as explained in the original article of this thread? If you do start specifying absolute addresses like 0x1F00 you may conflict with what the linker tries to do automatically - how do you know it wasn't already planning to place code or flash data at that location?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

By the way I like to use const with PROGMEM and I forgot it in my example, I think it is even required in new gcc versions

 const uint8_t fas PROGMEM = 5; 

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wish that the customer can easily find some constants before program firmware with isp

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How about copying them to eeprom, they can read them from there.

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm afraid that the compiler can change the position of my constant every time I change the firmware ..sig!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mmm, eprom? YES , can be a possible solution... i must think...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I saw that the compiler always puts constant from address 0x26 (ATmega8), then fine by me, I did as you told me you and alexan_e
thankss!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I wish that the customer can easily find some constants before program firmware with isp

But ISP doesn't allow subsections of the flash to be changed/replaced?!? I'd go with Alex's idea of keeping config value in EEPROM which the customer *can* program separately.
Quote:
I saw that the compiler always puts constant from address 0x26 (ATmega8), then fine by me,

It's not the compiler that did that, it's the linker. This is because the linker is given a "linker script" that tells it how to lay things out in flash. That file is ..\AVRToolchain\avr\lib\ldscripts\avr4.x which contains:

  /* Internal text space or external memory.  */
  .text   :
  {
    *(.vectors)
    KEEP(*(.vectors))
    /* For data that needs to reside in the lower 64k of progmem.  */
    *(.progmem.gcc*)
    *(.progmem*)
    . = ALIGN(2);

This says that anything in a section called .vectors will be placed at the very start of flash - that includes the reset jump and the interrupt vector table - this is then followed by anything in .progmem* sections which means anything where you use PROGMEM which is similar to __attribute__((section(".progmem")). The reset+vectors on a mega8 are clearly 0x26 bytes long.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lammelm wrote:
I have a question: I have many files in my project and I want to make visible a flash based (PROGMEM) const array as a global variable.

in globals.c I have:

const char global_progmem_const [] PROGMEM = "This is a global PROGMEM const string, visible from all modules!";

in globals.h:

extern const char global_progmem_const [] PROGMEM;

Is this the correct approach?

Can anyone confirm that this code is the correct way to use global progmem vars in multiple source files?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The PROGMEM attribute in the .h file means nothing to gcc, the symbol is just a hex offset from the start of a particular linker section and the code that refers to it still has to use the method appropriate to the hardware location of that section. Some compilers may do that automatically, but not gcc. For easier maintenance you can define both the attribute and the appropriate programmic references in the .h file and include that in all the c files.

/* httpd string storage is in RAM by default. Other storage can be defined here */
#define HTTPD_STRING_TYPE PROGMEM_TYPE
#define PROGMEM_TYPE 1
#define EEPROM_TYPE 2

#if HTTPD_STRING_TYPE==PROGMEM_TYPE
#define HTTPD_STRING_ATTR PROGMEM
/* These will fail if the server strings are above 64K in program flash */
#define httpd_memcpy       memcpy_P
#define httpd_strcpy       strcpy_P
#define httpd_strcmp       strcmp_P
#define httpd_strncmp      strncmp_P
#define httpd_strlen       strlen_P
#define httpd_snprintf     snprintf_P

#elif HTTPD_STRING_TYPE==EEPROM_TYPE
#define HTTPD_STRING_ATTR EEPROM
#define httpd_memcpy       memcpy_E
#define httpd_strcpy       strcpy_E
#define httpd_strcmp       strcmp_E
#define httpd_strncmp      strncmp_E
#define httpd_strlen       strlen_E
#define httpd_snprintf     snprintf_E
#else
#define httpd_memcpy       memcpy
#define httpd_strcpy       strcpy
#define httpd_strcmp       strcmp
#define httpd_strncmp      strncmp
#define httpd_strlen       strlen
#define httpd_snprintf     snprintf
#endif
extern const char httpd_http[];
...

The .c file that allocates the variable would have

const char httpd_http[]     HTTPD_STRING_ATTR = "HTTPfoo1.0 ";
static unsigned short
generate_status(void *sstr)
{
  httpd_memcpy(uip_appdata, httpd_http, sizeof(httpd_http)-1);

and the other references would just need

int foo = httpd_strlen(httpd_http);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Could anyone comment on the efficacy of this solution?

http://electronics4dogs.blogspot.com/2010_12_01_archive.html

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Could anyone comment on the efficacy of this solution?

Surely it'd be better to extend the implementation of the prinln method maybe using the static const modifier as an indicator as to when PSTR() is used.

EDIT: Looked at Print.h in Arduino and first the method is "println" not "prinln" but also note this:


    size_t println(const __FlashStringHelper *);
    size_t println(const String &s);
    size_t println(const char[]);

I don't know what "__FlashStringHelper" is but it sure looks like an attempt here to overload the println() method so it can handle flash strings anyway.

EDIT2: OK I do know what _FlashStringHelper is now:

WString.h:#define F(string_literal) (reinterpret_cast<__FlashStringHelper *>(PSTR(string_literal)))

So I would suggest that:

Serial.printfln(F("hello"));

probably already does what you are trying to achieve.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hello to all
i have problem with atmel studio 6.1
when i compile my program this error is shown:
"unknown type name 'prog_char'"
can you help me
i rushed
please .....
thnks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In AS6.1, PROGMEM is superseded by _flash, which is much easier to use.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just for the record this is what the user manual says about prog_char:

http://www.nongnu.org/avr-libc/u...

Quote:
prog_char

Note:
DEPRECATED

This typedef is now deprecated because the usage of the __progmem__ attribute on a type is not supported in GCC. However, the use of the __progmem__ attribute on a variable declaration is supported, and this is now the recommended usage.

The typedef is only visible if the macro __PROG_TYPES_COMPAT__ has been defined before including (either by a #define directive, or by a -D compiler option.)

Type of a "char" object located in flash ROM.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It means that I can use _flash instead of PROGMEM?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wowwwwwwww
thanks koshchi
merc

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now If I want to edit pgmspace header What should I do?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why would you want to do that?

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

i want edit the pgmspace.h header for AS6.1
and use this header file in may projects

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What do you think about this?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

What do you think about this?

No one should EVER edit any library/system header file that belongs to the compiler or the C library. Just consider what happens next week when you upgrade the compiler and everything, including your edits, are replaced.

The fix for your "problem" here is to replace prog_char as you have been told. It was never a valid definition anyway as it applied an attribute (progmem) to a typedef. The correct way is exactly what that text from the user manual I quoted above says. Instead of:

#include 

prog_char foo = 'A';

use:

#include 

const char PROGMEM foo = 'A';

though this is now 2014 and a much better idea is:

const __flash foo = 'A';

With a __flash variable there's no longer a need to use pgm_read_byte() (or even include pgmspace.h). You can just say:

PORTB = foo;

and the compiler knows that foo was defined in __flash so will do the equivalent of a pgm_read_byte(&foo) anyway when it reads the 'A' character.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hi clawson

in this line in your message :
const __flash foo = 'A';
This is the correct code:
const char __flash foo = 'A';
yes?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also, thank you for your answer clawson....

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why must variables be defined as a constant while is not it in pgmspace.h header?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Because the implementation of __flash requires it.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thanks koshchi and other friends

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Why must variables be defined as a constant while is not it in pgmspace.h header?


It's preparing you for the day when you program ARM micros ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello all,

what I haven't found yet is an example for this situation:

typedef struct {
  uint8_t m_scale;  uint32_t m_min;  uint32_t m_max;
} U32MMS_t;

U32MMS_t PgmStruct PROGMEM = { .m_scale = 2, .m_min = 111, .m_max = 2000000 };

So far, I get a U32MMS_t which lies in PROGMEM.

Now, in the code I want to quickly read these value into ram:

void pgmToRam(void* aTgt, void* aSrc, size_t aLen)
{
   while(aLen)
    {
        *((uint8_t*) aTgt) = pgm_read_byte(aSrc);
        ++aTgt;
        ++aSrc;
        --aLen;        
    }
}

...

U32MM_t ramStruct;

pgmToRam(&ramStruct, &PgmStruct, sizeof(U32MM_t));

For this example, please forget about casts and stylistic questions. What causes me headaches is the question, whether I may do this or not. In my actual code this works. But perhaps I'm just lucky.

So my question is:

- can it happen, that the members within this (or any other) struct have different aligments if they reside in different storage-types??

I mean: is it possible, that intermixed datatypes in structs (8 bit, function(i.e. callback)-pointers, 32 bit values and so on) cause different memory-alignemnts within a struct of the same type, depending on the location where they originate from?

AND: Does this differ between different atmega-processors?

Or can I carelessly copy from

PROGMEM to RAM ||
EEPROM to RAM (with an extra EEPROM-version of course)

that way?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi abcminiuser!
I'm searching for this and found yours, but your ways is complex so I tried (ATmega128):

#define F_CPU 16000000UL
#include 
#include 
#include 
static int uart_putchar(char c, FILE *stream)
{
loop_until_bit_is_set(UCSR0A, 5);
UDR0 = c;
return 0;
}
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,_FDEV_SETUP_WRITE);

const char messengers[][13] PROGMEM={
	"abcdefgh",
	"yeah",
	"Hello world!"
};
int main(void)
{
PORTE=0x03;
DDRE=0x02;
// USART0 Baud Rate: 115200 (Double Speed Mode)
UCSR0A=0x02;
UCSR0B=0x08;
UCSR0C=0x06;
UBRR0H=0x00;
UBRR0L=0x10;
stdout = &mystdout;	
puts_P(messengers[2]);
while(1);
}

13 is the lenght of longest string +1.
This code is workable, It's cost more program space but a easiest.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dear Dean,

 

I would say it is a fantastic tutorial for the beginners. You are doing a good job. Helps me a lot to understand the basic concepts. I wish you to create much more tutorials on AVR GCC macros with relative examples as in this tutorial. You can also try to go little advance once you establish the basic concepts.

 

AVR Beginner.

 

 

Thanks,

Srinivasa Varadhan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wouldn't hold your breath. Dean has a real job working on things a bit more advanced than the humble AVR, so i don't think you see much from him.
Note this tutorial is *only* ten years old - there's the _flash attribute now. Time for some reading methinks.

Last Edited: Sat. Feb 20, 2016 - 08:42 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dear Kartman,

 

I meant to thank Dean as I am beginner. It gives me a bit confidence to work with EEPROM. No intention of advising Dean on his work and not professional to do so also. Sorry if you took it other way.

Thanks,

Srinivasa Varadhan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What has progmem got to do with eprom? I think you took me the wrong way - I was merely pointing out that Dean has other things on his mind these days, so don't be expecting anything new from him.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hello,

I'm new in this, how can I pass a value parameter when I call a function using Flash obviously, I'm doing this:

 

void Play_Menu(){

     menu_texto( Menu_Esp );
}

 

I have to send Menu_Esp to this function:

void menu_texto( const char* vect )

 

I have problems when I send the Menu_Esp parameter

 

Menu_Esp is this:

const char* Menu_Esp[] PROGMEM = { M_E_op0, M_E_op1, M_E_op2, M_E_op3, M_E_op4, M_E_op5 };

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Use __flash unless you are forced to use some old out of date compiler then just treat "__flash" as any variable modifier:

const __flash char * something[] = { ... };

void menu_texto(const __flash char * vec) {
    etc.
}

But if you are sticking with PROGMEM then to be honest it doesn't really matter what you pass the pointers around as. In fact you might as well use "void *". The only time it's important what a pointer is pointing to is when you dereference it. So:

uint8_t do_something1(void * aPointer) {
    return *(uint8_t *)aPointer; // treat it as a RAM pointer
}
uint8_t do_something2(void * aPointer) {
    return pgm_read_byte(aPointer); // treat it as a flash/PROGMEM pointer
}

That's effectively the same code and only differs in how aPointer is eventually used.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"The only time it's important what a pointer is pointing to is when you dereference it."

Probably talking out of my arse here, but surely it's important when you increment, decrement etc. What happens if you try to increment a pointer to void? I'll have to try it.

 

 

Quebracho seems to be the hardest wood.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But you aren't going to be doing those operations on a pointer to data such as was being shown here. (but, yeah, you'll have too cast it from void * if you want to do arithmetic - but again it doesn't really matter what it points to as long as it's something of the same width).

 

When I said "it doesn't really matter what it points to" what I meant was RAM, flash, EEPROM or whatever. A "char *" is good for accessing 8bits whether it's in any of those memory spaces.

Last Edited: Thu. Dec 1, 2016 - 05:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How does one create a tripple array in program memory?

I have tried

const char MyArray1[][5] PROGMEM = {"Test",Test"};

const char MyArray2[][5] PROGMEM = {"Test",Test"};

PGM_P MainArray[] = {MyArray1,MyArray2};

 

I have tried many ways to fix the last line of code but keep getting either char pointer, or initialize(before char, but that is "PGM_P const" issues!

 

I attempted to do

const char MyArray[2][2][5] PROGMEM = {{"Test",Test"},{"Test",Test"}};

but I now am unable to draw the strings out using (PGM_P)pgm_read_word(&(MyArray[1][1]))

I believe this to be because the array does not have PGM_P in front of it, but when I do put it there, it crashes and i again cannot find a solution!?

thanks all!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Theone6000 wrote:
I attempted to do const char MyArray[2][2][5] PROGMEM = {{"Test",Test"},{"Test",Test"}}; but I now am unable to draw the strings out using (PGM_P)pgm_read_word(&(MyArray[1][1]))
Looks like you have a misconception about that three dimensional char array. There is no indirection involved. No need to read an address from flash first which then points to the text. &(MyArray[1][1]) is already the address of the text.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

so the code line:-

const char MyArray[2][2][5] PROGMEM = {{"Test",Test"},{"Test",Test"}};

is actually in program memory?,

what I have issue to with this way and not the other is in the deceleration of the array, if I declare an array such "const char MyArray[10][12][15] PROGMEM" does the chip set aside 10*12*15 bytes of program memory for the chars?

if so, unless the bytes are used they are wasted, correct?

thanks