1、FreeRTOS:
(1)调度策略使用了抢占式调度和时间片调度的结合,不同级别间的任务抢占式调度,同级别任务采用时间片调度。采用了动态内存申请。
(2)任务优先级:数字越高,任务优先级越高。STM32中最大为32-1
(3)时间片的长度,依据滴答定时器的设定。滴答定时器为最高优先级
(4)任务切换:高优先级会延时释放CPU控制权
电机控制任务balance:100Hz频率运行,优先级为4(vTaskDelayUntil()函数)
OLED显示任务show: 1Hz频率运行,优先级为3(vTaskDelayUntil()函数)
消息帧任务modbus:优先级为0,没有更高优先级任务抢占时会一直运行
(但这样相当于一直占用空闲CPU的资源,设置为更高优先级可能会更好)
vTaskDelayUntil()函数:FreeRTOS中任务延时函数之一,让任务按照指定周期性方式运行。会使任务挂起,直至指定时间到达为止。时间为:上次此任务运行的时间+周期频率。
(5)任务堆栈:任务切换时,存储任务数据的内存区域。设置为了512
(6)滴答定时器
时间节拍频率设置为1000,周期1ms
初始化于delay_init(u8 SYSCLK)函数,SYSCLK=168
reload是滴答定时器的计数值。
reload = SYSCLK*100000/configTICK_RATE_HZ = 168*1000000/1000 = 168*1000
所以单次时间:reload/168M = 1ms
(7)开始任务start_task:用于创建其他任务
(8)临界区:相当于关中断,关闭所有优先级的中断
创建完start_task任务后退出临界区
(9)任务调度器的挂起:未关闭中断,只是停止任务的切换,不会被其他任务打断
仅仅防止了任务间的资源争夺
vTaskSuspend()、vTaskResume()挂起单个任务
(10)FreeRTOS的中断管理:
通常只能管理5~15优先级的,通过portDISABLE_INTERRUPTS(),优先级分组默认为4,
设置FreeRTOS可管理的最高优先级,优先级高于5的都无法管理
(11)中断优先级分组
STM32用了中断优先级配置寄存器的高4位[7:4]来配置,最大16级优先级,5种分配方式
FreeRTOS中默认是用优先级分组4,不使用子优先级,方便抢占优先级判断。
2、FreeRTOS列表:双向环形链表
(1)列表
就绪列表:每个优先级都有一个列表,有标志位,当该优先级列表中有任务时置1,共32个
阻塞列表:因等待事件而被阻塞的任务
(2)列表结构体
uxNumberOfItems:列表中的列表项数量
*pxIndex:用于遍历列表项的指针
xListEnd:列表末尾项,就是一个列表项,方便对列表操作
(3)列表项结构体
xItemValue:列表项的值,用于确定插入列表时的位置
*pxNext:下一个列表项
*pxPrevious:上一个列表项
*pvOwner:列表项的拥有者,即任务
*pxContainer:列表项所在列表
(4)列表和列表项的初始化
最开始只需要给定一个该结构体类型的指针即可,函数中自动完成创建
(5)列表项的插入:两种方式
vListInsert( ):根据列表项值插入
vListInsertEnd( ):插入到pxIndex所指向列表项的前面
指定要插入的列表和列表项
3、FreeRTOS移植
移植步骤:
(1)添加FreeRTOS源码
include:FreeRTOS头文件,FreeRTOS.h、task.h、timers.h等
portable:FreeRTOS的移植文件。只保留Keil、RVDS、MemMang这三个。
Keil:指向RVDS文件夹
RVDS:不同内核芯片的移植文件
MemMang:内存管理文件
croutine.c:协程相关文件
event_groups.c:时间相关文件
list.c:列表相关文件
queue.c:队列相关文件
stream_buffer.c:流式缓冲区相关文件
tasks.c:任务相关文件
timers.c:软件定时器相关文件
(2)添加FreeRTOSConfig.h:
FreeRTOS操作系统的配置文件。FreeRTOS操作系统是可裁剪的,可以根据需求裁剪掉不需要用到的FreeRTOS功能,以此来节约MCU中内存资源。
三种获取途径:1、自行编写,根据需求裁剪 2、官方提供的演示工程 3、正点原子工程
(3)修改SYSTEM文件:
sys.h:定义系统文件夹是否支持FreeRTOS,宏SYSTEM_SUPPORT_OS改为1
usart.c:添加FreeRTOS头文件,删掉原先UCOS部分的代码(正点写的)
delay.c:对滴答定时器SysTick设定
1、SysTick_Handler():中断服务函数中添加xPortSysTickHandler();
2、delay_init():初始化SysTick重装载值等,设定FreeRTOS心跳节拍
7、FreeRTOS消息队列
(1)消息队列
是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递)
(2)队列的读写
对队列读写时都会进入临界区(关中断)防止多任务冲突。
(任务切换依靠PendSV,是最低优先级,可以停止任务切换)
写队列xQueueSend(),读队列xQueueReceive()。函数中包含了临界区的使用。
(3)队列数据传输方式
可以传递数据和指针(大数据时)
(4)阻塞时间设置
0:直接返回不等待
0~port_MAX_DELAY:若在阻塞时间内无法入队,超时后直接返回不再等待
Port_MAX_DELAY:死等,直到可以入队为止。出队阻塞与入队阻塞类似。
(5)队列操作基本过程:
a、创建队列
b、往队列写入消息
c、从队列读取消息。若无数据,则阻塞
(6)队列结构体
存储区域起始地址pcHead:首个队列项
下一个写入位置pcWriteTo:下个队列项位置
等待发送列表xTasksWaitingToSend、等待接收列表xTasksWaitingToReceive
非空闲队列项目数量uxMessagesWaiting
队列长度uxLength
队列项目大小:单个队列项大小uxItemSize
上锁计数器cRxLock:cRxLock上锁的时候,是无法操作等待发送列表xTaskWaitingToSend和等待接收列表的xTaskWaitingToReceive,
(7)创建过程
a、定义队列句柄,创建队列
第一个参数为队列中包含的最大项目数,第二个参数为每个项目所需的字节数。
b、不同按键值,入不同队
c、读取小数据队列
d、读取大数据队列
FreeRTOS值得注意的点
1、中断可以打断任意任务(不管优先级有多高)
2、任务可以同等优先级(软件上数量没有限制,使用时间片调度(应该要先设置为时间片调度)策略分配时间片)
3、如果高优先级任务一直在运行会怎么样?会一直运行,使得低优先级任务无法运行。(除非高优先级任务进入阻塞状态,释放CPU控制权)
4、优先级软件上没有限制,STM32硬件上限制为0~31;
5、优先级的高低与中断相反,数字越大,优先级越高。
6、FreeRTOS中,时间片调度时,每次每个任务只能使用一个时间片,若要增加运行时间,只能是设置SysTick中断周期(一个时间片就等于SysTick(滴答定时器)中断周期)。但ucos中可以设置每次运行2个、3个时间片。。。
7、
优先级策略调度与FreeRTOS中的调度器所使用的调度算法(例如时间片轮转、抢占式调度算法)之间有密切的关系,因为优先级策略决定了任务在调度器中执行的顺序和方式。
8、FreeRTOS为了方便管理,采用组4中断优先级分组,就不必区分子优先级,只用管抢占优先级。
9、中断服务函数若要调用FreeRTOS的API函数,中断服务函数的优先级需要在FreeRTOS的管理范围内,如5~15;他同时若在中断服务中调用,必须带有“FromISR”后缀的函数
10、FreeRTOS中断管理由BASEPRI寄存器实现:BASEPRI:屏蔽优先级低于某一个阈值的中断,设置为0时,则不关闭任何中断。(FreeRTOS可管理的最大优先级(中断的))
11、通过portDISABLE_INTERRUPTS()可以关闭比BASEPRI设置的优先级高的中断(禁止所有中断),比如: BASEPRI设置为0x50,代表中断优先级在5~15内的均被屏蔽,0~4的中断优先级正常执行。portENABLE_INTERRUPTS()打开中断。
12、SVC中断只会在启动第一次任务时,调用一次,以后都不调用,后面的任务切换都是在PenSV中断实现的
13、任务切换的过程在PendSV中断服务函数内完成
14、往任务堆栈内写入的位置,其实就是一块块内存,不是寄存器,出栈的时候就出栈到对应的寄存器里面
15、任务堆栈是由高地址向下,向低地址生长的
16、系统延时函数vTaskDelay(10)会释放当前任务的CPU的使用权给其他任务,引起任务调度,把当前运行的任务挂载到阻塞列表。
用自己写的延时函数delay_ms(10)会死等,不会引发任务调度
17、堆栈大小的设置:用于存放任务切换时,需要保存的资源。
18、在调度任务中,不同优先级任务,按优先级别高低顺序来执行,即抢占式任务调度策略,高优先级需要通过延时函数vTaskDelay(10);释放自身CPU使用权,低优先级任务才能运行,同时同一优先级任务按时间片调度策略运行,每个任务只运行很短的时间1ms,最后若是两个任务相同,相当于同时完成。
19、扫描检测按键时,需要保持按下状态才能被检测到,vTaskDelay(10)时,由于时间短,按下几乎都能成功检测。但当vTaskDelay(1000),几乎需要长按1s以上才能保证每次都被检测到