In this tutorial I describe how to build and deploy the FreeRTOS embedded operating system on STM32F4 Discovery board under 10 seconds. So why would you use operating system on such a small embedded system in the first place?
Typically, you don’t if you have a single task problem. However, if you have a multitasking problem (What’s that? See the youtube clip below.), OS provides you among other benefits an infrastructure to seamlessly implement tasks and to run them simultaneously.
Each task receives a fair proportion of CPU time (in our case ~1 ms per time slice). After a task uses its allocated time slice, OS interrupts its execution. Then context switch occurs. Current task is stored (i.e. current CPU register values, stack, heap,…) and next scheduled task is loaded and executed seamlessly. Actually, you could implement these OS functions all by yourself, however it is easier to use the existing, tested and already working OS code, such as FreeRTOS. 🙂
- Ubuntu 14.04 LTS (x86 architecture).
- STM32F4 Discovery Board (ARM architecture, costs less than 20 EUR).
- Complete the Template Project with Generic Makefile.
- Or at least go through chapter 2. Below is a quick-and-dirty instruction summary if you decide to stay on this page.
cd ~ # Remove the official package sudo apt-get purge binutils-arm-none-eabi \ gcc-arm-none-eabi \ gdb-arm-none-eabi \ libnewlib-arm-none-eabi # Add 3rd party repository sudo add-apt-repository ppa:terry.guo/gcc-arm-embedded sudo apt-get update # Check the GCC package version in the PPA repository sudo apt-cache policy gcc-arm-none-eabi # Install software requirements sudo apt-get install build-essential git openocd \ gcc-arm-none-eabi qemu-system-arm \ symlinks expect # Clone my git repository and init submodules git clone https://github.com/istarc/stm32.git cd ~/stm32 git submodule update --init
2. Build the FreeRTOS project
Use the provided Makefile to automagically build and deploy the project.
cd ~/stm32/examples/FreeRTOS make clean make -j4 # Non-optimized build # Other build options # make -j4 release # Non-optimized Build # make -j4 release-memopt # Size Optimized Build # make -j4 debug # Debug Build sudo make deploy
… and the result.
In fact, you can have this FreeRTOS project as a project template. As long as you add or remove appropriate files from inc and src directories, the Makefile will automagically include them during the build time. 😉
Q: Deployment fails repeatedly due to some OpenOCD issue. 😦 Is there a workaround?
A: Yes, the current official Ubuntu package (Aug 2014) contains a prehistoric OpenOCD version. You should build a newer version from scratch. I provide step-by-step instructions here (2nd section, it takes less than 3 minutes to build it). When you are done, just return here and continue as nothing happened. It will work out of the box. 🙂
3. How to Hook up FreeRTOS Source Code
In this section I describe how I hooked up FreeRTOS and a standalone project.
I used the generic project template from the section 5 of the Hello World! tutorial (stm32/examples/GPIO). I added FreeRTOS source and includes. More detailed file list is provided in the project’s tree structure below. I removed duplicate (conflicting) interrupt handlers from stm32f4xx_it.c file. I provided main.c file to initialize hardware, OS and 3 threads.
cd ~/stm32/examples/FreeRTOS # tree # . # ├── bin # ├── inc # │ │ # Added FreeRTOS Includes # │ ├── FreeRTOS # │ │ ├── Source/include/croutine.h # │ │ ├── Source/include/event_groups.h # │ │ ├── Source/include/FreeRTOS.h # │ │ ├── Source/include/list.h # │ │ ├── Source/include/mpu_wrappers.h # │ │ ├── Source/include/portable.h # │ │ ├── Source/include/timers.h # │ │ ├── Source/include/projdefs.h # │ │ ├── Source/include/queue.h # │ │ ├── Source/include/semphr.h # │ │ ├── Source/include/StackMacros.h # │ │ ├── Source/include/task.h # │ │ └── Source/portable/GCC/ARM_CM4F/portmacro.h # | | # | | # Added FreeRTOS Config File # | | # Here you can edit FreeRTOS settings # │ ├── FreeRTOSConfig.h # │ │ # │ │ # Existing Hello World Project Includes # │ ├── CMSIS/Include # │ ├── stm32f4_discovery.h # │ ├── STM32F4xx/Include # │ ├── stm32f4xx_conf.h # │ ├── stm32f4xx_it.h # │ └── STM32F4xx_StdPeriph_Driver/inc # | # ├── src # │ │ # Added New FreeRTOS Source Files # │ ├── FreeRTOS # │ │ ├── Source/croutine.c # │ │ ├── Source/event_groups.c # │ │ ├── Source/portable/MemMang/heap_1.c # │ │ ├── Source/list.c # │ │ ├── Source/portable/GCC/ARM_CM4F/port.c # │ │ ├── Source/queue.c # │ │ ├── Source/tasks.c # │ │ └── Source/timers.c # | | # | | # Here you can add multi-threaded applications. # │ ├── main.c # | | # | | # Existing Hello World Project Source Files # │ ├── startup_stm32f4xx.s # │ ├── stm32f4_discovery.c # | | # Removed duplicate (conflicting) # | | interrupt handlers # │ ├── stm32f4xx_it.c # │ ├── STM32F4xx_StdPeriph_Driver/src # │ └── system_stm32f4xx.c # | # | # Existing IO Toggle Project Makefile and # | # Linker Script # ├── Makefile # DON'T TOUCH # └── stm32_flash.ld # DON'T TOUCH
4. How to Write an RTOS Application
The source code of the above application is located in the main.c file (Just don’t freak out, when you open the file. 🙂 It’s only half as bad as it seems.) The main.c call flow is depicted in the UML activity diagram below.
First, the general purpose inputs/outputs are initialized (initHW()). Second, the following 3 tasks are created.
- ToggleLED_Timer() toggles the green LED each 1.5 s.
- It actually toggles GPIOD Pin 12. You may find the MCU-Board mapping in the STM32F4 Discovery User Manual (p. 30).
- DetectButtonPush() detects push button events and notifies ToggleLED_IPC task via inter-processes communication.
- When a button is pushed, two metal contacts of the button strike together. Unfortunately, they start to bounce apart before making a steady contact. Microprocessor samples the input fast enough to misinterpret this bouncing as repeated push events. We handle the contact bounce by waiting until transient response passes and input reaches steady state.
- The single event information is then relayed to ToggleLED_IPC task via FreeRTOS inter-process communication, i.e. a message queue.
- ToggleLED_IPC() reads the message queue. When it receives a push button event message, it toggles the red LED (GPIOD Pin 14).
After these task are created, they are immediately moved into the ready state. Third, the scheduler is started. Fourth, each of the following tasks receive a fair proportion of CPU time.