FreeRTOS 笔记二 (中断配置和临界段)

freeRTOS 中断配置,需要根据MCU来具体配置

Cortex-M 中断简介

  1. 简介(有时也称为异常)
    由硬件(外设、外部引脚产生),中断产生以后,CPU中断当前程序而去处理指定的操作,
  2. 所有cortex-M 内核都会有一个中断处理组件:NVIC(Nested Vectored Interrupt Controller m嵌套中断控制器)
  3. 最多支持240个IRQ(中断请求),1个不可屏蔽中断(NMI),1个定时器中断和多个系统异常
  4. 管理中断所使用的大部分寄存器都位于NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)和SCB(System Control Block,系统控制块)中

中断优先级分组

cortex

  1. 优先级是固定的,高优先级打断低优先级
  2. 3个固定优先级最多支持128个抢占优先级(优先级数量会被芯片厂家精简(裁减掉地段有效位),STM32使用前4位 只有16个)
  3. 优先级配置寄存器8位宽,分为高低两端:抢占有限级(也称分组)和亚优先级(子优先级)
    通过AIRCR[10:8](应用程序中断及复位控制寄存器,支持5个分组)
  4. 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

优先级设置

  1. 每个外部中断对应一个8位优先级寄存器,4个相邻寄存器构成32位寄存器,地址以0x E000_E400
  2. freertos 设置PendSV和Systick中断优先级直接访问0XE000_ED20
地址名称功能
0XE000_ED20PRI_12调试监视器优先级
-PRI_13
0XE000_ED22PRI_14PendSV优先级
0XE000_ED23PRI_15SysTick 优先级

中断屏蔽寄存器

  1. 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 //关闭
  1. FAULTMASK寄存器

关闭除NMI的所有中断

SPSIE	F ;//	清除FALULTMASK
SPSID F;// 开启

或者
MOVS	R0,#1 //使能FAULTMASK
MSR 	FAULTMASK,R0

MOVS R0,#0
MSR	FAULTMASK,R0
  1. 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

中断开关

  1. 宏定义(portmacr.h 中)
#define portDISABLE_INTERRUPTS()	vPortTaiseBASEPRI()
//低于ulNewBASEPRI 的中断会被屏蔽
	__asm
	{
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}
#define portENABLE_INTERRUPTS()		VPortSetBASEPRI(0)
msr basepri, ulBASEPRI//写入0,关闭BASEPRI中断

临界代码

  1. 任务区临界代码
    低于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();//开启中断
	}
}
  1. 中断级临界代码
    中断优先级一定要低于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库的

  1. 库函数版本的:
//定时器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));//中断的回调函数必须要有
    }
  }

历程主题代码

  1. 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);

}

  1. 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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值