深入理解FreeRTOS_学习笔记(7(2)

上一章已经了解了事件标志组,这章学习的依旧是任务之间的通信方式(任务通知)。如果有错误的地方,请批评指正,不胜感激!!

**


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

前言

本文学习的也是最后一个通信方式,也就是任务通知,也可以称作“通知任务”。本文的重点主要有两点:1.搞明白任务通知的三个状态(实现任务通知的关键),2.明白任务通知的优缺点,以及任务通知模拟出来的队列、信号量、事件组与真实的有何区别

一、任务通知是什么

FreeRTOS中,任务通知机制用于实现任务之间的同步与通信。任务通知可以用来通知其他任务某些事件的发生,例如某个资源的可用性或者某个数据的更新。
任务可以使用xTaskNotify()函数发送任务通知,其他任务可以使用xTaskNotifyWait()函数来等待任务通知。任务通知可以携带一个32位的数值作为参数,可以用来传递通知的具体内容。
任务通知机制可以用于多种场景,包括生产者消费者模型、状态机等。它是一种比较高效的任务间通信方式,相较于使用队列或者消息传递,它对系统资源的占用更少,延迟更小,响应速度更快。
它定义在task.c中,定义在每一个任务的任务控制块内

    #if ( configUSE\_TASK\_NOTIFICATIONS == 1 )
        volatile uint32\_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
        volatile uint8\_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    #endif

#ifndef configTASK\_NOTIFICATION\_ARRAY\_ENTRIES
    #define configTASK\_NOTIFICATION\_ARRAY\_ENTRIES 1
#endif

可以看到其实是两个数组,一个存储的是32位的数值,一个存储的是8位的状态,为什么要有状态呢?状态又是什么,有什么作用?我们一一来介绍

二、任务的状态

任务的通知状态:任务通知有三种状态

未等待通知状态:就是任务的初始状态
等待通知状态:当任务在没有通知的时候接收通知时(也就是任务没有接收到通知的时候调用了接收通知的函数,则此时必定接收不到通知,把该任务标记为等待通知状态(去等别的任务发给我通知),任务进入阻塞态),这样做的用处是什么呢? 答:当另外一个任务发通知给该任务时,此时发现任务处于等待通知的状态,然后就可以即可把该任务唤醒。
等待接收通知状态:当有其他任务向任务发送通知,但任务还未接收这一通知的这段期间内(当其他任务给该任务发了通知,但是该任务还没有接收,则将该任务标记为等待接收通知状态),这样做的用处就是当该任务调用了接收通知的函数,发现自身的状态为等待接收通知状态,则不用进入阻塞,直接接收通知值。
为什么要搞一个这样的通知状态?
答:
1.为了判断任务是否接收到了通知
2.不需要一个链表来挂载因等不到通知而阻塞任务,可以直接将任务挂入阻塞链表,因为当调用发送通知函数去唤醒该任务时只需要判断它是否处于等待通知状态(因等待通知进入阻塞)。像队列它有一个当前消息个数的变量可以知道队列中是否有消息,像信号量0就是没消息

那为什么任务通知不能以通知值是否为0判断是否有消息呢?
确实模拟信号量确实是怎么做的,但是如果是模拟队列的话,就不能怎么搞了,因为我发送一个0,0也算是数据,所以需要一个

如果现在还搞不懂这三个状态什么意思,没关系看后面的源码就懂了。

三、任务通知源码分析

任务通知的创建就不用说了,任务被创建时则就便有了任务通知,而且FreeRTOS默认任务通知是开启的。

1.xTaskNotify函数

这个函数其实也是个宏定义,实际上调用的是xTaskGenericNotify这个函数

#define xTaskNotify( xTaskToNotify, ulValue, eAction ) \
 xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT\_INDEX\_TO\_NOTIFY ), ( ulValue ), ( eAction ), NULL )

具体的原码介绍,看中文注释

#if ( configUSE\_TASK\_NOTIFICATIONS == 1 )//配置任务通知相关宏

    BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
                                   UBaseType_t uxIndexToNotify,
                                   uint32\_t ulValue,
                                   eNotifyAction eAction,
                                   uint32\_t \* pulPreviousNotificationValue )
    {
        TCB_t \* pxTCB;				//指向哪个任务句柄
        BaseType_t xReturn = pdPASS;//返回值,用于判断是否设置通知成功
        uint8\_t ucOriginalNotifyState;//原始的任务状态

        configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );
        configASSERT( xTaskToNotify );
        pxTCB = xTaskToNotify;//指向任务句柄

        taskENTER\_CRITICAL();//进入临界区
        {
            if( pulPreviousNotificationValue != NULL )//判断原始通知值是否存在
            {
                \*pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ];
                //覆盖任务通知值
            }
			//ucOriginalNotifyState 获得任务的原始状态
            ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ];
			//任务的状态设置为接收通知状态
            pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED;

            switch( eAction )
/\*eAction具有五个模式typedef enum
{
 eNoAction = 0, /\* 通知任务而不更新其通知值 
 eSetBits, /\* 在任务的通知值中设置位。 
 eIncrement, /\* 增加任务的通知值。
 eSetValueWithOverwrite, /\* 将任务的通知值设置为特定值,即使任务尚未读取以前的值。 
 eSetValueWithoutOverwrite /\* 如果任务已读取前一个值,则设置任务的通知值。
} eNotifyAction;
\*/
            {
                case eSetBits:
                    pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue;
                    break;

                case eIncrement:
                    ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++;
                    break;

                case eSetValueWithOverwrite:
                    pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;
                    break;

                case eSetValueWithoutOverwrite:

                    if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
                    {
                        pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;
                    }
                    else
                    {
                        /\* 无法将值写入任务 \*/
                        xReturn = pdFAIL;
                    }

                    break;

                case eNoAction:

                    /\* 正在通知任务,但未更新其通知值。\*/
                    break;

                default:

                    /\* 如果处理了所有枚举,则不应到达此处。通过测试编译器不能假定为常量的值来人为地强制断言。 \*/
                    configASSERT( xTickCount == ( TickType_t ) 0 );

                    break;
            }

            traceTASK\_NOTIFY( uxIndexToNotify );

            /\* 如果任务处于阻止状态,专门用于等待通知,则立即取消阻止。 \*/
            if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
            //如果原始的任务状态就是等待通知状态,就立刻从阻塞链表中移除加入就绪链表中
            {
                ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
                prvAddTaskToReadyList( pxTCB );
                

                /\*该任务不应出现在事件列表中。 \*/
                configASSERT( listLIST\_ITEM\_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );

                #if ( configUSE\_TICKLESS\_IDLE != 0 )
                    {
                        /\* 如果任务被阻止等待通知,则xNextTaskUnblockTime可能会设置为被阻止任务的超时时间。如果由于超时以外的原因取消阻止任务,xNextTaskUnblockTime通常保持不变,因为当勾号计数等于xNextTTaskUnblocktime时,它将自动重置为新值。然而,如果使用了无障碍空转,那么尽早进入睡眠模式可能更为重要——因此,请在此处重置xNextTaskUnblockTime,以确保其尽早更新。 \*/
                        prvResetNextTaskUnblockTime();
                    }
                #endif

                if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
                {
                    /\* 通知的任务的优先级高于当前执行的任务,实现任务切换 \*/
                    taskYIELD\_IF\_USING\_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE\_TEST\_MARKER();
                }
            }
            else
            {
                mtCOVERAGE\_TEST\_MARKER();
            }
        }
        taskEXIT\_CRITICAL();

        return xReturn;
    }

#endif 

其实也不难理解,前面的部分中通过switch case分支对任务的值进行了修改,之后就是对状态的判断,如果任务原始状态就是等待通知,此时发送了通知给他,则立刻将这个任务从阻塞态唤醒,并且修改它的任务状态。

2.xTaskNotifyWait函数

这个函数也是一个宏定义,实际调用的是xTaskGenericNotifyWait这个函数

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

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

片转存中…(img-8bh8Cx51-1715759088438)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

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

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: FreeRTOS V2 是一个实时操作系统(RTOS),V2 API 是它的应用程序编程接口。 FreeRTOS 是一个广泛应用于嵌入式系统的开源实时操作系统。它提供了可靠的任务调度、时间管理、内存管理和通信机制,使开发者能够轻松地编写出高效、可靠的嵌入式应用程序。 V2 API 是 FreeRTOS 的应用程序编程接口的第二个版本。它提供了一组函数和数据结构,用于控制和管理 FreeRTOS 内核的各个部分。通过这些 API,开发者可以创建和管理任务、队列、信号量、互斥锁等系统资源,实现任务间的通信和同步。 V2 API 在原有的 API 基础上进行了一些增强和改进。它增加了更多的功能和特性,提供了更丰富的资源管理和任务调度机制,提高了系统的可靠性和效率。 使用 FreeRTOS V2 API,开发者可以轻松地编写出符合实时要求的嵌入式应用程序。他们可以通过创建任务和使用任务通信机制,实现系统中不同任务之间的并发执行和数据传输。他们还可以使用 V2 API 中提供的事件标志和定时器功能,实现任务的同步和定时控制。 总之,FreeRTOS V2 API 是 FreeRTOS 实时操作系统的应用程序编程接口的第二个版本。它提供了一种方便、高效的方式来使用 FreeRTOS 的功能和特性,使开发者能够轻松地开发出符合实时要求的嵌入式应用程序。 ### 回答2: FreeRTOS是一个开源的实时操作系统内核,是由迈克尔·贝瑞(Michael Barry)创建的。它提供了一个可移植的、可扩展的、可定制的操作系统内核,用于嵌入式系统和实时应用。其中,FreeRTOS V2是FreeRTOS的第二个主要版本。 FreeRTOS V2提供了一些新的API接口和功能,以增强原有版本的功能和性能。它引入了动态任务优先级分配、任务通知和DMA支持,并对进行多核操作和IPC(进程间通信)做出了改进。同时,FreeRTOS V2添加了一些通用的编程接口,以提供更多的灵活性和互操作性。 在FreeRTOS V2中,新增的任务通知机制使得任务之间可以更加方便地进行通信和同步。它允许任务发送信号给其他任务,以通知其有任务需要处理。这个机制在实时系统中非常有用,可以提高系统的响应性和效率。 此外,FreeRTOS V2还引入了DMA支持,可以通过DMA传输数据,从而减少CPU的负担,提高系统的效率和响应速度。 另外的改进包括使用动态任务优先级分配,可以根据不同任务的优先级动态地分配系统资源,使得系统更加灵活。 总之,FreeRTOS V2作为FreeRTOS的新版本,通过新增API接口和功能的方式,进一步提升了它的功能和性能,使得嵌入式系统和实时应用能够更加高效地运行。 ### 回答3: FreeRTOS是一个开源的即时操作系统内核,提供轻量级的任务调度和资源管理功能,针对嵌入式系统设计开发。目前最新版本是FreeRTOS V10.4.1。 FreeRTOS V2 API是FreeRTOS内核的一种编程接口,用于实现任务的创建、删除、挂起和恢复等操作。V2 API是在旧版本API基础上进行了优化和扩展,提供了更强大和灵活的功能。 V2 API引入了任务通知功能,可以在任务之间进行通信和同步操作。通过信号量和消息队列等机制,任务可以共享和传递数据。这大大简化了任务之间的协作和数据交换。 V2 API还增加了事件组功能,可以用于等待和通知多个事件的发生。任务可以根据不同的事件发生状态执行不同的操作。这在多任务并发和同步控制方面非常有用。 另外,V2 API还提供了软件定时器功能,可以用于定期执行一些操作。这对于周期性任务和定时操作非常有帮助。 总之,FreeRTOS V2 API拓展了旧版本API的功能,提供了更多的任务管理和同步机制,使得嵌入式系统开发更加灵活和方便。它广泛应用于各种类型的嵌入式系统,并受到开发者的广泛欢迎。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值