How to access strings with pgm_read_byte_far() ???

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

Hello everybody

I am writing a bootloader for an AtMega128 (AVR-GCC) and have to place my string constants in the bootloader section. Because these strings are not located in the lower 64kB segment I have to use the pgm_read_byte_far() function to access the bytes of my string arrays.

I tried to do it in this way: (simplified)

#include 
#include 
...
prog_char MyString[] = "Hello World";
...
void UART0_puts_P(PGM_P str)
//---------------------------------------------------
// send a ProgMem string to UART0 Transmit-buffer
//---------------------------------------------------
{
  u08 c= pgm_read_byte_far(str);
  while (c)
  {
    UART0_putc(c);
    c=pgm_read_byte_far(++str);   
  }
}
...
int main(void)
{
  ...
  UART0_puts_P(MyString);
  while(1);
}

This is working when MyString is in the lower 64kB segment and if I use pgm_read_byte(str). But with pgm_read_byte_far() I get the following warnings:

Quote:

Q:\UART0.c: In function 'UART0_puts_P':
Q:\UART0.c:202: warning: cast from pointer to integer of different size
Q:\UART0.c:206: warning: cast from pointer to integer of different size

What am I doing wrong, respectively how can I achieve that PGM_P (const prog_char *) gives an unsigned long value instead of an unsigned short value!

Thanks for any help...

Peter

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

Hello,

For this particular case (bootloader placed at the top 64KB of the ATmega128 memory) you can use something like for every pgm_read_###_far function:

pgm_read_byte_far(0x10000UL + (uint32_t) (uint16_t) str))

Or even better with the help of a special macro to get the 24 bit address of a var

#include 
#include 
#include 
...
prog_char MyString[] = "Hello World";
...

#define GET_FAR_ADDRESS(var)                          \
({                                                    \
    uint_farptr_t tmp;                                \
                                                      \
    __asm__ __volatile__(                             \
                                                      \
            "ldi    %A0, lo8(%1)"           "\n\t"    \
            "ldi    %B0, hi8(%1)"           "\n\t"    \
            "ldi    %C0, hh8(%1)"           "\n\t"    \
            "clr    %D0"                    "\n\t"    \
        :                                             \
            "=d" (tmp)                                \
        :                                             \
            "p"  (&(var))                             \
    );                                                \
    tmp;                                              \
})

void UART0_puts_P(uint_farptr_t str)
//---------------------------------------------------
// send a ProgMem string to UART0 Transmit-buffer
//---------------------------------------------------
{
  u08 c= pgm_read_byte_far(str);
  while (c)
  {
    UART0_putc(c);
    c=pgm_read_byte_far(++str);   
  }
}
...
int main(void)
{
  ...
  UART0_puts_P(GET_FAR_ADDRESS(MyString));
  while(1);
}

Notes: uint_farptr_t is defined in inttypes.h in avr-libc 1.4.0 and is equivalent to uint32_t. Be aware also of the pointer parameter change in the function.

Regards.

Carlos.

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

Hi Carlos

Thanks for the macros, I have no warnings anymore, but it is still not working!

A question: If [uint_farptr_t] is equivalent to [uint32_t] then can I use [uint32_t] instead of [uint_farptr_t]? Is this correct?

I am trying to understand all the PROGMEM pointer stuff, but I am getting confused more and more... (unfortunately I'm not a C expert)

I found:

prog_char is a typedef of char PROGMEM
PSTR(s) is a macro of const PROGMEM char *(s)
PGM_P is a macro of const prog_char * = const char PROGMEM *

What is the difference between [const PROGMEM char *] and [const char PROGMEM *], are they equivalent?

Should I define my PROGMEM strings with:

const char MyString[] PROGMEM = "Hello World";

instead of using:

prog_char MyString[] = "Hello World";   
char PROGMEM MyString[] = "Hello World"; // same as above

Is there a difference in these declarations, if yes: what?

Regards Peter

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

Hello,

PROGMEM is defined as __attribute__((__progmem__)). The compiler is very flexible with the positioning of the attribute clause (never after the assignment) and the position does not affect the generated code.

Your code is very similar to the code I wrote some time ago for a bootloader in an ATmega128. This is directly extracted from it (and correctly functioning)

void LCD_puts_P (uint_farptr_t text)
{
    uint8_t c;

    while ((c = pgm_read_byte_far(text++))) {
        LCD_putchar(c);
    }
}


static const uint8_t text1[] PROGMEM = "######    V1.0.0";
static const uint8_t text2[] PROGMEM = "-- BOOTLOADER --";


void DisplayMessage (void)
{
    LCD_Init();
    LCD_Clear();
    LCD_puts_P(GET_FAR_ADDRESS(text1));
    LCD_SetCursorPosition(2, 0);
    LCD_puts_P(GET_FAR_ADDRESS(text2));
}

Maybe you are having a different problem: fuses, bootloader offsetting (in bytes, not in words),...

Regards.

Carlos.

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

Okydoky..

Maybe my bootloader does really hung up anythere else! Unfortunately it is still not possible to debug GCC-AVR C-Code with AVR-Studio above 64 kByte! This is because of an annoying bug in the dwarf2 output generation, I still wonder why this bug does not become a higher priority!

Anyway, thanks a lot for your help

Regards, Peter

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

************************************************************
** At first: A happy new year to all the AvrFreaks! **
************************************************************
And now, back to my Bootloader-Project:

Finally, after doing an elaborately Bootloader-Debugging, (on ASM-Code level and by comparing to the *.LSS file.) I found where my bootloader does hung.

strcpy_P() is not working because my Progmem-Strings are located above 64kB...!!!

I guess it will be the same problem with most of the "Program Space String Utilities" functions.

Is there a generic solution, to make the "Program Space String Utilities" usable for above 64 kB...???

Thanks for any help

Kind Regards Peter

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

Is really no one out there, who is familiar with this problem...? Maybe my question is stupid, nevertheless, I would be thankful for any hint!

Peter

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

Peter, you are right, all of the "Program Space String Utilities" functions should work under 64 kB.
You may "override" some of them in your source code for above 64KB usage, for example:

char* strcpy_P(char* des, PGM_P src)
{
	unsigned long p = 0x10000ul + (unsigned)src;
	char* s = des;
	do
	{ 
		*s = pgm_read_byte_far(p++);    
	}  while (*s++);
	return des;
}

Then call it as normal.
Hope it's helpful.

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

My finally conclusion was also, to write my own "Program Space String Utilities" functions to work above 64 kB.

I am using Carlos GET_FAR_ADDRESS macro in conjunction with pgm_read_byte_far(), this should work for the whole PROGMEM memory range!

The postfix "_PF" for my functions will indicate that they are usable for "Progmem Far" access, e.g. strcpy_PF(...)
_____________________________________________________________

But I am very concerned about the fact, that the avr_libc does not offer a solution for this problem, respectively will fail in this case!

There is neither a hint in the avr_libc documentation about that, nor a warning from the compiler or linker!

Well, for my bootloader, I always know that my strings are above 64kB. But what happens if my application grows over 64 kB and I don't care about if a string could be located above 64 kB...???

Peter

Last Edited: Wed. Jan 4, 2006 - 11:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Compiler or linker just can't tell you about that, they don't know
about that. It's a known limitation of the existing implementation
that they can only access the lower 64 KB.

Yes, the docs should mention that somehow, though remember it's only
(or mostly) a concern for the bootloader. In normal applications, the
progmem data are placed before the instructions, in order to place
them safely below 64 KB.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.