给CubeMX生成的Keil STM32工程移植主线FreeRTOS
需要的东西
本文以freertos 202210.01-lts
为例。
需要下载主线最新包(为了example)和一个稳定版(os内核源码)。
以LTS版本发布包作为内核源码为例,需要/FreeRTOS-LTS/FreeRTOS/FreeRTOS-Kernel
这个文件夹里面的东西。
对于keil开发环境,portable
文件夹下只需要RVDS
和MemMang
文件夹,删除除此以外的全部内容即可。
同时删除RVDS
文件夹内不是自己MCU架构的文件夹,因为用不到。
然后添加剩下的代码文件到你的Keil工程,但是MemMang
文件夹里的代码只需要也只能添加一个heapX.c
(根据需求)。
注意:要是用heap5,一定要在初始化OS前定义好内存区域(如下)。
1 | static uint8_t heap[1024*10]; |
可能遇到的报错
此时如果编译,会报错找不到FreeRTOSConfig.h
。因此,需要去GitHub再下载个示例包(也就是上文说的主线最新包)。
在https://github.com/FreeRTOS/FreeRTOS-Kernel/tree/main的FreeRTOS-Kernel-main/examples/template_configuration
文件夹里面可以找到FreeRTOSConfig.h
,将其添加到Keil工程即解决。
此时可能报错:
1 |
对此报错,在刚刚引入的头文件添加#define configUSE_16_BIT_TICKS 1
即可解决,但这将导致系统Tick使用16Bit计数;或添加#define configUSE_16_BIT_TICKS 0
,令系统Tick使用32Bit计数变量。
然后可能会报一些链接器错误,如:
1 | stmf4_clk\stmf4_clk.axf: Error: L6218E: Undefined symbol vApplicationGetIdleTaskMemory (referred from tasks.o). |
一般也是config没设置好,可以关闭相关功能,或在FreeRTOSConfig.h自行补齐相关函数,如静态分配及栈溢出检测
1 |
后续大部分报错可以参考 https://www.freertos.org/zh-cn-cmn-s/RTOS-Cortex-M3-M4.html,一般是config默认设置不合适导致的,请修改配置文件直到编译结果无错误。
对接必要的中断函数
然后对接相关中断函数,主要是SVCHandler,PendSVHandler和一个TickHandler(一般是定时器中断)。
前置知识:在 ARM Cortex-M 核心中,优先级的数值越小, 则中断的逻辑优先级越高,如优先级为0的中断大于优先级为1的中断,可以将其抢断。这一点可能很反直觉。
进入CubeMX,选择左侧System Core -> NVIC
,然后选择中间页面左上角的Code Generator,并取消system service call via SWI instruction选项和pendable request for system service选项的复选框勾选(也就是不让CubeMX生成默认的中断处理函数)。
然后修改中断优先级。需要提前确认configMAX_SYSCALL_INTERRUPT_PRIORITY
的值(上面改的,FreeRTOSConfig.h
里面有)。
此处假设为5,那么,所有需要操作FreeRTOS功能(即使是FromISR
类函数)的中断优先级必须低于5(数值上大于5),但是pendable request for system service和system service call via SWI instruction的优先级必须高于configMAX_SYSCALL_INTERRUPT_PRIORITY
数值上小于),详情可以参考Cortex-M4 权威手册
。
建立独立的供FreeRTOS使用的Tick中断
然后添加一个定时器中断,中断周期1kHz,可以按自己喜欢的方式设置Tim界面(尽量用基本定时器,就一个全局中断,操作更简单,后续实例就是全局中断)。由于HAL库的Tick默认依赖SysTick,故避免冲突将FreeRTOS的Tick改由独立的定时器产生。
然后回到NVIC界面调中断优先级,数值上大于configMAX_SYSCALL_INTERRUPT_PRIORITY
的值。
然后生成一次代码,目的是确认HAL如何处理该中断。
复制Core/Src/stm32f4xx_it.c
里面和定时器中断的相关代码备用。
1 | /** |
回到CubeMX的NVIC界面,进入Code Generator页面取消勾选刚刚你选择的定时器(本例为TIM5)的右侧的两个勾(即不生成中断处理函数,也不调用HAL函数)。
然后生成代码,启动Keil。
打开FreeRTOS-Kernel/portable/RVDS/ARM_CM4F/port.c
,将vPortSVCHandler
指向的函数名修改为SVC_Handler
,将xPortPendSVHandler
指向的函数名修改为PendSV_Handler
,将xPortSysTickHandler
指向的函数名修改为__你的定时器中断的函数名__
,如本例中的TIM5中断函数名为TIM5_IRQHandler
,并在函数中添加上文复制出来备用的HAL调用(去IRQHandle里面看看,可以扒出最简单的结构),一般是清Flag,如:
1 |
|
注:这些函数名都在启动文件startup_xxxxx.s这个汇编文件里面有写。
后记代码
main函数相关代码:
1 | __HAL_TIM_CLEAR_IT(&htim5, TIM_IT_UPDATE); |
最终配置的中断优先级:
1 | configMAX_SYSCALL_INTERRUPT_PRIORITY:4 |
给CubeMX生成的Keil STM32工程移植主线FreeRTOS