FreeRTOS 入门与实践 —— 入门(2)
对于以上乘法运算:a = a*b;还有问题,CPU内部怎么保存输入输出的数据?
1,ARM 架构
(1)RISC
ARM 芯片属于精简指令集计算机 RISC,指令简单,特点如下:
- 对内存只有读、写指令
- 对于数据的运算在 CPU 内部实现
- 使用 RISC 指令的 CPU 复杂度小一点,易于设计

对于以上乘法运算:a = a*b; 还有问题,CPU内部怎么保存输入输出的数据?
(2)CPU 内部寄存器

CPU 内部有 R0~R15 寄存器用来“暂存”数据,R13是栈指针 SP(Stack Pointer),R14用来保存返回地址是 LR(Link Register),R15表示当前指令地址修改后可跳转是程序计数器 PC (Program Counter)。
程序代码和常量存在 Flash,运行时必须加载到内存;变量、堆、栈等动态数据都在内存中。

注意:数据传输的三要素是 源、目的、长度
(3)汇编指令
需要掌握一些常用的汇编指令,许多单片机程序的问题要回到底层的汇编才能排查。
读内存:Load
写内存:Store
加减:ADD, SUB
比较:CMP
跳转:B, BL
纯机器码(.bin 文件),反汇编后只能看到 “地址 + 汇编指令”,无法对应到 C 代码行;ARM 开发环境(Keil MDK)生成的带调试信息的可执行文件(.axf文件) 带符号表,反汇编 / 调试时能直接看到 “变量名、函数名、C 代码行号”;汇编代码(.dis)可帮助调试 / 分析程序执行逻辑。
汇编码和机器码一一对应,汇编码易于阅读,只有机器码烧写在芯片上。
(4)堆栈
堆的概念:一块内存空间,可以分配 malloc 出一个 buffer 并在用完后 free 释放
简单的 malloc 无法实现对应的 free函数,因为无法得知所释放内存的长度(常见的堆管理函数通过增加一段头部保留数据大小,并用链表管理多段空闲 buf)
分配、释放内存的案例:
初始状态如下(全局头部找到第一个空闲块)。分配时查剩余空间,size足够则成功分配
之后想要申请内存时,从头部找到第一个空闲块,若其size不够,则从链表找到第二个空闲块;有空闲块满足大小要求则分配,若都不满足则失败
栈的概念【RTOS的基础】:一块内存空间,cpu 的 SP寄存器管理它,可用于 函数调用、局部变量、多任务系统里保存现场 等场景。深入理解栈需要对应反汇编指令解析。
调用子函数的本质 —— BL 指令(LR 会记录返回值,PC 指向跳转的子函数地址)
疑问:子函数中修改 LR 不会被覆盖吗?局部变量在栈里分配,如何分配?为何每个 RTOS 任务都有自己的栈?
答:每个c 函数入口会 先划分出自己的栈,提前保存 LR 到内存的栈区,保存局部变量到栈
RTOS如何运行两个并行的累加任务 —— 每个任务都有自己的栈。
保存现场就是 保存被切换瞬间的所有寄存器(内核切换时不知道哪些信息无用)
每个任务都有【自己的调用关系、局部变量、现场信息】,每个任务都需要自己的栈
2,FreeRTOS 源码分析
使用STM32CubeMX 创建的FreeRTOS工程中,FreeRTOS相关的源码结构如下:

核心是两个目录:1)Core(Inc目录下有配置文件,Src目录下的freertos.c是创建的默认任务);2)Middlewares\Third_Party\FreeRTOS\Source (根目录下是通用的核心文件,portable目录下是移植时需要实现的文件 名为 [compiler]/[architecture])
(1)核心文件
- FreeRTOS/Source/tasks.c
- FreeRTOS/Source/list.c

(2)头文件目录
- FreeRTOS 本身的头文件在Middlewares\Third_Party\FreeRTOS\Source\include
- 移植时用到的头文件在Middlewares\...\Source\portable\[compiler]\[architecture]
- 含有配置文件 FreeRTOSConfig.h 的目录:Core\Inc

(3)内存管理


heap_4/5的内存管理方案效率远高于heap_3用的标准库 malloc/free,因为malloc/free 是通用的内存分配器,要适配所有场景(桌面、服务器、嵌入式等),追求通用性和内存利用率【C库的这两个函数并非线程安全的,要先暂停 FreeRTOS 调度器再去调用】;而 heap_x 是为嵌入式实时操作系统(RTOS) 量身定制的,核心目标是确定性(实时性) 和高效性,为此牺牲了部分通用性,这是效率差异的根本原因。
Heap 相关的函数:分配、释放内存:pvPortMalloc / vPortFree,pvPortMalloc函数内部有malloc失败的钩子函数:vApplicationMallocFailedHook;空闲内存大小:xPortGetFreeHeapSize;空闲内存容量的最小值:xPortGetMinimumEverFreeHeapSize
(4)入口函数
/* Init scheduler */
osKernelInitialize(); /* 初始化FreeRTOS运行环境 */
MX_FREERTOS_Init(); /* 创建任务 */
/* Start scheduler */
osKernelStart(); /* 启动调度器 */
3,数据类型和编程该回复
(1)数据类型
每个移植的版本都含有自己的portmacro.h头文件,里面定义了2个数据类型:
- TickType_t:记录时钟中断的次数累计 tick count,一般配置为 uint32_t
- BaseType_t:该架构最高效的数据类型,一般配置为 uint32_t
(2)变量名
变量、函数、宏的命名都有统一的前缀表示,详见文档 7.7节。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐





所有评论(0)