FreeRTOS——中断管理

目录

  • 中断管理基本概念
    • 中断处理机制
    • 中断优先级
      • 中断优先级分组设置
    • 系统中断优先级配置寄存器
    • 中断屏蔽寄存器
  • FreeRTOS中断管理实验

在 FreeRTOS 中,中断管理是确保系统高效和可靠运行的关键部分。下面对 FreeRTOS 的中断管理进行详细介绍。

中断管理基本概念

中断是一种事件驱动的机制,可以在程序执行过程中打断当前的代码流,以响应硬件或软件事件。中断管理的目标是快速处理这些事件,确保系统能及时响应外部信号。

中断处理机制

对于中断的处理机制,可以概括为三步:

  1. 中断请求:是指由硬件设备或软件程序发出的请求信号(如GPIO外部中断、定时器中断等),用于通知处理器有事件发生,需要其注意和处理。
  2. 响应中断:CPU停止执行当前程序,转而去执行中断处理程序(ISR)。
  3. 退出中断:在中断服务例程执行完成后,系统恢复到中断发生前的状态,继续执行之前的任务。

中断优先级

FreeRTOS 允许使用多种优先级管理中断。每个中断源可以配置为不同的优先级,以决定哪些中断可以抢占其他中断的执行。这一机制使得系统能够处理优先级高的任务,同时不会被低优先级的任务打断。

中断优先级分组设置

ARM Cortex-M 使用了 8 位宽的寄存器来配置中断的优先等级,这个寄存器就是中断优先级配置寄存器,因此最大中断的优先级配置范围位 0~255。但是芯片厂商一般用不完这些资源,对于 STM32,只用到了中断优先级配置寄存器的高 4 位[7:4],低四位[3:0]取零处理,因此 STM32 提供了最大 2^4=16 级的中断优先等级,如下图所示:

STM32 的中断优先级可以分为抢占优先级和子优先级(中断优先级数值越小越优先),抢占优先级和子优先级的区别如下:
抢占优先级:抢占优先级高的中断可以打断正在执行但抢占优先级低的中断,即中断嵌套。
子优先级:抢占优先级相同时,子优先级数值小的优先执行,但当子优先级低的中断正在执行时,子优先级高的中断不能打断该子优先级低的中断,即子优先级不支持中断嵌套。
STM32对于中断优先级分组一共有 5 种分配方式,如下所示:在这里插入图片描述
对于STM32,推荐使用中断优先级分组 4(NVIC_PriorityGroup_4)即优先级配置寄存器的高 4 位全部用于抢占优先级,不使用子优先级,这么一来用户就只需要设置抢占优先级即可。

HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

系统中断优先级配置寄存器

除了外部中断,系统中断有独立的中断优先级配置寄存器,分别为 SHPR1、SHPR2、SHPR3。

  1. SHPR1:SHPR1 寄存器的地址为 0xE000ED18,用于配置MemManage(内存管理异常)、BusFault(总线错误异常)、UsageFault (使用错误异常)的中断优先级。
    在这里插入图片描述
  2. SHPR2:SHPR2 寄存器的地址为 0xE000ED1C,用于配置 SVCall (Supervisor Call)的中断优先级。
    在这里插入图片描述
    SVCall用于管理任务的调度和上下文切换。
  3. SHPR3:SHPR3 寄存器的地址为 0xE000ED20,用于配置 PendSV、SysTick 的中断优先级。
    在这里插入图片描述
    PendSV:通常用于上下文切换,确保任务之间的切换能够平滑进行。
    SysTick:用于生成系统节拍,定期触发调度。

中断屏蔽寄存器

ARM Cortex-M 有三个用于屏蔽中断的寄存器,分别为 PRIMASK、FAULTMASK 和BASEPRI。

  1. PRIMASK
    用于全局中断使能/禁用。设置PRIMASK寄存器的最低位(bit 0)为1会禁用所有中断(除了NMI和硬fault可以响应),而设置为0则恢复中断的响应。
  2. FAULTMASK
    当FAULTMASK的最低位为1时,用于屏蔽所有中断,只有NMI可以响应,所有其他的异常(包括中断和fault)通通不能响应。设置为0则恢复异常的响应。
  3. BASEPRI
    BASEPRI 用于设置一个中断屏蔽的阈值,设置好 BASEPRI 后,中断优先级低于 BASEPRI 的中断就都会被屏蔽掉,FreeRTOS 就是使用 BASEPRI 寄存器来管理受 FreeRTOS管理的中断的,而不受 FreeRTOS 管理的中断,则不受 FreeRTOS 的影响。

在FreeRTOSConfig.h文件中,定义了FreeRTOS所能管理的中断优先级(5~15)

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         15                  /* 中断最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5                   /* FreeRTOS可管理的最高中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << 4 )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << 4 )
#define configMAX_API_CALL_INTERRUPT_PRIORITY           configMAX_SYSCALL_INTERRUPT_PRIORITY

BASEPRI被设置成了0x50( 5 << 4),表示中断优先级在5~15内的均被屏蔽,0-4内中断优先级正常执行。
在这里插入图片描述
注意:

  1. 中断服务函数的优先级需在FreeRTOS所管理的范围内。
  2. 在中断服务函数里边需调用FreeRTOS的API函数,必须使用带“FromISR”后缀的函数。

FreeRTOS中断管理实验

本实验使用到两个定时器,一个时优先级为4的TIM2,一个时优先级为6的TIM3。两个定时器每1s打印一段字符串,观察开中断和关中断的现象。
本文的实验代码基于FreeRTOS——任务创建和删除

void task1(void *pvParameters)
{   
  static uint32_t Task_num = 0;  
    while(1)
    {
      if(Task_num  == 5)
      {
        Task_num = 0; 
        printf("关中断!!\r\n");
        portDISABLE_INTERRUPTS();
        HAL_Delay(5000);
        printf("开中断!!!\r\n");
        portENABLE_INTERRUPTS();
      }
      Task_num ++;
      vTaskDelay(1000);                                               
    }
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(tim_baseHandle->Instance==TIM2)
  {
    __HAL_RCC_TIM2_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    HAL_NVIC_SetPriority(TIM2_IRQn, 4, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);
  }
  else if(tim_baseHandle->Instance==TIM3)
  {
    __HAL_RCC_TIM3_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    HAL_NVIC_SetPriority(TIM3_IRQn, 6, 0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
  }
}

如上文所述,FreeRTOS使用的是中断优先级分组 4,即只需要设置抢占优先级而子优先级为0。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if (htim->Instance == TIM4) {
    HAL_IncTick();
  }
    if(htim->Instance==TIM2)
    {
        printf("TIM3优先级为6的正在运行!!!\r\n");
    }
    else if(htim->Instance==TIM3)
    {
        printf("TIM2优先级为4的正在运行!!!!!\r\n");
    }
}

用于处理定时器中断的回调函数在定时器溢出或达到预设的周期时被自动调用,TIM2和TIM3每1s会进行一次打印。

中断未关闭前,TIM2和TIM3每1s打印一串字符,中断关闭后,只有TIM2每1s打印一串字符,说明了优先级为4的TIM2不在FreeRTOS所管理的中断范围内,而优先级为6的TIM3在该范围(优先级为5~15)内,受FreeRTOS管理。

### FreeRTOS 中断配置方法 在FreeRTOS环境中,中断配置依赖于底层微控制器(MCU)的硬件架构。具体到STM32平台而言,其基于ARM Cortex-M内核构建,因此FreeRTOS中的中断实际上是利用了该内核自带的嵌套向量中断控制器(NVIC)[^1]。 当考虑将应用程序从一种MCU迁移到另一种时——例如从Cortex-M系列转移到RISC-V架构下的ESP-IDF环境——需特别关注不同处理器间存在的差异性[^2]。这不仅限于指令集的不同,还包括各自特有的外设接口以及异常/中断处理模型等方面。 #### 配置要点 - **优先级设置**:确保正确设定各个中断源相对于彼此的重要性级别。对于STM32来说,NVIC允许通过调整抢占式(preemptive)响应(subroutine)两个维度上的数值来精细化控制这一过程。值得注意的是,并非所有的优先级组合都适用于所有版本的FreeRTOS;某些情况下可能需要遵循特定范围内的分配方案以维持系统的稳定性[^4]。 - **API选用**:针对不同的应用场景选择合适的队列发送接收函数形式。例如,在标准的任务上下文中可以直接调用`xQueueSend()`等常规版API完成数据传递工作;然而一旦进入到了ISR内部,则应当改用诸如`xQueueSendFromISR()`这样的特殊变体,从而避免破坏实时性的要求并保障线程安全。 - **延迟操作限制**:鉴于ISRs本质上属于异步事件驱动型逻辑单元,任何可能导致长时间阻塞的行为都是不被推荐甚至严格禁止的。这意味着像`vTaskDelay()`这类会引发当前执行流暂停等待计时期满后再继续的功能在这里并不适用。取而代之的是可以采用轻量化的方法来进行短暂延时模拟,不过最好还是尽量减少此类做法的发生频率。 ```c // 正确的做法是在 ISR 内部仅做最少量的工作, // 并尽快返回以便让其他更高优先级的服务得到及时响应。 void EXTI0_IRQHandler(void){ BaseType_t xHigherPriorityTaskWoken = pdFALSE; // 清除标志位... if(/*条件满足*/){ // 向任务发送消息而不引起调度变化 xQueueSendFromISR(queueHandle, &data, &xHigherPriorityTaskWoken); // 如果确实唤醒了一个高优先级任务则更新状态 portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } ``` ### 移植注意事项 - 考虑目标平台上是否有现成的支持库可用,如果没有的话可能还需要额外投入精力去开发相应的适配层; - 对比原生与新宿主之间关于时间基准单位定义是否存在区别,必要时做出相应转换; - 测试阶段务必覆盖全面,尤其是那些涉及多任务协作的部分,因为这些地方最容易暴露出潜在兼容性隐患。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值