1. Memory and Bus Architecture
The STM32 architecture is built around a multi-layer bus matrix that facilitates communication between the Cortex-M3 core and various peripherals. Key components include:
- I-Code Bus: Fetches instructions from the Flash memory.
- D-Code Bus: Accesses data (constants) in Flash or variables in SRAM.
- System Bus (S-Bus): Used for accessing peripheral registers.
- DMA Bus: Enables direct data transfer between peripherals and memory (SRAM/Flash) without CPU intervention. The Bus Matrix arbitrates between DMA and D-Code access to avoid conflicts.
2. Direct Register Access via Pointers
While the standard library is preferred, understanding direct register manipulation via pointers is essential for low-level debugging. To access a hardawre address, the address must be cast to a volatile pointer.
/* Example: Driving GPIOB pins high using absolute addresses */
#define DEV_GPIOB_ODR (*(volatile uint32_t *)(0x40010C0C))
void SetPortBHigh(void) {
// Casting a hex address to a pointer and dereferencing it
*(volatile uint32_t *)(0x40010C0C) = 0xFFFF;
// Using an encapsulated macro
DEV_GPIOB_ODR = 0xFFFF;
}
3. GPIO (General Purpose Input/Output)
GPIO pins are managed through a set of configuration and data registers. Each port (GPIOA, GPIOB, etc.) has its own instance of the GPIO_TypeDef structure.
| Register | Description |
|---|---|
| CRL / CRH | Configuration registers for low (0-7) and high (8-15) pins. Defines mode and speed. |
| IDR / ODR | Input and Output Data registers. IDR is read-only; ODR is read/write. |
| BSRR / BRR | Bit Set/Reset registers. Atomic operations to modify specific pins without affecting others. |
GPIO Operating Modes
- Input Floating: High impedance, used for external signals like USART RX.
- Input Pull-up/down: Internal resistors ensure a default high or low state; ideal for buttons or EXTI.
- Analog Input: Disables digital input circuitry for ADC sampling to save power.
- Push-Pull Output: Actively drives the pin high or low; high current sourcing/sinking.
- Open-Drain Output: Pin is connected to ground or floating. Requires external pull-up for high logic; useful for "wired-AND" buses like I2C.
- Alternate Function (AF): Routes the pin to an internal peripheral (USART, SPI, etc.).
Pin Remapping (AFIO)
STM32 allows remapping peripheral pins to different physical I/O locations via the AFIO (Alternate Function I/O) controller. This is useful for resolving PCB routing conflicts.
// Enable AFIO clock and remap USART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
4. RCC (Reset and Clock Control)
The RCC manages the clock tree. Most peripherals are disabled by default to save power and must be manually enabled.
- HSE: High-Speed External (usually an 8MHz crystal). Reliable for high-speed operation.
- HSI: High-Speed Internal (8MHz RC oscillator). Less precise, used as a backup.
- PLL: Phase-Locked Loop. Multiplies input clocks to achieve high frequencies (e.g., 72MHz).
Typical clock configuration for a 72MHz system clock (SYSCLK):
void InitializeSystemClock(void) {
RCC_DeInit(); // Reset RCC to default
RCC_HSEConfig(RCC_HSE_ON);
if (RCC_WaitForHSEStartUp() == SUCCESS) {
// Flash latency configuration for 72MHz
FLASH_SetLatency(FLASH_Latency_2);
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
// HCLK = 72MHz, PCLK2 = 72MHz, PCLK1 = 36MHz
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
// PLL = HSE * 9 = 72MHz
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while (RCC_GetSYSCLKSource() != 0x08);
}
}
5. Interrupt Management (NVIC and EXTI)
The NVIC (Nested Vectored Interrupt Controller) handles interrupt priorities. Priorities are split into Preemption (can interrupt an ongoing ISR) and Sub-priority (determines order if multiple interrupts trigger simultaneously).
External Interrupts (EXTI)
EXTI allows GPIO pins to trigger interrupts on rising, falling, or both edges. All GPIOs are mapped to EXTI lines (e.g., PA0, PB0, PC0 all share EXTI Line 0).
void ConfigureEXTI_Line0(void) {
EXTI_InitTypeDef exti_init;
// Connect EXTI Line 0 to Port A
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
exti_init.EXTI_Line = EXTI_Line0;
exti_init.EXTI_Mode = EXTI_Mode_Interrupt;
exti_init.EXTI_Trigger = EXTI_Trigger_Falling;
exti_init.EXTI_LineCmd = ENABLE;
EXTI_Init(&exti_init);
}
6. Timers and SysTick
SysTick (Core Timer)
SysTick is a 24-bit down-counter built into the Cortex-M3 core, primarily used for OS ticks or precise delays.
// 1ms delay using SysTick
void Delay_ms(uint32_t n) {
SysTick_Config(SystemCoreClock / 1000);
for (uint32_t i = 0; i < n; i++) {
while (!((SysTick->CTRL) & (1 << 16)));
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
Basic Timers (TIM6/TIM7)
These timers are used for simple time-base generation. They lack input/output channels.
- PSC (Prescaler): Divides the clock frequency. Frequency = CK_INT / (PSC + 1).
- ARR (Auto-Reload Register): Defines the period. An update event occurs when the counter reaches ARR.
7. Communication Interfaces
USART (Universal Synchronous Asynchronous Receiver Transmitter)
Supports full-duplex asynchronous communication. Standard configuration includes baud rate, word length (8 or 9 bits), and stop bits.
I2C (Inter-Integrated Circuit)
A two-wire, multi-master/multi-slave bus. STM32 I2C hardware handles start/stop codnitions and address matching.
- SCL: Serial Clock.
- SDA: Serial Data. Both lines must be Open-Drain.
SPI (Serial Peripheral Interface)
A high-speed synchronous interface using four lines: SCK, MOSI, MISO, and NSS (Chip Select).
- CPOL (Clock Polarity): SCK level when idle.
- CPHA (Clock Phase): Data sampling edge (first or second).
8. PWM and Servo Control
Pulse Width Modulation (PWM) is generated by comparing the timer counter (CNT) with a Capture/Compare Register (CCR). For standard servos, a 50Hz frequency (20ms period) is required, with pulse widths typically between 0.5ms (0°) and 2.5ms (180°).
// Example setup for 50Hz PWM on a 72MHz clock
// PSC = 71 -> 1MHz counting frequency
// ARR = 19999 -> 20ms period
void PWM_Servo_Config(void) {
TIM_TimeBaseInitTypeDef tim_base;
tim_base.TIM_Prescaler = 71;
tim_base.TIM_Period = 19999;
tim_base.TIM_ClockDivision = 0;
tim_base.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &tim_base);
}