FreeRTOS 移植准备
移植步骤概述
- 添加 FreeRTOS 源码到工程。
- 配置 Cmake 编译脚本。
- 添加配置文件 FreeRTOSConfig.h。
- 修改三个中断函数。
- 编写程序验证。
工程准备
使用 STM32CubeMX 生成一个 GPIO 控制工程即可,记得修改时钟源。我使用的是 Cmake 工具链,但是无论什么工具链、HAL 库/标准库,移植思想流程是一致的。
FreeRTOS移植实践
下载源码
下载地址 :FreeRTOS™ - FreeRTOS™
移植内核源码
再 HAL 库工程的根目录新建目录Middlewares\Third_Party\FreeRTOS\Source
,将内核源码均移植到该目录下。
FreeRTOS 的源码目录:
移植后的工程:
配置编译工具链
再portable
文件夹下是对一些常见的工具链的适配,比如 Keil、IAR、GCC 等,其中MemMang
是 FreeRTOS 的内存管理文件,均要保留。
我的工程使用的是GCC
工具链,芯片是基于Arm cortex-m3
的 STM32F103 芯片,所以在 GCC 文件夹下仅保留ARM_CM3
;同时我使用heap_4
的堆管理,保留MemMang\heap_4.c
。
删除所以源文件以外的文件,特别是 Cmake 文件。
配置 Cmake 编译脚本
在根目录的CmakeLists.txt
文件中添加以下内容:
set(FreeRTOS_Src
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/croutine.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/list.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/queue.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/tasks.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/timers.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
)
# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
# Add user sources here
${FreeRTOS_Src}
)
# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined include paths
Middlewares/Third_Party/FreeRTOS/Source/include
Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3
)
- 添加
**FreeRTOSConfig.h**
文件
在下载的 FreeRTOS 源码目录FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel\examples\template_configuration
下有一个模板文件,但需要我们手动修改添加一些内容。
当然也可以使用别人编写好的,或者 STM32CubeMX 生成的(推荐)。
修改中断函数
下面两个函数在stm32f1xx_it.c
、portable\GCC\ARM_CM3\port.c
重复定义了,因此需要把stm32f1xx_it.c
下的函数注释掉。
void PendSV_Handler(void);
void SVC_Handler(void);
由于 FreeRTOS 需要使用SysTick
做计时,所以推荐将 HAL 库的时钟源修改为其他定时器。
接着,配置stm32f1xx_it.c
文件的SysTick_Handler
函数供 FreeRTOS 使用,如下所示。
注意看注释添加程序,不然下次生成会被覆盖删除。
/* USER CODE BEGIN Includes */
#include "FreeRTOS.h"
#include "task.h"
/* USER CODE END Includes */
/* USER CODE BEGIN EV */
extern void xPortSysTickHandler(void);
/* USER CODE END EV */
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
SysTick->CTRL;
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
/* Call tick handler */
xPortSysTickHandler();
}
/* USER CODE END SysTick_IRQn 0 */
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
应用程序验证
规范一些的话,就新建一套头文件做多线程管理,我比较懒,就直接在main.c
做简单测试:
我这里在做了多余的 LED 亮灭是为了判断线程是否创建成功,若是调度器成功启动最后的点亮是不会执行的。
/* USER CODE BEGIN Includes */
#include "FreeRTOS.h"
#include "task.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 0 */
void StartDefaultTask(void* argument)
{
(void)argument;
while (1)
{
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
vTaskDelay(100);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
vTaskDelay(100);
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* 。。。(省略自动生成的程序) */
/* USER CODE BEGIN 2 */
HAL_Delay(2000);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
HAL_Delay(1000);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
HAL_Delay(1000);
BaseType_t i = xTaskCreate(StartDefaultTask, "ledTask", 128*4, NULL, 24, NULL);
if (i == pdPASS)
{
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
}
vTaskStartScheduler();
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
/* USER CODE END 2 */
}
CMSIS_V2 移植准备
下载相关源码:
git clone git@github.com:ARM-software/CMSIS_5.git
git clone git@github.com:ARM-software/CMSIS-FreeRTOS.git
CMSIS_V2 移植实践
使用的是 ARM 开源标准版本,与 ST 版本略有不同,但不影响使用。(但推荐使用 ST 版本,对 STM32 支持更好)
若想使用 ST 版本,可以看 ST 的生成模板仓库,直接移植到对应目录做编译配置:https://github.com/STMicroelectronics/STM32CubeF1#
移植
Middlewares\Third_Party\FreeRTOS\Source
下新建文件夹CMSIS_RTOS_V2
。- 找到下列文件,全部移植到
CMSIS_RTOS_V2
文件夹下。CMSIS-FreeRTOS\CMSIS\RTOS2\FreeRTOS\Source\cmsis_os2.c
CMSIS-FreeRTOS\CMSIS\RTOS2\FreeRTOS\Include\freertos_mpool.h
CMSIS-FreeRTOS\CMSIS\RTOS2\FreeRTOS\Include\freertos_os2.h
CMSIS_5\CMSIS\RTOS2\Include\cmsis_os2.h
CMSIS_5\CMSIS\RTOS2\Source\os_systick.c
CMSIS_5\CMSIS\RTOS2\Source\os_tick.h
- 打开
os_systick.c
,找到下面内容注释掉。
// #include "RTE_Components.h"
// #include CMSIS_device_header
编译配置
在根目录的CmakeLists.txt
文件中添加以下内容:
set(FreeRTOS_Src
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/croutine.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/list.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/queue.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/tasks.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/timers.c
# 添加内容 start
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/os_systick.c
# 添加内容 end
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
)
# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
# Add user sources here
${FreeRTOS_Src}
# 添加内容 start
Core/Src/freertos.c
# 添加内容 end
)
# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined include paths
Middlewares/Third_Party/FreeRTOS/Source/include
Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3
# 添加内容 start
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2
# 添加内容 end
)
验证
为了让目录结构与 STM32CubeMX 生成的尽量一致,我们新建目录Core\Src\freertos.c
。
删除 main.c
中多余的程序(除头文件和自动生成的配置),添加如下内容:
/* USER CODE BEGIN Includes */
#include "cmsis_os2.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 0 */
void MX_FREERTOS_Init(void);
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* 。。。(省略自动生成的程序) */
/* USER CODE BEGIN 2 */
osKernelInitialize();
MX_FREERTOS_Init();
osKernelStart();
/* USER CODE END 2 */
}
在 freertos.c
中使用 CMSIS_RTOS_V2
标准建立线程,命名和 STM32CubeMX 生成的保持一致,并把 FreeRTOS 原生的 API 线程也放在该文件,方便切换添加。
以为我还没有添加其他外设驱动,不进行多任务验证,读者可自行验证。
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os2.h"
void MX_FREERTOS_Init(void);
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
void StartDefaultTask(void *argument)
{
(void)argument;
while (1)
{
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
vTaskDelay(1000);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
vTaskDelay(1000);
}
}
static void LedTask(void* argument)
{
(void)argument;
while (1)
{
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
vTaskDelay(300);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
vTaskDelay(300);
}
}
void MX_FREERTOS_Init(void)
{
// xTaskCreate(LedTask, "ledTask", 128*2, NULL, 24, NULL);
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
}