I have a variable which is read from my main loop, and is both read and written from an interrupt handler. The interrupt can change the value at any time, so clearly it needs to be volatile in the main loop. But when the interrupt is running, it won't change unexpectedly (both because the interrupt is the only thing that changes it, and because the interrupt cannot be interrupted itself). It's important that the interrupt is fast, and making the variable non-volatile helps with this. In C, what is the best way to handle this situation?
At the moment, the variable is volatile. This works, but the IR handler is painfully slow, probably because quite a few useful compiler optimisations are disabled by the volatile keyword.
I could cast the variable to a non-volatile one in the IR handler. But I think that is officially undefined behaviour. (C standard Annex J.2: An attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type). And I'm not sure exactly what syntax I should use.
I could copy the value from the volatile variable to a local one, then copy back at the end of the IR handler. This feels a bit clunky, especially as there are actually quite a few of these variables.
This feels like a common enough thing that there should be an established good practise, I just don't know what it is.
Example main loop:
union {float value[8]; uint8_t bytes[32];} uart_out;
while (1){
if (state.new_results) {
state.new_results = false;
uart_out.value[0] = cabs(state.v_ref);
uart_out.value[1] = cabs(state.v_sens);
uart_out.value[2] = creal(state.v_in);
uart_out.value[3] = cimag(state.v_in);
uart_out.value[4] = creal(state.z_ratio);
uart_out.value[5] = cimag(state.z_ratio);
uart_out.value[6] = creal(state.z_ratio_filt);
uart_out.value[7] = cimag(state.z_ratio_filt);
HAL_UART_Transmit_IT(&huart1, uart_out.bytes , 32);
}
example IR header
#include <complex.h>
struct State_s {
volatile double complex v_in;
volatile double complex v_ref;
volatile double complex v_sens;
volatile double complex z_ratio;
volatile double complex z_ratio_filt;
volatile bool new_results;
};
extern struct State_s state;
example IR handler fragment
state.v_in = new_x + new_y*I; // new_? variables come from ADC via DMA.
// The next line takes 100us if everything is volatile! using -O3.
// z1 and z2 are global complex doubles which change only rarely
state.z_ratio = -state.v_sens / (state.v_in/z1 + state.v_ref/z2);
// More code sets new values for all other complex doubles
// in the struct, based on the new z_ratio. As above they read
// other values from the struct when doing calculations, and it
// is that many-reads-of-volatiles which is probably making it slow
// Write a new value to a buffer which will go to DAC by DMA
state.new_results = true;
floatif you remain on Cortex M4.