FreeRTOS任务通知(Task Notifications)及内部机制

简介

        所谓" 任务通知 " ,你可以反过来读 " 通知任务 "
        我们使用队列、信号量、事件组等等方法时,并不知道对方是谁。使用任务通知时,可以明确指定:通知哪个任务。
        使用队列、信号量、事件组时,我们都要事先创建对应的结构体,双方通过中间的结构体通信。
        使用任务通知时,任务结构体 TCB 中就包含了内部对象,可以直接接收别人发过来的"通知 "

 

任务通知的特性

优势及限制 

任务通知的优势:
        效率更高:使用任务通知来发送事件、数据给某个任务时,效率更高。比队列、信号量、事件组都有大的优势。
        更节省内存:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。
任务通知的限制:
        不能发送数据给 ISR :ISR 并没有任务结构体,所以无法使用任务通知的功能给 ISR 发送数据。但是 ISR可以使用任务通知的功能,发数据给任务。
        数据只能给该任务独享使用队列、信号量、事件组时,数据保存在这些结构体中,其他任务和ISR 都可以访问这些数据。使用任务通知时,数据存放入目标任务中,只有它可以访问这些数据。
        在日常工作中,这个限制影响不大。因为很多场合是从多个数据源把数据发给某个任务,而不是把一个数据源的数据发给多个任务。
        无法缓冲数据:使用队列时,假设队列深度为 N ,那么它可以保持 N 个数据。使用任务通知时,任务结构体中只有一个任务通知值,只能保持一个数据。
        无法广播给多个任务:使用事件组可以同时给多个任务发送事件。使用任务通知,只能发个一个任务。
        如果发送受阻,发送方无法进入阻塞状态等待假设队列已经满了,使用 xQueueSendToBack() 给队列发送数据时,任务可以进入阻塞状态等待发送完成。使用任务通知时,即使对方无法接收数据,发送方也无法阻塞等待,只能即刻返回错误。

通知状态和通知值

每个任务都有一个结构体:TCB(Task Control Block),里面有 2 个成员:

        一个是 uint8_t 类型,用来表示通知状态。

        一个是 uint32_t 类型,用来表示通知值。

通知状态有 3 种取值:

taskNOT_WAITING_NOTIFICATION:任务没有在等待通知
taskWAITING_NOTIFICATION :任务在等待通知
taskNOTIFICATION_RECEIVED :任务接收到了通知,也被称为 pending( 有数据了,待处理)

 

通知值可以有很多种类型:
计数值
( 类似事件组 )
任意数值

 

任务通知的使用

        使用任务通知,可以实现轻量级的队列( 长度为 1) 、邮箱 ( 覆盖的队列 ) 、计数型信号量、二进制信号量、事件组。

两类函数

        任务通知有 2 套函数,简化版、专业版,列表如下:
        简化版函数的使用比较简单,它实际上也是使用专业版函数实现的。
        专业版函数支持很多参数,可以实现很多功能。
        

 

xTaskNotifyGive/ulTaskNotifyTake 

        在任务中使用 xTaskNotifyGive 函数,在 ISR 中使用 vTaskNotifyGiveFromISR 函数,都是直接给其他任务发送通知:
        使得通知值加一。
        并使得通知状态变为"pending" ,也就是 taskNOTIFICATION_RECEIVED ,表示有数据了、待处理。
        
        可以使用 ulTaskNotifyTake 函数来取出通知值
        如果通知值等于 0 ,则阻塞 ( 可以指定超时时间 )当通知值大于 0 时,任务从阻塞态进入就绪态 在 ulTaskNotifyTake 返回之前,还可以做些清理工作:把通知值减一,或者把通知值清零使用 ulTaskNotifyTake 函数可以实现轻量级的、高效的二进制信号量、计数型信号量。

 

这几个函数的原型如下:

 xTaskNotifyGive 函数的参数说明如下:

vTaskNotifyGiveFromISR 函数的参数说明如下:

 

ulTaskNotifyTake 函数的参数说明如下:

 

xTaskNotify/xTaskNotifyWait

xTaskNotify 函数功能更强大,可以使用不同参数实现各类功能,比如:
1、让接收任务的通知值加一:这时 xTaskNotify() 等同于 xTaskNotifyGive()
2、 设置接收任务的通知值的某一位、某些位,这就是一个轻量级的、更高效的事件组。
3、把一个新值写入接收任务的通知值:上一次的通知值被读走后,写入才成功。这就是轻量级的、长度为 1 的队列。
4、用一个新值覆盖接收任务的通知值:无论上一次的通知值是否被读走,覆盖都成功。类似
xQueueOverwrite() 函数,这就是轻量级的邮箱。
xTaskNotify() xTaskNotifyGive() 更灵活、强大,使用上也就更复杂。
xTaskNotifyFromISR()是它对应的 ISR 版本
这两个函数用来发出任务通知,使用哪个函数来取出任务通知呢?
        使用 xTaskNotifyWait() 函数!它比 ulTaskNotifyTake() 更复杂:
                可以让任务等待(可以加上超时时间 ) ,等到任务状态为 "pending"( 也就是有数据)。
                还可以在函数进入、退出时,清除通知值的指定位。
这几个函数的原型如下: 
xTaskNotify 函数的参数说明如下:

 

 

xTaskNotifyWait 函数列表如下: 

任务通知内部机制 

FreeRTOS的任务通知(task notification)提供了一种任务间轻量级的同步和通信机制,允许任务相互通知和等待事件。其内部机制如下:

1.32位的通知值(Notification Value):
2.每个任务通知都是一个32位的无符号整数,可以被发送和接收。
3.任务可以发送通知给其他任务,也可以等待来自其他任务的通知。
4.发送通知:
5.任务可以使用 xTaskNotifyGive() 函数向其他任务发送通知。
6.这个函数会将目标任务的通知值递增。如果目标任务在等待通知,它将会被唤醒。
7.接收通知:
8.任务可以使用 ulTaskNotifyTake() 函数等待通知。
9.如果任务的通知值为0,它将阻塞在这个函数调用上,直到接收到非零的通知值。
10.如果任务的通知值已经是非零的,它会立即返回,并减少通知值。
11.通知值的处理:
12.通知值可以被用来传递简单的状态信息或者事件。
13.接收任务可以根据通知值的具体内容执行不同的操作或者决策。
14.任务通知的清零和超时处理:
15.可以使用 ulTaskNotifyTake() 函数的第二个参数来设置超时时间,超时后函数会返回一个特殊的值以指示超时。
16.可以使用 ulTaskNotifyValueClear() 函数来清除任务的通知值。

        我们其实可以把任务通知的一系列操作看成之前讲的队列,只不过任务通知无需创建队列结构体,因为任务通知的对象是任务,在创建任务的时候,就已经在TCB结构体里面定义了任务通知值和任务状态,我们只需要对每个任务的这两个值进行操作就可以了。

        至于说的那些阻塞、唤醒、等待,思路和前面的一模一样,都是利用关闭调度、链表的方法来实现的,如果有不明白的,可以去看我之前的一系列的讲解,会有详细讲解如何把任务放进去阻塞链表和唤醒阻塞链表里的任务。

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值