GCC, flash and arrays. Do I have this right?

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

I'm trying to get up to speed on how to properly put data in flash.  Down the road I'm looking at putting a command table in there, but for now I just want to make sure I have the basics (i.e. arrays of strings) down right.

 

So lets say I have the following that I've attempted to compile with GCC in Atmel Studio:

 

#include <avr/io.h>
#include <avr/pgmspace.h>

// Block 1 - works
const char MenuItem1[] PROGMEM = "Menu Item 1";
const char MenuItem2[] PROGMEM = "Menu Item 2";
const char MenuItem3[] PROGMEM = "Menu Item 3";
const char * const MenuItemPointers1[] PROGMEM = {MenuItem1, MenuItem2, MenuItem3};

// Block 2 - doesn't work
const __flash char MenuItem4[] = "Menu Item 4";
const __flash char MenuItem5[] = "Menu Item 5";
const __flash char MenuItem6[] = "Menu Item 6";
const char * const MenuItemPointers2[] PROGMEM = {MenuItem4, MenuItem5, MenuItem6};

// Block 3 - works
const __flash char MenuItem7[] = "Menu Item 7";
const __flash char MenuItem8[] = "Menu Item 8";
const __flash char MenuItem9[] = "Menu Item 9";
const __flash char * const MenuItemPointers3[] PROGMEM = {MenuItem7, MenuItem8, MenuItem9};

// Block 4 - Array in RAM, works
const __flash char MenuItem10[] = "Menu Item 10";
const __flash char MenuItem11[] = "Menu Item 11";
const __flash char MenuItem12[] = "Menu Item 12";
const __flash char * MenuItemPointers4[] = {MenuItem10, MenuItem11, MenuItem12};

// Block 5 - works
const __flash char MenuItem13[] = "Menu Item 13";
const __flash char MenuItem14[] = "Menu Item 14";
const __flash char MenuItem15[] = "Menu Item 15";
const __flash char * const __flash MenuItemPointers5[] = {MenuItem13, MenuItem14, MenuItem15};

// Block 6 - works
const __flash char * const __flash MenuItemPointers6[] =
{
	(const __flash char[]){"MenuItem16"},
	(const __flash char[]){"MenuItem17"},
	(const __flash char[]){"MenuItem18"}
};

 

(Excuse the fact that I call the arrays MenuItemPoitersn[].  There's going to be pointers in there later...)

 

As I understand it:

 

Block 1 is the old way of doing things.  My strings are character arrays in flash courtesy of the PROGMEM modifier, and they have to be const as a result.

 

Block 2 doesn't work, but block 3 does.  Apparently I have to declare that MenuItemPoitersn[] is an array of character pointers in flash and not simply an array of character pointers as in Block 1?  And yes, I realise that mixing __flash and PROGMEM is an unholy union that needs to be killed.  With fire.

 

Block 4:  My strings are in flash, but my array is not?  (Currently my main loop just has a while(1), so no RAM is used.)

 

Block 5 is the way I should be doing it.  An array in flash of strings also in flash.

 

Block 6 is another way to do block 5.

 

Did I get all that correct?

 

Do I have to keep the "cost" keyword when using __flash?  I was just browsing around and came across some code where they didn't do that, though maybe that was for a different compiler.  Also, I've seen some code where they use uint8_t instead of char.  Is there a preferred/"better" choice between the two?

 

Also also, *is* there any difference between the following?

 

const char Name1[] PROGMEM = "Bob";
const __flash char Name2[] = "Tim";

 

This topic has a solution.
Last Edited: Thu. Jun 15, 2017 - 02:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

> Do I have to keep the "const" keyword when using __flash?

 

If you use avr-gcc, then yes.
 

avrfreaks does not support Opera. Profile inactive.

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

> Also also, *is* there any difference between the following?

 

Yes.  Just access the objects with ordinary C (no inline asm or pgm_read_xxx) and inspect the generated code.

 

__flash is a qualifier that sticks to respective pointer types, just as "const" or "volatile".  And it located data in flash.

 

Attribute progmem is a attribute that works very much like the section attribute and only controls where data is located.

 

 

avrfreaks does not support Opera. Profile inactive.

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

Lucien wrote:
Also, I've seen some code where they use uint8_t instead of char. Is there a preferred/"better" choice between the two?
You should use the data type that is appropriate for the data:

const __flash int8_t data1[] = { 17, 26 ,-13, -45, 117, -122 };
const __flash uint8_t data2[] = { 3, 17, 29, 51, 99, 117, 149, 221 };
const __flash char data3[] = { "Strings and chars"};

BTW now that __flash exists (apart from the C++ compiler) why would you ever bother with PROGMEM any more? Clearly you would use 5 or 6 in your examples.

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

Thanks all.

 

clawson wrote:

BTW now that __flash exists (apart from the C++ compiler) why would you ever bother with PROGMEM any more? Clearly you would use 5 or 6 in your examples.

 

Yes, and that's what I intend to do.  Right now however it looks like there's a lot more information about using PROGMEM out there, and I'm having difficulty getting what I want to work, so I thought it would be good to make sure I have the "fundamental" stuff down right before I make any more misassumptions.

 

Right now I'm trying to implement a command interpreter by means of a command table.  I found this which seemed like a good start for what I intend to do:

 

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

 

And my bare bones non-flash implementation is:

 

typedef struct
{
	char	cmdName[10];
	void	(*cmdFuncPtr)(void);
} command_t;

void cmdOn(void);
void cmdOff(void);

command_t commandTable[] = {
	{"on",		cmdOn},
	{"off",		cmdOff}
};

 

That complies OK, but I'd like to put it all into flash.  So I thought I'd start with the cmdName strings:

 

typedef struct
{
	char	cmdName[10];
	void	(*cmdFuncPtr)(void);
} command_t;

void cmdOn(void);
void cmdOff(void);

command_t commandTable[] = {
	{(const __flash char[]){"on"},		cmdOn},
	{(const __flash char[]){"off"},		cmdOff}
};

 

But that doesn't work.  I get the error messages

initializer element is not computable at load time
(near initialization for 'commandTable[0].cmdName[0]')
initializer element is not computable at load time
(near initialization for 'commandTable[0].cmdName[1]')
initializer element is not computable at load time
(near initialization for 'commandTable[1].cmdName[0]')
initializer element is not computable at load time
(near initialization for 'commandTable[1].cmdName[1]')

 

I'm also a rather unsure about the line "void (*cmdFuncPtr)(void)" that I have in the typedef.  It complies alright, but I'm not sure if that is actually what I intend it to be.

 

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

There should be no curly braces around the literal strings.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

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

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

JohanEkdahl wrote:
There should be no curly braces around the literal strings.

 

Already tried that I'm afraid:

cast specifies array type

 

 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lucien wrote:
So I thought I'd start with the cmdName strings:
You can't put only a part of a structure into Flash. Either the whole structure or nothing. If only the string should be in Flash, then you must replace the array in the structure by a pointer. Then your initialisation would be right.

If you put the whole structure as it is (with the array) into Flash, then the initialisation is simply {"on", cmdOn}.

 

Stefan Ernst

Last Edited: Wed. Jun 14, 2017 - 07:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sternst wrote:

Lucien wrote:
So I thought I'd start with the cmdName strings:
You can't put only a part of a structure into Flash. Either the whole structure or nothing. If only the string should be in Flash, then you must replace the array in the structure by a pointer. Then your initialisation would be right.

If you put the whole structure as it is (with the array) into Flash, then the initialisation is simply {"on", cmdOn}.

 

 

Ah OK.  "const __flash command_t commandTable[]" worked fine.  Thanks!

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

OK,  I've run into another problem.  I'm trying to declare an inline string as being in flash memory.  I know how to do this the old way with the PSTR macro:

 

// This works
{
    ...
    uart_puts_P(PSTR("String 1.\n"));
}


 

And I can do it this way:

 

// This works
const __flash char string2[] = "String 2.\n";

int main(void)
{
    ...
    uart_puts_P(string2);
}

 

But this doesn't.

 

//  Doesn't work
int main(void)
{
    ...
    uart_puts_P((const __flash char[]){"String 3.\n"});
}

 

I get the error message "compound literal qualified by address-space qualifier".  (Isn't that what I intend to do though?)

 

Aren't the second and third code snippets here the same?

 

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

 

Lucien wrote:
Also, I've seen some code where they use uint8_t instead of char. Is there a preferred/"better" choice between the two?

clawson wrote:
You should use the data type that is appropriate for the data:

Absolutely!

 

That is:

  • uint8_t for unsigned 8-bit values;
  • int8_t for signed 8-bit values;
  • char specifically & only for text.

 

This is  entirely general - nothing specifically to do with flash or AVR or GCC.

Last Edited: Fri. Jun 23, 2017 - 07:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lucien wrote:
I'm trying to declare an inline string as being in flash memory.  I know how to do this the old way with the PSTR macro:
And why do you not want to use PSTR?

 

Lucien wrote:
I get the error message "compound literal qualified by address-space qualifier".
A compound literal function argument is something that is created temporarily, a bit like a automatic local variable. You can't put that into Flash.

Stefan Ernst

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

awneil wrote:

 

 

Lucien wrote:

Also, I've seen some code where they use uint8_t instead of char. Is there a preferred/"better" choice between the two?

 

 

clawson wrote:

You should use the data type that is appropriate for the data:

 

Absolutely!

 

That is:

  • uint8_t for unsigned 8-bit values;
  • int8_t for signed 8-bit values;
  • char specifically & only for text.

 

This is  entirely general - nothing specifically to do with flash or AVR or GCC.

 

OK, noted.  I was curious though because I vaguely remember reading somewhere that a char is interpreted by the compiler as a either uint8_t or a int8_t depending on a compiler flag.  Unfortunately I can't remember the exact details, or why that might matter.

 

 

Edit:  Spelling mistake.

Last Edited: Fri. Jun 23, 2017 - 08:14 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sternst wrote:

Lucien wrote:
I'm trying to declare an inline string as being in flash memory.  I know how to do this the old way with the PSTR macro:
And why do you not want to use PSTR?

 

Well, I figured if I'm going to use __flash instead of PROGMEM, I should also use the current equivalent of the PSTR macro for the sake of consistency.

 

sternst wrote:

Lucien wrote:
I get the error message "compound literal qualified by address-space qualifier".
A compound literal function argument is something that is created temporarily, a bit like a automatic local variable. You can't put that into Flash.

 

I'm confused then.  Isn't putting "String 1.\n" into flash what happens when I do:

 

uart_puts_P(PSTR("String 1.\n"));

 

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

Lucien wrote:
I vaguely remember reading somewhere that a char is interperted by the compiler as a either uint8_t or a int8_t

Whether an "unadorned" (or "plain") char is signed or unsigned is implementation defined;  that's why you should specifically use uint8_t or int8_t for numeric values - where the sign matters.

 

For text, sign is not really meaningful and, since the 'C' standard defines all string-related things in terms of 'char' - that's what you should use for text.

 

for historic reasons plain char is a type distinct from both signed char and unsigned char. It may be a signed type or an unsigned type, depending on the compiler and the character set (C guarantees that members of the C basic character set have positive values). 

 

https://en.wikipedia.org/wiki/C_...

 

The char type is distinct from both signed char and unsigned char, but is guaranteed to have the same representation as one of them

 

https://en.wikipedia.org/wiki/C_...

 

EDIT

 

Typos 

Last Edited: Fri. Jun 23, 2017 - 08:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lucien wrote:
I should also use the current equivalent of the PSTR macro for the sake of consistency.
There is no "current equivalent". Why should be, PSTR works perfectly well together with functions expecting a __flash argument. It even works for __memx arguments.

 

Lucien wrote:
I'm confused then. Isn't putting "String 1.\n" into flash what happens when I do:
Yes, but not by using a compound literal.

Stefan Ernst

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

sternst wrote:

Lucien wrote:
I'm confused then. Isn't putting "String 1.\n" into flash what happens when I do:

Yes, but not by using a compound literal.

 

OK, I've been doing some reading up, but it seems the more I learn the less I know.  Could you explain why that is the case?  I do appreciate the help (and the patience!) as I'm finding it difficult to know whether what I'm actually implementing is actually what I intended to do.

 

So from reading up, this is what I have so far.  I know these two lines are different:

 

char stringA[] = "Test string.\n";
char * stringB = "Test string.\n";

 

stringA is an array with the contents "Test string.\n".  My understanding is that stringB is a pointer to an array with the contents "Test string.\n".

 

If I wanted to put that into flash I can try:

 

const __flash char stringA[] = "Test string.\n";
char * const __flash stringB = "Test string.\n";

 

But while this puts stringA into flash, the second line puts my pointer into flash, and as I understand it, the string that it points to into RAM.  Not exactly what I want, so I try this:

 

// const __flash char *stringB = "Test string.\n";
// const __flash char * const __flash stringB = "Test string.\n";

 

Both these lines fail with the error "initializer element is not computable at load time".  I don't understand this as surely the initializer element is the string, and therefore known, and in turn therefore computable?

 

What further confused me is when I looked up the avr-libc documentation, where I find PSTR is a macro that does

 

PSTR(s)   ((const PROGMEM char *)(s))

 

But in practice, this doesn't create a pointer to a string in RAM.  Which brings me back to the question at the start of this post.  Also, all these three lines compile fine:

 

uart_puts_p(PSTR("Point X\n"));
uart_puts_p((const PROGMEM char *){"Point X\n"});
uart_puts_p((const PROGMEM char []){"Point X\n"});

 

I tried using __flash in place of PROGMEM and found that

 

uart_puts_p((const __flash char *){"Point X\n"});

 

will compile, but this

 

uart_puts_p((const __flash char []){"Point X\n"});

 

results in an error.

 

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

Lucien wrote:
What further confused me is when I looked up the avr-libc documentation, where I find PSTR is a macro that does

 

PSTR(s)   ((const PROGMEM char *)(s))

That is a simplification for the documentation to demonstrate the intention of the macro.

The real implementation is this:

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

 

Lucien wrote:
I tried using __flash in place of PROGMEM and found that

Why are you so obsessed with replacing PSTR?

Again: there is absolutely no need to do that.
 

void PrintStr (const __flash char *str) {
    ...
}

PrintStr (PSTR("Hello"));

That works perfectly well.

Stefan Ernst

Last Edited: Thu. Jun 29, 2017 - 12:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can do something like

</p>
<p>const __flash char* getp (void)<br />
{<br />
    return ({ static const __flash char _[] = "string-in-flash"; _; });<br />
}

Maybe you get less warnings with -Waddr-space-convert then.

 

@Admin: The forum software gets nuts and adds HTML tags I don't see in edit mode. WOuld you fix it for me? Thanks

 

avrfreaks does not support Opera. Profile inactive.

Last Edited: Thu. Jun 29, 2017 - 08:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sternst wrote:

Lucien wrote:
I tried using __flash in place of PROGMEM and found that

Why are you so obsessed with replacing PSTR?

Again: there is absolutely no need to do that.
 

void PrintStr (const __flash char *str) {
    ...
}

PrintStr (PSTR("Hello"));

That works perfectly well.

 

It's actually not about wanting to replace PROGMEM.  What I want is to learn what can be done, what cannot be done, and why.  The more I understand the less likely I am to make silly mistakes.