2

Let's say I want to modify a global variable from inside an interrupt service routine. I must ensure that any variables I modify are volatile. Does it work to use a volatile pointer to data I want to modify if the data itself is not volatile?

In my case the data is a struct, but does the answer change if the data is a class, array, or just a variable?

#include <Arduino.h>
#define PIN_INTERRUPT 2
typedef struct MyStruct
{
 int a_field;
} MyStruct;
MyStruct struct_data;
int array_data[32];
volatile bool _flag_ISR = false;
volatile MyStruct *_struct_ISR = &struct_data;
void myISR()
{
 // just some example stuff
 int read = digitalRead(PIN_INTERRUPT);
 _struct_ISR->a_field = read;
 _arr_data_ISR[4] = read;
 
 _flag_ISR = true;
}
void setup()
{
 attachInterrupt(digitalPinToInterrupt(PIN_INTERRUPT), myISR, CHANGE);
}
void loop()
{
 if (_flag_ISR)
 {
 // do processing here...
 _flag_ISR = false;
 }
}
asked Mar 17, 2021 at 14:53
1
  • I think I misunderstood your question the first couple passes through, probably because (I'm guessing) by "volatile pointer" you really meant "pointer to volatile" and the distinction is important. So, are you asking: "with respect to communicating between the ISR and the main line of execution, is it sufficient to use a pointer-TO-volatile if the thing being pointed-to is, itself, not volatile qualified?" Commented Mar 17, 2021 at 15:50

1 Answer 1

1

First, beware that what you have written is not a volatile pointer, it's a pointer to volatile data. If you always access the object through this pointer, you should be fine. If you use the pointer in the ISR, but access the data directly in the main code (bypassing the volatile qualifier), then the program could fail.

Consider the following:

void loop()
{
 if (_flag_ISR) {
 _flag_ISR = false;
 Serial.println(struct_data.a_field);
 }
}

Once the compiler inlines the call to loop() from main(), the main program looks like

int main()
{
 init(); // Arduino core initialization
 setup();
 for (;;) { // inlined loop()
 if (_flag_ISR) {
 _flag_ISR = false;
 Serial.println(struct_data.a_field);
 }
 }
}

And now, since struct_data is not volatile, the compiler can legitimately optimize the code by avoiding repeatedly accessing the same RAM location:

int main()
{
 init(); // Arduino core initialization
 setup();
 register int data_copy = struct_data.a_field;
 for (;;) { // inlined loop()
 if (_flag_ISR) {
 _flag_ISR = false;
 Serial.println(data_copy);
 }
 }
}

And now the program prints only zeros.

answered Mar 17, 2021 at 15:11
3
  • Voting yours up. I think I took their "volatile pointer" phrasing too literally. Assuming they confirm this understanding of what they meant your answer has pretty much everything I'd have to say about it anyway. I'm sort of surprised you're using the register keyword. Commented Mar 17, 2021 at 16:01
  • @timemage: The register keyword is seldom useful in actual code. Here I am using it only to express the idea that the compiler can choose to keep a copy of the data in an internal CPU register (or set of registers). Commented Mar 17, 2021 at 16:31
  • It's the latter half of that that I was looking for. You know, you're not even allowed to use it in `17. Commented Mar 17, 2021 at 16:32

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.