IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> FreeRTOS学习九(锁机制) -> 正文阅读

[嵌入式]FreeRTOS学习九(锁机制)

????????在执行代码时,有的代码开始执行,是不允许被打断的。这部分的代码也叫作临界段代码。为了确保这些代码不被中断而增加了临界区的概念。所谓的临界区保护重要流程在执行的时候不会被其他事情打断。等流程运行结束后,再将程序重新恢复到原来的状态。这种临界区保护的做法也叫锁机制。在裸机系统中,只能通过开关中断来实现锁机制。在RTOS中,增加了多种锁机制。有调度锁、中断锁、任务锁和互斥锁

中断锁

????????顾名思义,中断锁的意思是防止程序进行中断切换,即不允许被其他中断打断。FreeRTOS中没有专门的中断锁函数,但是有临界区保护的代码。

重要函数如下:

#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() //进临界区
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) //退出临界区

#define portENTER_CRITICAL() vPortEnterCritical()//进临界区
#define portEXIT_CRITICAL() vPortExitCritical()//退出临界区


#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()//进临界区
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)//退出临界区

以上这几个函数都是关于中断锁的控制。portENTER_CRITICAL()函数调用vPortEnterCritical()函数,进入该函数。

void vPortEnterCritical( void )
{
/* The interrupt should not be masked when this API is 1st called.
in order to check the error usage, typical case is:
hal_nvic_save_and_set_interrupt_mask()

xQueueGenericSend() // Calling an FreeRTOS API function from within a critical section
hal_nvic_restore_interrupt_mask()
*/

configASSERT(uxCriticalNesting || !__get_BASEPRI());

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 );
}
}

发现该函数还是调用了portDISABLE_INTERRUPTS();函数。但是不同的是,在该函数中有变量uxCriticalNesting,该变量的作用是什么呢?看vPortExitCritical()函数

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

函数中对uxCriticalNesting变量进行了判断,只有该变量为0时,才会执行退出临界区的操作。所以,综上所述,portENTER_CRITICAL()和portEXIT_CRITICAL()函数必须是成对调用。而portDISABLE_INTERRUPTS()和portENABLE_INTERRUPTS()是可以不成对出现的。

portSET_INTERRUPT_MASK_FROM_ISR()函数也是关闭中断

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. */
mrs ulReturn, basepri
msr basepri, ulNewBASEPRI
dsb
isb
}

return ulReturn;
}

????????从源码中可以看到该函数在关闭中断后,将原来的寄存器值返回。那么可以通过portCLEAR_INTERRUPT_MASK_FROM_ISR函数来恢复之前的状态。

????????这里介绍一下basepri寄存器。如果向basepri寄存器写0的话,就会停止中断屏蔽如果要屏蔽优先级不高于xx的中断,则可以将该优先级传给这个参数。例如上边的代码中将configMAX_SYSCALL_INTERRUPT_PRIORITY传递给了basepri寄存器,configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY值为4.则表示屏幕优先级不高于4的中断。即屏蔽PenSV和systick中断。

#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x4

调度锁

????????调度锁的意思是防止程序进行上下文切换。当有一些数据是不允许上下文切换的话,就可以通过调度锁来保证。

重要的函数如下:

vTaskSuspendAll( void ) 开启调度锁(不允许调度)

xTaskResumeAll(void) 关闭调度锁(允许调度)
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! -
http://goo.gl/wu4acr */
++uxSchedulerSuspended;
}

可以看到该函数并没有执行其他的操作,只是将uxSchedulerSuspended计数值+1。那这个变量究竟是什么呢?

在看上下文切换函数uxSchedulerSuspended

void vTaskSwitchContext( void )
{
if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
{
/* The scheduler is currently suspended - do not allow a context
switch. */
xYieldPending = pdTRUE;
}

该函数在进行上下文切换的时候会先检查一下该变量,如果该变量为非零。则表示当前不允许进行任务切换。

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;

????????在该函数中,会将该变量的计数值减一。值得注意的是,在进行变量减一的操作前调用了taskENTER_CRITICAL()函数。看定义如下:

#define taskENTER_CRITICAL() portENTER_CRITICAL()

????????所以,该函数是调用了临界区保护。就是说在操作uxSchedulerSuspended参数的时候先进行了临界区保护,等操作结束后,在退出临界区。

任务锁

????????为了防止当前任务的执行被其他高优先级的任务打断而提供的锁机制就是任务锁。FreeRTOS没有专门的任何锁函数,但是可以通过已有的功能来实现。

1.通过给调度器加锁

????????利用FreeRTOS的调度锁功能给调度器加锁的话,将关闭任务切换功能,从而高优先级任务也就无法抢占低优先级任务的执行。同时高优先级任务也是无法向低优先级任务切换的。另外,调度锁只是禁止了调度器工作,并没有关闭任何中断。

2.通过关闭任务切换中断pendSV和系统时钟节拍中断systick

????????利用FreeRTOS的任务代码临界段处理函数就可以关闭PenSV中断和systick中断。操作寄存器basepri关闭。

????????可以说,任务锁和调度锁差别不大

互斥锁

??????? 当程序在访问某些共享数据的时候,可能会出现同时改变共享资源的情况。这种情况可能会造成程序运行出现异常。为了防止这种情况出现,增加了互斥锁的概念。互斥锁是依靠互斥信号量来实现的,具体的互斥信号量的内容,可以参考之前的文章。附上链接。

FreeRTOS学习五(信号量)_qq_34981的博客-CSDN博客

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-04-07 22:53:39  更:2022-04-07 22:54:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:27:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码