[TUT] [C] GCC and the PROGMEM Attribute

Go To Last Post
204 posts / 0 new

Pages

  • 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

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

Theone6000 wrote:
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?
Yes.

Stefan Ernst

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

Theone6000 wrote:
if so, unless the bytes are used they are wasted, correct?
Which is the exact reason people don't tend to use fixed width fields in such tables. The usual scheme is to put "char *" pointers in one table that then point to a collection of separately define char strings of varying length elsewhere and it's when you do that the need for double-indirection comes into play where you pgm_read_word() first to get the pointer (char *) to the string you actually want and then you pgm_read_byte() to get to the characters within what the pointer holds the address of.

 

__flash makes like easier (if not C++) and just today someone made a post that uses something called FSTR() that makes the definition of such string tables easier still (so search "FSTR" ;-)

 

EDIT: This in fact....

 

http://www.avrfreaks.net/comment...

 

(which deserves to be caught in this tutorial thread as it's fairly important!)

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

SO how does one build a table of tables? and "flash" is ram equivalent of a computer? and program memory is the equivalent of a hard-drive, I had a problem with "ram", and am attempting to push my strings into program space(had-drive) (yes/no/maybe)?

thanks

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

Theone6000 wrote:
and "flash" is ram equivalent of a computer? and program memory is the equivalent of a hard-drive,

'FLASH" is the equivalent of the hard drive, RAM is RAM.  Your AVR code is executed from FLASH the way a PC loads its program from a hard drive TO RAM, and executes it from there.

 

JIm

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

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

@Theone6000: Any attempt to map memory types of an AVR to memory types common on many PCs will fail in one or several aspects.

E.g Jims assertion re flash being the equivalent of a hard disk is true in the sense that both are non-volatile memories, but fails since code is executed directly from flash on an AVR but definitively not direct from a hard disk on a PC. From a code execution perspective the flash is closest to both RAM and a hard disk on a PC. On the other hand you store data non- volatively on a PC hard disk but while you store such in flash on an AVR also the more common cases are certainly EEPROM and possibly some kind of external non-volatile memory like a memory card.

Whatever you claim is equal between an AVR and a PC memory-wise a good case for the opposite can be made.

You just have to face the fact that you're dealing with a different beast here. With a different basic architecture. You'll get used to it soon (-:

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Pages