avr-libc-dev
[Top][All Lists]
Advanced

[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.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]