浅析Freertos内核
前言
作为一名学习时间一年左右的大学生,本文肯定对rtos的解释不如一些老师和一些博主,对一些解释可能不是很科学,希望各位读者大佬能够指正,本文是以记录学习过程为出发点,将自己学习过程中的理解,通过更通俗的解释分享出来,让读者初步的了解rots内核。
一、Freertos最最最最核心在哪?
通过这段时间的学习,以及自己实现的rtos内核来看,Freertos实时操作的核心在于任务调度,就是其中的sp指针的切换入栈以及上下文的切换
那么本文就以三个部分来讲解rtos的核心、分别是创建任务、任务调度、任务切换。其中更是以任务调度为重,剩下的进入临界区、支持优先级、时间片需要根据读者的反馈进行文章更新读者也可以读完本文之后自己涉及。
首先知道,Freertos和裸机的最大区别是什么?如果我们使用裸机开发,是不是轮询查找?(在主函数中的死循环里按照顺序依次执行)
充其量在主函数中添加标志位,让其在中断进行处理。这固然是一种响应和处理分开的方法。但是今天我们来讲解一下实时操作系统、主打的一个实时、我们在中断进行响应,处理在任务中执行。那么引入一个知识点、什么是任务? 所谓任务就是一个个在主函数中的死循环,根据任务的优先级来处理优先级最高的任务,再通过任务切换去另外一个任务.
此时大家脑海里有没有思路了?
在江湖中,强者为尊实力为大、每天都是在厮杀,但是大家都规定好了,谁是大哥谁先享受、如果期间谁成为新的大哥,那么新的大哥才有资格享受资源
这里面的“大家”就是任务、资源就是“CPU的使用”、大哥就是“优先级高的任务”
1、创建任务
创建任务可以分为动态创建、静态创建。本文以最简单的静态创建为例、读者可以阅读完本文之后去看看rtos动态创建的源码比较一下区别。
1、任务栈?
首先问大家一个问题、如果使用裸机开发,我们定义的全局变量、函数中的局部变量都放在哪里?是不是在栈中?栈就是单片机RAM里面一段连续的内存空间,大小一般在启动文件里面或者连接脚本里面定义,通过外在的调用初始化。
那么和任务栈有什么关系?在江湖中、各路豪杰英雌距地分割。每个人都有自己的地盘。那我内部的吃喝开销不是我自己负责?
同样,栈也是一个个独立的,每个栈都有自己的独立栈空间,只是这个栈空间需要提前定义好,就是一个全局数组
我们此时不管函数的参数(其实就是宏定义uint32_t 类型)
2、任务函数
其实在上面的时候大家都对任务函数脑中有了初步的构想,就是一个死循环函数
3、任务也有身份证?
你没听错,人在江湖混,别人怎么知道你叫啥籍贯哪里,那肯定需要一个身份证来证明这个是我。同乡的出去也能说我和英雄一个村从小玩到大的 (说过头了)、那么任务的身份证叫什么呢?TCB
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /* 栈顶 */
ListItem_t xStateListItem; /* 任务节点 */
StackType_t *pxStack; /* 任务栈起始地址 */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名称,字符串形式 */
TickType_t xTicksToDelay; /* 用于延时 */
UBaseType_t uxPriority;
}tskTCB;
typedef tskTCB TCB_t;
可以看到TCB就是结构体、里面包含了栈顶指针、任务节点(其实就是将该节点放入链表中,从而实现任务挂到链表上,因为TCB就是任务的身份证,挂TCB就是挂任务)、任务栈起始地址、任务名字~~、延时、优先级~~ (暂且不谈)
4、创建任务的函数
不是哥们,我看了这么久你就给我上理论啊?能不能来点实际的操作啊?
可以的读者,量大管饱,我们上干活,看看任务创建的内部是怎么实现
1、xTaskCreateStatic
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
TCB_t * const pxTaskBuffer )
{
TCB_t *pxNewTCB; //创建tcb、创建身份证
TaskHandle_t xReturn; //创建函数返回值
if ( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
{
pxNewTCB = ( TCB_t *</