Search code examples
cavravr-gcc

Good way around initializer element is not constant error in C?


Getting an initializer element is not constant compile error when using avr-gcc. Is there any good way to do what I'm trying to do here?

file.c

#include "file.h"
#include "../notes/octave_two_notes.h"

//F2 is defined in octave_two_notes.h
//This is the file giving me the compilation error
struct Song_Note F2_500ms = { .note = F2, .duration_ms = 500 };

song_note.h

#include "note.h"

struct Song_Note {
    struct Note note;
    uint16_t duration_ms;
} Song_Note;

octave_two_notes.h

extern struct Note F2;

octave_two_notes.c

#define CALC_CTC_FREQ(clock_freq, prescaler, note_freq_hz) ( (uint32_t) clock_freq / ( (uint16_t) note_freq_hz * (uint16_t) prescaler * 2) - 1)

struct Note F2 = {.freq_hz = 87, .ocr_val = CALC_CTC_FREQ(16000000, 8, 87)};

note.h

#include <stdint.h>

struct Note {
    uint16_t freq_hz;
    uint16_t ocr_val;
} Note;

Solution

  • First of all, these variables should all be declared as const. Mainly because you want them in flash, not in RAM.

    const will unfortunately not solve the main problem though, because the C language doesn't even regard const-qualified variables as constant expressions. And variables declared at file scope have "static storage duration" and must therefore be initialized with a constant expression.

    The only solution C provides is pretty much to declare the initializer list as an inelegant macro, in this case it has to be inside a header file:

    // header file
    
    #define CALC_CTC_FREQ ...
    
    #define F2_INIT                              \
    {                                            \
      .freq_hz = 87,                             \
      .ocr_val = CALC_CTC_FREQ(16000000, 8, 87)  \
    }
    

    and in the .c file, use that macro:

    // c file
    const struct Note F2 = F2_INIT;
    

    and then in a .c file elsewhere:

    const struct Song_Note F2_500ms = { .note = F2_INIT, .duration_ms = 500 };
    

    Note that the CALC_CTC_FREQ part should be fine, everything inside it is a constant expression and it is evaluated at compile-time.