# STM32 cannot reach individual registers and pins as PIC

I used to work with PIC and now I need to use STM32 chip. I was using HAL library but it gave me some problems so now I want to use it without HAL_Library. There are some questions I could not get any answer.

1. In the datasheet of any PIC there are registers and each bit of those registers are showed and explained. But in the datasheet of STM32 chips I could not find any registers. How can I find all registers in STM32 chips and specific bits of these register. For example I need to set PE bit in I2Cx_CR1 register but I have no idea which bit is the PE bit...

2. This one might be simple but I couldn't figure it out. Maybe it is not possible in STM32 I'm not sure. Let's say I want to set PA2. Do I need to GPIOA->ODR = 0b00000010;I am not sure how can I make it bitwise, something like this: GPIOA -> |= PA2 (?) What should I put instead of PA2 in this case?

3. Is it possible to use CubeMX and then do the rest in classical way like I mentioned before using individual registers without using HAL? Or if I create the project by using Cube then I have to use HAL library?

• Every MCU manufacturer provides 2 main documents. The datasheet which is for the hardware designer, and the user manual which is for the software designer. Aug 28 '19 at 14:57

In the datasheet of any PIC there are registers and each bit of those registers are showed and explained. But in the datasheet of STM32 chips I could not find any registers. How can I find all registers in STM32 chips and specific bits of these register.

The Datasheet contains the external description of the MCU, i.e. pin mappings, electrical characteristics, package dimensions.

The register descriptions are in the reference manual, that's where the function of each bit in each peripheral register is explained.

Then there is a programming manual where the ARM core is described, including the core peripheral registers like the interrupt controller or the SysTick timer.

For example I need to set PE bit in I2Cx_CR1 register but I have no idea which bit is the PE bit...

Each usable register bit has a definition in the CMSIS header for your controller. They have predictable names, e.g. to set the PE bit of the CR1 register on the first I2C controller, you can write

I2C1->CR1 |= I2C_CR1_PE;


Let's say I want to set PA2. Do I need to GPIOA->ODR = 0b00000010; I am not sure how can I make it bitwise, something like this: GPIOA -> |= PA2 (?) What should I put instead of PA2 in this case?

GPIO data register bits have their definitions in the CMSIS header too, using the same convention as above, so it's possible to write

GPIOA->ODR |= GPIO_ODR_OD2; // set PA2 to 1


You can of course assume that the layout of the bits are the same across the IDR, ODR and BSRR registers, and use the simplified GPIO_PIN_2 definition from the HAL headers.

You can use the GPIOx->BSRR register to set and reset some pins without bitwise instructions, e.g. to set PA2, reset PA4, and let the rest of the pins alone, do

GPIOA->BSRR = GPIO_PIN_2 | (GPIO_PIN_4 << 16);


it comes handy when some pins of a GPIO port are controlled by interrupt handlers, and others by the main code.

Is it possible to use CubeMX and then do the rest in classical way like I mentioned before using individual registers without using HAL? Or if I create the project by using Cube then I have to use HAL library?

Sure, I mix these approaches all the time. Just mind that whenever you re-generate the code from CubeMX, everything outside the USER CODE BEGIN / USER CODE END blocks will be overwritten. Once you got the HAL part right, the code is all yours.

The HAL driver for each peripheral is self-contained, you can even use HAL functions for one peripheral, LL for another, StdPeriph for the third, and direct register access for the fourth, or initialize one peripheral with HAL and access it through registers afterwards. (I had an actual project where some peripherals were initialized by HAL, legacy code taken from older code used StdPeriph, and new code accessed the registers directly)

• What about if the register I want to use has 2 pins to configure? Let's say GPIOx_CRH register. In this register I need to make bits 10 and 11 to 01. How can I do that? In pic it was easy to reach bits of each register: xxxx.B10 for example would let you to use B10 so I could make xxxx.B10 =0; xxxx.B11=1; But in here I m confused a little bit. Aug 29 '19 at 7:07
• Adjusting related configuration bits is not among the best practices, because then there is a window of time when the register is neither in the previous nor in the desired state, but in a third one. The recommened way of doing it is GPIOx_CRH = (GPIOx_CRH & ~GPIO_CRH_CNF10_Msk) | GPIO_CRH_CNF10_0; or GPIOx_CRH = (GPIOx_CRH & ~GPIO_CRH_CNF10_Msk) | (0b01 << GPIO_CRH_CNF10_Pos); If you want to do it anyway, bit-level access to peripheral registers is possible with bit-banding. Aug 29 '19 at 8:57
• To check a single bit, do if(GPIOB->ODR & GPIO_PIN_6). Bit-banding would work here too. Note that ODR holds the output state, the actual signal level of the pin is in IDR, which can be different for open-drain outputs. Aug 29 '19 at 9:12
• 30<<31 is 0b11110 shifted by 30 bit positions to the left, the result does not fit in a 32-bit register. I think 0b11<<30 (or 3<<30) is what you want, or 0b11<<GPIO_CRL_CNF7_Pos using the CMSIS convention. Aug 29 '19 at 11:03
• No, they are different operators. & ~ clears a bit, ^ toggles it. Aug 29 '19 at 13:49

You are looking at the datasheet. It is not there. It is in the reference manual. For STM32, datasheet is mostly for package specific things like pinouts and electrical specifications (that's the only time I ever look at it anyways). User reference manual is for device common things like registers and peripherals which is almost everything else.

• This is one of the reasons I love Microchip's datasheets even though a lot of people hate them. They include everything from feature list to register definition to instruction set format to how the programming cycle work. Aug 29 '19 at 1:34
• @slebetman Which makes them bloated, for someone accustomed to the other way of working. If the core is the same, and the peripheral functionality is the same, and all that's different is that they have 50 variations of pinouts and what package, then you don't need 50 datasheets for each pinout and package variation where each one repeats the same information about the core/peripherals. Besides anything else, it virtually guarantees changes and errata aren't going to be filled in properly across all 50 copies of the same information. Do it once, well, and refer to it from elsewhere. Aug 29 '19 at 10:02
1. You should not load (only) the datasheet, but also the reference manual, which can be found for e.g. STM32F103C8T6 on the page with resources, see page https://www.st.com/en/microcontrollers-microprocessors/stm32f103c8.html#resource.

In the Reference Manual, RM008, you find it on page 774. If you have another STM, find the right Reference manual.

2. You can indeed use | (or), I'm not sure what is defined for PE (you can try if you have the I2C instance open in your editor, to find the definition, and you will probably find the values possible for the register).

3. You can only use instead of HAL (or combined, not sure), the so called LL (Low Level) drivers, which give you a more lower level interface to the drivers/peripherals. I don't have experience with it myself.

I also moved from PIC to STM, some things changed and you need to read datasheets to understand.

1. The "datasheet" of the STM only contains some information one peripherals, electrical values... Take a look ate the "Reference Manual" for the registers.

2. To set a GPIO, it is not that simple:

• You need to enable the GPIO bank in RCC registers
• Set the GPIO direction (MODER register)
• Set the output value (ODR register, each bit is a pin, PA2 will be GPIOA->ODR |= (1 << 2);

Some registers help the set/reset of the GPIO (BSRR register).

1. Use Stm32IDE, you can configure the project without HAL. It will includes CMSIS, some useful headers with all registers already defined.

What you are looking is the CMSIS (Cortex-M Software Interface Standard) libraries for your processor. These should be available from ST, and they make it possible to access and modify registers exactly as you describe. Meaningful constants should be defined for bit-banging all of the registers.

• To be precise, ST provide functions on top of CMSIS. CMSIS was designed so that the common parts of a Cortex-M system could be reused across multiple silicon vendors. Vendors then bolt their differentiating hardware in using the CMSIS API. The aim is one can access the C-M's registers in the same way using ST, TI, etc microcontrollers, as well as other things that would be useful independent of the vendor. Aug 28 '19 at 15:07