汉口网站制作,企业网站 dede phpcms 帝国,郑州惠济区建设局网站,洛阳网站建设培训文章目录 何为任务通知#xff1f;任务通知使用例子任务通知的优势以及劣势优势劣势 深入源码看看API函数内部干了什么函数的种类函数都做了啥#xff1f; 软件定时器软件定时器的作用软件定时器内部到底做了什么实现了“闹钟”功能引入守护任务#xff0c;守护任务做了啥任务通知使用例子任务通知的优势以及劣势优势劣势 深入源码看看API函数内部干了什么函数的种类函数都做了啥 软件定时器软件定时器的作用软件定时器内部到底做了什么实现了“闹钟”功能引入守护任务守护任务做了啥守护任务的调度简单深入源码学习软件定时器 何为任务通知
所谓任务通知你可以反过来读通知任务。就是就是一个简单唤醒其他任务的功能吗那这样我队列、信号量、互斥量一样可以啊那为什么要引进任务通知呢那么我们就可以去看看任务通知到底有什么宇宙不同。 任务通知的通信是没有间隔的 使用队列、信号量、事件组时我们都要事先创建对应的结构体双方通过中间的结构体通信 使用任务通知时任务结构体TCB中就包含了内部对象可以直接接收别人发过来的通知 就是说创建任务的时候任务里面创建的TCB结构体里面的某些变量就足以使用任务通知了 通知状态 任务通知就是根据这个TCB里面的通知状态进行通信的。
任务通知使用例子 被通知的任务TCB结构体的通信状态的变换
任务通知的优势以及劣势
优势
明确性、效率高我们使用队列、信号量、事件组等等方法时并不知道对方是谁。使用任务通知时可以明确指定通知哪个任务。 节省资源相信深入学习过队列、信号量、互斥量的都知道它们在使用之前都需要创建创建的时候里面都会分配一个结构体空间里面会有链表、以及其他数据队列传递数据时更多成了一个Buffer缓冲区这些都是需要空间的对于队列的细节可以看看我的这个文章队列内部机制在嵌入式里面所谓是寸土寸金对于空间利用来说所谓是斤斤计较所以能省则省任务通知则是不用创建节省了很多空间
劣势
不能发送数据给ISR ISR并没有任务结构体所以无法使用任务通知的功能给ISR发送数据。但是ISR可以使用任务通知的功能发数据给任务。 数据只能给该任务独享 使用队列、信号量、事件组时数据保存在这些结构体中其他任务、ISR都可以访问这些数据。 使用任务通知时数据存放入目标任务中只有它可以访问这些数据。 在日常工作中这个限制影响不大。因为很多场合是从多个数据源把数据发给某个任务而不是把一个数据源的数据发给多个任务。 无法缓冲数据 使用队列时假设队列深度为N那么它可以保持N个数据。 使用任务通知时任务结构体中只有一个任务通知值只能保持一个数据。 无法广播给多个任务 使用事件组可以同时给多个任务发送事件。 使用任务通知只能发个一个任务。 如果发送受阻发送方无法进入阻塞状态等待 假设队列已经满了使用 xQueueSendToBack() 给队列发送数据时任务可以进入阻塞状态等待发送完成。 使用任务通知时即使对方无法接收数据发送方也无法阻塞等待只能即刻返回错误。
深入源码看看API函数内部干了什么
函数的种类
任务通知有2套函数简化版、专业版列表如下
简化版函数的使用比较简单它实际上也是使用专业版函数实现的 专业版函数支持很多参数可以实现很多功能 具体的API函数使用方法以及参数可以看看这个手册FreeRTOS开发手册
函数都做了啥
这些函数都无非和队列它们一样都是唤醒任务或者阻塞本身任务通知就是有一点不一样就是要修改任务里面的TCB里面的通知状态仅此而已关于任务的唤醒以及阻塞可以看看我之前写的文章任务的调度机制 就可以把他们的作为概括为这样 挑出其中一个函数来分析
软件定时器
软件定时器的作用
软件定时器就是闹钟你可以设置闹钟 1、在30分钟后让你起床工作 2、每隔1小时让你例行检查机器运行情况 软件定时器也可以完成两类事情 1、在未来某个时间点运行函数 2、周期性地运行函数 日常生活中我们可以定无数个闹钟这无数的闹钟要基于一个真实的闹钟。 在FreeRTOS里我们也可以设置无数个软件定时器它们都是基于系统滴答中断(Tick Interrupt)。
软件定时器内部到底做了什么实现了“闹钟”功能
前面所学的什么队列、信号量啊等等处理任务通知其他都需要创建这个软件定时器也是需要创建的那它的创建都创建了些什么呢毫无疑问还是那些结构体以及链表那么它们的结构体里面的内容成员是什么呢 在人类世界中我们都是以时分秒为单位计算的比如一个小时后去上班等我五分钟我上个厕所…等等。那么软件定时器是怎么样的呢它们的单位是什么呢其实它们的单位是Tick学习任务调度的时候任务调度也是由Tick Interrupt控制调度的那么对于它们来说就是多少个Tick后我就来运行还有就是我们人类世界是有一个表来计数时间的那它们是通过什么来计数时间的呢它们是在Tick中断里面进行计数的 那时间到了我要去哪里找到那些任务到点了需要运行呢这时候就需要链表上场表演了创建的定时器的时候创建的各个结构体都会储存在一个链表里面到时候我就会里面找比对一下就知道了谁时间到了周期性的还会更新里面的启动时间和到点时间方便下次运行
引入守护任务守护任务做了啥
在事件组中我们看到FreeRTOS的作者担心的问题就是唤醒任务你不知道要唤醒的任务的个数具体可以看看我写的这个文章事件组内部机制学习害怕唤醒所需的时间过长影响中断的性能那么定时器也会有类似的担忧问题讨论这个担忧的问题是什么之前我们来看看在哪里执行定时器回调函数呢第一印象就是在Tick中断中执行 1、在Tick中断中判断定时器是否到时间了 2、如果时间到了就调用软件定时器的回调函数 3、回调函数里面不得影响到别的任务需要尽快执行 不要调用会导致阻塞的API函数比如 vTaskDelay() 可以调用 xQueueReceive() 之类的函数但是超时时间要设为0即刻返回不可阻塞 但是在FreeRTOS实时操作系统中它想做到实时就不可能允许在内核在中断中执行不确定的大小万一定时器函数非常的长运行的时间非常长就会导致Tick中断迟迟无法退出影响任务的调度进入影响整个系统 所以在FreeRTOS中不再Tick中断中执行软件定时器的回调函数。 那么在哪里调用定时器回调函数呢它的做法和事件组的做法相同都是利用队列唤醒一个任务来执行其他的工作的软件定时器中的这个任务叫“守护任务”以前被称为Timer server但是这个任务要做并不仅仅是定时器相关所以改名为RTOS Damemon Task。因为这个做法使用了队列而不是在Tick中断中调用回调函数的所以软件定时器的启动、停止函数的参数有一个超时时间也就不足为怪了
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );当FreeRTOS的配置项 configUSE_TIMERS 被设置为1时在启动调度器时会自动创建RTOSDamemon Task。 守护任务的优先级为configTIMER_TASK_PRIORITY定时器命令队列的长度为configTIMER_QUEUE_LENGTH。 守护任务不是专为某个定时器服务的它还要处理其他定时器 守护任务的调度 守护任务的调度跟普通的任务并无差别。当守护任务是当前优先级最高的就绪态任务时它就可以运行。它的工作有两类 1、处理命令从命令队列里取出命令、 2、处理执行定时器的回调函数 能否及时处理定时器的命令、能否及时执行定时器的回调函数严重依赖于守护任务的优先级。下面使用2个例子来演示。 例子1守护任务的优先性级较低 t1Task1处于运行态守护任务处于阻塞态。 守护任务在这两种情况下会退出阻塞态切换为就绪态命令队列中有数据、某个定时器超时了。至于守护任务能否马上执行取决于它的优先级。 t2Task1调用 xTimerStart() 要注意的是 xTimerStart() 只是把start timer的命令发给定时器命令队列使得守护任务退出阻塞态。 在本例中Task1的优先级高于守护任务所以守护任务无法抢占Task1。 t3Task1执行完 xTimerStart() 但是定时器的启动工作由守护任务来实现所以 xTimerStart() 返回并不表示定时器已经被启动了。 t4Task1由于某些原因进入阻塞态现在轮到守护任务运行。 守护任务从队列中取出start timer命令启动定时器。 t5守护任务处理完队列中所有的命令再次进入阻塞态。 Idel任务时优先级最高的就绪态任务它执行。 注意假设定时器在后续某个时刻tX超时了超时时间是tX-t2而非tX-t4从xTimerStart() 函数被调用时算起。 例子2守护任务的优先性级较高 t1Task1处于运行态守护任务处于阻塞态。 守护任务在这两种情况下会退出阻塞态切换为就绪态命令队列中有数据、某个定时器超时了。 至于守护任务能否马上执行取决于它的优先级。 t2Task1调用 xTimerStart() 要注意的是 xTimerStart() 只是把start timer的命令发给定时器命令队列使得守护任务退出阻塞态。 在本例中守护任务的优先级高于Task1所以守护任务抢占Task1守护任务开始处理命令队列。 Task1在执行 xTimerStart() 的过程中被抢占这时它无法完成此函数。 t3守护任务处理完命令队列中所有的命令再次进入阻塞态。 此时Task1是优先级最高的就绪态任务它开始执行。 t4Task1之前被守护任务抢占对 xTimerStart() 的调用尚未返回。现在开始继续运行次函数、返回。 t5Task1由于某些原因进入阻塞态进入阻塞态。Idel任务时优先级最高的就绪态任务它执行。 注意定时器的超时时间是基于调用 xTimerStart() 的时刻tX而不是基于守护任务处理命令的时刻tY。假设超时时间是10个Tick超时时间是tX10而非tY10。 简单深入源码学习软件定时器
#define xTimerstart(xTimer,xTicksTowait ) \
xTimerGenericCommand(( xTimer ) tmrCONWMAND_START( xTaskGetTickCount())NULL( xTicksTowait ) )函数内部都做了什么 那么毫无疑问肯定是守护函数在接收队列那么守护函数里面具体做了什么呢