当前课程知识点:智能车制作:嵌入式系统 >  第三章 MCU基础 >  3.2.2 堆栈的概念-头脑体操 >  Video

返回《智能车制作:嵌入式系统》慕课在线视频课程列表

Video在线视频

Video

下一节:Video

返回《智能车制作:嵌入式系统》慕课在线视频列表

Video课程教案、知识点、字幕

各位同学大家好

我是清华大学工程物理系的曾鸣老师

欢迎大家继续回到我们

ARM微控制器与嵌入式系统的MOOC课堂

我们继续进行第三章的学习

那么这个单元呢

进入了一个非常有意思的部分

我们拿一个典型的CPU来开刀

来做一个头脑体操

那么我们开刀的这个对象是

我们全国大学生智能车比赛

包括产业界用的非常多的12这个系列的MCU

它的CPU在我们上节课的最后

讲认识几种CPU的时候已经讲了

那么它的内部结构呢

就是这样一种寄存器组

我们称为Register file

那么它会有一个16比特的D寄存器

可以拆成A和B两个8比特的寄存器来使用

有两个寻址寄存器

有一个堆栈指针寄存器

有一个PC指针寄存器

还会有一系列的标志位

那么这样一个CPU

有了这么个原形

再加上编程模型的另外一半

我们还有一些指令集

一个真正的程序

在里头运行的过程是什么样的呢

在这一小节

我们通过一个头脑体操来进行深入的学习

那么我们看看这个一段程序

那么这样一段程序我们把它罗列出来

在最左边是一段程序的代码

这就呼应了我们最开始对于程序

或者对于存储器的那段回忆

什么是存储器

存储器就是一段一段的编了地址的格子对吧

说格子好像low

一段一段的存储格子

每个格子有一个编号

有个地址

比如在我们这个程序里头呢

就从3005的地址开始

3006顺着往下编号

而它的右边

我们写了一些小英文字母比如叫NOP

比如叫LDS

那么所写的这些字母

就是我们所谓的汇编语言的助记符

所以这些汇编语言的序列

其实就是大家有些同学初学者很恐惧

或者很仰视的汇编语言程序

那么这些函数这些助记符两点

第一它的含义在我们的右下角

给大家了详细解释

每一条指令分别什么含义

在待会讲解过程中我会逐一讲

它都非常的简单

都是些英文的缩写

其次呢

这里每一个助记符都与我们真正的机器码

我们的机器计算机指令instruction一一对应

你可以理解这里的每一句英文

只是为了人类看得懂

而它对应的都是

一个零和一组成的序列

一段数 8比特 16比特放在这个存储器里

然后被我们的PC指针一条一条的取走

让CPU控制的单元加以解析最后运行

那我们现在来理解这件事情的运行对吧

然后右上角是一个存储单元

那么这个存储单元现在灰色

表示我们都还没有使用

每一个格子对应了一个地址

这些地址对应这些格子

就是一段还没有使用

等待我们使用的内存存储器

像草稿纸一样放在那儿

那么下面这几个红色的字

SP堆栈指针寄存器

PC指针寄存器

A和B刚才两个8比特的片内的数据寄存器

CPU内部的数据寄存器

是我们CPU内部的Register file 寄存器组

那么程序 内存 寄存器都有

指令解析

我们用我们的人脑来做一个头脑体操

看看这段程序究竟是怎么运行的

这个时候呢我们会有两个箭头

在第一页只有一个箭头就是蓝色的箭头

蓝色箭头指向的是

我们当前就在上一步已经执行完的指令它的值

它所指向的是上一个执行完的指令

那么在这个过程当中

我们来看看这个程序的运行

首先看看当前状态

蓝色箭头指向的3005地址

是一条指令叫NOP

NOP是计算机里经常各种CPU都会有的一条指令

是No operation

没有操作

所以这条指令就是什么都不干

浪费一个时钟周期

所以我们刚才那条指令

上一条指令只是浪费了一个时钟周期

而在这个状态里头

我们看看我们这边的CPU的寄存器的值

你会发现堆栈指针寄存器 anyway

是undefined 就是里头哪怕有值

我们并没有对它初始化过 它并没有意义 忽略

PC指针寄存器的值是什么

我们的程序计数器你会发现它是3006

3006是个地址

3006地址是什么

在左边的程序里

是我们下一条将要执行的指令

下一条将要执行的程序所在的地址

换言之我们的CPU

应该根据它的值自动的

把这条指令给加载到CPU里

接着运行 这是这个CPU

下一个时钟周期自动将会做的事

然后A和B这两个

数据寄存器里各有一个值

0x34 0x56 我也不知道它从哪来

反正当前状态它就在这儿了对吧

我们看看这程序往下走会怎么做

然后呢我们发现下一条指令是什么

3006这个地方是LDS 美元符号2000

这是一个汇编语言写法

LD是load的缩写

S是Stack pointer堆栈指针寄存器的缩写

所以这条指令是一条特殊指令

就是给SP

堆栈指针寄存器加载一个值

这个值就是后面跟的这个数

2000这个16进制数

美元符号在这个汇编语言里代表16进制

那换言之就是把这个数

赋给堆栈指针寄存器

我们看看它往下一执行

程序就变成了这样

这个时候我用红色标识了发生变化的部分

让大家更加醒目

那么我们首先看是不是左侧程序的那个蓝色箭头

当前指向了3006

表示这条指令已经执行完了对不对

我们现在右边看到结果

都是这条指令执行之后的结果

堆栈指针寄存器SP在右边

值变成了一个红色的新的值是2000

我们这条指令的结果给它赋了一个值

然后呢 我们的PC指针寄存器

它下面这个寄存器的值变成了3008

也就是下一条将要执行的指令

我们慢慢理解这是个自动过程

PC指针寄存器每执行一条指令

自动往下更新累加

这样CPU就能自动的一条条指令往下走

那么这个时候上面的内存空间

出现了一个红色箭头

红色的箭头指向的是1FFF往下一个地址

也就是2000

所以这个红色箭头

是堆栈指针寄存器所指向的那个地址

那么这个箭头往上标识着内存

已经被使用掉的地址

换言之如果把这段内存空间

我们把这个箭头指向的这段内存空间

就意味着它以上的空间是我们堆栈可以使用的

当我们像小猴子拿饼干一样

往里放数据和拿数据的时候

就在这个基准上开始往上涨和往下减

所以那这条指令我们干的什么呢

我们完成了堆栈的初始化

指定了刚才说闲置的这段内存空间给堆栈用

好了我们有堆栈了

那下一条指令是什么

先看一眼PC指针寄存器指向了3008地址

3008地址后面的那条指令PSHA

它是push A的缩写

意思是把A CPU内部的A寄存器的值 入栈

压栈Push进去

那我们脑袋想想它会干嘛

它会把这个值放到堆栈里对不对

这个值是一个字节所以执行一下

一执行我们就会发现

现在蓝色箭头指向PushA表示它已经执行完了

右边的PC指针寄存器指向了3009

又是下一条要执行的指令

而与之相应的

我们会发现上面内存是不是被用了

我们红箭头往上移了一格

到1FFF这儿

1FFF这个地址的格子里填了一个有用的数

这个数多少啊

是0x34

为什么呢

push A把A的值放入堆栈

把饼干放到筒里放进去一个34

存在一个存储空间上

所以这个存储空间的值变成了34

堆栈被用掉了一个格子

那我们看看与之相应的

底下堆栈指针寄存器变成多少啦

它往上减了个一 减啊

注意往上移 变成了1FFF

所以它标识当前的堆栈的内存

被用到了1FFF这个位置

下一个可用的位置是它上面一个地址对吧

这就是push A的结果

好 我们再看下一条指令

现在指的这个下一条指令是push B

那我们再来执行看它什么效果

我就不再多加解释

push B是把B寄存器的值压栈

所以大家会发现与之相应的

我们的PC指针寄存器是不是又往下移了一个地址

又进行了累加

自动指向了下一条指令

而我们的堆栈指针寄存器也往上移了一位

为什么

又被用掉了一个字节

而用掉的这个字节里填充的

是原来B寄存器的值也就0x56对不对

这个程序顺着往下走

然后我们会发现

PC指针寄存器所指向的地址是300A就是下一条指令

这条指令是什么

是JSR SubFunc

那么JSR是一条指令

是jump to subroutine

跳转到一个子函数的意思

那么是一个跳转

那么后面跟着一个SubFunc

我们可以把它称为是汇编语言了

如果按C语言来理解

它是一个子程序名或者函数名

就是我们这个代码的底下一小段

那么这个SubFunc我们可以理解为

它就是这个函数这个子程序的入口地址的别名

日后我们会理解它也是一个指针

所以在这里我们就理解它就是0x4050这个地址值

也就是下面这个子程序的第一条指令的地址

所以这条指令的意思是

跳转到4050这里去执行子程序对不对

那么这句话的执行会带来什么后果呢

聪明同学会想

什么叫跳转

就是PC指针寄存器指向一个新的地方

从这个地方开始执行指令这就跳转了对吧

那我们之前讲了那么多堆栈

会引入一个什么问题呢

就是函数子程序执行完了总会退出的

总会返回的

那么我们要想个办法把返回地址存下来

我们看看这件事情CPU里发生了什么

这么一执行我们会发现

哇 发生了很多变化

如果我们倒回去看一看

再倒回来

发现哪些地方变了呢

我们会发现首先蓝箭头JSR这条程序已经执行完了对吧

那我们PC指针寄存器的值变成什么了

我们会发现PC指针寄存器的值变成了0x4050

也就是说我们接下来就得执行

子函数里头的第一条指令了真的跳转过去了

那么一旦PC跳转下去从子函数往下走

我们总有一日要回来执行这个函数调用的下一条语句对不对

所以大家看看

堆栈里是不是多了两个东西啊

多了两个字节的内容

一个存的是0x30

另外一个存的是什么

另外一个存的是0x0C

这两个字节合起来是一个16比特的完整地址

0x300C

也就说在我们发生函数调用的一瞬间

CPU自动的往堆栈压了两个字节的值

这两个字节的值合起来是个地址

那这个300C是谁

大家到程序里去找

会发现是JSR调用函数的这条指令的下一条指令

为什么呀 因为这个函数一旦返回

是不是应该从它的下一条指令继续往下走啊

所以他把下一条指令的地址先存在这儿

那么这个过程是一个自动的压栈过程

那么一下子用掉了堆栈里两个字节

是不是我们堆栈指针寄存器相应的减了二

指向了0x1FFC对吧

用掉两个字节

所以大家会想一想这一个跳转函数发生了好几件事

PC指针指向了子函数的入口地址

然后子函数执行完以后的返回地址

被自动压入了堆栈

然后堆栈因为用掉了两个字节

所以堆栈指针寄存器自动的往上移了两位

这是一个过程对吧

我们再往下执行

再往下执行会发现这个函数的第一条地址

又是一条NOP指令

它很简单

什么都不干对吧

那么这条指令执行完

我们会发现跟上一步比的变化

有变化吗

只有一个变化

就是这条指令执行完了以后

PC指针寄存器加了2

指向了它的下一条指令其他什么事情都没有做

那么下一条指令是什么呢

在这个程序里头会发现

0x4052这个地址所写的是RTS

这条指令的含义是return from subroutine 从子函数返回

所以我们如果再往下执行

这条指令一执行会发现什么

会发现发生了一个比较大的变化

我们倒回来看一下

再正过来看

哪些值变了呢

你会发现这个时候PC指针寄存器的新的值

不再是4052的下一条指令

而是变成了300C

300C从哪来

你会发现我们的堆栈

这个指针寄存器往下移了两位

释放了两个单元

把30和0C的值

小猴子取饼干是不是取出来了赋给了PC指针寄存器

所以PC指针寄存器在RTS这条指令的操作下

只做了一件事就是从堆栈的顶部

拿两个地址

因为这肯定是我最后放进去的

赋给PC指针寄存器

从而它指向了300C地址

也就我们刚才那个子函数调用执行完以后返回的地址

也就是PULLA这条指令了对吧

那么大家要理清的一个概念是

堆栈指针寄存器往下加了2

往下水位往下降了两位

意味着堆栈的使用位置降了两位

上面两个存储空间已经释放了

那么我们刚才300C这两个值其实仍然在内存里

它只是不再被堆栈所承认

它已经无用了

换言之我下次再要往堆栈上放饼干

放数的时候是不是就直接把它们覆盖掉

所以注意堆栈的push和pull并不是销毁内存里的值

只是把它存储进去

覆盖原来的值和拿走注意这个概念

好anyway

我们的堆栈释放了两个字节

堆栈指针寄存器往下降了两个比特

这是整个这个指令的结果

PC指针寄存器指向了函数的下一个地址

然后再往下执行

就应该执行我们的300C这个位置的指令就PULLA

那么这条指令的执行的效果

我们现在看一眼会非常的简单pull弹出

从堆栈弹出一个数

弹出到A数据寄存器就这么简单

那弹出的数是什么呢

你会发现0x56这个堆栈最上面这个值被弹出来了

弹出给了A寄存器

大家注意右边寄存器的值是不是A的值变成了0x56啊

从堆栈里拿了一个值出来赋给了A

那么相应的PC指针寄存器又指向了下一条指令

也就是0x300D

而我们的堆栈指针寄存器

又加一内存水位又释放了一个字节对吧

那最后一条指令是什么

300D执行

PULLB弹出一个字节给数据寄存器B

那么当前堆栈指针寄存器

指向最后一个堆栈里使用的存储的字节就0x34

把它弹出来

于是这个值改写了寄存器原来B的值变成了0x34

于是我们堆栈指针寄存器又归回了水位的最低位 0x2000

我们的堆栈回到了初始状态

没有使用

我们的PC指针寄存器

整个程序跑完了指向了下一个地址

那我想问的是

大家理解了这样一个16位的MCU汇编指令

Register file 寄存器模型

程序的加载和内存堆栈的使用的这样一个流程吗

如果觉得还没有明白的同学请你倒回去

把这个过程用你的人脑

运行CPU做头脑体操再做一遍

那么这个单元的最后一句话

我要问大家一个问题

这个程序实现了什么功能

大家想一想你会发现它是把A和B的值做了个对换

说 哎呀妈呀

这么大一段程序才完成这么一件事

那么它就是一个非常简单的功能

而实际CPU的运行

我们一个12单片机可以运行在几十兆

所以每条指令只会使用几十个纳秒

所以刚才我们费这么多口舌

所执行的这些指令只在电光火石间

一个微秒不到就能被CPU所运行完

那么我们建立了这样一个初步的概念

这个单元就到这儿结束

智能车制作:嵌入式系统课程列表:

第一章 概览

-1.1 课程概览

--Video

-1.2 进入嵌入式系统的世界

--Video

-1.3 如何学好嵌入式系统

--Video

第二章 绪论

-2.1 计算机的基本概念、发展历史

--Video

-2.2 从晶体管到CPU

--Video

-2.3 概念CPU、微控制器MCU和嵌入式系统

--Video

-2.4 八卦计算机史

--Video

-2.5 不同领域、不同系列的嵌入式系统

--Video

-2.6 ARM历史与MKL25Z128 MCU

--Video

第三章 MCU基础

-3.1 CPU的基本结构和运行机制

--Video

-3.2.1 堆栈的概念

--Video

-3.2.2 堆栈的概念-头脑体操

--Video

-3.3.1 ARM的体系结构

--Video

-3.3.2 ARM的体系结构-头脑体操

--Video

-3.4 中断的概念和机制

--Video

-3.5 中断子程的概念和编程

--Video

-3.6 复位、时钟、存储器和总线

--Video

-3.7 小结:MCU的总体结构和程序运行机制

--Video

第四章 MCU外设与开发

-4.1 第一种外设:IO

--Video

-4.2 IO外设的编程实操-点亮LED

--Video

-4.3 IO外设的进阶知识

--Video

-4.4 嵌入式开发的基本概念与工具链

--Video

-4.5 嵌入式开发的进阶知识

--Video

-4.6 嵌入式开发中的C语言(上)

--Video

-4.7 嵌入式开发中的C语言(下)

--Video

-E0.1 实验零 开发板的初步认识与工具链的安装

--Video

-E0.2 实验零 体验一个例程的编译与下载

--Video

-E0.3 实验零 编写第一个程序:点亮核心板LED

--Video

-E1 实验一 点灯秘籍

--Video

第五章 MCU与嵌入式系统设计

-5 智能车视角的嵌入式设计

--Video

Video笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。