一、中断介绍(了解)
什么是中断?
让CPU打断正常运行的程序,转而去处理紧急的事件(程序),就叫中断

中断执行机制
答:可简单概括为三步:
-
中断请求 :外设产生中断请求(如:GPIO外部中断、定时器中断等)。
-
响应中断 :CPU停止执行当前程序,转而执行中断处理程序(ISR)。
-
退出中断 :执行完毕,返回被打断的程序处,继续往下执行。

二、中断优先级分组设置(熟悉)
中断优先级分组介绍
ARM Cortex-M 使用了 8 位宽的寄存器来配置中断的优先等级,这个寄存器就是中断优先级配置寄存器
但STM32,只用了中断优先级配置寄存器的高4位 [7 : 4],所以STM32提供了最大16级的中断优先等级

什么是抢占优先级,什么是子优先级
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中即可完成设置
特点:
-
低于configMAX_SYSCALL_INTERRUPT_PRIORITY优先级的中断才允许调用FreeRTOS 的API函数
-
建议将所有优先级位指定为抢占优先级位,方便FreeRTOS管理
(调用函数HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4) -
中断优先级数值越小越优先,任务优先级数值越大越优先

三、中断相关寄存器(熟悉)
关中断:portDISABLE_INTERRUPTS()
开中断: portENABLE_INTERRUPTS()
三个系统中断优先级配置寄存器
SHPR1寄存器地址:0xE000ED18
SHPR2寄存器地址:0xE000ED1C
SHPR3寄存器地址:0xE000ED20

表出自:《Cortex M3权威指南(中文)》第286页
注意:FreeRTOS主要是使用SHPR3寄存器对PendSV和Systick中断优先级进行设置(设置为最低优先级)。
为什么将PendSV和SysTick设置最低优先级
答:保证系统任务切换不会阻塞系统其他中断的响应。
三个中断屏蔽寄存器
PRIMASK、 FAULTMASK 和BASEPRI

FreeRTOS所使用的中断管理就是利用的BASEPRI这个寄存器
BASEPRI:屏蔽优先级低于某一个阈值的中断,当设置为0时,则不关闭任何中断
比如: BASEPRI设置为0x50,代表中断优先级在515内的均被屏蔽,04的中断优先级正常执行
当BASEPRI设置为0x50时:

在中断服务函数中调度FreeRTOS的API函数需注意:
1、中断服务函数的优先级需在FreeRTOS所管理的范围内
2、在中断服务函数里边需调用FreeRTOS的API函数,必须使用带“FromISR”后缀的函数
实践
FreeRTOS中断管理实验

初始化定时器
-
初始化定时器句柄
TIM_HandleTypeDef g_timx_handle; /* 定时器句柄 */ -
基本定时器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及其更新中断 */ } -
定时器底层驱动,开启时钟,设置中断优先级
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中断 */ } } -
定时器TIMX中断服务函数
void BTIM_TIMX_INT_IRQHandler(void) { HAL_TIM_IRQHandler(&g_timx_handle); /* 定时器中断公共处理函数 */ } -
定时器更新中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == BTIM_TIMX_INT) { printf("TIM6优先级为6的正在运行!!!\r\n"); } } -
启动定时器
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);
}
}