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、中断服务函数的定义

c 复制代码
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():退出临界区,恢复中断状态。此时允许中断和任务切换继续发生。

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

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

c 复制代码
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

c 复制代码
// 设置某个中断的优先级
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 微控制器:

c 复制代码
#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示例

c 复制代码
// 队列定义
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示例

c 复制代码
#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的执行情况,避免潜在的错误。
相关推荐
GUET_一路向前5 小时前
【C语言】解释形参void *data用法
c语言·开发语言·通用指针
pusue_the_sun6 小时前
数据结构——顺序表&&单链表oj详解
c语言·数据结构·算法·链表·顺序表
风铃7779 小时前
c/c++ Socket+共享内存实现本机进程间通信
linux·c语言
John.Lewis9 小时前
数据结构初阶(15)排序算法—交换排序(快速排序)(动图演示)
c语言·数据结构·排序算法
Cx330❀10 小时前
【数据结构初阶】--排序(三):冒泡排序、快速排序
c语言·数据结构·经验分享·算法·排序算法
lsnm10 小时前
【LINUX网络】HTTP协议基本结构、搭建自己的HTTP简单服务器
linux·运维·服务器·c语言·网络·c++·http
stone519514 小时前
TOTP算法与HOTP算法
c语言·物联网·算法·嵌入式·iot平台·智能门锁
的卢马飞快14 小时前
C语言指针(五):回调函数与 qsort 的深层关联
c语言·指针·qsort