上节我们简单了解了FreeRTOS相关理论,现在我们开始正式的实操
我们先介绍如何在CubeMX里面创建FreeRTOS工程:
1.、在 SYS 选项里,将 Debug 设为 Serial Wire ,并且将 Timebase Source 设为 TIM4 (其它定时器也行)。
Timebase Source 为什么不能设置为 SysTick ? 裸机的时钟源默认是 SysTick,但是开启 FreeRTOS 后,FreeRTOS会占用 SysTick (用来生成1ms 定时,用于任务调度),所以需要为其他总线提供另外的时钟源。
2、将RCC里的HSE设置为Crystal/Ceramic Resonator (晶振源)
3.、选择 FREERTOS 选项,并将 Interface 改为 CMSIS_V2 。
FreeRTOS 各配置选项卡的解释:
Events:事件相关的创建
Task and Queues: 任务与队列的创建
Timers and Semaphores: 定时器和信号量的创建
Mutexes: 互斥量的创建
FreeRTOS Heap Usage: 用于查看堆使用情况
Config parameters: 内核参数设置,用户根据自己的实际应用来裁剪定制FreeRTOS 内核
Include parameters: FreeRTOS 部分函数的使能
User Constants: 相关宏的定义,可以自建一些常量在工程中使用
Advanced settings:高级设置
接着我们就可以开始创建第一个多任务程序:
在上节我们了解到了任务其实就是在内存里分配一块堆,然后在堆里面分配栈(即创建任务)
任务通常都含有while(1)死循环:
在FreeRTOS里面,任务的原型如下:
voidStartDefaultTask(void*argument){while(1){//代码逻辑}}下面示例如何创建任务:
voidMyTask(void*argument){while(1){Led_Test();}}xTaskCreate(MyTask,"MyFirstTask",128,NULL,osPriorityNormal,NULL);上面的我们从理论那节都已经有很清晰的了解了吧
ARM架构:
从上图我们可以看到CPU在对内存进行一些程序操作,那那些操作的变量保存在CPU哪里呢?我们深入CPU内部看看:
可以看到CPU在对内存进行读写操作时,会涉及到四条汇编指令(就是我们的程序,这些汇编指令保存在Flash上),CPU从Flash得到指令,然后去对内存执行,可以看到CPU上有16个寄存器,这些寄存器就是暂时保留对内存读写操作过程的值,那这些寄存器只有暂存数据的作用吗?往下看
可以看到R13(保存栈地址),R14,R15(写入到要跳转的地址,跳转后执行程序)还有其它的作用,而R14我们之前介绍过
汇编指令:
下面是通过程序来了解汇编指令在程序的运行
机器码就是就是对应的汇编指令,如下图:
MOV 是指将右边值赋给左边
注意这里PUSH是指将1r,r1,r0寄存器该栈的值(内存)写入到寄存器中(CPU),所以1r寄存器的值是返回地址。
这里的LDRD是指从栈的sp开始的第4个字节和第8个字节的位置内存值分别写入到r0和r1寄存器中(CPU)
FreeRTOS源码:
可以看到,我们选择配置FreeRTOS并生成文件时,会有上图中关于FreeRTOS的源代码
这是入口函数:
其中MX_FREERTOS_Init()函数里面就包含了我们在Task.c里面的任务函数
数据类型:
内存管理:
这是堆相关的配置:
第一个是选择动态分配/静态分配
第二个是配置堆的大小
第三个是选择堆的配置文件
这四种配置文件区别如下:
平时我们用到的配置文件都是4和5,下面我来说说它们的区别:
如上图:
文件1只从内存中分配堆,却不能释放,也没有相关的头部来存储堆的信息
文件2能从内存中分配堆和头部,不过假设这块内存已经满了,而且我把buf1和buf2都释放了,然后我要重新分配一块120字节大小的堆,会发现分配不了,因为buf为100字节大小,buf为50字节大小,所以这120字节大小都分配不进去,所以这就是文件2的缺点,不能解决碎片问题(即不能将这两块堆合并)
文件4就是在文件二的基础上解决了碎片问题
文件5
可以看到除了能在RAM剩余内存中可以分配堆,在EXT_RAM中也能分配堆,所以文件5就能管理这两块堆,通过链表,空闲链表头指向第一个堆,再指向第二个堆,我们需要告诉链表头有多少块离散的堆
函数如下:
不过在程序中我们主要使用这两个函数就行了:
//分配一块内存void*pvPortMaalloc(size_txWantedSize)//释放一块内存voidvPortFree(void*pv)