freeRTOS 中断配置,需要根据MCU来具体配置
Cortex-M 中断简介
- 简介(有时也称为异常)
由硬件(外设、外部引脚产生),中断产生以后,CPU中断当前程序而去处理指定的操作, - 所有cortex-M 内核都会有一个中断处理组件:NVIC(Nested Vectored Interrupt Controller m嵌套中断控制器)
- 最多支持240个IRQ(中断请求),1个不可屏蔽中断(NMI),1个定时器中断和多个系统异常
- 管理中断所使用的大部分寄存器都位于NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)和SCB(System Control Block,系统控制块)中
中断优先级分组
cortex
- 优先级是固定的,高优先级打断低优先级
- 3个固定优先级最多支持128个抢占优先级(优先级数量会被芯片厂家精简(裁减掉地段有效位),STM32使用前4位 只有16个)
- 优先级配置寄存器8位宽,分为高低两端:抢占有限级(也称分组)和亚优先级(子优先级)
通过AIRCR[10:8](应用程序中断及复位控制寄存器,支持5个分组) - FreeRTOS 不处理亚优先级
分组位置 | 抢占优先级 | 亚优先级的位段 | stm32对应分组 |
---|---|---|---|
0(默认) | [7:1] | [0:0] | 低3位无效 |
1 | [7:2] | [1:0] | 低3位无效 |
2 | [7:3] | [2:0] | 低3位无效 |
3 | [7:4] | [3:0] | NVIC_PriorityGroup_4 |
4 | [7:5] | [4:0] | NVIC_PriorityGroup_3 |
5 | [7:6] | [5:0] | NVIC_PriorityGroup_2 |
6 | [7:7] | [6:0] | NVIC_PriorityGroup_1 |
7 | 无 | [7:0] | NVIC_PriorityGroup_0 |
优先级设置
- 每个外部中断对应一个8位优先级寄存器,4个相邻寄存器构成32位寄存器,地址以0x E000_E400
- freertos 设置PendSV和Systick中断优先级直接访问0XE000_ED20
地址 | 名称 | 功能 |
---|---|---|
0XE000_ED20 | PRI_12 | 调试监视器优先级 |
- | PRI_13 | |
0XE000_ED22 | PRI_14 | PendSV优先级 |
0XE000_ED23 | PRI_15 | SysTick 优先级 |
中断屏蔽寄存器
- PRIMASK(实时系统用来保护临界区)
禁止除NMI和HardFalut、复位外的所有异常和中断
使用汇编指令修改:
SPISE I //清楚PRIMASK 中断 INTX_DISABLE()函数
SPISD I // 使能PEIMASK 中断 INTX_ENABLE()中断
或者
MOVS R0,#0
MSR PRIMASK, R0 //使能
MOVS R0,#0
MSR PRIMASK,R0 //关闭
- FAULTMASK寄存器
关闭除NMI的所有中断
SPSIE F ;// 清除FALULTMASK
SPSID F;// 开启
或者
MOVS R0,#1 //使能FAULTMASK
MSR FAULTMASK,R0
MOVS R0,#0
MSR FAULTMASK,R0
- BASEPRI 寄存器
屏蔽优先级低于设定值的中断
MOV R0, #0X60 //优先级低于0x60 当为0时停止中断屏蔽
MSR BASEPRI,R0
MOV R0,#0
MSR BASEPRI,R0 //当为0时停止中断屏蔽
FreeRtos 中断配置:
1. configPRIO_BITS 4//MCU优先级bit位 STM32 4位
2. configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15//最低优先级 STM32设置位中断分组4,最低值为15
3. configKERNEL_INTERRUPT_PRIORITY //内核中断优先级 该值为0XF0
//cortex 内核中断配置寄存器8bit stm32用了高configPRIO_BITS位,所以真正的优先级为该值
(configLIBRARY_LOWEST_INTERRUPT_PRIORITY<<(8-configPRIO_BITS))
相关
//挂起中断优先级 0XE000_ED20的低8位
#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
//系统时钟中断优先级 0XE000_ED20的高8位
#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
在xPortStartScheduler()中定义(portNVIC_SYSPRI2_REG = 0XE000ED20)
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
4.configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5//系统可管理的最大优先级 屏蔽优先级低于设定值的中断 优先级大于该值的中断FreeRtos不管
5. configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )//高于此中断的FreeRTOs 不能禁止 ,低于此中断的可以调用API
中断开关
- 宏定义(portmacr.h 中)
#define portDISABLE_INTERRUPTS() vPortTaiseBASEPRI()
//低于ulNewBASEPRI 的中断会被屏蔽
__asm
{
msr basepri, ulNewBASEPRI
dsb
isb
}
#define portENABLE_INTERRUPTS() VPortSetBASEPRI(0)
msr basepri, ulBASEPRI//写入0,关闭BASEPRI中断
临界代码
- 任务区临界代码
低于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断不会调用
#define taskENTER_CRITICAL() portENTER_CRITICAL()
portDISABLE_INTERRUPTS();//关闭中断
uxCriticalNesting++; //记录嵌套次数
if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
}
#define taskEXIT_CRITIACAL() vPortExitCritical()
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;//嵌套次数减一
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();//开启中断
}
}
- 中断级临界代码
中断优先级一定要低于configMAX_SYSCALL_INTERRUPT_PRIORITY
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR(x) portCLEAR_INTERRUPT_MASK_FROM_ISR(x);
//portSET_INTERRUPT_MASK_FROM_ISR= ulPortRaiseBASEPRI 返回当前的优先级
//portCLEAR_INTERRUPT_MASK_FROM_ISR ulBASEPRI
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;//将默认优先写入基本优先级
__asm
{
mrs ulReturn, basepri
msr basepri, ulNewBASEPRI
dsb
isb
}
return ulReturn;
}
HAL库使用中断管理问题
正点原子的例程,修改为HAL库的
- 库函数版本的:
//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
{
printf("TIM3输出.......\r\n");
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
}
如果对应的HAL如下,发现FREEROTS无法关闭定时中断
void TIM3_IRQHandler(void)
{
if(__HAL_TIM_GET_FLAG(&TIM3_Handler, TIM_FLAG_UPDATE) != RESET)
{
LED0=!LED0;
__HAL_TIM_CLEAR_IT(&TIM3_Handler, TIM_IT_UPDATE);
}
}
如果改为如下方式,会发现FREErtos可以接管定时中断
void TIM3_IRQHandler(void)
{
LED0=!LED0;
HAL_TIM_IRQHandler(&TIM3_Handler);
}
HAL_TIM_IRQHandler中的重点如下
if(__HAL_TIM_GET_FLAG(&TIM3_Handler, TIM_FLAG_UPDATE) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(&TIM3_Handler, TIM_IT_UPDATE) !=RESET)
{
__HAL_TIM_CLEAR_IT(&TIM3_Handler, TIM_IT_UPDATE);
HAL_TIM_PeriodElapsedCallback((&TIM3_Handler));//中断的回调函数必须要有
}
}
历程主题代码
- tim3.c
#include "tim3.h"
#include "usart.h"
#include "led.h"
TIM_HandleTypeDef TIM3_Handler;
TIM_HandleTypeDef TIM5_Handler;
void TIM3_Init(u16 arr,u16 psc)
{
__HAL_RCC_TIM3_CLK_ENABLE(); //使能TIM3时钟
TIM3_Handler.Instance=TIM3; //通用定时器3
TIM3_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_NVIC_SetPriority(TIM3_IRQn,4,0); //设置中断优先级,抢占优先级4,子优先级0
HAL_NVIC_EnableIRQ(TIM3_IRQn); //开启ITM3中断
}
void TIM5_Init(u16 arr,u16 psc)
{
__HAL_RCC_TIM5_CLK_ENABLE(); //使能TIM5时钟
TIM5_Handler.Instance=TIM5; //通用定时器5
TIM5_Handler.Init.Prescaler=psc; //分频系数
TIM5_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器
TIM5_Handler.Init.Period=arr; //自动装载值
TIM5_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子
TIM5_Handler.Init.RepetitionCounter=1;
HAL_TIM_Base_Init(&TIM5_Handler);
HAL_TIM_Base_Start_IT(&TIM5_Handler); //使能定时器3和定时器3更新中断:TIM_IT_UPDATE
HAL_NVIC_SetPriority(TIM5_IRQn,5,0); //设置中断优先级,抢占优先级5,子优先级0
HAL_NVIC_EnableIRQ(TIM5_IRQn); //开启ITM3中断
}
//定时器底册驱动,开启时钟,设置中断优先级
//此函数会被HAL_TIM_Base_Init()函数调用
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
}
if(htim->Instance==TIM5)
{
}
}
//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
LED0=!LED0;
if(__HAL_TIM_GET_FLAG(&TIM3_Handler, TIM_FLAG_UPDATE) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(&TIM3_Handler, TIM_IT_UPDATE) !=RESET)
{
__HAL_TIM_CLEAR_IT(&TIM3_Handler, TIM_IT_UPDATE);
HAL_TIM_PeriodElapsedCallback((&TIM3_Handler));
}
}
}
void TIM5_IRQHandler(void)
{
LED1=!LED1;
HAL_TIM_IRQHandler(&TIM5_Handler);
}
- main.c
#include "led.h"
#include "key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "tim3.h"
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 256
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define INTERRUPT_TASK_PRIO 2
//任务堆栈大小
#define INTERRUPT_STK_SIZE 512
//任务句柄
TaskHandle_t INTERRUPTTask_Handler;
//任务函数
void interrupt_task(void *p_arg);
//#define TIM5_STACK_PRIO 3
//#define TIM5_STK_SIZE 256
//TaskHandle_t Tim5Task_Handler;
//void tim5_task(void *p_arg);
int main()
{
HAL_Init();
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
//Stm32_Clock_Init(336,8,2,7);
delay_init(168);uart_init(115200);
LED_Init();
TIM3_Init(1000-1,8400-1);
TIM5_Init(1000-1,8400-1);
// TIM5_Init(10000-1,840-1);
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(); //进入临界区
//创建中断测试任务
xTaskCreate((TaskFunction_t )interrupt_task, //任务函数
(const char* )"interrupt_task", //任务名称
(uint16_t )INTERRUPT_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )INTERRUPT_TASK_PRIO, //任务优先级
(TaskHandle_t* )&INTERRUPTTask_Handler); //任务句柄
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL();
}
void interrupt_task(void *p_arg)
{
static u32 total_num=0;
while(1)
{
total_num+=1;
if(total_num==5)
{
taskENTER_CRITICAL();
printf("关闭中断.............\r\n");
portDISABLE_INTERRUPTS(); //关闭中断
delay_xms(5000); //延时5s
printf("打开中断.............\r\n"); //打开中断
portENABLE_INTERRUPTS();
taskEXIT_CRITICAL();
}
// LED0=~LED0;
vTaskDelay(1000);
}
}