STM32 cannot reach individual registers and pins as PIC
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-&gt;CR1 |= I2C_CR1_PE;



  Let's say I want to set PA2. Do I need to GPIOA-&gt;ODR = 0b00000010; I am not sure how can I make it bitwise, something like this: GPIOA -&gt; |= 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-&gt;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-&gt;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-&gt;BSRR = GPIO_PIN_2 | (GPIO_PIN_4 &lt;&lt; 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)