1 中断
1.1中断简介
让CPU打断正常运行的程序,转而去处理紧急的事件(程序)。
中断执行机制,可简单概括为三步:
1)中断请求,外设产生中断请求(GPIO外部中断、定时器中断等)。
2)响应中断,CPU停止执行当前程序,转而去执行中断处理程序(ISR)。
3)退出中断,执行完毕,返回被打断的程序处,继续往下执行。
1.2中断优先级分组设置
ARM Cortex-M使用了8位宽的寄存器来配置中断的优先等级,这个寄存器就是中断优先级配置寄存器,但STM32只用了中断优先级配置寄存器的高4位[ 7 : 4 ],所以STM32提供了最大16级的中断优先等级。
STM32的中断优先级可以分为抢占优先级和子优先级。
抢占优先级:抢占优先级高的中断可以打断正在执行但抢占优先级低的中断。
子优先级:当同时发生具有相同抢占优先级的两个中断时,子优先级数值小的优先执行。
注意:中断优先级数值越小越优先。
中断优先级分组设置:
优先级分组 | 抢占优先级 | 子优先级 | 优先级配置寄存器高四位 |
NVIC_PriorityGroup_0 | 0 级抢占优先级 | 0-15 级子优先级 | 0bit 用于抢占优先级 |
NVIC_PriorityGroup_1 | 0-1 级抢占优先级 | 0-7 级子优先级 | 1bit 用于抢占优先级 |
NVIC_PriorityGroup_2 | 0-3 级抢占优先级 | 0-3级子优先级 | 2bit 用于抢占优先级 |
NVIC_PriorityGroup_3 | 0-7 级抢占优先级 | 0-1级子优先级 | 3bit 用于抢占优先级 |
NVIC_PriorityGroup_4 | 0-15 级抢占优先级 | 0级子优先级 | 4bit 用于抢占优先级 |
通过调用函数HAL_NVIC_SetPriorityGrouping(NVIC_PriorityGroup_4)即可完成设置(在HAL_Init中设置)
特点:
1)低于configMAX_SYSCALL_INTERRUPT_PRIORIT优先级(数值为5)的中断里才允许调用FreeRTOS的API函数,即优先级数值>5.
2)建议将所有中断优先级位指定为抢占优先级位,方便FreeRTOS管理 (调用函数HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4))
3)中断优先级数值越小越优先,任务优先级数值越大越优先
1.3 中断相关寄存器
三个系统中断优先级配置寄存器,分别为SHPR1、SHPR2、SHPR3、
SHPR1寄存器地址:0xE000ED18
SHPR2寄存器地址:0xE000ED1C
SHPR3寄存器地址:0xE000ED20
1.4 FreeRTOS配置PendSV和Systick中断优先权
指的是SHPR3 的首地址
由于上述的管理中断的寄存器中只有高4位配置优先级,因此就是将中断最低优先级配置给Systick和PendSV。
即PendSV和SysTick设置最低优先级,设置最低保证系统任务切换不会阻塞系统其他中断的响应,即中断可以打断任务,但任务不可以打断中断,PendSV的作用就是用来切换任务的。
1.5 中断屏蔽寄存器
三个中断屏蔽寄存器,分别为PRIMASK、FAULTMASK和BASEPRI
名字 | 功能描述 |
PRIMASK | 这是个只有1个位的寄存器。当他置1时,就关掉所有可屏蔽的异常,只剩下NMI和硬fault可以响应,它的缺省值是0,表示没有关中断,即开中断 |
FAULTMASK | 这是 个只有1个位的寄存器。当它置1时,只有NMI才能响应,包括中断和fault,通通关闭。它的缺省值也是0,表示没有关中断 |
BASEPRI | 这个寄存器最多有9位(由表达优先级的位数决定)。他定义了被屏蔽优先级的阈值,当它被设成某个值后,所有优先级号大于等于此值的中断都被关(优先级号越大,优先级越低)。但若被设置成0,则不关闭任何中断。 |
FreeRTOS所使用的中断管理就是利用的BASEPRI寄存器。
BASEPRI:屏蔽优先级低于某一个阈值的中断,比如BASEPRI设置为0X50,代表中断优先级在5~15内的均被屏蔽,0~4的中断优先级正常执行
在中断服务函数中调度FreeRTOS的API函数需注意:
1)中断服务函数的优先级需在FreeRTOS所管理的范围内
2)在中断服务函数里需调用FreeRTOS的API函数,必须使用带“FromISR”后缀的函数,并且优先级分组必须设置成组四。
2 FreeRTOS中断管理实验
2.1 实验内容
本实验会使用两个定时器,一个优先级为4,一个优先级为6,注意:系统管理的优先级范围:5~15。
现象:两个定时器每1s,打印一段字符串,当中断关闭时,停止打印,开中断时持续打印。
2.2 实验设计
将设计2个任务:start_task、task1
2个任务功能如下:
start_task 用来创建task1任务
task1 中断测试任务,任务中将调用关中断
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/btim.h"
#include "./SYSTEM/usart/usart.h"
TIM_HandleTypeDef g_timx_handler; /* 定时器参数句柄 */
TIM_HandleTypeDef g_tim7_handler; /* 定时器参数句柄 */
/**
* @brief 基本定时器TIMX定时中断初始化函数
* @note
* 基本定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
* 基本定时器的时钟为APB1时钟的2倍, 而APB1为42M, 所以定时器时钟 = 84Mhz
* 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
* Ft=定时器工作频率,单位:Mhz
*
* @param arr : 自动重装值。
* @param psc : 时钟预分频数
* @retval 无
*/
/*TIM6初始化*/
void btim_timx_int_init(uint16_t arr, uint16_t psc)
{
g_timx_handler.Instance = BTIM_TIMX_INT; /* 定时器x */
g_timx_handler.Init.Prescaler = psc; /* 分频 */
g_timx_handler.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_handler.Init.Period = arr; /* 自动装载值 */
HAL_TIM_Base_Init(&g_timx_handler);
HAL_TIM_Base_Start_IT(&g_timx_handler); /* 使能定时器x和定时器更新中断 */
}
/*TIM7初始化*/
void btim_tim7_int_init(uint16_t arr, uint16_t psc)
{
g_tim7_handler.Instance = BTIM_TIM7_INT; /* 定时器x */
g_tim7_handler.Init.Prescaler = psc; /* 分频 */
g_tim7_handler.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_tim7_handler.Init.Period = arr; /* 自动装载值 */
HAL_TIM_Base_Init(&g_tim7_handler);
HAL_TIM_Base_Start_IT(&g_tim7_handler); /* 使能定时器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); /* 抢占1,子优先级3 */
HAL_NVIC_EnableIRQ(BTIM_TIMX_INT_IRQn); /* 开启ITMx中断 */
}
if (htim->Instance == BTIM_TIM7_INT)
{
BTIM_TIM7_INT_CLK_ENABLE(); /* 使能TIMx时钟 */
HAL_NVIC_SetPriority(BTIM_TIM7_INT_IRQn, 4, 0); /* 抢占4,子优先级0 */
HAL_NVIC_EnableIRQ(BTIM_TIM7_INT_IRQn); /* 开启ITMx中断 */
}
}
/*TIM6中断服务函数*/
void BTIM_TIMX_INT_IRQHandler(void)
{
HAL_TIM_IRQHandler(&g_timx_handler); /* 定时器回调函数 */
}
/*TIM7中断服务函数*/
void BTIM_TIM7_INT_IRQHandler(void)
{
HAL_TIM_IRQHandler(&g_tim7_handler); /* 定时器回调函数 */
}
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");
}
}
定时器.c文件中主要用来创建定时器的任务,每1s打印一次的任务。
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/******************************************************************************************************/
void freertos_demo(void)
{
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();
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建任务1 */
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(); /* 退出临界区 */
}
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();
}
LED0_TOGGLE(); /* LED0闪烁 */
vTaskDelay(500); /* 延时500ms */
}
}
FreeRTOS的.c文件主要用来创建任务用来开中断和关中断。、
运行结果: