一、FreeRTOS 简介
1.1、嵌入式操作系统
嵌入式操作系统是为了区别于通用的计算机操作系统,人们把嵌入到对象体系中,为实现对象体系智能化控制的计算机操作系统,称作嵌入式计算机操作系统,简称嵌入式操作系统。
嵌入式操作系统分类:
① 按对外部事件的响应能力来分类,嵌入式操作系统分实时操作系统和分时操作系统。
如果操作系统能及时的响应外部事件请求,并能控制所有实时设备和实时任务协调运行,且能在一个规定的时间内完成对事件的处理,那么这种系统就称为实时操作系统。
如果操作系统按时间片轮转完成各个任务,任务没有优先级,这种系统就称为分时操作系统。
② 按时间的严格程度来分,实时操作系统又分为强实时操作系统和弱实时操作系统。系统必须在极其严格的时间内完成的任务叫做硬实时操作系统,比如飞机系统;如果不是很严格的话就是弱实时操作系统,比如信息采集系统。
1.2、FreeRTOS 实时操作系统
FreeRTOS 是一款开源免费的实时操作系统,商业使用的话不需要用户公开源代码,也不存在任何版权问题,是当前小型嵌入式操作系统市场使用率最高的。
FreeRTOS 的移植比较简单,只需要用户添加需要的源码文件,不需要做任何的底层工作,再添加几个宏定义即可,详情可以看第四节的手动移植 FreeRTOS 操作系统。
1.3、为什么选择 FreeRTOS 操作系统
① 开源免费;
② 简单易学;
③ 在 RTOS 中占有率最高;
④ FreeRTOS 被移植到了很多不同的微处理器,比如 STM32,极为方便;
⑤ 许多半导体厂商产品的 SDK 采用 FreeRTOS,比如 WIFI 与蓝牙模块;
⑥ 许多软件厂商使用 FreeRTOS 作为本公司软件的操作系统,比如 TouchGFX;
1.4、FreeRTOS 特点
① 支持抢占式调度,合作式调度和时间片调度;
② 支持 35 种系统架构;
③ 设计的简单易用,典型的内核使用大小在 4k-9k;
④ 移植非常简单,代码主要用 C 编写;
⑤ 支持消息队列、二值信号量、计数信号量、递归信号量和互斥信号量、事件通知,可用于任务与任务间的消息传递和同步,任务与中断间的消息传递和同步;
⑥ 提供丰富的,配置好的工程例子;
⑦ 多个任务可以分配相同优先级,即支持时间片调度;
⑧ 高效的软件定时器;
⑨ 强大的跟踪执行函数;
⑩ 任务优先级数量不限;
⑩① 任务的数量不限;
⑩② 开源免费;
二、裸机系统与多任务系统
2.1、裸机系统
裸机编程时,先初始化相关的硬件,然后让主程序在一个死循环里面不断的循环,顺序地处理各种事件。 对于紧急事件,采用中断功能进行快速响应。
伪代码如下:
#include <头文件>
#变量定义
int main()
{
// 硬件初始化
Hardware_Init();
// 主循环
while(1)
{
// 功能函数 1
Fun_1();
// 功能函数 2
Fun_2();
。。。
// 功能函数 n
Fun_n();
}
}
// 中断函数 1
void ISR_1()
{
// 中断任务
}
。。。
// 中断函数 n
void ISR_n()
{
// 中断任务
}
建议: 对于工业与消费级产品,大部分采用裸机系统。采用硬件家园主推的面向对象编程思维和模块化编程技术,规范程序架构,采用结构体封装外设,程序方便阅读、修改与移植,提高开发效率的同时,降低维护程序的难度。
2.2、多任务系统
相比于裸机系统,多任务系统的事件处理是在任务中完成的,对于紧急事件,同样采用中断功能进行快速响应。
多任务系统通常包含多个任务,彼此独立,方便编程。 同时,任务具有优先级,高优先级任务可以打断低优先级任务,系统的实时性进一步得到提高。
**伪代码如下:**
#include <头文件>
#变量定义
int main()
{
// 硬件初始化
Hardware_Init();
//FreeRTOS 初始化
FreeRTOS_Init();
//FreeRTOS 启动,开始任务调度,不再返回
FreeRTOS_Start();
}
// 任务函数 1
void Task_1()
{
// 无限循环,不能返回
for(;;)
{
// 任务 1 处理
}
}
。。。
// 任务函数 n
void Task_n()
{
// 无限循环,不能返回
for(;;)
{
// 任务 n 处理
}
}
// 中断函数 1
void ISR_1()
{
// 中断任务
}
。。。
// 中断函数 n
void ISR_n()
{
// 中断任务
}
说明:
引入操作系统后,在编程时不需要精心设计程序执行流,任务间不存在干扰,编程反而变得简单了。
当然,系统需要占用一定的 RAM 与 FLASH 资源,如今的单片机,RAM 与 FLASH 越来越大,为采用操作系统编程提供了便利。
2.3、裸机系统与多任务系统对比
裸机系统与多任务系统各自具有优势,在各自的领域都具有应用价值,不能简单的说孰优孰劣,掌握后,根据产品功能合理选择。
裸机系统:工业产品与消费级用的非常多,特别是低端的单片机,必须采用裸机系统,采用模块化技术,编程也可以很方便 。
多任务系统:适合单片机高端,功能复杂的产品,多任务管理,编程简单很多。
两者对比:
三、手动移植 FreeRTOS
第一步: 官网下载或 qq 群下载 FreeRTOS 源码
官网网址:https://www.freertos.org
qq 群获取:
第二步: 准备基础工程 - 使用 HAL05 - LED 流水灯,延时时钟基准由 SysTick 改到 TIM2,删除主循环内的代码
原因: FreeRTOS 操作系统使用 SysTick 作为基准时钟
第三步: 将 FreeROS 源码添加至工程中,设置头文件路径,编译工程
① 将 FreeRTOS 源码复制到工程中, 另外,将 portable 与 RVDS 文件夹多余的文件删除
② Keil 工程新建 2 个 Groups - FreeRTOS/Source 与 FreeRTOS/Port,同时添加相应的文件
③ 添加头文件路径
④ 编译工程
第四步: 将 FreeRTOSConfig.h 文件 (位于 Demo - CORTEX_STM32F103_Keil 工程中) 添加至工程的 MyApplication 文件内, 方便打开与修改,同时添加至 MyApplication 文件组内,再次编译
第五步: 修改 FreeRTOSConfig.h 文件,是 stm32f1xx_it.c 文件,编译 ok
① 修改 FreeRTOSConfig.h 文件,并编译
② 注释掉 stm32f1xx_it.c 文件的 SVC,PendSV,Systick 中断函数,编译 ok
第六步: 工程添加 MyTask.c 文件,里面预先写好了 3 个任务,同时 MyApplicatin.h 头文件添加 FreeRTOS 与 MyTask 的头文件,编译 ok
第七步: 在 main.c 文件调用任务创建函数与系统调度函数,编译 ok。
第八步: 将程序下载至 STM32 物联网实战板,正常运行。
LED1 - 间隔 100ms 闪烁
LED2 - 间隔 500ms 闪烁
LED3 - 间隔 1000ms 闪烁
至此,手动移植完成。
四、STM32CubeMX 生成 FreeRTOS
第一步: 官网下载或 qq 群下载 FreeRTOS 源码
官网网址:https://www.freertos.org
qq 群获取:
第二步: 准备基础工程 - 使用 HAL05 - LED 流水灯,延时时钟基准由 SysTick 改到 TIM2
原因: FreeRTOS 操作系统使用 SysTick 作为基准时钟
第三步: 左侧选择 Middleware 选项卡,点击 FERRRTOS,Interface 选择 CMSIS_V1,配置参数默认即可。
说明:
Disable - 禁用 FreeRTOS
CMSIS_V1 - CMSIS-RTOS 版本为 V1.02,一般选择这个
CMSIS_V2 - CMSIS-RTOS 版本为 V2.00,兼容更多的 CPU 架构,但代码量更大
第四步: 选择 Tasks and Queue,修改默认任务 defaultTask(双击进入编辑任务界面),并额外添加 LED2 与 LED3 任务。
说明:
编辑任务时,软件要求不同位置不能使用同样的名称,因为任务名称设定为 LED1_Task
, 任务函数设定为 LED1
第四步: 保存工程,并生产代码,删除主循环内的代码,编译 keil 工程
第五步: 打开 freertos.c 文件,在 LED1、LED2、LED3 函数内添加代码,编译 ok
第六步: 将程序下载至 STM32 物联网实战板,正常运行。
LED1 - 间隔 100ms 闪烁
LED2 - 间隔 500ms 闪烁
LED3 - 间隔 1000ms 闪烁