Leaving a data array (Font) in FLASH - PROGMEM in AVR GCC
Question
Ahhh, PROGMEM, pointers, pointers to pointers, addresses of pointers... My head boggles.
I have a data array for the font in question
const uint8_t dejaVuSans9ptBitmaps[] =
{
/* @0 ' ' (5 pixels wide) */
0x00, /* */
0x00, /* */
...
to which I have added PROGMEM
const uint8_t dejaVuSans9ptBitmaps[] PROGMEM =
This is referenced in another structure like so;
const FONT_INFO dejaVuSans9ptFontInfo = {
13,
' ',
'~',
dejaVuSans9ptDescriptors,
dejaVuSans9ptBitmaps,
};
The structure is defined as;
typedef struct {
const uint8_t height;
const uint8_t startChar;
const uint8_t endChar;
const FONT_CHAR_INFO* charInfo;
const uint8_t* data;
} FONT_INFO;
Am I correct in assuming this needs to change to;
typedef struct {
const uint8_t height;
const uint8_t startChar;
const uint8_t endChar;
const FONT_CHAR_INFO* charInfo;
const PGM_P data;
} FONT_INFO;
When I do so, it complains that
warning: pointer targets in initialization differ in signedness
For this particular line in the FONT_INFO variable;
const FONT_INFO dejaVuSans9ptFontInfo = {
13,
' ',
'~',
dejaVuSans9ptDescriptors,
--> dejaVuSans9ptBitmaps, <--
};
It is then drawn using the function;
void drawString(uint16_t x, uint16_t y, uint16_t color, const FONT_INFO *fontInfo, char *str) {
...
drawCharBitmap(currentX, y, color, &fontInfo->data[charOffset], charWidth, fontInfo->height);
...
Which finally draws the glyph;
void drawCharBitmap(const uint16_t xPixel, const uint16_t yPixel, uint16_t color, const uint8_t *glyph, uint8_t cols, uint8_t rows) {
...
if (glyph[indexIntoGlyph] & (0X80)) drawPixel(currentX, currentY, color);
...
I am in over my head :/ Can anyone give me some direction? I have spent hours trying to use PGM_P, and pgm_read_byte etc to no avail - I always get garbage on the screen.
Save me!
Solution
OK, I think I understand what is going on here.
First, const uint8_t* data
is a pointer to the data stored in PROGMEM.
In the function void drawString(uint16_t x, uint16_t y, uint16_t color, const FONT_INFO *fontInfo, char *str)
we pass a pointer to fontInfo
.
To continue, the following is important to understand;
fontInfo.data
(*ptr_to_fontInfo).data
ptr_to_fontInfo->data
are all the same. So ptr_to_fontInfo->data
returns the data (not address)
Then using the &
operator, we pass the 'address of' this data to the next function
drawCharBitmap(currentX, y, color,
&fontInfo->data[charOffset], charWidth, fontInfo->height)
This address is stored in the declared pointer variable unint8_t *glyph
here;
void drawCharBitmap(const uint16_t xPixel, const uint16_t yPixel,
uint16_t color, const uint8_t *glyph, uint8_t cols, uint8_t rows)
Keeping this in mind;
int *ptr;
int a;
ptr = &a;
Then glyph now points to the same address as fontInfo->data[charOffset]
.
The next thing to know is;
a[b] in C is just a fancy way for writing *(a + b)
So glyph being a pointer, when used like this glyph[indexIntoGlyph]
, it is the same as *(glyph + indexIntoGlyph)
, and the dereferencing operator *
means we get the data at that address.
From there, we can use the pgm rules as wex described;
If the variable is in PROGMEM, you use pgm_read_byte() as a replacement for the dereference operator *. For "normal" variables in RAM you can always write *(&a) instead of just a to return the value of the variable a; so to return a 8-bit wide variable from progmem you write pgm_read_byte(&x).
Hope this explanation is correct and helps people (newbies like me!) understand it a bit better.
OTHER TIPS
I got some great support over at AVRfreaks.net, and thought I'd post the answer here for future reference by this community. Thanks 'wek'!
'wek' identified that based on the information I gave, that I needed to send multiple bytes starting from &fontInfo->data[charOffset]
in drawCharBitmap()
.
If the variable is in PROGMEM, you use
pgm_read_byte()
as a replacement for the dereference operator*
. For "normal" variables in RAM you can always write*(&a)
instead of just a to return the value of the variablea
; so to return a 8-bit wide variable from progmem you writepgm_read_byte(&x)
.Now recall that
a[b]
in C is just a fancy way for writing*(a + b)
(wherea
is a pointer pointing to the first member of the array, thus rules of pointer arithmetics apply). So indrawCharBitmap
you can changeglyph[indexIntoGlyph]
either intopgm_read_byte(&(glyph[indexIntoGlyph]))
, orpgm_read_byte(glyph + indexIntoGlyph)
.
I'm still trying to understand the links here, but it was such a great answer it deserved to be put on here. Thanks to all who took the time to look at this.