First, thanks for the tutorial. It was helpful in understanding how things are supposed to be done.
Second... YUCK! Trying to define any data structure more complicated than a string or an array of simple data types reminds me of Fortran programs trying to implement 'data structures' with shared common areas and such. Does C++ permit operator overloading enough (ie including pointer dereference/etc) that one could use some C++ magic code to make the use of PROGMEM space invisible to the users?
I've got problems similar to everyone else; menus:
typedef const struct menu_item_ {
unsigned char menu_key;
menu_action_t menu_action; /* function or menu pointer */
} menu_item_t;
const menu_item_t foo[] PROGMEM = {
{'A', nextmenuA},
{'B', nextmenuB},
{0, "menu help string"}
};
And then code, for example, to draw a prompt (on a non-progmem implementation) that looks like:
menu_item_t *menu, *lastitem;
drawmenu:
lastitem = menu;
while ((lastitem->menu_key & 0x7F) != 0)
lastitem++; /* Find menu terminator to get type of draw */
if (lastitem->menu_action != 0) {
char *p = (char *) lastitem->menu_action;
while (*p)
printf("%c", *p++);
} else {
while (menu->menu_key != 0) {
printf("%c, ", menu->menu_key & 0x7F);
menu++;
}
}
printf("\r\n");
return; /* Done! */
and the code is looking really ugly as I try to insert pgm_read calls in the appropriate places...
Many thanks for this great tutorial, that helped me to optimize my code and understand better how to use flash to store data. I have one question though: is there a size limit in using flash to store static data ?
While I was switching/improving my strings to flash, it then suddenly did strange things (printing a long list of unreadable cars, as if the string wasn't terminated properly...). I had to go back to RAM for some parts of code.
Is there something I'm not considering ? Maybe a mistake somewhere else, but I don't see it right now.
I'm using an atmega128, and the summary is:
Program: 23934 bytes (18.3% Full)
(.text + .data + .bootloader)
Posted by abcminiuser: Thu. Jul 17, 2008 - 03:30 PM
1
2
3
4
5
Total votes: 0
There are two limits:
1) The hard limit of the amount of FLASH memory in the AVR - this is a physical limit
2) The 64KB limit due to GCC using 16-bit pointers - this can be worked around using special "far pointer" functions and macros
Your code is only 23KB long, so you're hitting neither. Does your code have any large RAM arrays which might be stepping on the stack or string if you're copying it into RAM before use?
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
Well, I don't copy anything myself to RAM. I just use large structure to store all states/transitions of the state machine driving the AVR. Something to do with mapping and 64k limit ? On the 64K limit, could you explain further the far pointer trick (is it in a tutorial I would have missed ?) ?
Posted by abcminiuser: Thu. Jul 17, 2008 - 05:03 PM
1
2
3
4
5
Total votes: 0
The avr-libc pgmspace.h module contains "far" versions of the existing methods - "pgm_read_byte_far()", for example. To use them on a string located over the 64KB barrier, you'd just OR the string's address (typecast as a long) with 0x10000 to create a psudo-pointer.
char StringLocatedOver64KB[] = "Test";
int main (void)
{
char a = pgm_read_byte_far(0x10000 | (uint16_t)&StringLocatedOver64KB);
}
You'd need to write your own "far" versions of the other library routines which deal with FLASH memory, such as memcmp_P(), using the far accessor routines.
Alternatively, use Carlos' macro to get the far address of a variable in FLASH space instead:
Which has the distinct advantage that it returns the correct address (albeit a long address) for any FLASH data whether it is over the 64KB boundary or not -- coupled with the far accessor routines and you have bulletproof code.
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
Nice tutorial, but I'm still totally screwed.
I read also the avr-libc FAQ http://www.nongnu.org/avr-libc/u... but I'm still not getting how to make an array of structs into flash.
I want to have a struct of strings for a menu.
like
struct {
char z[2][20]
} mystruct
And I want to make an array of this structs for different languages.
mystruct myarray[n_languages]
So that i can access the strings by something like
this example:
array[French].z[0]
or
array[French].z[1]
And now I want to have my 2.5k of strings in flash using Progmem :lol:
How to do that?
Posted by abcminiuser: Wed. Jul 30, 2008 - 04:28 PM
1
2
3
4
5
Total votes: 0
Easy, just add the PROGMEM attribute to the structure itself:
struct {
char z[2][20]
} mystruct PROGMEM;
If you want an array of structures, use the PROGMEM on the array instead.
To access the structure elements you can either read out the structs from FLASH into a RAM structure using pgm_read_block (and then use the RAM strucuture as you would normally), or use the pgm_read_* macros on the structure elements individually as you use them.
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
I know I cannot do it this way anymore, because the ROM stuff is constant so I cannot write to it during runtime. But how to do it know in a smart way?
Posted by abcminiuser: Thu. Jul 31, 2008 - 12:42 AM
1
2
3
4
5
Total votes: 0
Rule of thumb: when in doubt, add more PROGMEM. I think you'd end up with a PROGMEM array of structures, since you've got an actual array of structures, rather than an array of pointers to the structures (which *would* result in a SRAM array of pointers to PROGMEM space).
You cannot change the PROGMEM data at runtime (since it is embedded in the FLASH), but you can initialize it via standard C methods:
mystruct myarray[n_languages] PROGMEM =
{
{z: "Salut "}, // Initializer for myarray[0]
{z: "Test "}, // Initializer for myarray[1]
{z: "Hello "}, // Initializer for myarray[2]
// etc.
};
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
struct {
char z[menupoints][20]
} mystructX;
mystructX myarrayX[n_languages] PROGMEM =
{
{z: "Salut "}, // Initializer for myarray[0]
{z: "Test "}, // Initializer for myarray[1]
{z: "Hello "}, // Initializer for myarray[2]
// etc.
};
Now I have this. I have different mystructX foe different menu sizes (so different menupoints values)
I'm building my menu on lcd by copying the myarrayX[language] to LCD buffer using strcpy_P.
This works fine, but now I have for every menu one function where this is done.
What I want to do is to have only one function and give the myarrayX as a parameter to the function. I think I have to realize this using a pointer, because the myarrayX are all different in size as I mentioned before.
I think I can give a pinter to myarrayX to the function and give the size (menupoints) also to it.
But I couldn't figure out how to do this. So, if someone has an idea about that, please share it with me.
Just a mad idea but to pack your strings how about using:
"Salut$Hello$Hola"
Then when you display(HELLO_ID, language) where language is 0=French, 1=English, 2=Spanish. You just first point to the right sting using "HELLO_ID" and then skip 'language' number of '$'s along the string to find the start point to display and then continue displaying characters until NUL or another '$' is encountered. Use something other than '$' if there's a chance it ever needs to be displayed.
In the code you present above, otherwise, you are wasting huge amounts of bytes for ' 's
I would do it a little differently in this case. (there are many ways to accomplish your goal) Since size is also of concern, I would create individual strings for each phrase in each language, then create an array of pointers (for each language) to the appropriate phrases. And finally create an array of pointers to each of the language arrays. Any other menu data is kept in its own array of structures. To select the current language to display a variable is loaded with the address of the desired language array. This pointer can be global, and then accessed by any display routines, or passed as an additional parameter to them.
It may seem tedious, and it is to do by hand. However it can easily be managed with a small generator app that takes text files and generates the necessary data tables. This allows you to edit the resource strings for each language easily.
Writing code is like having sex.... make one little mistake, and you're supporting it for life.
Since size is also of concern, I would create individual strings for each phrase in each language,
I don't get it glitch, your way to do it is very clean but it does take more space than what clawson has above. You need N_LANGAUGES * 2 + (N_LANGAUGES - 1) * N_STRINGS * 2 bytes extra for all the pointer arrays.
/Lars
Yes Cliffs solution is, slightly, more dense than mine, but it requires more processing time, as you have to search the string for the correct entry.
My comment about size is aimed at the 2 dimensional array approach shown earlier. In which case it only takes more space if all the strings are close to the same length. As long as the average length of a string is more than 2 shorter than the longest string, you will be saving space with my method. (you still may if it is not, but it is not guaranteed)
Note that some of that overhead you show is required for Cliffs solution as well. The solution "as proposed" does not work in progmem.
char * strings[] = {
"Salut$Hello$Hola",
"Aurevoir$Bye$Adios"
etc.
}
The above needs to be either stored as a 2 dimensional array (lots of wasted space, if the amalgamated strings differ in length greatly), or as an array of pointers (same as my method). Thus my method only adds one more level of pointers to the mix. The trade off here is a few hundred bytes, vs execution speed.
Working progmem versions of Cliffs Solution:
1) 2-dimensional Array
char strings[][20] PROGMEM = {
"Salut$Hello$Hola",
"Aurevoir$Bye$Adios"
etc.
}
Actually it was (2) I was thinking about when I compared the memory requirement (the size for the single pointer array is the reason for the - 1 in the expression).
/Lars
The parameters to pgm_read_byte()/pgm_read_word() don't need to be a variable name - it can just as easily be an absolute address. All you are doing here is calling a function that wraps an LPM instruction.
I'm also struggling with PROGMEM, in particular passing them to other functions... I can't see why the code below shouldn't work, I assume that the pointer is wrong, but I don't know why...
I was following this tutorial so I could create a COS table, as the COS function from math.h is taking up too much cpu time. But my program fails if I add in this statement:
cosresult = pgm_read_word(&costable[ICR1L]));
I have included pgmspace.h:
#include
And I created the table in the first place with a little perl:
I was following this tutorial so I could create a COS table, as the COS function from math.h is taking up too much cpu time. But my program fails at runtime, even though it compiles, if I add in this statement:
cosresult = pgm_read_word(&costable[ICR1L]));
I have included pgmspace.h:
#include
And I created the table in the first place with a little perl:
Ya I'm on linux here, and unfortunately my windoze partition is completely borked. We have simulavr, but I have not had the most luck with it previously, maybe it's time I did some research on that end. By fail, I mean that when I upload it into the AVR I get continuous resets like I'm blowing the stack, which is the reason why I moved this array out to progmem in the first place (only 512 bytes of ram on the atmega168 so I could put the entire array in ram, but 256 16-bit values would be the entire RAM and I'm pretty sure that would not work in any instance I can think of ;). Before trying progmem I was using signed 8-bit values in half my eeprom and accessing like this:
myphase = EEPROM_read(ICR1L);
But after reading up on EEPROM corruption, and the very fact I have so much more progmem space, I'd like to be using progmem instead.
So far as static, I think you're right, so I'm going to switch out static for const (which still gives me continuous resets after I changed it out).
I will definitely get after simulavr in the morning and find out if the data is at least pulling out.
and get the warning that indeed shows what is happening:
"
../blink.c:27: warning: only initialized variables can be placed into program memory area
"
What the...?
This disagrees with all the theory I've ever read about it.
Now with all my C++ code big and fat, I have problems squeezing strings to PROGMEM desite few days of trials..
???
Compilation:
I will find the solution since we do not negotiate with terrorists. Still, the output from PROGMEM version is garbage. At least the compiler digested it.
---------- OK now the solution for C++:
changing
masks the warning but also makes the code not work. strcpy_P, nothing will work.
So you have to live with the flood of warnings.
The only version of function arguments turned out to be this:
----
at the end, the only thing that is really missing, is sprintf_P that would take format string directly from PROGMEM area.
-----
here is the BUG: http://www.mail-archive.com/gcc-...
it is not fixed even in the most recent
WinAVR-20081124rc3
I would like to verify that printf_P only changes the format argument to reside in program flash. And that if the format has %s, the following string pointer passed will be in RAM, and not program flash. The doxygen comments in the header seem to say only fmt is in program flash for print_P.
I would also like to ask about the var args functions vsnprintf_P/vsprintf_P, i assume since there is only one va_start() in the avr stdarg.h, that it doesnt care that its used with a program memory pointer or ram.
Posted by abcminiuser: Fri. Dec 5, 2008 - 09:18 AM
1
2
3
4
5
Total votes: 0
Correct - the only difference between printf and printf_P is that the format string of the second is stored in FLASH rather than ram. In the format string, you can use %s to denote a SRAM string argument, and %S to denote a FLASH string.
The va_args macros deal with parameters pass on the stack, which are pointers to data or (for the basic primatives) the data themselves. The variable arguments are stored in the SRAM stack, but the pointers may point to SRAM or FLASH memory -- the differentiation is how you use the pointer, not the pointer itself.
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
I'm using the usart and the string store int the flash memory.
I've made the next program, but it doesn't work good.
#include
#include
#include
#define F_CPU 4000000
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
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};
//Funciones
//Funcion para un byte
void USART_Tx( unsigned char dato ) {
while (!(UCSR0A & _BV(UDRE0)))
/* nada */ ;
UDR0 = dato;
}
//Funcion para mandar cadenas
void USART_TxString_P(const char *data)
{
while (pgm_read_byte(data) != 0x00)//Funcion obtiene dato señala *ptr
USART_Tx(pgm_read_byte(data++));
}
int main (void)
{ //Variables
char indice;
// Turn on the transmission and reception circuitry:
UCSR0B |= (1 << RXEN0) | (1 << TXEN0);
// Use 8-bit character sizes:
//UCSR0C |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1);
UCSR0C |= (1 << UCSZ00) | (1 << UCSZ01);
// Load lower 8-bits of the baud rate value into the low byte
// of the UBRR register:
UBRR0L = BAUD_PRESCALE;
// Load upper 8-bits of the baud rate value into the high byte
// of the UBRR register:
UBRR0H = (BAUD_PRESCALE >> 8);
for (;;) // Loop forever
{
// Do nothing until data have been recieved and is ready
// to be read from the UDR register:
while ((UCSR0A & (1 << RXC0)) == 0) {};
// Fetch the recieved byte value into the variable
// called "ByteReceived":
indice = UDR0; // Fetch the recieved byte value into the variable "ByteReceived"
USART_TxString_P((char*)pgm_read_word(&MenuItemPointers[indice]));
}
}
When i press '0',i get "ÿ.ÀHÀGÀFÀEÀDÀCÀBÀAÀ@À?À>À=À<À;À:À9À8À7À6À5À4À3À2À1À0ÀMenu Item 1"
When i press '1', i get "ÿ.ÀHÀGÀFÀEÀDÀCÀBÀAÀ@À?À>À=À<À;À:À9À8À7À6À5À4À3À2À1À0ÀMenu Item 1"
When i press '2', i get "2À1À0ÀMenu Item 1"
Can anyone help me?Does anyone know what is wrong?
If you press '0' what arrives at the AVR is not 0x00 but the ASCII code for character '0' which is 0x30. So it'll then try to read the 0x30th (48th) entry in your MenuItemPointers array (which doesn't exist). Try:
indice = UDR0; // Fetch the recieved byte value into the variable "ByteReceived"
indice -= '0'; // convert ASCII to binary
USART_TxString_P((char*)pgm_read_word(&MenuItemPointers[indice]));
That's the thing about UART - if you are just using it as a terminal (rather than sending/receiving files with binary content) that everything going out of the AVR probably needs to be converted from binary to ASCII (printf, itoa, etc.) and everything coming into the AVR probably needs to be converted from ASCII into binary (atoi, scanf, etc.)
Posted by abcminiuser: Thu. Mar 26, 2009 - 09:27 PM
1
2
3
4
5
Total votes: 0
I removed it due to it being old and inaccurate after several revisions. Making new PDFs of all my tutorials is on my todo list, which never seems to be getting any shorter.
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
Posted by lucbeullens: Fri. Mar 27, 2009 - 09:52 AM
1
2
3
4
5
Total votes: 0
Hello,
I have used the PROGMEM attribute , but it is not working like it should be.
When I read a byte from flash I can see in the disassembler that the routine starts with the right values (right index , right pointer to the flashlocation), but the result is not right.
I have no idee why the LDD R30,Z+0 and the LDI R31,0x00 are there. Theye are changing the right value store in the Z register cousing the result of the last instruction to be wrong.
I created a function readFlash() which can be used for all different kinds of data types and pointers.
But just to be clear - that function overloading will only work in C++, not C. Those of us who program in C will just need to continue to pick the right pgm_read() function.
this way I hope that all this data and the pointer table are in program space.
now how can I pass this arrays to a function? where I have to use the pgm_read()?
this is not the only way to handle this code, but it fits well in my project structure. I've got several of those arrays (12-15) and I have to pass 3 of them (selected by a comand) to some functions...
thanks again,
cheers!
bo.
2) pointers whether they are to RAM or code flash are really just 16 bit numbers and could be passed around as such or you can use the universal get out clause which is "void *" ;-)
First, thanks for the tutorial. It was helpful in understanding how things are supposed to be done.
Second... YUCK! Trying to define any data structure more complicated than a string or an array of simple data types reminds me of Fortran programs trying to implement 'data structures' with shared common areas and such. Does C++ permit operator overloading enough (ie including pointer dereference/etc) that one could use some C++ magic code to make the use of PROGMEM space invisible to the users?
I've got problems similar to everyone else; menus:
And then code, for example, to draw a prompt (on a non-progmem implementation) that looks like:
and the code is looking really ugly as I try to insert pgm_read calls in the appropriate places...
- Log in or register to post comments
TopSo how come the definition of PSTR defines the string as
instead of using the typedefs for prog_char:
It seems like the latter would allow C++ code to run different methods for progmem strings rather than ram strings (which would be a good thing.)
- Log in or register to post comments
TopThanks Dean! Your tutorials helped me a lot.
Nicu reSpawn.
- Log in or register to post comments
TopMany thanks for this great tutorial, that helped me to optimize my code and understand better how to use flash to store data. I have one question though: is there a size limit in using flash to store static data ?
While I was switching/improving my strings to flash, it then suddenly did strange things (printing a long list of unreadable cars, as if the string wasn't terminated properly...). I had to go back to RAM for some parts of code.
Is there something I'm not considering ? Maybe a mistake somewhere else, but I don't see it right now.
I'm using an atmega128, and the summary is:
Program: 23934 bytes (18.3% Full)
(.text + .data + .bootloader)
Data: 642 bytes (15.7% Full)
(.data + .bss + .noinit)
Once more, thanks to avrfreaks folks !
Vincent.
- Log in or register to post comments
TopThere are two limits:
1) The hard limit of the amount of FLASH memory in the AVR - this is a physical limit
2) The 64KB limit due to GCC using 16-bit pointers - this can be worked around using special "far pointer" functions and macros
Your code is only 23KB long, so you're hitting neither. Does your code have any large RAM arrays which might be stepping on the stack or string if you're copying it into RAM before use?
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
- Log in or register to post comments
TopWell, I don't copy anything myself to RAM. I just use large structure to store all states/transitions of the state machine driving the AVR. Something to do with mapping and 64k limit ? On the 64K limit, could you explain further the far pointer trick (is it in a tutorial I would have missed ?) ?
Thanks.
Vincent.
- Log in or register to post comments
TopThe avr-libc pgmspace.h module contains "far" versions of the existing methods - "pgm_read_byte_far()", for example. To use them on a string located over the 64KB barrier, you'd just OR the string's address (typecast as a long) with 0x10000 to create a psudo-pointer.
You'd need to write your own "far" versions of the other library routines which deal with FLASH memory, such as memcmp_P(), using the far accessor routines.
Alternatively, use Carlos' macro to get the far address of a variable in FLASH space instead:
Which has the distinct advantage that it returns the correct address (albeit a long address) for any FLASH data whether it is over the 64KB boundary or not -- coupled with the far accessor routines and you have bulletproof code.
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
- Log in or register to post comments
TopNice tutorial, but I'm still totally screwed.
I read also the avr-libc FAQ http://www.nongnu.org/avr-libc/u... but I'm still not getting how to make an array of structs into flash.
I want to have a struct of strings for a menu.
like
struct {
char z[2][20]
} mystruct
And I want to make an array of this structs for different languages.
mystruct myarray[n_languages]
So that i can access the strings by something like
this example:
array[French].z[0]
or
array[French].z[1]
And now I want to have my 2.5k of strings in flash using Progmem :lol:
How to do that?
http://xkcd.com/221/
- Log in or register to post comments
TopEasy, just add the PROGMEM attribute to the structure itself:
If you want an array of structures, use the PROGMEM on the array instead.
To access the structure elements you can either read out the structs from FLASH into a RAM structure using pgm_read_block (and then use the RAM strucuture as you would normally), or use the pgm_read_* macros on the structure elements individually as you use them.
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
- Log in or register to post comments
TopThank you for your fast answer.
To clarify my understanding:
When using
I will have the structs in Flash and the array in RAM?
Or do I have to use
?
And how do I fill my array of structs with strings when it resides in ROM?
In the moment I'm filling it during menu_init() function with this code:
I know I cannot do it this way anymore, because the ROM stuff is constant so I cannot write to it during runtime. But how to do it know in a smart way?
Regards,
Baldrian
http://xkcd.com/221/
- Log in or register to post comments
TopRule of thumb: when in doubt, add more PROGMEM. I think you'd end up with a PROGMEM array of structures, since you've got an actual array of structures, rather than an array of pointers to the structures (which *would* result in a SRAM array of pointers to PROGMEM space).
You cannot change the PROGMEM data at runtime (since it is embedded in the FLASH), but you can initialize it via standard C methods:
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
- Log in or register to post comments
TopThanks alot man Very Happy I think I'm on the right way now.
The only thing is, that I cannot test out everything, because I have to wait for my hardware for 2 more weeks. Sad
I made the "readout the strings to my lcd buffer" routine like this:
It gives no warnings, which is good. I will see if it is working like expected.
Again, thanks a lot for your help.
Regards,
Baldrian
http://xkcd.com/221/
- Log in or register to post comments
TopNow I have this. I have different mystructX foe different menu sizes (so different menupoints values)
I'm building my menu on lcd by copying the myarrayX[language] to LCD buffer using strcpy_P.
This works fine, but now I have for every menu one function where this is done.
What I want to do is to have only one function and give the myarrayX as a parameter to the function. I think I have to realize this using a pointer, because the myarrayX are all different in size as I mentioned before.
I think I can give a pinter to myarrayX to the function and give the size (menupoints) also to it.
But I couldn't figure out how to do this. So, if someone has an idea about that, please share it with me.
Regards,
Baldrian
http://xkcd.com/221/
- Log in or register to post comments
TopBaldrian,
Just a mad idea but to pack your strings how about using:
"Salut$Hello$Hola"
Then when you display(HELLO_ID, language) where language is 0=French, 1=English, 2=Spanish. You just first point to the right sting using "HELLO_ID" and then skip 'language' number of '$'s along the string to find the start point to display and then continue displaying characters until NUL or another '$' is encountered. Use something other than '$' if there's a chance it ever needs to be displayed.
In the code you present above, otherwise, you are wasting huge amounts of bytes for ' 's
Cliff
- Log in or register to post comments
TopThat means, delete the structs and just make a list of strings which all have a specific name like
And then I will give some pointers (based on which menu items (strings) have to be displayed) to my menu() function.
Like:
Or better give a struct of pointers to all needed strings to the menu() functions.
I will try it.
http://xkcd.com/221/
- Log in or register to post comments
TopNo I was thinking of an array of strings such as:
And possibly an enum or some defines:
- Log in or register to post comments
TopI would do it a little differently in this case. (there are many ways to accomplish your goal) Since size is also of concern, I would create individual strings for each phrase in each language, then create an array of pointers (for each language) to the appropriate phrases. And finally create an array of pointers to each of the language arrays. Any other menu data is kept in its own array of structures. To select the current language to display a variable is loaded with the address of the desired language array. This pointer can be global, and then accessed by any display routines, or passed as an additional parameter to them.
It may seem tedious, and it is to do by hand. However it can easily be managed with a small generator app that takes text files and generates the necessary data tables. This allows you to edit the resource strings for each language easily.
Writing code is like having sex.... make one little mistake, and you're supporting it for life.
- Log in or register to post comments
Top/Lars
- Log in or register to post comments
TopYes Cliffs solution is, slightly, more dense than mine, but it requires more processing time, as you have to search the string for the correct entry.
My comment about size is aimed at the 2 dimensional array approach shown earlier. In which case it only takes more space if all the strings are close to the same length. As long as the average length of a string is more than 2 shorter than the longest string, you will be saving space with my method. (you still may if it is not, but it is not guaranteed)
Note that some of that overhead you show is required for Cliffs solution as well. The solution "as proposed" does not work in progmem.
The above needs to be either stored as a 2 dimensional array (lots of wasted space, if the amalgamated strings differ in length greatly), or as an array of pointers (same as my method). Thus my method only adds one more level of pointers to the mix. The trade off here is a few hundred bytes, vs execution speed.
Working progmem versions of Cliffs Solution:
1) 2-dimensional Array
2) Array of pointers:
**for the given example strings, option 1 is more compact.
[edit]
one could reduce memory footprint of my method further, by storing all the string pointers in a 2d array.
Writing code is like having sex.... make one little mistake, and you're supporting it for life.
- Log in or register to post comments
TopFor the move to PROGMEM it was Glitch's (2) I was thinking of in fact.
- Log in or register to post comments
TopActually it was (2) I was thinking about when I compared the memory requirement (the size for the single pointer array is the reason for the - 1 in the expression).
/Lars
- Log in or register to post comments
TopHi,
How can allocate the table in a specific address?
Thanks
- Log in or register to post comments
TopYes (use a named section and set its address using --section-start to the linker) but the question is "why?"
- Log in or register to post comments
TopBecause I have to save a table in specific flash page. Then I will update this page only when the flash want to make a self update (once a month).
my table is defined as:
char myTable[8][256] PROGMEM = {....}
Then how can I change to ensure it is at the address 0x2000?
Thanks
- Log in or register to post comments
TopTry:
and in the Makefile add:
(note that is BYTE address 0x2000 which in Atmel code flash addressing is WORD address 0x1000)
- Log in or register to post comments
TopThanks for your tip. But then I can access this table with the PROGMEM?
Thanks again ;)
- Log in or register to post comments
TopThe parameters to pgm_read_byte()/pgm_read_word() don't need to be a variable name - it can just as easily be an absolute address. All you are doing here is calling a function that wraps an LPM instruction.
- Log in or register to post comments
TopI'm also struggling with PROGMEM, in particular passing them to other functions... I can't see why the code below shouldn't work, I assume that the pointer is wrong, but I don't know why...
It's arduino code, but I'm sure that won't bother you folks. I've tried const char* and _lots_ of variations instead of the PGM_P, can anyone help?
Thanks.
- Log in or register to post comments
TopI was following this tutorial so I could create a COS table, as the COS function from math.h is taking up too much cpu time. But my program fails if I add in this statement:
I have included pgmspace.h:
And I created the table in the first place with a little perl:
That way I'm taking advantage of the signed 16-bit space.
My variable were declared as so:
Anyone spot a reason why this would not work?
- Log in or register to post comments
TopI was following this tutorial so I could create a COS table, as the COS function from math.h is taking up too much cpu time. But my program fails at runtime, even though it compiles, if I add in this statement:
I have included pgmspace.h:
And I created the table in the first place with a little perl:
That way I'm taking advantage of the signed 16-bit space.
My variables were declared as so:
Anyone spot a reason why this would not work?
- Log in or register to post comments
TopWhat does that mean? If you run the code in the simulator do you see the expected cos() values being extracted from the array?
By the way I don't think you want 'static' on that array definition.
- Log in or register to post comments
TopYa I'm on linux here, and unfortunately my windoze partition is completely borked. We have simulavr, but I have not had the most luck with it previously, maybe it's time I did some research on that end. By fail, I mean that when I upload it into the AVR I get continuous resets like I'm blowing the stack, which is the reason why I moved this array out to progmem in the first place (only 512 bytes of ram on the atmega168 so I could put the entire array in ram, but 256 16-bit values would be the entire RAM and I'm pretty sure that would not work in any instance I can think of ;). Before trying progmem I was using signed 8-bit values in half my eeprom and accessing like this:
But after reading up on EEPROM corruption, and the very fact I have so much more progmem space, I'd like to be using progmem instead.
So far as static, I think you're right, so I'm going to switch out static for const (which still gives me continuous resets after I changed it out).
I will definitely get after simulavr in the morning and find out if the data is at least pulling out.
- Log in or register to post comments
TopIs the PROGMEM compatible with C++?
I got the following problem:
and get the warning that indeed shows what is happening:
"
../blink.c:27: warning: only initialized variables can be placed into program memory area
"
What the...?
This disagrees with all the theory I've ever read about it.
Now with all my C++ code big and fat, I have problems squeezing strings to PROGMEM desite few days of trials..
???
Compilation:
Using WinAVR-20080610
----------
OK sorry found the solution
// Following https://www.avrfreaks.net/index.p...
// in pgmspace.h change:
This is silly trick that internal avrfreaks search engine ignores C++ while external google search can point you back inside the forum.
-------------
still there is something stinking:
I will find the solution since we do not negotiate with terrorists. Still, the output from PROGMEM version is garbage. At least the compiler digested it.
----------
OK now the solution for C++:
changing
to
masks the warning but also makes the code not work. strcpy_P, nothing will work.
So you have to live with the flood of warnings.
The only version of function arguments turned out to be this:
----
at the end, the only thing that is really missing, is sprintf_P that would take format string directly from PROGMEM area.
-----
here is the BUG:
http://www.mail-archive.com/gcc-...
it is not fixed even in the most recent
WinAVR-20081124rc3
- Log in or register to post comments
TopI would like to verify that printf_P only changes the format argument to reside in program flash. And that if the format has %s, the following string pointer passed will be in RAM, and not program flash. The doxygen comments in the header seem to say only fmt is in program flash for print_P.
I would also like to ask about the var args functions vsnprintf_P/vsprintf_P, i assume since there is only one va_start() in the avr stdarg.h, that it doesnt care that its used with a program memory pointer or ram.
- Log in or register to post comments
TopCorrect - the only difference between printf and printf_P is that the format string of the second is stored in FLASH rather than ram. In the format string, you can use %s to denote a SRAM string argument, and %S to denote a FLASH string.
The va_args macros deal with parameters pass on the stack, which are pointers to data or (for the basic primatives) the data themselves. The variable arguments are stored in the SRAM stack, but the pointers may point to SRAM or FLASH memory -- the differentiation is how you use the pointer, not the pointer itself.
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
- Log in or register to post comments
TopHi, i'm new with the avr microcontroller.
I'm using the usart and the string store int the flash memory.
I've made the next program, but it doesn't work good.
When i press '0',i get "ÿ.ÀHÀGÀFÀEÀDÀCÀBÀAÀ@À?À>À=À<À;À:À9À8À7À6À5À4À3À2À1À0ÀMenu Item 1"
When i press '1', i get "ÿ.ÀHÀGÀFÀEÀDÀCÀBÀAÀ@À?À>À=À<À;À:À9À8À7À6À5À4À3À2À1À0ÀMenu Item 1"
When i press '2', i get "2À1À0ÀMenu Item 1"
Can anyone help me?Does anyone know what is wrong?
- Log in or register to post comments
TopIf you press '0' what arrives at the AVR is not 0x00 but the ASCII code for character '0' which is 0x30. So it'll then try to read the 0x30th (48th) entry in your MenuItemPointers array (which doesn't exist). Try:
- Log in or register to post comments
TopThanks, now it's work fine. I thought that i was sending the number not its code ascii.
- Log in or register to post comments
TopThat's the thing about UART - if you are just using it as a terminal (rather than sending/receiving files with binary content) that everything going out of the AVR probably needs to be converted from binary to ASCII (printf, itoa, etc.) and everything coming into the AVR probably needs to be converted from ASCII into binary (atoi, scanf, etc.)
Cliff
- Log in or register to post comments
TopThere was mentioning of a PDF. Anybody knows where I can find this PDF?
Thanks,
Leon
- Log in or register to post comments
TopI removed it due to it being old and inaccurate after several revisions. Making new PDFs of all my tutorials is on my todo list, which never seems to be getting any shorter.
- Dean :twisted:
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
- Log in or register to post comments
TopHello,
I have used the PROGMEM attribute , but it is not working like it should be.
When I read a byte from flash I can see in the disassembler that the routine starts with the right values (right index , right pointer to the flashlocation), but the result is not right.
61: j = pgm_read_byte(EEPROMerrorCheckString[i]);
+00000180: 01F9 MOVW R30,R18 Copy register pair
+00000181: 5CEC SUBI R30,0xCC Subtract immediate
+00000182: 4FFF SBCI R31,0xFF Subtract immediate with carry
+00000183: 81E0 LDD R30,Z+0 Load indirect with displacement
+00000184: E0F0 LDI R31,0x00 Load immediate
+00000185: 91E4 LPM R30,Z Load program memory
I have no idee why the LDD R30,Z+0 and the LDI R31,0x00 are there. Theye are changing the right value store in the Z register cousing the result of the last instruction to be wrong.
Is there somebody who knows why this happens?
- Log in or register to post comments
TopJust a guess since all pgm_read_xxxx functions need an address:
/Lars
- Log in or register to post comments
TopI read a lot about this amazing pdf tutorial, can't wait to read it. Yes I also read jabius's comment.
- Log in or register to post comments
Topyeah you're right bro.. :twisted:
ArcticSoul
Industrial Electronic Engineering, College Student
- Log in or register to post comments
TopPROGMEM is such a code that i used in my dotmatriks project. :D
Thanks a lot for the tutorial Dean :)
ArcticSoul
Industrial Electronic Engineering, College Student
- Log in or register to post comments
TopIt is not easy to manipulate data in flash so I made some effort to simplify it.
Solution
I created a function readFlash() which can be used for all different kinds of data types and pointers.
Example
int i PROGMEM = 32767; // write data to program memory
int x = readFlash(i); // read data from program memory.
See here
http://www.arduino.cc/cgi-bin/ya...
- Log in or register to post comments
TopBut just to be clear - that function overloading will only work in C++, not C. Those of us who program in C will just need to continue to pick the right pgm_read() function.
- Log in or register to post comments
TopHi,
frist of all thanks for the tutorial ;-)
let's see if I get it:
1)
it's the same to declare
and
2)
I've got some constant arrays (like the one before) in the prog.mem.
this way I hope that all this data and the pointer table are in program space.
now how can I pass this arrays to a function? where I have to use the pgm_read()?
if not in prog space it will be something like:
this is not the only way to handle this code, but it fits well in my project structure. I've got several of those arrays (12-15) and I have to pass 3 of them (selected by a comand) to some functions...
thanks again,
cheers!
bo.
- Log in or register to post comments
Top1) yes, pgmspace.h just has this:
2) pointers whether they are to RAM or code flash are really just 16 bit numbers and could be passed around as such or you can use the universal get out clause which is "void *" ;-)
- Log in or register to post comments
TopPages