给CubeMX生成的Keil STM32工程移植主线FreeRTOS

给CubeMX生成的Keil STM32工程移植主线FreeRTOS

需要的东西

本文以freertos 202210.01-lts为例。

需要下载主线最新包(为了example)和一个稳定版(os内核源码)。
以LTS版本发布包作为内核源码为例,需要/FreeRTOS-LTS/FreeRTOS/FreeRTOS-Kernel这个文件夹里面的东西。
对于keil开发环境,portable文件夹下只需要RVDSMemMang文件夹,删除除此以外的全部内容即可。
同时删除RVDS文件夹内不是自己MCU架构的文件夹,因为用不到。
然后添加剩下的代码文件到你的Keil工程,但是MemMang文件夹里的代码只需要也只能添加一个heapX.c(根据需求)。
注意:要是用heap5,一定要在初始化OS前定义好内存区域(如下)。

1
2
3
4
5
6
7
8
9
10
11
12
13
static uint8_t heap[1024*10];
/* Create an array of HeapRegion_t definitions, with an index for each of the three
RAM regions, and terminating the array with a NULL address. The HeapRegion_t
structures must appear in start address order, with the structure that contains the
lowest start address appearing first. */
const HeapRegion_t xHeapRegions[] =
{
{heap, 10240},
{NULL, 0} /* Marks the end of the array. */
};
/* Initialize heap_5. */
vPortDefineHeapRegions(xHeapRegions);

可能遇到的报错

此时如果编译,会报错找不到FreeRTOSConfig.h。因此,需要去GitHub再下载个示例包(也就是上文说的主线最新包)。
https://github.com/FreeRTOS/FreeRTOS-Kernel/tree/main
FreeRTOS-Kernel-main/examples/template_configuration文件夹里面可以找到FreeRTOSConfig.h,将其添加到Keil工程即解决。

此时可能报错:

1
#error Missing definition:  configUSE_16_BIT_TICKS must be defined in FreeRTOSConfig.h as either 1 or 0.  See the Configuration section of the FreeRTOS API documentation for details.

对此报错,在刚刚引入的头文件添加#define configUSE_16_BIT_TICKS 1即可解决,但这将导致系统Tick使用16Bit计数;或添加#define configUSE_16_BIT_TICKS 0,令系统Tick使用32Bit计数变量。

然后可能会报一些链接器错误,如:

1
2
stmf4_clk\stmf4_clk.axf: Error: L6218E: Undefined symbol vApplicationGetIdleTaskMemory (referred from tasks.o).
die450_h743_aio\die450_h743_aio.axf: Error: L6218E: Undefined symbol vApplicationStackOverflowHook (referred from tasks.o).

一般也是config没设置好,可以关闭相关功能,或在FreeRTOSConfig.h自行补齐相关函数,如静态分配及栈溢出检测

1
2
#define configSUPPORT_STATIC_ALLOCATION      0
#define configCHECK_FOR_STACK_OVERFLOW 0

后续大部分报错可以参考 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
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @brief This function handles TIM5 global interrupt.
*/
void TIM5_IRQHandler(void)
{
/* USER CODE BEGIN TIM5_IRQn 0 */

/* USER CODE END TIM5_IRQn 0 */
HAL_TIM_IRQHandler(&htim5);
/* USER CODE BEGIN TIM5_IRQn 1 */

/* USER CODE END TIM5_IRQn 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "stm32f4xx_hal.h"//必须第一个定义,要不玄学报错找不到HAL_status 的类型
#include "stm32f4xx_hal_tim.h"
#include "tim.h"

//......


if (__HAL_TIM_GET_FLAG(&htim5, TIM_FLAG_UPDATE) != RESET)
{
if (__HAL_TIM_GET_IT_SOURCE(&htim5, TIM_IT_UPDATE) != RESET)
{
__HAL_TIM_CLEAR_IT(&htim5, TIM_IT_UPDATE);

}
}


///////////////////////////////////////////////////////////////////////////////
void TIM5_IRQHandler( void )
{

// stm32 tim interrupt process
if (__HAL_TIM_GET_FLAG(&htim5, TIM_FLAG_UPDATE) != RESET)
{
if (__HAL_TIM_GET_IT_SOURCE(&htim5, TIM_IT_UPDATE) != RESET)
{
__HAL_TIM_CLEAR_IT(&htim5, TIM_IT_UPDATE);

/* The SysTick runs at the lowest interrupt priority, so when this interrupt
* executes all interrupts must be unmasked. There is therefore no need to
* save and then restore the interrupt mask value as its value is already
* known - therefore the slightly faster vPortRaiseBASEPRI() function is used
* in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
vPortRaiseBASEPRI();
{
/* Increment the RTOS tick. */
if (xTaskIncrementTick() != pdFALSE)
{
/* A context switch is required. Context switching is performed in
* the PendSV interrupt. Pend the PendSV interrupt. */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}

vPortClearBASEPRIFromISR();
}
}

// end
}

注:这些函数名都在启动文件startup_xxxxx.s这个汇编文件里面有写。

后记代码

main函数相关代码:

1
2
3
4
5
6
7
8
__HAL_TIM_CLEAR_IT(&htim5, TIM_IT_UPDATE);
HAL_TIM_Base_Start_IT(&htim5);

/* Start the scheduler. */
xTaskCreate(task1, "1", 512, NULL, 1, NULL);
xTaskCreate(task2, "1", 512, NULL, 1, NULL);

vTaskStartScheduler();

最终配置的中断优先级:

1
2
3
4
5
configMAX_SYSCALL_INTERRUPT_PRIORITY:4
SVC_Call: 0
PendSV: 3
TIM5: 6
其他RTOS要用到的外设: 6

给CubeMX生成的Keil STM32工程移植主线FreeRTOS

https://wuxiproj.mzy7.cn/posts/f4f72065.html

作者

【社区】UM4I

发布于

2024-08-10

更新于

2024-12-07

许可协议

CC BY-NC-SA 4.0

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×