Using ‘volatile’ with ISR Part-1
Use volatile when your code is dealing with below scenarios
- Memory-mapped peripheral registers of the microcontrollers
- Multiple tasks accessing global variables(read/write) in an RTOS multithreaded application
- When a global variable is used to share data between the main code and an ISR code.
In the previous articles, we explored the case of using the ‘volatile‘ keyword with memory-mapped peripheral register access. In this article, let’s explore when a global variable is used to share data between the main code and an ISR code.
Now let’s see how we can use volatile with this scenario.
Here I have a project 008button_ISR. So, let me explain this project’s source code.
#include <stdint.h> #include <stdio.h> //global shared variable between main code and ISR uint8_t g_button_pressed = 0; uint32_t g_button_press_count =0; void button_init(void); uint32_t *pEXTTIPendReg = (uint32_t*) (0x40013C00 + 0x14); uint32_t *pClkCtrlReg = (uint32_t*) (0x40023800 + 0x30); uint32_t *pClkCtrlRegApb2 = (uint32_t*) (0x40023800 + 0x44); uint32_t *pGPIOAModeReg = (uint32_t*) (0x40020000 + 0x00); uint32_t *pEXTIMaskReg = (uint32_t*) (0x40013C00 + 0x00); uint32_t *pEXTTIEdgeCtrlReg = (uint32_t*) (0x40013C00 + 0x08); uint32_t *pNVICIRQEnReg = (uint32_t*) 0xE000E100; int main(void) { button_init(); while(1) { //Disable interrupt *pEXTIMaskReg &= ~( 1 << 0); if(g_button_pressed){ //Some delay until button debouncing gets over for(uint32_t i=0;i<500000/2;i++); g_button_press_count++; printf("Button is pressed : %lu\n",g_button_press_count); g_button_pressed = 0; } //Enable interrupt *pEXTIMaskReg |= ( 1 << 0); } } void button_init(void) { *pClkCtrlReg |= ( 1 << 0); *pClkCtrlRegApb2 |= ( 1 << 14); *pEXTTIEdgeCtrlReg |= ( 1 << 0); *pEXTIMaskReg |= ( 1 << 0); *pNVICIRQEnReg |= ( 1 << 6); } /* This is button interrupt handler*/ void EXTI0_IRQHandler(void) { //Make this flag SET . if button pressed g_button_pressed = 1; *pEXTTIPendReg |= ( 1 << 0); }
There are 2 main functions in this code.
The first function is the main function and the second function is the ISR. This is the button ISR. This button ISR will get executed whenever the onboard button of the board is pressed.
What we do in this ISR is very simple, we set the global flag. So, g_button_pressed is a global flag and that will be set and the ISR exits. So, that’s the only thing ISR does.
What the main function does is, it is actually trapped inside the infinite loop and for every iteration, the main code checks this g_button_pressed global flag.
And if this global flag is really set(that is by the ISR), then it prints how many times the button is pressed so far.
What it does is, it just uses one count variable, it just increments that count, And then it just prints that count value to indicate these many times the button is pressed so far. After that, it resets this global flag. g_button_pressed = 0 is like invalidating the flag.
And again the flag will be set by the ISR, and again if condition executes, then printf will be printed, like that.
for(uint32_t i =0; i<500000/2; i++); This is a small delay. This delay is for button debouncing. So, this introduces some delay until the button debouncing gets over.
If you want to reproduce this code on your board, then you should be using the STM32F4 discovery board. So, if you have some other board then you have to modify this code. Because different boards have different pins where the button is connected and also the interrupt configuration changes. So, you cannot simply download it on some other board and you can make it work.
So, if you have a discovery board, then you can try this code. Otherwise, please identify the places where you can use ‘volatile’ in this project to make this application behave as intended. Because this project works only with the O0 optimization level. So the moment you change the optimization level this application breaks.
Let me show you how to run this code.
First, let’s make the optimization is None(-O0). And after that, let’s build the project, and then debug. And after that, go to SWV/ITM Data Console, and let’s start the tracing, and after that just run the code.
The code is running, and press the button. You can see in Figure 1, it is printing when the button is pressed: 1 times , 2 times, 3 times, 4 times, like that. This is working properly.
Let’s change the optimization level to O3. Let’s start the trace first. Let’s start the tracing and then hit run.
Now let me press the button once, it printed 18 times, as shown in Figure 2. So, I just pressed the button once but it printed 18 times. That means there is some problem with the code.
The application is not behaving properly, so you have to fix this. Identify the places where, and you can use volatile to fix this issue. I’ll explain in the next article.
FastBit Embedded Brain Academy Courses
Click here: https://fastbitlab.com/course1