本文参考了很多优秀的FreeRTOS的文章,如有不妥,及时联系
任务特性
- 支持抢占、优先级
- 每个任务都拥有堆栈
- 使用抢占要考虑重入
任务状态
阻塞态:vTaskDelay(),任务在等待队列、信号量、事件组、通知或互斥信号量的时候。有一个超时时间,当超过这个时间,不论等待的事件是否到来,都会退出阻塞态
挂起态:无超时时间
任务优先级
在FreeRTOS中,任务优先级的范围 0~configMAX_PRIORITIES-1。空闲任务优先级默认是0,软件定时器默认是MAX-1,最好不要用这两个优先级
数字越大,优先级越高
默认一个优先级下可以挂多个任务,这些任务使用时间片轮转调度器获取运行时间
使能了configUSE_PORT_OPTIMISED_TASK_SELECTION,最大数目32
任务实现
任务的具体内容,比如一个while(1)循环,里面包括任务的应用程序和延时vTaskDelay。如果要退出while(1)要删除vTaskDelete(NULL)此任务
任务控制块
描述任务属性的数据结构
任务堆栈
任务堆栈用来保存任务现场(CPU寄存器值),创建任务的时候需要指定任务堆栈,任务堆栈的变量类型为StackType_t,再次运行任务时会从上次中断的地方开始运行
任务的创建和删除
xTaskCreate() //(常用)用动态方法创建一个任务,内存自动创建
xTaskCreateStatic() //用静态方法创建一个任务,内存需要自己创建内存(空闲任务和软件定时器任务)
xTaskDelete(NULL) //删除一个任务,NULL为删除自己,删除别人可以用句柄
任务创建删除示例
实现功能:
动态方式创建两个任务,task1和task2
task1:实现led0灯 500ms延时翻转,执行5次后删除task2
task2:实现led1灯 亮100ms,灭900ms
写main函数,执行如下3步
写task1和task2实现函数
任务的挂起和恢复
vTaskSuspend() //挂起一个任务
vTaskResume() //恢复一个任务的运行
vTaskResumeFromISR() //中断服务函数中恢复一个任务的运行
任务挂起后堆栈还在,如果删除了任务,对应的堆栈将被释放
FreeRTOS的调度策略
调度器在任何时候总是从当前所有状态为就绪状态的任务中选取优先级最高的那个来让其执行。如果当前处于就绪态的且优先级最高的任务有多个,则调度器将轮流将每个任务转换为运行状态,然后再将它切换回就绪态,使得每个任务在每轮中最多执行一个时间片的时间。即按时间片轮流执行他们。
任务切换
执行系统调用,比如vTaskDelay
系统节拍时钟中断
调度器并不只会在时间片结束后执行。
时间片与tick中断
FreeRTOS会使用宿主硬件提供的系统定时器sysTick,来产生周期性的中断,我们把它叫做“tick中断”,例如对于Cortex-M3内核的单片机,就是利用了CM3内核的SysTick系统滴答定时器来实现的。tick中断的频率在FreeRTOSConfig.h中使用configTICK_RATE_HZ宏来定义,单位Hz,典型值为100,一般不会超过1000。tick中断频率的倒数就是一个tick周期,而一个时间片的长度就等于一个tick周期。1000Hz==>1ms
空闲任务
FreeRTOS程序在任意时刻,必须至少有一个任务处于运行状态,为了达到这个要求,FreeRTOS使用了Idle任务:当vTaskStartScheduler调用后,调度器会自动创建Idle任务,这个任务的任务函数就是一个连续性工作的任务,所以他总是可以处于就绪态(在运行态和就绪态之间转换,没有其他状态)。由于Idle任务的优先级是最低的(优先级为0),所以Idle任务不会抢占用户任务的运行。当其他高优先级的任务需要运行时,他们会抢占Idle任务。
Idle任务主要用于资源回收清理工作,例如当你在程序中删除一个任务后,就需要Idle任务去清理这个任务占用的资源。因此,不要让Idle任务“饿死”,具体而言,不要创建一个优先级比Idle任务优先级高,且连续性工作的任务。如果应用程序也需要一个在背后连续工作的任务,则应该设置其优先级和Idle任务相同。当然这个需求更好的实现方法是通过下面介绍的Idle钩子来完成。
空闲任务钩子函数
为了使用Idle任务钩子函数,必须在FreeRTOSConfig.h中将configUSE_IDLE_HOOK 定义为1,并自己实现vApplicationIdleHook()函数
一定不要在vApplicationIdleHook()函数中执行会让Idle任务阻塞或者挂起的代码,否则将导致其它任务无法切换到运行态,也尽量不要让流程卡在这个钩子函数中,vApplicationIdleHook函数应该在简短的运行后立刻返回。特别是在应用程序有删除任务的需求的时候。因为在Idle任务的主循环中,除了调用vApplicationIdleHook(),还需要执行清理那些被删除的任务的TCB存储空间和分配的stack空间的代码,如果不能保证以上两点,则这些空间无法得到释放,会导致内存泄露。
哪些情况下推荐用空闲钩子函数?
不太重要的后台程序,优先级低,持续工作
测量CPU空闲时间