I am learning STM32 microcontroller programming by using a STM32F4DISC-1 board. I am trying to blink an LED connected to PD14 pin of the STM32F407vg controller. As part of the exercise , I have to set the GPIO port mode register where the 28th bit need to be set as output so that I can place a 1 to light the LED.
The pointer variable for the port mode register is defined as below , its initialized to base address + offset.
uint32_t * pPortDModeReg = (uint32_t*)0x40020C00;
to set the 28th bit to 1 , I use the following statement to clear the 28th and 29th bits before setting the 28th bit to 1.
*2.configure the mode the IO pin as output
clear the 28th and 29th bits by negated & ing with 011 shifted left by 28 bits*
*pPortDModeReg &= ~(3 << 28);
I get the following error in gcc based IDE STM32CubeIDE.
../Src/main.c:36:4: **error: invalid operands to binary * (have 'int' and 'uint32_t ' {aka 'long unsigned int '}) 36 |
*pPortDModeReg &= ~(3 << 28);
I even tried casting the RHS expression as (uint32_t*)like below , still the error persists.
pPortDModeReg &= (uint32_t*)(~(3 << 28));
**
But for the instructor of the tutorial video , this statement builds without errors.
Is any compiler setting playing with me ? , please help.
I give below the main.c file for reference
#include <stdint.h>
// This program has been modified to blink the RED LED connected to
PD14 pin and with bitwise operators
int main(void) {
uint32_t *pClkCtrlReg = (uint32_t*)0x40023830;
uint32_t *pPortDModeReg = (uint32_t*)0x40020C00;
uint32_t *pPortDOutReg = (uint32_t*)0x40020C14;
//1. Enable clock for GPIOD peripheral in AHB1ENR ( SET the 3rd bit position)
*pClkCtrlReg |= (1<<3) /* Surprisingly , this line doesn't cause error */
/*configure the mode to set the GPIO pin 14 as output
// clear the 28th and 29th bits
*pPortDModeReg &= ~(3 << 28); /*This line causes error*/
// set the 28th bit
*pPortDModeReg |= (1> << 28);
1 Answer 1
Start by figuring out what the compiler is trying to tell you. What does "error: invalid operands to binary *" mean? In C programming we speak of unary, binary and ternary operands, depending on if they take 1, 2 or 3 operands. So the binary * operator would be the *
with two operands, also known as multiplication. Now why does the compiler think you are doing multiplication?
pClkCtrlReg |= (1<<3) / Surprisingly , this line doesn't cause error */
It does. The missing semicolon makes the compiler thinking you are writing a single expression on multiple lines:
*pClkCtrlReg |= (1<<3)*pPortDModeReg &= ~(3 << 28); // this is what the compiler sees
And therefore it thinks the *
in (1<<3)*pPortDModeReg
is multiplication, the binary * operator instead of the unary * for indirection.
Furthermore, here is a collection of other bugs:
uint32_t *pClkCtrlReg = (uint32_t*)0x40023830;
Whenever using hardware registers you absolutely must add volatile
qualifiers, or you might get all kinds of strange optimization bugs. See How to access a hardware register from firmware? From that link we can also learn that all integer constants should be written with an U
or u
in the end, to prevent getting signed integer types by accident.
This leads us to another bug source 1 << 28
. This line works but mostly out of luck. 1
is an integer constant and it has type signed int
. As long as it is a positive one we can shift up to bit 30. Should we instead attempt something like 1 << 31
to set the MSB, we invoke undefined behavior bugs, because now we are shifting data into a sign bit of the unnamed int
formed by 1
. Yet again, we must write 1u << 31
to avoid bugs.
Why only the left operand? Well there's no harm in writing 1u << 31u
but the shift operators are a special case in C since they always give the result in the same type as the left operand. Normal C expressions would use implicit type promotion to make both operands the same type and then the result would be of that type.
int main (void)
is senseless in "bare metal" and RTOS MCU applications. These are so-called freestanding systems and they never return from main(). Because whom would they return to? It is common that all freestanding systems uses the implementation-defined form void main (void)
instead. (If you are using gcc, compile with -ffreestanding
when coding embedded systems.)
-
\$\begingroup\$ Thanks @Lundin for the detailed answer, very enlightening indeed. \$\endgroup\$Madavan Viswanathan– Madavan Viswanathan2021年06月01日 12:06:13 +00:00Commented Jun 1, 2021 at 12:06
;
, confusing the parser, and causing an error. \$\endgroup\$