Search |
 |
|
 |
| Author |
Message |
|
|
Posted: Jul 30, 2008 - 05:43 PM |
|


Joined: Jul 10, 2008
Posts: 174
Location: Ruhrpott, Germany
|
|
Thank you for your fast answer.
To clarify my understanding:
When using
Code:
struct {
char z[2][20]
} mystruct PROGMEM;
mystruct myarray[n_languages];
I will have the structs in Flash and the array in RAM?
Or do I have to use
Code:
struct {
char z[2][20]
} mystruct;
mystruct myarray[n_languages] PROGMEM;
?
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:
Code:
menu_init{
strcpy(my_array[French].z[0], "Salut ");
strcpy(my_array[French].z[1], "Test ");
strcpy(my_array[English].z[1], "Hello ");
.
.
.
}
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 |
|
|
| |
|
|
|
|
|
Posted: Jul 31, 2008 - 01:42 AM |
|


Joined: Jan 23, 2004
Posts: 9830
Location: Trondheim, Norway
|
|
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:
Code:
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  |
_________________ Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
|
| |
|
|
|
|
|
Posted: Jul 31, 2008 - 01:38 PM |
|


Joined: Jul 10, 2008
Posts: 174
Location: Ruhrpott, Germany
|
|
Thanks 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:
Code:
// disp_buf is NOT in Flash. myarray is in flash.
for (i = 0; i < LCD_H; i++)
strncpy_P(dispbuf.z[i], myarray[curlanguage].z[i], LCD_W);
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 |
|
|
| |
|
|
|
|
|
Posted: Aug 09, 2008 - 11:08 AM |
|


Joined: Jul 10, 2008
Posts: 174
Location: Ruhrpott, Germany
|
|
|
Code:
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.
Regards,
Baldrian |
|
|
| |
|
|
|
|
|
Posted: Aug 09, 2008 - 04:04 PM |
|


Joined: Jul 18, 2005
Posts: 62324
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
Baldrian,
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 |
_________________
|
| |
|
|
|
|
|
Posted: Aug 10, 2008 - 12:47 PM |
|


Joined: Jul 10, 2008
Posts: 174
Location: Ruhrpott, Germany
|
|
That means, delete the structs and just make a list of strings which all have a specific name like
Code:
char hello_id PROGMEM = "Salut$Hello$Hola"
And then I will give some pointers (based on which menu items (strings) have to be displayed) to my menu() function.
Like:
Code:
menu(&hello_ID)
Or better give a struct of pointers to all needed strings to the menu() functions.
I will try it. |
_________________ http://xkcd.com/221/
|
| |
|
|
|
|
|
Posted: Aug 10, 2008 - 03:28 PM |
|


Joined: Jul 18, 2005
Posts: 62324
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
No I was thinking of an array of strings such as:
Code:
char * strings[] = {
"Salut$Hello$Hola",
"Aurevoir$Bye$Adios"
etc.
}
And possibly an enum or some defines:
Code:
typedef enum {
HELLO_ID,
GOODBYE_ID,
etc.
} STRINGS;
or
#define HELLO_ID 1
#define GOODBYE_ID 2
|
_________________
|
| |
|
|
|
|
|
Posted: Aug 10, 2008 - 08:10 PM |
|


Joined: Jan 12, 2002
Posts: 7829
Location: Canada
|
|
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.
Code:
typedef enum
{
HELLO_ID,
GOODBYE_ID,
etc.
} string_id_t;
typedef enum
{
ENGLISH_ID,
FRENCH_ID,
SPANISH_ID,
etc.
} language_id_t;
// language-specific phrase strings
char hello_en[] PROGMEM = "Hello";
char goodbye_en[] PROGMEM = "Bye";
char hello_fr[] PROGMEM = "Salut";
char goodbye_fr[] PROGMEM = "Aurevoir";
char hello_es[] PROGMEM = "Hola";
char goodbye_es[] PROGMEM = "Adios";
etc.
// language-specific phrase arrays
const char *prompts_en[] PROGMEM =
{
hello_en,
goodbye_en,
etc.,
NULL
};
const char *prompts_fr[] PROGMEM =
{
hello_fr,
goodbye_fr,
etc.,
NULL
};
const char *prompts_es[] PROGMEM =
{
hello_es,
goodbye_es,
etc.,
NULL
};
etc.
// language array
const char **languages[] PROGMEM =
{
prompts_en,
prompts_fr,
prompts_es,
etc.,
NULL
};
// global var to hold the language pointer
const char **current_language =
pgm_read_word(&languages[ENGLISH_ID]);
// accessing individual strings
const char *phrase =
pgm_read_word(¤t_language[string_id]);
while(ch = pgm_read_word(phrase++))
{
// output code here
}
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. |
|
|
| |
|
|
|
|
|
Posted: Aug 10, 2008 - 11:10 PM |
|


Joined: Mar 12, 2004
Posts: 1174
Location: Linköping, Sweden
|
|
|
Quote:
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 |
|
|
| |
|
|
|
|
|
Posted: Aug 10, 2008 - 11:32 PM |
|


Joined: Jan 12, 2002
Posts: 7829
Location: Canada
|
|
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.
Code:
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
Code:
char strings[][20] PROGMEM = {
"Salut$Hello$Hola",
"Aurevoir$Bye$Adios"
etc.
}
2) Array of pointers:
Code:
char hello_str[] PROGMEM = "Salut$Hello$Hola";
char goodbye_str[] PROGMEM = "Aurevoir$Bye$Adios";
const char *strings[] PROGMEM = {
hello_str,
goodbye_str,
etc.
}
**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.
Code:
typedef enum
{
HELLO_ID,
GOODBYE_ID,
etc.
} string_id_t;
typedef enum
{
ENGLISH_ID,
FRENCH_ID,
SPANISH_ID,
etc.
} language_id_t;
// language-specific phrase strings
char hello_en[] PROGMEM = "Hello";
char goodbye_en[] PROGMEM = "Bye";
char hello_fr[] PROGMEM = "Salut";
char goodbye_fr[] PROGMEM = "Aurevoir";
char hello_es[] PROGMEM = "Hola";
char goodbye_es[] PROGMEM = "Adios";
etc.
// language-specific phrase arrays
const char *languages[N_LANGUAGES][N_WORDS] PROGMEM = {
{
hello_en,
goodbye_en,
etc.
},{
hello_fr,
goodbye_fr,
etc.
},{
hello_es,
goodbye_es,
etc.
}
};
// global var to hold the language pointer
const char **current_language = languages[ENGLISH_ID];
// accessing individual strings
const char *phrase =
pgm_read_word(¤t_language[string_id]);
while(ch = pgm_read_word(phrase++))
{
// output code here
}
|
|
|
| |
|
|
|
|
|
Posted: Aug 11, 2008 - 11:28 AM |
|


Joined: Jul 18, 2005
Posts: 62324
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
| For the move to PROGMEM it was Glitch's (2) I was thinking of in fact. |
_________________
|
| |
|
|
|
|
|
Posted: Aug 11, 2008 - 09:17 PM |
|


Joined: Mar 12, 2004
Posts: 1174
Location: Linköping, Sweden
|
|
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 |
|
|
| |
|
|
|
|
|
Posted: Aug 20, 2008 - 11:43 AM |
|

Joined: Mar 03, 2008
Posts: 26
|
|
Hi,
How can allocate the table in a specific address?
Thanks |
|
|
| |
|
|
|
|
|
Posted: Aug 20, 2008 - 12:43 PM |
|


Joined: Jul 18, 2005
Posts: 62324
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
| Yes (use a named section and set its address using --section-start to the linker) but the question is "why?" |
_________________
|
| |
|
|
|
|
|
Posted: Aug 20, 2008 - 12:58 PM |
|

Joined: Mar 03, 2008
Posts: 26
|
|
Because 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 |
|
|
| |
|
|
|
|
|
Posted: Aug 20, 2008 - 01:04 PM |
|


Joined: Jul 18, 2005
Posts: 62324
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
Try:
Code:
char myTable[8][256] __attribute__ ((section (".mysection"))) = {....}
and in the Makefile add:
Code:
LDFLAGS += -Wl,--section-start=.mysection=0x2000
(note that is BYTE address 0x2000 which in Atmel code flash addressing is WORD address 0x1000) |
_________________
|
| |
|
|
|
|
|
Posted: Aug 21, 2008 - 12:52 PM |
|

Joined: Mar 03, 2008
Posts: 26
|
|
Thanks for your tip. But then I can access this table with the PROGMEM?
Thanks again  |
|
|
| |
|
|
|
|
|
Posted: Aug 21, 2008 - 01:05 PM |
|


Joined: Jul 18, 2005
Posts: 62324
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
| 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. |
_________________
|
| |
|
|
|
|
|
Posted: Aug 25, 2008 - 12:07 PM |
|

Joined: Aug 25, 2008
Posts: 1
|
|
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...
Code:
#include <avr/pgmspace.h> // PROGMEM
const prog_char PROGMEM stringTest[] = "Test working?";
PGM_P PROGMEM stringTestTable[] = { stringTest };
char buffer[17];
void setup() {
Serial.begin(9600);
}
void loop() {
strcpy_P(buffer, (char*)pgm_read_word(&(stringTestTable[0])));
Serial.print(buffer);
Serial.print(" : ");
strcpy_P(buffer, (char*)pgm_read_word(&(stringTest)));
Serial.print(buffer);
Serial.print(" : ");
Print(stringTestTable[0]);
Serial.print(" : ");
Print(stringTest);
}
void Print(PGM_P str) {
strcpy_P(buffer, (char*)pgm_read_word(&(str)));
Serial.println(buffer);
}
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. |
|
|
| |
|
|
|
|
|
Posted: Sep 24, 2008 - 10:32 PM |
|

Joined: Mar 17, 2007
Posts: 25
Location: Austin, TX
|
|
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:
Code:
cosresult = pgm_read_word(&costable[ICR1L]));
I have included pgmspace.h:
Code:
#include <avr/pgmspace.h>
And I created the table in the first place with a little perl:
Code:
open(TABLE, ">table.c");
print TABLE "static unsigned int costable[] PROGMEM =\n";
print TABLE "{\n";
for ($i=0; $i <= 89; $i++)
{
$cosr = (cos((90/256)*$i)*32765);
printf TABLE "%.0f,\n", $cosr;
}
$cosr = (cos((90/256)*90)*32765);
printf TABLE "%.0f\n", $cosr;
print TABLE "};";
That way I'm taking advantage of the signed 16-bit space.
My variable were declared as so:
Code:
signed short cosresult;
static signed int costable[] PROGMEM =
{
32765,
30761,
24994,
16170,
5367,
-6092,
-16806,
-25464,
-31007,
-32757,
-30500,
-24512,
-15525,
-4639,
6814,
17433,
25921,
31237,
32732,
30223,
24017,
14873,
3910,
-7532,
-18052,
-26364,
-31451,
-32691,
-29931,
-23510,
-14213,
-3178,
8247,
18662,
26795,
31650,
32633,
29624,
22991,
13546,
2444,
-8957,
-19262,
-27212,
-31832,
-32558,
-29302,
-22461,
-12873,
-1709,
9663,
19853,
27615,
31998,
32468,
28965,
21920,
12192,
974,
-10364,
-20434,
-28004,
-32149,
-32361,
-28614,
-21367,
-11506,
-238,
11060,
21004,
28379,
32283,
32237,
28248,
20803,
10814,
-498,
-11750,
-21564,
-28740,
-32400,
-32097,
-27868,
-20229,
-10116,
1234,
12434,
22113,
29086,
32502,
31941
};
Anyone spot a reason why this would not work? |
|
|
| |
|
|
|
|
|
|
|
|