1

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 any vendor provided 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?

1

Design clear abstractions that hide underlying details and promote clear thinking at the higher levels. Use multiple layers of abstractions when appropriate.

Your second example does not do this. It does not hide any details or provide a useful abstraction. The application layer developer will need to know and consider details of clocks, GPIO, external interrupts and physical layer interfaces. It doesn't make the application layer developer's job any easier.

Your first example is better. It lets the application developer think in terms of LEDs instead of GPIO. That's a helpful abstraction.

Consider whether another layer of abstraction could make things even clearer. For example,. Your application works with devices on the SPi and I2C busses. So you're application layer should interface to a device-specific APIs, which in turn interface to a lower level SPI or I2C API.

  • Thank you very much for your answer. Actually both examples reside in an own hardware layer that provides an API to the application developer. I have just left out the rest of the API. BSP_Init would be called by the application developer in order to init the MCU, its peripherals and board components. In both examples there would be other APIs like Led_On, Led_Off, Button_GetState, ComponentxDoBla, ComponentyDoFoo. – Sinushyperbolikus Sep 2 '18 at 17:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.