FreeRTOS的学习系列文章目录
FreeRTOS的学习(一)——STM32上的移植问题
FreeRTOS的学习(二)——任务优先级问题
FreeRTOS的学习(三)——中断机制
FreeRTOS的学习(四)——列表
FreeRTOS的学习(五)——系统延时
FreeRTOS的学习(六)——系统时钟
FreeRTOS的学习(七)——1.队列概念
FreeRTOS的学习(七)——2.队列入队源码分析
FreeRTOS的学习(七)——3.队列出队源码分析
FreeRTOS的学习(八)——1.二值信号量
FreeRTOS的学习(八)——2.计数型信号量
FreeRTOS的学习(八)——3.优先级翻转问题
FreeRTOS的学习(八)——4.互斥信号量
FreeRTOS的学习(九)——软件定时器
FreeRTOS的学习(十)——事件标志组
FreeRTOS的学习(十一)——任务通知
1 FreeRTOS
最近完成了论文方面的任务,要逐步开始迈向技术学习的新阶段了,接下来准备学习一下操作系统。以前没有对这东西多感冒,因为自己基于arm写的代码都是在中断里的,基本上两个中断基本就解决了项目所需。但是随着产品化的想法逐渐深入,对于目标的实现也不仅仅是完成目标本身了,需要加入更多的可视界面以及交互功能。那么如何在多个功能中能够进行可靠的任务调度切换呢,RTOS就逐步进入了我的视野。
选择FreeRTOS的来优先学习的原因也很简单,目前其市场占有率很高,其次是相对内容比较简单,没有复杂的东西。今天花了点时间把移植的过程过了一遍。
2 资料下载
这个东西简单,百度搜索官网,直接右上角点击下载即可,另外值得推荐的就是正点原子的FreeRTOS的学习资料,他家的开发手册看着还是挺好的,另外还有他家的例程也是值得参考的。不过我学东西喜欢下载最新版,所以优先使用的FreeRTOS的版本是V10.4.6 。
3 移植开始
移植的主要文件夹是FreeRTOSv202112.00\FreeRTOS\Source文件,其中该文件下的C文件加入到工程中的FreeRTOS_CORE(自己新建的文件),另外需要添加portable文件夹,原子说这是RTOS系统于具体编译环境和MCU的连接桥梁,我觉得这个比喻还可以,因为这个文件夹里包括要选择的编译器(Keil、IAR等)以及内存管理(MemMang)、架构的选择(RVDS)。比如我使用的是F103,Keil环境下,所以选择arm3的架构,以及Keil文件,具体见下图。
接下来,需要将内存管理文件夹下的某个内存管理源文件heap(随便选一个,简单应用不需要考虑)以及RVDS下的具体架构文件下的port.c文件导入到新建的FreeRTOS_PORTABLE文件夹。导入成功后如下:
另外需要注意添加头文件路径,不然编译找不到的。
4 开始编译
第一次编译会报错找不到FreeRTOSConfig.h,这个文件官方给的source是没有的,其实是根据开发者的需求自己加的,也就是开启和设置某些系统的功能。为了方便管理配置,我们自然是要搞一个的,前面官网下载下来的Demo里是有这个头文件的,但是并不好用,我在加入之后发现还是报了错,例如未定义xTaskGetCurrentTaskHandle,这是因为FreeRTOS.h中并未开启该功能,我们可以在FreeRTOS.h直接开启,给1就行,如下。
#define INCLUDE_xTaskGetCurrentTaskHandle 1
但是这样一个个改显然有点不舒服,这时候FreeRTOSConfig.h的作用就出现了,点开他的文件可以发现官方头文件里已经开启了一部分,我们可以根据自己的需求进行设置。但是对于目前小白的我来说,管理这个整体还有点困难,所以我选择直接导入了原子的FreeRTOSConfig.h,它里面管理的就比较漂亮了,还有中文注释,分的很清楚。
5 出现的问题
值得说明的是32的系统文件也需要修改,具体包括delay.c,sys.c,uarst.c。具体的过程可以忽略,直接复制原子的也行,不过如果对于systick不是很清楚的可以好好查看下。
搞完这些编译会出现一个奇怪的问题,如下。
…\FreeRTOS\queue.c(2762): error: #268: declaration may not appear after executable statement in block
双击后跳到了这句话,在FreeRTOS_CORE文件下的queue.c内的
QueueRegistryItem_t * pxEntryToWrite = NULL
说实话 第一眼看到我就懵了一下,这句话按照C的编译来看确实不起作用,但是官方写在里面我能随便改吗?显然不行啊,到时候用到这里出问题了怎么整???
好在后来找到了解决办法,在Keil的魔术棒中C/C++选项卡下选择C99mode,该模式下能通过以下省略参数的语句,因此编译至此通过,移植也就到此结束了。
具体的原理期待下次博客讲解。
总结
写在最后的小Tips,对于FreeRTOS的任务函数中,具体如led0_task(),其内部定义一个字符变量char i = 0;(局部变量一般都要赋值初始值,不然是不确定的。)对于裸机操作来说,函数中定义一个局部变量,该局部变量的内存的生存期只有在该函数被调用到该函数结束调用(当然,像主函数这种的局部变量值可以一直生存,因为主函数会一直运行),生存周期结束后,该变量的内存就会被释放,下次进入函数调用时会被重新写入。然而,FreeRTOS的任务函数中对于局部变量的使用会存在一点区别,因为每个任务函数都存在自己的堆栈,所以定义的局部变量应该也会一直放在任务函数的堆栈中而不会被释放,所以任务函数定义的局部变量可以认为是自带static功能,即函数不运行时也会一直存在(实质上可能进入了阻塞态或者挂起态,任务函数也是一直生存的)。