What are the downsides of using STM32 HAL in production?
The most impactful downside is code size. The HAL is designed for generality — every function handles every possible configuration and checks for error conditions that may not apply to your use case. A simple HAL_UART_Transmit() call includes parameter validation, lock acquisition, timeout management, and status flag checking. On a flash-constrained part like the STM32F030 (16 KB flash), the HAL UART driver plus its dependencies can consume 3-4 KB. Multiply that across five peripherals and half your flash is gone before you write any application logic.
Hidden timeouts are a more subtle problem. Many HAL functions use polling loops with timeout parameters — HAL_SPI_Transmit(&hspi1, data, len, HAL_MAX_DELAY) will spin indefinitely if the SPI peripheral is misconfigured. Even with a finite timeout, a 100 ms HAL timeout inside an ISR or a tight control loop conflicts with watchdog timers and real-time deadlines. The HAL's timeout mechanism is based on HAL_GetTick(), which depends on the SysTick interrupt — if you call a HAL function from a higher-priority ISR, HAL_GetTick() never increments and the timeout never expires, creating a deadlock.
Abstraction hiding timing bugs is the third concern. The HAL abstracts away the exact sequence and timing of register writes, making it difficult to reason about what happens at the hardware level. For example, HAL_ADC_Start_DMA() configures the DMA, enables the ADC, and starts conversion — but the exact ordering and any inserted delays are buried in the implementation. If a cache coherency issue or bus contention bug appears, debugging requires stepping through HAL source code that you did not write and may not fully understand. In a bare-metal implementation, every register write is visible in your code and the timing is explicit.
Source: Driver Design Q&A
