STM32 RTTHREAD线程编写知识
知识储备:
CORTEX-M3/M4寄存器组
1.R0-R12
寄存器R0〜R12为通用目的寄存器,前8个(R0-R7)也被称作低寄存器。由于指令中 可用的空间有限,
许多16位指令只能访问低寄存器。高寄存器(R8-R12)则可以用于32位 指令和几个16位指令,如
MOV(move)。R0-R12的初始值是未定义的。
2.R13,栈指针(SP)
R13为栈指针,可通过PUSH和POP操作实现栈存储的访问。物理上存在两个栈指针: 主栈指针(MSP,
有些ARM文献也称其为SP_main)为默认的栈指针,在复位后或处理器处于 处理模式时,其会被处理器
选择使用。另外一个栈指针名为进程栈指针(PSP,有些ARM文 献也称其为SP_process),其只能用于线
程模式。栈指针的选择由特殊寄存器CONTROL决 定,4. 2. 3节中有对该寄存器的描述。对于一般的程序,
这两个寄存器只会有一个可见。
MSP和PSP都是32位的,不过栈指针(MSP或PSP)的最低两位总是为0,对这两位的写 操作不起作用。
对于ARM Cortex-M处理器,PUSH和POP总是32位的,栈操作的地址也 必须对齐到32位的字边界上。
大多情况下,若应用不需要嵌入式OS,PSP也没必要使用。许多简单的应用可以完全依 赖于MSP,-般在
用到嵌入式OS时才会使用PSP,此时OS内核同应用任务的栈是相互独立 的。PSP的初始值未定义,而
MSP的初始值则需要在复位流程中从存储器的第一个字中 取出。
3.R14,链接寄存器(LR)
R14也被称作链接寄存器(LR),用于函数或子程序调用时返回地址的保存。在函数或子 程序结束时,
程序控制可以通过将LR的数值加载程序计数器(PC)中返回调用程序处并继续 执行。当执行了函数
或子程序调用后,LR的数值会自动更新。若某函数需要调用另外一个 函数或子程序,则它需要首先
将LR的数值保存在栈中,否则,当执行了函数调用后,LR的当 前值会丢失。
在异常处理期间,LR也会被自动更新为特殊的EXC_RETURN(异常返回)数值,之后该 数值会在
异常处理结束时触发异常返回。本书第8章将会对这方面进行更加深入的介绍。
尽管Cortex-M处理器中的返回地址数值总是偶数(由于指令会对齐到半字地址上,因 此,第0位为0)
,LR的第0位为可读可写的,有些跳转/调用操作需要将LR(或正使用的任何 寄存器)的第0位置1以表
示Thumb状态。
4.R15,程序计数器(PC)
R15为程序计数器(PC),是可读可写的,读操作返回当前指令地址加4(由于设计的流水 线特性及同
ARM7TDMI处理器兼容的需要)。写PC(例如,使用数据传输/处理指令)会引起 跳转操作。
由于指令必须要对齐到半字或字地址,PC的最低位(LSB)为0。不过,在使用一些跳转/ 读存储器指
令更新PC时,需要将新PC值的LSB置1以表示Thumb状态,否则就会由于试 图使用不支持的ARM指令
(如ARM7TDMI中的32位ARM指令)而触发错误异常。对于 高级编程语言(包括C和C++),编译器会
自动将跳转目标的LSB置位。
多数情况下,跳转和调用由专门的指令实现,利用数据处理指令更新PC的情况较为少 见。不过,在访
问位于程序存储器中的字符数据时,PC的数值非常有用,因此,会经常发现存 储器读操作将PC作为基
地址寄存器,而地址偏移则由指令中的立即数生成。
特殊功能寄存器组
Cortex‐M3 中的特殊功能寄存器包括:
- 程序状态寄存器组(PSRs 或曰 xPSR)
- 中断屏蔽寄存器组(PRIMASK, FAULTMASK,以及 BASEPRI)
- 控制寄存器(CONTROL)
它们只能被专用的 MSR 和 MRS 指令访问,而且它们也没有存储器地址。
MRS <gp_reg>, <special_reg> ;读特殊功能寄存器的值到通用寄存器
MSR <special_reg>, <gp_reg> ;写通用寄存器的值到特殊功能寄存器
程序状态寄存器(PSRs 或曰 PSR)
程序状态寄存器在其内部又被分为三个子状态寄存器:
- 应用程序 PSR(APSR)
- 中断号 PSR(IPSR)
- 执行 PSR(EPSR)
通过 MRS/MSR 指令,这 3 个 PSRs 即可以单独访问,也可以组合访问(2 个组合,3 个组合都可以)。
当使用三合一的方式访问时,应使用名字“xPSR”或者“PSR”
其实,为了快速地开关中断,CM3 还专门设置了一条 CPS 指令,有 4 种用法
CPSID I ;PRIMASK=1, ;关中断
CPSIE I ;PRIMASK=0, ;开中断
CPSID F ;FAULTMASK=1, ;关异常
CPSIE F ;FAULTMASK=0 ;开异常
ARM-THUMB子程序调用规则ATPCS
为了使C语言程序和汇编程序之间能够互相调用,必须为子程序间的调用制定规则,在ARM处理器中,
这个规则被称为ATPCS;ARM程序和Thumb程序中子程序调用的规则。基本的ATPCS规则包括寄存器
使用规则、数据栈使用规则、参数传递规则。
1. 寄存器的使用必须满足下面的规则:
1)子程序间通过寄存器R0一R3来传递参数,这时,寄存器R0~R3可以记作A1-A4。被调用的子程序在
返回前无需恢复寄存器R0~R3的内容。
2)在子程序中,使用寄存器R4~R11来保存局部变量.这时,寄存器 R4 ~ R11可以记作V1 ~ V8。
如果在子程序中使用到了寄存器V1~V8中的某些寄存器,子程序进入时必须保存这些寄存器的值,在
返回前必须恢复这些寄存器的值;对于子程序中没有用到的寄存器则不必进行这些操作。在Thumb程
序中,通常只能使用寄存器R4~R7来保存局部变量。
3)寄存器R12用作过程调用时的临时寄存器(用于保存SP,在函数返回时使用该寄存器出栈), 记作
ip。在子程序间的连接代码段中常有这种使用规则。
4)寄存器R13用作数据栈指针,记作sp。在子程序中寄存器R13不能用作其他用途。寄存器sp在进入子
程序时的值和退出子程序时的值必须相等。
5)寄存器R14称为连接寄存器,记作lr。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,
寄存器R14则可以用作其他用途。
6)寄存器R15是程序计数器,记作pc。它不能用作其他用途。
总结一下:
r0 ~ r3是函数调用时用来传递参数或者保存函数返回值的,r4 ~ r11是用来保存局部变量的,r12 ~ r15是特殊
功能寄存器。这些寄存器一般会被拆分成2部分:调用者保存的寄存器(r0-r3, r12, lr, psr)、被调用者保存的寄存器(r4-r11)。
比如函数A调用函数B,那么函数A就是调用者,函数B就是被调用者。那么根据这份ATPCS规则,函数A在调
用函数B之前,函数A就应该要保存r0-r3, r12, lr, psr这几个调用者要保存的寄存器;而对于r4-r11这几个寄存器,
调用函数B的前后,函数B本身会保证调用前后这些寄存器的值保持不变。
那么,如果函数B是中断服务函数,因为函数B本身会保证r4-r11的值不会被改变,所以保存现场也就是保存r0-r3,
r12, lr, psr这几个寄存器的值即可。
2、堆栈使用规则:
ATPCS规定堆栈为FD类型,即满递减堆栈。并且堆栈的操作是8字节对齐。
而对于汇编程序来说,如果目标文件中包含了外部调用,则必须满足以下条件:
外部接口的数据栈一定是8位对齐的,也就是要保证在进入该汇编代码后,直到该汇编程序调用外部代码之间,
数据栈的栈指针变化为偶数个字;
在汇编程序中使用PRESERVE8伪操作告诉连接器,本汇编程序是8字节对齐的.
3、参数的传递规则:
根据参数个数是否固定,可以将子程序分为参数个数固定的子程序和参数个数可变的子程序.这两种子程序的参
数传递规则是不同的.
1.参数个数可变的子程序参数传递规则
对于参数个数可变的子程序,当参数不超过4个时,可以使用寄存器R0~R3来进行参数传递,当参数超过4个时,还
可以使用数据栈来传递参数.
在参数传递时,将所有参数看做是存放在连续的内存单元中的字数据。然后,依次将各名字数据传送到寄存器
R0,R1,R2,R3; 如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数
据先入栈.
按照上面的规则,一个浮点数参数可以通过寄存器传递,也可以通过数据栈传递,也可能一半通过寄存器传递,
另一半通过数据栈传递。
example:
void func(a,b,c,d,e)
a -- r0
b -- r1
c -- r2
d -- r3
e -- 栈
2.参数个数固定的子程序参数传递规则
对于参数个数固定的子程序,参数传递与参数个数可变的子程序参数传递规则不同,如果系统包含浮点运算的硬件部件。
浮点参数将按照下面的规则传递:
(1)各个浮点参数按顺序处理;
(2)为每个浮点参数分配FP寄存器;
分配的方法是,满足该浮点参数需要的且编号最小的一组连续的FP寄存器.第一个整数参数通过寄存器R0~R3来传递,
其他参数通过数据栈传递.
3.子程序结果返回规则
1.结果为一个32位的整数时,可以通过寄存器R0返回.
2.结果为一个64位整数时,可以通过R0和R1返回,依此类推.
3.对于位数更多的结果,需要通过调用内存来传递.
举例:
使用r0 接收返回值
int func1(int m, int n)
m – r0
n – r1
返回值给 r0
为什么有的编程规范要求自定义函数的参数不要超过4个?
答:因为参数超过4个就需要压栈退栈,而压栈退栈需要增加很多指令周期。
对于参数比较多的情况,我们可以把数据封装到结构体中,然后传递结构体变量的地址。