Post

STM32 Firmware Design Patterns

STM32 Firmware Design Patterns

This article explores essential design patterns and programming techniques commonly used in STM32F10x firmware development.

1. Robust Clock Stability Check

1
2
3
4
do {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
    StartUpCounter++;  
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

This pattern shows an elegant approach to clock stability verification:

  • Uses a timeout counter to prevent infinite waiting
  • Continuously monitors the ready flag
  • Combines timeout and status check in a single condition
  • Smart use of do-while to ensure at least one check

2. Careful Status Verification

1
2
3
4
5
if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
    HSEStatus = (uint32_t)0x01;
} else {
    HSEStatus = (uint32_t)0x00;
}

Instead of using the previous HSEStatus directly, the code:

  • Re-checks the register status for extra safety
  • Uses explicit casting to ensure type safety
  • Employs the RESET constant for better readability
  • Avoids potential race conditions in status checking

3. Smart Flash Configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL && !defined STM32F10X_HD_VL
    /* Enable Prefetch Buffer */
    FLASH->ACR |= FLASH_ACR_PRFTBE;
    /* Flash wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    
    #ifndef STM32F10X_CL
        FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;
    #else
        if (HSE_VALUE <= 24000000) {
            FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;
        } else {
            FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1;
        }
    #endif

This section demonstrates sophisticated configuration handling:

  • Uses nested preprocessor conditions for different device families
  • Enables prefetch buffer for performance optimization
  • Adjusts flash wait states based on clock frequency
  • Maintains clear register manipulation despite complex conditions

4. Clock Switch Verification

1
2
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x04) {
}

A simple but crucial check that:

  • Ensures clock switching is complete
  • Uses a busy-wait loop for timing-critical operations
  • Explicitly checks the status bits
  • Guarantees system stability before proceeding

Key Takeaways

  1. Defensive Programming
    • Multiple layers of status checking
    • Timeout mechanisms
    • Register re-verification
  2. Performance Optimization
    • Smart use of prefetch buffer
    • Dynamic flash wait states
    • Efficient register operations
  3. Hardware Abstraction
    • Device family conditional compilation
    • Clock frequency-based adjustments
    • Clear register manipulation patterns
This post is licensed under CC BY 4.0 by the author.