Build apps with FreeRTOS
All development for the Coral Dev Board Micro is done in C/C++ and relies heavily on FreeRTOS: a real-time operating system kernel that provides a variety of system features that are traditionally not available on microcontrollers, such as multitasking.
This page describes the basic workflow to create an app for the Dev Board Micro with FreeRTOS, including as how to set up a new project, build it, and flash it to the board.
If you instead want information about the FreeRTOS platform features, refer to the FreeRTOS documentation. Or for details about using board-specific features, such as the camera, microphone, GPIO pins, and TensorFlow Lite, refer to the coralmicro API reference.
Project overview
The basic project setup requires just two files: A C++ source file with an
app_main() function and a CMakeLists.txt file that defines your build
requirements.
For example, here's a "Hello World" source file (it prints to the serial console):
#include <cstdio>
#include "third_party/freertos_kernel/include/FreeRTOS.h"
#include "third_party/freertos_kernel/include/task.h"
extern "C" [[noreturn]] void app_main(void *param) {
(void)param;
printf("Hello world!\r\n");
vTaskSuspend(nullptr);
}
main() function here. However,
that function is defined internally to initialize hardware you
might need and then it calls your app_main() within a new FreeRTOS task.
And here's the CMakeLists.txt file to go with it:
add_executable_m7(hello_world
hello_world.cc
)
target_link_libraries(hello_world
libs_base-m7_freertos
)
This CMake configuration declares
the executable name and source file with add_executable_m7() (a wrapper for
add_executable())
and specifies the library dependencies with
target_link_libraries().
You can learn more about how to write this file in the
CMake documentation.
That's basically all you need. That is, assuming CMake can find
the libs_base-m7_freertos library that you require. To ensure that it can,
your project needs to include the coralmicro source code. There are two
common ways you can set up your project to do this, as described in the next
section.
Project setup
To start a new project that includes coralmicro, you first need to
decide where you want to keep your code. You basically have two options:
-
Out-of-tree project: Your code exists independent from the
coralmicrosource tree, and includescoralmicroas a submodule. (It's also possible to link to thecoralmicrosource from another external location, but using a submodule is a bit easier so that's what we'll covered here.)This is a good choice if you don't plan to modify
coralmicro. It will be easier to keep thecoralmicrosubmodule up-to-date with any mainline changes. -
In-tree project: Your code exists inside the
coralmicrosource tree, just like the includedexamplesandapps. Basically, you will fork thecoralmicrorepo and put your project(s) inside it.This is a good choice if you plan to modify some of the
coralmicrolibraries.
The following sections provide a step-by-step guide to get started with either option.
Create an out-of-tree project
To create an out-of-tree project, you need to add coralmicro as a submodule.
To get you started, we've created an example project you can clone or fork:
-
Clone our out-of-tree example project (it prints "Hello World"):
Tip: If you will manage your project in GitHub, fork this out-of-tree-sample and clone that fork instead.git clone https://github.com/google-coral/coralmicro-out-of-tree-sample -
Initialize the
coralmicrosubmodule and all its submodules:cd out-of-tree-sample git submodule add https://github.com/google-coral/coralmicro coralmicro git submodule update --init --recursive -
Install the required development tools (such as CMake) with this script:
bash coralmicro/setup.sh
That's it. You now have an out-of-tree project and you can start coding. Or continue to the next section and try flashing it to your board.
Build and flash
You can build and flash your out-of-tree project like this:
-
Generate the project Makefile (run this from the
out-of-tree-sampleroot):# -B specifies the path for your build output path and # -S specifies the path to the CMakeLists.txt file. cmake -B out -S . -
Build the app:
make -C out -j4To maximize your CPU usage, replace
-j4with either-j$(nproc)on Linux or-j$(sysctl -n hw.ncpu)on Mac. -
Flash the app to your board:
python3 coralmicro/scripts/flashtool.py --build_dir out --elf_path out/coralmicro-appcoralmicro-appis the executable name that's specified inCMakeLists.txt, so that's the ELF file name you must specify with--elf_path.Note: In addition to specifying the path to your ELF file with--elf_path, you must specify the build output directory with--build_dirbecause flashtool needs to get the elf_loader (bootloader) program from there. Whereas, when flashing in-tree examples and apps,--build_dircan be ommitted because flashtool uses the default in-tree "build" directory. Similarly, in-tree examples/apps don't need to specifyelf_pathbecause those files reside in the same build directory, so you can instead specify just the project name with--example(or-e) and--app(or-a).
When flashing is done, the board reboots and loads the app. You should see the green LED turn on. To see the "Hello World" message, connect to the serial console.
When you modify the source code or CMake configuration, just rebuild and reflash the app:
make -C out -j4
python3 coralmicro/scripts/flashtool.py --build_dir out --elf_path out/coralmicro-app
Create an in-tree project
To create an in-tree project, you'll start with the coralmicro repository and
add your project inside it:
-
Clone the
coralmicrorepo (you probably already did this during setup):Tip: If you will manage your project in GitHub, fork the coralmicro repo and clone that fork instead.git clone https://github.com/google-coral/coralmicro/
-
Create a new directory in
coralmicro/apps/and populate it with some files by copying code from an existing example project. For example:cd coralmicro # Copy the "hello world" code cp -r examples/hello_world apps/my_project # Rename the executable mv apps/my_project/hello_world.cc apps/my_project/main.cc -
Update the project and file names in
apps/my_project/CMakeLists.txt, respective to their new names:add_executable_m7(my_project main.cc ) target_link_libraries(my_project libs_base-m7_freertos ) -
Add the project as a new directory in
coralmicro/apps/CMakeLists.txt:add_subdirectory(my_project) -
Install the required development tools (such as CMake) with the script that's appropriate for your system (run this from the
coralmicroroot):- On Linux:
bash setup_linux.sh- On Mac:
bash setup_mac.sh
That's it. You now have an in-tree project and you can start coding. Or continue to the next section and try flashing it to your board.
Build and flash
You can build and flash your in-tree project like this:
-
Build the app along with everything else in the tree (run this from the
coralmicroroot):bash build.sh -
Flash the app to your board:
python3 scripts/flashtool.py --app my_projectNote: By default, flashtool looks for all binaries in "build" directory, so you need only specify the project name with--app. If you specify a different build path withcmake -B, then you must specify that path with the--build_dirargument, because that path is also where flashtool needs to find the elf_loader (bootloader) program.
When flashing is done, the board reboots and loads the app. You should see the green LED turn on. To see the "Hello World" message, connect to the serial console.
When you modify the source code or CMake configuration, just rebuild and reflash the app:
make -C build/apps/my_project -j4
python3 scripts/flashtool.py --app my_project
To maximize your CPU usage, replace -j4 with either -j$(nproc) on
Linux or -j$(sysctl -n hw.ncpu) on Mac.
FreeRTOS tasks
By default, your main app is automatically executed as a FreeRTOS task, so it shares execution time on the MCU with other coralmicro tasks that automatically run as needed. For example, when you use the camera API, the camera task manages the camera hardware and sends images back to your app.
The default priority level assigned to your app is defined by the constant
kAppTaskPriority (from libs/base/tasks.h), so you should always use this
level use when starting a new task with the FreeRTOS function
xTaskCreate().
For example:
#include "libs/base/led.h"
#include "libs/base/tasks.h"
#include "third_party/freertos_kernel/include/FreeRTOS.h"
#include "third_party/freertos_kernel/include/task.h"
[[noreturn]] void blink_task(void* param) {
auto led_type = static_cast<coralmicro::Led*>(param);
bool on = true;
while (true) {
on = !on;
coralmicro::LedSet(*led_type, on);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
extern "C" void app_main(void* param) {
(void)param;
auto user_led = coralmicro::Led::kUser;
xTaskCreate(&blink_task, "blink_user_led_task", configMINIMAL_STACK_SIZE,
&user_led, coralmicro::kAppTaskPriority, nullptr);
vTaskSuspend(nullptr);
}
coralmicro libraries
Although FreeRTOS provides the foundational OS features for the Dev Board Micro,
most of the behavioral features in your app will come from the coralmicro
libraries, which you can explore in the coralmicro API
reference.
Fortunately, a lot of coralmicro libraries are included by default with the
libs_base-m7_freertos library, such as libraries to use the board GPIOs,
camera, audio, TensorFlow, filesystem, and much more. You need to include this
library anyway because it also
provides the main() function that FreeRTOS requires (which calls
through to your program's app_main() function).
However, if your build fails with an "undefined reference," then you're missing
the link for a library you're trying to use. Take note of which code is
undefined and then go to the source file for the corresponding header, which
you should be able to find linked in each section of the coralmicro API
reference. In the same directory as the header file,
look in the local CMakeLists.txt and you'll see
the library name specified with add_library_m7() and/or add_library_m4()
(these are thin wrappers for
add_library()),
followed by the .cc files that are included with that library name.
For example, if your code uses libs/curl/curl.h, you'll need the
library name that's defined in libs/curl/CMakeLists.txt:
add_library_m7(libs_curl STATIC
...
)
So simply update your CMakeLists.txt file by adding libs_curl in the
target_link_libraries()
command:
target_link_libraries(my_project
libs_base-m7_freertos
libs_curl
)
Beware that some libraries offer both an M7 and an M4 version (such as
libs_base-m7_freertos and libs_base-m4_freertos), so it's important that you
specify the library name that corresponds to the MCU core (either M7 or M4)
where your executable will run.
For information about how to use the M4 core, see how to create a multi-core app.
Is this content helpful?