单片机入门之FreeRTOS(超基础,不复杂)
本文是作者自己的一段学习感悟,主要是一些基础认知,适用于小白中的小白。主要是进行一个总体的介绍,让各位初步认识它,知道它,不会针对某个点大讲特讲,也做不到面面俱到的细致讲解,大佬勿喷。
什么是操作系统(偏向单片机)
单片机的操作系统一般是实时操作系统。主要负责管理单片机的硬件资源,调度任务,确保实时性和响应速度。它让你可以同时运行多个任务,比如监测传感器数据和控制设备,同时确保这些任务按优先级和时间要求顺利完成。
操作系统介于硬件和软件之间,将二者联系起来,确保其协通工作。通俗一点将就是操作系统可以将你写的代码也就是c语言等开发语言转换成汇编语言(机器语言,01010100……),从而实现软件控制硬件。
单片机为什么要使用操作系统
想必开始学习FreeRTOS的同学应该或多或少都有写过一个完整的代码了,比如成功让一颗LED亮起来。现在我们来谈一下为什么要用操作系统。
众所皆知,单片机是单核的,不知道单核是什么意思没关系,只要知道单片机是按顺序执行代码的就好了按顺序运行也就是只能同时执行一个任务。当你做一个大项目时,任务多起来了,你想要同时运行多个任务,但由于单片机是单核的,它只能按顺序一个任务一个任务的去执行,此时你的最佳解法之一便是引入操作系统。
为什么我们要用FreeRTOS
除了FreeRTOS以外,还有其他的操作系统可以选择。
FreeRTOS是一个免费的操作系统。
FreeRTOS具有多线程,线程交互(4种交互方式)的特点。
网上关于FreeRTOS的教程众多,代码历程也丰富,比较好上手。
FreeRTOS是怎么实现多任务“同时”执行
本质:FreeRTOS的内置算法让cpu在不同任务之间快速切换执行。
单片机依然是按顺序执行的,只不过它在不同的任务重反复来回执行。就像大脑的“视觉暂留”,当帧率达到一定值时,人眼无法快速分辨图片,于是静态的图片就变成了一系列连续的动态视频。只要在任务之间切换的够快,那么所有的任务就可以看做是“同时”执行了。
有些人可能会问,一个任务怎么可能那么快的执行完。确实,不太可能,就拿我们都熟悉的延时来说,如果一个任务里出现延时600ms,在有FreeRTOS的调度下,单片机不会像往常一样在这个任务硬生生等待600ms,而会去执行一下一个任务,这个600msFreeRTOS会挂在后台执行,等到600ms时间一到,就把cpu再拉回来继续执行任务剩下来的部分。
怎么使用FreeRTOS
第一步:移植相关代码(库)
首先要把操作系统那些调用内核程序的代码,对应的头文件之类的移植过来,然后再改一下中断以及系统时钟的部分。本来想写一篇移植地文章,但相关移植文章已经有很多了,并且都很详细,笔者这边就不再赘述了。
第二步:编写任务文件
笔者的习惯是将所有任务单独再开一个文件去写,而不是放在main文件里写,这样会更加有条理一点(maybe)
操作系统中一般一个任务都是死循环,而且没有返回值,任务的参数我们也很少使用,可以不用管他。
void Your_Task1(void *Parameters)
{
while(1)
{
//代码逻辑功能
//babababa....
}
}
void Your_Task2(void *Parameters)
{
while(1)
{
//代码逻辑功能
//babababa....
}
}
第三步:编写main文件
初始化定义:
“栈”,RAM中的一段连续空间。
因为多任务系统中,每个任务都是独立的,所有我们要为每个任务都分配独立的栈空间。通常是一段预定义的全局数组,也可以是动态的分配的一段内存空间。无论静态还是动态,都存在于RAM之中。
///*******TASK1任务的宏定义*********/
#define TASK1_TASK_PRIO 2 //任务优先级
#define TASK1_STK_SIZE 50 //任务堆栈大小
///*******TASK2任务的宏定义*********/
#define TASK2_TASK_PRIO 2 //任务优先级
#define TASK2_STK_SIZE 50 //任务堆栈大小
操作系统中一般一个任务都是死循环
任务初始化
//任务初始化
void Task_Init(void)
{
//创建开始任务
xTaskCreate((TaskFunction_t )Your_Task1, //任务函数
(const char* )"Your_Task1", //任务名称
(uint16_t )TASK1_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )TASK1_TASK_PRIO, //任务优先级
(TaskHandle_t* )NULL); //任务句柄
xTaskCreate((TaskFunction_t )Your_Task2, //任务函数
(const char* )"Your_Task2", //任务名称
(uint16_t )TASK2_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )TASK2_TASK_PRIO, //任务优先级
(TaskHandle_t* )NULL); //任务句柄
}
整理
#include "main.h"
///*******TASK1任务的宏定义*********/
#define TASK1_TASK_PRIO 2 //任务优先级
#define TASK1_STK_SIZE 50 //任务堆栈大小
///*******TASK2任务的宏定义*********/
#define TASK2_TASK_PRIO 2 //任务优先级
#define TASK2_STK_SIZE 50 //任务堆栈大小
void Task_Init(void);
int main(void)
{
Initialize_Init(); //底层初始化
Task_Init(); //任务初始化
vTaskStartScheduler(); //开始任务调度---在task文件里可以找到,是官方已经写好的
while(1);
}
//任务初始化
void Task_Init(void)
{
//创建开始任务
xTaskCreate((TaskFunction_t )Your_Task1, //任务函数
(const char* )"Your_Task1", //任务名称
(uint16_t )TASK1_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )TASK1_TASK_PRIO, //任务优先级
(TaskHandle_t* )NULL); //任务句柄
xTaskCreate((TaskFunction_t )Your_Task2, //任务函数
(const char* )"Your_Task2", //任务名称
(uint16_t )TASK2_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )TASK2_TASK_PRIO, //任务优先级
(TaskHandle_t* )NULL); //任务句柄
}
作者:宇宙无敌大帅佳