当前课程知识点:智能车制作:嵌入式系统 >  第三章 MCU基础 >  3.3.2 ARM的体系结构-头脑体操 >  Video

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

Video在线视频

Video

下一节:Video

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

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

各位同学大家好

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

欢迎大家回到我们

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

我们来继续进行第三章

特别是这个单元

关于ARM的微处理器的

微控制器的CPU内核的学习

在上个单元里头

我们已经看了ARM Cortex M系列

从M0到M3 M4的

片内的编程模型寄存器组

特别是草草的看一下

它的指令集的相互之间子集的关系

我相信绝大多数同学看完了的感觉呢

是有点晕

所以这个单元没关系

我们来做一个头脑体操

就跟前面几个单元

对16位的MCU做了个头脑体操一样

我们做完这个头脑体操

看看真正的汇编程序

在ARM Cortex M的CPU上怎么运行

相信大家对它的理解就会上一个层次

我们同样来看这样一段程序

在这样一段程序里头

左边是一条条的指令

每条指令放在它对应的地址上

跟我们上一次做头脑体操是一样的

那么右边呢我们最上面有一段内存空间

这段内存空间每一个地址里头可以存一个

32位的数对吧

那么所以我们的地址大家注意

是以4来累加的

分别是8 C 0这样的数往下累加

然后右下角不太起眼的地方

是我们刚才所提到的

我们的编程模型寄存器组

我们会有一个堆栈指针寄存器

我们会有一个PC指针寄存器

然后我们还会有另外三个我们没有用到的

但是可以使用的通用的数据寄存器

比如说R4 R5和R6

那么这段程序在运行之前

我们可以发现蓝色的箭头

总是指向我们上一条执行完了的指令

红色的箭头呢

总是指向我们当前堆栈

已经使用到的使用过的内存地址

那么对于ARM这样一种体结构

堆栈的使用

跟我们之前举起的例子很像

就是堆栈总是指向这个内存

已经使用的地址

也就是说它的下一个地址

是我们进行压栈的时候可以使用的

空白的内存空间

那么我们在这样一段汇编程序里

大家会非常敏锐地发现

我们同样使用了

汇编语言的助记符

这些助记符

也就是我们的汇编指令

汇编程序每一条

都跟一组0和1对应的ARM的指令相呼应

所以大家如果想

深入理解这些指令其实不多

就五十多条

可以去看一看ARM专门的

这个CPU的手册

而对于这些指令

它的含义我们可以非常简单得用几句话

都给大家给出来

而跟着我执行的过程当中

大家可以非常容易理解

它都很简单

我们来一条一条的对这些程序加以运行

我们进行一个人脑计算机来头脑体操

看看一个ARM Cortex M0的CPU上

这么一段程序

它的运行过程是什么样

来把我们刚才学的知识用上

那么我们刚刚第一条指令

也就是在当前这个状态下

刚刚执行完了的一条指令

是在02这个地址上

是nop指令 nop指令在很多MCU里都有

我们之前16位也讲过

是no operation

什么都不做

也就是我们上一条指令

什么都没有做

那么上一条指令

执行完了的结果

我们这些寄存器当中

最重要的一件事情是PC指针寄存器

那么这个PC指针寄存器的值呢

因为是在上一条指令执行过程当中

我们把它给标红了的

那么他现在的地址

指向了多少呢我们仔细看一看

发现它指向了0000804这个地址

这个地址我们到左边的程序里去看

会发现他就是我们接下来要执行的

下一条指令的地址

这是PC指针的功能

我们还记得吧

它总是指向下一条地址

每次运行完指令它会自动的累加

使我们的程序 一条条指令往下走

那下一条指令是是movs r4#18

那么这条指令的含义非常简单是个MOV指令

把后面18这样一个数 #表示十进制

十进制的18给R4这个寄存器存进去

就这么简单

那么来把它运行一下看一下

这条指令运行我们发现整个CPU

哪些东西发生了变化呢

首先我们蓝色的箭头往下移了一位

这是我们自己挪的

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

那么在CPU内部我们看到的是什么呢

首先PC指针寄存器的值进行了更新

它现在指向的是806

也就是下一条指令

那么我们的R4也随之在刚才那条执行的

结果上被赋上了一个新值

赋的值是多少呢

是0x0000 0012

为什么是12啊

因为18是个十进制数赋给了寄存器

十六进制就是十六进制的12对吧

那么我们来看下一条要执行的指令

对应的指令是什么呢

是上面左边这个第三行movs r5 #52

立即数 十进制的52

也就是把52这个数

赋给R5寄存器

好那我们没有执行

同学已经很快能想到这条指令执行效果是什么了

对吧 它非常清楚就是个赋值

我们来执行一下

大家会发现红色区域

就是我又标识发生了变化的寄存器

那么在这条指令执行完了以后

我们的PC指针寄存器再一次的向下移了两位

也就指向了8008 这个地址

也就是下一条要运行的指令

同时发生变化的另外一个CPU内部的寄存器呢

就是我们的R5寄存器

它现在被赋上了一个新的值

对应的是十进制的52

那么它在这里写的是十六进制

也就是我们这个赋值成功的完成了

那么我们再看看下一条指令

也就是现在PC指针寄存器所指向的

8008这个地址

这条指令是什么

是push {r4}

这条指令的意思我们都有个印象

堆栈只有两种操作

一个是push压栈

把一个数据放入堆栈里

一个呢是pull

或者叫pop把堆栈里的一个数给拿出来

大家还记得吗

小猴子拿饼干对吧

所以呢

这条指令是把R4寄存器的值放入堆栈

那我提前问一句堆栈在哪里

堆栈在内存里对不对

那堆栈怎么找

堆栈根据SP堆栈指针寄存器的值

它所存的那个地址

指向的内存空间是我们要用的堆栈

所以我们现在回过头来看看这个寄存器里

SP这个堆栈指针寄存器存的值

是我们堆栈的底部

也是堆栈现在用到的位置

所以我们要压栈的时候

像这个水桶也好

像小猴子拿饼干也好

我们只能往它上面的一个地址

自动的放入一个数

也就是我们现在内存空间灰色的

可以用这些格子当中

最下面的一个是我们待会要用的

所以我们来执行这条指令

执行完了以后

我们发现若干个值发生了变化

哪些值发生了变化呢

首先我们发现

PC指针寄存器的值再次发生了累加

指向了下一条指令

这个时候堆栈指针

SP寄存器的值发生了变化

它减少了四

为什么呢

因为我们的堆栈看上面的内存区域

被用掉了四个字节一个格子对吧

我们R4寄存器的值

也就是刚才的那个0x0000 0012

这个时候被放入了堆栈用掉了四个字节

堆栈指针寄存器的值水位

顺着往上长了四个字节

这是堆栈的使用对吧

那么我们的下一条指令是什么呢

是push {r5} 把R5寄存器的值也放入堆栈

有了刚才的经验

我们也对堆栈又回忆起来了

那会发生什么事呢

我们想想

是不是 PC指针寄存器的值会累加

指向再下一条指令

堆栈指针寄存器的值会再减少

再往上水位

涨4个字节

堆栈也会再填入一个格子

填入R5的值对不对

我们来看运行的结果

确实就是这样吧

指令又往下走了一步

堆栈里多了一个数

这个数是刚才R5寄存器里头的值

这就是我们整个程序执行的

这样一个结果

所以这个值是十六进制的0x34

那么我们下一条指令

也就是现在PC指针寄存器指向的

再往下一条指令

也就是80c这样指令

它对应的是什么呢

我们发现是bl SubFunc

这是一个跳转指令

B是branch的缩写

那么它是一个子函数

或者叫子程序的调用指令

那么它的含义是

跳转到下面SubFunc这个函数

来进行执行调用子函数了对吧

那么我们前面

做过16位的头脑体操

我们现在想想

我们现在调用这个函数会发生什么事情

我们PC指针寄存器应该跳转到SubFunc

所指向的这个子函数第一条指令的地址

确保CPU接下来执行这个子函数

同时呢我们应该有一个机制

保存这个函数

执行完以后应该返回的地址

是不是这么两件事儿

我们来看CPU怎么处理的

那么这件事情执行完了以后

我们会发现因为我们这是一个ARM的CPU

而且这是一个一级的函数调用

我们会发现在这次调动发生以后

我们的PC指针寄存器

果不其然

被更新到了SubFunc的入口地址

也就是底下的145结尾的这个地址上

那么接下来我们

就该执行这个子函数了

同时我们发现

在这个CPU上在这样一级调用里

我们堆栈暂时没有用

堆栈没有动

返回地址到哪了呢

到了我刚才讲的CPU体结构所讲的

Link Register里头

这个里头保存了一个地址

这个地址稍微有一点点特殊性

为什么呢

因为这个地址大家仔细看

它存的是0x0000 0811

大家仔细看

我们程序里头所有的地址在左边

有一个什么规律啊

这些地址全是偶数

因为我们ARM Cortex M 的指令

都是16比特或者32比特宽的

所以 指令占的存储空间

都是两个 两个 四个 四个在用的

所以地址都是偶数

那么为什么存过来的

这个返回地址变成0811

待会返回的时候应该返回哪呢

这个地方告诉大家

这个就是ARM这个体系结构的一个

向下兼容的特殊性

当这个保存的返回地址的最低位是1的时候

函数调用和任何跳转返回的时候

CPU仍然工作在我们前面所讲过的

Thumb指令集的状态下

而如果这个最低位

变成了0就表示告诉CPU在返回的时候

应该切换到ARM指令集的模式

因为我们现在使用的是Cortex M的

这样一种CPU体系结构

所以所有的这样的

跳转的返回地址自动的把最低位给置1

而在返回的时候

这一位被忽略

只许它偶数的部分做我们的返回地址

所以存的是040811

实际的信息是返回Thumb模式

回到0x0000 0810这个地址

那0810这个地址是哪一个呢

回到左边的程序看一看

就是我们调用子函数的下一条指令对吧

所以这个地址是正确的

那么在这个过程中

没有用到堆栈

好 我们接着再往下执行

我们会发现PC指针指向的

已经是子函数的第一个指令了

它这条指令非常好

又是NOP指令什么都不干

那么想想

这条指令执行下去会发生什么事儿

很显然什么事都不会发生

只有PC指针寄存器

更新到下一条指令流水

这个车轮继续往前滚动

然后我们会发现

果然这条指令执行完了被执行的指令

是这个nop指令

而我们的PC指针寄存器

指向了这个nop下的下一条指令

也就是bx lr这样一条指令

那么这条指令的含义

是根据link register

也就是我们刚才右边

LR寄存器里头的值

进行跳转函数的返回

回到我们调用的主函数里头去

就这么一个功能

那么这条指令我们执行下来

大家看一看 会发现

这条指令一旦执行完

发生什么事了呢

其它的堆栈内存都没有使用

我们刚才的LR寄存器的值0x0811

是不是如我所说

最低位的那个1

被用来给CPU判断应该在什么指令集

我们因为都是Thumb指令集所以它就被使用了

而它的偶数部分被取出来

还给了PC指针寄存器

也就是说

PC指针寄存器还原成了程序调用完函数以后

应该返回的地址

也就是我们刚才函数调用的

bl SubFunc的下一条指令

POP指令这个地方

所以程序是不是从主程序

调用了一个函数

又从函数成功的返回了

主程序接着往下跑啦

这接下来就应该执行

0x0810的这条POP指令

那POP指令的含义非常的简单

从堆栈弹出一个数

大家注意啊

回忆我们最开始堆栈的使用

是忽略地址的

它用堆栈指针

来指向这个水位

所以对它操作非常简单

只是放进去拿出来对吧

现在要拿出来了

POP弹出一个值放回给r4寄存器

那么这个时候弹出的这个值R4寄存器呢

是我们堆栈当前可以使用的最上面这一个

内存单元的值也就是这个0x34

所以这条指令执行完以后

这个值就应该赋给R4数据寄存器

我们来看看效果

果然执行完以后

发生了很多变化对不对

不像我刚才说那么简单

我们仔细看一看会发现哪些东西发生变化

首先堆栈里头水位下降了一位为什么下降了

我这里标的是蓝颜色

对于计算机来讲

是堆栈指针寄存器的地址值

这个右边CPU里头加了4

表示我可以使用的堆栈往下降了一格

释放了四个字节

虽然值还在那

但它已经对我没有用了

下次可以被覆盖掉

而现在有效的堆栈使用的位置已经降下来了

同时呢这个值到哪去了呢

它被写给了R4寄存器

R4寄存器的值发生了变化

被弹出的3 4覆盖了r4寄存器原来的值

然后同时还有个变化的

就是我们的PC指针寄存器顺序加二

又执行到了下一条指令

下一条指令就是我们最后一条指令

是什么是POP R5

把堆栈里头弹出一个值

取出一个值

取出最后一个值

给r5数据寄存器

那么它执行的结果

我们已经非常清楚把这个值拿出来

还给R5寄存器

运行的效果就是这个样子

会发现我们堆栈里头所有刚才压进去的

两个数已经都拿出来了

所以堆栈指针再次回到了初始的位置

也就是这段内存的最底部

而PC指针寄存器也顺序累加执行到了

我们现在看不见的

再下一条指令那去了

而堆栈里最后一个值

这个值0x0000 0012

被取出来覆盖了R5寄存器原来的值

这样就形成了一个堆栈的使用

我们脑袋头脑体操

运行完了这一段汇编程序

那么这段程序汇编运行完了以后

我们想想在这里头

回忆一下

我们会领悟点什么呢

我想我们领悟到这么几件事

第一这个程序的功能是什么

跟我们上一次的头脑体操一样

这个程序的功能

就是把R4 R5寄存器的值换了个位置

对不对

一开始的时候是你是1 2 我是3 4

现在运行完了

变成我的3 4 你是1 2

互换了位置

第二它们为什么会换个位置呢

因为我们在这个过程当中使用了堆栈

堆栈的数据结构能够保持数据的顺序

所以我们是按R4 R5的顺序压入堆栈的

然后呢堆栈要求后入必须先出

所以数据在里头是有顺序的

而我们再次按R4 R5的顺序弹出

是不是把原来R5的给了R4 R4的给了R5

这就是堆栈的理解

然后最后呢

我们还领悟到了什么

这就是刚才

讲了一个单元十分钟好像很复杂的

ARM Cortex M的CPU内部的指令寄存器

编程模型汇编程序的运行流程

我们用人脑也能运行

那你是不就突然觉得小菜一碟

并不是那么困难了呢

如果有这种感觉

那我觉得就对了

那么有兴趣的同学你可以倒过来

把这个头脑体操自己再多做几遍

或者日后自己写一小段C语言或者汇编语言

用单步调试去看一看这些指令的运行

在这门课的范畴里具备汇编语言阅读的能力

我觉得是一个非常非常重要和有意义的能力

希望大家加以锻炼

如果大家觉得

ARM Cortex M的处理器你已经了然于胸

从M0到M4好像都很简单

我多说两句

我们这门课程所学习的是M0+的处理器

它跟M0有什么区别呢

大家日后会慢慢理解

我在这里点一下

一个是它将三节流水减成了两节

所以我们这样一款

学习的微处理器是一个非常简单

而且是目前地球上功耗最低的微处理器

此外呢

它还有若干个可选的功能

加强原来M0处理器的一些基本的能力

比如它有单指令周期的IO访问

比如说它有更加强大的调试能力等等

这样一些的扩充都是我们对于M0处理器

跟M0+这样一种处理器的差异

那么如果大家想更加深入地学习

M0处理器和M0+处理器

它的内部的指令集

包括我刚才所讲到这些体系架构

我给大家推荐一本书是叫Joseph Yiu

这是一个华裔的学者写的

那么这本书有一个中文版

叫《ARM Cortex-M0权威指南》

讲了里头很多详细的架构

那么他有个新版

目前只有英文版是ARM Cortex-M0

和ARM Cortex-M0+的权威指南是英文版的

这两本书任何一个都可以

有兴趣同学可以加以学习

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

第一章 概览

-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笔记与讨论

也许你还感兴趣的课程:

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