1、FreeRTOS
FreeRTOS 是一个迷你的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录功能、软件定时器、协程等,可基本满足较小系统的需要。
FreeRTOS 在内核中增加了对称多处理 (SMP) 支持,使开发人员在设计基于 FreeRTOS 的应用程序时能够利用多核微控制器的 SMP 功能。最重要的是 FreeRTOS 支持树莓派 Pico RP2040 的两个内核的 SMP。
2、创建工程
工具 | 版本 |
---|---|
CMake | 3.24.1 |
Arm GNU Toolchain | 12.2.0 |
python | 3.10.6 |
首先使用 pico-project-generator 创建 树莓派 pico 的工程
../pico-project-generator/pico_project.py --gui
进入工程文件夹,使用 vscode 打开项目
编译
cmake .. && make
3、移植FreeRTOS
获取 FreeRTOS 源码
git clone https://github.com/FreeRTOS/FreeRTOS-SMP-Demos.git
进入 FreeRTOS-SMP-Demos 目录
cd FreeRTOS-SMP-Demos
更新子模块
git submodule update --init --recursive
将 FreeRTOS-SMP-Demos/FreeRTOS/Source 目录放入我们创建的 Pico 工程中
cp -r FreeRTOS-SMP-Demos/FreeRTOS/Source .
将 FreeRTOS-SMP-Demos/FreeRTOS/Demo/CORTEX_M0+_RP2040/OnEitherCore/FreeRTOS_Kernel_import.cmake 文件放入 Pico 工程中
cp FreeRTOS-SMP-Demos/FreeRTOS/Demo/CORTEX_M0+_RP2040/OnEitherCore/FreeRTOS_Kernel_import.cmake .
将 FreeRTOS-SMP-Demos/FreeRTOS/Demo/CORTEX_M0+_RP2040/OnEitherCore/FreeRTOSConfig.h 文件放入 Pico 工程中
cp FreeRTOS-SMP-Demos/FreeRTOS/Demo/CORTEX_M0+_RP2040/OnEitherCore/FreeRTOSConfig.h .
最后 Pico 工程的结构是
修改 .vscode/c_cpp_properties.json 文件
在 includePath 中加入 FreeRTOS 的头文件
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"${env:PICO_SDK_PATH}**",
"${workspaceFolder}/Source/include/**" // FreeRTOS 头文件
],
"defines": [],
"compilerPath": "/usr/bin/arm-none-eabi-gcc",
"cStandard": "gnu17",
"cppStandard": "gnu++14",
"intelliSenseMode": "linux-gcc-arm",
"configurationProvider" : "ms-vscode.cmake-tools"
}
],
"version": 4
}
修改 FreeRTOS_Kernel_import.cmake 文件
在开头加入
set(FREERTOS_KERNEL_PATH "${CMAKE_CURRENT_LIST_DIR}/Source")
set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "../Source/portable/ThirdParty/GCC/RP2040")
set(FREERTOS_KERNEL_RP2040_BACK_PATH "../Source/../../../..")
修改 CMakeLists.txt 文件
在 include(pico_sdk_import.cmake)
下面添加
# Pull in FreeRTOS
include(FreeRTOS_Kernel_import.cmake)
修改target_link_libraries
为
target_link_libraries(FreeRTOS PRIVATE
pico_stdlib
FreeRTOS-Kernel
FreeRTOS-Kernel-Heap4
pico_multicore)
在最后添加target_include_directories
target_include_directories(FreeRTOS PRIVATE
${CMAKE_CURRENT_LIST_DIR})
添加target_compile_definitions
target_compile_definitions(FreeRTOS PRIVATE
mainRUN_FREE_RTOS_ON_CORE=1
PICO_STACK_SIZE=0x1000
PICO_STDIO_STACK_BUFFER_SIZE=64 # use a small printf on stack buffer
)
CMakeLists.txt 文件
# Generated Cmake Pico project file
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/kjlsai/pico/pico-sdk")
# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
# Pull in FreeRTOS
include(FreeRTOS_Kernel_import.cmake)
project(FreeRTOS C CXX ASM)
set(PICO_CXX_ENABLE_EXCEPTIONS 1)
set(PICO_CXX_ENABLE_RTTI 1)
# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()
# Add executable. Default name is the project name, version 0.1
add_executable(FreeRTOS main.cc )
pico_set_program_name(FreeRTOS "FreeRTOS")
pico_set_program_version(FreeRTOS "0.1")
pico_enable_stdio_uart(FreeRTOS 0)
pico_enable_stdio_usb(FreeRTOS 1)
# Add the standard library to the build
target_link_libraries(FreeRTOS PRIVATE
pico_stdlib
FreeRTOS-Kernel
FreeRTOS-Kernel-Heap4
pico_multicore)
pico_add_extra_outputs(FreeRTOS)
target_include_directories(FreeRTOS PRIVATE
${CMAKE_CURRENT_LIST_DIR})
target_compile_definitions(FreeRTOS PRIVATE
mainRUN_FREE_RTOS_ON_CORE=1
PICO_STACK_SIZE=0x1000
PICO_STDIO_STACK_BUFFER_SIZE=64 # use a small printf on stack buffer
)
修改 FreeRTOSConfig.h 文件,将宏定义
- configUSE_TICK_HOOK
- configCHECK_FOR_STACK_OVERFLOW
- configUSE_MALLOC_FAILED_HOOK
- configTICK_CORE
改为 0
将宏定义
- configNUM_CORES
改为 2
添加宏定义
- #define configUSE_CORE_AFFINITY 1
4、编写代码
我们写一个打印程序来验证移植是否成功
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/mutex.h"
#include "pico/sem.h"
#include "FreeRTOS.h" /* Must come first. */
#include "task.h" /* RTOS task related API prototypes. */
#include "queue.h" /* RTOS queue related API prototypes. */
#include "timers.h" /* Software timer related API prototypes. */
#include "semphr.h" /* Semaphore related API prototypes. */
void task0(void *pvParam)
{
for(;;)
{
uint CORE_ID;
CORE_ID = portGET_CORE_ID(); // 获取核心ID
printf("task0 is runing in core %d\r\n",CORE_ID); // 打印核心ID
vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时1秒
}
}
void task1(void *pvParam)
{
for(;;)
{
uint CORE_ID;
CORE_ID = portGET_CORE_ID();
printf("task1 is runing in core %d\r\n",CORE_ID);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
int main()
{
stdio_init_all();
/* 创建任务0 */
TaskHandle_t task0_Handle = NULL;
UBaseType_t task0_CoreAffinityMask = (1 << 0); // 指示该任务可以运行在哪个核心上
xTaskCreate(task0, "task0", 1024, NULL, 1, &task0_Handle);
vTaskCoreAffinitySet(task0_Handle, task0_CoreAffinityMask); // 为任务设置核心关联掩码
/* 创建任务1 */
TaskHandle_t task1_Handle = NULL;
UBaseType_t task1_CoreAffinityMask = (1 << 1);
xTaskCreate(task1, "task1", 1024, NULL, 1, &task1_Handle);
vTaskCoreAffinitySet(task1_Handle, task1_CoreAffinityMask);
vTaskStartScheduler();
return 0;
}
编译工程
烧录程序后,打开串口助手可以看到任务0运行在核心0上,任务1运行在核心1上。
可见我们成功将 FreeRTOS 移植到 树莓派 Pico 上,并且成功调用 SMP 功能。