FreeRTOSFreeRTOS学习笔记— 中断+临界区的保护
Posted 果果小师弟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FreeRTOSFreeRTOS学习笔记— 中断+临界区的保护相关的知识,希望对你有一定的参考价值。
1、什么是临界段
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建TASK1任务
xTaskCreate((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
//创建TASK2任务
xTaskCreate((TaskFunction_t )task2_task,
(const char* )"task2_task",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
大家有没有注意在开始任务中创建了两个任务,在开头和结尾分别加上了进入临界区taskENTER_CRITICAL();
和退出临界区taskEXIT_CRITICAL();
的代码。
临界段用一句话概括就是一段在执行的时候不能被中断的代码段。
在FreeRTOS
里面,这个临界段最常出现的就是对全局变量的操作,全局变量就好像是一个枪把子,谁都可以对他开枪,但是我开枪的时候,你就不能开枪,否则就不知道是谁命中了靶子。可能有人会说我可以在子弹上面做个标记,我说你能不能不要瞎扯淡。
那么什么情况下临界段会被打断?一个是系统调度,还有一个就是外部中断。在FreeRTOS
,系统调度,最终也是产生Pendsv
中断,在PendSV handler
里面实现任务的切换,所以还是可以归结为中断。既然这样,FreeRTos对临界段的保护最终还是回到对中断的开和关的控制。
2、关中断
FreeRTOS关中断的函数在portmacro.h
中定义,分不带返回值和带返回值两种
不带返回值的关中断函数
1、不带返回值的关中断函数,不能嵌套,不能在中断里面使用。不带返回值的意思是:在往 BASEPRI
写入新的值的时候,不用先将BASEPRI
的值保存起来,即不用管当前的中断状态是怎么样的,既然不用管当前的中断状态,也就意味着这样的函数不能在中断里面调用
2、configMAX_SYSCALL_INTERRUPT_PRIORITY
是一个在FreeRtOSConfig.h
中定义的宏,即要写入到BASEPRI
寄存器的值。该宏默认定义为191,高四位有效,即等于0xb0
,或者是11,即优先级大于等于11的中断都会被屏蔽,11以内的中断则不受FreeRTOS管理。
3、将configMAX_SYSCALL_INTERRUPT_PRIORITY
的值写入BASEPRI
寄存器,实现关中断(准确来说是关部分中断)。
带返回值的关中断函数
1、带返回值的关中断函数,可以嵌套,可以在中断里面使用。带返回值的意思是:在往 BASEPRI
写入新的值的时候,先将BASEPRI
的值保存起来,在更新完BASEPRI
的值的时候,将之前保存好的BASEPRI
的值返回,返回的值作为形参传入开中断函数。
2、configMAX_SYSCALL_INTERRUPT_PRIORITY
是一个在FreeRTOSConfig.h
中定义的宏,即要写入到BASEPRI
寄存器的值。该宏默认定义为191,高四位有效,即等于0xb0,或者是11,即优先级大于等于11的中断都会被屏蔽,11以内的中断则不受FreeRTOS
管理。
3、保存BASEPRI
的值,记录当前哪些中断被关闭
4、返回原来BASEPRI
的值
3、开中断
FreeRTOS
开中断的函数在portmacro.h
中定义
4、进入临界段
4.1、不带版本保护,不能嵌套
/* ==========进入临界段,不带中断保护版本,不能嵌套=============== */
/* 在 task.h 中定义 */
#define taskENTER_CRITICAL() portENTER_CRITICAL()
/* 在 portmacro.h 中定义 */
#define portENTER_CRITICAL() vPortEnterCritical()
/* 在 port.c 中定义 */
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 );
}
}
/* 在 portmacro.h 中定义 */
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
/* 在 portmacro.h 中定义 */
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. */
/* *INDENT-OFF* */
msr basepri, ulNewBASEPRI
dsb
isb
/* *INDENT-ON* */
}
}
4.2、带版本保护,可以嵌套
/* ==========进入临界段,带中断保护版本,可以嵌套=============== */
/* 在 task.h 中定义 */
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
/* 在 portmacro.h 中定义 */
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
/* 在 portmacro.h 中定义 */
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;
}
5、退出临界段
5.1、不带中断保护的版本,不能嵌套
/* ==========退出临界段,不带中断保护版本,不能嵌套=============== */
/* 在 task.h 中定义 */
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
/* 在 portmacro.h 中定义 */
#define portEXIT_CRITICAL() vPortExitCritical()
/* 在 port.c 中定义 */
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if ( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}
/* 在 portmacro.h 中定义 */
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )
/* 在 portmacro.h 中定义 */
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
msr basepri, ulBASEPRI
}
}
5.2、带中断保护的版本,可以嵌套
/* ==========退出临界段,带中断保护版本,可以嵌套=============== */
/* 在 task.h 中定义 */
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
/* 在 portmacro.h 中定义 */
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)
/* 在 portmacro.h 中定义 */
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
msr basepri, ulBASEPRI
}
}
试验应用
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建TASK1任务
xTaskCreate((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
//创建TASK2任务
xTaskCreate((TaskFunction_t )task2_task,
(const char* )"task2_task",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
以上是关于FreeRTOSFreeRTOS学习笔记— 中断+临界区的保护的主要内容,如果未能解决你的问题,请参考以下文章
FreeRTOSFreeRTOS学习笔记— 手写FreeRTOS双向链表/源码分析
FreeRTOSFreeRTOS学习笔记— 开始创建任务并测试任务代码
FreeRTOSFreeRTOS学习笔记— 学习FreeRTOS的编程风格和本质
FreeRTOSFreeRTOS学习笔记— 任务创建删除挂起和恢复
FreeRTOSFreeRTOS学习笔记— FreeRTOS任务与协程
FreeRTOSFreeRTOS学习笔记(10)— FreeRTOS的osThreadDef创建任务(CMSIS_API)