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).
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.
| Adresse | Bus | Périphérique |
|---|---|---|
| 0xE000E000 | Cortex-M | NVIC, SysTick, SCB (Cortex-M Core) |
| 0x40023800 | AHB1 | RCC — Reset & Clock Control |
| 0x40023C00 | AHB1 | FLASH interface (latence, prefetch) |
| 0x40020000 | AHB1 | GPIOA |
| 0x40020400 | AHB1 | GPIOB |
| 0x40020800 | AHB1 | GPIOC |
| 0x40026000 | AHB1 | DMA1 |
| 0x40026400 | AHB1 | DMA2 |
| 0x40011000 | APB2 | USART1 |
| 0x40004400 | APB1 | USART2 |
| 0x40013000 | APB2 | SPI1 |
| 0x40005400 | APB1 | I2C1 |
| 0x20000000 | SRAM | RAM interne |
| 0x08000000 | Flash | Code firmware |
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 |= (1U << 0) active GPIOA.APB1ENR bit 17.// 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);
Chaque port GPIO (A à K) a son propre bloc de registres identique. Configuration en deux temps : mode + caractéristiques électriques.
00 = Input · 01 = Output · 10 = Alternate Function (UART, SPI...) · 11 = Analog (ADC)
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.// 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 !)
UE=Enable UART · TE=Transmit Enable · RE=Receive Enable · RXNEIE=interruption sur octet reçu · TXEIE=interruption sur buffer TX vide
// 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); } }
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).
// 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)
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.
// 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 }
Les timers STM32 sont extrêmement flexibles : génération PWM, capture d'événements, base de temps précise, IRQ périodique.
// 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
MSTR mode maître · BR[2:0] diviseur d'horloge · CPOL/CPHA mode (0–3) · SSI/SSM NSS software · SPE enable
TXE buffer TX vide · RXNE octet reçu · BSY transfert en cours · OVR overrun
PE Peripheral Enable · START condition START · STOP condition STOP · ACK acquittement · SWRST reset logiciel
SB Start bit envoyé · ADDR adresse envoyée · BTF octet transféré · RXNE/TXE · AF NACK · MSL master mode
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.