Pointers and Flash RAM storage

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

Hi guys

 

My current project has a help system built in to it. This consists of a short description of each command that can be entered by RS232 (USART) session.

I've quickly found that storing the help text in variables is chomping up large amounts of SRAM. To get around this I've started reading the help strings directly from Flash:

#include <avr/pgmspace.h>

const char test = "hello!";

void main (void)
{
    uint8_t i;
    for (i=0; pgm_read_byte(&test[i]); i++) 
        Serial0_SendByte(pgm_read_byte(&test[i]));
    
    while(1) {}
}

Works like a charm.

 

What I'd like to do now is embed the display routine in a function/subroutine, but I'm a  little hazy on how to pass a pointer the test.  This gives me an error:

 

void test(unsigned long *addr[])
{
	uint8_t i;
	for (i=0; pgm_read_byte(addr[i]); i++)
	    Serial0_SendByte(pgm_read_byte(addr[i]));
}

The error complains about incompatible pointer type. I assume this is  coming from the conversion of  '*' to '&'' ?

I know that '&' designates an address of a variable, so I've tried passing the address instead, but this also does not work.

 

 

 

 

 

 

This topic has a solution.
Last Edited: Sat. Oct 3, 2015 - 07:53 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You want to look at the __flash feature of the compiler. (That's 2 underbars) You can define your messages like this:

 

const __flash char PowerOn[] =    "Power On   " ;
const __flash char PowerOff[] =   "Power Off  " ;

These sit in flash and are accessed with read program memory instructions. There are a bunch of string functions defined that start with a p, that look in flash instead of ram for the string. Even more handy is the memx feature. You can define a function this way:

void LCDwrite(const __memx  char * s)
{
	while (*s != 0) LCDchar(*s++);
}

Again, that's 2 underbars.

 

This function uses the memx feature and can take a pointer to flash OR ram in s. I can call "LCDwrite" either way:

LCDwrite(PowerOn) ;
LCDwrite(VarInRam);

And it will automagically fetch from flash or ram, depending on what kind of pointer I passed.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

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

 

 

I use this approach to storing strings in flash memory:

 

char buffer[MAX_STRING_SIZE_PLUS_ONE];

 

prog_char myString0[] PROGMEM = "string_0 short";
prog_char myString1[] PROGMEM = "string_1 longer";      
prog_char myString2[] PROGMEM = "string_2 longest";

 

PROGMEM const char *myStriPtrTable[] = {                                      

 myString0, myString1, myString2  };
                                                           
 for (int i=0; i<2; i++) {                                                           
    strcpy_P(buffer, (char*) pgm_read_word( & myStriPtrTable[i] ));           
    serial.println(buffer);  
 }  

 

Each string gets stored in flash (after the interrupt vector table).  A table of each string's flash address is also stored because the strings are of different length.  This adds two bytes for each string.  There's also the terminating zero byte at the end of each string.  Then the string's flash address is passed to strcpy_P which gets the char from flash and puts it into an SRAM buffer.  This buffer holds the string that is stored in flash.

Last Edited: Fri. Oct 2, 2015 - 11:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
prog_char myString0[] PROGMEM = "string_0 short";
prog_char myString1[] PROGMEM = "string_1 longer";      
prog_char myString2[] PROGMEM = "string_2 longest";

 

PROGMEM const char *myStriPtrTable[] = {                                      

 myString0, myString1, myString2  };

This can be done in one step:

https://www.avrfreaks.net/comment/1455601#comment-1455601

const __flash char * const __flash myStriPtrTable[] = {
  (const __flash char[]) { "string_0 short" },
  (const __flash char[]) { "string_1 longer" },
  (const __flash char[]) { "string_2 longest" }
};

This makes it easier to maintain, as you need only change the initialisation of one array.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Sat. Oct 3, 2015 - 03:43 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You tried to fetch a byte with a pointer to unsigned long.

 

__flash is likely the way to go.

Iluvatar is the better part of Valar.

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

Fantastic - thank you, guys. I've tried the __flash method and the examples that you've provided and that does the job nicely.

As a bonus, it seems faster (although that might be just my imagination!).

 

Thanks again smiley

 

P.S. For anyone new to C/AVRs (like me), this reduced my Data Memory Use from 68% to 30.5% using an ATMEGA 328P. Program Memory Usage is at 18.2%. Well worth doing if you have a lot of strings.

Last Edited: Sat. Oct 3, 2015 - 08:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did not know this feature. Thank you, it will be very helpful. I tested it here and it worked perfectly.