0

I'm working on understanding Nordic's embedded system timer library. I found that they define preprocessor directives in a way that I don't understand and haven't seen before:

/** @brief The configuration structure of the timer driver instance. */
typedef struct
{
    nrf_timer_frequency_t frequency;          ///< Frequency. 
    nrf_timer_mode_t      mode;               ///< Mode of operation.
    nrf_timer_bit_width_t bit_width;          ///< Bit width.
    uint8_t               interrupt_priority; ///< Interrupt priority.
    void *                p_context;          ///< Context passed to interrupt handler.
} nrfx_timer_config_t; 

/** @brief Timer driver instance default configuration. */
#define NRFX_TIMER_DEFAULT_CONFIG                                                    \
{                                                                                    \
    .frequency          = (nrf_timer_frequency_t)NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY,\ 
    .mode               = (nrf_timer_mode_t)NRFX_TIMER_DEFAULT_CONFIG_MODE,          \
    .bit_width          = (nrf_timer_bit_width_t)NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH,\
    .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,                    \
    .p_context          = NULL                                                       \
}

What I don't understand is the notation in their preprocessor directive #define NRFX_TIMER_DEFAULT_CONFIG. The dot operator is used to access struct members but there is nothing to the left of the dot so how does the compiler know what struct to access? I would have expected something like:

nrfx_timer_config_t.frequency = NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY, \

but there is no reference to the nrfx_timer_config_t struct. I'm guessing the (nrf_timer_frequency_t) is denoting membership somehow, but I don't understand how it does this since .interrupt_priority and .p_context don't seem to have any references to the nrfx_timer_config_t struct at all. How does the compiler know where these values come from and how to assign them?

Also I'm not sure if this context is needed but nrf_timer_frequency_t, nrf_timer_mode_t, and nrf_timer_bit_width_t are all enumerations.

Any help with understanding what is going on here would be greatly appreciated, thanks!

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • The macro is basically a c struct initializer for the nrfx_timer_config_t struct. You can do 'nrfx_timer_config_t myTimerConfig = NRFX_TIMER_DEFAULT_CONFIG'. This will initialize 'myTimerConfig' to the default values. – lulle2007200 May 21 '21 at 16:57
  • I don't like horzontal scrolling. It's evil. Don't trust on it. – wildplasser May 21 '21 at 17:52
  • Make it 500 chars wide ;-? – wildplasser May 21 '21 at 17:58
  • I tend to keep my source inside 80 or 132 columns. (for the same reason why newspapers are formatted in (4..8) columns; people just dont like to read long lines) – wildplasser May 21 '21 at 18:03
  • This is a somewhat common (and somewhat ugly) way to keep multiple default configurations without messing up the initializing code too much. Apart from that they use designated initializers instead of "initialize everything in order of appearance", which is probably just a cosmetic detail. Designated initializers give better self-documenting code. – Lundin May 24 '21 at 08:16

1 Answers1

2

It is nothing to do with macro syntax or pre-processor directives. It is a designated initializer and have been part of the C language since ISO C99.

In this case the use case would be to instantiate an object of type nrfx_timer_config_t and initialise it using NRFX_TIMER_DEFAULT_CONFIG thus:

nrfx_timer_config_t timer1 = NRFX_TIMER_DEFAULT_CONFIG ;

which expands to:

nrfx_timer_config_t timer1 = {
    .frequency          = (nrf_timer_frequency_t)NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY,
    .mode               = (nrf_timer_mode_t)NRFX_TIMER_DEFAULT_CONFIG_MODE,
    .bit_width          = (nrf_timer_bit_width_t)NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH,
    .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
    .p_context          = NULL
};

(ignoring the fact that the NULL and any other macros would be expanded also).

Regarding:

I would have expected something like:

nrfx_timer_config_t.frequency = NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY, \ 

... the left-hand side of the dot-operator (.) cannot be a type name, it has to be an object. The macro is used to initialise such an object (on the lhs of the =)

Regarding:

I'm guessing the (nrf_timer_frequency_t) is denoting membership somehow,

No, it is exactly what it looks like, a regular type cast. If NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY if an enumeration symbol (or a macro that expands to one) of the enum nrf_timer_frequency_t the cast is unnecessary.

Clifford
  • 88,407
  • 13
  • 85
  • 165