Writing device-drivers for microcontrollers like STM32 is very complicated if you haven’t written a few of them before; not only do you need to understand the peripheral module hardware and their special function registers (SFR’s), you also have to be an ace on the meaning of SFR bit-fields and bits, understand interrupt vector tables, #pragmas, inline assembly and other intricate things.
Not too many years ago, semiconductor vendors didn’t provide device-driver libraries with their microcontrollers, and so each customer had to write them from scratch. What a waste of engineering time and duplication of resources! Way too many years later, they started to ship static device driver libraries or device-driver generations tools to help struggling developers. STMicroelectronics for example provide both static STM32 device-driver libraries, as well as the STM32CubeMX device initialization tool. And so, with readymade device driver libraries available, do STM32 developers need to worry about SFR’s anymore? I’d say yes, for reasons outlined in this blog article.
I spent the first 10 years of my career developing device-driver generation tools for several major chip-vendors, and as such I have to confess I got quite good at writing low-level device drivers for embedded microcontrollers. Most embedded developers (at least the good ones) isolate the hardware dependent driver code in a device driver library, thus avoiding to mix hardware dependent code with the application logic.
The problem is that developing device-drivers are so incredibly device dependent, and requires intricately detailed device and tool skills. First, you a have a microcontroller like STM32 with its peripheral modules such as UART, USB, DMA, A/D or D/A converters, digital I/O, Timers, or LCD drivers. To control the use of any peripheral module (let’s take a UART as an example here), you typically need to handle three things:
- Initialization - provide a UART_Init() function that setup communication parameters like baudrate, interrupt or polled operation, etc.
- Runtime control - provide UART_GetChar() and UART_PutChar() functions to access the services provided by the peripheral module hardware.
- Interrupt handling – provide UART_DataReceivedIntrHandler() or similar, dependent what is suitable in the specific use-case.
To write any device-driver function (say, UART_PutChar), your code needs to interact with the STM32 hardware in general, and the UART peripheral module in particular. That is done by querying as well as setting and clearing the value of certain bits in various SFR’s according to a very specific protocol enforced by the STM32 chip manual (for example, there is no incoming UART data to read until a SFR bit flag this). Here, you will become an expert in bit-shifting and bit-masking in C too.
It can be argued that it is mostly the device-driver developers who need to understand the meaning of various SFR’s, and developers merely using the static STM32 device-driver library or STM32CubeMX do not need to worry about SFR’s anymore. This is partially true, but only partially.
Even if semiconductor manufacturers like STMicroelectronics now ship static device driver libraries and/or provide device driver generation tools like STM32CubeMX to ease the pain for their customers, developers might still need to understand what happens in the hardware side of things to be able to debug their application software.
Assume for example you want to use timer output pins, perhaps for stepper motor control. You do not get the behavior expect when calling various device-driver library API’s. To debug this or other cases, you can use the SFR register view in the debugger to view what the hardware says about the current situation. For example:
- Are the timers enabled?
- Are the timers running?
- Are interrupts globally enabled?
- Is the timer interrupt enabled?
- Are the timer output pins enabled?
- Are there any pin-multiplex collisions?
And so, even if you rely on ready-made device driver libraries you haven’t hand-crafted yourself, you may still have very much need for knowledge on the meaning of various STM32 SFR bit-fields and bits. The SFR viewer in the debugger, such as Atollic TrueSTUDIO, is the perfect companion as it let you both read and change SFR bit values.
With some knowledge on the SFR registers, and a debugger SFR register viewer, you can save yourself a lot of pain when debugging your embedded system; STM32-based or not. The Atollic TrueSTUDIO debugger contains an SFR viewer that works with both Segger J-Link and ST-LINK for STM32 projects.
Read more on STM32 development and debugging in this whitepaper: