• 基于STM32的实时操作系统FreeRTOS移植教程(手动移植)


            前言:此文为笔者FreeRTOS专栏下的第一篇基础性教学文章,其主要目的为:帮助读者朋友快速搭建出属于自己的公版FreeRTOS系统,实现后续在实时操作系统FreeRTOS上的开发与运用。操作系统的学习与运用可以说是每位嵌入式开发工程师必须掌握和熟悉的技能,对于MCU新手来说,将FreeRTOS作为第一个入手学习的操作系统可以说是非常合适的。本文将手把手对FreeRTOS实时操作系统的移植进行教学,分别包含:手动移植(本文为手动移植)CubeMX快速生成。(文末有代码开源!

    一、FreeRTOS的简述

    1.1 RTOS简介

            在提到FreeRTOS之前就不得不提到它的统称——RTOS实时操作系统Real Time Operating System,简称RTOS)是指当外界事件数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统做出快速响应,调度一切可利用的资源完成实时任务,并控制所有实时任务协调一致运行的操作系统。

    注意:细心的读者朋友可以看出,读者反复标红了快速规定时间快速响应实时等词语。这就是RTOS系统一个非常显著的特点,相较之下Linux操作系统可能在实时性上会差点。如果,以后读者朋友搞工程项目的时候一定要注意这点。

    这里作者总结一下,实时操作系统RTOS提供的主要功能有:(1)应用程序的调度管理(2)堆栈和内存管理(3)文件管理(4)队列管理(5)中断和定时器管理(6)资源管理(7)输入输出管理

            RTOS只是一个统称,他可以分为各种各样的版本以及平台,由于RTOS需占用一定的系统资源(尤其是RAM资源,所以熟练掌握RTOS之后,工程师需要根据需求剪裁操作系统的大小和功能。),只有μC/OS-IIembOSsalvoFreeRTOS等少数能在小RAM单片机上运行。相对μC/OS-IIembOS等商业操作系统,FreeRTOS操作系统完全免费的操作系统,具有源码公开、可移植、可裁减、调度策略灵活的特点,可以方便地移植到各种单片机上运行。

            接下来,我们就来学习今天的主角——FreeRTOS

    1.2 FreeRTOS简介

            FreeRTOS是一个迷你的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录功能、软件定时器、协程等,可基本满足较小系统的需要。

    其功能特点如下:
            (1)用户可配置内核功能( 可裁剪 )
            (2)多平台的支持
            (3)提供一个高层次的信任代码的完整性
            (4)目标代码小,简单易用
            (5)遵循MISRA-C 标准的编程规范
            (6)强大的执行跟踪功能
            (7)堆栈溢出检测
            (8)没有限制的任务数量
            (9)没有限制的任务优先级
            (10)多个任务可以分配相同的优先权队列,二进制信号量,计数信号灯和递归通信和同步的任务
            (11)优先级继承
            (12) 免费开源的源代码

            当然,由于开源免费使用的缘故。FreeRTOS整个社区生态很好,使用的人数众多,许多使用过程中出现的问题网上都可以轻松找到解决办法。

    1.3 多任务操作系统

            说到多任务操作系统,我们就不得不提到我们的裸机开发,裸机开发的话,它并不是相对于多任务操作系统就很弱,他们俩在各自的领域有各自的使用场景,也有各自的优缺点(如果只有一两个任务用裸机比RTOS好很多)。

            比如说我们直接进行裸机开发,它主要是在我们的一个while循环中进行所有的项目操作,有些情况下,会有一个或者多个中断来处理一些突然发生的事或者已经设计好的事情,如果我们仅仅是开发一些小项目或者功能比较单一的项目,裸机是最可靠最有效而且很方便开发的一种模式,但是当我们的项目功能越来越多,我们就要引入多任务的操作系统,多任务操作系统顾名思义就是可以处理很多个任务,就像我们的手机一样,我们的手机后台也是可以挂起多个应用的。

            在我们引入操作系统之后,我们就不需要去精心的设计我们的时序流程,因为各个任务之间是不存在相互干扰的,我们只需要使用我们的命令去开启或者关闭某一些任务就可以了(这一点与Linux系统下实现任务一致,所以学习FreeRTOS对于后续Linux系统板的学习是很有意义的),当然这肯定是需要占用一些系统资源的,不过现在的单片机的内存都是足够大的,所以我们一般情况下是不需要担心我们的操作系统内核,而是直接移植进行使用,并且我们的多任务操作系统的思维,就很像我们人去思考的思维更加的方便,而我们的裸机开发更像是机械思维,在开发过程中以及开发流程中操作系统是非常方便使用以及开发的。

            说了这么多,为了帮助大家进一步理解实际的多任务操作系统,这里简单举一个例子。这里就以2个LED点亮操作为例,向大家展现出裸机RTOS之间的区别:

    任务要求:这是只有一个LED的情况如果有2个LED,一个需要500ms闪烁,一个需要1s闪烁一次。

    直接意识代码:

    1. int main()
    2. {
    3. while(1)
    4. {
    5. //LED1置为高电平
    6. ......
    7. //延时1000MS
    8. ......
    9. //LED1置为低电平
    10. ......
    11. //延时1000MS
    12. ......
    13. //LED2置为高电平
    14. ......
    15. //延时500MS
    16. ......
    17. //LED2置为低电平
    18. ......
    19. //延时500MS
    20. ......
    21. }
    22. }

            这是我们大脑最希望的添加代码方式,很显然他是错的两个任务之间产生了相互的影响,使得两个任务都执行错误,这种思想在裸机开发中肯定是错的,但是在我们的RTOS中他就可以是对的

    任务型代码:

    1. //创建LED1任务
    2. void LED1_Task()
    3. {
    4. //LED1置为高电平
    5. ......
    6. //延时1000MS
    7. ......
    8. //LED1置为低电平
    9. ......
    10. //延时1000MS
    11. ......
    12. }
    13. //创建LED2任务
    14. void LED2_Task()
    15. {
    16. //LED2置为高电平
    17. ......
    18. //延时500MS
    19. ......
    20. //LED2置为低电平
    21. ......
    22. //延时500MS
    23. ......
    24. }

            这是独立的两个任务内容,我们只需要把他的扔到我们的任务执行器里,他就会“同时”运行了,很多小伙伴就会疑问了,单片机明明只有一个核,为什么可以同时执行多个任务呢?这就需要去了解RTOS操作系统中任务“同时”执行的原理

    1.4 任务“同时”执行的原理

            在RTOS中,RTOS利用了一种类似于“视觉暂留”的工作原理,多个任务之间快速切换。在ROTS中,可以让我们的每个任务执行一个时间单位,然后就切换到另外一个任务执行一个时间单位,再切换回去,两个任务都是独立运行的,互不影响,由于切换的频率很快就感觉像是同时运行的一样

            上图为一个简单的示意图,读者朋友们可以结合该图好好理解一下RTOS系统的执行过程。

    二、 FreeRTOS的手动移植

            为了图方便,笔者这里直接使用CubeMX去生成一个基于HAL库的初始STM32工程文件。不习惯用HAL库的,可以找一个标准库的初始STM32工程文件,进行手动移植工作。

    ①、建立STM32空工程

            这里使用STM32CubeMX快速创建项目,要注意的是我们完成最基本的配置以后,需要将我们的Timebase Source修改一下,修改成除了滴答定时器的其他定时器,如下图:

            这里我使用的TIM2作为Timebase Source

    为什么不可以使用滴答定时器呢?

            在FreeRTOS中我们的SysTick定时器被用于了我们的始终基准,它用来实现我们的任务切换,我们的SysTick定时器每次触发我们的中断默认是一毫秒,可以自行修改为其他值

    Timebase Source是干嘛的呢?

            简单的说,正常裸机开发中我们的SysTick定时器是用来主要是用来进行我们的HAL_Delay()延时的,使用其作为基准,前面我们说了SysTick定时器被用于系统任务切换了,所以它就是一直在工作,或者说一直在触发中断,这样的话我们的HAL_Delay()就需用使用我们的其他定时器(TIM2)进行替代其作用了

    ②、FreeRTOS内核下载 

            这一步将会教大家如何去下载FreeRTOS内核以及如何进行移植到我们的项目里面。

            首先,登入FreeRTOS的官网,地址:FreeRTOS - Market leading RTOS (Real Time Operating System) for embedded systems with Internet of Things extensions

            点击右上角的Download FreeRTOS;  

            来到版本选择界面

            上面的是FreeRTOS的最新版本,下面的就是长期维护的版本

            我们直接点击上面这个Github超链接

            然后就依次带点击CodeDownload ZIP

            但是,这时候下载完成的压缩波其实并不包含FreeRTOS的子模块。所以,在该页面向下翻找。

            找到Kernel source(内核资源) 这里,点击箭头处的超链接;

            按照上方步骤下载压缩包,之后进行解压,最终成功得到2个文件夹;

            其中,FreeRTOS-Kernel-mainFreeRTOS的内核文件,FreeRTOS-main文件夹下的内容则比较繁杂,我们主要看里面Demo文件

            Demo文件夹里面的文件非常多,不过我们可以找到这样一个文件夹CORTEX_STM32F103_Keil

            CORTEX_STM32F103_Keil文件夹为官方将系统已经移植好的实例,但是这并不是我们直接移植的,感兴趣的小伙伴可以先打看官方移植好的能不能看懂。

    ③、手动移植FreeRTOS内核文件 

    1、打开第一步建立的新项目 

    2、左侧项目文件添加两个新的分组

    3、建立对应的FreeRTOS文件夹

    4、打开FreeRTOS-Kernel文件夹 

    5、主目录下所有的.C后缀的文件全部复制我们的FreeRTOS文件夹下(一共七个)

    6、复制include文件夹以及portable文件夹到FreeRTOS文件夹下

    7、保留FreeRTOS/portable文件夹中的Keil、MemMang、RVDS文件夹,其余全部删除

    8、保留FreeRTOS/portable/RVDS文件夹中的ARM_CM3文件夹

    9、然后如图进行Keil项目的分组文件配置

    10、添加对应的头文件

    11、直接编译(出现9个错误)

     ④、项目问题修复

    缺少FreeRTOSConfig.h文件(FreeRTOS的配置文件)

    示例项目中复制粘贴并且添加到项目文件夹中(记得添加头文件路径) 

    编译无错误  

    ⑤、项目添加处理

    虽然没有错误了,但是我们的移植没有完成,还有一些小步骤需要完成,的确有些繁琐,好在逻辑性还是比较强的,理解起来相对容易。

    在FreeRTOSConfig.h中添加#define xPortPendSVHandler PendSV_Handler 

    在FreeRTOSConfig.h中添加#define xPortSysTickHandler SysTick_Handler

    在FreeRTOSConfig.h中添加#define vPortSVCHandler SVC_Handler

    左边三个是我们Free RTOS中定义好的函数,右边的是系统项目本来就定义好的函数,他们的作用小伙伴们可以去了解一下,作为初学者这里不进行拓展,反正就是和我们操作系统的中断还有任务切换有关的函数 

    编译,还是报错,重复定义 

    处理错误: 进入对应的文件stm32f1xx_it.c删除重复的3个函数

    OK移植成功了!!! 

    ⑥、FreeRTOS点灯项目创建

    相关的头文件引入:用到的头文件有“FreeRTOS.h”(操作系统相关)"task.h"(任务相关)

    任务函数创建:

    1. //任务1
    2. void vTask1(void *pvParameters)
    3. {
    4. for(;;)
    5. {
    6. HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,0);
    7. HAL_Delay(1000);
    8. HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,1);
    9. HAL_Delay(1000);
    10. }
    11. }
    12. //任务2
    13. void vTask2(void *pvParameters)
    14. {
    15. for(;;)
    16. {
    17. HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,0);
    18. HAL_Delay(500);
    19. HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,1);
    20. HAL_Delay(500);
    21. }
    22. }

     主函数创建任务,以及开启任务调度器

    ⑦、程序烧录到开发板查看效果

            笔者使用的是正点原子的精英板,可以发现LED0和LED1分别按照既定任务同时运行中。各位读者朋友可以按照自己的实际情况去试试看FreeRTOS的效果。

    总结

            本文为手动移植FreeRTOS的教程,目的仅为快速完成FreeRTOS的移植,并不过多深入去了解FreeRTOS。笔者认为只有先学会移植FreeRTOS,之后再去慢慢了解和使用FreeRTOS。通过这种学习方式,可能更方便大家去快速接受和体会操作系统的运行原理和流程。

    代码

    链接:https://pan.baidu.com/s/13a6uHqVXE5aVZg8PLEyx0g 提取码:8qdu

  • 相关阅读:
    django路由匹配、反向解析、无名有名反向解析、路由分发、名称空间
    week10|查阅文章
    商家收款码费率是什么意思
    V8引擎和JS垃圾回收机制的详解
    混合劳动力情况下需要的 3 种移动工具
    通过源码来理解Cglib与JDK动态代理
    基于word2vec 和 fast-pytorch-kmeans 的文本聚类实现,利用GPU加速提高聚类速度
    SpringCloud微服务(三)——Zookeeper服务注册中心
    Apache HTTP服务器配置syslog
    typescript新规范及vue3常用的属性解析【2024】
  • 原文地址:https://blog.csdn.net/black_sneak/article/details/126599882