【嵌入式系统开发】学习笔记二:堆栈以及ARM的概念及举例代码解析

提示:本文内容参考慕课课程:《ARM微控制器与嵌入式系统》

一、堆栈Stack

1.堆栈的概念

堆栈是一段连续的存储器空间,堆栈按照后入先出的方式工作(Last In First Out),只能向/从堆栈的顶部加入或取出数据,即堆栈能够保持数据的顺序。

• 堆:是一个进程开启以后,系统分配给它的间,一般这个空间是全局的,系统中所有动态分配的对象(比如指针)都是在这个空间上分配。堆里的数据是有数据结构的,其空间占用是不连续的。在没有OS的嵌入式系统中,通常不使用堆。

• 栈:是一个线程维护的一个先进后出的数据构,主要用于静态变量的分配及维护程序各个函数调用时使用。栈里的内容是没有数据结构的,其空间占用是连续的。栈是微处理器的必要特性,是支持高级语言编程的前提。
在这里插入图片描述
堆栈有两种基本的操作方式:
推入PUSH: 将内容加入到堆栈顶端
取出PULL : 将堆栈顶端的内容取出

对于大多数CPU而言,“顶端”是指
低位的地址空间

2.堆栈的作用

• 汇编程序可以使用堆栈来保存局部变量,寄存器值。

• C语言编译器使用堆栈来完成参数传递和返回值传递 →C语言的函数调

• CPU硬件 使用堆栈来保存返回地址和寄存器上下文(register context) →中断

3.堆栈指针寄存器Stack Pointer

1.堆栈顶端位置通过CPU内的堆栈指针寄存器确定(SP,Stack Pointer)。

2.堆栈指针的初始位置由程序代码确定,指向预先划定的堆
栈空间的底部

3.如果要自己操作堆栈,记住:
· Once push must pull
· Last In First Out

在使用C语言时,我们对于堆栈的绝大多数使用是感觉不到的,因为C语言的函数调用,函数的返回值,参数传递,局部变量的开销都是由编译器帮我们隐性的使用了堆栈。

开发工具会写出代码初始化堆栈,C语言编译器会隐性使用堆栈,用汇编编写中断时,需要注意堆栈。

4.举例

先了解一些汇编的基本指令:

  1. NOP: no operation,浪费一个时钟周期什么都不做
  2. LDS : load stack pointer with a value,给SP指针赋值
  3. PSHx: push the contains of register ‘x’ into stack,把内部的x寄存器的值入栈
  4. PULLx: pull from the stack and put in register ‘x’,从堆栈弹出一个数到寄存器x
  5. JSR : jump to subroutine,跳转到一个子函数
  6. RTS : return from subroutine,从子函数返回

下面我们将分析一段代码在16bitCPU的执行过程中堆栈的变化

0x3005: NOP
0x3006: LDS $2000
0x3008: PSHA
0x3009: PSHB
0x300A: JSR SubFunc
0x300C: PULLA
0x300D: PULLB
………..…
SubFunc: 0x4050: NOP
0x4052: RTS

解析过程如下:

1初始状态:
在这里插入图片描述
2执行以下代码,完成堆栈初始化,指定闲置内存空间给堆栈用

0x3005: NOP
0x3006: LDS $2000

在这里插入图片描述
SP指针初始化为0x2000,即0x2000以上都可以用作堆栈,PC指向下一条待执行的指令地址0x3008(因为下一条指令的地址为0x3008)

3执行以下代码,把A的值入栈

0x3008: PSHA

在这里插入图片描述
A寄存器的值被放到了堆栈里,0x1FFF里存储了数据,SP指针寄存器变为0x1FFF,同样PC指针寄存器指向下一条待执行指令的地址

4执行以下代码,原理同上,不再赘述

0x3009: PSHB

在这里插入图片描述
5执行以下代码,跳转到子程序SubFunc

0x300A: JSR SubFunc

在这里插入图片描述

堆栈里多了0x30和0x0C,两个字节合起来是一个16bit的完整地址0x300C

即在函数调用的一瞬间,

PC指针指向子函数的入口地址0x4050;CPU自动向对堆栈里压了两个字节(0x300C是调用子程序下一条指令的地址)因而SP指针向上移两位为0x1FFC.

6执行以下代码,执行子程序

SubFunc: 0x4050: NOP

在这里插入图片描述
PC指针指向子函数的下一条指令地址0x4052

7执行以下代码,从子函数返回

0x4052: RTS

在这里插入图片描述
堆栈指针寄存器释放了两个单元,把30和0C的值赋给了PC指针寄存器(0x300C是调用子程序下一条指令的地址),相应的SP指针向下变成0x1FFE

8执行以下代码

0x300C: PULLA

在这里插入图片描述

从堆栈弹出到寄存器A,即A=0x56
堆栈释放一个单元,SP下移变为0x1FFF
PC指针指向下一条指令的地址

9执行以下代码,原理同上,不再赘述

0x300D: PULLB

在这里插入图片描述
这个完整的程序实现了到子函数的跳转以及AB寄存器内值的交换

二、ARM Cortex M0+ CPU(32bits)

1. 基本指令

1. NOP: no operation,浪费一个时钟周期什么都不做
2. MOVS : move data, update APSR register,给寄存器赋值
3. PUSH: push the contains of register into stack,把寄存器的放入堆栈
4. POP: pop from the stack and put in register,从堆栈弹出给寄存器
5. BL : jump to subroutine, update LR,跳转到子程序
6. BX : return from subroutine, with LR value ,根据Link Register里的值进行跳转函数的返回

2. 举例

下面我们将分析以下代码在ARM Cortex M0+ CPU(32bits)的执行过程中堆栈的变化

00000802: nop
00000804: movs r4,#18
00000806: movs r5,#52
00000808: push {r4}
0000080a: push {r5}
0000080c: bl SubFunc
00000810: pop {r4}
00000812: pop {r5}
SubFunc:00000910: nop
00000912: bx lr

解析过程如下:

1执行以下代码:

00000802: nop

在这里插入图片描述
内存空间里每个单元里可以存一个32bit的数,所以地址以4来累加的。

2执行以下代码,把10进制的18存入r4寄存器内,换成16进制就是0x12

00000804: movs r4,#18

在这里插入图片描述
3执行以下代码,原理同上

00000806: movs r5,#52

在这里插入图片描述
4执行以下代码,把寄存器r4,r5的值压入堆栈

00000808: push {r4}
0000080a: push {r5}

在这里插入图片描述
5执行以下代码,跳转到SubFunc函数

0000080c: bl SubFunc

在这里插入图片描述
函数跳转瞬间,SP指针指向子函数第一条指令的地址即00000910

返回地址保存在了Link Register里,保存为0x0811,这是由于ARM体系的向下兼容性,当保存的返回地址的最低位是1时,函数调用
和任何跳转返回的时候,CPU仍然工作在Thumb指令集的状态下,最低为变为0就表示CPU应处于ARM指令集模式。所以存的是0x0811,实际信息是回到Thumb模式,回到0x0810地址。

6执行以下代码,执行子函数,返回主程序

SubFunc:00000910: nop
		00000912: bx lr

在这里插入图片描述
所以PC指针寄存器指向了跳转函数下一条指令地址即0x0810

7执行以下代码

00000810: pop {r4}
00000812: pop {r5}

在这里插入图片描述
虽然值还在堆栈里,但是每执行一条pop指令,SP指针寄存器向下移动一个。下一次那些值将会被覆盖掉。

程序的功能为交换r4,r5寄存器的值。因为堆栈要求先入后出。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
《STM32嵌入式系统开发实战指南:FreeRTOS和lwIP联合移植》是一本关于嵌入式系统开发的实战指南。本书介绍了如何在STM32微控制器上使用FreeRTOS实时操作系统和lwIP网络协议栈进行嵌入式系统开发。通过本书的学习,读者可以了解到如何基于STM32开发板搭建嵌入式系统开发环境,学习STM32微控制器和FreeRTOS操作系统的基本概念和原理,以及如何使用lwIP网络协议栈进行网络通信。 该书内容分为多个章节,每个章节都有详细的实例和代码进行讲解。首先,介绍了STM32微控制器的硬件架构和开发环境的搭建。然后,详细介绍了FreeRTOS的使用,包括任务、信号量、消息队列等基本概念和操作方法。接着,介绍了lwIP网络协议栈的基础知识和使用方法,包括网络套接字编程、TCP/IP通信、UDP广播等内容。最后,通过一个完整的示例项目,展示了如何将FreeRTOS和lwIP联合移植到STM32微控制器上,实现一个简单的TCP/IP网络通信应用程序。 本书适合嵌入式系统开发初学者或已有基础的开发学习使用。通过学习本书,读者可以全面了解STM32的基本知识和嵌入式系统开发的流程,同时深入学习FreeRTOS和lwIP的使用方法,能够独立进行嵌入式系统开发和网络通信的实际项目。整本书结合理论和实践,具有很强的实用性,是学习STM32嵌入式系统开发的重要参考资料。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KkKde小火柴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值