找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3174|回复: 19
打印 上一主题 下一主题
收起左侧

汇编之从单片机设计者的角度来学习单片机

  [复制链接]
跳转到指定楼层
楼主
ID:266429 发表于 2019-10-24 21:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

先说某一组端口接一排8LED灯的情况,如何做到让这八个灯想亮哪个就亮哪个,想不亮哪个就不亮哪个。

比如说,8个灯,全部负极接地电位,正极接P18个端口,则此种情况下,如果要某个灯亮,则对应的那一个单片机的端口就应该为高电位,照此推算下来,如果要让从P1.7P1.0对应的灯为灭亮灭灭亮亮灭亮,则端口的电位应为低高低低高高低高,写成二进制八位数,就是01001101

要实现这个,在单片机的设计上,就只需要把01001101送到P1端口就完成任务了。

这里就有一个问题,这个01001101在送到P1端口之前它在哪儿呢?在不同的位置,送法也肯定是不同的。

我们先说一种情况,就是我们的程序不做其它任何事,就只是实现这个功能,那么,最简单的做法,就是这个01001101已存在程序存储器中某个单元中,我们把程序存储器中的这个数,直接抓出来送到P1端口就行了,用语言描述其过程,就是:将程序存储器中的某一单元中的数据,当作数值,送入P1

按这个思路,在单片机设计中,要考虑几个问题,首先要考虑的是,让其如何区分命令与数值,因为,在计算机中,命令与数值,都是以二进制数来表示的,而51单片机采用哈佛结构,存在程序存储器中的同一个区域,且并未做命令与数值的区别性标记,也就是说,单片机控制器即不可能根据数据的特征来区分,也不可能根据其存在的物理区域来区分是命令还是数值,所以必然另想办法。

在这个问题上,单片机是这样设计的:程序的执行过程,是随时钟一步一步地按程序存储器单元顺序从低到高地从程序存储中取出数据并送入控制器中(遇跳转指令则在跳转后,从跳转指令指定的地址开始,依然按这个规则进行),程序存储器中的第一个单元中的数据是必然的命令,每一命令紧随其后的单元中的数据是否是数值、如果是,有多少个单元是数值,均由这条命令来指定,取完这一个或几个单元的数据后,再下一个单元中的数据,规定为命令。

我们可以认为单片机可以采用这样一种方式进行处理(当然还有其它方式):从程序存储器中取出的数据,进入控制通道,通道最前面有两道并联且不会同时开同时关的门,命令进甲道门,数值进乙道门。对于将数值送入P1这个操作,其流程是:控制系统先收到将紧随其后第二个单元中的数据送入P1的命令后,关闭甲门并打开乙门,之后再根据紧随其后第一个单元中的数值所指向的P1的地址,将通向P1的门(而不是别的单元的门)全部打开,然后下一步,从程序存储器中收到的数据就直接送到P1口了。如果仅有一个单元的数据被当作数值传到端口,则这三步操作完成后,乙门关而甲门开,接收下一个数据并被当作命令送入控制器中。

再一个要解决的问题是:这个要被送入的数值存在哪个单元中?

单片机数值存放地的设计是这样的:前面说过,它们与命令一同都存在程序存储器中,那么存放的方式就有两种,一种是可以由程序员任意存放于任一单元中,也可以就近存放于操作这个数值的命令之后。程序员在编程中可以任选取这两者中的任何一种进行数值存放,对了,后一种,就是汇编语言中说的立即数,也就是上面所说到的那种处理方式。

对于前一种存放方式,我们在操作时,就必须在编程中确定并输入其地址。至于立即数方式,则直接在立即数的操作命令中指明:这个数你们别再找了,就在我身后,也就是说,前一种方式,需要向控制系统专门提交(源数值)地址值,而立即数方式,不再需要提交地址值。为什么有这两种设计?立即数方式具有一定的专用性,命令少速度快,前一种方式具有通用性但命令长而执行时间相对较长。

下面,我们就来说具体编程,流程是找到那个将立即数送入P1的命令,然后写出这个命令,再后面跟一个立即数,这个就可以运行了。但这里还有一个问题:51机是8位机,其命令最多只能做到256条,所以,理论上,在单片机设计时,可以设计这么一条直接将立即数送入P1的命令,但是,因为256条的限制,综合考虑,设计人员不一定会给这条命令让路也就是说,这个单片机中不一定有这条命令。

下面,我们就来在51单片机的机器指令中找一下,看有没有这么一条指令:我们知道,单片机中,P1属于片内存储单元,其地址是90H,而在其指令系统表中,我们找到了一条MOV direct,#data,这条汇编指令的意思是将立即数传递到片内存储器中指定的一个单元,这个命令的机器码就是75H,这个机器码本身已经指明紧随之后的程序存储器单元中的两个数据为数值而不是命令,其中第一个数值direct是接收数值的单元地址,第二个数值就是要被传送的数值010011014DH),三字节指令。这个指令比” MOV A, #data”要多一个字节,它的机器命令是74H,这个命令中,已经指明了接收数据的地址为RAM中的E0H单元,也就是累加器A,所以,不用你再写一个接收地址了,二字节指令,专用性很强,就是针对累加器A,当然,你也可以写成那个三字节指令,也当然,这样做真不当然。

好了,写汇编程序:“MOV direct,4DH”,再加一条让其不断重复执行的程序,汇编编程就完成了。当然,别忘了end,至于前面的ORG,可以不用,如果用的话,要么地址是0000H,要么前面还得加一条跳转

不想写汇编,那就可以直接写机器语言:“75H  90H 4DH”,再加一条跳转指令,完成。

如果你用汇编,那就得用上汇编工具;如果你用机器语言,那你可以不用汇编工具,直接在烧写工具中,在第一个单元(0000H)中写入75H,在第二个单元中写入90H,在第三个单元中写入4DH,再写入一个跳转到第一个单元的指令,搞定,然后烧入、运行。当然,在这个程序中,你也可以不加跳转,但程序存储器中后面的单元必须全部清空,否则结果不定。

说明:在以上例子中,单片机中控制器的作用,就只是开门关门。控制器接收到命令后,就是根据这个命令开相应的门关相应的门。

PS:精神不太好,发一下神经,如果有错,千万要指出,免得害人啊。

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏7 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:253767 发表于 2019-10-25 07:16 | 只看该作者
谢谢分享!!!好资料,51黑有你更精彩!!!
回复

使用道具 举报

板凳
ID:266429 发表于 2019-10-25 08:56 | 只看该作者
     这里介绍了MOV direct,#data  和  MOV A, #data这两条指令,其实,在单片机设计上,前一条指令完全覆盖了后一条指令,也就是说,即便机器并没有设计后一条指令,也能用前一条指令来实现这个功能。但设计者为什么又要设计这样一条指令呢?一个目的,就是为了让机器能以较短的时间实现这个把数值送到累加器A中的功能。
     那能不能在设计中也加上一条这样直接把数值送入P1中的二字节指令呢?当然可以,问题是,目前256个机器指令已经满额了,如果要加上这么一条指令,那就得有一个现有的指令退出。也不是不能这样做,如果你这样做了,基本就是一个半新的指令集,与现有51机指令集就会出现兼容问题,除非你以后的软硬件全部重新另起炉灶。
回复

使用道具 举报

地板
ID:266429 发表于 2019-10-25 15:42 | 只看该作者
"至于前面的ORG,可以不用,如果用的话,要么地址是0000H,要么前面还得加一条跳转",这个的确错了,应该删除。
回复

使用道具 举报

5#
ID:266429 发表于 2019-10-28 21:01 | 只看该作者
     所谓“立即数”,是指机器指令(机器码)中已指定了所需传输的数值型数据在程序存储器中所在单元地址的一类情况,换一种说法,就是源数据所在程序存储器地址在机器码中已指定。
     同样的,我们可以思考,对于目标地址,应该同样可以在机器码中指定,“ MOV A, #data”就是一例。
     光看汇编指令,其实会让人产生混乱,你无法直观地明白“MOV A, #data” 和 “MOV direct,#data  ”的区别在哪,所以,学习汇编语言,最好也要同时学习机器码的来由,理解之后,汇编真的是很简单。(在这里,我想说的是,这两条汇编指令,都采用MOV来标记,是不太合理的)
    再说了,机器码,其实道理很单调,很好理解。
回复

使用道具 举报

6#
ID:299519 发表于 2019-10-29 15:59 | 只看该作者
汇编太残忍了。。。。。。
回复

使用道具 举报

7#
ID:266429 发表于 2019-10-29 17:05 | 只看该作者
      51机的数据传输指令,在其学习中是一个难点。为了便于理解,我们重新分个类:
      存储数据的地方,我们先来理解两大类,一类就是程序存储器,一类就是单片机内的256字节的RAM,先不谈片外RAM,也不谈间接寻址的那128字节的RAM。数据的传输,无非是程序存储器到RAM一类、RAM内部之间相互传输一类,也就这两类。
      程序存储器到RAM类,前面已经说过了,程序存储器中的源数据有两种存法,其中的“立即数”法已经讲解过了。“立即数”这里面也分为两类,就是通用传输指令和专用传输指令,你如果不计较运行效率又想偷懒,只要记住通用传输机器指令就行了,它能完成立即数到任何一个RAM单元的传输;如果你想规矩学,那你无非是去找相对于目标地址,有没有专用传输指令,比如说送到累加器A,那个是有专用指令的,而如果是传到B寄存器,查遍其指令执行代码表,它没有,你只能老老实实地输入其地址F0H。
      256字节RAM之间的数据传输类,首先是设计有有通用类指令,即这类指令机器码,可以完成任何两个RAM单元之间的数据传输,只要在后面写上源地址和目的地址就可以了,这个就是完全直接寻址,即“MOV direct1,direct2”;第二种是源地址或目的地址之一,已在机器码中表明,算是半直接寻址;第三种是源地址和目的地址,均在机器码中指定,这个是所谓的寄存器寻址,单字节指令,速最快。这第二种和第三种是否设计有相应的专用指令,自己可以查,如Rn与累加器之间的相互数据传输就设计了第三种,累加器与任一RAM之间相互传输就设计了第二种。
      这些东西看懂后,其它的传输指令,很容易自己去理解。
      PS1:我这里归类寻址的方式,与教材上的有些不同,我不知它们把“MOV direct1,direct2”给归到哪类去了。不过没关系,你们觉得哪种好理解就学哪种就行。
      PS2:51机现有指令集未必设计得就合理,但没法,它已通用了。如果你想设计专用芯片,你完全可以把这个指令集丢到太平洋,然后自己设计一套高效的专用指令集,设计完成后,你想不想申请专利,那是你自己的事。但是作为8位机,最多也只能设计256条指令,如果是16位机及以上,那可以设计的指令,就多太多了。
      PS3:有没有错啊,请版主、大侠们千万指出啊,我真的很怕害人。

回复

使用道具 举报

8#
ID:266429 发表于 2019-10-29 17:27 | 只看该作者
      所谓的“寄存器寻址”,因为在指令中直接指定了源地址和目标地址,所以,称其为“完全指令寻址”,似乎更合适。
回复

使用道具 举报

9#
ID:282850 发表于 2019-10-30 16:09 | 只看该作者
写的很好,思路清晰,一定是一个汇编高手。我不懂汇编,知道 MOV ADD JN几个字,三行汇编都读不了。文章收藏了。
回复

使用道具 举报

10#
ID:266429 发表于 2019-11-4 11:43 | 只看该作者
myemcu 发表于 2019-10-29 15:59
汇编太残忍了。。。。。。

      我觉得汇编本身并不怎么残忍,而是在于汇编教材,没一本合适的,所有教材千篇一律,没一本是象其它语言教材循序渐进的,而是一下子把所有概念、功能和指令全抛给你,让你不知从哪里下手。
      单片机的设计,是根据需要一个功能一个功能地弄出来的,所谓的指令,也是这样一条一条根据需要的设计出来的,如果我们跟随单片机设计者在当初做设计时的思路来学习,则无论是单片机的硬件方面的设计原理,还是指令的功能设计原理,都可以弄得很清楚,这样学起来,不一定比C难。
      不懂汇编,在单片机编程方面,始终是个缺憾。我学汇编,是因为C总是让人有雾里看花的感觉,很不舒服。
回复

使用道具 举报

11#
ID:612635 发表于 2019-11-4 12:30 | 只看该作者
我是先会汇编,再学C语言。汇编的效率高,但不好移植。
回复

使用道具 举报

12#
ID:266429 发表于 2019-11-4 20:16 | 只看该作者
huangxiangzhong 发表于 2019-11-4 12:30
我是先会汇编,再学C语言。汇编的效率高,但不好移植。

的确如此,但学了它,就能基本理解单片机的工作原理,对打通C、应用C帮助很大。而且,有些底层的东西,用C比较麻烦,汇编就简单多了。
回复

使用道具 举报

13#
ID:514974 发表于 2019-11-5 08:22 | 只看该作者
很好,思路清晰,文章收藏了!
谢谢分享!好资料!!
回复

使用道具 举报

14#
ID:266429 发表于 2019-11-5 11:32 | 只看该作者
我们把前面说的数据传输,从单片机设计者的角度疏理一下(以下仅以8位数据即一字节数为例):
首先说程序程序器到RAM的数据传输。
单片机在芯片功能设计中,很重要的也是最基本的一个功能项目,就是将程序存储器中保存的数值型数据,送入单片机内的RAM中。设计者首先想到的,是设计这样一条指令:这条指令指出要从程序存储器中将某一地址中的数据送入RAM,而源地址和目标地址均在这条指令后明确给出,这样就成为“指令+目标地址+源地址”式指令。然后,设计想到为了缩短指令执行时长,就想到设计另一种指令:将源地址包含在指令中,目标地址仍然在指令后给出,这就是“指令(含源地址)+目标地址”式指令,也就是所谓的立即寻址方式。根据这个思路,再设计一种指令以继续缩短运行时长,将源地址和目标地址均包含在指令中,成为“指令(含源地址+目标地址)”,这个算是立即寻址方式中的一个子集。还有很重要的一种,也是我们自然会想到的,就是将目标地址包含在指令中,而源地址则仍在指令后明确给出。这样,我们就有了实现程序存储器到RAM的四种设计传输指令的思路。
设计思路归设计思路,在实际设计中,因为要综合考虑很多因素,所以,我们所用的单片机中,这四类指令不一定都有,实际上,第一种方式并没有设计相应的指令。第二种方式有,就是“MOV direct,#data”,时钟3,对应一个机器码,可以传输到任意RAM单元;第四种方式也有,但仅设计了对累加器A,也就是说,如果指定了源地址,那这个数据只能传输给累加器A,也就是说,这个数据如果想传输到其它地方,只能先通过A中转,这个就是两条MOVX指令,时钟4或5,对应2个机器码,所谓的变址寻址方式;第三种方式也有,仅设计了对累加器A及工作寄存器Rn(这里暂不谈间接RAM,这个间接RAM是后面的设计才考虑进去的),就是“MOV A,#data”和“MOV Rn,#data”,时钟2,对应1+8个机器码(n从0到7,每个R对应一个机器码)。
PS1:如果你觉得上面的设计不合理,你完全可以自己设计芯片,当然,想推广出去比较难,估计也只能你自己用了。
PS2:以前说过,每一条机器码,在芯片中都对应有一套硬件电路,所以,指令越多,则芯片的晶体管数越多。
回复

使用道具 举报

15#
ID:266429 发表于 2019-11-6 09:42 | 只看该作者
      补充一点:程序存储器到RAM的数据传输指令的设计,很重要的一点是要考虑到51机的程序存储器是十六位地址,片内RAM是八位地址。

      下面我们疏理一下单元地址(包括源和目标)的给出方式。
      其实可以分为两大类,第一类是在机器指令码中包含,第二类是在机器指令码后面给出地址值。
第一类往下可以这样细分类:机器码中包含源与目标地址、机器码中只包含源地址、机器码中只包含目标地址,三种。
      第二类往下可以有两种细分类方法:一种就是按上面的分法,另一种分类法是重点,分为以下三种:
      1.        直接给出地址值。汇编中的“direct”就是地址值;
      2.        将地址值放在某一RAM单元中。51机中的“某一RAM”其实只有2X4个,就是Ri,再加一个十六位的DPTR;
      3.        该地址值需经计算得出,得出的结果放在某一RAM单元中,然后再用第2种指令来寻址传输,但这样做就用到了多个指令了,不能算作一条机器指令,所以,就设计这样一类指令:将计算命令包含在这条指令中,即这条指令明确指明要进行计算的原始数据在哪些单元中、要进行什么样的计算、计算完成后的结果直接作为地址值(包含指明物理存储器的种类)去寻址并进行传输,一个字节就全部搞定,即为一字节指令。这个其实就是51机指令中的所谓的变址寻址,两条机器指令,对应两条汇编指令,指明物理存储器为片内程序存储器,也就是说仅针对片内程序存储器的寻址,至于对RAM的,对不起,没设计这样的指令,只能自己去编程计算地址值,然后采用第2种来寻址并传输。
回复

使用道具 举报

16#
ID:638805 发表于 2019-11-9 19:37 | 只看该作者
支持分享
回复

使用道具 举报

17#
ID:477152 发表于 2019-11-30 19:50 | 只看该作者
思路清晰,文章收藏了!
回复

使用道具 举报

18#
ID:266429 发表于 2020-1-6 08:47 | 只看该作者
汇编指令中的地址,包括源地址和目标地址,还有一个很重要的分类方法,那就是地址值的可变性和不可变性,可变地址类,其地址值在程序运行过程中是可以通过程序运行计算得出的,而不可变地址类,则只能在编程时由程序员给出唯一值。如MOV A,direct中,A是不可变地址,direct则是可变地址;JB bit,rel中,bit和rel都是不可变地址。
可变地址类,在编程中具有灵活性;不可变地址,则执行周期短,但编程中实在是不灵活。
回复

使用道具 举报

19#
ID:681890 发表于 2020-1-6 09:01 | 只看该作者
真是个好平台
回复

使用道具 举报

20#
ID:681905 发表于 2020-1-6 09:18 | 只看该作者
谢谢大佬
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表