200字
RTOS中断管理
2024-02-29
2026-03-23

一、中断介绍(了解)

什么是中断?

让CPU打断正常运行的程序,转而去处理紧急的事件(程序),就叫中断

image

中断执行机制

答:可简单概括为三步:

  1. 中断请求 :外设产生中断请求(如:GPIO外部中断、定时器中断等)。

  2. 响应中断 :CPU停止执行当前程序,转而执行中断处理程序(ISR)。

  3. 退出中断 :执行完毕,返回被打断的程序处,继续往下执行。

    image

二、中断优先级分组设置(熟悉)

中断优先级分组介绍

ARM Cortex-M 使用了 8 位宽的寄存器来配置中断的优先等级,这个寄存器就是中断优先级配置寄存器

但STM32,只用了中断优先级配置寄存器的高4位 [7 : 4],所以STM32提供了最大16级的中断优先等级

image

什么是抢占优先级,什么是子优先级

STM32 的中断优先级可以分为抢占优先级子优先级

抢占优先级: 抢占优先级高的中断可以打断正在执行但抢占优先级低的中断

子优先级:当同时发生具有相同抢占优先级的两个中断时,子优先级数值小的优先执行

注意 :中断优先级(抢占优先级和子优先级)数值越小,优先级越高。

注意:中断优先注意 :中断优先级(抢占优先级和子优先级)数值越小,优先级越高。级数值越小越优先

中断优先级配置方式

一共有5种配置方式,对应着中断优先级分组的5个组。

优先级分组 抢占优先级 子优先级 优先级配置寄存器高4位
NVIC_PriorityGroup_0 0级抢占优先级 0-15级子优先级 0bit用于抢占优先级
4bit用于子优先级
NVIC_PriorityGroup_1 0-1级抢占优先级 0-7级子优先级 1bit用于抢占优先级
3bit用于子优先级
NVIC_PriorityGroup_2 0-3级抢占优先级 0-3级子优先级 2bit用于抢占优先级
2bit用于子优先级
NVIC_PriorityGroup_3 0-7级抢占优先级 0-1级子优先级 3bit用于抢占优先级
1bit用于子优先级
NVIC_PriorityGroup_4 0-15级抢占优先级 0级子优先级 4bit用于抢占优先级
0bit用于子优先级

注意:FreeRTOS中为了方便管理,采用第4号(NVIC_PriorityGroup_4)分配方式。

FreeRTOS中对中断优先级的管理

通过调用函数HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4)在HAL_Init中即可完成设置

特点:

  1. 低于configMAX_SYSCALL_INTERRUPT_PRIORITY优先级的中断才允许调用FreeRTOS 的API函数

  2. 建议将所有优先级位指定为抢占优先级位,方便FreeRTOS管理

    (调用函数HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4)

  3. 中断优先级数值越小越优先,任务优先级数值越大越优先

image

三、中断相关寄存器(熟悉)

关中断:portDISABLE_INTERRUPTS()

开中断: portENABLE_INTERRUPTS()

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

SHPR1寄存器地址:0xE000ED18

SHPR2寄存器地址:0xE000ED1C

SHPR3寄存器地址:0xE000ED20

image

	表出自:《Cortex M3权威指南(中文)》第286页

注意:FreeRTOS主要是使用SHPR3寄存器对PendSVSystick中断优先级进行设置(设置为最低优先级)。

为什么将PendSV和SysTick设置最低优先级

答:保证系统任务切换不会阻塞系统其他中断的响应。

三个中断屏蔽寄存器

PRIMASK、 FAULTMASK 和BASEPRI

image

FreeRTOS所使用的中断管理就是利用的BASEPRI这个寄存器

BASEPRI:屏蔽优先级低于某一个阈值的中断,当设置为0时,则不关闭任何中断

比如: BASEPRI设置为0x50,代表中断优先级在515内的均被屏蔽,04的中断优先级正常执行

当BASEPRI设置为0x50时:

image

在中断服务函数中调度FreeRTOS的API函数需注意:

1、中断服务函数的优先级需在FreeRTOS所管理的范围内

2、在中断服务函数里边需调用FreeRTOS的API函数,必须使用带“FromISR”后缀的函数

实践

FreeRTOS中断管理实验

image

初始化定时器

  1. 初始化定时器句柄

    TIM_HandleTypeDef g_timx_handle; /* 定时器句柄 */

  2. 基本定时器TIMX定时中断初始化函数

    void btim_timx_int_init(uint16_t arr, uint16_t psc)
    {
        g_timx_handle.Instance = BTIM_TIMX_INT;                      /* 通用定时器X */
        g_timx_handle.Init.Prescaler = psc;                          /* 设置预分频系数 */
        g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP;         /* 递增计数模式 */
        g_timx_handle.Init.Period = arr;                             /* 自动装载值 */
        HAL_TIM_Base_Init(&g_timx_handle);
    
        HAL_TIM_Base_Start_IT(&g_timx_handle);    /* 使能定时器x及其更新中断 */
    }
    
  3. 定时器底层驱动,开启时钟,设置中断优先级

    void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
    {
        if (htim->Instance == BTIM_TIMX_INT)
        {
            BTIM_TIMX_INT_CLK_ENABLE();                     /* 使能TIM时钟 */
            HAL_NVIC_SetPriority(BTIM_TIMX_INT_IRQn, 6, 0); /* 抢占6,子优先级0,组2 */
            HAL_NVIC_EnableIRQ(BTIM_TIMX_INT_IRQn);         /* 开启ITM6中断 */
        }
    }
    
  4. 定时器TIMX中断服务函数

    void BTIM_TIMX_INT_IRQHandler(void)
    {
        HAL_TIM_IRQHandler(&g_timx_handle); /* 定时器中断公共处理函数 */
    }
    
  5. 定时器更新中断回调函数

    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
          if (htim->Instance == BTIM_TIMX_INT)
        {
            printf("TIM6优先级为6的正在运行!!!\r\n");
        }
    }
    
  6. 启动定时器btim_timx_int_init(10000 - 1,3600 - 1 );

    定时器时钟频率(90M即90000000)/预分配次数/自动重装载值=秒

main.c

    btim_timx_int_init(10000 - 1,3600 - 1 );
    btim_tim7_int_init(10000 - 1,3600 - 1 );

btim.c


#include "./BSP/LED/led.h"
#include "./BSP/TIMER/btim.h"
#include "./SYSTEM/usart/usart.h"

TIM_HandleTypeDef g_timx_handle;  /* 定时器句柄 */
TIM_HandleTypeDef g_tim7_handle;  /* 定时器句柄 */

/**
 * @brief       基本定时器TIMX定时中断初始化函数
 * @note
 *              基本定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
 *              基本定时器的时钟为APB1时钟的2倍, 而APB1为36M, 所以定时器时钟 = 72Mhz
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft=定时器工作频率,单位:Mhz
 *
 * @param       arr: 自动重装值。
 * @param       psc: 时钟预分频数
 * @retval      无
 */
void btim_timx_int_init(uint16_t arr, uint16_t psc)
{
    g_timx_handle.Instance = BTIM_TIMX_INT;                      /* 通用定时器X */
    g_timx_handle.Init.Prescaler = psc;                          /* 设置预分频系数 */
    g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP;         /* 递增计数模式 */
    g_timx_handle.Init.Period = arr;                             /* 自动装载值 */
    HAL_TIM_Base_Init(&g_timx_handle);

    HAL_TIM_Base_Start_IT(&g_timx_handle);    /* 使能定时器x及其更新中断 */
}

void btim_tim7_int_init(uint16_t arr, uint16_t psc)
{
    g_tim7_handle.Instance = BTIM_TIM7_INT;                      /* 通用定时器X */
    g_tim7_handle.Init.Prescaler = psc;                          /* 设置预分频系数 */
    g_tim7_handle.Init.CounterMode = TIM_COUNTERMODE_UP;         /* 递增计数模式 */
    g_tim7_handle.Init.Period = arr;                             /* 自动装载值 */
    HAL_TIM_Base_Init(&g_tim7_handle);

    HAL_TIM_Base_Start_IT(&g_tim7_handle);    /* 使能定时器7及其更新中断 */
}

/**
 * @brief       定时器底层驱动,开启时钟,设置中断优先级
                此函数会被HAL_TIM_Base_Init()函数调用
 * @param       htim:定时器句柄
 * @retval      无
 */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == BTIM_TIMX_INT)
    {
        BTIM_TIMX_INT_CLK_ENABLE();                     /* 使能TIM时钟 */
        HAL_NVIC_SetPriority(BTIM_TIMX_INT_IRQn, 6, 0); /* 抢占6,子优先级0,组2 */
        HAL_NVIC_EnableIRQ(BTIM_TIMX_INT_IRQn);         /* 开启ITM6中断 */
    }
    if(htim->Instance == BTIM_TIM7_INT)
     {
        BTIM_TIM7_INT_CLK_ENABLE();                     /* 使能TIM时钟 */
        HAL_NVIC_SetPriority(BTIM_TIM7_INT_IRQn, 4, 0); /* 抢占4,子优先级0,组2 */
        HAL_NVIC_EnableIRQ(BTIM_TIM7_INT_IRQn);         /* 开启ITM7中断 */
      }
}

/**
 * @brief       定时器TIMX中断服务函数
 * @param       无
 * @retval      无
 */
void BTIM_TIMX_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_timx_handle); /* 定时器中断公共处理函数 */
}
void BTIM_TIM7_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_tim7_handle); /* 定时器中断公共处理函数 */
}
/**
 * @brief       定时器更新中断回调函数
 * @param       htim:定时器句柄
 * @retval      无
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
      if (htim->Instance == BTIM_TIMX_INT)
    {
        printf("TIM6优先级为6的正在运行!!!\r\n");
    }else if(htim->Instance == BTIM_TIM7_INT)
    {
        printf("TIM7优先级为4的正在运行!!!!!\r\n");
    }
}

btim.h

#ifndef __BTIM_H
#define __BTIM_H

#include "./SYSTEM/sys/sys.h"

/******************************************************************************************/
/* 基本定时器 定义 */

/* TIMX 中断定义 
 * 默认是针对TIM6/TIM7
 * 注意: 通过修改这4个宏定义,可以支持TIM1~TIM8任意一个定时器.
 */
 
#define BTIM_TIMX_INT                       TIM6
#define BTIM_TIMX_INT_IRQn                  TIM6_DAC_IRQn
#define BTIM_TIMX_INT_IRQHandler            TIM6_DAC_IRQHandler
#define BTIM_TIMX_INT_CLK_ENABLE()          do{ __HAL_RCC_TIM6_CLK_ENABLE(); }while(0)   /* TIM6 时钟使能 */

#define BTIM_TIM7_INT                       TIM7
#define BTIM_TIM7_INT_IRQn                  TIM7_IRQn
#define BTIM_TIM7_INT_IRQHandler            TIM7_IRQHandler
#define BTIM_TIM7_INT_CLK_ENABLE()          do{ __HAL_RCC_TIM7_CLK_ENABLE(); }while(0)   /* TIM7 时钟使能 */

/******************************************************************************************/

void btim_timx_int_init(uint16_t arr, uint16_t psc);    /* 基本定时器 定时中断初始化函数 */
void btim_tim7_int_init(uint16_t arr, uint16_t psc); 
#endif

freertos_demo.c

void TASK1(void *pvParameters)
{
    uint8_t task_num=0;
    while(1)
    {
        if(++task_num==5)
        {
           task_num=0;
           printf("关中断!!\r\n");
           portDISABLE_INTERRUPTS();
           delay_ms(5000);
           printf("开中断!!!\r\n");
           portENABLE_INTERRUPTS();
        }
        vTaskDelay(1000);
    }
}

评论