深入理解FreeRTOS_学习笔记(5

#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后缀,其他信号量使用的都是这两个函数。以下为这两个函数的原码解析:

xQueueSemaphoreTake

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

xQueueSemaphoreGive(xQueueGenericSend)

这个释放信号量的本质使用的是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)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值