【05】FreeRTOS的中断管理
创始人
2024-05-21 15:09:50
0

目录

1.什么是中断

2.中断优先级分组

2.1中断优先级分组-介绍

2.2中断优先级分组-配置

2.3中断优先级分组-特点

3.中断相关寄存器

3.1寄存器地址

3.2在FreeRTOS中配置PendSV和Systick中断优先级

3.3中断相关寄存器

4.FreeRTOS中断管理实验

4.1修改freertos_demo.c

4.2移除工程中exti.c文件

4.3实现定时器功能

4.4实现关闭和开启中断功能

5.总结


1.什么是中断

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

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

1,中断请求:外设产生中断请求(GPIO外部中断、定时器中断等);

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

3,退出中断:执行完毕,返回被打断的程序处,继续往下执行。(打断哪个点,就返回哪个点继续执行)

举例说明:小明在看视频的时候突然肚子疼,必须要上厕所,打断看视频的任务,去上厕所, 暂停视频播放,上完厕所后,继续从暂停处继续观看视频。

2.中断优先级分组

2.1中断优先级分组-介绍

        ARM Cortex-M 使用了 8 位宽的寄存器来配置中断的优先等级(最大{\color{Red} 2^{8}},256个优先级,0~255),这个寄存器就是中断优先级配置寄存器。但STM32,只用了中断优先级配置寄存器的高4位 [7 : 4],所以STM32提供了最大16级的中断优先等级。

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

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

子优先级:当同时发生具有相同抢占优先级的两个中断时,子优先级数值小的优先执行(例如有两个中断int1、int2,抢占优先级都为2,而子优先级int1为1,int2为0。假设int1正在执行,触发了int2,int2并不会抢占int1,因为抢占优先级相同,int2会在int1执行完后被执行。)

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

2.2中断优先级分组-配置

        一共有 5 种分配方式,对应着中断优先级分组的 5 个组,FreeRTOS为了方便管理使用的是优先级分组4,全部用于抢占优先级。

优先级分组抢占优先级子优先级优先级配置寄存器高 4 位
NVIC_PriorityGroup_00 级抢占优先级0-15 级子优先级

0bit 用于抢占优先级

4bit 用于子优先级

NVIC_PriorityGroup_1 0-1 级抢占优先级0-7 级子优先级 

1bit 用于抢占优先级

3bit 用于子优先级

NVIC_PriorityGroup_20-3 级抢占优先级0-3 级子优先级

2bit 用于抢占优先级

2bit 用于子优先级

NVIC_PriorityGroup_3 0-7 级抢占优先级0-1 级子优先级

3bit 用于抢占优先级

1bit 用于子优先级

NVIC_PriorityGroup_40-15 级抢占优先级 0 级子优先级

4bit 用于抢占优先级

0bit 用于子优先级

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

FreeRTOS官网关于中断说明:FreeRTOS官网中断说明地址

2.3中断优先级分组-特点

1、低于configMAX_SYSCALL_INTERRUPT_PRIORITY优先级的中断里才允许调用FreeRTOS 的API函数(在代码中configMAX_SYSCALL_INTERRUPT_PRIORITY设置的是5,所以能被FreeRTOS操作的中断优先级是5~15)

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

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

3.中断相关寄存器

3.1寄存器地址

三个系统中断优先级配置寄存器,分别为 SHPR1、 SHPR2、 SHPR3。

SHPR1寄存器首地址:0xE000ED18

SHPR2寄存器首地址:0xE000ED1C

SHPR3寄存器首地址:0xE000ED20

        表中一个地址是8个位,一个寄存器是32位。如果想设置PendSV的优先级,则需要从SHPR3首地址偏移16位;如果想设置SysTick的优先级,则SHPR3首地址偏移24位。

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

         注意:SysTick和PendSV的优先级设置,嘀嗒定时器Systick给系统提供心跳节拍,任务切换、调度都是在PendingSV中实现。

3.2在FreeRTOS中配置PendSV和Systick中断优先级

以下程序中SHPR3寄存器设置PendSV和SysTick的优先级。

 其中oxe00ed20是寄存器SHPR3的首地址。

PendSV的首地址等于变量configKERNEL_INTERRUPT_PRIORITY左移16位,同理,Systick的首地址等于configKERNEL_INTERRUPT_PRIORITY左移24位。

 configKERNEL_INTERRUPT_PRIORITY为变量configLIBRARY_LOWEST_INTERRUPT_PRIORITY左移8-configPRIO_BITS位(其中configPRIO_BITS宏定义为4,configLIBRARY_LOWEST_INTERRUPT_PRIORITY宏定义为15),即将15左移4位,STM32优先级配置低4位并没有用到,左移4位到高四位。

         经过以上配置将PendSV和Systick设置优先级位15。

PendSV和SysTick设置最低优先级

设置最低:保证系统任务切换不会阻塞系统其他中断的响应(中断可以打断任务,任务不能打断中断,因为中断是较为紧急的事情)

3.3中断相关寄存器

 三个中断屏蔽寄存器,分别为 PRIMASK、 FAULTMASK 和BASEPRI 

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

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

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

关中断程序示例(中断优先级在5 ~ 15的全部被关闭):

#define portDISABLE_INTERRUPTS() 		vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void ) 
{ uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; __asm {msr basepri, ulNewBASEPRI dsb isb} 
}
#define configMAX_SYSCALL_INTERRUPT_PRIORITY            ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY        5      /* FreeRTOS可管理的最高中断优先级 */ 

        以上程序中变量 configMAX_SYSCALL_INTERRUPT_PRIORITY为0x50,赋值给变量ulNewBASEPRI ,使用汇编语言将ulNewBASEPRI 赋值给basepri寄存器。

 当BASEPRI设置为0x50时:

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

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

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

3、优先级分组必须设置为组4,全部设置成抢占优先级。

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

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

         以上程序将寄存器basepri设置成0。

4.FreeRTOS中断管理实验

1、实验目的:学会使用FreeRTOS的中断管理! 本实验会使用两个定时器,一个优先级为4,一个优先级为6,注意:系统所管理的优先级范围:5~15, 现象:两个定时器每1s,打印一段字符串,当关中断时,停止打印,开中断时持续打印。(优先级为6的定时器,在关闭中断时会停止打印,而优先级为4的定时器,不在5~15内,不受影响)

2、实验设计:将设计2个任务:start_task、task1

2个任务的功能如下:

start_task:用来创建task1任务;

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

本次实验基于本系列文章中【04】FreeRTOS的任务挂起与恢复   工程文件实现。

4.1修改freertos_demo.c

        删除上一节中用到的任务2、任务3相关代码,并删除任务1中的数字打印和LED翻转,删除完如下所示

#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.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();                              /*退出临界区*/
}/* 任务一,实现每5000ms关闭和开启中断 */
void task1( void * pvParameters )
{uint32_t task1_num=0;while(1){vTaskDelay(500);}
}

4.2移除工程中exti.c文件

        移除以下文件,并且在main.c文件中删除相关调用程序。

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "lcd.h"
#include "sdram.h"
#include "malloc.h"
#include "freertos_demo.h"int main(void)
{HAL_Init();                     //初始化HAL库   Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhzdelay_init(180);                //初始化延时函数uart_init(115200);              //初始化USARTLED_Init();KEY_Init();freertos_demo();
}

4.3实现定时器功能

timer.c原始程序如下所示:

#include "timer.h"
#include "led.h"TIM_HandleTypeDef TIM3_Handler;      //定时器句柄 //通用定时器3中断初始化
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//这里使用的是定时器3!(定时器3挂在APB1上,时钟为HCLK/2)
void TIM3_Init(u16 arr,u16 psc)
{  TIM3_Handler.Instance=TIM3;                          //通用定时器3TIM3_Handler.Init.Prescaler=psc;                     //分频系数TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;    //向上计数器TIM3_Handler.Init.Period=arr;                        //自动装载值TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子HAL_TIM_Base_Init(&TIM3_Handler);HAL_TIM_Base_Start_IT(&TIM3_Handler); //使能定时器3和定时器3更新中断:TIM_IT_UPDATE   
}//定时器底册驱动,开启时钟,设置中断优先级
//此函数会被HAL_TIM_Base_Init()函数调用
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance==TIM3){__HAL_RCC_TIM3_CLK_ENABLE();            //使能TIM3时钟HAL_NVIC_SetPriority(TIM3_IRQn,1,3);    //设置中断优先级,抢占优先级1,子优先级3HAL_NVIC_EnableIRQ(TIM3_IRQn);          //开启ITM3中断   }
}//定时器3中断服务函数
void TIM3_IRQHandler(void)
{HAL_TIM_IRQHandler(&TIM3_Handler);
}//回调函数,定时器中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim==(&TIM3_Handler)){LED1=!LED1;        //LED1反转}
}

由于timer.c中已经实现了定时器3的程序,定时器3为通用定时器,此处选用TIM3和TIM4都为通用定时器(2022正点原子FreeRTOS教程中选用的是TIM6和TIM7基本定时器,此处有所不同)。timer.c修改完如下所示( 在中断回调函数中不建议使用printf()函数进行打印,耗时较长,此处为了方便演示):

#include "timer.h"
#include "led.h"TIM_HandleTypeDef TIM3_Handler;      //定时器3句柄 
TIM_HandleTypeDef TIM4_Handler;      //定时器4句柄 
//通用定时器3,4中断初始化
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//这里使用的是定时器3!(定时器3挂在APB1上,时钟为HCLK/2)
void TIM3_Init(u16 arr,u16 psc)
{  TIM3_Handler.Instance=TIM3;                          //通用定时器3TIM3_Handler.Init.Prescaler=psc;                     //分频系数TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;    //向上计数器TIM3_Handler.Init.Period=arr;                        //自动装载值TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子HAL_TIM_Base_Init(&TIM3_Handler);HAL_TIM_Base_Start_IT(&TIM3_Handler); //使能定时器3和定时器3更新中断:TIM_IT_UPDATE   
}
void TIM4_Init(u16 arr,u16 psc)
{  TIM4_Handler.Instance=TIM4;                          //通用定时器4TIM4_Handler.Init.Prescaler=psc;                     //分频系数TIM4_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;    //向上计数器TIM4_Handler.Init.Period=arr;                        //自动装载值TIM4_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子HAL_TIM_Base_Init(&TIM4_Handler);HAL_TIM_Base_Start_IT(&TIM4_Handler); //使能定时器4和定时器4更新中断:TIM_IT_UPDATE   
}//定时器底册驱动,开启时钟,设置中断优先级
//此函数会被HAL_TIM_Base_Init()函数调用
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance==TIM3){__HAL_RCC_TIM3_CLK_ENABLE();            //使能TIM3时钟HAL_NVIC_SetPriority(TIM3_IRQn,6,0);    //设置中断优先级,抢占优先级6,子优先级0HAL_NVIC_EnableIRQ(TIM3_IRQn);          //开启ITM3中断   }if (htim->Instance==TIM4){__HAL_RCC_TIM4_CLK_ENABLE();            //使能TIM4时钟HAL_NVIC_SetPriority(TIM4_IRQn,4,0);    //设置中断优先级,抢占优先级4,子优先级0HAL_NVIC_EnableIRQ(TIM4_IRQn);          //开启ITM4中断   }
}//定时器3中断服务函数
void TIM3_IRQHandler(void)
{HAL_TIM_IRQHandler(&TIM3_Handler);
}
//定时器4中断服务函数
void TIM4_IRQHandler(void)
{HAL_TIM_IRQHandler(&TIM4_Handler);
}//回调函数,定时器中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim==(&TIM3_Handler)){printf("TIM3优先级为6正在运行!\r\n");}else if (htim==(&TIM4_Handler)){printf("TIM4优先级为4正在运行!!!\r\n");}
}

在timer.h中声明timer4的初始化函数,修改完如下所示:

#ifndef _TIMER_H
#define _TIMER_H
#include "sys.h"extern TIM_HandleTypeDef TIM3_Handler;      //定时器句柄 void TIM3_Init(u16 arr,u16 psc);
void TIM4_Init(u16 arr,u16 psc);
#endif

在main.c中调用TIM3和TIM4的初始化函数,并添加头文件,修改完如下所示:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "lcd.h"
#include "sdram.h"
#include "malloc.h"
#include "freertos_demo.h"#include "timer.h"int main(void)
{HAL_Init();                     //初始化HAL库   Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhzdelay_init(180);                //初始化延时函数uart_init(115200);              //初始化USARTLED_Init();KEY_Init();TIM3_Init(10000-1,9000-1);      //定时器1s中断一次TIM4_Init(10000-1,9000-1);freertos_demo();
}

现象:

以上程序可以实现每隔1s,进入TIM3和TIM4,并打印。 

4.4实现关闭和开启中断功能

在task1中关闭中断后延时5s使用的是死延时delay_ms(),并不使用vTaskDelay()函数,因为vTaskDelay()函数退出临界区时调用了开启中断函数portENABLE_INTERRUPTS(),因为刚刚关闭了中断,并不能调用开启中断函数。

freertos_demo.c部分程序修改如下(需要在一开始引用delay.h头文件):

#include "delay.h"
/* 任务一,实现每5000ms关闭和开启中断 */
void task1( void * pvParameters )
{uint32_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);}
}

现象:

在串口中每隔一秒进入TIM3和TIM4中断,进入5次,关闭中断;在串口中每隔1s进入TIM4中断, 开启中断;重复以上两步骤。

5.总结

相关内容

热门资讯

最绚丽的安卓系统,最绚丽版本全... 哇,你知道吗?在安卓的世界里,有一款系统,它就像是一颗璀璨的明珠,闪耀着最绚丽的色彩。它就是——最绚...
小米系统安卓通知权限,深度解析... 亲爱的手机控们,你是否曾为手机通知栏里乱糟糟的信息而烦恼?又或者,你是否好奇过,为什么有些应用总是能...
安卓7.0系统能玩吗,体验全新... 你有没有想过,你的安卓手机升级到7.0系统后,那些曾经陪伴你度过无数时光的游戏,还能不能继续畅玩呢?...
平板安卓系统哪家好,安卓平板系... 你有没有想过,在这个科技飞速发展的时代,拥有一台性能出色的平板电脑是多么重要的事情呢?想象无论是追剧...
安卓好的点歌系统,打造个性化音... 你有没有想过,在安卓手机上,点歌系统竟然也能如此精彩?没错,就是那个我们每天都会用到,却又常常忽略的...
熊猫安卓系统直播软件,解锁互动... 你知道吗?最近有个超级酷炫的直播软件在熊猫迷们中间火得一塌糊涂!它就是熊猫安卓系统直播软件。别看它名...
安卓点播系统开发,Androi... 你有没有想过,手机里那些让你爱不释手的视频,其实背后有着一套复杂的安卓点播系统在默默支撑呢?今天,就...
安卓6.0系统加权限,深度解析... 你有没有发现,自从手机升级到安卓6.0系统后,权限管理变得超级严格呢?这可真是让人又爱又恨啊!今天,...
哪些电视带安卓系统,多款热门智... 你有没有想过,家里的电视竟然也能装上安卓系统?听起来是不是有点不可思议?没错,现在市面上就有不少电视...
苹果怎么运用安卓系统,揭秘如何... 你知道吗?最近有个大新闻在科技圈里炸开了锅,那就是苹果竟然开始运用安卓系统了!是不是觉得有点不可思议...
安卓系统能转什么系统好,探索最... 你有没有想过,你的安卓手机是不是也能换换口味,体验一下其他系统的魅力呢?没错,今天就来聊聊这个话题:...
龙之狂热安卓系统,释放龙族狂热 亲爱的手机控们,你是否曾为拥有一款独特的安卓系统而疯狂?今天,就让我带你走进一个充满奇幻色彩的龙之狂...
vivo手机安卓系统怎么升级系... 亲爱的手机控们,你是不是也和我一样,对手机的新功能充满期待呢?尤其是vivo手机的用户,是不是也在想...
鸿蒙2.0退回安卓系统,一场系... 你知道吗?最近科技圈里可是炸开了锅,因为华为的鸿蒙2.0操作系统竟然要退回安卓系统了!这可不是一个简...
安卓系统怎么复制卡,安卓系统卡... 你有没有遇到过这种情况:手机里的照片、视频或者重要文件,突然想备份到电脑上,却发现安卓系统的卡复制功...
app兼容低安卓系统,打造全民... 你有没有发现,现在手机APP更新换代的速度简直就像坐上了火箭!不过,你知道吗?有些APP可是特别贴心...
中间安卓系统叫什么,中间安卓系... 你有没有想过,安卓系统里竟然还有一个中间的版本?没错,就是那个让很多手机用户既熟悉又陌生的版本。今天...
安卓怎么用os系统,利用And... 你有没有想过,你的安卓手机其实可以变身成一个功能强大的操作系统呢?没错,就是那个我们平时在电脑上使用...
pe系统安卓能做么,探索安卓平... 亲爱的读者,你是否曾好奇过,那款在安卓设备上大受欢迎的PE系统,它究竟能做什么呢?今天,就让我带你一...
安卓 打印机系统,安卓打印机系... 你有没有想过,家里的安卓手机和打印机之间竟然能建立起如此紧密的联系?没错,就是那个安卓打印机系统!今...