韦东山硬件编程学习随笔

CPU,MPU,MCU

CPU和MPU差不多,MPU功能更单一

芯片=CPU+RAM+FLASH

MCU(也叫单片机,微控制器)=芯片+其他模块(比如GPIO)

stm32里,cortex_m3就是cpu,

ARM架构的cpu,cpu内部的寄存器和外部的外设寄存器,是在一个地址空间内,而x86则是分开的。cpu内部的寄存器cpu可以直接访问,不用经过存储控制器

ARM芯片一般用的精简指令集,精简指令就是将各个动作拆成了很小的单元,这样的指令集灵活性高,占空间小,但是步骤复杂。x86用的复杂指令集,通过一条指令执行一个微程序,微程序是更多简单指令组合起来的集合。

 

 

xPSR实际上是3个寄存器,用来保存程序状态。三个寄存器都用的不同位,可以一次访问三个。

 

 A7内核的更复杂。各个模式下都有自己的寄存器,比如在FIQ快速中断模式下,访问R8,出来的值是R8_fiq的值,也就是快中断自己的R8寄存器,如果没有自己的寄存器,那么进入中断时,要保存被打断的状态,这就会耗时,所以用自己的寄存器就能更快中断。

指令集

一开始,ARM公司发布了两种指令集,ARM指令集每条指令占据32位,而Thumb指令集占16位。

 

 

 模拟器,只模拟了cpu,ram和rom,没模拟外设。

最右侧就是cpu内的寄存器,最下面是程序状态寄存器。pointer是指针。

 

LDRB,读一个字节,LDR读4字节。

STM和LDM指令

STMIA 

可以看到,栈出现了,栈就是一块特殊的内存

 

STMDB

可以看到,栈的结构和内容,对比上面的STMIA

STMIB

STMDA

 通常使用满减的方式操作栈,如下

R1,R2,R3的值存到了栈里面,压栈是先减后存,即DB,d减少,b表示before。可以看到,指针sp在0x1fff4

如果SP后没有!。则sp指针重新指向0X20000。如下

可以看到,执行到这里时,R1R2R3都被清零。 

 可以看到,执行LDMFD后,R1,R2,R3的值都恢复了,SP也恢复了0X20000。出栈是先读后加,即IA

数据处理指令

ADD加法,SUB减法,AND与,ORR或,BIC位清除,CMP比较

 MOVEQ,当程序状态寄存器的Z为0,才执行该指令。

 

程序跳转,即C语言的函数调用

R13,14,15都是特殊的寄存器。R13是SP寄存器(栈指针),R14是LR寄存器(临时保存程序返回的地址),R15是PC寄存器(程序下一步执行的地址)。

当R0减到0时,程序状态寄存器的Z位将为1。

下面代码,由于没有保存调用delay函数前函数执行的位置,即LR寄存器全程为0,所以当RO为0时,跳出循环,然后将LR值赋给PC,导致PC为0,然后程序下一步又开始从头执行,不断地调用Delay函数,甚至会崩溃。

 改为BL指令跳转后,在执行第一步BL时,返回地址(0x4)就被保存在了LR寄存器,跳出循环后,就能跳回原位置,可以看到。R1确实等于1

 当然。也可以直接给PC寄存器赋值跳转,如下,使用B指令,手动赋值跳转,也成功了

 当然,如下写法也可以,Re只是起到占位的作用,无其他意义,将Re地址赋给LR寄存器(保存返回地址),然后将Delay函数地址赋给PC,然后就会跳转到Delay执行。

汇编与反汇编

 同样的汇编代码,同样的编译器,会汇编出不同的机器码,以适应不同的cpu架构。

汇编调用函数,直接用跳转指令就可以了,比如B或BL,如果要传参数呢?将参数值放入cpu内的寄存器即可,比如RO,R1,这是由ARM公司指定的一套复杂的规则。

 

 如下规则,当然R4到R11并不仅仅保存局部变量,还能保存全局变量等等。

 如果在一个函数内调用另一个函数,万一两个函数都使用了某个寄存器呢,比如R4,这时候就要用到栈,将外层函数的某些值先保存到栈中,后面再取出来恢复。

分析汇编代码

 main函数分析,可以看到,调用delay时传入的参数保存在了R0寄存器,并且在跳转到delay前,将返回地址记录在了LR寄存器。

 delay函数分析,BNE判断d是否为0,不为0则继续循环,为0就执行BX指令,反回LR值对应的地址。

但是上述代码没有用到栈,因为代码太简单用不到,稍微将delay改变一下,加了volatile,编译器就不会擅自优化代码,就会用到栈,push将R0和LR寄存器的值放入了栈。

段和重定位

程序至少包含:代码段+数据段
1、代码段:.text
2、数据段:.data
        一般存储全局变量,初值不为0的经过初始化的全局变量(可读可写)
        如:char g_char = 'A';    //初值为A的字符型全局变量
只读数据段:.rodata
const的全局变量,只读数据段
.bss段:初值为0,或者没有初值的全局变量,不保存在bin文件中
        如:int g_A = 0;                //初值为0的整形全局变量
               int g_B;                      //无初值的整形全局变量
commen:注释,不保存在bin文件中
 

为什么要重定位,如下代码,如果直接打印,A会显示乱码,为什么,因为g_char是普通的全局变量,程序运行时必须拷贝到内存,而我们并没有这么做,打印的时候就会从内存读取,所以是乱码,怎么办呢,重定位即可。

而B是const修饰的在ROM,在Stm32就是Nor Flash。NorFlash是可以直接寻址到的。

打印出他们的地址, 如下可以看到,A和B的地址,一个在RAM一个在ROM,因为Const修饰的是常量,不能被更改,而ROM又恰好是只读的,所以就将Const常量放到ROM了,而非const全局变量,可能会被修改,而Flash是只读的,所以必须拷贝到内存RAM。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值