tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第6章 中断管理" author: "尚硅谷研究院" date: 2026-07-17
用生活理解:中断就像领导突然敲门安排紧急任务——你得先把手头工作保存好(保存上下文),处理完紧急任务再回来继续。临界区则像"开会时手机静音"——某些关键时刻不允许被打断。
FreeRTOS 需要管理中断以确保系统的实时性和确定性。其核心思路是:
FreeRTOS 提供两个层次的中断控制:
1. 临界段保护(完整中断屏蔽)
| API | 描述 |
|---|---|
taskENTER_CRITICAL() |
进入临界区,关闭所有中断(有嵌套计数) |
taskEXIT_CRITICAL() |
退出临界区,恢复之前的中断状态 |
taskENTER_CRITICAL_FROM_ISR() |
从中断中进入临界区 |
taskEXIT_CRITICAL_FROM_ISR() |
从中断中退出临界区 |
任务级临界区内部维护一个嵌套计数器(uxCriticalNesting):
2. 中断开关(直接操作)
| API | 描述 |
|---|---|
portDISABLE_INTERRUPTS() |
直接关闭所有中断 |
portENABLE_INTERRUPTS() |
直接打开所有中断 |
| 注意:无嵌套保护,不推荐用于临界段 |
3. BASEPRI 优先级屏蔽
FreeRTOS 使用 Cortex-M3 的 BASEPRI 寄存器实现优先级屏蔽:
configKERNEL_INTERRUPT_PRIORITY = 15 << 4 // 内核中断优先级(最低)
configMAX_SYSCALL_INTERRUPT_PRIORITY = 5 << 4 // API可调用的最高中断优先级
4. 延后中断处理(Deferred Interrupt Processing)
在 ISR 中只做最少的处理(记录事件、发送信号量/消息到队列),将耗时逻辑推迟到任务中执行。这是 RTOS 中推荐的中断处理模式。
portDISABLE_INTERRUPTS() 关闭中断(LED1 停止响应)portENABLE_INTERRUPTS() 打开中断(LED1 恢复)vTaskDelay() 和 HAL_Delay() 的行为差异File: 04_中断管理/Core/Src/freertos_demo.c
#include "freertos_demo.h"
#include "FreeRTOS.h"
#include "task.h"
#include "LED.h"
#include "Key.h"
/* 起始任务配置 */
#define START_TASK_STACK 128
#define START_TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void *pvParameters);
/* 任务1配置 */
#define TASK1_STACK 128
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void *pvParameters);
void freertos_start(void)
{
/* 动态创建起始任务 */
xTaskCreate((TaskFunction_t)start_task,
(char *)"start_task",
(configSTACK_DEPTH_TYPE)START_TASK_STACK,
(void *)NULL,
(UBaseType_t)START_TASK_PRIORITY,
(TaskHandle_t *)&start_task_handle);
/* 启动调度器 */
vTaskStartScheduler();
}
void start_task(void *pvParameters)
{
/* 进入临界区,防止创建过程被打断 */
taskENTER_CRITICAL();
/* 创建1个任务 */
xTaskCreate((TaskFunction_t)task1, (char *)"task1",
(configSTACK_DEPTH_TYPE)TASK1_STACK, (void *)NULL,
(UBaseType_t)TASK1_PRIORITY, (TaskHandle_t *)&task1_handle);
/* 删除起始任务自身 */
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
/**
* @description: 任务1——按键控制中断开关
* KEY1 → portDISABLE_INTERRUPTS() 关闭中断
* KEY2 → portENABLE_INTERRUPTS() 打开中断
* 使用HAL_Delay模拟工作负载(非vTaskDelay)
*/
void task1(void *pvParameters)
{
uint8_t key = 0;
while (1)
{
key = Key_Detect();
if (key == KEY1_PRESS)
{
/* KEY1按下:关闭所有中断——系统将无法响应任何中断 */
printf(">>>>关闭中断.....\r\n");
portDISABLE_INTERRUPTS();
}
else if (key == KEY2_PRESS)
{
/* KEY2按下:打开所有中断 */
printf(">>>>打开中断.....\r\n");
portENABLE_INTERRUPTS();
}
/* 注意:这里使用HAL_Delay而非vTaskDelay
HAL_Delay依赖SysTick中断,如果中断被关闭,HAL_Delay将永远卡死!
vTaskDelay不依赖中断,在中断关闭时仍能正常工作 */
HAL_Delay(500);
}
}
FreeRTOSConfig.h(中断优先级配置):
/* Cortex-M3 中断优先级配置 */
/* 内核中断优先级:最低优先级 15<<4 = 240(数值最大,优先级最低) */
#define configKERNEL_INTERRUPT_PRIORITY (15 << 4)
/* 系统调用中断优先级:优先级 5 以上的中断可以调用 FreeRTOS API
优先级为 0-4 的中断被屏蔽,不能调用 FreeRTOS API
数值越小优先级越高:0 > 5 > 15 */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (5 << 4)
/* 别名配置 */
#define configMAX_API_CALL_INTERRUPT_PRIORITY configMAX_SYSCALL_INTERRUPT_PRIORITY
| Function | Parameters | Return | Description |
|---|---|---|---|
taskENTER_CRITICAL() |
无 | void | 进入临界区(支持嵌套) |
taskEXIT_CRITICAL() |
无 | void | 退出临界区(嵌套计数减一) |
taskENTER_CRITICAL_FROM_ISR() |
无 | uint32_t | 从中断进入临界区,返回中断屏蔽状态 |
taskEXIT_CRITICAL_FROM_ISR(ux) |
uxSavedMaskStatus | void | 从中断退出临界区,恢复中断状态 |
portDISABLE_INTERRUPTS() |
无 | void | 直接关闭所有中断 |
portENABLE_INTERRUPTS() |
无 | void | 直接打开所有中断 |
portDISABLE_INTERRUPTS_FROM_ISR() |
无 | UBaseType_t | ISR中关闭中断 |
portENABLE_INTERRUPTS_FROM_ISR(ux) |
uxSavedStatus | void | ISR中恢复中断 |
HAL_Delay() 依赖 SysTick 中断更新 uwTick 变量。如果通过 portDISABLE_INTERRUPTS() 关闭了中断,SysTick 无法触发,HAL_Delay 将永远死等。taskENTER_CRITICAL() 必须有对应的 taskEXIT_CRITICAL(),否则中断永久关闭。taskENTER_CRITICAL() 不同,portDISABLE_INTERRUPTS() 不维护嵌套计数,在嵌套使用时可能会导致提前恢复中断。5 << 4 = 0x50。FromISR 后缀的版本,否则可能导致内核数据结构损坏。