When would you use HAL vs bare-metal register access? Give a real example.
The decision is rarely all-or-nothing — most production firmware is a hybrid. HAL (Hardware Abstraction Layer) accelerates prototyping and improves portability within a vendor family. If you are evaluating three different STM32 boards for a new product, HAL lets you swap targets by changing a build configuration rather than rewriting every peripheral driver. It is also the right choice for peripherals that are complex to configure but not performance-critical — USB device stacks, Ethernet MAC setup, or RTC calendar initialization, where getting all 15 registers right on the first try matters more than saving a few microseconds.
Bare-metal register access is essential for timing-critical hot paths and size-constrained targets. Consider a motor control loop running at 20 kHz on a Cortex-M4: the ADC conversion-complete ISR must read the result, compute a PID update, and write a new PWM duty cycle — all within 50 microseconds. Calling HAL_ADC_GetValue() and HAL_TIM_PWM_ConfigChannel() adds function call overhead, parameter validation, and lock acquisition that you cannot afford in this ISR. Writing directly to ADC1->DR and TIM1->CCR1 eliminates that overhead entirely. Similarly, on an STM32F0 with 16 KB flash, the HAL UART driver alone can consume 2-4 KB — a significant fraction of the total.
The practical pattern in production: use HAL for initial peripheral setup (clock configuration, GPIO alternate function mapping, DMA channel configuration) and bare-metal register access inside ISRs and real-time loops. This gives you the convenience of HAL where it matters and the performance of direct access where it counts.
Source: Driver Design Q&A
