七、FreeRTOS之FreeRTOS中断管理

这部分非常重要,小伙伴们必须要掌握的哈~本节需要学的内容如下:

1,什么是中断?(了解)

2,中断优先级分组设置(熟悉)

3,中断相关寄存器(熟悉)

4FreeRTOS中断管理实验(掌握)

5,课堂总结(掌握)

,什么是中断?(了解)

简介:让CPU打断正常运行的程序,转而去处理紧急的事件(程序)(其实就是ISR中断服务函数),就叫中断。

Example如下图所示,像不像屏幕前各位小伙伴呢?

七、FreeRTOS之FreeRTOS中断管理_第1张图片

中断执行机制,可简单概括为三步:

  1. 中断请求:外设产生中断请求(GPIO外部中断、定时器中断等)
  2. 响应中断:CPU停止执行当前程序,转而去执行中断处理程序(ISR)
  3. 退出中断:执行完毕,返回被打断的程序处,继续往下执行

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

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

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

七、FreeRTOS之FreeRTOS中断管理_第2张图片

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

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

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

(example:两个中断的抢占优先级相同都是2,子优先级一个是1,一个是0,明显0高于1。假设子优先级是1的中断正在执行,但是子优先级是0的中断被触发了,子优先级是0的中断不会去抢占的,因为它们的抢占优先级是相同的,会等待子优先级是1的中断执行完才会去执行子优先级是0的中断)

注意:中断优先级数值越小越优先!

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

一共有 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,全部用于抢占优先级,这样就很容易区分哪个优先级高,优先级高的可以去打断优先级低的,但是用作子优先级的,就不能实现打断操作。

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

FreeRTOS官网关于中断说明:FreeRTOS官网关于中断说明。

特点:

1、低于configMAX_SYSCALL_INTERRUPT_PRIORITY优先级的中断里才允许调用FreeRTOS 的API函数。这个宏我们设置的是15。

2、建议将所有优先级位指定为抢占优先级位,方便FreeRTOS管理。(调用函数HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4)

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

七、FreeRTOS之FreeRTOS中断管理_第3张图片

,中断相关寄存器(熟悉)

三个系统中断优先级配置寄存器,分别为 SHPR1SHPR2SHPR3

SHPR1寄存器地址:0xE000ED18

SHPR2寄存器地址:0xE000ED1C

SHPR3寄存器地址:0xE000ED20

七、FreeRTOS之FreeRTOS中断管理_第4张图片

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

下面两条需要我们尤为关注:

一个是PendSV的优先级:任务切换,任务调度都是在PendSV里面实现的。

一个是SysTick的优先级:滴答定时器的优先级设置,滴答定时器就是给我们的系统提供心跳节拍的。

比如要设置PendSV的优先级就是   SHPR3寄存器地址再偏移16位

SysTick的优先级                             SHPR3寄存器地址再左移24位

3.1 FreeRTOS如何配置PendSVSystick中断优先级?

害,我讨厌水印!我的水印盖住了地址。。。。。

 七、FreeRTOS之FreeRTOS中断管理_第5张图片

 所以:PendSVSysTick设置最低优先级

设置最低:保证系统任务切换不会阻塞系统其他中断的响应

三个中断屏蔽寄存器,分别为 PRIMASKFAULTMASK BASEPRI

七、FreeRTOS之FreeRTOS中断管理_第6张图片

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

BASEPRI屏蔽优先级低于某一个阈值的中断

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

(0x50>>4 = 0x05)

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

关中断程序示例:

七、FreeRTOS之FreeRTOS中断管理_第7张图片

configPRIO_BITS = 4  8-4 = 4 为什么左移四位就是因为优先级的设置低四位是无效的。 

中断优先级在5 ~ 15的全部被关闭

 BASEPRI设置为0x50时:

七、FreeRTOS之FreeRTOS中断管理_第8张图片

 在中断服务函数中调度FreeRTOSAPI函数需注意:

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

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

开中断程序示例:

七、FreeRTOS之FreeRTOS中断管理_第9张图片

 FreeRTOS中断管理就是利用BASEPRI寄存器实现的

FreeRTOS中断管理实验(掌握)

4.1 实验目的

        学会使用FreeRTOS的中断管理!会使用两个定时器,一个优先级为4,一个优先级为6注意:系统所管理的优先级范围:5~15

        现象:两个定时器每1s,打印一段字符串,当关中断时,停止打印,开中断时持续打印。

4.2、实验设计

        将设计2个任务:start_task、task1

   2个任务的功能如下:

  • start_task:用来创建task1任务
  • task1:中断测试任务,任务中将调用关中断和开中断函数来体现对中断的管理作用!

4.3、参考代码

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)  /* TIM6 时钟使能 */

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

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

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为45M, 所以定时器时钟 = 90Mhz
 *              定时器溢出时间计算方法: 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和定时器更新中断 */
}

/* TIM7初始化函数 */
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);                       /* 使能定时器x和定时器更新中断 */
}
/**
 * @brief       定时器底层驱动,开启时钟,设置中断优先级
                此函数会被HAL_TIM_Base_Init()函数调用
 * @param       无
 * @retval      无
 */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == BTIM_TIMX_INT)
    {
        BTIM_TIMX_INT_CLK_ENABLE();                     /* 使能TIMx时钟 */
        HAL_NVIC_SetPriority(BTIM_TIMX_INT_IRQn, 6, 0); /* 抢占6,子优先级0 */
        HAL_NVIC_EnableIRQ(BTIM_TIMX_INT_IRQn);         /* 开启ITMx中断 */
    }
    if(htim->Instance == BTIM_TIM7_INT)
    {
        BTIM_TIM7_INT_CLK_ENABLE();                     /* 使能TIM7时钟 */
        HAL_NVIC_SetPriority(BTIM_TIM7_INT_IRQn, 4, 0); /* 抢占4,子优先级0 */
        HAL_NVIC_EnableIRQ(BTIM_TIM7_INT_IRQn);         /* 开启ITM7中断 */
    }
}

/**
 * @brief       基本定时器TIMX中断服务函数
 * @param       无
 * @retval      无
 */
void BTIM_TIMX_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_timx_handle);  /* 定时器回调函数 */
}

/* TIM7中断服务函数 */
void BTIM_TIM7_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_tim7_handle);  /* 定时器回调函数 */
}

/**
 * @brief       回调函数,定时器中断服务函数调用
 * @param       无
 * @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");
    }
}

demo.c

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

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );

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


/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
    uint8_t task1_num = 0;
    while(1)
    {
        if(++task1_num == 5)
        {
            task1_num = 0;
            printf("关中断!!\r\n");
            portDISABLE_INTERRUPTS();
            delay_ms(5000);
            printf("开中断!!!\r\n");
            portENABLE_INTERRUPTS();
        }
        vTaskDelay(1000);
    }
}

 五、总结

七、FreeRTOS之FreeRTOS中断管理_第10张图片

 需要的小伙伴可以私信或者评论哦~

七、FreeRTOS之FreeRTOS中断管理_第11张图片

整理不易,我太喜欢点赞了!

七、FreeRTOS之FreeRTOS中断管理_第12张图片

你可能感兴趣的:(FreeRTOS实时操作系统,单片机,嵌入式硬件)