# Accessing stm32 registers via structs in GCC

I'm trying to roll my own USB device using an STM32F103 blue pill board in C with the GCC arm-none-eabi compiler, but I've run into some strange behavior that I'm trying to understand.

I've created a struct that models the endpoint buffer descriptor tables associated with the USB peripheral:

typedef struct _EP_BUF_DSCR {
uint32_t COUNT_TX;
uint32_t COUNT_RX;
} EP_BUF_DSCR;


And I've created a pointer to one of these structs and set it to address the beginning of the USB packet memory area of the MCU:

EP_BUF_DSCR *EP0_DSCR = (EP_BUF_DSCR *) 0x40006000;


So now I can set the ADDR_TX and ADDR_RX fields by just dereferencing the appropriate fields and assigning them values. However, when I try to do this with the COUNT_RX field, it appears to have no effect:

// debugger memory view shows register as set to 0 after running this:
EP0_DSCR->COUNT_RX = 0x8400;


But I am able to change the value in this register by creating a pointer directly to it and dereferencing it:

// debugger memory view shows register set to correct value after running:
*((uint32_t *)(0x40006000 + 12)) = 0x8400;


Could someone provide some insight as to why this happens? Have I done something bone-headed, or am I unintentionally relying on undocumented compiler behavior?

Typically memory mapped hardware special function registers must be declared volatile so that the hardware knows that something may need or alter their state beyond the local thread of execution - otherwise it is free to cache values in registers or neglect to even compute apparently unused ones at all.

On the STM32, certain SFRs may also be restricted to a particular width of access.

If what you are trying to do is legitimate, there is likely already a suitable definition within the vendor (or alternate) driver or HAL files. And even if there isn't, you can learn a lot by reading those to see how something simple like a GPIO block is declared.

Of course it's also possible that there is something specifically wrong with the particular operation you are trying to perform - for example, that might not be writable, or only be meaningfully writable when the hardware is in a particular state, which the debugger lucks out in achieving while your program usually does not.

• Ah! Declaring it volatile seems to have done the trick. Thank you! – user3513491 Aug 14 '17 at 1:35
• @user3513491 why don't use CMSIS - you have all declared already. There is no need to reinvent the wheel. None CMSIS in not HAL so you can do your bare register programming. – P__J__ Aug 14 '17 at 20:58

There are a couple things that could be going on here:

1. As noted by Chris Stratton, you need to declare EP0_DSCR (and any other pointers to hardware registers) as volatile.

2. Do you have APB1 and the USB peripheral powered on and clocked appropriately? The registers may not respond properly if the peripherals aren't running. In particular, the USB peripheral needs to be clocked at 48 MHz, and APB1 needs to be clocked at ≥8 MHz.

3. Do you have the CAN peripheral enabled? Both CAN and USB share a memory buffer, so you can't use both at the same time.