[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [avr-libc-dev] const struct in program memory
From: |
David Brown |
Subject: |
Re: [avr-libc-dev] const struct in program memory |
Date: |
Mon, 17 Jun 2019 22:29:05 +0200 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.7.0 |
On 17/06/2019 09:38, avr-libc-devel wrote:
You certainly can use PROGMEM (or the newer __flash) on structs in
program memory. But you need to use the right methods to access the
data. Can you post a short sample code that you tried but which did
not work?
typedef struct {
uint16_t int_part;
uint8_t frac_part;
float fl_part;
} __attribute__ ((packed)) bw_data;
(This is just incidental advice, and not related to you problem.
I recommend never using the "packed" attribute. It rarely helps (and
should make no difference on the AVR), and can often result in
significantly less efficient code. Only use it if you /really/ need it,
and need to be sure on identical layouts on widely different target
processors. Even in such cases, I would prefer to manually add dummy
padding members to make everything naturally aligned (here that would be
a "uint8_t dummy" after "frac_part") and use gcc's "-Wpadded" warning to
let you know if you've got things wrong.)
const bw_data bw_tab[] PROGMEM;
const bw_data bw_tab[] PROGMEM = {
[BW_7_8] = { //7.81
.int_part = 7,
.frac_part = 81,
.fl_part = 7.81f,
},
.... skip ....
}
float symbols_time_ms(const param_t *const par, const uint8_t symbols_num){
float temp;
temp = 1 << (par->sf + 6); //2^SF
temp = (float) temp * (float) symbols_num /
bw_tab[par->bw].fl_part;
return temp;
}
When giving sample code for a problem like this, try to keep it short
and valid - this function is too complicated, and involves different
variables and types.
Let's just use the simplified function :
float get_fl_part(const bw_data * par) {
float temp = par->fl_part;
return temp;
}
This is wrong.
PROGMEM tells the compiler to put the data (bw_tab here) in flash
memory. On the AVR, flash is accessed with different instructions from
ram (and IO registers). So if you have told the compiler to put the
data in flash, you have to tell it to read the data from flash:
float get_fl_part(const bw_data * par){
float temp = pgm_read_float(&par->fl_part);
return temp;
}
Yes, this is a little awkward and inconvenient. But you have to do it.
The reason the program worked when you omitted the PROGMEM on bw_tab is
that the table is then allocated in ram, and initialised at startup.
For smaller tables, or if you are not using the ram for anything else,
this is the most convenient and efficient method. But you must be
consistent - either PROGMEM on definitions and "pgm_read_" functions in
use, or omit both.
Newer gcc versions also support the "__flash" address space. This lets
you write:
const __flash bw_data bw_tab[] = {
[BW_7_8] = { //7.81
.int_part = 7,
.frac_part = 81,
.fl_part = 7.81f,
},
.... skip ....
}
float get_fl_part(const __flash bw_data * par){
float temp = par->fl_part;
return temp;
}
Much nicer. But again, be consistent!
------------------
When I call the function symbols_time_ms (), the result is incorrect if
the PROGMEM modifier is uncommented.
Thank you very much for your answer and attention to my question.
No problem.