【5】STM32·FreeRTOS·临界段保护与调度器挂起

news/2024/11/27 22:28:47/

目录

一、临界段代码保护简介

二、临界段代码保护函数介绍

2.1、调用示例

2.2、内部实现

三、任务调度器的挂起和恢复

3.1、调用示例

3.2、内部实现


一、临界段代码保护简介

什么是临界段:临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段

适用场合如:

1、外设需严格按照时序初始化的外设:IIC、SPI等等
2、系统系统自身需求
3、用户用户需求

中断任务调度可以打断当前程序的运行

二、临界段代码保护函数介绍

FreeRTOS 在进入临界段代码的时候需要关闭中断,当处理完临界段以后再开中断

函数描述

taskENTER_CRITICAL()

任务级进入临界段

taskEXIT_CRITICAL()

任务级退出临界段

taskENTER_CRITICAL_FROM_ISR()

中断级进入临界段

taskEXIT_CRITICAL_FROM_ISR()

中断级退出临界段

2.1、调用示例

任务级临界区调用格式示例:

taskENTER_CRITICAL();
{...    /* 临界区 */
}
taskEXIT_CRITICAL();

中断级临界区调用格式示例:

uint32_t save_status;
save_status = taskENTER_CRITICAL_FROM_ISR();
{...    /* 临界区 */
}
taskEXIT_CRITICAL_FROM_ISR(save_status);

特点

1、成对使用

2、支持嵌套

3、尽量保持临界段耗时短

2.2、内部实现

任务级进入临界段:taskENTER_CRITICAL()

#define taskENTER_CRITICAL()               portENTER_CRITICAL()
#define portENTER_CRITICAL()               vPortEnterCritical()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 );}
}#define portDISABLE_INTERRUPTS()           vPortRaiseBASEPRI()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, ulNewBASEPRIdsbisb
/* *INDENT-ON* */}}

任务级退出临界段:taskEXIT_CRITICAL()

#define taskEXIT_CRITICAL()                portEXIT_CRITICAL()
#define portEXIT_CRITICAL()                vPortExitCritical()void vPortExitCritical( void )
{configASSERT( uxCriticalNesting );uxCriticalNesting--;if( uxCriticalNesting == 0 ){portENABLE_INTERRUPTS();}
}#define portENABLE_INTERRUPTS()            vPortSetBASEPRI( 0 )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. */
/* *INDENT-OFF* */msr basepri, ulBASEPRI
/* *INDENT-ON* */}}

中断级进入临界段:taskENTER_CRITICAL_FROM_ISR()

#define taskENTER_CRITICAL_FROM_ISR()      portSET_INTERRUPT_MASK_FROM_ISR()
#define portSET_INTERRUPT_MASK_FROM_ISR()  ulPortRaiseBASEPRI()static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void ){uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;__asm{/* Set BASEPRI to the max syscall priority to effect a critical* section. */
/* *INDENT-OFF* */mrs ulReturn, baseprimsr basepri, ulNewBASEPRIdsbisb
/* *INDENT-ON* */}return ulReturn;}

中断级退出临界段:taskEXIT_CRITICAL_FROM_ISR()

#define taskEXIT_CRITICAL_FROM_ISR( x )           portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
#define portCLEAR_INTERRUPT_MASK_FROM_ISR( x )    vPortSetBASEPRI( x )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. */
/* *INDENT-OFF* */msr basepri, ulBASEPRI
/* *INDENT-ON* */}}

三、任务调度器的挂起和恢复

挂起任务调度器,调用此函数不需要关闭中断

函数描述

vTaskSuspendAll()

挂起任务调度器

xTaskResumeAll()

恢复任务调度器

3.1、调用示例

vTaskSuspendAll();
{···    /* 内容 */
}
xTaskResumeAll();

1、与临界区不一样的是,挂起任务调度器,未关闭中断

2、它仅仅是防止了任务之间的资源争夺,中断照样可以直接响应

3、挂起调度器的方式,适用于临界区位于任务与任务之间

4、既不用去延时中断,又可以做到临界区的安全

3.2、内部实现

挂起任务调度器:vTaskSuspendAll()

void vTaskSuspendAll( void )
{/* A critical section is not required as the variable is of type* BaseType_t.  Please read Richard Barry's reply in the following link to a* post in the FreeRTOS support forum before reporting this as a bug! -* https://goo.gl/wu4acr *//* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that* do not otherwise exhibit real time behaviour. */portSOFTWARE_BARRIER();/* The scheduler is suspended if uxSchedulerSuspended is non-zero.  An increment* is used to allow calls to vTaskSuspendAll() to nest. */++uxSchedulerSuspended;/* Enforces ordering for ports and optimised compilers that may otherwise place* the above increment elsewhere. */portMEMORY_BARRIER();
}

当变量 uxSchedulerSuspended 的值不为 0,将会导致 Systick 无法触发 PendSV 中断,即挂起任务调度器

恢复任务调度器:xTaskResumeAll()

BaseType_t xTaskResumeAll( void )
{TCB_t * pxTCB = NULL;BaseType_t xAlreadyYielded = pdFALSE;/* If uxSchedulerSuspended is zero then this function does not match a* previous call to vTaskSuspendAll(). */configASSERT( uxSchedulerSuspended );/* It is possible that an ISR caused a task to be removed from an event* list while the scheduler was suspended.  If this was the case then the* removed task will have been added to the xPendingReadyList.  Once the* scheduler has been resumed it is safe to move all the pending ready* tasks from this list into their appropriate ready list. */taskENTER_CRITICAL();{--uxSchedulerSuspended;if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ){if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U ){/* Move any readied tasks from the pending list into the* appropriate ready list. */while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ){pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */listREMOVE_ITEM( &( pxTCB->xEventListItem ) );portMEMORY_BARRIER();listREMOVE_ITEM( &( pxTCB->xStateListItem ) );prvAddTaskToReadyList( pxTCB );/* If the moved task has a priority higher than or equal to* the current task then a yield must be performed. */if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ){xYieldPending = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}if( pxTCB != NULL ){/* A task was unblocked while the scheduler was suspended,* which may have prevented the next unblock time from being* re-calculated, in which case re-calculate it now.  Mainly* important for low power tickless implementations, where* this can prevent an unnecessary exit from low power* state. */prvResetNextTaskUnblockTime();}/* If any ticks occurred while the scheduler was suspended then* they should be processed now.  This ensures the tick count does* not  slip, and that any delayed tasks are resumed at the correct* time. */{TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */if( xPendedCounts > ( TickType_t ) 0U ){do{if( xTaskIncrementTick() != pdFALSE ){xYieldPending = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}--xPendedCounts;} while( xPendedCounts > ( TickType_t ) 0U );xPendedTicks = 0;}else{mtCOVERAGE_TEST_MARKER();}}if( xYieldPending != pdFALSE ){#if ( configUSE_PREEMPTION != 0 ){xAlreadyYielded = pdTRUE;}#endiftaskYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();return xAlreadyYielded;
}

当变量 uxSchedulerSuspended 的值等于 0,则允许调度

1、当任务数量大于 0 时,恢复调度器才有意义,如果没有一个已创建的任务就无意义

2、移除等待就绪列表中的列表项,恢复至就绪列表,直到 xPendingReadyList 列表为空

3、如果恢复的任务优先级比当前正在执行任务优先级更高,则将 xYieldPending 赋值为 pdTRUE,表示需要进行一次任务切换

4、在调度器被挂起的期间内,是否有丢失未处理的滴答数。xPendedTicks 是丢失的滴答数,有则调用 xTaskIncrementTick() 补齐丢失的滴答数

5、判断是否允许任务切换

6、返回任务是否已经切换,已经切换返回 pdTRUE,反之返回 pdFALSE


http://www.ppmy.cn/news/1550452.html

相关文章

大连环保公益管理系统|Java|SSM|Vue| 前后端分离

【重要①】前后端源码万字文档部署文档 【重要②】正版源码有问题包售后 【包含内容】 【一】项目提供非常完整的源码注释 【二】相关技术栈文档 【三】源码讲解视频 【其它服务】 【一】可以提供远程部署安装,包扩环境 【…

哈希表理解与底层模拟实现

内容摘要 本文内容包括红黑树和哈希表的性能比较逻辑分析及实现、哈希表的概念、哈希表映射关系建立的最常用的两种方法直接地址法和除留余数法介绍、介绍了哈希冲突的原因以及解决解决哈希冲突的方法、负载因子的概念、哈希表的扩容、开散列实现哈希表的思路及代码实现、闭散列…

Linux把文件夹压缩成tar.gz

在 Linux 中,可以使用 tar 命令将文件夹压缩成 .tar.gz 文件。 基本命令格式 tar -czvf archive_name.tar.gz folder_name-c:创建一个新的归档文件。-z:通过 gzip 压缩归档文件。-v:显示处理过程(可选,便于…

mac 如何查看 export NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node 是否正确

在 macOS 上,如果你想查看环境变量 NVM_NODEJS_ORG_MIRROR 是否已正确设置为 https://npm.taobao.org/mirrors/node,你可以按照以下步骤进行检查: 1. 检查当前环境变量值 打开终端并运行以下命令来查看 NVM_NODEJS_ORG_MIRROR 环境变量的当…

浅谈Python库之lxml

一、基本介绍 lxml 是一个用 Python 编写的库,它提供了对 XML 和 HTML 文档的解析和操作功能。它使用 C 语言编写的 libxml2 和 libxslt 库作为后端,因此解析速度非常快,并且能够处理大型文档。lxml 支持 XPath 和 XSLT,这使得它在…

vue 下拉框字典

列表查询 <j-dict-select-tag type"list" v-model"queryParam.type" dictCode"xxxx_type" placeholder"请选择分类"/> 新增页面 <j-dict-select-tag type"list" v-decorator"[type, validatorRules.type]&…

替代Postman ,17.3K star!

现在&#xff0c;许多人都朝着全栈工程师的方向发展&#xff0c;API 接口的编写和调试已成为许多开发人员必备的技能之一。 工欲善其事&#xff0c;必先利其器。拥有一款优秀的 API 工具对于任何工程师来说都是极为重要的&#xff0c;它能够帮助我们高效地完成各种开发任务。 …

随手记:鼠标触顶方法

// 鼠标触顶方法 scrollMethod() { window.onscroll () > { let t document.documentElement.scrollTop || document.body.scrollTop; if(t > 10) { this.positionStyle.top 0px; }else{ this.positionStyle.top 128px; } } },