gardner:
I would like to second robtillaart's suggestion that Nick update his example of this to actually show different-lengths strings being used.
Well, that is covered half-way down the page using the "classic" method of having lots of variables, before I move on to show how you can avoid that if you use fixed-length strings. Once again, I think that in the microprocessor area, where strings are likely to appear on LCD screens, or LED strips, the need for widely-varying string sizes is is likely to be minimal. And if you must, use the more tedious two-step process for storing them.
The bit under the heading "Putting the strings themselves into PROGMEM" (maybe they should be numbered)...
(1) uses strings that are contrived to be the same length as one another
(2) is contrived to have content identical to the identifier name, which is confusing, and
(3) there are WAY too many of them. 5 would be enough to illustrate the point. Having 40 just makes for a lot of scrolling and reduced clarity, IMO.
I believe one of your objectives of this writeup should be as a starting point for someone trying to understand a recommendation given in the forums or an example obtained from somewhere. In that context needing to understand strcpy_P(PSTR("foo"),...) is entirely probable. Having this reference source de-emphasize explaining this angle may be okay, but having it ignore it totally is not so great. Heck, your own sample programs use strcpy_P() and memcpy_P(). How's a beginner supposed to follow that?
gardner:
The bit under the heading "Putting the strings themselves into PROGMEM" (maybe they should be numbered)...
(1) uses strings that are contrived to be the same length as one another
(2) is contrived to have content identical to the identifier name, which is confusing, and
(3) there are WAY too many of them. 5 would be enough to illustrate the point. Having 40 just makes for a lot of scrolling and reduced clarity, IMO.
Ah well, I can see more work is required.
I've reworked the page to address most of those concerns.
It isn't so much the Arduino 1.5.7 release but rather the version of gcc.
The newer version of gcc included in Arduino 1.5.7 is more strict on the use of the AVR progmem hack
declarations.
The older avr-gcc compiler was fairly lax and as a result many of the declarations in wide use
were actually incorrect including many of the examples on the Arduino PROGMEM page.
My suggestion would be make sure it works on 1.5.7 as that is one of the easier ways to ensure
that you are using the newer compiler.
The nice thing is that when the declarations are finally correct,
it will work on the newer or the older compiler.
Keep in mind that some users may be using a newer version of the compiler
even though they are still using the 1.x s/w.
This is probably more likely from those that are not using the IDE but rather using
Makefiles.
Although I use the newer compiler with the 1.x IDE by renaming/removing the compiler
that comes with the IDE so the locally installed version of avr-gcc is used instead.
bperrybap:
It isn't so much the Arduino 1.5.7 release but rather the version of gcc.
The newer version of gcc included in Arduino 1.5.7 is more strict on the use of the AVR progmem hack
declarations.
Thanks, Bill, you were right. I had to throw some const declarations in there and now it compiles on both compilers.
I'm still not too happy with what you have under "PROGMEM functions". Would you consider putting:
PROGMEM functions
These examples and the Arduino support for PROGMEM in general are built using features from the the AVR Libc (AVR standard library) [avr-libc: <avr/pgmspace.h>: Program Space Utilities] that you may also encounter in developing Arduino sketches and looking at ones developed by others.
AVR Libc, and therefore Arduino provides several standard string and memory manipulation functions in versions that address program memory rather than RAM. The AVR Libc convention is to append "_P" to the name of the standard function, thus we have strcpy_P() and memcpy_P() which are functions used in the above examples to copy strings or memory from PROGMEM to RAM. The "source" argument is assumed to be in PROGMEM and the destination argument in RAM. Otherwise they function like the standard library functions strcpy() and memcpy(). Some useful functions of this kind are:
There are many others documented in the AVR Libc manual.
The AVR Libc provides a macro PSTR() that is similar to the F() macro mentioned above. PSTR() works with the AVR Llibc "_P" functions, for example:
if (strcmp_P(inputBuffer, PSTR("SUCCESS")) == 0) {
}
Here we are using a strcmp() to check that the input buffer contains a certain string. By using strcmp_P() and PSTR() we are arranging that the comparison string doesn't have to occupy RAM.
F() and PSTR() although similar in concept, are not interchangeable, and you may have to think carefully about which is appropriate in various situations.
I think one of the things that might be helpful, especially for newbies is to state right up front
in the tutorial is that the issues associated with constant data storage in flash memory is strictly an AVR problem
and that PROGMEM and the associated access routines and _P functions is an AVR proprietary thing.
The other micro-controllers used on Arduino, like Pic32 and ARM don't have this issue and when using
those other processors PROGMEM and all the associated support routines are not needed.
So one alternative to greatly simplify coding when const data is used
is to switch processors to avoid having to deal with the complexities of
AVR flash data storage.
Just me, but when doing a complex one off project that uses lots of flash storage, I sometimes switch
to pic32 or ARM to avoid having to deal with all the progmem stuff.
On those, const "just works" as it is supposed to.
Those other processors also typically have more internal resources and in some cases
can actually be a bit cheaper like in the case of the PIC32MX250F128B vs the AVR atmega328p.
Just tossing out another potential way to "solve" the problem.
bperrybap:
I think one of the things that might be helpful, especially for newbies is to state right up front
in the tutorial is that the issues associated with constant data storage in flash memory is strictly an AVR problem
and that PROGMEM and the associated access routines and _P functions is an AVR proprietary thing.
The other micro-controllers used on Arduino, like Pic32 and ARM don't have this issue and when using
those other processors PROGMEM and all the associated support routines are not needed.
But Bill, how do these other chips solve this? Do they use von Neumann architecture? For any chip with Harvard architecture this must be an issue because program memory (which survives power off) and RAM (which doesn't) are different address spaces. Or does the compiler manage it? And if it does, why can't the avr-gcc compiler do it?
Both devices support a Harvard memory architecture, with separate data and program memory interfaces.
...
The PIC maintains a completely separate address space for program and data.
So even if the compiler is smart enough to generate different code when it knows the string is in PROGMEM, what happens if you store a pointer (and particularly if you cast away the program memory attribute)? Then the compiler now no longer knows which part of memory to go to.
I found another reference to PIC programming:
rom char *s1[] = "Hello";
The use of the keyword "rom" seems to me to be equivalent to using PROGMEM.
Nick,
Look at the pic32 parts rather than the pic parts. They are very different.
The pic32 uses the MIPs architecture and does not have the Harvard architecture separate address problem.
With pic32 and ARM you can simply declare the data as const which is how
C intended it to work since C expects a single address space.
I believe that if Arduino were starting over today it would use
a MIcroChip pic32 PIC32MX250F128B vs the AVR atmega328p.
Have you looked at that chip?
It is pretty incredible for only about $3.50 USD which is about the same
cost as the m328.
No I haven't. Do they use the gcc compiler or their own one? Last time I looked at PICs they had proprietary compilers, for which you had to pay for anything above basic functionality. I've moved on from proprietary and closed systems personally.