By now we should have our model validated, and we have measured the performance. So, that we know it can fit into the STM32 in terms of memory and CPU usage.

In this tutorial, we are going to deploy the model on the STM32. Firstly, we are going to use the ApplicationTemplate to generate the code for our application. Secondly, we are going to build code for running inference, and then send the output to serial terminal.

At the end of this tutorial, you should be able to build an application that runs inference, and sends output the output to serial terminal, as show in the following figure.

New Project (Application)

Run the STM32CubeMX, and do the same steps as in the previous tutorial to create a new project. On the Additional Software Components selection, select X-CUBE-AI/Application, and select ApplicationTemplate. After that, select X-CUBE-AI/X-CUBE-AI and enable the core and click Ok.

Enable one of the UART available on your STM32. In my case, I use the UART2 because this UART is connected to the virtual COM port over a USB connection. After that, import the model, and generate the code.

Writing Code

Open the app_x-cube-ai.c file. Add the following codes (line 8-11) to include the stdio.h and math.h library. The stdio.h is required for sprintf() function, while the math.h is required for round function().

I recommend you to write user codes within the USER CODE BEGIN and USER CODE END comment. So, when you need to regenerate project from the STM32CubeMX, it will not delete the codes within this comment.

In line 15, we instantiate the UART_HandleTypeDef structure for UART2 named as huart2. Note that we use extern keyword because the huart2 is already instantiated in main.c file. Basically the extern keyword extends the visibility of the huart2, so we can use it in app_x-cube-ai.c file.

Run Inference

We have four functions which are generated by STM32Cube.AI: aiInit(), aiRun(), MX_X_CUBE_AI_Init(), and MX_X_CUBE_AI_Process(). The MX_X_CUBE_AI_Init() and MX_X_CUBE_AI_Process() functions are called in main.c file. We can leave the aiInit(), aiRun(), MX_X_CUBE_AI_Init() functions as they are. We need to modify only the MX_X_CUBE_AI_Process() function.

This is the MX_X_CUBE_AI_Process()function:

This might seem complicated, but just take it one step at a time.

Firstly, in line 4-8, they are variable declarations for storing input (in_data) and output (out_data) of the neural network. The buffer variable stores the neural network output (after converted to characters) to be sent to the UART.

Secondly, in line 17-18, they set the input of the neural network. Thirdly, in line 19, we run the neural network by calling the aiRun() function.

Finally, in line 20-25, they convert the output to characters. So, the UART can send it to PC. The rest of the codes, in line 27-55, are the same, we only change the input of the neural network.

The printf for Float

In order to use printf() and also sprintf() functions with float value, we should add -u _printf_float to the linker flags.  To do that, in the Project Explorer tab, right click on the project, and then select Properties. After that, in the dialog that pops up, go to C/C++ Build > Settings > Tool Settings > MCU Settings, and then click on the -u _printf_float checkbox. Finally, click Apply and Close.

In addition to that, if you use STM32CubeIDE, then the generated linker scripts are wrong. We need to modify it in order to make the printf() works with float value.

In project folder, open the STM32F446RETX_FLASH.ld and STM32F446RETX_FLASH.ld files. Change the _estack value from _estack = 0x2001ffff; to be point one byte after the stack _estack = 0x20020000;.

Summary

The STM32CubeIDE generates useful functions for initializing and running the neural network. It also generates example code how to use these functions. It makes model deployment on STM32 easier.

Next: Add LED and Buttons to the Application

Leave a Reply

Close Menu