Reset Mechanisms
STM32 microcontrollers support three primary reset categories: system reset, power-on reset, and backup domain reset. System reset encompasses five different reset sources, with the NRST pin reset being the most commonly used method in minimum system designs. This hardware reset triggers when the NRST pin is pulled low, resetting all major components except for the backup domain.
The remaining four system reset sources are software-based: Independent Watchdog (IWDG) reset, Window Watchdog (WWDG) reset, and software reset triggered by the SW bit in the Application Interrupt and Reset Control Register. Low-power mode reset completes the list and becomes relevant in battery-powered applications where the device transitions through various power states.
Clock Architecture
STM32F103 devices provide five distinct clock sources, though only three can serve as the system clock (SYSCLK): HSE (High-Speed External), HSI (High-Speed Internal), and PLL (Phase-Locked Loop). The two low-speed sources—LSI and LSE—are reserved for RTC real-time clock operations and IWDG watchdog functionality.
The HSE oscillator requires an external crystal, typically 8MHz in most minimum system configurations. The HSI is an internal 8MHz RC oscillator that, while convenient due to not requiring external components, exhibits frequency drift that makes it unsuitable for precision timing applications. The PLL multiplier accepts input from either HSE or HSI and can generate frequencies up to 72MHz for the system clock.
Clock Tree Analysis
The clock tree diagram in the reference manual illustrates how these five sources distribute clock signals throughout the microcontroller. For achieving 72MHz operation, the signal path flows from the external 8MHz crystal through HSE, enters the PLL multiplier configured for 9x multiplication, and emerges as the 72MHz SYSCLK. This path requires properly configured control registers in the Reset and Clock Control (RCC) peripheral.
The clock configuration register (CFGR) contains multiplexers that select between these clock sources, while the control register (CR) manages enable bits and status flags for each oscillator. Understanding the register layout is essential for manual clock configuration.
MCU Startup Sequence
Upon power-up, the STM32 begins execution at the Reset_Handler label located in the startup assembly file. This handler calls SystemInit() before branching to the user application's main() function, ensuring the clock system reaches the desired operating frequency before any application code executes.
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
This sequence means the clock configuration happens automatically during startup, though the implementation details differ significantly between the Standard Peripheral Library and HAL library approaches.
SystemInit() Functon Deep Dive
The SystemInit() function establishes default clock configurations for debugging purposes. It resets the RCC_CR register's HSION bit first, though this is technically redundant since this bit resets to 1 by default. The function then clears multiple configuration fields in the CFGR register, preparing them for fresh setup.
void SystemInit (void)
{
RCC->CR |= (uint32_t)0x00000001;
RCC->CFGR &= (uint32_t)0xF8FF0000;
RCC->CR &= (uint32_t)0xFEF6FFFF;
RCC->CR &= (uint32_t)0xFFFBFFFF;
RCC->CFGR &= (uint32_t)0xFF80FFFF;
SetSysClock();
}
The final operation calls SetSysClock(), which examines preprocessor definitions to determine the target system frequency and delegates to the appropriate configuration function.
SetSysClockTo72() Implementation
This function orchestrates the complete clock configuration process. It begins by enabling the external high-speed oscillator and polling its ready flag, implementing a timeout mechanism to handle oscillator failure gracefully.
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
Once HSE stabilizes, the function configures FLASH memory wait states. At 72MHz, the flash requires 2 wait states to operate reliably, which the ACR (Access Control Register) manages through the LATENCY field.
FLASH->ACR |= FLASH_ACR_PRFTBE;
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
The peripheral bus prescalers then divide down the system clock appropriately: AHB runs at full speed, APB2 at full speed, and APB1 at half speed (limiting its maximum to 36MHz).
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
The PLL configuration clears previous settings and establishes HSE as the PLL source with a 9x multiplication factor, yielding 72MHz output.
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
After enabling the PLL and waiting for its lock flag, the function switches the system clock source to PLL and verifies the switch completes successfully. If HSE fails to initialize, the system defaults to the internal 8MHz HSI oscillator.
Standard Library Versus HAL Library Approach
Both libraries follow the same startup sequence of Reset_Handler calling SystemInit(), but they differ substantially in how SystemInit() operates. The Standard Peripheral Library's SystemInit() automatically calls SetSysClock() to configure 72MHz operation, while the HAL library's version omits this step entirely, requiring explicit clock configuration in user code.
The HAL approach requires manually initializing the clock in main():
int main(void)
{
HAL_Init();
Stm32_Clock_Init(RCC_PLL_MUL9);
while(1) { }
}
The custom clock initialization function uses HAL's structured initialization approach with OscInit and ClkInit structures:
void Stm32_Clock_Init(u32 PLL)
{
RCC_OscInitTypeDef RCC_OscInitStructure;
RCC_ClkInitTypeDef RCC_ClkInitStructure;
RCC_OscInitStructure.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStructure.HSEState = RCC_HSE_ON;
RCC_OscInitStructure.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStructure.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStructure.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStructure.PLL.PLLMUL = PLL;
HAL_RCC_OscConfig(&RCC_OscInitStructure);
RCC_ClkInitStructure.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK |
RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStructure.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStructure.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStructure.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStructure.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStructure, FLASH_LATENCY_2);
}
Both approaches ultimately manipulate the same CR and CFGR registers—the distinction lies purely in abstraction level. The Standard Library offers direct register manipulation, while the HAL provides type-safe structures that reduce errors but introduce additional function call overhead.
Register-Level Fundamentals
Understanding the underlying register behavior clarifies both approaches. The CR register's HSEON bit (bit 16) enables the external oscillator, while HSERDY (bit 17) indicates stability. The CFGR register's SW bits (bits 0-1) select the active system clock source, and SWS bits (bits 2-3) report which source currently drives SYSCLK. The PLLMUL field (bits 18-21) controls the multiplication factor, with 0x07 representing 9x multiplier.
Peripheral clock dividers occupy bits 8-15, with HPRE controlling AHB division, PPRE1 managing APB1 division, and PPRE2 handling APB2 division. These divider settings determine the maximum achievable peripheral bus frequencies, with APB1 capped at 36MHz due to hardware limitations.
When the HSE crystal fails to start—perhaps due to a defective component or improper circuit design—the startup routine's timeout mechanism detects this condition and falls back to HSI. This graceful degradation prevents the system from hanging indefinitely, though the resulting 8MHz operation may be insufficient for certain applications requiring precise timing or communication speeds.
Flash wait state configuration becomes critical at higher frequencies. The STM32F103 flash memory cannot respond instantly to CPU requests at 72MHz, requiring the insertion of wait cycles. The LATENCY field in the FLASH_ACR register specifies these wait states, with a value of 2 required for 48MHz to 72MHz operation. The prefetch buffer, when enabled, improves performance by reading ahead in the instruction stream.