FreeRTOS第五节——中断配置和临界段

一、中断介绍

我所使用的是的STM32F407内核是Cortex-M4内核,支持的中断有240个IRQ(中断请求)、1个是不可屏蔽的中断(NMI)、STM32F407内核是Cortex-M4处理器、1个是Systick(滴答定时器)定时器中断和多个系统异常。

1.中断优先级
中断是有优先级的,有优先级就会可能被打断,被打断就会出现中断嵌套的现象。中断优先级位数支持8位,但是芯片的生产厂家为了精简,不会全部实现,比如STM32一共4位,也就是有24=16级

2.优先级配置寄存器
优先级配置寄存器是8位宽,STM32把优先级分为高低两段:抢占优先级(分组优先级)和亚优先级(子优先级),所以怎么比较优先级呢,就是先比较分组优先级,如果分组优先级相同就比较亚优先级。
所以我们来看一下关于优先级分组,一共5组

                        ((LP) == NVIC_LP_SLEEPONEXIT))
/**
  * @}
  */

/** @defgroup MISC_Preemption_Priority_Group 
  * @{
  */

#define NVIC_PriorityGroup_0         ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
                                                            4 bits for subpriority */
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
                                                            3 bits for subpriority */
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
                                                            2 bits for subpriority */
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
                                                            1 bits for subpriority */
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
                                                            0 bits for subpriority */

在使用FreeRTOS的时候,一般配置为NVIC_PriorityGroup_4,也就是4位抢占位,无亚优先级,之所以这么做是因为RTOS没有处理亚优先级

3.中断屏蔽寄存器
为什么需要终端屏蔽寄存器,当我们要执行一些不能被打断的,时序性很强的的操作的时候,就需要进行中断屏蔽寄存器,将中断进行屏蔽。
这三个寄存器是PRIMASK、FAULTMASK、BASEPRI

4.FreeRTOS的中断配置


#define configPRIO_BITS       		4      //4位的优先级位
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			15              //配置最低的优先级        
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY	   5             //可管理的最大优先级 ,高于5的优先级不归RTOS管理
#define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )		//最低内核中断优先级
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )//最高内核中断优先级

5.FreeRTOS开关中断

#define portDISABLE_INTERRUPTS()				vPortRaiseBASEPRI()	//关闭函数
#define portENABLE_INTERRUPTS()					vPortSetBASEPRI( 0 )	//开启函数

6.FreeRTOS临界段
所谓的临界段也叫临界区就是必须要完完整整的运行,不能被打断的代码段,比如一些初始化代码,进入临界去就要关闭中断,退出的时候就要打开中断。
进入临界段和退出分为两种,一种是在非中断服务中,一种是在中断服务中,我们来看一下源码的定义

/**
 * task. h
 *
 * Macro to mark the start of a critical code region.  Preemptive context
 * switches cannot occur when in a critical region.
 *
 * NOTE: This may alter the stack (depending on the portable implementation)
 * so must be used with care!
 *
 * \defgroup taskENTER_CRITICAL taskENTER_CRITICAL
 * \ingroup SchedulerControl
 */
#define taskENTER_CRITICAL()		portENTER_CRITICAL()
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()

/**
 * task. h
 *
 * Macro to mark the end of a critical code region.  Preemptive context
 * switches cannot occur when in a critical region.
 *
 * NOTE: This may alter the stack (depending on the portable implementation)
 * so must be used with care!
 *
 * \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL
 * \ingroup SchedulerControl
 */
#define taskEXIT_CRITICAL()			portEXIT_CRITICAL()
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )

源码的实现:
进入/退出临界区的代码是:


void vPortEnterCritical( void )
{
	portDISABLE_INTERRUPTS();
	uxCriticalNesting++;

	/* This is not the interrupt safe version of the enter critical function so
	assert() if it is being called from an interrupt context.  Only API
	functions that end in "FromISR" can be used in an interrupt.  Only assert if
	the critical nesting count is 1 to protect against recursive calls if the
	assert function also uses a critical section. */
	if( uxCriticalNesting == 1 )
	{
		configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
	}
}
/*-----------------------------------------------------------*/

void vPortExitCritical( void )
{
	configASSERT( uxCriticalNesting );
	uxCriticalNesting--;
	if( uxCriticalNesting == 0 )
	{
		portENABLE_INTERRUPTS();
	}
}

我们可以看到代码的是portENABLE_INTERRUPTS()和portDISABLE_INTERRUPTS()

static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
	__asm
	{
		/* Barrier instructions are not used as this function is only used to
		lower the BASEPRI value. */
		msr basepri, ulBASEPRI
	}
}
/*-----------------------------------------------------------*/

static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}
}

二、代码测试

话不多说,直接进行代码测试:
这是COPY正点原子的代码,先进入临界去再退出,中间进行代码创建


//任务优先级
#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 		256  
//任务句柄
TaskHandle_t INTERRUPTTask_Handler;
//任务函数
void interrupt_task(void *p_arg);

int main(void)
{ 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	delay_init(168);					//初始化延时函数
	uart_init(115200);     				//初始化串口
	LED_Init();		        			//初始化LED端口
	TIM3_Int_Init(10000-1,8400-1);		//初始化定时器3,定时器周期1S
	TIM5_Int_Init(10000-1,8400-1);		//初始化定时器5,定时器周期1S
	
	//创建开始任务
    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 *pvParameters)
{
	static u32 total_num=0;
    while(1)
    {
		total_num+=1;
		if(total_num==5) 
		{
			printf("关闭中断.............\r\n");
			portDISABLE_INTERRUPTS();				//关闭中断
			delay_xms(5000);						//延时5s
			printf("打开中断.............\r\n");	//打开中断
			portENABLE_INTERRUPTS();
		}
        LED0=~LED0;
        vTaskDelay(1000);
    }
}  


你可能感兴趣的:(单片机)