目录
CUBEMX上的配置以及使用
信号量
互斥量
信号量与互斥量都是从队列中衍生出来的,他们是一种特殊的队列。不同的地方在于:他们不含有队列的数据部分,只有队列结构体。
定义属性(这里只有一个名字)和创建
信号量又分二值信号量和计数信号量,本质上都是是资源可用的个数。
二值信号量只有0和1两个值。
二值信号量的创建 ( 参数部分为 队列长度为1(也就是计数值),数据成员类型大小为 0,队列类型为信号量类型)
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif
pxQueue->uxMessagesWaiting //在队列里现有数据个数,在信号量里则是计数值。
//二值信号量则这个值只有 0 或 1
数据部分为0然后调用创建队列的哪一套库函数。(初始化了等待发送信号量链表,等待接受信号链接,这和队列里的等待读与等待写是一样的),以及主要的超时阻塞与任务的唤醒也一样。
经过源码发现,FREE RT OS重命名了获取信号量的函数
BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait )
然而 释放信号量还是通过宏调用通用的队列写数据的方法
超时阻塞与任务唤醒参考
基于STM32结合CubeMX学习Free-RT-OS的源码之消息队列_昊月光华的博客-CSDN博客
本文主要还是学习互斥量。
为什么?因为它有优先级继承这一特殊的机制防止优先级反转。
在操作系统的日常任务中,有些操作看起来只有一步,但在汇编代码中却有好几步!在抢占式得实时操作系统中,比如有多个任务对同一个值进行写回操作,则必须要求对该值操作时互斥。
为什么要互斥?
任务1: a++;
任务2 :a++
试想进行这两个任务,尽管我的STM32F1是一个只能单核运行的CPU,但是任务是会抢占的,抢占的同时,保留现场,当前任务的变量值入栈。
任务1运行,a++ 。(从汇编上看分三步,第一步将内存里a的值取出装载到寄存器,第二步,该寄存器的值自增1,第三步写回)
若在a++执行的第三步写回之前被任务2抢占则会发生:
1:任务1的a的值(未写回)入栈。
2: sp(此时指向psp)指针切换任务2函数入口开始执行a++,由于上一个任务a还没有写回,a++执行并写回a为1。任务2执行完毕再恢复任务1的现场,a的值从栈中取出为0,这时候执行a++则a为1再写回。这样就导致了丢失修改。
这种情况下,如何解决,把a定义成__IO 也就是volatile 能解决这个丢失修改的问题。
volatile是一个特征修饰符(type specifier)。volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
简单地说:编译器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份
我们知道,从内存里读取与从寄存器里读取的速度效率是不同的,寄存机效率大于内存。
使用互斥量则可以很好地解决在使用临界资源互斥的问题。
创建互斥量
递归互斥量与普通互斥量的区别:
释放和获取调用的是信号量的API,在函数内部对互斥量的情形进行判断。
获取互斥锁
xSemaphoreTake(USEESPMUTEXHandle,portMAX_DELAY);
xTaskPriorityInherit //优先级继承
当高优先级的任务获取互斥锁时会干以下事情:
若是能获取锁(队列里的计数值为1)则减1为0。互斥量的队列头部记录当前持有互斥量的任务。
若无法获取锁,且超时阻塞等待获取锁时(把自己放入互斥量的等待读链表中),则进行判断当前获取互斥量的任务的优先级是否大于持有互斥量的任务的优先级,若是,则继承当前任务(高优先级)的优先级,同时再次判断持有互斥量的任务是否就绪,若是则加入被继承的优先级的就绪链表中,防止在两者优先级之间的任务插队。这就是优先级继承,防止了优先级的反转。
当继承了高优先级的低优先级的任务一旦释放了互斥量,若高优先级任务还在等待,则唤醒高优先级的任务,自己则恢复为原来的优先级,并把自己由于继承高优先级所在的高优先级链表移除并移动到原来的优先级链表。(相当于高优先级体验卡体验结束)。
BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder )
{
TCB_t * const pxMutexHolderTCB = ( TCB_t * ) pxMutexHolder;
BaseType_t xReturn = pdFALSE;
/* If the mutex was given back by an interrupt while the queue was
locked then the mutex holder might now be NULL. _RB_ Is this still
needed as interrupts can no longer use mutexes? */
if( pxMutexHolder != NULL )
{
/* If the holder of the mutex has a priority below the priority of
the task attempting to obtain the mutex then it will temporarily
inherit the priority of the task attempting to obtain the mutex. */
if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority )
{
/* Adjust the mutex holder state to account for its new
priority. Only reset the event list item value if the value is
not being used for anything else. */
if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
{
listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* If the task being modified is in the ready state it will need
to be moved into a new list. */
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE )
{
if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
taskRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Inherit the priority before being moved into the new list. */
pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
prvAddTaskToReadyList( pxMutexHolderTCB );
}
else
{
/* Just inherit the priority. */
pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
}
traceTASK_PRIORITY_INHERIT( pxMutexHolderTCB, pxCurrentTCB->uxPriority );
/* Inheritance occurred. */
xReturn = pdTRUE;
}
else
{
if( pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority )
{
/* The base priority of the mutex holder is lower than the
priority of the task attempting to take the mutex, but the
current priority of the mutex holder is not lower than the
priority of the task attempting to take the mutex.
Therefore the mutex holder must have already inherited a
priority, but inheritance would have occurred if that had
not been the case. */
xReturn = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
return xReturn;
}
释放互斥锁
xSemaphoreGive(USEESPMUTEXHandle);
xTaskPriorityDisinherit //优先级取消继承(恢复为原来的优先级)
BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder )
{
TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
BaseType_t xReturn = pdFALSE;
if( pxMutexHolder != NULL )
{
/* A task can only have an inherited priority if it holds the mutex.
If the mutex is held by a task then it cannot be given from an
interrupt, and if a mutex is given by the holding task then it must
be the running state task. */
configASSERT( pxTCB == pxCurrentTCB );
configASSERT( pxTCB->uxMutexesHeld );
( pxTCB->uxMutexesHeld )--;
/* Has the holder of the mutex inherited the priority of another
task? */
if( pxTCB->uxPriority != pxTCB->uxBasePriority )
{
/* Only disinherit if no other mutexes are held. */
if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 )
{
/* A task can only have an inherited priority if it holds
the mutex. If the mutex is held by a task then it cannot be
given from an interrupt, and if a mutex is given by the
holding task then it must be the running state task. Remove
the holding task from the ready list. */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Disinherit the priority before adding the task into the
new ready list. */
traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority );
pxTCB->uxPriority = pxTCB->uxBasePriority;
/* Reset the event list item value. It cannot be in use for
any other purpose if this task is running, and it must be
running to give back the mutex. */
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
prvAddTaskToReadyList( pxTCB );
/* Return true to indicate that a context switch is required.
This is only actually required in the corner case whereby
multiple mutexes were held and the mutexes were given back
in an order different to that in which they were taken.
If a context switch did not occur when the first mutex was
returned, even if a task was waiting on it, then a context
switch should occur when the last mutex is returned whether
a task is waiting on it or not. */
xReturn = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
return xReturn;
}
在释放互斥锁时判断此任务是否有继承过高优先级的任务,
if( pxTCB->uxPriority != pxTCB->uxBasePriority )
若是则恢复为原优先级, 并把自己从高优先级的链表中移除,加入到自己原来的优先级链表中。
若是一个高优先级的任务阻塞超时,低优先级的任务继承后,被更高优先级的任务抢占,则高优先级的任务在获取互斥量时判断超时后调用取消优先级继承函数。低优先级的继承体验卡只在高优先级阻塞时有效。
void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask )