当前位置: 首页 > news >正文

FreeRTOS中断服务程序(ISR)详细讲解

个人博客:blogs.wurp.top

简介

1、什么是中断服务程序(ISR):

  • 中断服务程序(Interrupt Service Routine, ISR)是用于处理硬件中断的函数。当某个外设(如定时器、串口、GPIO等)触发中断时,CPU会暂停当前执行的任务,转而执行对应的ISR。

2、主要特点

  • 快速响应:ISR必须尽可能快地执行完毕,以减少对系统实时性的影响。
  • 不能阻塞:ISR中不能调用任何可能引起阻塞的函数(如vTaskDelay())。
  • 使用特定API:FreeRTOS提供了专门的API来在ISR中与任务通信,例如xQueueSendFromISR()xSemaphoreGiveFromISR()

3、FreeRTOS中的中断处理机制:

  • FreeRTOS 提供了完整的中断处理支持,包括中断服务程序(ISR)的编写、中断优先级管理以及与任务之间的交互。

中断的基本概念

1、中断的触发与响应

  • 中断 是一种由硬件或软件触发的事件,用于引起处理器的响应。
  • 中断源 可以是定时器、外设(如串口、ADC)、异常(如除零错误)等。

2、中断优先级与嵌套

  • 每个中断都有一个优先级,用于决定多个中断同时发生时的处理顺序。
  • FreeRTOS 支持多级中断优先级管理。

FreeRTOS中中断服务程序的结构

1、中断服务函数的定义

void vMyISR(void)
{// 1. 保存寄存器(可选,取决于具体硬件)portSAVE_CONTEXT();// 2. 处理中断逻辑// 例如:读取中断源、清除中断标志等// 3. 如果需要触发任务切换,使用 FreeRTOS 提供的函数if (xHigherPriorityTaskWoken == pdTRUE) {portYIELD_FROM_ISR();}// 4. 恢复寄存器(可选,取决于具体硬件)portRESTORE_CONTEXT();
}

portSAVE_CONTEXT() portRESTORE_CONTEXT() 是用于保存和恢复处理器寄存器的宏,确保中断处理过程中不会破坏任务的上下文。

portYIELD_FROM_ISR() 是 FreeRTOS中用于在中断中请求任务调度的函数。只有在中断处理过程中有更高优先级的任务被唤醒时才调用。

在 FreeRTOS 中,不能在中断服务程序中直接调用任何 FreeRTOS API 函数,除非它们是专门设计用于中断上下文的(如 xQueueSendFromISR()xSemaphoreGiveFromISR())。

2、使用portENTER_CRITICAL()和portEXIT_CRITICAL()保护临界区

portENTER_CRITICAL():进入临界区,通常会禁用所有中断(具体实现依赖于硬件平台)。在此之后执行的代码将不会被中断。

portEXIT_CRITICAL():退出临界区,恢复中断状态。此时允许中断和任务切换继续发生。

portENTER_CRITICAL();
// 临界区代码,例如修改共享变量
x = x + 1;
portEXIT_CRITICAL();

3、中断服务程序中的任务通知与队列操作

void vTimerISR(void) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;// 向任务发送通知xTaskNotifyFromISR(xTaskHandle, 0x1234, eSetBits, &xHigherPriorityTaskWoken);// 如果需要切换上下文portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

在FreeRTOS中使用中断

1、配置中断源

  • 使用 NVIC_EnableIRQ() 启用特定的中断源。
  • 在 FreeRTOS 中,中断优先级的配置通常依赖于所使用的处理器架构(如 ARM Cortex-M 系列)。
  • 使用 NVIC_SetPriority() 函数设置中断优先级。
  • 需要将中断优先级设置为低于内核中断(如 PendSV 和 SysTick),以避免抢占内核任务。

2、注册中断服务函数

  • 每个中断源都需要一个对应的中断服务函数。
  • 在 ISR 中,应尽量减少处理时间,并避免调用 FreeRTOS API 函数(除非使用专门的中断安全函数)。
  • 如果需要在 ISR 中触发任务调度,可以使用 xTaskNotifyFromISR()xSemaphoreGiveFromISR() 等函数。

3、使用FreeRTOS提供的中断API

// 设置某个中断的优先级
NVIC_SetPriority(USART1_IRQn, configLIBRARY_KERNEL_INTERRUPT_PRIORITY);// 启用中断
NVIC_EnableIRQ(USART1_IRQn);// 中断服务函数
void USART1_IRQHandler(void) {// 处理中断逻辑if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {// 读取数据uint8_t data = USART_ReceiveData(USART1);// 触发任务通知xTaskNotifyFromISR(xTaskHandle, data, eSetValue, NULL);}
}

中断服务程序的最佳实践

1、简单的中断服务程序示例

以下是一个简单的 FreeRTOS 中断服务程序示例,假设使用的是 STM32 微控制器:

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"// 定义一个二值信号量
SemaphoreHandle_t xSemaphore = NULL;// 中断服务程序
void EXTI0_IRQHandler(void)
{// 检查是否发生中断if (EXTI_GetITStatus(EXTI_Line0) != RESET){// 清除中断标志位EXTI_ClearITPendingBit(EXTI_Line0);// 使用 xSemaphoreGiveFromISR 发送信号量xSemaphoreGiveFromISR(xSemaphore, NULL);}
}// 任务函数
void vTaskFunction(void *pvParameters)
{while (1){// 等待信号量if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE){// 处理中断事件printf("Interrupt occurred!\n");}}
}int main(void)
{// 初始化系统时钟、GPIO 等// ...// 创建信号量xSemaphore = xSemaphoreCreateBinary();// 创建任务xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);// 启动调度器vTaskStartScheduler();// 如果调度器启动失败,进入死循环for (;;);
}

2、使用队列传递数据的ISR示例

// 队列定义
QueueHandle_t xQueue;// 初始化队列
xQueue = xQueueCreate(10, sizeof(int));
if (xQueue == NULL) {// 队列创建失败处理
}// 在 ISR 中发送数据
void vTimerIsr(void) {int data = 42;// 将数据发送到队列if (xQueueSendFromISR(xQueue, &data, NULL) != pdTRUE) {// 发送失败处理}
}// 在任务中接收数据
void vTaskFunction(void *pvParameters) {int receivedData;while (1) {if (xQueueReceive(xQueue, &receivedData, portMAX_DELAY) == pdTRUE) {// 处理接收到的数据printf("Received: %d\n", receivedData);}}
}

3、使用任务通知的ISR示例

#include "FreeRTOS.h"
#include "task.h"// 定义任务句柄
TaskHandle_t xTaskHandle = NULL;// 任务函数
void vTaskFunction(void *pvParameters)
{// 任务进入等待状态,等待任务通知while (1) {ulTaskNotifyTake(pdTRUE, portMAX_DELAY);// 接收到通知后的处理逻辑printf("任务接收到通知,执行操作...\n");}
}// 外设中断服务程序
void vMyInterruptHandler(void)
{// 发送任务通知给任务xTaskNotifyGiveFromISR(xTaskHandle, NULL);
}// 主函数
int main(void)
{// 创建任务xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, &xTaskHandle);// 启动调度器vTaskStartScheduler();// 不会到达这里for (;;);
}

总结

1、中断服务程序的重要性

1.1 实时响应:

  • 中断服务程序能够迅速响应外部事件(如按键、定时器溢出、传感器信号等),确保系统对突发事件的及时处理。

1.2 任务调度:

  • 在某些情况下,ISR可能会触发任务切换或通知其他任务,从而实现高效的多任务调度和资源管理。

1.3 系统稳定性:

  • 正确编写和使用ISR可以避免因中断处理不当导致的系统崩溃或死锁问题,提高系统的稳定性和可靠性。

1.4 资源管理:

  • ISR通常用于处理硬件资源(如外设寄存器、DMA传输等),确保这些资源被正确访问和释放,防止资源冲突。

1.5 性能优化:

  • 通过合理设计ISR,可以减少中断处理时间,提升系统整体性能,尤其是在高频率中断场景下。

2、FreeRTOS中ISR的设计原则

2.1 简短且快速执行

  • ISR应尽可能简短,避免长时间的执行。
  • 长时间运行的ISR可能导致系统响应延迟,甚至影响其他任务的调度。

2.2 避免使用阻塞操作

  • 在ISR中不应调用任何会导致任务挂起或等待的函数,例如vTaskDelay()、xQueueReceive()等。
  • 这些操作可能引起死锁或系统不稳定。

2.3 使用FreeRTOS提供的中断安全函数

  • FreeRTOS提供了一些专门用于ISR的函数,如:xQueueSendFromISR()、xSemaphoreGiveFromISR()、xTaskNotifyFromISR()
  • 这些函数可以在ISR中安全地与任务通信。

2.4 避免直接修改共享资源

  • 在ISR中应避免直接访问共享变量或数据结构,以免引发竞态条件。
  • 可以通过队列、信号量等方式间接传递数据。

2.5 保持中断优先级的合理分配

  • 中断优先级应根据任务的实时性要求进行合理配置。
  • 高优先级中断应尽量减少对低优先级中断的影响。

2.6 及时清除中断源

  • 在ISR中处理完中断后,应立即清除中断源,防止重复触发。

2.7 使用中断嵌套时需谨慎

  • 如果使用中断嵌套(即高优先级中断可以打断低优先级中断),需确保不会导致任务调度异常。

2.8 调试和测试

  • 在开发过程中,应充分测试ISR的行为,确保其符合预期。
  • 使用调试工具跟踪ISR的执行情况,避免潜在的错误。
http://www.dtcms.com/a/332333.html

相关文章:

  • 从ChatGPT到智能助手:Agent智能体如何颠覆AI应用
  • 基于uiautomation的自动化流程RPA开源开发演示
  • 机器学习——PCA(主成分分析)降维
  • 开源 Arkts 鸿蒙应用 开发(十五)自定义绘图控件--仪表盘
  • STM32 - Embedded IDE - GCC - 解决LWRB库在GCC编译器会编译失败,在ARMCC编译器时却正常编译
  • 【GUI】ssh实现gui本地可视
  • 公司的服务器怎么个事,服务器是什么东西
  • 系统思考:情绪内耗与思维模式
  • 开源长期记忆 短期记忆 框架调研对比19999字
  • 4.4 vue3生命周期函数
  • 解决在uniapp真机运行上i18n变量获取不到问题
  • Vue2与Vue3生命周期函数全面解析:从入门到精通
  • 【测试用例】
  • Qt 常用控件 - 9
  • 小兔鲜儿-小程序uni-app(二)
  • 手机端的音视频界面或者图片文档界面共享给大屏
  • 从源码到可执行文件:hello.c 的二进制之旅
  • Java项目基本流程(四)
  • 基于阿里云音频识别模型的网页语音识别系统实现
  • 人工智能与社会治理:从工具到生态的范式重构
  • spring中异步任务注解@Async和@scheduled的使用
  • Redis核心应用场景及代码案例
  • 璞致fpga Zynq UltraScale Plus RFSoC PZ-ZU47DR 核心板与开发板用户手册
  • StringBoot-SSE和WebFlux方式消息实时推送-默认单向-可增加交互接口
  • 力扣 hoot100 搜索二维矩阵
  • Unity 绳子插件 ObjRope 使用简记
  • 从应用场景看国产化FPGA潜力,紫光同创研讨会武汉·北京站回顾
  • Java面试实战系列【并发篇】- Semaphore深度解析与实战
  • 算法学习远程访问:借助 cpolar 内网穿透服务使用 Hello-Algo
  • Spring IOC容器在Web环境中的启动奥秘:深入源码解析