Use of const & PROGMEM...

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

HI - I'm using the following...

static void output_two_digits( uint8_t num )
{
	const char* const pDigit PROGMEM = "0123456789";

	lcd_send_char( pgm_read_byte( &pDigit[num / 10] ) );
	lcd_send_char( pgm_read_byte( &pDigit[num % 10] ) );
}

pDigit is a const pointer to const char, and the string it points at should be in program memory... Howevre, it doesn't seem to be afftecting the memory sizes as reported after doing a rebuild all. Am I doing something wrong?

I'm using:

    AVR Studio 4.13.557 Service Pack 1 GUI Version 4, 13, 0, 557
    JTAG ICE 1, 0, 0, 39
    ATMEGA128 270

    Operating System
    Major 5
    Minor 1
    PlatformID 2
    Build 2600
    Service Pack 2

    Plugins:

    AvrPluginAvrAsmObject 1, 0, 0, 46
    AvrPluginavrgccplugin 1, 0, 0, 9
    Stk500Dll 1, 0, 1, 0

Nicko

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

The 3 step plan to happiness-

Step 1- read the warning messages

Step 2- move that PROGMEM string out of the function

Step 3- const uint8_t pDigit[] PROGMEM = "0123456789";

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

You can also use PSTR("0123456789"). However, the string is redundat in this case:

static void output_two_digits(uint8_t num) 
{ 
   lcd_send_char(num / 10 + '0'); 
   lcd_send_char(num % 10 + '0'); 
}

/Lars

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

Whatever way you do it, you may want to also make sure 'num' is <100.

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

curtvm wrote:
The 3 step plan to happiness-

Step 1- read the warning messages

Step 2- move that PROGMEM string out of the function

Step 3- const uint8_t pDigit[] PROGMEM = "0123456789";

The attribute was ignored because the pointer was not static. This works fine:

	static const char* const pDigit PROGMEM = "0123456789";

Your steps 2&3 have no effect at all - the code as it stands was valid, but the memory allocation was not what I wanted. I go for const-correctness (being an ex-professional programmer, but new to AVRs). In my code, indexing from pDigit is perfectly valid ANSI C, and can be also used in bizarre ways such as:

char ch = "0123456789"[ 4 ];

The name of an array is typed as a pointer to the array type, so can be used in interesting ways. The real issue here is what PROGMEM applies to, and in this case its a "char *", i.e. not the initialiser string.

curtvm wrote:
Whatever way you do it, you may want to also make sure 'num' is <100.

The number is never bigger than 59 (decimal), hence the lack of a bounds check, but your point is valid.

Lajon wrote:

static void output_two_digits(uint8_t num) 
{ 
   lcd_send_char(num / 10 + '0'); 
   lcd_send_char(num % 10 + '0'); 
}


Thanks - This is obviously a far better approach, but I was experimenting with PROGMEM and trying to understand it. I think I'm just about getting there!

Nicko

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

Quote:
Your steps 2&3 have no effect at all
Then I will have to change it to '1 step to happiness'.

Step 2 was to make it a 'global' which would make is static, which would make it work. Your 'static' later took care of that.

Step 3. Well step 3 was to simplify. I guess I don't understand what the difference is. Either way you end up with an array of characters in flash. I also think pDigit is a pointer no matter what you call it, and since its pointer value can't be changed when done as an array, I would say its quite constant.

But I'm certainly no professional programmer, so I can easily be mistaken.

and this is step 2 & 3-

const uint8_t pDigit[] PROGMEM = "0123456789";

static void output_two_digits( uint8_t num )
{
   lcd_send_char( pgm_read_byte( &pDigit[num / 10] ) );
   lcd_send_char( pgm_read_byte( &pDigit[num % 10] ) );
} 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the help - its all much clearer now - its difficult to change the habits of a lifetime when moving to uPs from somewhat larger hosts though having the GNU compiler for AVRs is an enormous win for those who've used it on UNIX & other platforms... The scarey thing is that you could do real ANSI C++, but not too much of it ;-)

Much easier/nicer than PICs...

Nick

Nicko

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

Quote:

Step 3. Well step 3 was to simplify. I guess I don't understand what the difference is. Either way you end up with an array of characters in flash.


Not really.

static const char* myvar PROGMEM = "0123456789";

results in 2 distinct statically allocated objects in memory: a 2-byte pointer in Flash, which permanently points to the beginning of an 11-byte string in SRAM

static const char myvar[] PROGMEM = "0123456789";

ought to result in only one statically allocated object: a permanently unchangeable 11-byte string in Flash.

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

I am assuming that your given routine is not the Real(tm) routine, since the given function could easily be done much easier as:

static void output_two_digits( uint8_t num )
{
   lcd_send_char( pgm_read_byte( (num / 10) + '0' );
   lcd_send_char( pgm_read_byte( (num % 10) + '0' );
} 

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

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

Quote:
results in 2 distinct statically allocated objects in memory: a 2-byte pointer in Flash, which permanently points to the beginning of an 11-byte string in SRAM
I should have shut up after my 3 steps to happiness.

Stu- 3rd post. You forgot to remove the pgm_read_byte, otherwise it may be more clever than I realize.

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

curtvm wrote:
Stu- 3rd post. You forgot to remove the pgm_read_byte, otherwise it may be more clever than I realize.
Dang, you're right. It should be:
static void output_two_digits( uint8_t num )
{
   lcd_send_char( (num / 10) + '0' );
   lcd_send_char( (num % 10) + '0' );
}

You could even throw in protection against numbers greater than 99 messing up the first digit. Also, I forgot that you would probably need to cast the result to char for the lcd_send_char() function:

static void output_two_digits( uint8_t num )
{
   num = num %100;
   lcd_send_char( (char) ( (num / 10) + '0' ) );
   lcd_send_char( (char) ( (num % 10) + '0' ) );
}

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!