欢迎光临
我们一直在努力

FreeRTOS 中断管理实战:BASEPRI 寄存器实验

本文基于正点原子 FreeRTOS 开发指南 V1.10 第 4 章 4.1~4.4 内容,搭建可直接复制粘贴运行的 STM32 FreeRTOS 中断测试工程,复现教材的 TIM3/TIM5 中断对比实验,直观理解BASEPRI寄存器、FreeRTOS 开关中断对不同优先级中断的屏蔽效果。 硬件:STM32F103(战舰 / 精英),HAL 库,FreeRTOS V10.4.6;MDK‑ARM‑AC5 编译器; 实验现象:TIM3 (抢占优先级 4,不受 FreeRTOS 管理),TIM5 (抢占优先级 6,受 FreeRTOS 管理);FreeRTOS 调用portDISABLE_INTERRUPTS()关中断时,TIM5 停止输出,TIM3 仍然正常输出。

一、原理

1.1 Cortex‑M 关键寄存器

  • BASEPRI 寄存器:设置中断屏蔽阈值,优先级数值≥阈值的中断被屏蔽;数值更小(优先级更高)不受影响,FreeRTOS 开关中断底层就是操作 BASEPRI,而不是 PRIMASK。
  • FreeRTOS 配置项configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY,本实验设置为5。
    • 抢占优先级 0~4:优先级高于阈值,不受 FreeRTOS 开关中断影响;
    • 抢占优先级 5~15:受 FreeRTOS 管理,调用portDISABLE_INTERRUPTS()会被屏蔽。
  • FreeRTOS 强制要求:中断优先级分组设置为 NVIC_PRIORITYGROUP_4,全部 4bit 用于抢占优先级,无子优先级。
  • 本实验定时器配置:

    表格

    定时器抢占优先级是否受 FreeRTOS 管理现象
    TIM3 4 ❌不受管理 FreeRTOS 关中断依然可以正常进中断
    TIM5 6 ✅受管理 FreeRTOS 关中断,中断被屏蔽,停止输出

    ⚠️重点区分: 中断优先级:数值越小优先级越高;FreeRTOS 任务优先级:数值越大优先级越高,二者逻辑相反,很多 bug 来源于此。

    1.2 实验逻辑

  • 初始化 TIM3、TIM5,1Hz 更新中断,每 1s 串口打印对应字符串;
  • 创建 task1 任务,计数到第 5 次循环,调用portDISABLE_INTERRUPTS()关闭 FreeRTOS 管理的中断,忙等待 5 秒;之后调用portENABLE_INTERRUPTS()打开中断;
  • 串口观察:关中断期间,TIM3 持续打印,TIM5 停止打印;开中断后 TIM5 恢复打印。
  • 二、工程前置条件

    本教程基于正点原子 FreeRTOS 移植好的基础工程,你需要已经完成 FreeRTOS 移植:

  • FreeRTOS 已经添加进工程,heap_4.c已加入;
  • FreeRTOSConfig.h配置关键项:
  • #define configPRIO_BITS 4
    #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
    #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
    #define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 – configPRIO_BITS) )
    #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 – configPRIO_BITS) )

  • 系统优先级分组设置HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
  • 串口 1 初始化波特率 115200;
  • 头文件:FreeRTOS.h、task.h已经可以正常包含。
  • 👉 如果还没有移植,参考正点原子 FreeRTOS 指南第二章移植章节。

    三、全部代码(freertos_demo.c,完整可复制)

    将下面全部代码直接替换你的freertos_demo.c,freertos_demo.h只声明void freertos_demo(void);

    #include "freertos_demo.h"
    #include "./SYSTEM/usart/usart.h"
    #include "./BSP/LED/led.h"
    #include "./BSP/LCD/lcd.h"
    #include "./BSP/btim/btim.h"

    /* FreeRTOS头文件 */
    #include "FreeRTOS.h"
    #include "task.h"

    /************************ 任务配置 ************************/
    #define START_TASK_PRIO 1
    #define START_STK_SIZE 128
    TaskHandle_t StartTask_Handler;
    void start_task(void *pvParameters);

    #define TASK1_PRIO 2
    #define TASK1_STK_SIZE 128
    TaskHandle_t Task1Task_Handler;
    void task1(void *pvParameters);

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

    /**
    * @brief FreeRTOS例程入口函数,main中调用
    */
    void freertos_demo(void)
    {
    lcd_show_string(10, 10, 220,32,32,"STM32", RED);
    lcd_show_string(10, 47, 220,24,24,"Interrupt Test", RED);
    lcd_show_string(10, 76, 220,16,16,"ATOM@ALIENTEK", RED);

    /* 创建开始任务 */
    xTaskCreate((TaskFunction_t )start_task,
    (const char* )"start_task",
    (uint16_t )START_STK_SIZE,
    (void* )NULL,
    (UBaseType_t )START_TASK_PRIO,
    (TaskHandle_t* )&StartTask_Handler);

    vTaskStartScheduler(); /* 开启任务调度器 */
    }

    /**
    * @brief start_task:初始化硬件、创建测试任务,然后删除自己
    */
    void start_task(void *pvParameters)
    {
    taskENTER_CRITICAL();

    /* 初始化TIM3、TIM5基本定时器更新中断,1Hz */
    btim_tim3_int_init(10000-1, 7200-1); //72M系统时钟 7200分频,10000重装载 1Hz
    btim_tim5_int_init(10000-1, 7200-1);

    /* 创建测试任务task1 */
    xTaskCreate((TaskFunction_t )task1,
    (const char* )"task1",
    (uint16_t )TASK1_STK_SIZE,
    (void* )NULL,
    (UBaseType_t )TASK1_PRIO,
    (TaskHandle_t* )&Task1Task_Handler);

    vTaskDelete(StartTask_Handler);
    taskEXIT_CRITICAL();
    }

    /**
    * @brief task1任务:触发FreeRTOS开关中断,观察TIM3 TIM5现象
    */
    void task1(void *pvParameters)
    {
    uint32_t task1_num = 0;
    while(1)
    {
    if(++task1_num == 5)
    {
    printf("———- FreeRTOS 关闭中断 portDISABLE_INTERRUPTS() ———-\\r\\n");
    portDISABLE_INTERRUPTS(); /* 操作BASEPRI,屏蔽受FreeRTOS管理中断 */

    delay_ms(5000); /* 忙延时5s!!!注意这里是裸机delay_ms,不是vTaskDelay */

    printf("———- FreeRTOS 打开中断 portENABLE_INTERRUPTS() ———-\\r\\n");
    portENABLE_INTERRUPTS(); /* 清除BASEPRI,恢复全部受管理中断 */
    }
    vTaskDelay(pdMS_TO_TICKS(1000));
    }
    }

    freertos_demo.h

    #ifndef __FREERTOS_DEMO_H
    #define __FREERTOS_DEMO_H

    void freertos_demo(void);

    #endif

    四、btim.c 定时器驱动代码(基本定时器中断)

    如果你的工程没有btim.c,直接复制下面代码;STM32F1 版本。 ⚠️重点!!这里设置 NVIC 抢占优先级:TIM3 =4,TIM5=6,这是实验的灵魂!

    #include "btim.h"
    #include "stm32f1xx_hal.h"
    #include "./SYSTEM/usart/usart.h"

    static TIM_HandleTypeDef g_tim3_handle;
    static TIM_HandleTypeDef g_tim5_handle;

    /**
    * @brief 基本定时器TIM3更新中断初始化
    * @param arr:自动重装载值
    * @param psc:预分频系数
    * @retval 无
    * @note 抢占优先级4,高于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY(5),不受FreeRTOS管理
    */
    void btim_tim3_int_init(uint16_t arr,uint16_t psc)
    {
    g_tim3_handle.Instance = TIM3;
    g_tim3_handle.Init.Prescaler = psc;
    g_tim3_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    g_tim3_handle.Init.Period = arr;
    g_tim3_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&g_tim3_handle);

    HAL_TIM_Base_Start_IT(&g_tim3_handle);

    /* 抢占优先级 4!!!! */
    HAL_NVIC_SetPriority(TIM3_IRQn,4,0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
    }

    /**
    * @brief 基本定时器TIM5更新中断初始化
    * @param arr:自动重装载值
    * @param psc:预分频系数
    * @retval 无
    * @note 抢占优先级6,在FreeRTOS管理范围内
    */
    void btim_tim5_int_init(uint16_t arr,uint16_t psc)
    {
    g_tim5_handle.Instance = TIM5;
    g_tim5_handle.Init.Prescaler = psc;
    g_tim5_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    g_tim5_handle.Init.Period = arr;
    g_tim5_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&g_tim5_handle);

    HAL_TIM_Base_Start_IT(&g_tim5_handle);

    /* 抢占优先级 6!!!! */
    HAL_NVIC_SetPriority(TIM5_IRQn,6,0);
    HAL_NVIC_EnableIRQ(TIM5_IRQn);
    }

    /**
    * @brief 定时器底层Msp回调
    */
    void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
    {
    if(htim->Instance == TIM3)
    {
    __HAL_RCC_TIM3_CLK_ENABLE();
    }
    else if(htim->Instance == TIM5)
    {
    __HAL_RCC_TIM5_CLK_ENABLE();
    }
    }

    /**
    * @brief 定时器更新中断回调函数
    */
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
    if(htim->Instance == TIM3)
    {
    printf("TIM3 输出\\r\\n");
    }
    else if(htim->Instance == TIM5)
    {
    printf("TIM5 输出\\r\\n");
    }
    }

    btim.h 头文件

    #ifndef __BTIM_H
    #define __BTIM_H
    #include "stm32f1xx_hal.h"

    void btim_tim3_int_init(uint16_t arr,uint16_t psc);
    void btim_tim5_int_init(uint16_t arr,uint16_t psc);

    #endif

    五、main.c 关键片段

    main 函数完成硬件初始化,调用freertos_demo();注意一定要设置中断优先级分组 4!

    int main(void)
    {
    HAL_Init();
    sys_stm32_clock_init(RCC_PLL_MUL9); /*72M时钟*/

    /* ✅FreeRTOS强制:优先级分组4!!!!!*/
    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

    delay_init(72);
    usart_init(115200);
    led_init();
    lcd_init();
    key_init();

    freertos_demo(); /*进入FreeRTOS应用,永不返回*/
    }

    六、实验现象说明

    串口波特率:115200

  • 关中断之前:TIM3 输出、TIM5 输出交替打印,1 秒一次;
  • TIM3 输出
    TIM5 输出
    TIM3 输出
    TIM5 输出

  • 打印———- FreeRTOS 关闭中断 portDISABLE_INTERRUPTS() ———-之后 5 秒时间内:
  • ✅ TIM3 仍然持续输出;❌ TIM5 完全停止输出。 原理:portDISABLE_INTERRUPTS()写 BASEPRI 寄存器,屏蔽优先级≥5 的中断;TIM3 抢占优先级 4 高于阈值不受影响,TIM5 优先级 6 被屏蔽。

  • 5 秒之后打印———- FreeRTOS 打开中断 portENABLE_INTERRUPTS() ———-:
  • BASEPRI 写 0,全部中断恢复,TIM5 重新开始打印输出。

    串口输出样例:

    TIM3 输出
    TIM5 输出
    TIM3 输出
    TIM5 输出
    ———- FreeRTOS 关闭中断 portDISABLE_INTERRUPTS() ———-
    TIM3 输出
    TIM3 输出
    TIM3 输出
    TIM3 输出
    TIM3 输出
    ———- FreeRTOS 打开中断 portENABLE_INTERRUPTS() ———-
    TIM5 输出
    TIM3 输出
    TIM5 输出

    七、源码底层拆解(对应 4.3 章节)

    portDISABLE_INTERRUPTS () 宏定义

    #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()

    static portFORCE_INLINE void vPortRaiseBASEPRI( void )
    {
    uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
    __asm
    {
    msr basepri, ulNewBASEPRI
    dsb
    isb
    }
    }

    本质汇编指令msr basepri, ulNewBASEPRI修改 BASEPRI 寄存器,不是 PRIMASK!不会关掉最高优先级中断。

    #define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )
    static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
    {
    __asm
    {
    msr basepri, ulBASEPRI
    }
    }

    写 0 清除 BASEPRI 屏蔽。

    赞(0)
    未经允许不得转载:171主机测评 » FreeRTOS 中断管理实战:BASEPRI 寄存器实验
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址