[TUT] [C] GCC and the PROGMEM Attribute

Go To Last Post
204 posts / 0 new

Pages

  • 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               

Pages