Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
Baldrian
PostPosted: Jul 30, 2008 - 05:43 PM
Hangaround


Joined: Jul 10, 2008
Posts: 176
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
 
 View user's profile Send private message  
Reply with quote Back to top
abcminiuser
PostPosted: Jul 31, 2008 - 01:42 AM
Moderator


Joined: Jan 23, 2004
Posts: 9891
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 Twisted Evil

_________________
Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
Baldrian
PostPosted: Jul 31, 2008 - 01:38 PM
Hangaround


Joined: Jul 10, 2008
Posts: 176
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
 
 View user's profile Send private message  
Reply with quote Back to top
Baldrian
PostPosted: Aug 09, 2008 - 11:08 AM
Hangaround


Joined: Jul 10, 2008
Posts: 176
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
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Aug 09, 2008 - 04:04 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62944
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

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
Baldrian
PostPosted: Aug 10, 2008 - 12:47 PM
Hangaround


Joined: Jul 10, 2008
Posts: 176
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/
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Aug 10, 2008 - 03:28 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62944
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

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
glitch
PostPosted: Aug 10, 2008 - 08:10 PM
Raving lunatic


Joined: Jan 12, 2002
Posts: 7832
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(&current_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.
 
 View user's profile Send private message  
Reply with quote Back to top
Lajon
PostPosted: Aug 10, 2008 - 11:10 PM
Posting Freak


Joined: Mar 12, 2004
Posts: 1177
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
 
 View user's profile Send private message  
Reply with quote Back to top
glitch
PostPosted: Aug 10, 2008 - 11:32 PM
Raving lunatic


Joined: Jan 12, 2002
Posts: 7832
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(&current_language[string_id]);

while(ch = pgm_read_word(phrase++))
{
  // output code here
}
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Aug 11, 2008 - 11:28 AM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62944
Location: (using avr-gcc in) Finchingfield, Essex, England

For the move to PROGMEM it was Glitch's (2) I was thinking of in fact.

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
Lajon
PostPosted: Aug 11, 2008 - 09:17 PM
Posting Freak


Joined: Mar 12, 2004
Posts: 1177
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
 
 View user's profile Send private message  
Reply with quote Back to top
softjad
PostPosted: Aug 20, 2008 - 11:43 AM
Rookie


Joined: Mar 03, 2008
Posts: 26


Hi,

How can allocate the table in a specific address?

Thanks
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Aug 20, 2008 - 12:43 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62944
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?"

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
softjad
PostPosted: Aug 20, 2008 - 12:58 PM
Rookie


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
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Aug 20, 2008 - 01:04 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62944
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)

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
softjad
PostPosted: Aug 21, 2008 - 12:52 PM
Rookie


Joined: Mar 03, 2008
Posts: 26


Thanks for your tip. But then I can access this table with the PROGMEM?

Thanks again Wink
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Aug 21, 2008 - 01:05 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62944
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.

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
morrijr
PostPosted: Aug 25, 2008 - 12:07 PM
Newbie


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.
 
 View user's profile Send private message  
Reply with quote Back to top
th0th696
PostPosted: Sep 24, 2008 - 10:32 PM
Rookie


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?
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits