ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

STM32 RTTHREAD线程编写知识

2021-12-18 14:34:01  阅读:217  来源: 互联网

标签:调用 R0 RTTHREAD STM32 指令 参数 寄存器 线程 子程序


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个就需要压栈退栈,而压栈退栈需要增加很多指令周期。
对于参数比较多的情况,我们可以把数据封装到结构体中,然后传递结构体变量的地址。

标签:调用,R0,RTTHREAD,STM32,指令,参数,寄存器,线程,子程序
来源: https://blog.csdn.net/weixin_43522787/article/details/122008887

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有