目录
0. 前言
硬件平台:STM32F103
FreeRTOS移植教程:FreeRTOS移植STM32F103:保姆级教程
FreeRTOS任务相关:01_FreeRTOS任务的动态/静态创建与删除:保姆级教程
1. 中断管理基础知识
1.1 STM32中的中断
什么是STM32的中断?
- 中断就是当程序执行时遇到突发事件,转而去处理事件,处理完后又回到被打断的地方重新执行程序,就叫中断。
STM32中断的作用:
- 中断功能可以让系统自动识别指定事件发生,不需要CPU检测某个事件是否发生,从而减少CPU负担, 提高系统的稳定性。
- 比如当当我们需要检测一个按键是否按下时,使用CPU轮询检测会一直在while循环内检测对应引脚的信号变化,当我们对该引脚配置外部中断,即按键按下的时候即可自动触发对应的事件,从而解放CPU,让CPU可以执行其他事务。
Cortex-M3内核支持256个中断
- 16个内部中断
- 240个外部中断
- 具备256级的可编程中断设置
一般厂商都会对Cortex-M3内核进行裁剪
- STM32有84个中断
- 16个内部中断
- 68个可屏蔽中断
- 具有16级可编程中断设置
- 16种优先级
- 00最大
- 16种优先级
STM32中断优先级配置:
- STM32中,中断优先级的配置通过中断有限制配置寄存器的高4位[7:4]来配置
1.2 FreeRTOS中断管理
什么是FreeRTOS中断管理?
- 为了更灵活的任务调度和管理功能,FreeRTOS提供了自己的中断管理机制。
- FreeRTOS利用BASEPRI寄存器实现中断管理,屏蔽优先级低于阈值的中断。
- BASEPRI寄存器的作用:
- 将一些优先级低于设定值的,进行排除
- 也就是优先级的值,大于BASEPRI设置的值,就排除该中断
简单来说,FreeRTOS中断管理就是通过配置一个阈值,当开启中断管理时,优先级比阈值低的中断,就会被关闭,只留优先级比阈值高的中断。
- 比如阈值为5,那么优先级比5小的,也就是优先级高,就可以继续使用,数字比5大的,也就是优先级比5小的,中断会被关闭。
在中断服务函数中调用FreeRTOS的API函数需注意:
- 中断服务函数的优先级需在FreeRTOS所管理的分为内,阈值由configMAX_SYSCALL_INTERRUPT_PRIORITY指定。
- NVIC的优先级组设置位3,也就是4位全部用于抢占优先级
- 中断服务函数中调用FreeRTOS的函数时,该函数必须带有FromISR后缀
2. 中断管理示例流程
示例流程:
- 创建一个起始任务
- 创建一个任务1
- 扫面按键
- KEY1按下的时候关闭中断
- KEY2被按下时打开中断
- 扫面按键
- 配置1个定时器6
- 每500ms打印一次信息
2.1 在项目中配置
配置流程:
- 创建定时器驱动文件
- Driver_TIM6.h
- Driver_TIM6.c
- Keil配置项目结构
- 配置timer目录
- 配置timer头文件路径
- 编写定时器驱动驱动寄存器
2.1.1 创建定时器驱动文件
在Driver文件夹中创建timer文件夹,用于存放定时器的驱动文件。
2.1.2 Keil配置项目结构
将驱动文件添加进Driver文件夹
将timer路径添加进头文件路径中:
2.1.3 编写定时器驱动驱动寄存器
定时器初始化函数:
中断处理函数:
2.2 创建任务与配置中断管理
- 配置FreeRTOS中断管理
- 起始任务
- 用于创建任务1
- 任务1
- 扫描按键1:关闭中断
- 扫描按键2:启动中断
2.2.1 配置FreeRTOS中断管理
配置中断管理的阈值为5,配置滴答定时器的中断优先级为15,配置内核中断优先级为最低级。
2.2.2 起始任务
在启动函数中启动定时器,同时创建起始任务,在起始任务中创建任务1。
void FreeRTOS_Start(void)
{
/* 启动定时器 */
Driver_TIM6_Init();
Driver_TIM6_Start();
/* 1. 创建启动任务 */
xTaskCreate(startTask, /* 启动任务执行函数 */
START_TASK_NAME, /* 启动任务的名称 */
START_TASK_STACK_DEPTH, /* 启动任务栈大小 = 2 * 128 */
NULL, /* 启动任务优先级 */
START_TASK_PRIORITY, /* 启动任务的控制块 */
&startTaskTCB);
printf("创建任务......");
/* 2. 启动任务调度器 */
vTaskStartScheduler();
}
void startTask(void *args)
{
// 1. 代码进入临界区,防止代码中断
vPortEnterCritical();
printf("执行启动任务...");
// 2. 创建3个任务
xTaskCreate( /* 任务1 */
taskFunc1,
TASK_NAME_1,
START_TASK_STACK_DEPTH,
NULL,
TASK1_PRIORITY,
&task1TCB);
// 3. 代码退出临界区
printf("启动任务执行完毕...\n回收启动任务...");
vPortExitCritical();
// 4. 回收启动任务
vTaskDelete(NULL);
}
2.2.3 任务1
在任务1中进行扫描按键:
- 按键1:关闭中断
- 按键2:开启中断
void taskFunc1(void *args) /* 任务1执行函数 */
{
uint8_t key;
while (1)
{
key = Inf_Key_Detect();
switch (key)
{
case KEY1_PRESS: /* 按下1,关闭中断 */
portDISABLE_INTERRUPTS();
debug_printfln("关闭中断...");
break;
case KEY2_PRESS: /* 按下2,开启中断 */
portENABLE_INTERRUPTS();
debug_printfln("开启中断...");
break;
}
vTaskDelay(20);
}
}
运行效果:
注意事项,中断的原理是让代码进入了灵界去,因此,在关闭了中断后,不能使用vTaskDelay延时函数,这个函数本身会关闭临界区,从而导致关闭中断失败
3. 总结
FreeRTOS中断管理是为了让系统更加灵活实现任务调度等功能,提高系统的实时性和稳定性,FreeRTOS是通过配置BASEPRI寄存器达到效果的,该寄存器的作用就是让阈值以外的优先级的中断不可用。
portDISABLE_INTERRUPTS函数本身是让代码进入临界区,而vTaskDelay也是使用了相同的原理,因此会让关闭中断效果失效。
文章声明:
- 本文章为作者学习FreeRTOS实时操作系统的笔记与巩固输出,欢迎同行交流与指教,文章借鉴来源小破站的某谷RTOS教程。