[TUT] [C] GCC and the PROGMEM Attribute

Go To Last Post
220 posts / 0 new

Pages

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

I made this table illustrating the complexity and lack of "one-to-one" mapping between PC and AVR memory types. I'm sure it does not cover everything, and might be wrong in details. See it as an illustration rather than an absolute truth..

 

 

Memory type       Volatile  Speed[1] PC                                     AVR
                            R   W
---------------------------------------------------------------------------------------------------------------------
RAM               Yes       F   F    * Program code when executing
                                     * Data for programs while running      * Data for programs while running

Hard disk         No        S   S    * Permanent program code storage       * n/a [2]
                                       (must be loaded into RAM to be 
                                        executed)
                                     * Permanent data storage
                                       (e.g. store data between executions
                                       i.e. when program is not running)

Flash             No        F   S    * n/a [4]                             * Program code permanent storage, and
                                                                             is executed from here

EEPROM            No        F   S    * n/a [5]                              * Low volume read and write data
                                                                              (e.g. store data between executions
                                                                              i.e. when AVR is not powered)

External          No        S   S    * As hard disk above                   * High(er) volume read and write data
Non-volatile                                                                  (e.g. store data between executions
Memory [3]                 			                              i.e. when AVR is not powered)
                                                                              Possibly for needing moveable/detachable
                                                                              memory, or just for high volumes

----------------------------------------------------------------------------------------------------------------------
[1] R - Read, W - Write, F - Fast, S - Slow
[2] While technically possible attaching a "classical" hard disk (SATA/ESATA)
    to an AVR is complex and in most all cases a memory card is more fitting
    and definitively easier to do
[3] Memory cards, USB sticks/drives
[4] I'm avoiding a somewhat complex discussion on PC BIOS storage by simply ignoring it and
    saying n/a while it might not be true :-)
[5] It's possible (or even likely?) that BIOS Setup data is stored in EEPROM on a PC

 

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Fri. May 5, 2017 - 06:36 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I managed to solve my problem gracefuly!:_

 

const char PROGMEM ErrorArray1[] = "testa";
const char PROGMEM ErrorArray2[] = "testaaa";
const char PROGMEM ErrorArray3[] = "test";
const char PROGMEM ErrorArray4[] = "test";
const char PROGMEM ErrorArray5[] = "testdddddd";
const char PROGMEM ErrorArray6[] = "test";
const char PROGMEM ErrorArray7[] = "test";
PGM_P const ErrorArray8[][3] PROGMEM = {{ErrorArray1,ErrorArray2},{ErrorArray3,ErrorArray4},{ErrorArray5,ErrorArray6,ErrorArray7}};

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

AND a 3 multi array of pointers and the 4th array being char array

    const char PROGMEM ErrorArray1[] = "testa";
    const char PROGMEM ErrorArray2[] = "testaaa";
    const char PROGMEM ErrorArray3[] = "test";
    const char PROGMEM ErrorArray4[] = "test";
    const char PROGMEM ErrorArray5[] = "testdddddd";
    const char PROGMEM ErrorArray6[] = "test";
    const char PROGMEM ErrorArray7[] = "test";
    PGM_P const ErrorArray8[][2][3] PROGMEM = {{{ErrorArray1,ErrorArray2},{ErrorArray3,ErrorArray4}},{{ErrorArray5,ErrorArray6,ErrorArray7}}};

Last Edited: Sat. May 6, 2017 - 03:40 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I have no shame in posting on old threads when they're tutorials.

 

Thanks for this; cleared up a lot for me. Clear, concise and very helpful. 

 

I have an application I threw an ATMega2560 in because I needed high io count and a good chunk of memory for data acquisition of many sensors, and to write that data to an SDHC card. I didn't have a lot of time to spend researching MCU's but I'm hoping to go with a smaller chip once I'm done writing the program. This goes a long way in helping me reduce my memory footprint along the way. PTSD from university CS classes 10 years ago still has me often dragging my feet any time I need to do memory allocation (*cough* segfault *cough*) so all the little things I can do to save space first are very welcome ;)

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

Just to remind you that if you use C not C++ then __flash is a far better option than PROGMEM these days. If it's C++ you don't get a choice. 

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

Yes! I learned this in the few hours after submitting my comment. Took a while to figure out what I needed to do but I landed on still using _p functions for in-line string literals. Looking through the rest now to see what else could be set as __flash. I tend to be a #define guy, but I’m looking for places where I’m potentially using those defines repeatedly and moving them instead to constants in flash. My degree is CE and as such I have good CS knowledge and embedded really is my favorite but I’ve only done this work sporadically the last 8 years since college. All that to say I’m geeking out a bit on increasing my code efficiency with these methods. 
 

A couple questions I still have:

 

how would I know when I need to use __memx vs __flash? I understand that memx is for data that crosses the 64 Kb boundary, but am I going to know when that happens?

 

I was also running into trouble trying to debug a variable, I thought the issue was due to it being in flash, but ended up being because it only lived in a register. So I guess my question is, if it’s static and constant, shouldn’t the debugger still know it’s location in flash? Or is the answer “maybe yes maybe no, it depends”. Which I think is the case…when I scattered my variable around a bit more I was able to break and see it in my watch list. 

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

Welcome!

jasonkelly214 wrote:
... ATMega2560 ... but I'm hoping to go with a smaller chip once I'm done writing the program.
The follow-on to megaAVR is AVR Dx (AVRxt, up to 128 KB program space, up to 16 KB data space, up to 64 pins); AVRxm also has an EBI (16 MB data space, up to 100 pins)

 

Migration from the megaAVR® to AVR® Dx Microcontroller Families

Device Tables | AVR® Instruction Set Manual

 

"Dare to be naïve." - Buckminster Fuller

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

gchapman wrote:
how would I know when I need to use __memx vs __flash? I understand that memx is for data that crosses the 64 Kb boundary, but am I going to know when that happens?
You need to police the boundaries thing yourself so try to avoid creating anything that straddles a 64K boundary. __memx is most useful when you want to. for example, create a common printing routine that doesn't care if text is in RAM or flash - you can just pass a __memx pointer to it then use a __builtin to determine where it's located.

jasonkelly214 wrote:
I was also running into trouble trying to debug a variable, I thought the issue was due to it being in flash, but ended up being because it only lived in a register.
See my article (also in Tutorial) about the use of "volatile" in GCC. That is the quick/easy way to make something easily "watchable" by debuggers (though it impacts the efficiency of the code). There should not be a problem with const __flash data though - the debugger should always know the location of that (and if not read it to a volatile and look at that!).

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

clawson wrote:
If it's C++ you don't get a choice. 
can mix

How to mix C and C++, C++ FAQ

 

"Dare to be naïve." - Buckminster Fuller

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

I made a flash card program from a long list of English two-three word expressions.  I used this format:
 


const char PROGMEM englishStr0000[] = "a babe in arms";     // extends up to 5850 expression strings

const char * const englishStrItems[ 1 ] PROGMEM = {englishStr0000};  // extends up to 5850 array elements                                                                                         

String    proverbString;      // construct an object of class String in order to word-wrap on OLED screen

uint16_t stringIndex = random(5850);                                  
proverbString = String(FPSTR( englishStrItems[stringIndex]));

The final code for 5850 expression strings was about 100 K bytes.  It loads and runs in a 32-bit CPU through Arduino. 

This is a good tutorial.

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

It was my impression that the __flash constants don't work with string functions such as strcpy and strcat (or, that there aren't versions of those that work with __flash). Is that still true?

 

I often construct strings for serial IO from constant strings and ASCIIfied variables.

 

Thanks

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

ka7ehk wrote:
It was my impression that the __flash constants don't work with string functions such as strcpy and strcat

avr-libc manual says

There are many string functions available that work with strings located in Program Space. All of these special string functions have a suffix of _P in the function name, and are declared in the <avr/pgmspace.h> header file.

 

In Imagecraft C, the string functions that use strings in flash start with c, such as cstrcpy, or cstrcat...   so may vary with compiler used.

 

FF = PI > S.E.T

 

Last Edited: Thu. Oct 28, 2021 - 05:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Then the question is: (in gcc) does strcat_P work properly with __flash values?

 

Thanks

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

Jim, exactly that. You just use _P() functions with __flash data in exactly the same way you would have done with PROGMEM. 

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

Ahh, big help! In previous project, I stuck with PROGMEM attribute not knowing that __flash would work.

 

Appreciate that.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

clawson wrote:

Jim, exactly that. You just use _P() functions with __flash data in exactly the same way you would have done with PROGMEM. 

 

just for my curiosity......

if they do exactly the same and you still need the <avr/progmem.h> to get to the special _P functions why use __Flash instead of PROGMEM al together?

 

or is this a thing with portability of the code to other platforms that in the future you no longer need the visible <avr/progmem.h> but after defining 'variables' to be '__flash char' during compile time the right function is called

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

If you are asking that you have completely missed the point of __flash. You no longer need to use any of the pgm_read_XXX() nonsense when you use it. You don't even need pgmspace.h unless you choose to use something from it.

 

You literally can just write:

const __flash char txt[] = "Hello";

... 

UDR = txt[1];

See what the PROGMEM / pgm_read_byte() version of that looks like! 

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

clawson wrote:
If you are asking that you have completely missed the point of __flash..........

 

well that is then why I never understood the advantages of using __flash.......

Till now I always was under the impression that __flash only replaced the "progmem"  part but that in the end you still needed to use the special _P functions to have access....

That will make life a lot easier indeed.

 

so if I have a:

const __flash char txt1[] = "Hello";

and a

volatile char txt2[10] = "world/r/n/0";

 

can I then acces them both using the same function like

 

void sendit(* char tosend)

{

 UDR = tosend[1];

}

 

Or do you on that end still do need the separation between the two as the pointers are different?

 

note I always have been strugling with pointers and I have not been coding for a while now as my time has been consumed by other hobbies.

 

 

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

meslomp wrote:
but that in the end you still needed to use the special _P functions to have access....
No again. The thing you no longer need is pgm_read_xxx(). If you are going to use something like printf() hten you DO still need to use printf_P(). Here is an example:

const __flash char fmt[] = "x = %d";

int x = 123;

int main(void) {
    UDR = fmt[2]; // no pgm_read_byte() neeeded
    printf_P(fmt, x); // but you do still need to use _P
    
    // by the way you could do this but it's pointless
    UDR = pgm_read_byte(&fmt[2]);
}

meslomp wrote:
can I then acces them both using the same function like
No that is when you would use __memx which is a pointer that can point to both RAm and flash. At the point of dereference the compiler needs to know whether to generate RAM accessing LD code or flash accessing LPM code. It does that in __memx using the state of bit 23.

Pages