STM32 Bare-Metal Registres clés · Configuration · 16 min

Registres STM32
& Configuration

Vue d'ensemble des registres essentiels du STM32 — RCC, GPIO, USART, SPI, I2C, NVIC, SysTick, TIM. Mappings mémoire, bits clés, et séquences d'initialisation type. Références basées sur la famille STM32F4 (Cortex-M4).

Carte mémoire STM32F4

Tous les périphériques sont mappés à des adresses fixes. Le header CMSIS définit les pointeurs (RCC, GPIOA, USART1...) qui pointent directement vers ces zones.

// memory_map — adresses de base STM32F4
AdresseBusPériphérique
0xE000E000Cortex-MNVIC, SysTick, SCB (Cortex-M Core)
0x40023800AHB1RCC — Reset & Clock Control
0x40023C00AHB1FLASH interface (latence, prefetch)
0x40020000AHB1GPIOA
0x40020400AHB1GPIOB
0x40020800AHB1GPIOC
0x40026000AHB1DMA1
0x40026400AHB1DMA2
0x40011000APB2USART1
0x40004400APB1USART2
0x40013000APB2SPI1
0x40005400APB1I2C1
0x20000000SRAMRAM interne
0x08000000FlashCode firmware

RCC — Horloges

Le RCC contrôle toutes les horloges de la puce. Tout périphérique nécessite l'activation préalable de son horloge dans RCC, sinon les écritures dans ses registres sont ignorées silencieusement.

RCC_AHB1ENR
0x40023830 — AHB1 Peripheral Clock Enable
31res.
...reserved [30:23]
22DMA2EN
21DMA1EN
...
3GPIODEN
2GPIOCEN
1GPIOBEN
0GPIOAEN
Chaque bit active l'horloge d'un périphérique du bus AHB1. RCC->AHB1ENR |= (1U << 0) active GPIOA.
RCC_APB2ENR
0x40023844 — APB2 (bus rapide)
...
14SYSCFG
...
12SPI1EN
...
5USART6
4USART1EN
Active USART1, SPI1, ADC1 et autres périphériques rapides. Pour USART2 (sur APB1) : voir APB1ENR bit 17.
C · rcc_init.c
// Activer GPIOA, GPIOB, USART2 (sur APB1) et SPI1 (sur APB2)
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;

// PLL pour horloge système à 168 MHz (cas typique STM32F407)
// PLL : HSE 8 MHz → /M=8 → x N=336 → /P=2 = 168 MHz
RCC->CR    |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY));   // attendre stabilisation HSE

RCC->PLLCFGR = (8 << 0) | (336 << 6) | (0 << 16) | RCC_PLLCFGR_PLLSRC_HSE;
RCC->CR    |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY));

// FLASH : 5 wait states à 168 MHz
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | 5;

RCC->CFGR |= RCC_CFGR_SW_PLL;  // SYSCLK = PLL
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);

GPIO — Entrées/Sorties

Chaque port GPIO (A à K) a son propre bloc de registres identique. Configuration en deux temps : mode + caractéristiques électriques.

GPIOx_MODER
offset 0x00 — Mode register (2 bits/pin)
[31:30]MODER15
[29:28]MODER14
[...]
[11:10]MODER5
[...]
[1:0]MODER0
00 = Input · 01 = Output · 10 = Alternate Function (UART, SPI...) · 11 = Analog (ADC)
GPIOx_BSRR
offset 0x18 — Bit Set/Reset Register (atomique)
[31:16]BR — Reset bits
[15:0]BS — Set bits
Écrire 1 dans BSn (bits 0-15) met la broche à 1. Écrire 1 dans BRn (bits 16-31) la met à 0. Opération atomique — pas de risque entre lecture et écriture vs ODR.
C · gpio_init.c — séquence complète
// 1. Activer l'horloge GPIOA (cf. RCC ci-dessus)
// 2. Configurer PA5 en sortie push-pull
GPIOA->MODER  &= ~(0x3 << (5 * 2));   // effacer bits 11:10
GPIOA->MODER  |=  (0x1 << (5 * 2));   // 01 = output

GPIOA->OTYPER &= ~(1 << 5);            // 0 = push-pull (1 = open-drain)
GPIOA->OSPEEDR |= (0x2 << (5 * 2));   // vitesse moyenne
GPIOA->PUPDR  &= ~(0x3 << (5 * 2));   // pas de pull-up/down

// Allumer / éteindre / toggle de manière atomique
GPIOA->BSRR = (1U << 5);          // set PA5 à 1
GPIOA->BSRR = (1U << (5 + 16));     // reset PA5 à 0
GPIOA->ODR  ^= (1U << 5);          // toggle (non atomique !)

USART — Communication série

USARTx_CR1
offset 0x0C — Control Register 1
13UE
12M
10PCE
9PS
8PEIE
7TXEIE
6TCIE
5RXNEIE
3TE
2RE
UE=Enable UART · TE=Transmit Enable · RE=Receive Enable · RXNEIE=interruption sur octet reçu · TXEIE=interruption sur buffer TX vide
USARTx_BRR
offset 0x08 — Baud Rate Register
[15:4]MANTISSA
[3:0]FRACTION
USARTDIV = f_PCLK / (8 × (2-OVER8) × baud). Pour 115200 bauds à PCLK=42MHz : BRR = 0x16C.
C · usart_init.c
// Configuration USART2 à 115200 bauds, 8N1, sur PA2/PA3
// 1. Configurer PA2 (TX) et PA3 (RX) en Alternate Function 7
GPIOA->MODER  |= (0x2 << 4) | (0x2 << 6);  // 10 = AF
GPIOA->AFR[0] |= (7 << 8) | (7 << 12);   // AF7 = USART2

// 2. Baud rate
USART2->BRR = 0x16C;  // 115200 @ PCLK1=42MHz

// 3. Activer TX, RX, UART + interruption RX
USART2->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;

// 4. Envoi d'un octet bloquant
while (!(USART2->SR & USART_SR_TXE));  // attendre buffer TX vide
USART2->DR = 'A';

// 5. ISR — déclenchée par NVIC sur réception
void USART2_IRQHandler(void) {
    if (USART2->SR & USART_SR_RXNE) {
        uint8_t b = USART2->DR;     // lecture acquitte le flag
        ring_push(&rx_buf, b);
    }
}

NVIC — Interruptions

Le NVIC est le contrôleur d'interruptions intégré au Cortex-M. Chaque IRQ a un numéro fixe (IRQn) et une priorité configurable (0 = plus haute).

C · nvic_setup.c
// Activer l'IRQ USART2 dans le NVIC
NVIC_SetPriority(USART2_IRQn, 5);  // 0..15, 0 = max
NVIC_EnableIRQ(USART2_IRQn);

// Configurer les groupes de priorité (preemption / sub)
NVIC_SetPriorityGrouping(3);  // 4 bits preempt, 0 bits sub

// IRQn typiques (CMSIS) :
//   SysTick_IRQn = -1   (Cortex-M core)
//   EXTI0_IRQn   = 6    (GPIO externe ligne 0)
//   USART2_IRQn  = 38   (USART2)
//   TIM2_IRQn    = 28   (Timer 2)
⚠ priorité NVIC vs FreeRTOS

Sur FreeRTOS, seules les IRQ de priorité ≥ configMAX_SYSCALL_INTERRUPT_PRIORITY peuvent appeler les API ...FromISR. Une IRQ trop prioritaire qui appelle xQueueSendFromISR = comportement indéfini.

SysTick — Tick système

C · systick_init.c
// Timer 24 bits intégré au Cortex-M — base de temps universelle
void SysTick_Init_1ms(void) {
    // SystemCoreClock = 168 000 000 (Hz) → 168 000 cycles = 1 ms
    SysTick->LOAD = (SystemCoreClock / 1000U) - 1U;
    SysTick->VAL  = 0;
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk    // horloge CPU directe
                  | SysTick_CTRL_TICKINT_Msk     // IRQ activée
                  | SysTick_CTRL_ENABLE_Msk;     // démarrer
}

volatile uint32_t ms_count = 0;

void SysTick_Handler(void) {
    ms_count++;     // appelé toutes les 1 ms — base de temps globale
}

TIM — Timers généraux

Les timers STM32 sont extrêmement flexibles : génération PWM, capture d'événements, base de temps précise, IRQ périodique.

C · tim_pwm.c — PWM 1 kHz sur TIM2_CH1 (PA0)
// Activer l'horloge timer
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;

// Configurer PA0 en AF1 = TIM2_CH1
GPIOA->MODER  |= (0x2 << 0);
GPIOA->AFR[0] |= (1 << 0);

// Diviseur d'horloge : 84MHz → 84 kHz tick interne
TIM2->PSC = 84 - 1;
// Period = 84 → fréquence PWM = 84kHz / 84 = 1 kHz
TIM2->ARR = 84 - 1;

// Rapport cyclique 50%
TIM2->CCR1 = 42;

// Mode PWM 1 sur Channel 1
TIM2->CCMR1 = (6 << 4) | TIM_CCMR1_OC1PE;  // PWM mode 1 + preload
TIM2->CCER  = TIM_CCER_CC1E;                  // activer sortie
TIM2->CR1   = TIM_CR1_CEN;                    // démarrer

SPI & I2C — Référence rapide

SPI_CR1 — bits clés
MSTR mode maître · BR[2:0] diviseur d'horloge · CPOL/CPHA mode (0–3) · SSI/SSM NSS software · SPE enable
SPI_SR — flags status
TXE buffer TX vide · RXNE octet reçu · BSY transfert en cours · OVR overrun
I2C_CR1 — bits clés
PE Peripheral Enable · START condition START · STOP condition STOP · ACK acquittement · SWRST reset logiciel
I2C_SR1 / SR2
SB Start bit envoyé · ADDR adresse envoyée · BTF octet transféré · RXNE/TXE · AF NACK · MSL master mode
// règle générale STM32

Toujours dans cet ordre : 1) activer l'horloge RCC du périphérique, 2) configurer les GPIO en mode Alternate Function (AFR), 3) configurer le périphérique (CR/BRR/etc.), 4) activer enfin via le bit Enable. Sauter une étape = silence radio garanti.