# Getting error when setting GPIO port mode register

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.

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);

• Could you please post a minimal main.c file that causes this error? Jun 1, 2021 at 9:54
• You need to understand your compiler errors, it says it has invalid operands to the binary operation, being an int and a uint32_t. Your cast is a mess and not needed. Instead you have to tell the compiler that your literals are also unsigned, so use 3UL<<28 - this makes your literal an unsigned long and you have unsigned and unsigned for the operator and it should work. Jun 1, 2021 at 9:57
• The line with the comment "surprisingly , this line doesn't cause error" is missing a ;, confusing the parser, and causing an error.
– Mat
Jun 1, 2021 at 10:32
• I think you are following a wrong tutorial and starting from the wrong end. Nobody should have to write variables that point to correct memory addresses. Normally you would just use manufacturer provided header and simply use the register names provided in there. Also you don't say which compiler you use and unless you are using exact same compiler version with same settings you might get errors. Jun 1, 2021 at 10:44
• @Lundin I get the point. However the OP is using ST microcontrollers with ST IDE tools that use GCC compiler. Using those tools you should be able to blink LEDs under RTOS in 15 mins of installing them. With ST provided headers already included for you. Jun 1, 2021 at 11:48

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.)

• Thanks @Lundin for the detailed answer, very enlightening indeed. Jun 1, 2021 at 12:06