FreeRtos

tips:学习韦东山老师课程中的一些顺手散记,主要看纸质笔记

FreeRtos的本质是任务的轮流运转

一、栈

任务包含:一段代码、运行位置、运行环境        》        也就是运行起来的函数(函数和它的栈)

现场:被打断的瞬间,CPU里所有寄存器的值

保护现场:把寄存器的值保存在内存里,也就是栈里;三个场景:函数调用、中断处理、任务切换

栈:保存寄存器的一些内存空间?

二、创建任务的2个核心

1.栈

创建任务时分配了栈,在栈里写入了函数地址、参数

目的:保存局部变量,保护现场

大小:局部变量数量、调用深度

2.任务结构体

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,                    //函数句柄
                        const char* const pcName,                     //函数名
                        const configSTACK_DEPTH_TYPE usStackDepth,    //栈大小,malloc分配
                        void* const pvParameters,                     //参数
                        UBaseType_t uxPriority,                       //优先级
                        TaskHandle_t* const pxCreatedTask)            //TCB(Task Control Block)结构体,任务控制块,传出的参数

三、任务调度机制(重点)

3.1 优先级和状态

1.优先级不同

高优先级的任务优先执行,可以抢占低优先级的任务

高优先级的任务不停止,低优先级的任务永远无法执行

同等优先级的任务轮流执行(时间片轮转)

空闲任务礼让:同是优先级0的其他就绪任务,空闲任务主动放弃一次运行机会(可配置项)

2.状态不同

运行running、就绪ready、阻塞blocked、暂停/挂起suspend

阻塞:想干点活,但得等待某个时间/事件来触发

挂起:摆烂,老板来催活才干

3.2 如何管理

1. 怎么取出要运行的任务?

(1)找到最高优先级的运行态、就绪态任务,运行

(2)如果大家平级,轮流执行;TCB指向的任务首先执行,然后链表前面的先运行,一定时间后去链表尾部排队

每一次都从优先级高到低的顺序挨个找;

那么,谁取出?谁放置到链表尾部?

2. 谁进行调度?

TICK中断(定时器中断)1ms。到时间后,进入TICK中断函数。首先取出下一个Task,然后切换任务。切换时,首先保存当前Task,然后恢复新Task。

3.3 状态切换

1.启动调度器的时候会创建空闲任务

xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);    //创建任务1,当前TCB指向任务1
xTaskCreate(vTask2, "Task 2", 1000, NULL, 1, NULL);    //创建任务2,当前TCB指向任务2

vTaskStartScheduler();        //启动调度器,同时创建空闲任务,优先级为0

2.使用不同的链表来维护不同状态的任务

链表容易管理不同状态的任务:一种状态一条链表

改变状态时链被移动到相应的状态链表下,这样和原来的状态不产生冲突

3.状态切换

关于优先级导致的任务和空闲任务运行顺序问题,可以看P9任务调度深入探讨20min左右

//case1:同优先级
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);    //创建任务1,当前TCB指向任务1
xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);    //创建任务2,当前TCB指向任务2
xTaskCreate(vTask3, "Task 3", 1000, NULL, 0, NULL);    //创建任务3,当前TCB指向任务3

vTaskStartScheduler();        //启动调度器,同时创建空闲任务,和上面三个任务同优先级0,当前TCB指向空闲任务
//TCB指向空闲任务,因此第一个被执行。然后按照链表顺序,先运行任务1,再运行任务2,最后是任务3

//case2:任务优先级高于空闲
xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);    //创建任务1,当前TCB指向任务1
xTaskCreate(vTask2, "Task 2", 1000, NULL, 1, NULL);    //创建任务2,当前TCB指向任务2
xTaskCreate(vTask3, "Task 3", 1000, NULL, 1, NULL);    //创建任务3,当前TCB指向任务3

vTaskStartScheduler();        //启动调度器,同时创建空闲任务,优先级0
//TCB指向任务3,因此第一个被执行,然后按照链表顺序,先运行任务1,再运行任务2。最后检索下一个优先级0,开始执行该优先级里的空闲任务

四、队列(重点)

4.1 队列的互斥访问(读/写)

如何实现互斥?

在QueueSend()函数里先关中断,再写数据,最后再开中断。在中断关闭的时间里没有别的任务会来打断。这里关的中断包括Tick。在FreeRtos内部已经实现了,只要调用函数,写数据就行

为什么关中断?

能写入一个队列的有:多个Task和中断IRQ。如果只是为了防止其他任务写入队列,关闭调度器就行,如果要防止中断写入,那么就要彻底关中断,此时调度器也没办法运行,不需要再单独关调度器了。

4.2 队列的休眠唤醒

        Tick1的时候首先轮到Task1运行,但Task1等某种条件到来才能写队列。此时Tick2的时候轮到Task2运行,Task2需要读队列才能执行。若此时队列里有Data,则返回,若此时队列里没Data,则休眠。

        等到某个Tickn的时候,Task1终于写Data进队列了,会查看哪个Task处于读队列的阻塞(休眠)状态,有的话就把那个任务(Task2)放回就绪态。

好处:休眠Task2,Task1能全速运行,Task2不占用CPU资源,程序效率更高

Task1承担的功能:1.把Data写到队列里;2.唤醒(Task2)

那么如何唤醒?唤醒谁?休眠都干什么事?

        Task1要写的队列是个结构体,有个链表Queue.list1。

Task2休眠:1. 把自己从Ready链表移动到某个Delay链表;2. 把自己记录在Queue.list1链表里

        

五、事件组和任务通知

1. 事件组只关调度器,不关中断。

不需要关中断意味着,不会在中断中使用事件组

2. 任务通知

六、链表

1.数组在内存中是连续的单元,但链表的内存可以不连续,因此可以更好地利用内存(直接截图老师的了)

初始化链表

删除某成员

加入头节点的目的是为了统一操作

实时和非实时?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值