Home | Projects | Notes > C Programming > Using const
& volatile
Type Qualifiers Together
const
& volatile
Type Qualifiers Together
const
and volatile
TogetherBoth const
and volatile
qualifiers can be used when declaring a variable depending on what you want to achieve. For example,
xxxxxxxxxx
101uint8_t volatile *const pReg = (uint8_t *)0x40000000;
2 // pReg is a const pointer to a volatile data
3 // 1. From pReg's perspective, data in 0x40000000 is not modifiable.
4 // 2. Data in 0x40000000 may undergo unexpected changes so don't optimize read/write
5 // operations on this data using pReg.
6
7uint8_t const volatile *const pReg = (uint8_t *)0x40000000;
8 // pReg is a const pointer to a volatile const data
9 // 1. Data pointed to by pReg may undergo unexpected changes,
10 // but the programmer must not change the data present at this address.
L7 - How to interpret
const volatile
?
const
is for the programmer, not to modify the data usingpReg
.
volatile
is for the compiler not to optimize the read/write operation on this address.
Embedded program application:
Case of reading from read-only buffer or address which is prone to unexpected change of data:
xxxxxxxxxx
11uint8_t const volatile *const pReg = (uint8_t *)0x40000000;
Read-only, so from the programmer's (
pReg
's) perspective the data must not be modifiable (const
), but the data can be changed any time by the external world.
Such a read-only buffer may include "input data register of a peripheral", or a "shared memory from which you are supposed to read-only", etc.
volatile
in an Embedded ProgramWhen accessing memory-mapped peripheral registers of the microcontrollers
Following is the code snippet from the "pin_read" exercise.
xxxxxxxxxx
141...
2uint32_t *pPortAInReg = (uint32_t *)0x40020010;
3...
4
5while (1)
6{
7 // 3. read PA0 input status
8 uint8_t pinStatus = (uint8_t)(*pPortAInReg & 0x1);
9
10 if (pinStatus)
11 *pPortDOutReg |= (1 << 12);
12 else
13 *pPortDOutReg &= ~(1 << 12);
14}
This code works fine with no optimization, but when you do -O2
optimization the code will not work as intended. This is because the compiler executes L4 only once and then ignores it afterwards. The compiler assumes that *pPortAInReg
will never change.
Now, you have to tell the compiler that the data pointed to by this pointer pPortAInReg
may change at any time, so not to do any optimization on "data read" and "data write" operations using this pointer. This can be done by using volatile
keyword as follows:
xxxxxxxxxx
51uint32_t volatile *const pPortAInReg = (uint32_t *)0x40020010; // 'const' to guard the ptr
2
3// due to the nature of GPIOx IDR, 'const' can be added to the data part as well
4// this is the general way to declare a pointer pointing to a 'read-only' memory mapped registers
5uint32_t const volatile * const pPortAInReg = (uint32_t *)0x40020010;
As a rule of thumb, whenever you are accessing memory mapped peripheral registers, use volatile
generously. Use it for "data", not for the "pointer". For example:
xxxxxxxxxx
51uint32_t volatile *const pClkCtrlReg = (uint32_t *)0x40023830;
2uint32_t volatile *const pPortAModeReg = (uint32_t *)0x40020000;
3uint32_t volatile *const pPortAInReg = (uint32_t *)0x40020010;
4uint32_t volatile *const pPortDModeReg = (uint32_t *)0x40020C00;
5uint32_t volatile *const pPortDOutReg = (uint32_t *)0x40020C14;
When a global variable is used to share data between the main code and an ISR code.
Following is the "button_press_ISR" exercise.
xxxxxxxxxx
561
2
3
4// global shared variable between main code and ISR
5uint8_t volatile g_button_pressed = 0;
6uint32_t g_button_press_count =0;
7
8void button_init(void);
9
10uint32_t volatile *const pEXTTIPendReg = (uint32_t*) (0x40013C00 + 0x14);
11uint32_t volatile *const pClkCtrlReg = (uint32_t*) (0x40023800 + 0x30);
12uint32_t volatile *const pClkCtrlRegApb2 = (uint32_t*) (0x40023800 + 0x44);
13uint32_t volatile *const pGPIOAModeReg = (uint32_t*) (0x40020000 + 0x00);
14uint32_t volatile *const pEXTIMaskReg = (uint32_t*) (0x40013C00 + 0x00);
15uint32_t volatile *const pEXTTIEdgeCtrlReg = (uint32_t*) (0x40013C00 + 0x08);
16uint32_t volatile *const pNVICIRQEnReg = (uint32_t*) 0xE000E100;
17
18int main(void)
19{
20 button_init();
21
22 while(1)
23 {
24 // dissable interrupt
25 *pEXTIMaskReg &= ~( 1 << 0);
26
27 if(g_button_pressed){
28 // some delay until button debouncing gets over
29 for(uint32_t volatile i=0;i<500000/2;i++);
30 g_button_press_count++;
31 printf("Button is pressed : %lu\n",g_button_press_count);
32 g_button_pressed = 0;
33 }
34
35 // enable interrupt
36 *pEXTIMaskReg |= ( 1 << 0);
37 }
38}
39
40void button_init(void)
41{
42 *pClkCtrlReg |= (1 << 0);
43 *pClkCtrlRegApb2 |= (1 << 14);
44 *pEXTTIEdgeCtrlReg |= (1 << 0);
45 *pEXTIMaskReg |= (1 << 0);
46 *pNVICIRQEnReg |= (1 << 6);
47}
48
49/* button interrupt handler */
50void EXTI0_IRQHandler(void)
51{
52 // set this flag on button press
53 g_button_pressed = 1;
54
55 *pEXTTIPendReg |= ( 1 << 0);
56}
L27 - If
volatile
keyword is not used in L5, the compiler will assume, when compiled with the higher optimization level, thatg_button_pressed
will always be 0 since nowhere in the code this global variable gets changed. Therefore, make sure to tell the compiler thatg_button_pressed
may change at any time by the ISR, so not to do any optimization on "data read" and "data write" operations on that global variable.L6 -
volatile
not necessary since this global variable does not share data between the main code and the ISR code.L29 - If
volatile
keyword is not used fori
, the compiler, when compiled with the higher optimization level, is likely to remove (ignore) this loop (i.e., delay for the button press debouncing). This is because the compiler is not aware of our intention and it just thinks that the repetitive operation oni
is meaningless and redundant (i.e., not affecting any other part of the code) only to slow down the program execution. So, tell the compiler not to do any optimization oni
.Notice that
const
has been used along withvolatile
to prevent the pointers from being changed by accident.
Nayak, K. (2022). Microcontroller Embedded C Programming: Absolute Beginners [Video file]. Retrieved from https://www.udemy.com/course/microcontroller-embedded-c-programming/