Use-case code ¶
This section focuses on the code that implements the logic of the use-case. The configuration of the resources is covered in the Resources configuration section.
Overview ¶
The STM32Cube Software Examples demonstrate the STM32Cube APIs to use the STM32 microcontrollers through meaningful use-cases. Nevertheless, this code is for demonstration purposes and not intended for direct use in production. The main priorities of the code are:
The code is meant to be educational and illustrative.
The code is structured for easy reuse and adaptation.
The functional code is designed to be hardware-agnostic and toolchains agnostic.
The benefits are:
Homogeneity: all examples are written the same way, with the same structure.
Education: the code is easy to read and understand, easy to link to the documentation.
Diversity: the code enables reuse and adaptation across all STM32 series.
The examples are preconfigured IDE projects created with STM32CubeMX2. It is easy to reconfigure them with STM32CubeMX2 and generate the configuration code for customized use-cases.
The use-case code is gathered in the
application/
folder of the project.
The main entry-points of the use-case are located in the
example.c
file.
The infrastructure of the example is centralized in the
main.c
file.
Infrastructure ¶
main.c
implements the infrastructure of the example. This code is almost the same for all examples.
It is only a basic infrastructure to schedule the functional code implemented in
example.c
and manage the termination of the example.
|
Function |
Description |
|---|---|
|
|
The main entry point of the example:
|
|
|
This function is entered when the example is successfully completed. It is used to report the success when the example terminates. For examples running in an infinite loop, this function is not implemented. |
|
|
This function is entered when an unrecoverable error occurs. It is used to report the error and to stop the execution of the example. Out of the box, this code does not execute because all examples are verified as functional. |
|
|
This function is entered when a fault occurs. It is used to report the fault and to stop the execution of the example. Out of the box, this code does not execute because all examples are verified as functional. |
|
Function |
Description |
|---|---|
|
|
The main entry point of the example:
|
|
|
This task, created in
|
|
|
This function is entered when an unrecoverable error occurs.
|
|
|
This function is entered when a fault occurs.
|
Processing ¶
example.c
is the file implementing the example scenario logic with the HAL APIs:
Each scenario step is implemented here, and identified with a tag
########## Step X ##########The example code is structured in
app_xxx()functions andapp_process()is the functional part of the use-case.This code can leverage the
PRINTFservice to write application logs to the console. These logs are enabled by setting theUSE_TRACEcompiler switch to1.
This file also contains the user callbacks, and all local functions used to implement the example scenario.
Local functions are used to implement some helpers that are not at the core of the example scenario.
This keeps
app_process()
focused on the HAL calls, and the code snippets to copy/paste in custom applications.
The system and resource configuration code is not located in
example.c. Refer to the
Resources configuration
page.
This is where the clocks, pins, and peripherals are configured:
The system initialization code is called from
main()inmain.c.The resources configuration services are called from
app_init()inexample.c.
LL examples follow the same structure as HAL examples
with specific differences for Low-Layer API usage.
LL examples never use
PRINTF
logs to ensure maximum performance and minimal overhead.
The key distinction is the addition of device family-specific files :
``ll_example.h``: Declares abstracted functions for the use-case (API agnostic interface)
``stm32yyxx_ll_example.c``: Implements LL-specific peripheral operations for the target STM32 series
In ``example.c``:
The scenario logic calls abstracted functions defined in
ll_example.h:
// Example: ADC continuous conversion
ADC_Activate();
ADC_Calibrate();
ADC_StartConversion_IT();
In ``stm32yyxx_ll_example.c``:
Direct LL API usage with inline functions for performance:
inline system_status_t ADC_Activate(void) {
LL_ADC_ClearFlag_ADRDY(MX_ADCx);
LL_ADC_Enable(MX_ADCx);
while (LL_ADC_IsActiveFlag_ADRDY(MX_ADCx) == 0U) { ... }
return SYSTEM_OK;
}
Middleware examples follow the HAL structure but vary depending on the middleware type.
Two middleware patterns:
API-based middleware: Provides functional APIs called from
app_process()(similar to HAL structure) - examples: mbedTLS, STCryptoLibFramework-based middleware: Offers a comprehensive framework with tasks and event-driven architecture - examples: FreeRTOS, LwIP, USBX, FileX
Framework-based middleware:
In ``example.c``:
``app_init()``: Initializes middleware components (FreeRTOS tasks, LwIP stack, USBX threads, FileX system)
``app_process()``: Often empty or not called, as processing happens in middleware threads
app_status_t app_init(void) {
app_lwip_init(); // Start LwIP stack
app_tcp_echo_server_start(); // Launch server thread
return EXEC_STATUS_INIT_OK;
}
// app_process() is NOT used - middleware handles execution
Middleware-specific modules:
FreeRTOS: Processing delegated to tasks in
app_freertos.cLwIP: Network operations in background threads (
app_tcp_echo_server.c,app_tcp_echo_client.c)USBX: USB events handled by USBX threads with application callbacks
FileX: File operations called from
app_filex_process()
The architecture is event-driven and thread-based rather than sequential polling.
Part drivers examples follow the HAL structure
.
The functional code in
example.c
calls the part driver APIs directly to interact with the external component.
Utilities examples follow the HAL structure demonstrating reusable utility components.
Conventions ¶
Admonitions ¶
The comments use admonitions such as the
@user
tag to highlight possible modifications (see
How to port an example).
Additional admonitions are used to highlight important information.
|
Type |
Description |
Example |
|---|---|---|
|
Attention |
Highlights a particularly important note that must not be overlooked. |
Attention: the resistors values condition the data rate that can be achieved. |
|
Warning |
Used when there may be negative consequences to consider if a recommendation is not obeyed. |
Warning: this variable must be 32-bit aligned. |
|
Note |
Notes have the lowest importance. |
Note: no callback is associated with this interrupt as it is only used to wake the system up. |
Naming rules ¶
In the example code, the naming rules aim at identifying easily:
The portions of code which are related to the internal design of the example.
The code snippets which can be reused in custom applications.
These rules apply to the code located in
example.c.
|
Element |
Naming Rule |
Description |
|---|---|---|
|
Global variables |
Global variables are written in
CamelCase
.
For example:
|
The example code requires global variables to operate and connect all services, but they are likely to be replaced by application-specific ones. |
|
Local variables |
Local variables are written in
snake_case
.
For example:
|
Functions like
|
|
Main functions |
The major functions implementing the example are written in
snake_case
.
For example:
|
These functions implement the use-case with the functional APIs being demonstrated. They are designed as code to be reused in other applications. |
|
Helper functions |
Helper functions are written in
CamelCase
.
For example:
|
Helper functions are used to implement some services that are not at the core of the example scenario. This code is specific to the example and is not intended for reuse in other applications. |
In a nutshell:
Elements written in CamelCase are specific to the example.
Elements written in snake_case can be of interest as code snippets for custom applications.