Example architecture

This section focuses on the major concepts of the example architecture. It is not exhaustive. It is a quick way to spot the major architecture points that are of interest to port and adapt the example code.

It complements the information available in How to read an example.

Layered architecture

Each example application is separated into units, with minimal overlap between the functions of the individual units. This separation of concerns is achieved using modularization, encapsulation, and by arranging the software in layers, as depicted below.

This is a block diagram of the software layers of the example.

Block diagram of the software layers of the example.

The hardware elements are:

  • The device

  • The board specific elements.

These elements are the ones that change and require the porting of the software layers.

The services layer contains the hardware abstraction layer (HAL) and other STM32Cube components:

  • Drivers

    • The HAL drivers are series-specific. Select the right HAL for the target board.

    • The part drivers are series agnostic. The same part drivers are used across all STM32 series.

  • Middleware is series agnostic: the same set of modules is used across all STM32 series.

  • Utilities: the utilities are series agnostic. The same utilities are used across all STM32 series.

The layered architecture of the example application is designed to separate the different concerns of the example code clearly:

  • The infrastructure code is located in the main.c file. This code is the same for all examples.

  • The functional code is located in the example.c file. This code is:
    • specific to the example

    • hardware-agnostic

    • toolchain-agnostic

  • The configuration code is located in the [board_name]/ folder. This code is specific to:
    • the STM32 series

    • the toolchain.

This is the detailed layered architecture of the example, down to the file names.

Detailed layered architecture of the example, down to the file names.

In particular:

  • Infrastructure: nothing to port at this level.

    • The system initialization is called from here but implemented in the Application resources layer.

    • The mx_system.h file abstracts the dependencies to configure the system.

    Note

    This layer gathers all the collateral elements to run the example. It is meant to be replaced by the custom infrastructure code of the target application.

  • Application use-case: nothing to port at this level.

    • The example.c file implements the use-case code and is hardware-agnostic.

    • The example.h file abstracts the dependencies to configure the use-case.

  • Application resources: this is the layer that needs to be ported.

    • The [board_name]/generated/ folder contains the configuration code for the STM32 microcontroller and its peripherals.

    • This layer is split in software units, each of them implementing a specific service.

      • mx_[pppi].c implements the configuration code for the hardware instance [pppi] of the peripheral.

      • mx_[pppi].h collects all required dependencies so that this configuration code can run. It includes the HAL drivers in a series agnostic way (stm32hal.h).

    • The modular organization of this code allows the rework and reuse of each unit independently.

Example dependencies

All example projects handle their dependencies in the same way. This ensures that all example projects are compatible with each other and can be used together. It also defines which elements of an example must be ported and which elements are just reused “as is”.

Typical dependencies

Typical example dependencies overview

Overview of typical HAL example dependencies

See the detailed dependency tables below for specific information.

The infrastructure code has the following direct dependencies:

HAL example infrastructure dependencies

Dependency

Origin

Description

Porting

The STM32 device startup code.

This code is provided by the DFP library of the STM32 device.

It provides the vector table, the system startup code, and the linker file.

Select the proper DFP library for the STM32 device.

The system configuration for this use-case.

This code is written in the example project and leverages the HAL driver (for instance: RCC and PWR).

It provides the system clock configuration and the system initialization code.

The STM32CubeMX2 tool generates this code.

The PRINTF service if it is used.

This code is provided by the basic_stdio utility.

It provides the basic I/O functions for printing serial logs.

This utility is hardware-agnostic, but it requires a serial interface to be configured.

The LED part driver if it is used.

This code is provided by the led part driver.

It provides the basic I/O functions to control the LED over a GPIO.

This part driver is hardware-agnostic, but it requires a GPIO to be configured.

This code has indirect dependencies when the LED part driver and the PRINTF services are used:

HAL example infrastructure indirect dependencies

Dependency

Origin

Description

Porting

The GPIO peripheral.

This code is provided by the HAL driver.

It provides the basic I/O functions to control the GPIO.

This code is hardware-agnostic, but it requires a GPIO to be configured in the application resource layer.

The USART peripheral if the PRINTF service is used.

This code is provided by the HAL driver.

It provides the basic I/O functions to control the USART.

This code is hardware-agnostic, but it requires a USART to be configured in the application resources layer.

The application use-case code has the following dependencies:

HAL example use-case dependencies

Dependency

Origin

Description

Porting

The HAL driver of the peripheral being demonstrated.

This code is provided by the HAL library.

It provides the functional APIs which are hardware-agnostic.

The HAL code implementing the APIs is hardware-specific. The proper HAL library must be selected. The API calls themselves are hardware-agnostic. No porting required in example.c.

The resource configuration code for the peripheral being demonstrated.

This code is provided by the example project.

It configures the STM32 peripherals.

This code is generated in the application resources layer by the STM32CubeMX2 tool.

The application resources layer has the following dependencies:

HAL application resource dependencies

Dependency

Origin

Description

Porting

The HAL driver of the peripheral being demonstrated.

This code is provided by the HAL library.

It provides the initialization and configuration APIs which are hardware-specific.

The code in mx_[pppi].c/h must be ported to use the proper HAL APIs.

The HAL system drivers.

This code is provided by the HAL library.

It provides system services for the HAL peripheral driver, such as Cortex®, RCC, DMA.

The proper HAL library must be selected for the STM32 device.

Encapsulation concept

The examples are designed to be as simple as possible, so global variables are used to share data between the different layers. Nevertheless, the examples are also designed to be modular and reusable.

The code is divided into layers and each layer can contain several software units. Each unit is composed of two files as listed below:

  • The .h file contains the interface of the unit,

  • and the .c file contains the implementation of the unit (for instance the code to configure a peripheral driver).

The dependencies of a unit are also minimized, and handled in the most hardware agnostic way possible. For example, the HAL library is always included through its stm32hal.h file to avoid series dependencies.

This means that when a unit needs porting or reusing, only the .c and .h files of this unit need to be updated. The dependencies are clearly identified, as well as what must be ported and what can be reused.

In particular:

  • mx_hal_def.h: this gives the list of all the HAL drivers used in the example and the configuration code in use.

  • mx_[pppi].c/h: this gives the porting scope of the peripheral instance [pppi].

Besides, the HAL library itself is modular. The components that the example requires are defined in the file stm32[tn]xx_hal_conf.h, where [tn] identifies the STM32 series.

Resources handling

Portability comes at the cost of abstraction between the application logic and the system interfaces.

In a general manner:

  • The HAL library offers the hardware abstraction.

  • The exact hardware resources in use must also be abstracted. For example, direct hardware instance identifiers like USART1 are not used in example.c.

The hardware resources are abstracted thanks to the concept of aliasing . Aliases connect the use-case layer (software-oriented) and the resources configuration layer (hardware-oriented).

The STM32CubeMX2 tool generates the aliases, based on user labels defined in this tool.

The aliases are defined in the mx_hal_def.h file.

The same concept of alias exists also for the other libraries like the part drivers, middleware, or utilities. For instance, the aliases for the part drivers are defined in the mx_[part_driver].h file.

Important

Any change in the use-case code ( example.c) must use these aliases when resources are involved. This ensures that the code is hardware agnostic and can be reused on any STM32 series.