cancel
Showing results for 
Search instead for 
Did you mean: 

How do you design board support drivers?

PPan.19
Associate

I have got to implement a board support package for a custom board, and I would like to know your opinion about the different approaches of designing board support drivers. My custom board pupulates an STM32 microcontroller besides other components like LEDs, buttons and communication components. STM provides example board support drivers for their nucleo, discovery and eval dev-boards. I have studied STM's board support packages, besides other examples provided by Nxp and Ti, and I have been able to extract the following approaches in terms of design / architecture:

According to the first approach, each board driver, i.e. button driver, LED driver, low level component SPI driver, low level component I2C driver and so forth allocates and configures it's required MCU resources for proper operation. It's best to explain it by example, so the board support API would be like this:

  BSP_Init() {
 
       SYSTEM_CLOCK_Init();
 
       LED_Init();
 
       BUTTON_Init();
 
       COMPONENTx_Init();
 
       COMPONENTy_Init();
 
   }
 
   SYSTEM_CLOCK_Init() {
 
       // Configure MCU system clock, e.g. 16 MHz.
 
   }
 
   LED_Init() {
 
     // Enable GPIO port clocks for LED pins.
 
     // For each LED, configure GPIO pin, e.g. output, no-pull, speed.
 
   }
 
   BUTTON_Init() {
 
     // Enable GPIO port clocks for button pins.
 
     // For each Button, configure GPIO pin, e.g. input, pull-up, speed.
 
     // Configure EXTI.
 
     // Configure NVIC.
 
   }
 
   COMPONENTx_Init() {
 
       // Enable GPIO port clock for slave select pin.
 
       // Configure GPIO pin for slave select.
 
       SPI1_Init();
 
       // Init component by using SPI bus operations SPI1_Transmit() ...
 
   }
 
   COMPONENTy_Init() {
 
       // Enable GPIO port clock for write / command pin.
 
       // Configure GPIO pin for command / write pin.
 
       I2C1_Init();
 
       // Init component by using I2C bus operations I2C1_Transmit() ...
 
   }
 
   SPI1_Init() {
 
     // Enable SPI peripheral clock.
 
     // Enable GPIO port clocks for SPI pins.
 
     // Configure GPIO pins for SCK, MISO, MOSI.
 
     // Configure DMA.
 
     // Configure SPI registers.
 
     // Configure NVIC.
 
   }
 
   I2C1_Init() {
 
     // Enable I2C peripheral clock.
 
     // Enable GPIO port clocks for I2C pins.
 
     // Configure GPIO pins for SCK, SDA.
 
     // Configure DMA
 
     // Configure I2C registers.
 
     // Configure NVIC.
 
   }

According to the second approach, the MCU resource allocation and configuration is not split into the different driver API groups, but is split into one API for each MCU resource, e.g.

BSP_Init() {
 
       CLOCK_Init();
 
       GPIO_Init();
 
       EXTI_Init();
 
       NVIC_Init();
 
       DMA_Init();
 
       SPI1_Init();
 
       I2C1_Init();
 
   }
 
   CLOCK_Init() {
 
       // Configure system clock, e.g. 16 MHz.
 
       // Enable GPIO port clocks.
 
       // Enable SPI clock.
 
       // Enable I2C clock.
 
   }
 
   GPIO_Init() {
 
       // Configure LED GPIO pins.
 
       // Configure Button GPIO pins.
 
       // Configure SPI GPIO pins.
 
       // Configure I2C GPIO pins.
 
   }
 
   EXTI_Init() {
 
       // Configure interrupt generation for Button GPIO pins.
 
   }
 
   NVIC_Init() {
 
       // Enable interrupts for DMA transfer complete, DMA error, button, SPI, I2C, ...
 
   }
 
   SPI1_Init() {
 
       // Configure SPI registers.
 
   }
 
   I2C1_Init() {
 
     // Configure I2C registers.
 
   }

Finally, I would like to note that I'm not using ST's HAL (Hardware abstraction layer) except direct register access by means of the ARM CMSIS.

What is your opinion about the provided design approaches. What are the advantages, disadvantages of each approach? How do you design board support drivers?

4 REPLIES 4
LMI2
Lead

What does your teacher say?

Pavel A.
Evangelist III

> What is your opinion about the provided design approaches

I don't see any signs of design here yet 🙂 You provide a function BSP_Init that does all the initialization, once. How it does it is its private matter.

Basically you've decided not to use CubeMX for the init code (because it's based on the ST's HAL). Ok. This is all about the "BSP_Init".

Think about the services the BSP should provide.

Any non-trivial device drivers, besides the buttons and LEDs?

Is any dynamic reconfiguration of chip functions or pins needed? Any power management? Fault recovery?

-- pa

PPan.19
Associate

Hi,

I have just left out the rest of the BSP API. Of course in both examples there would be other APIs like Led_On, Led_Off, Button_GetState, ComponentxDoBla, ComponentyDoFoo. Dynamic reconfiguration of chip functions and pins is not required.

The former approach groups the "C-language commands" by functionality, which is good for source code readability and modifications and reuse. The latter is more execution-time and memory efficient (especially if you avoid the unnecessary read-modify-writes by writing whole registers at once). The former aspect is usually more important, especially given the huge increase of memory resources, and the fact that the code is executed only once, at startup, when there's usually time enough.

I personally do sort of both, writing "functionality-group-wise", but using my own preprocessor to reorder code so that it ends being in the more efficient form.

JW