FreeRTOS 遇坑(1)
1. 问题
1.1 原理方案
STM32F04
系列上增加了 FreeRTOS
实时系统,创建了二值信号量,功能为通过 USART2
发送数据给 MCU
,MCU
通过 DMA
接收数据,在 DMA
中断服务函数内用到了 FreeRTOS API
函数,然后在 USART1
上打印出系统出结果。
这里
USART1
做了重定向。
2. 报错结果
- 通过系统报错信息
Error: ...\FreeRTOS\portable\RVDS\ARM_CM4F\port.c, 768
,也就此路径的 768 行。
3. 解决方案
NVIC_InitTypeDef NVIC_InitStr;
NVIC_InitStr.NVIC_IRQChannel = DMA1_Stream5_IRQn;
NVIC_InitStr.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStr.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStr.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStr);
- 把
UART2
的DMA
中断优先级更改大于FreeRTOSConfig.h
里所设定的宏定义configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
。
NVIC_InitTypeDef NVIC_InitStr;
NVIC_InitStr.NVIC_IRQChannel = DMA1_Stream5_IRQn;
NVIC_InitStr.NVIC_IRQChannelPreemptionPriority = 6;
NVIC_InitStr.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStr.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStr);
修改完之后,重新编译烧录,就会发现不会报错了。
4. 原理性问题
如上图报错结果,追踪到此函数出现问题,大概率能够判断是因为当前优先级大于系统最大优先级,导致报错。
configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );
能够追踪到 port.c
里,从注释可得知,这是一个 Interrupt priority
的问题:
也就是说,我们对 FreeRTOSConfig.h
中的与 FreeRTOS
中断配置有关的选项,要有一定的了解:
/* 根据 MCU 架构设置 */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
/* 此用于设置 FreeRTOS 系统可管理的最大优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* 内核中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
4.1 NVIC 中断控制器
在 FreeRTOSConfig.h
配置之前,我们要来讲一下,STM32
的 Cortex-M
处理器的中断优先级,这个是非常重要的,只有清除了解之后,才能懂得为何我们要这么去配置。
这里以 STM32F407
的 Cortex-M4
处理器为例,它拥有 4
位优先级,也就是 16
个优先级数。
这 16
个优先级在使用 FreeRTOS
系统时,全部设置为抢占优先级,那怎么设置呢?调用下面的函数,就可以实现。那为什么要把 4
位优先级全部设置成抢占优先级呢?主要是我们使用了 FreeRTOS
系统,它的中断配置没有处理亚优先级的这种情况。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
此函数放在 main 函数内部第一行中,最先开始就要告诉 RTOS 系统。
那优先级级数等级是怎么设定的?
首先它拥有 16
个优先级数,有一个优先级级别为 2
和 优先级级别为 5
的进行比较,优先级别为 2
的优先级比较高。所说的优先级别 为 n
的我们称之为数字级别,那么得出结果我们称之为逻辑级别,这点大家一定要清楚。接下来说明的,都以逻辑级别来说明。
4.2 FreeRTOS 中断级别配置
/* 根据 MCU 架构设置 */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
此宏定义是用来设置 MCU
架构的最小优先级,由于 STM32
只拥有 16
级的优先级,所以我们设置为 15
,也就是 0 ~ 15
的优先级号。
/* 此用于设置 FreeRTOS 系统可管理的最大优先级数 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
此宏定义是用来设置 FreeRTOS
系统可管理的最大优先级数,那么我们从上一个宏定义可知,我们拥有 16
级优先级。那么可以简单理解为,0 ~ 4
归 FreeRTOS
系统管理, 而 5 ~ 15 归我们用户去管理,这说明,权限肯定就有差别了。那有什么用的差别呢?
所说的 FreeRTOS API 函数是以 “FromISR” 结尾的 API 的函数。
/* 内核中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
那最后这个 FreeRTOSConfig.h
的配置,其实简单来理解,它就是对我们所设置好的内核架构及 FreeRTOS
可管理的最大优先级数进行移位,移位到我们用户可用的优先级位置上,也就是 Bit4 ~ Bit7
位。
总结:所以,我们每次使用到中断服务函数的时候,就要设置 NVIC
中断控制器,也就是中断号。那这个中断号要设置到用户管理还是系统管理的位置,就需要根据我们实际的情况去设置。如果你设置到系统管理位置,记住一件事情,绝对不允许中断当中使用 FreeRTOS API
提供的函数,也就是以 ”FromISR“
结尾的。