Inspirel banner

YAMI4 with FreeRTOS/LwIP on STM32F429

YAMI4 is a messaging solution for distributed systems.

FreeRTOS is a market leading real time operating system (or RTOS), and the de-facto standard solution for microcontrollers and small microprocessors.

LwIP is a small independent implementation of the TCP/IP protocol suite.

STM32F429Zi Nucleo board is a board from ST that allows to prototype IoT products and looks like here:

STM32F429Zi Nucleo-144 board

Last but not least, the IAR Embedded Workbench is a development environment, recognized in the embedded world and representative with regard to the general workflow. Other IDEs with comparable features will require similar setup.

This article describes their integration, the steps taken to build and run the example program and the concerns that need to be taken into account when developing similar applications.

Note that there is a sibling article about the use of YAMI4 with the ThreadX RTOS and the NetX network stack.

Article outline:

General idea

STM32CubeMX setup

IDE workspace and options

A bit of manual work

Running the subscription example

API changes in YAMI4

General idea

Both FreeRTOS and LwIP can be managed from the STM32CubeMX project configurator, which makes it very easy to set up a project template that already relies on these two components, with complete workspace configuration for selected IDE. In order to fit this scheme, the YAMI4 libraries can be added to the workspace with already prepared project definitions. The two YAMI4 components are the core library, which provides basic messaging services, and the general-purpose C++ library, which supports higher-level messaging patterns on top of the core library.
The actual application (here called FreeRTOSTest) and the two YAMI4 libraries can be integrated in a single workspace setup like here:

General view on the IAR workspace

Above, YAMI4-FreeRTOS-core and YAMI4-FreeRTOS-cpp are library projects and FreeRTOSTest is the actual application program that directly relies on the general-purpose C++ library, and that contributes also the FreeRTOS and LwIP middleware components.

Both YAMI4 library project files are included in the YAMI4 package, in src/core and src/cpp directories and the workspace setup described here assumes that YAMI4 and the actual test application are in sibling directories (that is, at the same level).

STM32CubeMX setup

STM32CubeMX is a graphical wizard and project configurator that allows to generate a skeleton for the application, taking into account the selection and configuration of general system properties (like clocks) and peripherals. Considering the level of complexity of modern microcontrollers, it is a proper tool to generate the basic structure of the final project. Interestingly, in addition to providing code templates and abstration layers for the basic usage patterns of selected peripheral deviced, it can also contribute the whole RTOS (FreeRTOS in this case) and a network stack (LwIP).

There are two concerns that need to be taken into account with regard to the default project configuration for the STM32F429 device. First, the ETH peripheral needs to have interrupts enabled:

STM32CubeMX ETH settings

Second, the default heap space of 0x200 is certainly too small for handling the needs of all involved components and something reasonably bigger needs to be set up (the actual size will depend on the messaging needs of the application):

STM32CubeMX heap settings

The middleware components should also be configured reasonably. Here, the FreeRTOS configuration relies on the CMSIS v2 API and ensures that basic synchronization functionalities (mutexes, semaphores and event groups) are available:

STM32CubeMX FreeRTOS settings

It should be noted that FreeRTOS has its own notion of the available heap size (this heap can be used to allocate stack space for newly created tasks) and that is independent on the heap size configured above.

The LwIP network stack is also configured appropriately, with at least UDP, TCP and ARP functions enabled (note that the selected IP addresses are examples that are valid only in the particular network environment and will need to be chosen appropriately for the target installation, it is also possible to set them at run-time):

STM32CubeMX LwIP settings

Special care should be taken with regard to the checksum calculation, which should be done by hardware - this can be selected in one of the tabs of this window.

IDE workspace and options

Once the project skeleton is generated by STM32CubeMX and put together with the YAMI4 libraries in a single workspace, the more detailed look at the application project structure is:

IAR project - detailed view

There are multiple files copied or generated by STM32CubeMX, but the ones that are of direct interest to the developer are in the User group:

There is an interesting interaction between the application project and the YAMI4 libraries:

The above looks like a circular dependency, but fortunately it does not introduce compilation or linking problems and the only unusual consequence of this scheme is that the YAMI4 library needs to have a view on the main (application) project's directories, so that it can find appropriate header files, and that the involved header files are actually understood by the compiler while the YAMI4 libraries are processed. This will be seen below.

Having the workspace and file structure in place, the following options need to be considered to properly tie together all involved projects. These settings can be used as a source of inspiration for preparing similar project structures in other development environments.

For the YAMI4 Core library:

YAMI4 Core project settings 1

The device setting above just reflects the choice of target device. YAMI4 does not have any particular requirements with regard to the FPU settings (it does not perform any FPU operations, only passes values for serialization purposes), but the option chosen needs to be consistent across the whole workspace.

YAMI4 Core project settings 2

The setting above makes sure that the language extensions used within the application project are understood by the compiler even while compiling the YAMI4 library. The YAMI4 library does not refer to any compiler extensions, but the header files that are pulled from the application project do. The YAMI4 Core library does not use exceptions or RTTI, but calls back into higher levels that do.

YAMI4 Core project settings 3

Finally, the YAMI4 Core library relies on services provided by FreeRTOS and LwIP and also has its own system-abstraction layer that needs to be referenced. The include directories reflect these dependencies and the whole set of paths was just copied from the application project before the single freertos path that is contributed by YAMI4 as part of its system-abstraction layer. Note also the YAMI4_WITH_LWIP symbol, it needs to be defined to enable code paths specific to the LwIP integration. The other symbols are copied from the application project and are needed for the application-contributed header files to resolve properly.

With all the options set appropriately, the YAMI4 Core library project should involve all these source files from the YAMI4 distribution package:

For the general-purpose YAMI4 C++ library:

YAMI4 Core project settings

Above, the include paths refer to the other projects and also contain the whole set copied from the application project. Note again the YAMI4_WITH_LWIP symbol.

Similarly to the core part, the YAMI4 C++ library project should refer to the following sources from the YAMI4 distribution package:

Finally, for the actual application:

Test project settings 1

The application project was generated by STM32CubeMX and most of the include directories are for the involved CMSIS drivers and the contributed FreeRTOS and LwIP modules. Still, it is also necessary to provide dependencies to YAMI4. Note again the YAMI4_WITH_LWIP symbol added to the list, it is still needed, even at the application level.

Test project settings 2

Above, the application contains both C and C++ files and all C++ facilities need to be available.

Test project settings 3

... and of course the application needs to link with both involved YAMI4 libraries, picked from where they are built.

A bit of manual work

The advantage of using the STM32CubeMX tool is that its generated project template does not require any tweaks and essentially works out-of-the-box. Still, some minor modifications of the main function were needed to integrate the generated application code with the actual example from the YAMI4 package.

The main function initializes the hardware components as defined by the STM32CubeMX project configurator, starts the example FreeRTOS task and includes an example infinite loop where the user code can be written. In the case of network-oriented application that infinite loop is elsewhere and is more involved and in fact it can be managed by one of the FreeRTOS tasks. The following code does this:

void run_publisher(void * argument);
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART3_UART_Init();
  MX_USB_OTG_FS_PCD_Init();
  
  osKernelInitialize();
  pbuf_init();
  udp_init();

  const osThreadAttr_t defaultTask_attributes = {
    .name = "defaultTask",
    .priority = (osPriority_t) osPriorityNormal,
    .stack_size = 2048
  };
  
  defaultTaskHandle = osThreadNew(&run_publisher, NULL, &defaultTask_attributes);
  
  osKernelStart();
}

Above, the run_publisher function is declared and then called as a task function from main. It is implemented in a separate file publisher.cpp.

Running the subscription example

The subscription example is the most elaborate in the YAMI4 distribution package, because the publisher program acts both as a server (accepts subscription requests from clients) and as a client (sends a stream of data updates to subscribers).

The following screenshot (click for full size) demonstrates the publisher program running on the STM32F429 board, with two subscribers launched on the host computer - they are started several seconds after the program starts on the board. As can be seen, values generated by the board are properly delivered to both subscriber programs.

Publisher screeshot

Note that in the background, in the central pane of the development environment, a fragment of publisher.cpp file is visible with the loop that actually is responsible for periodic generation of new published values. This code is very high-level, with a single line actually devoted to publishing the value to all currently active subscribers - yet it is all that is needed and the YAMI4, FreeRTOS and LwIP libraries do the rest.

API changes in YAMI4

One of the main design goals of YAMI4 is to be composable and non-intrusive with regard to its application in the target program. These goals mean in particular that YAMI4 should not hijack the environment and it should not assume that it is the only user of some resource, like a memory space or a network stack. These goals are straightforward on a POSIX or Windows system, where system resources are managed outside of the user code, but in RTOS it is the user code that decides when and how the environment is set up. In order not to interfere with the policies of the user code:

This approach allows the user retain complete control over when and how these critical system resources are created, which includes decisions like whether resources are allocated statically or dynamically or what should be the task priorities or their stack sizes. See the example programs (like the complete content of the publisher.cpp presented above) to check how the user code can provide the necessary resources to the YAMI4 agent.

Go to the YAMI4 homepage to learn more about the project and to find its distribution packages.