Microcontroller Embedded C Programming Lecture 142| Using ‘volatile’ with ISR Part-2

  • Post author:
  • Post category:Blog

 

Using ‘volatile’ with ISR Part-2

 

In the previous article, I gave you a small assignment to identify the places where we can keep volatile in this application. And we know that, that application breaks in the O3 optimization level and now let’s get started. Let’s analyze the code below.

First of all, as a rule of thumb, there are memory-mapped register addresses, that’s why I use ‘volatile’ generously. That’s the first change you should be doing. 

#include <stdint.h>
#include <stdio.h>

//global shared variable between main code and ISR
uint8_t volatile g_button_pressed = 0;
uint32_t g_button_press_count = 0;

void button_init(void);

uint32_t volatile *pEXTTIPendReg = (uint32_t*) (0x40013C00 + 0x14);
uint32_t volatile *pClkCtrlReg = (uint32_t*) (0x40023800 + 0x30);
uint32_t volatile *pClkCtrlRegApb2 = (uint32_t*) (0x40023800 + 0x44);
uint32_t volatile *pGPIOAModeReg = (uint32_t*) (0x40020000 + 0x00);
uint32_t volatile *pEXTIMaskReg = (uint32_t*) (0x40013C00 + 0x00);
uint32_t volatile *pEXTTIEdgeCtrlReg = (uint32_t*) (0x40013C00 + 0x08);
uint32_t volatile *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 volatile 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);
}

Look at the main function, there is one function button_init(); It is just to modify the memory-mapped registers. 

 

Next, while(1) there is nothing to do here.

And *pEXITMaskReg &= ~(1<<0); this is again modifying a register.

And after that, there is a global flag ‘g_button_pressed’. That’s why this global flag or global variable should be made volatile according to our rule of thumb(Figure 1). Because it has the potential to undergo unexpected changes by the ISR, or by the multiple tasks, etc in a multiple-threaded application. That’s why as a rule of thumb we’ll make it volatile. 

By the way, g_button_press_count = 0 is also a global variable but this is not shared between a task and an ISR. That’s why I need not make this as volatile. 

After that, we have a for loop. This for loop is being used to introduce a small delay for the button debouncing. But the compiler will definitely remove this.  

Why?  Because the compiler thinks that this will slow down the application. 

The compiler is absolutely right, because the compiler doesn’t know what is our requirement. Our requirement is, to compensate for the button debouncing by introducing a small delay. So, for us, this delay is really required, but the compiler doesn’t think in that direction.

What the compiler thinks is, the ‘i’ is simply incremented to some level, and afterward ‘i’ is not at all used anywhere in the code. So, the compiler thinks that the operations on the ‘i’ are redundant. That’s why the compiler will consider this for loop as redundant.

And it also tries to optimize it, because optimizing this will make the application faster. But making the application faster will break our application. That’s why we have to tell the compiler not to do any optimization on ‘i’. That’s why we have to make ‘i’ also volatile here.

After that, printf statement. No optimization is required here, anyway it will get called. 

And then we are invalidating this flag g_button_pressed = 0. So, operations on this flag will not be optimized, because it is already volatile. That’s how you fix the code by using the ‘volatile’ keyword. 

 

First, you have to remember all these 3 cases where you should use volatile generously.

When to use ‘ Volatile’ qualifier?

Use volatile when your code is dealing with below scenarios.

  1. Memory-mapped peripheral registers of the microcontrollers
  2. Multiple tasks accessing global variables(read/write) in an RTOS multithreaded application
  3. When a global variable is used to share data between the main code and an ISR code.

 

 

And after that, you should also remember this case: where if you have an empty loop, the loop variable has to be made volatile to skip the optimization.

In the following article, let’s discuss the usage of ‘const’ and ‘volatile’ together. 

 

FastBit Embedded Brain Academy Courses

Click here: https://fastbitlab.com/course1

 

FastBitLab

The FastBit Embedded Brain Academy uses the power of internet to bring the online courses related to the field of embedded system programming, Real time operating system, Embedded Linux systems, etc at your finger tip with very low cost. Backed with strong experience of industry, we have produced lots of courses with the customer enrolment over 3000+ across 100+ countries.