RTOS必备基础
一、ARM基础知识
1、ARM架构
- 程序编译后生成 .bin、.hex文件,(汇编代码)烧入Flash中。
- 启动设备,程序在Flash中一条一条执行。
- 程序告诉CPU执行操作,如分配内存、分配栈、计算。
- CPU操作,如从内存中某个地址读写数据、开辟空间,GPIO的读写等。
CPU运行时,先去取得指令,再执行指令:
① 把内存a的值读入CPU寄存器R0
② 把内存b的值读入CPU寄存器R1
③ 把R0、R1累加,存入R0(对于数据的运算是在cpu内部执行)
④ 把R0的值写入内存a
2、重要寄存器
R# | APCS别名 | 意义 |
---|---|---|
R0 | a1 | 参数/结果/scratch 寄存器1 |
R1 | a2 | 参数/结果/scratch 寄存器2 |
R2 | a3 | 参数/结果/scratch 寄存器3 |
R3 | a4 | 参数/结果/scratch 寄存器4 |
R4 | v1 | arm 状态局部变量寄存器1 |
R5 | v2 | arm 状态局部变量寄存器2 |
R6 | v3 | arm 状态局部变量寄存器3 |
R7 | v4 / wr | arm 状态局部变量寄存器4 / thumb状态工作寄存器 |
R8 | v5 | arm 状态局部变量寄存器5 |
R9 | v6 / sb | arm 状态局部变量寄存器6 / 在支持RWPI的ATPCS中作为静态基址寄存器 |
R10 | v7 / sl | arm 状态局部变量寄存器7 / 在支持数据栈检查的ATPCS中作为数据栈限制指针 |
R11 | v8 / fp | arm 状态局部变量寄存器8 / 帧指针 |
R12 | ip | 内部过程调用 scratch寄存器 |
R13 | sp | 栈指针 |
R14 | lr | 链接寄存器 |
R15 | pc | 程序计数器 |
高编号的寄存器保存在高地址,低编号的寄存器保存在低地
- R0-R12:数据寄存器
调用C函数时 第一个参数保存在R0中 第二个参数保存在R1中 - R13:SP:栈
- R14:LR
跳转到子过程的时候,r14保存了返回地址,可以在调用过程结尾恢复。
异常中断发生时,这个异常模式特定的物理R14被设置成该异常模式将要返回的地址 - R15:PC
存放指令(函数)的地址 (后续会放入R0寄存器中)
3、汇编指令详解
读:load
LDR R0, [addr A]
将addr A的值读到 R0寄存器
LDR:将 存储器地址 所指地址处连续的4个字节(1个字)的数据传送到目的寄存器中。
写: store
STR R0,[addr A]
将addr A的值写到 R0寄存器
STR:用于从源寄存器中将一个32位的字数据传送到存储器
加: ADD
ADD R0,R1,R2
R0=R1+R2
ADD:两数相加
减: SUB
SUB A,B //A=A-B;
功能: 两个操作数的相减,即从A中减去B,其结果放在A中.
出栈: push
push {R3,LR}//括号中的顺序不强制规定
作用: push寄存器:将一个寄存器中的数据入栈(写入内存)
-
写入内存的位置由SP指定
-
高编号的寄存器保存在高地址,低编号的寄存器保存在低地址
出栈:pop
pop {R3,PC}//读数据赋值给RC和PC
读数据从SP指向的位置读取数据
- R3=[SP]:读取R3中的数据
- SP递增
- PC=[SP]:读取LR (函数返回地址)
4、栈和堆:
1. 栈
发生中断时,会保存现场:值保存在栈中
栈的整体作用
(1)保存现场/上下文
(2)传递参数:汇编代码调用c函数时,需传递参数
(3)保存临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量。
需要保存哪些数值
C程序中的保存数值
1.任务切换的时候 寄存器都要保护
2.函数调用的时候只保存改变后的值
用于传参的寄存器则不需要保存如上面的R0 R1 R2
内核中的中断保存数值
cortex M3和M4
- 硬件保存一部分 R0 R1 R2 入栈
- 软件就保存用到的值
栈的大小取决于
- 局部变量的多少
- 调用深度
栈的内存从哪里分配
- freertos的栈 由一个巨大的 全局数组 分配
- 调用深度
2. 堆
一块空闲内存,可以使用malloc/free函数来管理
volatile char my_buf[20*1024];
volatile int index = 0;
void *malloc(int size)
{
char *ret = &my_buf[index];
index += size;
return ret;
}
5、 局部变量和全局变量的分配与初始化
1. 局部变量初始化
C语言:
void my_main(void)
{
int a = 456;
}
汇编:
// 执行my_main()
PUSH {r3,lr} //进入函数,寄存器r3、lr的值,都存入内存的栈中(lr保存程序返回地址)
// 执行 int a = 456
MOV r0,#0x1C8 //0x1c8 = 456
STR r0,[sp,#0x00]
2. 全局变量、静态变量初始化
在调用main函数之前,使用copy、SetZero函数对全局变量、静态变量初始化
6、 函数是什么
二、RTOS系统注意事项
- 对于实时操作系统 高优先级的任务 永远优先执行(如果没有delay 将程序进入阻塞态 那实时操作系统就一直执行高优先级)
- 空闲任务(优先级通常较低)由调度器创建 会影响同样是0的优先级任务(相同优先级 空闲任务最后创建 所以最先执行)
- 空闲任务礼让:同等优先级 先等其他任务运行一次后 再运行空闲任务
- 在大于或 等于 优先级的时 如果任务1先创建 任务2后创建 就会先执行任务2
- 当任务 配置成 不抢占 不轮流 不礼让的时候
创建 顺序从左到右: 任务①<<任务②<<任务③<<空闲任务A
执行顺序如下:空闲任务 A 执行释放内存后 会礼让
空闲任务A << 任务① << 任务① << 任务①
一直会执行 任务①(可设置主动放弃入 即加入delay进入阻塞态就可以运行任务②)
不抢占就不会轮流执行 中断的优先级>任务优先级
参考:散文诗