#if ( configSUPPORT\_DYNAMIC\_ALLOCATION == 1 )
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE\_TYPE\_MUTEX )
#endif
#if ( ( configUSE\_MUTEXES == 1 ) && ( configSUPPORT\_DYNAMIC\_ALLOCATION == 1 ) )
QueueHandle_t xQueueCreateMutex( const uint8\_t ucQueueType )
{
QueueHandle_t xNewQueue;//句柄
const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;
//设置长度为1,缓存区大小为0
//生成队列,返回句柄
xNewQueue = xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );
prvInitialiseMutex( ( Queue_t \* ) xNewQueue );
//初始化互斥量
return xNewQueue;
}
#endif /\* configUSE\_MUTEXES \*/
#if ( configUSE\_MUTEXES == 1 )
static void prvInitialiseMutex( Queue_t \* pxNewQueue )
{
if( pxNewQueue != NULL )//判断
{//将队列句柄里面的信号量持有者置为NULL,类型置为互斥量,递归次数为0
pxNewQueue->u.xSemaphore.xMutexHolder = NULL;
pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;
pxNewQueue->u.xSemaphore.uxRecursiveCallCount = 0;
traceCREATE\_MUTEX( pxNewQueue );
//队列通用发送
( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );
}
else
{
traceCREATE\_MUTEX\_FAILED();
}
}
#endif /\* configUSE\_MUTEXES \*/
信号量是需要获取和释放的,在FreeRTOS中使用xQueueSemaphoreTake()和xQueueSemaphoreGive()来获取和释放。除了中断中使用的api需要在后面加上FromISR后缀,其他信号量使用的都是这两个函数。以下为这两个函数的原码解析:
BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue,
TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t \* const pxQueue = xQueue;
#if ( configUSE\_MUTEXES == 1 )
BaseType_t xInheritanceOccurred = pdFALSE;
#endif
/\* 确认句柄是否空 \*/
configASSERT( ( pxQueue ) );
/\* 检查这是否真的是一个信号量,在这种情况下,项目大小将是0 \*/
configASSERT( pxQueue->uxItemSize == 0 );
/\* 如果计划程序已挂起,则无法阻止 \*/
#if ( ( INCLUDE\_xTaskGetSchedulerState == 1 ) || ( configUSE\_TIMERS == 1 ) )
{
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
}
#endif
for( ; ; )
{
taskENTER\_CRITICAL();//关闭中断,进入临界区
{
/\*信号量是项目大小为0的队列,其中队列中的消息数是信号量的计数值。\*/
const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;
/\*判断队列中现在有数据吗?要运行调用任务必须是要访问队列的最高优先级任务\*/
if( uxSemaphoreCount > ( UBaseType_t ) 0 )
{
traceQUEUE\_RECEIVE( pxQueue );
/\*信号量是数据大小为零的队列,其中等待的消息是信号量的计数。将计数值减一 \*/
pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1;
/\* 配置互斥量 \*/
#if ( configUSE\_MUTEXES == 1 )
{//判断队列类型 == 互斥量
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
/\*记录信号量持有者实施所需的信息,如果有必要,优先继承\*/
pxQueue->u.xSemaphore.xMutexHolder = pvTaskIncrementMutexHeldCount();
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
}
#endif
/\* 配置互斥量 \*/
/\* 检查是否有其他任务被阻止,等待信号量,如果是,则取消阻止优先级最高的此类任务\*/
if( listLIST\_IS\_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
{
//判断pxQueue->xTasksWaitingToSend的链表里面是否为空,这个链表是用来记录等待发送而进入阻塞态的链表,返回pdFALSE说明不为空,即pxQueue->xTasksWaitingToSend存在数据项,返回pdTRUE说明为空,即pxQueue->xTasksWaitingToSend不存在数据项
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
//因为pxQueue->xTasksWaitingToSend存在数据项,移除其中的数据项
{
queueYIELD\_IF\_USING\_PREEMPTION();
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
taskEXIT\_CRITICAL();//退出临界区
return pdPASS;
}
else
//返回pdTRUE说明为空,即pxQueue->xTasksWaitingToSend不存在数据项,就不需要在移除任务,所以直接判断时间
{
if( xTicksToWait == ( TickType_t ) 0 )
{
/\* 要使继承发生,必须有初始时间,调整后的时间不能变为0,因为如果为0,函数就会退出\*/
/\* 配置互斥量 \*/
#if ( configUSE\_MUTEXES == 1 )
{
configASSERT( xInheritanceOccurred == pdFALSE );
/\* 断言判断继承变量是否为pdFALSE,即判断是否没有发生继承\*/
}
#endif
/\* 配置互斥量 \*/
/\* xTicksToWait 为0,未指定块时间,所以现在退出。\*/
taskEXIT\_CRITICAL();
traceQUEUE\_RECEIVE\_FAILED( pxQueue );
return errQUEUE_EMPTY;//返回一个错误指示
}
else if( xEntryTimeSet == pdFALSE )
{
/\*信号量计数为0,并且指定了块时间,因此配置准备阻止的超时结构\*/
vTaskInternalSetTimeOutState( &xTimeOut );
xEntryTimeSet = pdTRUE;
}
else
{
/\*已设置进入时间\*/
mtCOVERAGE\_TEST\_MARKER();
}
}
}
taskEXIT\_CRITICAL();
/\*中断和其他任务可以给予信号量,也可以从信号量中获取,现在关键部分已经退出 \*/
vTaskSuspendAll();//挂起所有任务,即挂起调度器
//上锁队列
prvLockQueue( pxQueue );
/\* 更新超时状态以查看它是否已过期。 \*/
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
/\* 块时间已指定且未过期。如果信号量计数为0,则进入Blocked状态以等待信号量变为可用。由于信号量是用队列实现的,因此队列为空相当于信号量计数为0。\*/
if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) //判断信号量是否为空
{
traceBLOCKING\_ON\_QUEUE\_RECEIVE( pxQueue );
#if ( configUSE\_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
taskENTER\_CRITICAL();
{
xInheritanceOccurred = xTaskPriorityInherit( pxQueue->u.xSemaphore.xMutexHolder );//实现优先级继承,如果继承成功则返回pdTRUE
}
taskEXIT\_CRITICAL();
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
}
#endif
//将任务放入阻塞队列
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
//解锁队列
prvUnlockQueue( pxQueue );
//如果恢复任务失败 则重新完成一次任务调度
if( xTaskResumeAll() == pdFALSE )
{
portYIELD\_WITHIN\_API();
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
}
else
{
/\* 没有超时,信号量计数也不是0,所以尝试再次使用信号灯。\*/
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
else
{
/\*超时,解锁队列 \*/
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
/\* 如果信号量计数为0,则现在退出,因为超时过期。否则返回以尝试获取,由于信号量是由队列实现的队列为空相当于信号量计数为0。\*/
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
{
#if ( configUSE\_MUTEXES == 1 )
{
/\* 只能在以下情况下设置xInheritanceOccurred,pxQueue->uxQueueType==queueQUEUE\_IS\_MUTEX,因此无需再次测试互斥对象类型,以检查它实际上是一个互斥对象。 \*/
if( xInheritanceOccurred != pdFALSE )
{
taskENTER\_CRITICAL();
{
UBaseType_t uxHighestWaitingPriority;
/\* 此任务对互斥体的阻塞导致另一个任务继承此任务的优先级。现在这个任务已超时,应取消优先级继承。同样,但仅低至下一个最高优先级正在等待同一互斥对象的任务。 \*/
uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout( pxQueue );
vTaskPriorityDisinheritAfterTimeout( pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority );
}
taskEXIT\_CRITICAL();
}
}
#endif
traceQUEUE\_RECEIVE\_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
}
}
}
这个释放信号量的本质使用的是xQueueGenericSend这个函数
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
const void \* const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition )
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t \* const pxQueue = xQueue;
configASSERT( pxQueue );//判断队列是否为空
configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );//判断队列中项目是否为空,数量大小是否为0,确定是否是信号量
configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
#if ( ( INCLUDE\_xTaskGetSchedulerState == 1 ) || ( configUSE\_TIMERS == 1 ) )
{
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );//判断调度器状态不能为挂起
}
#endif
/\*此函数将编码标准放宽到允许函数本身中的返回语句。这是在执行时间效率的利益。\*/
for( ; ; )
{
taskENTER\_CRITICAL();
{
/\* 现在队列有空位吗?正在运行的任务必须是要访问队列的优先级最高的任务。如果要覆盖队列中的头项,那么队列是否已满也无关紧要。\*/
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
{
traceQUEUE\_SEND( pxQueue );
#if ( configUSE\_QUEUE\_SETS == 1 )//判断队列集宏定义
{
const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting;
//记录pxQueue(要操作的队列)之前的等待消息数量
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
//调用函数prvCopyDataToQueue 将数据插入队列中,并记录是否需要执行任务切换(也就是是否需要进行上下文切换)。如果需要切换,则将xYieldRequired 置为true
if( pxQueue->pxQueueSetContainer != NULL )
{
if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) )
{
/\* 不将队列集通知为现有项目在队列中被覆盖,因此项目数队列中的值没有更改。\*/
mtCOVERAGE\_TEST\_MARKER();
}
else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE )
{
/\* 队列是队列集的成员,并且发布到队列集导致更高优先级的任务取消阻止。需要上下文切换。\*/
queueYIELD\_IF\_USING\_PREEMPTION();
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
}
else
{
/\* 如果有任务正在等待数据到达队列,然后立即取消阻止。 \*/
if( listLIST\_IS\_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
/\* 未阻塞的任务的优先级高于我们自己的任务,因此立即屈服。是的,在关键部分中这样做是可以的——内核会处理好这一点。 \*/
queueYIELD\_IF\_USING\_PREEMPTION();
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
}
else if( xYieldRequired != pdFALSE )
{
/\* 该路径是一种特殊情况,只有当任务持有多个互斥对象,并且互斥对象的返回顺序与获取互斥对象的顺序不同时,才会执行该路径。 \*/
queueYIELD\_IF\_USING\_PREEMPTION();
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
}
}
#else
{
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
/\* 如果有任务正在等待数据到达队列,然后立即取消阻止。 \*/
if( listLIST\_IS\_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
/\* 未阻塞的任务的优先级高于我们自己的任务,因此立即屈服。是的,在关键部分中这样做是可以的——内核会处理好这一点。 \*/
queueYIELD\_IF\_USING\_PREEMPTION();
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
}
else if( xYieldRequired != pdFALSE )
{
/\*该路径是一种特殊情况,只有当任务持有多个互斥对象,并且互斥对象的返回顺序与获取互斥对象的顺序不同时,才会执行该路径。\*/
queueYIELD\_IF\_USING\_PREEMPTION();
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
}
#endif /\* configUSE\_QUEUE\_SETS \*/
taskEXIT\_CRITICAL();
return pdPASS;
}
else
{
if( xTicksToWait == ( TickType_t ) 0 )
{
/\*队列已满,并且未指定块时间(或块时间已经过期),所以现在就离开。 \*/
taskEXIT\_CRITICAL();
/\* 在退出功能之前返回到原始权限级别。 \*/
traceQUEUE\_SEND\_FAILED( pxQueue );
return errQUEUE_FULL;
}
else if( xEntryTimeSet == pdFALSE )
{
/\*队列已满,并且指定了块时间,因此配置超时结构。 \*/
vTaskInternalSetTimeOutState( &xTimeOut );
xEntryTimeSet = pdTRUE;
}
else
{
mtCOVERAGE\_TEST\_MARKER();
}
}
}
taskEXIT\_CRITICAL();
## 最后
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**
**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/1f1379ca777b4a26a4bd50b4c5ccf868.png)
![img](https://img-blog.csdnimg.cn/img_convert/71f7826fe511378da14140f437e4aca0.jpeg)
![img](https://img-blog.csdnimg.cn/img_convert/bb13b6c0d719331421313b62d2cea156.png)
![img](https://img-blog.csdnimg.cn/img_convert/5017bf2440f34b00ace21c6a58c547b5.png)
![img](https://img-blog.csdnimg.cn/img_convert/55999d6c26c27b001cb13ed3c8969933.png)
![img](https://img-blog.csdnimg.cn/img_convert/a7f8ae81ddefc1c8d599ee468da0334b.png)
![](https://img-blog.csdnimg.cn/img_convert/d959f3a0b1b952db2a45bc78a554f5d6.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!
793)]
[外链图片转存中...(img-JTEhnFXv-1715759017794)]
[外链图片转存中...(img-Ute1Frb7-1715759017794)]
[外链图片转存中...(img-M3Xpinou-1715759017795)]
[外链图片转存中...(img-fR53lrpu-1715759017796)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!