平凡的单片机教程

push什么意思  时间:2021-02-01  阅读:()

自学单片机第1页共62页1原作:平凡的单片机平凡的单片机教程自学单片机第2页共62页2目录单片机教程第1课:单片机概述.
3单片机教程第2课:单片机的内部、外部结构(一)3单片机教程第3课:几个基本概念.
5单片机教程第4课:第一个小程序.
7单片机教程第5课:延时程序分析.
8单片机教程第6课:单片机的内外部结构分析(四)10单片机教程第7课:单片机内部结构分析(五)11单片机第8课(寻址方式与指令系统)14单片机教程第9课:数据传递指令.
16单片机教程第10课数据传递类指令指令.
18单片机教程第11课:算术运算类指令.
20单片机教程第12课:逻辑运算类指令:22单片机教程第13课:逻辑与指令.
24单片机第14课:条件转移指令.
26单片机第15课:位及位操作指令.
28单片机教程第16课:计数器与定时器.
30单片机教程第17课:定时/计数器的方式控制字.
31单片机第18课:中断系统.
33单片机教程第19课:定时、中断练习一.
36单片机教程第20课:定时/计数器实验2.
39单片机教程第21课:串行接口.
40单片机教程第22课:串行口应用编程实例.
44单片机教程第23课:LED数码显示器的连接与编程.
46单片机教程第24课:动态扫描显示接口.
49单片机教程第25课:键盘接口与编程.
53单片机教程第26课:矩阵式键盘接口技术及编程.
56单片机教程第27课:初学单片机几个不易掌握的概念.
59单片机教程第28课:单片机音乐程序的设计与实验.
61平凡的单片机教程自学单片机第3页共62页3单片机教程第1111课:单片机概述1、何谓单片机一台能够工作的计算机要有这样几个部份构成:CPU(进行运算、控制)、RAM(数据存储)、ROM(程序存储)、输入/输出设备(例如:串行口、并行输出口等).
在个人计算机上这些部份被分成若干块芯片,安装一个称之为主板的印刷线路板上.
而在单片机中,这些部份,全部被做到一块集成电路芯片中了,所以就称为单片(单芯片)机,而且有一些单片机中除了上述部份外,还集成了其它部份如A/D,D/A等.
天!
PC中的CPU一块就要卖几千块钱,这么多东西做在一起,还不得买个天价!
再说这块芯片也得非常大了.

不,价格并不高,从几元人民币到几十元人民币,体积也不大,一般用40脚封装,当然功能多一些单片机也有引脚比较多的,如68引脚,功能少的只有10多个或20多个引脚,有的甚至只8只引脚.
为什么会这样呢功能有强弱,打个比方,市场上面有的组合音响一套才卖几百块钱,可是有的一台功放机就要卖好几千.
另外这种芯片的生产量很大,技术也很成熟,51系列的单片机已经做了十几年,所以价格就低了.
既然如此,单片机的功能肯定不强,干吗要学它呢话不能这样说,实际工作中并不是任何需要计算机的场合都要求计算机有很高的性能,一个控制电冰箱温度的计算机难道要用PIII应用的关键是看是否够用,是否有很好的性能价格比.
所以8051出来十多年,依然没有被淘汰,还在不断的发展中.
2、MCS51单片机和8051、8031、89C51等的关系我们平常老是讲8051,又有什么8031,现在又有89C51,它们之间究竟是什么关系MCS51是指由美国INTEL公司(对了,就是大名鼎鼎的INTEL)生产的一系列单片机的总称,这一系列单片机包括了好些品种,如8031,8051,8751,8032,8052,8752等,其中8051是最早最典型的产品,该系列其它单片机都是在8051的基础上进行功能的增、减、改变而来的,所以人们习惯于用8051来称呼MCS51系列单片机,而8031是前些年在我国最流行的单片机,所以很多场合会看到8031的名称.
INTEL公司将MCS51的核心技术授权给了很多其它公司,所以有很多公司在做以8051为核心的单片机,当然,功能或多或少有些改变,以满足不同的需求,其中89C51就是这几年在我国非常流行的单片机,它是由美国ATMEL公司开发生产的.
以后我们将用89C51来完成一系列的实验.
单片机教程第2222课:单片机的内部、外部结构((((一))))一、单片机的外部结构拿到一块芯片,想要使用它,首先必须要知道怎样连线,我们用的一块称之为89C51的芯片,下面我们就看一下如何给它连线.
1、电源:这当然是必不可少的了.
单片机使用的是5V电源,其中正极接40引脚,负极(地)接20引脚.
2、振蒎电路:单片机是一种时序电路,必须提供脉冲信号才能正常工作,在单片机内部已集成了振荡器,使用晶体振荡器,接18、19脚.
只要买来晶振,电容,连上就可以了,按图1接上即可.
3、复位引脚:按图1中画法连好,至于复位是何含义及为何需要复要复位,在单片机功能中介绍.

4、EA引脚:EA引脚接到正电源端.
至此,一个单片机就接好,通上电,单片机就开始工作了.
我们的第一个任务是要用单片机点亮一只发光二极管LED,显然,这个LED必须要和单片机的某个引脚相连,否则单片机就没法控制它了,那么和哪个引脚相连呢单片机上除了刚才用掉的5个引脚,还有35个,我们将这个LED和1脚相连.
(见图1,其中R1是限流电阻)按照这个图的接法,当1脚是高电平时,LED不亮,只有1脚是低电平时,LED才发亮.
因此要1脚我们要能够控制,也就是说,我们要能够让1引脚按要求变为高或低电平.
即然我们要控制1脚,就得给它起个名字,总不能就叫它一脚吧叫它什么名字呢设计51芯片的INTEL公司已经起好了,就叫它P1.
0,这是规定,不可以由我们来更改.
平凡的单片机教程自学单片机第4页共62页4图1名字有了,我们又怎样让它变'高'或变'低'呢叫人做事,说一声就可以,这叫发布命令,要计算机做事,也得要向计算机发命令,计算机能听得懂的命令称之为计算机的指令.
让一个引脚输出高电平的指令是SETB,让一个引脚输出低电平的指令是CLR.
因此,我们要P1.
0输出高电平,只要写SETBP1.
0,要P1.
0输出低电平,只要写CLRP1.
0就可以了.
现在我们已经有办法让计算机去将P10输出高或低电平了,但是我们怎样才能计算机执行这条指令呢总不能也对计算机也说一声了事吧.
要解决这个问题,还得有几步要走.
第一,计算机看不懂SETBCLR之类的指令,我们得把指令翻译成计算机能懂的方式,再让计算机去读.
计算机能懂什么呢它只懂一样东西——数字.

因此我们得把SETBP1.
0变为(D2H,90H),把CLRP1.
0变为(C2H,90H),至于为什么是这两个数字,这也是由51芯片的设计者--INTEL规定的,我们不去研究.
第二步,在得到这两个数字后,怎样让这两个数字进入单片机的内部呢这要借助于一个硬件工具"编程器".
我们将编程器与电脑连好,运行编程器的软件,然后在编缉区内写入(D2H,90H)见图2,写入……好,拿下片子,把片子插入做好的电路板,接通电源……什么灯不亮这就对了,因为我们写进去的指令就是让图2P10输出高电平,灯当然不亮,要是亮就错了.
现在我们再拨下这块芯片,重新放回到编程器上,将编缉区的内容改为(C2H,90H),也就是CLRP1.
0,写片,拿下片子,把片子插进电路板,接电,好,灯亮了.
因为我们写入的()就是让P10输出低电平的指令.
这样我们看到,硬件电路的连线没有做任何改变,只要改变写入单片机中的内容,就可以改变电路的输出效果.
二、单片机内部结构分析我们来思考一个问题,当我们在编程器中把一条指令写进单片要内部,然后取下单片机,单片机就可以执行这条指令,那么这条指令一定保存在单片机的某个地方,并且这个地方在单片机掉电后依然可以保持这条指令不会丢失,这是个什么地方呢这个地方就是单片机内部的只读存储器即ROM(READONLYMEMORY).
为什么称它为只读存储器呢刚才我们不是明明把两个数字写进去了吗原来在89C51中的ROM是一种电可擦除的ROM,称为FLASHROM,刚才我们是用的编程器,在特殊的条件下由外部设备对ROM进行写的操作,在单片机正常工作条件下,只能从那面读,不能把数据写进去,所以我们还是把它称为ROM.
平凡的单片机教程自学单片机第5页共62页5单片机教程第3333课:几个基本概念数的本质和物理现象.
我们知道,计算机可以进行数学运算,这可令我们非常的难以理解,计算机吗,我们虽不了解它的组成,但它总只是一些电子元器件,怎么可以进行数学运算呢我们做数学题如37+45是这样做的,先在纸上写37,然后在下面写45,然后大脑运算,最后写出结果,运算的原材料:37、45和结果:82都是写在纸上的,计算机中又是放在什么地方呢为了解决这个问题,先让我们做一个实验:这里有一盏灯,我们知道灯要么亮,要么不亮,就有两种状态,我们可以用'0'和'1'来代替这两种状态,规定亮为'1',不亮为'0'.
现在放上两盏灯,一共有几种状态呢我们列表来看一下:状态表达00011011请大家自已写上3盏灯的情况000001010011100101110111我们来看,这个000,001,101不就是我们学过的的二进制数吗本来,灯的亮和灭只是一种物理现象,可当我们把它们按一按的顺序排更好后,灯的亮和灭就代表了数字了.
让我们再抽象一步,灯为什么会亮呢看电路1,是因为输出电路输出高电平,给灯通了电.
因此,灯亮和灭就可以用电路的输出是高电平还是低电平来替代了.
这样,数字就和电平的高、低联系上了.
(请想一下,我们还看到过什么样的类似的例子呢(海军之)灯语、旗语,电报,甚至红、绿灯)位的含义:通过上面的实验我们已经知道:一盏灯亮或者说一根线的电平的高低,可以代表两种状态:0和1.
实际上这就是一个二进制位,因此我们就把一根线称之为一"位",用BIT表示.
字节的含义:一根线可以表于0和1,两根线可以表达00,01,10,11四种状态,也就是可以表于0到3,而三根可以表达0-7,计算机中通常用8根线放在一起,同时计数,就可以表过到0-255一共256种状态.
这8根线或者8位就称之为一个字节(BYTE).
不要问我为什么是8根而不是其它数,因为我也不知道.
(计算机世界是一个人造的世界,不是自然界,很多事情你无法问为什么,只能说:它是一种规定,大家在以后的学习过程中也要注意这个问题)存储器的工作原理:1、存储器构造存储器就是用来存放数据的地方.
它是利用电平的高低来存放数据的,也就是说,它存放的实际上是电平的高、低,而不是我们所习惯认为的1234这样的数字,这样,我们的一个谜团就解开了,计算机也没什么神秘的吗.
图2图3平凡的单片机教程自学单片机第6页共62页6让我们看图2.
这是一个存储器的示意图:一个存储器就象一个个的小抽屉,一个小抽屉里有八个小格子,每个小格子就是用来存放"电荷"的,电荷通过与它相连的电线传进来或释放掉,至于电荷在小格子里是怎样存的,就不用我们操心了,你可以把电线想象成水管,小格子里的电荷就象是水,那就好理解了.
存储器中的每个小抽屉就是一个放数据的地方,我们称之为一个"单元".
有了这么一个构造,我们就可以开始存放数据了,想要放进一个数据12,也就是00001100,我们只要把第二号和第三号小格子里存满电荷,而其它小格子里的电荷给放掉就行了(看图3).
可是问题出来了,看图2,一个存储器有好多单元,线是并联的,在放入电荷的时候,会将电荷放入所有的单元中,而释放电荷的时候,会把每个单元中的电荷都放掉,这样的话,不管存储器有多少个单元,都只能放同一个数,这当然不是我们所希望的,因此,要在结构上稍作变化,看图2,在每个单元上有个控制线,我想要把数据放进哪个单元,就给一个信号这个单元的控制线,这个控制线就把开关打开,这样电荷就可以自由流动了,而其它单元控制线上没有信号,所以开关不打开,不会受到影响,这样,只要控制不同单元的控制线,就可以向各单元写入不同的数据了,同样,如果要某个单元中取数据,也只要打开相应的控制开关就行了.
2、存储器译码那么,我们怎样来控制各个单元的控制线呢这个还不简单,把每个单元元的控制线都引到集成电路的外面不就行了吗事情可没那么简单,一片27512存储器中有65536个单元,把每根线都引出来,这个集成电路就得有6万多个脚不行,怎么办要想法减少线的数量.
我们有一种方法称这为译码,简单介绍一下:一根线可以代表2种状态,2根线可以代表4种状态,3根线可以代表几种,256种状态又需要几根线代表8种,8根线,所以65536种状态我们只需要16根线就可以代表了.
3、存储器的选片及总线的概念至此,译码的问题解决了,让我们再来关注另外一个问题.
送入每个单元的八根线是用从什么地方来的呢它就是从计算机上接过来的,一般地,这八根线除了接一个存储器之外,还要接其它的器件,如图4所示.
这样问题就出来了,这八根线既然不是存储器和计算机之间专用的,如果总是将某个单元接在这八根线上,就不好了,比如这个存储器单元中的数值是0FFH另一个存储器的单元是00H,那么这根线到底是处于高电平,还是低电平岂非要打架看谁历害了所以我们要让它们分离.
办法当然很简单,当外面的线接到集成电路的引脚进来后,不直接接到各单元去,中间再加一组开关(参考图4)就行了.
平时我们让开关打开着,如果确实是要向这个存储器中写入数据,或要从存储器中读出数据,再让开关接通就行了.
这组开关由三根引线选择:读控制端、写控制端和片选端.
要将数据写入片中,先选中该片,然后发出写信号,开关就合上了,并将传过来的数据(电荷)写入片中.
如果要读,先选中该片,然后发出读信号,开关合上,数据就被送出去了.
注意图4,读和写信号同时还接入到另一个存储器,但是由于片选端不同,所以虽有读或写信号,但没有片选信号,所以另一个存储器不会"误会"而开门,造成冲突.
那么会不同时选中两片芯片呢只要是设计好的系统就不会,因为它是由计算控制的,而不是我们人来控制的,如果真的出现同时出现选中两片的情况,那就是电路出了故障了,这不在我们的讨论之列.
从上面的介绍中我们已经看到,用来传递数据的八根线并不是专用的,而是很多器件大家共用的,所以我们称之为数据总线,总线英文名为BUS,总即公交车道,谁者可以走.
而十六根地址线也是连在一起的,称之为地址总线.
半导体存储器的分类按功能可以分为只读和随机存取存储器两大类.
所谓只读,从字面上理解就是只可以从里面读,不能写进去,它类似于我们的书本,发到我们手回之后,我们只能读里面的内容,不可以随意更改书本上的内容.
只读存储器的英文缩写为ROM(READONLYMEMORY)所谓随机存取存储器,即随时可以改写,也可以读出里面的数据,它类似于我们的黑板,我可以随时写东西上去,也可以用黑板擦擦掉重写.
随机存储器的英文缩写为RAM(READRANDOMMEMORY)这两种存储器的英文缩写一定要记牢.
注意:所谓的只读和随机存取都是指在正常工作情况下而言,也就是在使用这块存储器的时候,而不是指制造这块芯片的时候.
否则,只读存储器中的数据是怎么来的呢其实这个道理也很好理解,书本拿到我们手里是不能改了,可以当它还是原材料——白纸的时候,当然可以由印刷厂印上去了.

顺便解释一下其它几个常见的概念.
平凡的单片机教程自学单片机第7页共62页7PROM,称之为可编程存储器.
这就象我们的练习本,买来的时候是空白的,可以写东西上去,可一旦写上去,就擦不掉了,所以它只能用写一次,要是写错了,就报销了.
EPROM,称之为紫外线擦除的可编程只读存储器.
它里面的内容写上去之后,如果觉得不满意,可以用一种特殊的方法去掉后重写,这就是用紫外线照射,紫外线就象"消字灵",可以把字去掉,然后再重写.
当然消的次数多了,也就不灵光了,所以这种芯片可以擦除的次数也是有限的——几百次吧.

FLASH,称之为闪速存储器,它和EPROM类似,写上去的东西也可以擦掉重写,但它要方便一些,不需要光照了,只要用电学方法就可以擦除,所以就方便许多,而且寿面也很长(几万到几十万次不等).
再次强调,这里的所有的写都不是指在正常工作条件下.
不管是PROM、EPROM还是FLASHROM,它们的写都要有特殊的条件,一般我们用一种称之为"编程器"的设备来做这项工作,一旦把它装到它的工作位置,就不能随便改写了.
单片机教程第4444课:第一个小程序上一次我们的程序实在是没什么用,要灯亮还要重写一下片子,下面我们要让灯不断地闪烁,这就有一定的实用价值了,比如可以把它当成汽车上的一个信号灯用了.
怎样才能让灯不断地闪烁呢实际上就是要灯亮一段时间,再灭一段时间,也就是说要P10不断地输出高和低电平.
怎样实现这个要求呢请考虑用下面的指令是否可行:SETBP10CLRP10……这是不行的,有两个问题,第一,计算机执行指令的时间很快,执行完SETBP10后,灯是灭了,但在极短时间(微秒级)后,计算机又执行了CLRP10指令,灯又亮了,所以根本分辨不出灯曾灭过.
第二,在执行完CLRP10后,不会再去执行SETBP10指令,所以以后再也没有机会让灭了.
为了解决这两个问题,我们可以做如下设想,第一,在执行完SETBP10后,延时一段时间(几秒或零点几秒)再执行第二条指令,就可以分辨出灯曾灭过了.
第二在执行完第二条指令后,让计算机再去执行第一条指令,不断地在原地兜圈,我们称之为"循环",这样就可以完成任务了.
以下先给出程序(后面括号中的数字是为了便于讲解而写的,实际不用输入):;主程序:LOOP:SETBP10;(1)LCALLDELAY;(2)CLRP10;(3)LCALLDELAY;(4)AJMPLOOP;(5);以下子程序DELAY:MOVR7,#250;(6)D1:MOVR6,#250;(7)D2:DJNZR6,D2;(8)DJNZR7,D1;(9)RET;(10)END;(11)按上面的设想分析一下前面的五条指令.
第一条是让灯灭,第二条应当是延时,第三条是让灯亮,第四条和第二条一模一样,也是延时,第五条应当是转去执行第一条指令.
第二和第四条实现的原理稍后谈,先看第五条,LJMP是一条指令,意思是转移,往什么地方转移呢后面跟的是LOOP,看一下,什么地方还有LOOP,对了,在第一条指令的前面有一个LOOP,所以很直观地,我们可以认识到,它要转到第一条指令处.
这个第一条指令前面的LOOP被称之为标号,它的用途就是给这一行起一个名字,便于使用.
是否一定要给它起名叫LOOP呢当然不是,起什么名字,完全由平凡的单片机教程自学单片机第8页共62页8编程序的人决定,可以称它为A,X等等,当然,这时,第五条指令LJMP后面的名字也得跟着改了.
第二条和第四条指令的用途是延时,它是怎样实现的呢指令的形式是LCALL,这条指令称为调用子程序指令,看一下指令后面跟的是什么,DELAY,找一下DELAY,在第六条指令的前面,显然,这也是一个标号.
这条指令的作用是这样的:当执行LCALL指令时,程序就转到LCALL后面的标号所标定的程序处执行,如果在执行指令的过程中遇到RET指令,则程序就返回到LCALL指令的下面的一条指令继续执行,从第六行开始的指令中,可以看到确实有RET指令.
在执行第二条指令后,将转去执行第6条指令,而在执行完6,7,8,9条指令后将遇到第10条令:RET,执行该条指令后,程序将回来执行第三条指令,即将P10清零,使灯亮,然后又是第四条指令,执行第四条指令就是转去执行第6,7,8,9,10条指令,然后回来执行第5条指令,第5条指令就是让程序回到第1条开始执行,如此周而复始,灯就在不断地亮、灭了.
在标号DELAY标志的这一行到RET这一行中的所有程序,这是一段延时程序,大概延时零点几秒,至于具体的时间,以后我们再学习如何计算.
程序的最后一行是END,这不是一条指令,它只是告诉我们程序到此结束,它被称为"伪指令".
单片机内部结构分析:为了知道延时程序是如何工作的,我们必需首先了解延时程序中出现的一些符号,就从R1开始,R1被称之为工作寄存器.
什么是工作寄存器呢让我们从现实生活中来找找答案.
如果出一道数学题:123+567,让你回答结果是多少,你会马上答出是690,再看下面一道题:123+567+562,要让你要上回答,就不这么容易了吧我们会怎样做呢如果有张纸,就容易了,我们先算出123+567=690,把690写在纸上,然后再算690+562得到结果是1552.
这其中1552是我们想要的结果,而690并非我们所要的结果,但是为了得到最终结果,我们又不得不先算出690,并记下来,这其实是一个中间结果,计算机中做运算和这个类似,为了要得到最终结果,往往要做很多步的中间结果,这些中间结果要有个地方放才行,把它们放哪呢放在前面提到过的ROM中可以吗显然不行,因为计算机要将结果写进去,而ROM是不可以写的,所以在单片机中另有一个区域称为RAM区(RAM是随机存取存储器的英文缩写),它可以将数据写进去.
特别地,在MCS-51单片机中,将RAM中分出一块区域,称为工作寄存器区单片机教程第五课:延时程序分析上一次课中,我们已经知道,程序中的符号R7、R6是代表了一个个的RAM单元,是用来放一些数据的,下面我们再来看一下其它符号的含义.
DELAY:MOVR7,#250;(6)D1:MOVR6,#250;(7)D2:DJNZR6,D2;(8)DJNZR7,D1;(9)RET;(10)MOV:这是一条指令,意思是传递数据.
说到传递,我们都很清楚,传东西要从一个人的手上传到另一个人的手上,也就是说要有一个接受者,一个传递者和一样东西.
从指令MOVR7,#250中来分析,R7是一个接受者,250是被传递的数,传递者在这条指令中被省略了(注意:并不是每一条传递指令都会省的,事实上大部份数据传递指令都会有传递者).
它的意义也很明显:将数据250送到R7中去,因此执行完这条指令后,R7单元中的值就应当是250.
在250前面有个#号,这又是什么意思呢这个#就是用来说明250就是一个被传递的东西本身,而不是传递者.
那么MOVR6,#250是什么意思,应当不用分析了吧.
平凡的单片机教程自学单片机第9页共62页9DJNZ:这是另一条指令,我们来看一下这条指令后面跟着的两个东西,一个是R6,一个是D2,R6我们当然已知是什么了,查一下D2是什么.
D2在本行的前面,我们已学过,这称之为标号.
标号的用途是什么呢就是给本行起一个名字.
DJNZ指令的执行过程是这样的,它将其后面的第一个参数中的值减1,然后看一下,这个值是否等于0,如果等于0,就往下执行,如果不等于0,就转移,转到什么地方去呢可能大家已猜到了,转到第二个参数所指定的地方去(请大家用自已的话讲一下这条语句是怎样执行的).
本条指令的最终执行结果就是,在原地转圈250次.
执行完了DJNZR6,D2之后(也就是R6的值等于0之后),就会去执行下面一行,也就是DJNZR7,D1,请大家自行分析一下这句话执行的结果.
(转去执行MOVR6,#250,同时R7中的值减1),最终DJNZR6,D2这句话将被执行250*250=62500次,执行这么多次同一条指令干吗就是为了延时.
一个问题:如果在R6中放入0,会有什么样的结果.
二、时序分析:前面我们介绍了延时程序,但这还不完善,因为,我们只知道DJNZR6,D2这句话会被执行62500次,但是执行这么多次需要多长时间呢是否满足我们的要求呢我们还不知道,所以下面要来解决这个问题.

先提一个问题:我们学校里什么是最重要的.
(铃声)校长可以出差,老师可以休息,但学校一日无铃声必定大乱.
整个学校就是在铃声的统一指挥下,步调一致,统一协调地工作着.
这个铃是按一定的时间安排来响的,我们可以称之为"时序��时间的顺序".
一个由人组成的单位尚且要有一定的时序,计算机当然更要有严格的时序.
事实上,计算机更象一个大钟,什么时候分针动,什么时候秒针动,什么时候时针动,都有严格的规定,一点也不能乱.
计算机要完成的事更复杂,所以它的时序也更复杂.
我们已知,计算机工作时,是一条一条地从ROM中取指令,然后一步一步地执行,我们规定:计算机访问一次存储器的时间,称之为一个机器周期.
这是一个时间基准,好象我们人用"秒"作为我们的时间基准一样,为什么不干脆用"秒",多好,很习惯,学下去我们就会知道用"秒"反而不习惯.
一个机器周期包括12个时钟周期.
下面让我们算一下一个机器周期是多长时间吧.
设一个单片机工作于12M晶振,它的时钟周期是1/12(微秒).
它的一个机器周期是12*(1/12)也就是1微秒.
(请计算一个工作于6M晶振的单片机,它的机器周期是多少).
MCS-51单片机的所有指令中,有一些完成得比较快,只要一个机器周期就行了,有一些完成得比较慢,得要2个机器周期,还有两条指令要4个机器周期才行.
这也不难再解,不是吗我让你扫地的执行要完成总得比要你完成擦黑板的指令时间要长.
为了恒量指令执行时间的长短,又引入一个新的概念:指令周期.
所谓指令周期就是指执行一条指令的时间.
INTEL对每一条指令都给出了它的指令周期数,这些数据,大部份不需要我们去记忆,但是有一些指令是需要记住的,如DJNZ指令是双周期指令.
下面让我们来计算刚才的延时.
首先必须要知道晶振的频率,我们设所用晶振为12M,则一个机器周期就是1微秒.
而DJNZ指令是双周期指令,所以执行一次要2个微秒.
一共执行62500次,正好125000微秒,也就是125毫秒.
练习:设计一个延时100毫秒的延时程序.
要点分析:1、一个单元中的数是否可以超过255.
2、如何分配两个数.
三、复位电路任何单片机在工作之前都要有个复位的过程,复位是什么意思呢它就象是我们上课之前打的预备铃.
预备铃一响,大家就自动地从操场、其它地方进入教室了,在这一段时间里,是没有老师干预的,对单片机来说,是程序还没有开始执行,是在做准备工作.
显然,准备工作不需要太长的时间,复位只需要5ms的时间就可以了.
如何进行复位呢只要在单片机的RST引脚上加上高电平,就可以了,按上面所说,时间不少于5ms.
为了达到这个要求,可以用很多种方法,这里提供一种供参考,见图1.
实际上,我们在上一次实验的图中已见到过了.
这种复位电路的工作原理是:通电时,电容两端相当于是短路,于是RST引脚上为高电平,然后电源通过电阻对电容充电,RST端电压慢慢下降,降到一定程序,即为低电平,单片机开始正常工作.
平凡的单片机教程自学单片机第10页共62页10单片机教程第六课:单片机的内外部结构分析(四)上两次我们做过两个实验,都是让P1.
0这个引脚使灯亮,我们可以设想:既然P1.
0可以让灯亮,那么其它的引脚可不可以呢看一下图1,它是8031单片机引脚的说明,在P1.
0旁边有P1.
1,P1.
2….
P1.
7,它们是否都可以让灯亮呢除了以P1开头的外,还有以P0,P2,P3开头的,数一下,一共是32个引脚,前面我们以学过7个引脚,加上这32个这39个了.
它们都以P字开头,只是后面的数字不一样,它们是否有什么联系呢它们能不能都让灯亮呢在我们的实验板上,除了P10之外,还有P11��P17都与LED相连,下面让我们来做一个实验,程序如下:MAIN:MOVP1,#0FFHLCALLDELAYMOVP1,#00HLCALLDELAYLJMPMAINDELAY:MOVR7,#250D1:MOVR6,#250D2:DJNZR6,D2DJNZR7,D1RETEND将这段程序转为机器码,用编程器写入芯片中,结果如何通电以后我们可以看到8只LED全部在闪动.
因此,P10��P17是全部可以点亮灯的.
事实上,凡以P开头的这32个引脚都是可以点亮灯的,也就是说:这32个引脚都可以作为输出使用,如果不用来点亮LED,可以用来控制继电器,可以用来控制其它的执行机构.
程序分析:这段程序和前面做过的程序比较,只有两处不一样:第一句:原来是SETBP1.
0,现在改为MOVP1,#0FFH,第三句:原来是CLRP1.
0,现在改为MOVP1.
0,#00H.
从中可以看出,P1是P1.
0��P1.
7的全体的代表,一个P1就表示了所有的这八个管脚了.
当然用的指令也不一样了,是用MOV指令.
为什么用这条指令看图2,我们把P1作为一个整体,就把它当作是一个存储器的单元,对一个单元送进一个数可以用MOV指令.
二、第四个实验除了可以作为输出外,这32个引脚还可以做什么呢下面再来做一个实验,程序如下:MAIN:MOVP3,#0FFHLOOP:MOVA,P3MOVP1,ALJMPLOOP先看一下实验的结果:所有灯全部不亮,然后我按下一个按钮,第()个灯亮了,再按下另一个按钮,第()个灯亮了,松开按钮灯就灭了.
从这个实验现象结合电路来分析一下程序.
从硬件电路的连线可以看出,有四个按钮被接入到P3口的P32,P33,P34,P35.
第一条指令的用途我们可以猜到:使P3口全部为高电平.
第二条指令是MOVA,P3,其中MOV已经见,是送数的意思,这条指令的意思就是将P3口的数送到A中去,我们可以把A当成是一个中间单元(看图3),第三句话是将A中的数又送到P1口去,第四句话是循环,就是不断地重复这个过程,这我们已见过.
当我们按下第一个按钮时,第(3)只灯亮了,所以P12口应当输出是低电平,为什么P12口会输出低电平呢我们看一下有什么被送到了P1口,只有从P3口进来的数送到A,又被送到了P1口,所以,肯定是P3口进来的数使得P12位输出电平的.
P3口的P32位的按钮被按下,使得P32位的电平为低,通过程序,又使P12口输出低电平,所以P3口起来了一个输入的作用.
验证:按第二、三、四个按钮,同时按下2个、3个、4个按钮都可以得到同样的结论,所以P3口确实起到了输入作用,这样,我们可以看到,以P字开头的管脚,不仅可以用作输出,还可以用作输入,其它的管脚是否可以呢是的,都可以.
这32个引脚就称之为并行口,下面我们就对并行口的结构作一个分析,看一下它是怎样实平凡的单片机教程自学单片机第11页共62页11现输入和输出的.
并行口结构分析:1、输出结构先看P1口的一位的结构示意图(只画出了输出部份):从图中可以看出,开关的打开和合上代表了引脚输出的高和低,如果开关合上了,则引脚输出就是低,如果开关打开了,则输出高电平,这个开关是由一根线来控制的,这根数据总线是出自于CPU,让我们回想一下,数据总线是一根大家公用的线,很多的器件和它连在一起,在不同的时候,不同的器件当然需要不同的信号,如某一时刻我们让这个引脚输出高电平,并要求保持若干时间,在这段时间里,计算机当然在忙个不停,在与其它器件进行联络,这根控制线上的电平未必能保持原来的值不变,输出就会发生变化了.
怎么解决这个问题呢我们在存储器一节中学过,存储器中是可以存放电荷的,我们不妨也加一个小的存储器的单元,并在它的前面加一个开关,要让这一位输出时,就把开关打开,信号就进入存储器的单元,然后马上关闭开关,这样这一位的状态就被保存下来,直到下一次命令让它把开关再打开为止.
这样就能使这一位的状态与别的器件无关了,这么一个小单元,我们给它一个很形象的名字,称之为"锁存器".
2、输入结构这是并行口的一位的输出结构示意图,再看,除了输出之外,还有两根线,一根从外部引脚接入,另一根从锁存器的输出接出,分别标明读引脚和读锁存器.
这两根线是用于从外部接收信号的,为什么要两根呢原来,在51单片机中输入有两种方式,分别称为'读引脚'和'读锁存器',第一种方式是将引脚作为输入,那是真正地从外部引脚读进输入的值,第二种方式是该引脚处于输出状态时,有时需要改变这一位的状态,则并不需要真正地读引脚状态,而只是读入锁存器的状态,然后作某种变换后再输出.
请注意输入结构图,如果将这一根引线作为输入口使用,我们并不能保证在任何时刻都能得到正确的结果(为什么)参考图2输入示意图.
接在外部的开关如果打开,则应当是输入1,而如果闭合开关,则输入0,但是,如果单片机内部的开关是闭合的,那么不管外部的开关是开还是闭,单片机接受到的数据都是0.
可见,要让这一端口作为输入使用,要先做一个'准备工作',就是先让内部的开关断开,也就是让端口输出'1'才行.
正因为要先做这么一个准备工作,所以我们称之为"准双向I/O口".
以上是P1口的一位的结构,P1口其它各位的结构与之相同,而其它三个口:P0、P2、P3则除入作为输入输出口之外还有其它用途,所以结构要稍复杂一些,但其用于输入、输出的结构是相同的.
看图().
对我们来说,这些附加的功能不必由我们来控制,所以我们就不去关心它了.
单片机教程第七课:单片机内部结构分析(五)通过前面的学习,我们已知单片机的内部有ROM、有RAM、有并行I/O口,那么,除了这些东西之外,单片机内部究竟还有些什么,这些个零碎的东西怎么连在一起的,让我们来对单片机内部作一个完整的分析吧!

看图(1)(本图太大,请大家找本书看吧,一般讲单片机的书,随便哪本都有).
从图中我们可以看出,在51单片机内部有一个CPU用来运算、控制,有四个并行I/O口,分别是P0、P1、P2、P3,有ROM,用来存放程序,有RAM,用来存放中间结果,此外还有定时/计数器,串行I/O口,中断系统,以及一个内部的时钟电路.
在一个51单片机的内部包含了这么多的东西.
对上面的图进行进一步的分析,我们已知,对并行I/O口的读写只要将数据送入到相应I/O口的锁存器就可以了,那么对于定时/计数器,串行I/O口等怎么用呢在单片机中有一些独立的存储单元是用来控制这些器件的,被称之为特殊功能寄存器(SFR).
事实上,我们已接触过P1这个特殊功能寄存器了,还有哪些呢看表1符号地址功能介绍BF0HB寄存器ACCE0H累加器平凡的单片机教程自学单片机第12页共62页12PSWD0H程序状态字IPB8H中断优先级控制寄存器P3B0HP3口锁存器IEA8H中断允许控制寄存器P2A0HP2口锁存器SBUF99H串行口锁存器SCON98H串行口控制寄存器P190HP1口锁存器TH18DH定时器/计数器1(高8位)TH08CH定时器/计数器1(低8位)TL18BH定时器/计数器0(高8位)TL08AH定时器/计数器0(低8位)TMOD89A定时器/计数器方式控制寄存器TCON88H定时器/计数器控制寄存器DPH83H数据地址指针(高8位)DPL82H数据地址指针(低8位)SP81H堆栈指针P080HP0口锁存器PCON87H电源控制寄存器表1下面,我们介绍一下几个常用的SFR,看图2.
ACC:累加器,通常用A表示.
这是个什么东西,可不能从名字上理解,它是一个寄存器,而不是一个做加法的东西,为什么给它这么一个名字呢或许是因为在运算器做运算时其中一个数一定是在ACC中的缘故吧.
它的名字特殊,身份也特殊,稍后我们将学到指令,可以发现,所有的运算类指令都离不开它.

2、B:一个寄存器.
在做乘、除法时放乘数或除数,不做乘除法时,随你怎么用.

3、PSW:程序状态字.
这是一个很重要的东西,里面放了CPU工作时的很多状态,借此,我们可以了解CPU的当前状态,并作出相应的处理.
它的各位功能请看表2D7D6D5D4D3D2D1D0CYACF0RS1RS0OVP平凡的单片机教程自学单片机第13页共62页13表2下面我们逐一介绍各位的用途(1)CY:进位标志.
8051中的运算器是一种8位的运算器,我们知道,8位运算器只能表示到0-255,如果做加法的话,两数相加可能会超过255,这样最高位就会丢失,造成运算的错误,怎么办最高位就进到这里来.

这样就没事了.
例:78H+97H(01111000+10010111)(2)AC:半进位标志.
例:57H+3AH(01010111+00111010)(3)F0:用户标志位,由我们(编程人员)决定什么时候用,什么时候不用.
(4)RS1、RS0:工作寄存器组选择位.
这个我们已知了.
(5)0V:溢出标志位.
什么是溢出我们稍后再谈吧.
(6)P:奇偶校验位:它用来表示ALU运算结果中二进制数位"1"的个数的奇偶性.
若为奇数,则P=1,否则为0.
例:某运算结果是78H(01111000),显然1的个数为偶数,所以P=0.
4、DPTR(DPH、DPL):数据指针,可以用它来访问外部数据存储器中的任一单元,如果不用,也可以作为通用寄存器来用,由我们自已决定如何使用.
5、P0、P1、P2、P3:这个我们已经知道,是四个并行输入/输出口的寄存器.
它里面的内容对应着管脚的输出.

6、SP:堆栈指针.
堆栈介绍:日常生活中,我们都注意到过这样的现象,家里洗的碗,一只一只摞起来,最晚放上去的放在最上面,而最早放上去的则放在最下面,在取的时候正好相反,先从最上面取,这种现象我们用一句话来概括:"先进后出,后进先出".
请大家想想,还有什么地方有这种现象其实比比皆是,建筑工地上堆放的砖头、材料,仓库里放的货物,都是"先进后出,后进先出",这实际是一种存取物品的规则,我们称之为"堆栈".
在单片机中,我们也可以在RAM中构造这样一个区域,用来存放数据,这个区域存放数据的规则就是"先进后出,后进先出",我们称之为"堆栈".
为什么需要这样来存放数据呢存储器本身不是可以按地址来存放数据吗对,知道了地址的确就可以知道里面的内容,但如果我们需要存放的是一批数据,每一个数据都需要知道地址那不是麻烦吗如果我们让数据一个接一个地放置,那么我们只要知道第一个数据所在地址单元就可以了(看图2)如果第一个数据在27H,那么第二、三个就在28H、29H了.
所以利用堆栈这种方法来放数据可以简化操作那么51中堆栈什么地方呢单片机中能存放数据的区域有限,我们不能够专门分配一块地方做堆栈,所以就在内存(RAM)中开辟一块地方,用于堆栈,但是用内存的哪一块呢还是不好定,因为51是一种通用的单片机,各人的实际需求各不相同,有人需要多一些堆栈,而有人则不需要那么多,所以怎么分配都不合适,怎样来解决这个问题分不好干脆就不分了,把分的权利给用户(编程者),根据自已的需要去定吧,所以51单片机中堆栈的位置是可以变化的.
而这种变化就体现在SP中值的变化,看图2,SP中的值等于27H不就相当于是一个指针指向27H单元吗当然在真正的51机中,开始指针所指的位置并非就是数据存放的位置,而是数据存放的前一个位置,比如一开始指针是指向27H单元的,那么第一个数据的位置是28H单元,而不是27H单元,为什么会这样,我们在学堆栈命令时再说明.
其它的SFR,我们在用到时再介绍.
平凡的单片机教程自学单片机第14页共62页14单片机第八课(寻址方式与指令系统)通过前面的学习,我们已经了解了单片机内部的结构,并且也已经知道,要控制单片机,让它为我们干学,要用指令,我们已学了几条指令,但很零散,从现在开始,我们将要系统地学习8051的指令部份.
一、概述1、指令的格式我们已知,要让计算机做事,就得给计算机以指令,并且我们已知,计算机很"笨",只能懂得数字,如前面我们写进机器的75H,90H,00H等等,所以指令的第一种格式就是机器码格式,也说是数字的形式.
但这种形式实在是为难我们人了,太难记了,于是有另一种格式,助记符格式,如MOVP1,#0FFH,这样就好记了.
这两种格式之间的关系呢,我们不难理解,本质上它们完全等价,只是形式不一样而已.

2、汇编我们写指令使用汇编格式,而计算机只懂机器码格式,所以要将我们写的汇编格式的指令转换为机器码格式,这种转换有两种方法:手工汇编和机器汇编.
手工汇编实际上就是查表,因为这两种格式纯粹是格式不同,所以是一一对应的,查一张表格就行了.
不过手工查表总是嫌麻烦,所以就有了计算机软件,用计算机软件来替代手工查表,这就是机器汇编.
二、寻址让我们先来复习一下我们学过的一些指令:MOVP1,#0FFH,MOVR7,#0FFH这些指令都是将一些数据送到相应的位置中去,为什么要送数据呢第一个因为送入的数可以让灯全灭掉,第二个是为了要实现延时,从这里我们可以看出来,在用单片机的编程语言编程时,经常要用到数据的传递,事实上数据传递是单片机编程时的一项重要工作,一共有28条指令(单片机共111条指令).
下面我们就从数据传递类指令开始吧.
分析一下MOVP1,#0FFH这条指令,我们不难得出结论,第一个词MOV是命令动词,也就是决定做什么事情的,MOV是MOVE少写了一个E,所以就是"传递",这就是指令,规定做什么事情,后面还有一些参数,分析一下,数据传递必须要有一个"源"也就是你要送什么数,必须要有一个"目的",也就是你这个数要送到什么地方去,显然在上面那条指令中,要送的数(源)就是0FFH,而要送达的地方(目的地)就是P1这个寄存器.
在数据传递类指令中,均将目的地写在指令的后面,而将源写在最后.
这条指令中,送给P1是这个数本身,换言之,做完这条指令后,我们可以明确地知道,P1中的值是0FFH,但是并不是任何时候都可以直接给出数本身的.
例如,在我们前面给出的延时程序例是这样写的:MAIN:SETBP1.
0;(1)LCALLDELAY;(2)CLRP1.
0;(3)LCALLDELAY;(4)AJMPMAIN;(5);以下子程序DELAY:MOVR7,#250;(6)D1:MOVR6,#250;(7)D2:DJNZR6,D2;(8)DJNZR7,D1;(9)RET;(10)END;(11)表1平凡的单片机教程自学单片机第15页共62页15MAIN:SETBP1.
0;(1)MOV30H,#255LCALLDELAY;CLRP1.
0;(3)MOV30H,#200LCALLDELAY;(4)AJMPMAIN;(5);以下子程序DELAY:MOVR7,30H;(6)D1:MOVR6,#250;(7)D2:DJNZR6,D2;(8)DJNZR7,D1;(9)RET;(10)END;(11)这样一来,我每次调用延时程序延时的时间都是相同的(大致都是0.
13S),如果我提出这样的要求:灯亮后延时时间为0.
13S灯灭,灯灭后延时0.
1秒灯亮,如此循环,这样的程序还能满足要求吗不能,怎么办我们可以把延时程序改成这样(见表2):调用则见表2中的主程,也就是先把一个数送入30H,在子程序中R7中的值并不固定,而是根据30H单元中传过来的数确定.
这样就可以满足要求.
从这里我们可以得出结论,在数据传递中要找到被传递的数,很多时候,这个数并不能直接给出,需要变化,这就引出了一个概念:如何寻找操作数,我们把寻找操作数所在单元的地址称之为寻址.
在这里我们直接使用数所在单元的地址找到了操作数,所以称这种方法为直接寻址.
除了这种方法之外,还有一种,如果我们把数放在工作寄存器中,从工作寄存器中寻找数据,则称之为寄存器寻址.
例:MOVA,R0就是将R0工作寄存器中的数据送到累加器A中去.
提一个问题:我们知道,工作寄存器就是内存单元的一部份,如果我们选择工作寄存器组0,则R0就是RAM的00H单元,那么这样一来,MOVA,00H,和MOVA,R0不就没什么区别了吗为什么要加以区分呢的确,这两条指令执行的结果是完全相同的,都是将00H单元中的内容送到A中去,但是执行的过程不同,执行第一条指令需要2个周期,而第二条则只需要1个周期,第一条指令变成最终的目标码要两个字节(E5H00H),而第二条则只要一个字节(E8h)就可以了.
这么斤斤计较!
不就差了一个周期吗,如果是12M的晶振的话,也就1个微秒时间了,一个字节又能有多少不对,如果这条指令只执行一次,也许无所谓,但一条指令如果执行上1000次,就是1毫秒,如果要执行1000000万次,就是1S的误差,这就很可观了,单片机做的是实时控制的事,所以必须如此"斤斤计较".
字节数同样如此.
再来提一个问题,现在我们已知,寻找操作数可以通过直接给的方式(立即寻址)和直接给出数所在单元地址的方式(直接寻址),这就够了吗看这个问题,要求从30H单元开始,取20个数,分别送入A累加器.
就我们目前掌握的办法而言,要从30H单元取数,就用MOVA,30H,那么下一个数呢是31H单元的,怎么取呢还是只能用MOVA,31H,那么20个数,不是得20条指令才能写完吗这里只有20个数,如果要送200个或2000个数,那岂不要写上200条或2000条命令这未免太笨了吧.
为什么会出现这样的状况是因为我们只会把地址写在指令中,所以就没办法了,如果我们不是把地址直接写在指令中,而是把地址放在另外一个寄存器单元中,根据这个寄存器单元中的数值决定该到哪个单元中取数据,比如,当前这个寄存器中的值是30H,那么就到30H单元中去取,如果是31H就到31H单元中去取,就可以解决这个问题了.
怎么个解决法呢既然是看的寄存器中的值,那么我们就可以通过一定的方法让这里面的值发生变化,比如取完一个数后,将这个寄存器单元中的值加1,还是执行同一条指令,可是取数的对象却不一样了,不是吗.
通过例子来说明吧.

MOVR7,#20MOVR0,#30H平凡的单片机教程自学单片机第16页共62页16LOOP:MOVA,@R0INCR0DJNZR7,LOOP这个例子中大部份指令我们是能看懂的,第一句,是将立即数20送到R7中,执行完后R7中的值应当是20.
第二句是将立即数30H送入R0工作寄存器中,所以执行完后,R0单元中的值是30H,第三句,这是看一下R0单元中是什么值,把这个值作为地址,取这个地址单元的内容送入A中,此时,执行这条指令的结果就相当于MOVA,30H.
第四句,没学过,就是把R0中的值加1,因此执行完后,R0中的值就是31H,第五句,学过,将R7中的值减1,看是否等于0,不等于0,则转到标号LOOP处继续执行,因此,执行完这句后,将转去执行MOVA,@R0这句话,此时相当于执行了MOVA,31H(因为此时的R0中的值已是31H了),如此,直到R7中的值逐次相减等于0,也就是循环20次为止,就实现了我们的要求:从30H单元开始将20个数据送入A中.
这也是一种寻找数据的方法,由于数据是间接地被找到的,所以就称之为间址寻址.
注意,在间址寻址中,只能用R0或R1存放等寻找的数据.
单片机教程第九课:数据传递指令数据传递类指令1)以累加器为目的操作数的指令MOVA,RnMOVA,directMOVA,@RiMOVA,#data第一条指令中,Rn代表的是R0-R7.
第二条指令中,direct就是指的直接地址,而第三条指令中,就是我们刚才讲过的.
第四条指令是将立即数data送到A中.
下面我们通过一些例子加以说明:MOVA,R1;将工作寄存器R1中的值送入A,R1中的值保持不变.
MOVA,30H;将内存30H单元中的值送入A,30H单元中的值保持不变.
MOVA,@R1;先看R1中是什么值,把这个值作为地址,并将这个地址单元中的值送入A中.
如执行命令前R1中的值为20H,则是将20H单元中的值送入A中.
MOVA,#34H;将立即数34H送入A中,执行完本条指令后,A中的值是34H.
2)以寄存器Rn为目的操作的指令MOVRn,AMOVRn,directMOVRn,#data这组指令功能是把源地址单元中的内容送入工作寄存器,源操作数不变.

单片机指令(二)数据传递类指令(3)以直接地址为目的操作数的指令MOVdirect,A例:MOV20H,AMOVdirect,RnMOV20H,R1MOVdirect1,direct2MOV20H,30HMOVdirect,@RiMOV20H,@R1MOVdirect,#dataMOV20H,#34H(4)以间接地址为目的操作数的指令MOV@Ri,A例:MOV@R0,AMOV@Ri,directMOV@R1,20HMOV@Ri,#dataMOV@R0,#34H(5)十六位数的传递指令MOVDPTR,#data168051是一种8位机,这是唯一的一条16位立即数传递指令,其功平凡的单片机教程自学单片机第17页共62页17能是将一个16位的立即数送入DPTR中去.
其中高8位送入DPH,低8位送入DPL.
例:MOVDPTR,#1234H,则执行完了之后DPH中的值为12H,DPL中的值为34H.
反之,如果我们分别向DPH,DPL送数,则结果也一样.
如有下面两条指令:MOVDPH,#35H,MOVDPL,#12H.
则就相当于执行了MOVDPTR,#3512H.
综合练习:给出每条指令执行后的结果MOV23H,#30HMOV12H,#34HMOVR0,#23HMOVR7,#22HMOVR1,12HMOVA,@R0MOV34H,@R1(23h)=30h(12h)=34h(R0)=23H(R7)=22H(R1)=12H(A)=30H(34H)=34HMOV45H,34HMOVDPTR,#6712HMOV12H,DPHMOVR0,DPLMOVA,@R0(45H)=34H(DPTR)=6712H(12H)=67H(R0)=12H(A)=67H说明:用括号括起来代表内容,如(23H)则代表内部RAM23H单元中的值,(A)则代表累加器A单元中的值.
上机练习:进入DOS状态,进入WAVE所在的目录,例D:\WAVE键入MCS51,出现如下画面图1按File->Open,出现对话框后,在Name处输入一个文件名(见图2),如果是下面列表中已存在的,则打开这个文件,如果不存在这个文件,则新建一个文件(见图3)图2在空白处将上面的程序输入.
见图4.
用ALT+A汇编通过.
用F8即可单步执行,在执行过程中注意观察屏幕左边的工作寄存器及A累加器中的值的变化.
图4内存中值的变化在此是看不到的,可以用如下方法观察(看图5):将鼠标移到DATA,双击,则光标进入此行,此时可以键盘上的上下光标键上下翻动来观察内存值的变化.
本行的最前面DATA后面的数据代表的是"一段"的开始地址,如现在为20H,再看屏幕的最上方,数字从0到F,显示两者相加就等于真正的地址值,如现在图上所示的内存20H、21H、22H、23H中的值分别是FBH、0EH、E8H、30H.
图56、当运行完程序后,即进入它的反汇编区,不是我们想要的东西.
为了再从头开始,可以用CTRL+F2功能键复位PC值.
注意此时不会看到原来的窗口,为看到原来的窗口,请用ALT+4或ALT+5等来切换.
当然以上操作也可以菜单进行.
CTRL+F2是程序复位,用RUN菜单.
窗口用WINDOWS菜单.
此次大家就用用熟这个软件吧,说实话,我并不很喜欢它,操作起来不方便,但给我的机器只能上这个,没办法,平凡的单片机教程自学单片机第18页共62页18下次再给网友单独介绍一个好一点的吧.
本页图片较多,如果大家无法忍受它的等待,请下载单片机教程第10101010课数据传递类指令指令累加器A与片外RAM之间的数据传递类指令MOVXA,@RiMOVX@Ri,AMOVXA,@DPTRMOVX@DPTR,A说明:1)在51中,与外部存储器RAM打交道的只可以是A累加器.
所有需要送入外部RAM的数据必需要通过A送去,而所有要读入的外部RAM中的数据也必需通过A读入.
在此我们可以看出内外部RAM的区别了,内部RAM间可以直接进行数据的传递,而外部则不行,比如,要将外部RAM中某一单元(设为0100H单元的数据)送入另一个单元(设为0200H单元),也必须先将0100H单元中的内容读入A,然后再送到0200H单元中去.
要读或写外部的RAM,当然也必须要知道RAM的地址,在后两条指令中,地址是被直接放在DPTR中的.
而前两条指令,由于Ri(即R0或R1)只是一个8位的寄存器,所以只提供低8位地址.
因为有时扩展的外部RAM的数量比较少,少于或等于256个,就只需要提供8位地址就够了.
使用时应当首先将要读或写的地址送入DPTR或Ri中,然后再用读写命令.
例:将外部RAM中100H单元中的内容送入外部RAM中200H单元中.
MOVDPTR,#0100HMOVXA,@DPTRMOVDPTR,#0200HMOVX@DPTR,A程序存储器向累加器A传送指令MOVCA,@A+DPTR本指令是将ROMROMROMROM中的数送入A中.
本指令也被称为查表指令,常用此指令来查一个已做好在ROMROMROMROM中的表格说明:此条指令引出一个新的寻址方法:变址寻址.
本指令是要在ROM的一个地址单元中找出数据,显然必须知道这个单元的地址,这个单元的地址是这样确定的:在执行本指令立脚点DPTR中有一个数,A中有一个数,执行指令时,将A和DPTR中的数加起为,就成为要查找的单元的地址.
查找到的结果被放在A中,因此,本条指令执行前后,A中的值不一定相同.
例:有一个数在R0中,要求用查表的方法确定它的平方值(此数的取值范围是0-5)MOVDPTR,#TABLEMOVA,R0MOVCA,@A+DPTRTABLE:DB0,1,4,9,16,25设R0中的值为2,送入A中,而DPTR中的值则为TABLE,则最终确定的ROM单元的地址就是TABLE+2,也就是到这个单元中去取数,取到的是4,显然它正是2的平方.
其它数据也可以类推.
标号的真实含义:从这个地方也可以看到另一个问题,我们使用了标号来替代具体的单元地址.
事实上,标号的真实含义就是地址数值.
在这里它代表了,0,1,4,9,16,25这几个数据在ROM中存放的起点位置.
而在以前我们学过的如LCALLDELAY指令中,DELAY则代表了以DELAY为标号的那段程序在ROM中存放的起始地址.
事实上,CPU正是通过这个地址才找到这段程序的.
可以通过以下的例子再来看一看标号的含义:MOVDPTR,#100H平凡的单片机教程自学单片机第19页共62页19MOVA,R0MOVCA,@A+DPTRORG0100H.
DB0,1,4,9,16,25如果R0中的值为2,则最终地址为100H+2为102H,到102H单元中找到的是4.
这个可以看懂了吧那为什么不这样写程序,要用标号呢不是增加疑惑吗如果这样写程序的话,在写程序时,我们就必须确定这张表格在ROM中的具体的位置,如果写完程序后,又想在这段程序前插入一段程序,那么这张表格的位置就又要变了,要改ORG100H这句话了,我们是经常需要修改程序的,那多麻烦,所以就用标号来替代,只要一编译程序,位置就自动发生变化,我们把这个麻烦事交给计算机��指PC机去做了.
堆栈操作PUSHdirectPOPdirect第一条指令称之为推入,就是将direct中的内容送入堆栈中,第二条指令称之为弹出,就是将堆栈中的内容送回到direct中.
推入指令的执行过程是,首先将SP中的值加1,然后把SP中的值当作地址,将direct中的值送进以SP中的值为地址的RAM单元中.
例:MOVSP,#5FHMOVA,#100MOVB,#20PUSHACCPUSHB则执行第一条PUSHACC指令是这样的:将SP中的值加1,即变为60H,然后将A中的值送到60H单元中,因此执行完本条指令后,内存60H单元的值就是100,同样,执行PUSHB时,是将SP+1,即变为61H,然后将B中的值送入到61H单元中,即执行完本条指令后,61H单元中的值变为20.
POP指令的执行是这样的,首先将SP中的值作为地址,并将此地址中的数送到POP指令后面的那个direct中,然后SP减1.
接上例:POPBPOPACC则执行过程是:将SP中的值(现在是61H)作为地址,取61H单元中的数值(现在是20),送到B中,所以执行完本条指令后B中的值是20,然后将SP减1,因此本条指令执行完后,SP的值变为60H,然后执行POPACC,将SP中的值(60H)作为地址,从该地址中取数(现在是100),并送到ACC中,所以执行完本条指令后,ACC中的值是100.
这有什么意义呢ACC中的值本来就是100,B中的值本来就是20,是的,在本例中,的确没有意义,但在实际工作中,则在PUSHB后往往要执行其他指令,而且这些指令会把A中的值,B中的值改掉,所以在程序的结束,如果我们要把A和B中的值恢复原值,那么这些指令就有意义了.
还有一个问题,如果我不用堆栈,比如说在PUSHACC指令处用MOV60H,A,在PUSHB处用指令MOV61H,B,然后用MOVA,60H,MOVB,61H来替代两条POP指令,不是也一样吗是的,从结果上看是一样的,但是从过程看是不一样的,PUSH和POP指令都是单字节,单周期指令,而MOV指令则是双字节,双周期指令.
更何况,堆栈的作用不止于此,所以一般的计算机上都设有堆栈,而我们在编写子程序,需要保存数据时,通常也不采用后面的方法,而是用堆栈的方法来实现.
例:写出以下程序的运行结果MOV30H,#12MOV31H,#23PUSH30HPUSH31H平凡的单片机教程自学单片机第20页共62页20POP30HPOP31H结果是30H中的值变为23,而31H中的值则变为12.
也就两者进行了数据交换.
从这个例子可以看出:使用堆栈时,入栈的书写顺序和出栈的书写顺序必须相反,才能保证数据被送回原位,否则就要出错了.

作业:在MCS51下执行上面的例程,注意观察内存窗口和堆栈的变化.
单片机教程第11111111课:算术运算类指令不带进位位的加法指令ADDA,#DATA;例:ADDA,#10HADDA,direct;例:ADDA,10HADDA,Rn;例:ADDA,R7ADDA,@Ri;例:ADDA,@R0用途:将A中的值与其后面的值相加,最终结果否是回到A中.
例:MOVA,#30HADDA,#10H则执行完本条指令后,A中的值为40H.
下面的题目自行练习MOV34H,#10HMOVR0,#13HMOVA,34HADDA,R0MOVR1,#34HADDA,@R1带进位位的加法指令ADDCA,RnADDCA,directADDCA,@RiADDCA,#data用途:将A中的值和其后面的值相加,并且加上进位位C中的值.
说明:由于51单片机是一种8位机,所以只能做8位的数学运算,但8位运算的范围只有0-255,这在实际工作中是不够的,因此就要进行扩展,一般是将2个8位的数学运算合起来,成为一个16位的运算,这样,可以表达的数的范围就可以达到0-65535.
如何合并呢其实很简单,让我们看一个10进制数的例子:66+78.
这两个数相加,我们根本不在意这的过程,但事实上我们是这样做的:先做6+8(低位),然后再做6+7,这是高位.
做了两次加法,只是我们做的时候并没有刻意分成两次加法来做罢了,或者说我们并没有意识到我们做了两次加法.
之所以要分成两次来做,是因为这两个数超过了一位数所能表达的范置(0-9).
在做低位时产生了进位,我们做的时候是在适当的位置点一下,然后在做高位加法是将这一点加进去.
那么计算机中做16位加法时同样如此,先做低8位的,如果两数相加产生了进位,也要"点一下"做个标记,这个标记就是进位位C,在PSW中.
在进行高位加法是将这个C加进去.
例:1067H+10A0H,先做67H+A0H=107H,而107H显然超过了0FFH,因此最终保存在A中的是7,而1则到了PSW中的CY位了,换言之,CY就相当于是100H.
然后再做10H+10H+CY,结果是21H,所以最终的结果是2107H.
带借位的减法指令SUBBA,RnSUBBA,direct平凡的单片机教程自学单片机第21页共62页21SUBBA,@RiSUBBA,#data设(每个H,(R2)=55H,CY=1,执行指令SUBBA,R2之后,A中的值为73H.
说明:没有不带借位的减法指令,如果需要做不带位的减法指令(在做第一次相减时),只要将CY清零即可.
乘法指令MULAB此指令的功能是将A和B中的两个8位无符号数相乘,两数相乘结果一般比较大,因此最终结果用1个16位数来表达,其中高8位放在B中,低8位放在A中.
在乘积大于FFFFFH(65535)时,0V置1(溢出),否则OV为0,而CY总是0.
例:(A)=4EH,(B)=5DH,执行指令MULAB后,乘积是1C56H,所以在B中放的是1CH,而A中放的则是56H.
除法指令DIVAB此指令的功能是将A中的8位无符号数除了B中的8位无符号数(A/B).
除法一般会出现小数,但计算机中可没法直接表达小数,它用的是我们小学生还没接触到小数时用的商和余数的概念,如13/5,其商是2,余数是3.
除了以后,商放在A中,余数放在B中.
CY和OV都是0.
如果在做除法前B中的值是00H,也就是除数为0,那么0V=1.
加1指令INCAINCRnINCdirectINC@RiINCDPTR用途很简单,就是将后面目标中的值加1.
例:(A)=12H,(R0)=33H,(21H)=32H,(34H)=22H,DPTR=1234H.
执行下面的指令:INCA(A)=13HINCR2(R0)=34HINC21H(21H)=33HINC@R0(34H)=23HINCDPTR(DPTR)=1235H后结果如上所示.
说明:从结果上看INCA和ADDA,#1差不多,但INCA是单字节,单周期指令,而ADD#1则是双字节,双周期指令,而且INCA不会影响PSW位,如(A)=0FFH,INCA后(A)=00H,而CY依然保持不变.
如果是ADDA,#1,则(A)=00H,而CY一定是1.
因此加1指令并不适合做加法,事实上它主要是用来做计数、地址增加等用途.
另外,加法类指令都是以A为核心的��其中一个数必须放在A中,而运算结果也必须放在A中,而加1类指令的对象则广泛得多,可以是寄存器、内存地址、间址寻址的地址等等.

减1指令减1指令DECADECRNDECdirectDEC@Ri与加1指令类似,就不多说了.
综合练习:MOVA,#12HMOVR0,#24H平凡的单片机教程自学单片机第22页共62页22MOV21H,#56HADDA,#12HMOVDPTR,#4316HADDA,DPHADDA,R0CLRCSUBBA,DPLSUBBA,#25HINCASETBCADDCA,21HINCR0SUBBA,R0MOV24H,#16HCLRCADDA,@R0先写出每步运行结果,然后将以上题目建入,并在软件仿真中运行,观察寄存器及有关单元的内容的变化,是否与自已的预想结果相同.
单片机教程第12121212课:逻辑运算类指令:对累加器A的逻辑操作:CLRA;将A中的值清0,单周期单字节指令,与MOVA,#00H效果相同.
CPLA;将A中的值按位取反RLA;将A中的值逻辑左移RLCA;将A中的值加上进位位进行逻辑左移RRA;将A中的值进行逻辑右移RRCA;将A中的值加上进位位进行逻辑右移SWAPA;将A中的值高、低4位交换.
例:(A)=73H,则执行CPLA,这样进行:73H化为二进制为01110011,逐位取反即为10001100,也就是8CH.
RLA是将(A)中的值的第7位送到第0位,第0位送1位,依次类推.
例:A中的值为68H,执行RLA.
68H化为二进制为01101000,按上图进行移动.
01101000化为11010000,即D0H.
RLCA,是将(A)中的值带上进位位(C)进行移位.
例:A中的值为68H,C中的值为1,则执行RLCA101101000后,结果是011010001,也就是C进位位的值变成了0,而(A)则变成了D1H.
RRA和RRCA就不多谈了,请大家参考上面两个例子自行练习吧.
SWAPA,是将A中的值的高、低4位进行交换.
例:(A)=39H,则执行SWAPA之后,A中的值就是93H.
怎么正好是这么前后交换呢因为这是一个16进制数,每1个16进位数字代表4个二进位.
注意,如果是这样的:(A)=39,后面没H,执行SWAPA之后,可不是(A)=93.
要将它化成二进制再算:39化为二进制是10111,也就是0001,0111高4位是0001,低4位是0111,交换后是01110001,也就是71H,即113.
平凡的单片机教程自学单片机第23页共62页23练习,已知(A)=39H,执行下列指令后写出每步的结果CPLARLACLRCRRCASETBCRLCASWAPA通过前面的学习,我们已经掌握了相当一部份的指令,大家对这些枯燥的指令可能也有些厌烦了,下面让我们轻松一下,做个实验.
实验五:ORG0000HLJMPSTARTORG30HSTART:MOVSP,#5FHMOVA,#80HLOOP:MOVP1,ARLALCALLDELAYLJMPLOOPdelay:movr7,#255d1:movr6,#255d2:nopnopnopnopdjnzr6,d2djnzr7,d1retEND先让我们将程序写入片中,装进实验板,看一看现象.
看到的是一个暗点流动的现象,让我们来分析一下吧.
前而的ORG0000H、LJMPSTART、ORG30H等我们稍后分析.
从START开始,MOVSP,#5FH,这是初始化堆栈,在本程序中有无此句无关紧要,不过我们慢慢开始接触正规的编程,我也就慢慢给大家培养习惯吧.

MOVA,#80H,将80H这个数送到A中去.
干什么呢不知道,往下看.
MOVP1,A.
将A中的值送到P1端口去.
此时A中的值是80H,所以送出去的也就是80H,因此P1口的值是80H,也就是10000000B,通过前面的分析,我们应当知道,此时P1.
7接的LED是不亮的,而其它的LED都是亮的,所以就形成了一个"暗点".
继续看,RLA,RLA是将A中的值进行左移,算一下,移之后的结果是什么对了,是01H,也就是00000001B,这样,应当是接在P1.
0上的LED不亮,而其它的都亮了,从现象上看"暗点"流到了后面.
然后是调用延时程序,这个我们很熟悉了,让这个"暗点""暗"一会儿.
然后又调转到LOOP处(LJMPLOOP).
请大家计算一下,下面该哪个灯不亮了.
.
.
.
.
对了,应当是接在P1.
1上灯不亮了.
这样依次循环,就形成了"暗点流动"这一现象.
问题:平凡的单片机教程自学单片机第24页共62页24如何实现亮点流动如何改变流动的方向答案:1、将A中的初始值改为7FH即可.
2、将RLA改为RRA即可.
单片机教程第13131313课:逻辑与指令ANLA,Rn;A与Rn中的值按位'与',结果送入A中ANLA,direct;A与direct中的值按位'与',结果送入A中ANLA,@Ri;A与间址寻址单元@Ri中的值按位'与',结果送入A中ANLA,#data;A与立即数data按位'与',结果送入A中ANLdirect,A;direct中值与A中的值按位'与',结果送入direct中ANLdirect,#data;direct中的值与立即数data按位'与',结果送入direct中.
这几条指令的关键是知道什么是逻辑与.
这里的逻辑与是指按位与例:71H和56H相与则将两数写成二进制形式:(71H)01110001(56H)00100110结果00100000即20H,从上面的式子可以看出,两个参与运算的值只要其中有一个位上是0,则这位的结果就是0,两个同是1,结果才是1.
理解了逻辑与的运算规则,结果自然就出来了.
看每条指令后面的注释下面再举一些例子来看.
MOVA,#45H;(A)=45HMOVR1,#25H;(R1)=25HMOV25H,#79H;(25H)=79HANLA,@R1;45H与79H按位与,结果送入A中为41H(A)=41HANL25H,#15H;25H中的值(79H)与15H相与结果为(25H)=11H)ANL25H,A;25H中的值(11H)与A中的值(41H)相与,结果为(25H)=11H在知道了逻辑与指令的功能后,逻辑或和逻辑异或的功能就很简单了.
逻辑或是按位"或",即有"1"为1,全"0"为0.
例:10011000或01100001结果11111001而异或则是按位"异或",相同为"0",相异为"1".
例:10011000异或01100001结果11111001而所有的或指令,就是将与指仿中的ANL换成ORL,而异或指令则是将ANL换成XRL.
即或指令:ORLA,Rn;A和Rn中的值按位'或',结果送入A中ORLA,direct;A和与间址寻址单元@Ri中的值按位'或',结果送入A中ORLA,#data;A和立direct中的值按位'或',结果送入A中ORLA,@Ri;A和即数data按位'或',结果送入A中ORLdirect,A;direct中值和A中的值按位'或',结果送入direct中ORLdirect,#data;direct中的值和立即数data按位'或',结果送入direct中.
平凡的单片机教程自学单片机第25页共62页25异或指令:XRLA,Rn;A和Rn中的值按位'异或',结果送入A中XRLA,direct;A和direct中的值按位'异或',结果送入A中XRLA,@Ri;A和间址寻址单元@Ri中的值按位'异或',结果送入A中XRLA,#data;A和立即数data按位'异或',结果送入A中XRLdirect,A;direct中值和A中的值按位'异或',结果送入direct中XRLdirect,#data;direct中的值和立即数data按位'异或',结果送入direct中.
练习:MOVA,#24HMOVR0,#37HORLA,R0XRLA,#29HMOV35H,#10HORL35H,#29HMOVR0,#35HANLA,@R0四、控制转移类指令无条件转移类指令短转移类指令AJMPaddr11长转移类指令LJMPaddr16相对转移指令SJMPrel上面的三条指令,如果要仔细分析的话,区别较大,但初学时,可不理会这么多,统统理解成:JMP标号,也就是跳转到一个标号处.
事实上,LJMP标号,在前面的例程中我们已接触过,并且也知道如何来使用了.
而AJMP和SJMP也是一样.
那么他们的区别何在呢在于跳转的范围不一样.
好比跳远,LJMP一下就能跳64K这么远(当然近了更没关系了).
而AJMP最多只能跳2K距离,而SJMP则最多只能跳256这么远.
原则上,所有用SJMP或AJMP的地方都可以用LJMP来替代.
因此在初学时,需要跳转时可以全用LJMP,除了一个场合.
什么场合呢先了解一下AJMP,AJMP是一条双字节指令,也就说这条指令本身占用存储器(ROM)的两个单元.
而LJMP则是三字节指令,即这条指令占用存储器(ROM)的三个单元.
下面是第四条跳转指令.

间接转移指令JMP@A+DPTR这条指令的用途也是跳转,转到什么地方去呢这可不能由标号简单地决定了.
让我们从一个实际的例子入手吧.
MOVDPTR,#TAB;将TAB所代表的地址送入DPTRMOVA,R0;从R0中取数(详见下面说明)MOVB,#2MULA,B;A中的值乘2(详见下面的说明)JMPA,@A+DPTR;跳转TAB:AJMPS1;跳转表格AJMPS2AJMPS3图2图3应用背景介绍:在单片机开发中,经常要用到键盘,见上面的9个按键的键盘.
我们的要求是:当按下功能键A……….
.
G时去完成不同的功能.
这用程序设计的语言来表达的话,就是:按下不同的键去执行不同的程序平凡的单片机教程自学单片机第26页共62页26段,以完成不同的功能.
怎么样来实现呢看图2,前面的程序读入的是按键的值,如按下'A'键后获得的键值是0,按下'B'键后获得的值是'1'等等,然后根据不同的值进行跳转,如键值为0就转到S1执行,为1就转到S2执行.
.
.
.
如何来实现这一功能呢先从程序的下面看起,是若干个AJMP语句,这若干个AJMP语句最后在存储器中是这样存放的(见图3),也就是每个AJMP语句都占用了两个存储器的空间,并且是连续存放的.
而AJMPS1存放的地址是TAB,到底TAB等于多少,我们不需要知道,把它留给汇编程序来算好了.
下面我们来看这段程序的执行过程:第一句MOVDPTR,#TAB执行完了之后,DPTR中的值就是TAB,第二句是MOVA,R0,我们假设R0是由按键处理程序获得的键值,比如按下A键,R0中的值是0,按下B键,R0中的值是1,以此类推,现在我们假设按下的是B键,则执行完第二条指令后,A中的值就是1.
并且按我们的分析,按下B后应当执行S2这段程序,让我们来看一看是否是这样呢第三条、第四条指令是将A中的值乘2,即执行完第4条指令后A中的值是2.
下面就执行JMP@A+DPTR了,现在DPTR中的值是TAB,而A+DPTR后就是TAB+2,因此,执行此句程序后,将会跳到TAB+2这个地址继续执行.
看一看在TAB+2这个地址里面放的是什么就是AJMPS2这条指令.
因此,马上又执行AJMPS2指令,程序将跳到S2处往下执行,这与我们的要求相符合.
请大家自行分析按下键"A"、"C"、"D"……之后的情况.
这样我们用JMP@A+DPTR就实现了按下一键跳到相应的程序段去执行的这样一个要求.
再问大家一个问题,为什么取得键值后要乘2如果例程下面的所有指令换成LJMP,即:LJMPS1,LJMPS2……这段程序还能正确地执行吗如果不能,应该怎么改单片机第14141414课:条件转移指令条件转移指令是指在满足一定条件时进行相对转移.
判A内容是否为0转移指令JZrelJNZrel第一指令的功能是:如果(A)=0,则转移,否则顺序执行(执行本指令的下一条指令).
转移到什么地方去呢如果按照传统的方法,就要算偏移量,很麻烦,好在现在我们可以借助于机器汇编了.
因此这第指令我们可以这样理解:JZ标号.
即转移到标号处.
下面举一例说明:MOVA,R0JZL1MOVR1,#00HAJMPL2L1:MOVR1,#0FFHL2:SJMPL2END在执行上面这段程序前如果R0中的值是0的话,就转移到L1执行,因此最终的执行结果是R1中的值为0FFH.
而如果R0中的值不等于0,则顺序执行,也就是执行MOVR1,#00H指令.
最终的执行结果是R1中的值等于0.
第一条指令的功能清楚了,第二条当然就好理解了,如果A中的值不等于0,就转移.
把上面的那个例子中的JZ改成JNZ试试吧,看看程序执行的结果是什么比较转移指令平凡的单片机教程自学单片机第27页共62页27CJNEA,#data,relCJNEA,direct,relCJNERn,#data,relCJNE@Ri,#data,rel第一条指令的功能是将A中的值和立即数data比较,如果两者相等,就顺序执行(执行本指令的下一条指令),如果不相等,就转移,同样地,我们可以将rel理解成标号,即:CJNEA,#data,标号.
这样利用这条指令,我们就可以判断两数是否相等,这在很多场合是非常有用的.
但有时还想得知两数比较之后哪个大,哪个小,本条指令也具有这样的功能,如果两数不相等,则CPU还会反映出哪个数大,哪个数小,这是用CY(进位位)来实现的.
如果前面的数(A中的)大,则CY=0,否则CY=1,因此在程序转移后再次利用CY就可判断出A中的数比data大还是小了.
例:MOVA,R0CJNEA,#10H,L1MOVR1,#0FFHAJMPL3L1:JCL2MOVR1,#0AAHAJMPL3L2:MOVR1,#0FFHL3:SJMPL3上面的程序中有一条指令我们还没学过,即JC,这条指令的原型是JCrel,作用和上面的JZ类似,但是它是判CY是0,还是1进行转移,如果CY=1,则转移到JC后面的标号处执行,如果CY=0则顺序执行(执行它的下面一条指令).
分析一下上面的程序,如果(A)=10H,则顺序执行,即R1=0.
如果(A)不等于10H,则转到L1处继续执行,在L1处,再次进行判断,如果(A)>10H,则CY=1,将顺序执行,即执行MOVR1,#0AAH指令,而如果(A)10H,则(R1)=0AAH,如果(R0)外中断1->外中断0->实时器1->串行中断.
MCS-51的中断响应过程:1、中断响应的条件:讲到这儿,我们依然对于计算机响应中断感到神奇,我们人可以响应外界的事件,是因为我们有多种"传感器"――眼、耳可以接受不同的信息,计算机是如何做到这点的呢其实说穿了,一点都不希奇,MCS51工作时,在每个机器周期中都会去查询一下各个中断标记,看他们是否是"1",如果是1,就说明有中断请求了,所以所谓中断,其实也是查询,不过是每个周期都查一下而已.
这要换成人来说,就相当于你在看书的时候,每一秒钟都会抬起头来看一看,查问一下,是不是有人按门铃,是否有电话.
.
.
.
很蠢,不是吗可计算机本来就是这样,它根本没人聪明.
了解了上述中断的过程,就不难解中断响应的条件了.
在下列三种情况之一时,CPU将封锁对中断的响应:CPU正在处理一个同级或更高级别的中断请求.
现行的机器周期不是当前正执行指令的最后一个周期.
我们知道,单片机有单周期、双周期、三周期指令,当前执行指令是单字节没有关系,如果是双字节或四字节的,就要等整条指令都执行完了,才能响应中断(因为中断查询是在每个机器周期都可能查到的).
当前正执行的指令是返回批令(RETI)或访问IP、IE寄存器的指令,则CPU至少再执行一条指令才应中断.
这些都是与中断有关的,如果正访问IP、IE则可能会开、关中断或改变中断的优先级,而中断返回指令则说明本次中断还没有处理完,所以都要等本指令处理结束,再执行一条指令才可以响应中断.

2、中断响应过程CPU响应中断时,首先把当前指令的下一条指令(就是中断返回后将要执行的指令)的地址送入堆栈,然后根据中断标记,将相应的中断入口地址送入PC,PC是程序指针,CPU取指令就根据PC中的值,PC中是什么值,就会到什么地方去取指令,所以程序就会转到中断入口处继续执行.
这些工作都是由硬件来完成的,不必我们去考虑.
这里还有个问题,大家是否注意到,每个中断向量地址只间隔了8个单元,如0003-000B,在如此少的空间中如何完成中断程序呢很简单,你在中断处安排一个LJMP指令,不就可以把中断程序跳转到任何地方了吗一个完整的主程序看起来应该是这样的:ORG0000HLJMPSTARTORG0003HLJMPINT0;转外中断0ORG000BHRETI;没有用定时器0中断,在此放一条RETI,万一"不小心"产生了中断,也不会有太大的后果.
.
中断程序完成后,一定要执行一条RETI指令,执行这条指令后,CPU将会把堆栈中保存着的地址取出,送回PC,那么程序就会从主程序的中断处继续往下执行了.
注意:CPU所做的保护工作是很有限的,只保护了一个地址,而其它的所有东西都不保护,所以如果你在主程序中用到了如A、PSW等,在中断程序中又要用它们,还要保证回到主程序后这里面的数据还是没执行中断以前的数据,就得自己保护起来.

平凡的单片机教程自学单片机第36页共62页36单片机教程第19191919课:定时、中断练习一在学单片机时我们第一个例子就是灯的闪烁,那是用延时程序做的,现在回想起来,这样做不很恰当,为什么呢我们的主程序做了灯的闪烁,就不能再干其它的事了,难道单片机只能这样工作吗当然不是,我们可以用定时器来实现灯的闪烁的功能.
例1:查询方式ORG0000HAJMPSTARTORG30HSTART:MOVP1,#0FFH;关所灯MOVTMOD,#00000001B;定时/计数器0工作于方式1MOVTH0,#15HMOVTL0,#0A0H;即数5536SETBTR0;定时/计数器0开始运行LOOP:JBCTF0,NEXT;如果TF0等于1,则清TF0并转NEXT处AJMPLOOP;否则跳转到LOOP处运行NEXT:CPLP1.
0MOVTH0,#15HMOVTL0,#9FH;重置定时/计数器的初值AJMPLOOPENDAJMPLOOPEND键入程序,看到了什么灯在闪烁了,这可是用定时器做的,不再是主程序的循环了.
简单地分析一下程序,为什么用JBC呢TF0是定时/计数器0的溢出标记位,当定时器产生溢出后,该位由0变1,所以查询该位就可知宇时时间是否已到.
该位为1后,要用软件将标记位清0,以便下一次定时是间到时该位由0变1,所以用了JBC指令,该指位在判1转移的同时,还将该位清0.
以上程序是可以实现灯的闪烁了,可是主程序除了让灯闪烁外,还是不能做其他的事啊!
不,不对,我们可以在LOOP:……和AJMPLOOP指令之间插入一些指令来做其他的事情,只要保证执行这些指令的时间少于定时时间就行了.
那我们在用软件延时程序的时候不是也可以用一些指令来替代DJNZ吗是的,但是那就要求你精确计算所用指令的时间,然后再减去相应的DJNZ循环次数,很不方便,而现在只要求所用指令的时间少于定时时间就行,显然要求低了.
当然,这样的方法还是不好,所以我们常用以下的方法来实现.

程序2:用中断实现ORG0000HAJMPSTARTORG000BH;定时器0的中断向量地址AJMPTIME0;跳转到真正的定时器程序处ORG30HSTART:MOVP1,#0FFH;关所灯MOVTMOD,#00000001B;定时/计数器0工作于方式1MOVTH0,#15HMOVTL0,#0A0H;即数5536SETBEA;开总中断允许SETBET0;开定时/计数器0允许平凡的单片机教程自学单片机第37页共62页37SETBTR0;定时/计数器0开始运行LOOP:AJMPLOOP;真正工作时,这里可写任意程序TIME0:;定时器0的中断处理程序PUSHACCPUSHPSW;将PSW和ACC推入堆栈保护CPLP1.
0MOVTH0,#15HMOVTL0,#0A0H;重置定时常数POPPSWPOPACCRETIEND上面的例子中,定时时间一到,TF0由0变1,就会引发中断,CPU将自动转至000B处寻找程序并执行,由于留给定时器中断的空间只有8个字节,显然不足以写下所有有中断处理程序,所以在000B处安排一条跳转指令,转到实际处理中断的程序处,这样,中断程序可以写在任意地方,也可以写任意长度了.
进入定时中断后,首先要保存当前的一些状态,程序中只演示了保存存ACC和PSW,实际工作中应该根据需要将可能会改变的单元的值都推入堆栈进行保护(本程序中实际不需保存护任何值,这里只作个演示).

上面的两个程序运行后,我们发现灯的闪烁非常快,根本分辨不出来,只是视觉上感到灯有些晃动而已,为什么呢我们可以计算一下,定时器中预置的数是5536,所以每计60000个脉冲就是定时时间到,这60000个脉冲的时间是多少呢我们的晶振是12M,所以就是60000微秒,即60毫秒,因此速度是非常快的.
如果我想实现一个1S的定时,该怎么办呢在该晶振濒率下,最长的定时也就是65.
536个毫秒啊!
上面给出一个例子.
ORG0000HAJMPSTARTORG000BH;定时器0的中断向量地址AJMPTIME0;跳转到真正的定时器程序处ORG30HSTART:MOVP1,#0FFH;关所灯MOV30H,#00H;软件计数器预清0MOVTMOD,#00000001B;定时/计数器0工作于方式1MOVTH0,#3CHMOVTL0,#0B0H;即数15536SETBEA;开总中断允许SETBET0;开定时/计数器0允许SETBTR0;定时/计数器0开始运行LOOP:AJMPLOOP;真正工作时,这里可写任意程序TIME0:;定时器0的中断处理程序PUSHACCPUSHPSW;将PSW和ACC推入堆栈保护INC30HMOVA,30HCJNEA,#20,T_RET;30H单元中的值到了20了吗T_L1:CPLP1.
0;到了,取反P10MOV30H,#0;清软件计数器T_RET:平凡的单片机教程自学单片机第38页共62页38MOVTH0,#15HMOVTL0,#9FH;重置定时常数POPPSWPOPACCRETIEND先自己分析一下,看看是怎么实现的这里采用了软件计数器的概念,思路是这样的,先用定时/计数器0做一个50毫秒的定时器,定时是间到了以后并不是立即取反P10,而是将软件计数器中的值加1,如果软件计数器计到了20,就取反P10,并清掉软件计数器中的值,否则直接返回,这样,就变成了20次定时中断才取反一次P10,因此定时时间就延长了成了20*50即1000毫秒了.
这个思路在工程中是非常有用的,有的时候我们需要若干个定时器,可51中总共才有2个,怎么办呢其实,只要这几个定时的时间有一定的公约数,我们就可以用软件定时器加以实现,如我要实现P10口所接灯按1S每次,而P11口所接灯按2S每次闪烁,怎么实现呢对了我们用两个计数器,一个在它计到20时,取反P10,并清零,就如上面所示,另一个计到40取反P11,然后清0,不就行了吗这部份的程序如下ORG0000HAJMPSTARTORG000BH;定时器0的中断向量地址AJMPTIME0;跳转到真正的定时器程序处ORG30HSTART:MOVP1,#0FFH;关所灯MOV30H,#00H;软件计数器预清0MOVTMOD,#00000001B;定时/计数器0工作于方式1MOVTH0,#3CHMOVTL0,#0B0H;即数15536SETBEA;开总中断允许SETBET0;开定时/计数器0允许SETBTR0;定时/计数器0开始运行LOOP:AJMPLOOP;真正工作时,这里可写任意程序TIME0:;定时器0的中断处理程序PUSHACCPUSHPSW;将PSW和ACC推入堆栈保护INC30HINC31H;两个计数器都加1MOVA,30HCJNEA,#20,T_NEXT;30H单元中的值到了20了吗T_L1:CPLP1.
0;到了,取反P10MOV30H,#0;清软件计数器T_NEXT:MOVA,31HCJNEA,#40,T_RET;31h单元中的值到40了吗T_L2:CPLP1.
1MOV31H,#0;到了,取反P11,清计数器,返回T_RET:平凡的单片机教程自学单片机第39页共62页39MOVTH0,#15HMOVTL0,#9FH;重置定时常数POPPSWPOPACCRETIEND程序一下载代码下载程序二下载代码下载程序三下载代码下载程序四下载代码下载您能用定时器的方法实现前面讲的流水灯吗试试看.
单片机教程第20202020课:定时////计数器实验2222前面我们做了定时器的实验,现在来看一看计数实验,在工作中计数通常会有两种要求:第一、将计数的值显示出来,第二、计数值到一定程度即中断报警.
第一种如各种计数器、里程表,第二种如前面例中讲到的生产线上的计数.
先看第一种吧.
我们的硬件中是这样连线的:324构成的振荡器连到定时/计数器1的外部引脚T1上面,我们就利用这个来做一个计数实验,要将计数的值显示出来,当然最好用数码管了,可我们还没讲到这一部份,为了避免把问题复杂化,我们用P1口的8个LED来显示计到的数据.
程序如下:ORG0000HAJMPSTARTORG30HSTART:MOVSP,#5FHMOVTMOD,#01000000B;定时/计数器1作计数用,0不用全置0SETBTR1;启动计数器1开始运行.
LOOP:MOVA,TL0MOVP1,AAJMPLOOPEND在硬件上用线将324的输出与T1连通(印板上有焊盘)运行这种程序,注意将板按正确的位置放置(LM324放在左手边,LED排列是按从高位到低们排列)看到什么随着324后接的LED的闪烁,单片机的8只LED也在不断变化,注意观察,是不是按二进制:00000000000000010000001000000011这样的顺序在变呢这就对了,这就是TL0中的数据.
程序二:ORG0000HAJMPSTARTORG001BHAJMPTIMER1;定时器1的中断处理ORG30HSTART:MOVSP,#5FHMOVTMOD,#01010000B;定时/计数器1作计数用,模式1,0不用全置0MOVTH1,#0FFH平凡的单片机教程自学单片机第40页共62页40MOVTL1,#0FAH;预置值,要求每计到6个脉冲即为一个事件SETBEASETBET1;开总中断和定时器1中断允许SETBTR1;启动计数器1开始运行.
AJMP$TIMER1:PUSHACCPUSHPSWCPLP1.
0;计数值到,即取反P1.
0MOVTH1,#0FFHMOVTL1,#0FAH;重置计数初值POPPSWPOPACCRETIEND上面这个程序完成的工作很简单,就是在每6个脉冲到来后取反一次P1.
0,因此实验的结果应当是:LM324后接的LED亮、灭6次,则P1.
0口所接LED亮或灭一次.
这实际就是我们上面讲的计数器的第二种应用.
程序三:外部中断实验ORG0000HAJMPSTARTORG0003H;外部中断地直入口AJMPINT0ORG30HSTART:MOVSP,#5FHMOVP1,#0FFH;灯全灭MOVP3,#0FFH;P3口置高电平SETBEASETBEX0AJMP$INT0:PUSHACCPUSHPSWCPLP1.
0POPPSWPOPACCRETIEND本程序的功能很简单,按一次按键1(接在12引脚上的)就引发一次中断0,取反一次P1.
0,因此理论上按一下灯亮,按一下灯灭,但在实际做实验时,可能会发觉有时不"灵",按了它没反应,但在大部份时候是对的,这是怎么回事呢我们在讲解键盘时再作解释,这个程序本身是没有问题的.

单片机教程第21212121课:串行接口概述串行接口的一般概念单片机与外界进行信息交换称之为通讯.
平凡的单片机教程自学单片机第41页共62页418051单片机的通讯方式有两种:并行通讯:数据的各位同时发送或接收.
串行通讯:数据一位一位顺序发送或接收.
参看下图:串行通讯的方式:异步通讯:它用一个起始位表示字符的开始,用停止位表示字符的结束.
其每帧的格式如下:在一帧格式中,先是一个起始位0,然后是8个数据位,规定低位在前,高位在后,接下来是奇偶校验位(可以省略),最后是停止位1.
用这种格式表示字符,则字符可以一个接一个地传送.
在异步通讯中,CPU与外设之间必须有两项规定,即字符格式和波特率.
字符格式的规定是双方能够在对同一种0和1的串理解成同一种意义.
原则上字符格式可以由通讯的双方自由制定,但从通用、方便的角度出发,一般还是使用一些标准为好,如采用ASCII标准.
波特率即数据传送的速率,其定义是每秒钟传送的二进制数的位数.
例如,数据传送的速率是120字符/s,而每个字符如上述规定包含10数位,则传送波特率为1200波特.
同步通讯:在同步通讯中,每个字符要用起始位和停止位作为字符开始和结束的标志,占用了时间;所以在数据块传递时,为了提高速度,常去掉这些标志,采用同步传送.
由于数据块传递开始要用同步字符来指示,同时要求由时钟来实现发送端与接收端之间的同步,故硬件较复杂.
通讯方向:在串行通讯中,把通讯接口只能发送或接收的单向传送方法叫单工传送;而把数据在甲乙两机之间的双向传递,称之为双工传送.
在双工传送方式中又分为半双工传送和全双工传送.
半双工传送是两机之间不能同时进行发送和接收,任一时该,只能发或者只能收信息.
2.
8051单片机的串行接口结构8051串行接口是一个可编程的全双工串行通讯接口.
它可用作异步通讯方式(UART),与串行传送信息的外部设备相连接,或用于通过标准异步通讯协议进行全双工的8051多机系统也可以通过同步方式,使用TTL或CMOS移位寄存器来扩充I/O口.
8051单片机通过引脚RXD(P3.
0,串行数据接收端)和引脚TXD(P3.
1,串行数据发送端)与外界通讯.
SBUF是串行口缓冲寄存器,包括发送寄存器和接收寄存器.
它们有相同名字和地址空间,但不会出现冲突,因为它们两个一个只能被CPU读出数据,一个只能被CPU写入数据.
串行口的控制与状态寄存器串行口控制寄存器SCON它用于定义串行口的工作方式及实施接收和发送控制.
字节地址为98H,其各位定义如下表:D7D6D5D4D3D2D1D0SM0SM1SM2RENTB8RB8TIRISM0、SM1:串行口工作方式选择位,其定义如下:SM0、SM1工作方式功能描述波特率平凡的单片机教程自学单片机第42页共62页4200方式08位移位寄存器Fosc/1201方式110位UART可变10方式211位UARTFosc/64或fosc/3211方式311位UART可变其中fosc为晶振频率SM2:多机通讯控制位.
在方式0时,SM2一定要等于0.
在方式1中,当(SM2)=1则只有接收到有效停止位时,RI才置1.
在方式2或方式3当(SM2)=1且接收到的第九位数据RB8=0时,RI才置1.
REN:接收允许控制位.
由软件置位以允许接收,又由软件清0来禁止接收.
TB8:是要发送数据的第9位.
在方式2或方式3中,要发送的第9位数据,根据需要由软件置1或清0.
例如,可约定作为奇偶校验位,或在多机通讯中作为区别地址帧或数据帧的标志位.

RB8:接收到的数据的第9位.
在方式0中不使用RB8.
在方式1中,若(SM2)=0,RB8为接收到的停止位.
在方式2或方式3中,RB8为接收到的第9位数据.
TI:发送中断标志.
在方式0中,第8位发送结束时,由硬件置位.
在其它方式的发送停止位前,由硬件置位.

TI置位既表示一帧信息发送结束,同时也是申请中断,可根据需要,用软件查询的方法获得数据已发送完毕的信息,或用中断的方式来发送下一个数据.
TI必须用软件清0.
RI:接收中断标志位.
在方式0,当接收完第8位数据后,由硬件置位.
在其它方式中,在接收到停止位的中间时刻由硬件置位(例外情况见于SM2的说明).
RI置位表示一帧数据接收完毕,可用查询的方法获知或者用中断的方法获知.
RI也必须用软件清0.
特殊功能寄存器PCONPCON是为了在CHMOS的80C51单片机上实现电源控制而附加的.
其中最高位是SMOD.
串行口的工作方式8051单片机的全双工串行口可编程为4种工作方式,现分述如下:方式0为移位寄存器输入/输出方式.
可外接移位寄存器以扩展I/O口,也可以外接同步输入/输出设备.
8位串行数据者是从RXD输入或输出,TXD用来输出同步脉冲.
输出串行数据从RXD引脚输出,TXD引脚输出移位脉冲.
CPU将数据写入发送寄存器时,立即启动发送,将8位数据以fos/12的固定波特率从RXD输出,低位在前,高位在后.
发送完一帧数据后,发送中断标志TI由硬件置位.
输入当串行口以方式0接收时,先置位允许接收控制位REN.
此时,RXD为串行数据输入端,TXD仍为同步脉冲移位输出端.
当(RI)=0和(REN)=1同时满足时,开始接收.
当接收到第8位数据时,将数据移入接收寄存器,并由硬件置位RI.
下面两图分别是方式0扩展输出和输入的接线图.
方式1为波特率可变的10位异步通讯接口方式.
发送或接收一帧信息,包括1个起始位0,8个数据位和1个停止位1.
输出当CPU执行一条指令将数据写入发送缓冲SBUF时,就启动发送.
串行数据从TXD引脚输出,发送完一帧数据后,就由硬件置位TI.
输入在(REN)=1时,串行口采样RXD引脚,当采样到1至0的跳变时,确认是开始位0,就开始接收一帧平凡的单片机教程自学单片机第43页共62页43数据.
只有当(RI)=0且停止位为1或者(SM2)=0时,停止位才进入RB8,8位数据才能进入接收寄存器,并由硬件置位中断标志RI;否则信息丢失.
所以在方式1接收时,应先用软件清零RI和SM2标志.
方式2方式月为固定波特率的11位UART方式.
它比方式1增加了一位可程控为1或0的第9位数据.
输出:发送的串行数据由TXD端输出一帧信息为11位,附加的第9位来自SCON寄存器的TB8位,用软件置位或复位.
它可作为多机通讯中地址/数据信息的标志位,也可以作为数据的奇偶校验位.
当CPU执行一条数据写入SUBF的指令时,就启动发送器发送.
发送一帧信息后,置位中断标志TI.
输入:在(REN)=1时,串行口采样RXD引脚,当采样到1至0的跳变时,确认是开始位0,就开始接收一帧数据.
在接收到附加的第9位数据后,当(RI)=0或者(SM2)=0时,第9位数据才进入RB8,8位数据才能进入接收寄存器,并由硬件置位中断标志RI;否则信息丢失.
且不置位RI.
再过一位时间后,不管上述条件时否满足,接收电路即行复位,并重新检测RXD上从1到0的跳变.
工作方式3方式3为波特率可变的11位UART方式.
除波特率外,其余与方式2相同.
波特率选择如前所述,在串行通讯中,收发双方的数据传送率(波特率)要有一定的约定.
在8051串行口的四种工作方式中,方式0和2的波特率是固定的,而方式1和3的波特率是可变的,由定时器T1的溢出率控制.
方式0方式0的波特率固定为主振频率的1/12.
方式2方式2的波特率由PCON中的选择位SMOD来决定,可由下式表示:波特率=2的SMOD次方除以64再乘一个fosc,也就是当SMOD=1时,波特率为1/32fosc,当SMOD=0时,波特率为1/64fosc3.
方式1和方式3定时器T1作为波特率发生器,其公式如下:波特率=定时器T1溢出率T1溢出率=T1计数率/产生溢出所需的周期数式中T1计数率取决于它工作在定时器状态还是计数器状态.
当工作于定时器状态时,T1计数率为fosc/12;当工作于计数器状态时,T1计数率为外部输入频率,此频率应小于fosc/24.
产生溢出所需周期与定时器T1的工作方式、T1的预置值有关.
定时器T1工作于方式0:溢出所需周期数=8192-x定时器T1工作于方式1:溢出所需周期数=65536-x定时器T1工作于方式2:溢出所需周期数=256-x因为方式2为自动重装入初值的8位定时器/计数器模式,所以用它来做波特率发生器最恰当.
当时钟频率选用11.
0592MHZ时,取易获得标准的波特率,所以很多单片机系统选用这个看起来"怪"的晶振就是这个道理.
下表列出了定时器T1工作于方式2常用波特率及初值.
常用波特率Fosc(MHZ)SMODTH1初值1920011.
05921FDH960011.
05920FDH480011.
05920FAH平凡的单片机教程自学单片机第44页共62页44240011.
05920F4h120011.
05920E8h单片机教程第22222222课:串行口应用编程实例1.
串口方式0应用编程8051单片机串行口方式0为移位寄存器方式,外接一个串入并出的移位寄存器,就可以扩展一个并行口.
例:用8051串行口外接CD4094扩展8位并行输出口,如图所示,8位并行口的各位都接一个发光二极管,要求发光管呈流水灯状态.
串行口方式0的数据传送可采用中断方式,也可采用查询方式,无论哪种方式,都要借助于TI或RI标志.
串行发送时,可以靠TI置位(发完一帧数据后)引起中断申请,在中断服务程序中发送下一帧数据,或者通过查询TI的状态,只要TI为0就继续查询,TI为1就结束查询,发送下一帧数据.
在串行接收时,则由RI引起中断或对RI查询来确定何时接收下一帧数据.
无论采用什么方式,在开始通讯之前,都要先对控制寄存器SCON进行初始化.
在方式0中将,将00H送SCON就可以了.
ORG2000HSTART:MOVSCON,#00H;置串行口工作方式0MOVA,#80H;最高位灯先亮CLRP1.
0;关闭并行输出(避象传输过程中,各LED的"暗红"现象)OUT0:MOVSBUF,A;开始串行输出OUT1:JNBTI,OUT1;输出完否CLRTI;完了,清TI标志,以备下次发送SETBP1.
0;打开并行口输出ACALLDELAY;延时一段时间RRA;循环右移CLRP1.
0;关闭并行输出JMPOUT0;循环说明:DELAY延时子程序可以用前面我们讲P1口流水灯时用的延时子程序,这里就不给出了.
二、异步通讯org0000HAJMPSTARTORG30HSTART:movSP,#5fh;movTMOD,#20h;T1:工作模式2movPCON,#80h;SMOD=1平凡的单片机教程自学单片机第45页共62页45movTH1,#0FDH;初始化波特率(参见表)movSCON,#50h;StandardUARTsettingsMOVR0,#0AAH;准备送出的数SETBREN;允许接收SETBTR1;T1开始工作WAIT:MOVA,R0CPLAMOVR0,AMOVSBUF,ALCALLDELAYJBCTI,WAIT1;如果TI等于1,则清TI并转WAIT1AJMPWAITWAIT1:JBCRI,READ;如果RI等于1,则清RI并转READAJMPWAIT1READ:MOVA,SBUF;将取得的数送P1口MOVP1,ALJMPWAITDELAY:;延时子程序MOVR7,#0ffHDJNZR7,$RETEND将程序编译通过,写入芯片,插入实验板,用通读电缆将实验板与主机的串口相连就可以实验了.
上面的程序功能很简单,就是每隔一段时间向主机轮流送数55H和AAH,并把主机送去的数送到P1口.
可以在PC端用串口精灵来做实验.
串口精灵在我主页上有下载.
运行串口精灵后,按主界面上的"设置参数"按钮进入"设置参数"对话框,按下面的参数进行设置.
注意,我的机器上用的是串口2,如果你不是串口2,请自行更改串口的设置.
设置完后,按确定返回主界面,注意右边有一个下拉列表,应当选中"按16进制".
然后按"开始发送"、"开始接收"就可以了.
按此设置,实验板上应当有两只灯亮,6只灯灭.
大家可以自行更改设置参数中的发送字符如55,00,FF等等,观察灯的亮灭,并分析原因,也可以在主界面上更改下拉列表中的"按16进制"为"按10进制"或"按ASCII字符"来观察现象,并仔细分析.
这对于大家理解16进制、10进制、ASCII字符也是很有好处的.
程序本身很简单,又有注释,这里就不详加说明了.
三、上述程序的中断版本org0000HAJMPSTART平凡的单片机教程自学单片机第46页共62页46org0023hAJMPSERIAL;ORG30HSTART:movSP,#5fh;movTMOD,#20h;T1:工作模式2movPCON,#80h;SMOD=1movTH1,#0FDH;初始化波特率(参见表)movSCON,#50h;StandardUARTsettingsMOVR0,#0AAH;准备送出的数SETBREN;允许接收SETBTR1;T1开始工作SETBEA;开总中断SETBES;开串口中断SJMP$SERIAL:MOVA,SBUFMOVP1,ACLRRIRETIEND本程序没有写入发送程序,大家可以自行添加.
单片机教程第23232323课:LEDLEDLEDLED数码显示器的连接与编程在单片机系统中,通常用LED数码显示器来显示各种数字或符号.
由于它具有显示清晰、亮度高、使用电压低、寿命长的特点,因此使用非常广泛.
八段LED显示器引入:还记得我们小时候玩的"火柴棒游戏"吗,几根火柴棒组合起来,可以拼成各种各样的图形,LED显示器实际上也是这么一个东西.
八段LED显示器由8个发光二极管组成.
基中7个长条形的发光管排列成"日"字形,另一个贺点形的发光管在显示器的右下角作为显示小数点用,它能显示各种数字及部份英文字母.
LED显示器有两种不同的形式:一种是8个发光二极管的阳极都连在一起的,称之为共阳极LED显示器;另一种是8个发光二极管的阴极都连在一起的,称之为共阴极LED显示器.
如下图所示.
`平凡的单片机教程自学单片机第47页共62页47共阴和共阳结构的LED显示器各笔划段名和安排位置是相同的.
当二极管导通时,相应的笔划段发亮,由发亮的笔划段组合而显示的各种字符.
8个笔划段hgfedcba对应于一个字节(8位)的D7D6D5D4D3D2D1D0,于是用8位二进制码就可以表示欲显示字符的字形代码.
例如,对于共阴LED显示器,当公共阴极接地(为零电平),而阳极hgfedcba各段为0111011时,显示器显示"P"字符,即对于共阴极LED显示器,"P"字符的字形码是73H.
如果是共阳LED显示器,公共阳极接高电平,显示"P"字符的字形代码应为10001100(8CH).
这里必须注意的是:很多产品为方便接线,常不按规则的方法去对应字段与位的关系,这时字形码就必须根据接线来自行设计了,后面我们会给出一个例子.
静态显示接口在单片机应用系统中,显示器显示常用两种方法:静态显示和动态扫描显示.
所谓静态显示,就是每一个显示器都要占用单独的具有锁存功能的I/O接口用于笔划段字形代码.
这样单片机只要把要显示的字形代码发送到接口电路,就不用管它了,直到要显示新的数据时,再发送新的字形码,因此,使用这种方法单片机中CPU的开销小.
可以提供单独锁存的I/O接口电路很多,这里以常用的串并转换电路74LS164为例,介绍一种常用静态显示电路,以使大家对静态显示有一定的了解.
MCS-51单片机串行口方式押为移们寄存器方式,外接6片74LS164作为6位LED显示器的静态显示接口,把8031的RXD作为数据输出线,TXD作为移位时钟脉冲.
74LS164为TTL单向8位移位寄存器,可实现串行输入,并行输出.
其中A、B(第1、2脚)为串行数据输入端,2个引脚按逻辑与运算规律输入信号,公一个输入信号时可并接.
T(第8脚)为时钟输入端,可连接到串行口的TXD端.
每一个时钟信号的上升沿加到T端时,移位寄存器移一位,8个时钟脉冲过后,8位二进制数全部移入74LS164中.
R(第9脚)为复位端,当R=0时,移位寄存器各位复0,只有当R=1时,时钟脉冲才起作用.
Q1…Q8(第3-6和10-13引脚)并行输出端分别接LED显示器的hg---a各段对应的引脚上.
关于74LS164还可以作如下的介绍:所谓时钟脉冲端,其实就是需要高、低、高、低的脉冲,不管这个脉冲是怎么来的,比如,我们用根电线,一端接T,一端用手拿着,分别接高电平、低电平,那也是给出时钟脉冲,在74LS164获得时钟脉冲的瞬间(再讲清楚点,是在脉冲的沿),如果数据输入端(第1,2引脚)是高电平,则就会有一个1进入到74LS164的内部,如果数据输入端是低电平,则就会有一个0进入其内部.
在给出了8个脉冲后,最先进入74LS164的第一个数据到达了最高位,然后再来一个脉冲会有什么发生呢再来一个脉冲,第一个脉冲就会从最高位移出,就象车站排队买票,栏杆就那么长,要从后面进去一个人,前面必须要从前面走出去一个人才行.
搞清了这一点,下面让我们来看电路,6片7LS164首尾相串,而时钟端则接在一起,这样,当输入8个脉冲时,从单片机RXD端输出的数据就进入到了第一片74LS164中了,而当第二个8个脉冲到来后,这个数据就进入了第二片74LS164,而新的数据则进入了第一片74LS164,这样,当第六个8个脉冲完成后,首次送出的数据被送到了最左面的164中,其他数据依次出现在第一、二、三、四、五片74LS164中.
有个问题,在第一个脉冲到来时,除了第一片74LS164中接收数据外,其他各片在干吗呢它们也在接收数据,因为它们的时钟端都是被接在一起的,可是数据还没有送到其他各片呢,它们在接收什么数据呢.
.
.
.
.
.
其实所谓数据不过是一种说法而已,实际就是电平的高低,当第一个脉冲到来时,第一片164固然是从单片机接收数据了,而其它各片也接到前一片的Q8上,而Q8是一根电线,在数字电路中它只可能有两种状态:低电平或高电平,也就是"0"和"1".
所以它的下一片74LS164也相当于是在接收数据啊.
只是接收的全部是0或1而已.
这个问题放在这儿说明,可能有朋友不屑一顾,而有的朋友可能还是不清楚,这实际上涉及到数的本质的问题,如果不懂的,请仔细思考,并找一些数字电路的数,理解164的工作原理,再来看这个问题,或者去看看我的另一篇文章《初学单片机易掌握的概念》.
务必搞懂,搞懂了这一点,你的级别就高过初学者,可谓入门者了.
入口:把要显示的数分别放在显示缓冲区60H-65H共6个单元中,并且分别对应各个数码管LED0-LED5.
出口:将预置在显示缓冲区中的6个数成相应的显示字形码,然后输出到显示器中显示.
显示程序如下:DISP:MOVSCON,#00H;初始化串行口方式0MOVR1,#06H;显示6位数平凡的单片机教程自学单片机第48页共62页48MOVR0,#65H;60H-65H为显示缓冲区MOVDPTR,#SEGTAB;字形表的入口地址LOOP:MOVA,@R0;取最高位的待显示数据MOVCA,@A+DPTR;查表获取字形码MOVSBUF,A;送串口显示DELAY:JNBTI,DELAY;等待发送完毕CLRTI;清发送标志DECR0;指针下移一位,准备取下一个待显示数DJNZR1,LOOP;直到6个数据全显示完.
RETSETTAB:;字形表,前面有介绍,以后我们再介绍字形表的制作.
DB03H9FH27H0DH99H49H41H1FH01H09H0FFH;0123456789消隐码测试用主程序ORG0000HAJMPSTARTORG30HSTART:MOVSP,#6FHMOV65H,#0MOV64H,#1MOV63H,#2MOV62H,#3MOV61H,#4MOV60H,#5LCALLDISPSJMP$如果按图示数码管排列,则以上主程序将显示的是543210,想想看,如果要显示012345该怎样送数下面我们来分析一下字形表的制作问题.
先就上述"标准"的图形来看吧.
写出数据位和字形的对应关系并列一个表如下(设为共阳型,也就是相应的输出位为0时笔段亮)如何,字形表会做了吧,就是这样列个表格,根据要求(0亮或1亮)写出相应位的0和1,就成了.
做个练习,写出A-F的字形码吧.
如果为了接线方便而打乱了接线的顺序,那么字形表又该如何接呢也很简单,一样地列表啊.
以新实验板为例,共阳型.
接线如下:P0.
7P0.
6P0.
5P0.
4P0.
3P0.
2P0.
1P0.
0CEHDGFAB则字形码如下所示:;00010100028H;1011111107EH;2101001000A4H;30110010064H;40111001072H;50110000161H;60010000121H;7011111007CH平凡的单片机教程自学单片机第49页共62页49;80010000020H;90110000060H作为练习,大家写出A-F的字形代码.
本来这里是讲解显示器的静态接口的,到此应当可算结束了,但是我还想接着上面讲到的数的本质的问题再谈一点.
单片机中有一些术语、名词本来是帮助我们理解事物的,但有时我们会被这些术语的相关语义所迷惑,以致不能进一步认清他们的本质,由此往往陷入困惑的境界.
只有深入地了解了74LS164的工作特性,才能真正理解何谓串行的数据.
有兴趣的朋友还可以再看看我网站上"其他资料"中的"银行利率屏的设计"一文.

单片机教程第24242424课:动态扫描显示接口动态扫描显示接口是单片机中应用最为广泛的一种显示方式之一.
其接口电路是把所有显示器的8个笔划段a-h同名端连在一起,而每一个显示器的公共极COM是各自独立地受I/O线控制.
CPU向字段输出口送出字形码时,所有显示器接收到相同的字形码,但究竟是那个显示器亮,则取决于COM端,而这一端是由I/O控制的,所以我们就可以自行决定何时显示哪一位了.
而所谓动态扫描就是指我们采用分时的方法,轮流控制各个显示器的COM端,使各个显示器轮流点亮.
在轮流点亮扫描过程中,每位显示器的点亮时间是极为短暂的(约1ms),但由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位显示器并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感.
下图所示就是我们的实验板上的动态扫描接口.
由89C51的P0口能灌入较大的电流,所以我们采用共阳的数码管,并且不用限流电阻,而只是用两只1N4004进行降压后给数码管供电,这里仅用了两只,实际上还可以扩充.
它们的公共端则由PNP型三极管8550控制,显然,如果8550导通,则相应的数码管就可以亮,而如果8550截止,则对应的数码管就不可能亮,8550是由P2.
7,P2.
6控制的.
这样我们就可以通过控制P27、P26达到控制某个数码管亮或灭的目的.
下面的这个程序,就是用实验板上的数码管显示0和1.
FIRSTEQUP2.
7;第一位数码管的位控制SECONDEQUP2.
6;第二位数码管的位控制DISPBUFFEQU5AH;显示缓冲区为5AH和5BHORG0000HAJMPSTARTORG30HSTART:MOVSP,#5FH;设置堆栈MOVP1,#0FFHMOVP0,#0FFHMOVP2,#0FFH;初始化,所显示器,LED灭MOVDISPBUFF,#0;第一位显示0MOVDISPBUFF+1,#1;第二握显示1LOOP:LCALLDISP;调用显示程序AJMPLOOP;主程序到此结束DISP:平凡的单片机教程自学单片机第50页共62页50PUSHACC;ACC入栈PUSHPSW;PSW入栈MOVA,DISPBUFF;取第一个待显示数MOVDPTR,#DISPTAB;字形表首地址MOVCA,@A+DPTR;取字形码MOVP0,A;将字形码送P0位(段口)CLRFIRST;开第一位显示器位口LCALLDELAY;延时1毫秒SETBFIRST;关闭第一位显示器(开始准备第二位的数据)MOVA,DISPBUFF+1;取显示缓冲区的第二位MOVDPTR,#DISPTABMOVCA,@A+DPTRMOVP0,A;将第二个字形码送P0口CLRSECOND;开第二位显示器LCALLDELAY;延时SETBSECOND;关第二位显示POPPSWPOPACCRETDELAY:;延时1毫秒PUSHPSWSETBRS0MOVR7,#50D1:MOVR6,#10D2:DJNZR6,$DJNZR7,D1POPPSWRETDISPTAB:DB28H,7EH,0a4H,64H,72H,61H,21H,7CH,20H,60HEND从上面的例子中可以看出,动态扫描显示必须由CPU不断地调用显示程序,才能保证持续不断的显示.
上面的这个程序可以实现数字的显示,但不太实用,为什么呢这里仅是显示两个数字,并没有做其他的工作,因此,两个数码管轮流显示1毫秒,没有问题,实际的工作中,当然不可能只显示两个数字,还是要做其他的事情的,这样在二次调用显示程序之间的时间间隔就不一不定了,如果时间间隔比较长,就会使显示不连续.
而实际工作中是很难保证所有工作都能在很短时间内完成的.
况且这个显示程序也有点"浪费",每个数码管显示都要占用1个毫秒的时间,这在很多合是不允许的,怎么办呢我们可以借助于定时器,定时时间一到,产生中断,点亮一个数码管,然后马上返回,这个数码管就会一直亮到下一次定时时间到,而不用调用延时程序了,这段时间可以留给主程序干其他的事.
到下一次定时时间到则显示下一个数码管,这样就很少浪费了.

CounterEQU59H;计数器,显示程序通过它得知现正显示哪个数码管FIRSTEQUP2.
7;第一位数码管的位控制SECONDEQUP2.
6;第二位数码管的位控制DISPBUFFEQU5AH;显示缓冲区为5AH和5BHORG0000HAJMPSTARTORG000BH;定时器T0的入口AJMPDISP;显示程序平凡的单片机教程自学单片机第51页共62页51ORG30HSTART:MOVSP,#5FH;设置堆栈MOVP1,#0FFHMOVP0,#0FFHMOVP2,#0FFH;初始化,所显示器,LED灭MOVTMOD,#00000001B;定时器T0工作于模式1(16位定时/计数模式)MOVTH0,#HIGH(65536-2000)MOVTL0,#LOW(65536-2000)SETBTR0SETBEASETBET0MOVCounter,#0;计数器初始化MOVDISPBUFF,#0;第一位始终显示0MOVA,#0LOOP:MOVDISPBUFF+1,A;第二位轮流显示0-9INCALCALLDELAYCJNEA,#10,LOOPMOVA,#0AJMPLOOP;在此中间可以按排任意程序,这里仅作示范.
;主程序到此结束DISP:;定时器T0的中断响应程序PUSHACC;ACC入栈PUSHPSW;PSW入栈MOVTH0,#HIGH(65536-2000);定时时间为2000个周期,约2170微秒(11.
0592M)MOVTL0,#LOW(65536-2000)SETBFIRSTSETBSECOND;关显示MOVA,#DISPBUFF;显示缓冲区首地址ADDA,CounterMOVR0,AMOVA,@R0;根据计数器的值取相应的显示缓冲区的值MOVDPTR,#DISPTAB;字形表首地址MOVCA,@A+DPTR;取字形码MOVP0,A;将字形码送P0位(段口)MOVA,Counter;取计数器的值JZDISPFIRST;如果是0则显示第一位CLRSECOND;否则显示第二位AJMPDISPNEXTDISPFIRST:CLRFIRST;显示第一位DISPNEXT:INCCounter;计数器加1MOVA,Counter平凡的单片机教程自学单片机第52页共62页52DECA;如果计数器计到2,则让它回0DECAJZRSTCOUNTAJMPDISPEXITRSTCOUNT:MOVCounter,#0;计数器的值只能是0或1DISPEXIT:POPPSWPOPACCRETIDELAY:;延时130毫秒PUSHPSWSETBRS0MOVR7,#255D1:MOVR6,#255D2:NOPNOPNOPNOPDJNZR6,D2DJNZR7,D1POPPSWRETDISPTAB:DB28H,7EH,0a4H,64H,72H,61H,21H,7CH,20H,60HEND从上面的程序可以看出,和静态显示相比,动态扫描的程序稍有点复杂,不过,这是值得的.
这个程序有一定的通用性,只要改变端口的值及计数器的值就可以显示更多位数了.
下面给出显示程序的流程图.

平凡的单片机教程自学单片机第53页共62页53单片机教程第25252525课:键盘接口与编程键盘是由若干按键组成的开关矩阵,它是微型计算机最常用的输入设备,用户可以通过键盘向计算机输入指令、地址和数据.
一般单片机系统中采和非编码键盘,非编码键盘是由软件来识别键盘上的闭合键,它具有结构简单,使用灵活等特点,因此被广泛应用于单片机系统.
按键开关的抖动问题组成键盘的按键有触点式和非触点式两种,单片机中应用的一般是由机械触点构成的.
在下图中,当开图1图2关S未被按下时,P1.
0输入为高电平,S闭合后,P1.
0输入为低电平.
由于按键是机械触点,当机械触点断开、闭合时,会有抖动动,P1.
0输入端的波形如图2所示.
这种抖动对于人来说是感觉不到的,但对计算机来说,则是完全可以感应到的,因为计算机处理的速度是在微秒级,而机械抖动的时间至少是毫秒级,对计算机而言,这已是一个"漫长"的时间了.
前面我们讲到中断时曾有个问题,就是说按键有时灵,有时不灵,其实就是这个原因,你只按了一次按键,可是计算机却已执行了多次中断的过程,如果执行的次数正好是奇数次,那么结果正如你所料,如果执行的次数是偶数次,那就不对了.
为使CPU能正确地读出P1口的状态,对每一次按键只作一次响应,就必须考虑如何去除抖动,常用的去抖动的方法有两种:硬件方法和软件方法.
单片机中常用软件法,因此,对于硬件方法我们不介绍.
软件法其实很简单,就是在单片机获得P1.
0口为低的信息后,不是立即认定S1已被按下,而是延时10毫秒或更长一些时间后再次检测P1.
0口,如果仍为低,说明S1的确按下了,这实际上是避开了按键按下时的抖动时间.
而在检测到按键释放后(P1.
0为高)再延时5-10个毫秒,消除后沿的抖动,然后再对键值处理.
不过一般情况下,我们通常不对按键释放的后沿进行处理,实践证明,也能满足一定的要求.
当然,实际应用中,对按键的要求也是千差万别,要根据不同的需要来编制处理程序,但以上是消除键抖动的原则.
键盘与单片机的连接图3图41、通过1/0口连接.
将每个按键的一端接到单片机的I/O口,另一端接地,这是最简单的方法,如图3所示是实验板上按键的接法,四个按键分别接到P3.
2、P3.
3、P3.
4和P3.
5.
对于这种键各程序可以采用不断查询的方法,功能就是:检测是否有键闭合,如有键闭合,则去除键抖动,判断键号并转入相应的键处理.
下面给出一个例程.
其功能很简单,四个键定义如下:P3.
2:开始,按此键则灯开始流动(由上而下)P3.
3:停止,按此键则停止流动,所有灯为暗P3.
4:上,按此键则灯由上向下流动P3.
5:下,按此键则灯由下向上流动平凡的单片机教程自学单片机第54页共62页54UpDownEQU00H;上下行标志StartEndEQU01H;起动及停止标志LAMPCODEEQU21H;存放流动的数据代码ORG0000HAJMPMAINORG30HMAIN:MOVSP,#5FHMOVP1,#0FFHCLRUpDown;启动时处于向上的状态CLRStartEnd;启动时处于停止状态MOVLAMPCODE,#0FEH;单灯流动的代码LOOP:ACALLKEY;调用键盘程序JNBF0,LNEXT;如果无键按下,则继续ACALLKEYPROC;否则调用键盘处理程序LNEXT:ACALLLAMP;调用灯显示程序AJMPLOOP;反复循环,主程序到此结束DELAY:MOVR7,#100D1:MOVR6,#100DJNZR6,$DJNZR7,D1RET延时程序,键盘处理中调用KEYPROC:MOVA,B;从B寄存器中获取键值JBACC.
2,KeyStart;分析键的代码,某位被按下,则该位为1(因为在键盘程序中已取反)JBACC.
3,KeyOverJBACC.
4,KeyUpJBACC.
5,KeyDownAJMPKEY_RETKeyStart:SETBStartEnd;第一个键按下后的处理AJMPKEY_RETKeyOver:CLRStartEnd;第二个键按下后的处理AJMPKEY_RETKeyUp:SETBUpDown;第三个键按下后的处理AJMPKEY_RETKeyDown:CLRUpDown;第四个键按下后的处理KEY_RET:RETKEY:CLRF0;清F0,表示无键按下.
平凡的单片机教程自学单片机第55页共62页55ORLP3,#00111100B;将P3口的接有键的四位置1MOVA,P3;取P3的值ORLA,#11000011B;将其余4位置1CPLA;取反JZK_RET;如果为0则一定无键按下ACALLDELAY;否则延时去键抖ORLP3,#00111100BMOVA,P3ORLA,#11000011BCPLAJZK_RETMOVB,A;确实有键按下,将键值存入B中SETBF0;设置有键按下的标志K_RET:ORLP3,#00111100B;此处循环等待键的释放MOVA,P3ORLA,#11000011BCPLAJZK_RET1;直到读取的数据取反后为0说明键释放了,才从键盘处理程序中返回AJMPK_RETK_RET1:RETD500MS:;流水灯的延迟时间PUSHPSWSETBRS0MOVR7,#200D51:MOVR6,#250D52:NOPNOPNOPNOPDJNZR6,D52DJNZR7,D51POPPSWRETLAMP:JBStartEnd,LampStart;如果StartEnd=1,则启动MOVP1,#0FFHAJMPLAMPRET;否则关闭所有显示,返回LampStart:JBUpDown,LAMPUP;如果UpDown=1,则向上流动MOVA,LAMPCODERLA;实际就是左移位而已MOVLAMPCODE,AMOVP1,ALCALLD500MS平凡的单片机教程自学单片机第56页共62页56AJMPLAMPRETLAMPUP:MOVA,LAMPCODERRA;向下流动实际就是右移MOVLAMPCODE,AMOVP1,ALCALLD500MSLAMPRET:RETEND以上程序功能很简单,但它演示了一个键盘处理程序的基本思路,程序本身很简单,也不很实用,实际工作中还会有好多要考虑的因素,比如主循环每次都调用灯的循环程序,会造成按键反应"迟钝",而如果一直按着键不放,则灯不会再流动,一直要到松开手为止,等等,大家可以仔细考虑一下这些问题,再想想有什么好的解决办法.
2、采用中断方式:如图4所示.
各个按键都接到一个与非上,当有任何一个按键按下时,都会使与门输出为低电平,从而引起单片机的中断,它的好处是不用在主程序中不断地循环查询,如果有键按下,单片机再去做相应的处理.
单片机教程第26262626课:矩阵式键盘接口技术及编程在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式,如图1所示.
在矩阵式键盘中,每条水平线和垂直线在交叉处不直接连通,而是通过一个按键加以连接.
这样,一个端口(如P1口)就可以构成4*4=16个按键,比之直接将端口线用于键盘多出了一倍,而且线数越多,区别越明显,比如再多加一条线就可以构成20键的键盘,而直接用端口线则只能多出一键(9键).
由此可见,在需要的键数比较多时,采用矩阵法来做键盘是合理的.
矩阵式结构的键盘显然比直接法要复杂一些,识别也要复杂一些,上图中,列线通过电阻接正电源,并将行线所接的单片机的I/O口作为输出端,而列线所接的I/O口则作为输入.
这样,当按键没有按下时,所有的输出端都是高电平,代表无键按下.
行线输出是低电平,一旦有键按下,则输入线就会被拉低,这样,通过读入输入线的状态就可得知是否有键按下了.
具体的识别及编程方法如下所述.
矩阵式键盘的按键识别方法确定矩阵式键盘上何键被按下介绍一种"行扫描法".
平凡的单片机教程自学单片机第57页共62页57行扫描法行扫描法又称为逐行(或列)扫描查询法,是一种最常用的按键识别方法,如上图所示键盘,介绍过程如下.
判断键盘中有无键按下将全部行线Y0-Y3置低电平,然后检测列线的状态.
只要有一列的电平为低,则表示键盘中有键被按下,而且闭合的键位于低电平线与4根行线相交叉的4个按键之中.
若所有列线均为高电平,则键盘中无键按下.
判断闭合键所在的位置在确认有键按下后,即可进入确定具体闭合键的过程.
其方法是:依次将行线置为低电平,即在置某根行线为低电平时,其它线为高电平.
在确定某根行线位置为低电平后,再逐行检测各列线的电平状态.
若某列为低,则该列线与置为低电平的行线交叉处的按键就是闭合的按键.

下面给出一个具体的例子:图仍如上所示.
8031单片机的P1口用作键盘I/O口,键盘的列线接到P1口的低4位,键盘的行线接到P1口的高4位.
列线P1.
0-P1.
3分别接有4个上拉电阻到正电源+5V,并把列线P1.
0-P1.
3设置为输入线,行线P1.
4-P.
17设置为输出线.
4根行线和4根列线形成16个相交点.
检测当前是否有键被按下.
检测的方法是P1.
4-P1.
7输出全"0",读取P1.
0-P1.
3的状态,若P1.
0-P1.
3为全"1",则无键闭合,否则有键闭合.
去除键抖动.
当检测到有键按下后,延时一段时间再做下一步的检测判断.

若有键被按下,应识别出是哪一个键闭合.
方法是对键盘的行线进行扫描.
P1.
4-P1.
7按下述4种组合依次输出:P1.
71110P1.
61101P1.
51011P1.
40111在每组行输出时读取P1.
0-P1.
3,若全为"1",则表示为"0"这一行没有键闭合,否则有键闭合.
由此得到闭合键的行值和列值,然后可采用计算法或查表法将闭合键的行值和列值转换成所定义的键值为了保证键每闭合一次CPU仅作一次处理,必须却除键释放时的抖动.
平凡的单片机教程自学单片机第58页共62页58键盘扫描程序:从以上分析得到键盘扫描程序的流程图如图2所示.
程序如下SCAN:MOVP1,#0FHMOVA,P1ANLA,#0FHCJNEA,#0FH,NEXT1SJMPNEXT3NEXT1:ACALLD20MSMOVA,#0EFHNEXT2:MOVR1,AMOVP1,AMOVA,P1ANLA,#0FHCJNEA,#0FH,KCODE;MOVA,R1SETBCRLCAJCNEXT2NEXT3:MOVR0,#00HRETKCODE:MOVB,#0FBHNEXT4:RRCAINCBJCNEXT4MOVA,R1SWAPANEXT5:RRCAINCBINCBINCBINCBJCNEXT5NEXT6:MOVA,P1平凡的单片机教程自学单片机第59页共62页59ANLA,#0FHCJNEA,#0FH,NEXT6MOVR0,#0FFHRET键盘处理程序就作这么一个简单的介绍,实际上,键盘、显示处理是很复杂的,它往往占到一个应用程序的大部份代码,可见其重要性,但说到,这种复杂并不来自于单片机的本身,而是来自于操作者的习惯等等问题,因此,在编写键盘处理程序之前,最好先把它从逻辑上理清,然后用适当的算法表示出来,最后再去写代码,这样,才能快速有效地写好代码.
到本课为止,本站教程暂告一个段落!
感谢大家的关心和支持!
单片机教程第27272727课:初学单片机几个不易掌握的概念随着电子技术的迅速发展,计算机已深入地渗透到我们的生活中,许多电子爱好者开始学习单片机知识,但单片机的内容比较抽象,相对电子爱好者已熟悉的模拟电路、数字电路,单片机中有一些新的概念,这些概念非常基本以至于一般作者不屑去谈,教材自然也不会很深入地讲解这些概念,但这些内容又是学习中必须要理解的,下面就结合本人的学习、教学经验,对这些最基本概念作一说明,希望对自学者有所帮助.

一、总线:我们知道,一个电路总是由元器件通过电线连接而成的,在模拟电路中,连线并不成为一个问题,因为各器件间一般是串行关系,各器件之间的连线并不很多,但计算机电路却不一样,它是以微处理器为核心,各器件都要与微处理器相连,各器件之间的工作必须相互协调,所以就需要的连线就很多了,如果仍如同模拟电路一样,在各微处理器和各器件间单独连线,则线的数量将多得惊人,所以在微处理机中引入了总线的概念,各个器件共同享用连线,所有器件的8根数据线全部接到8根公用的线上,即相当于各个器件并联起来,但仅这样还不行,如果有两个器件同时送出数据,一个为0,一个为1,那么,接收方接收到的究竟是什么呢这种情况是不允许的,所以要通过控制线进行控制,使器件分时工作,任何时候只能有一个器件发送数据(可以有多个器件同时接收).
器件的数据线也就被称为数据总线,器件所有的控制线被称为控制总线.
在单片机内部或者外部存储器及其它器件中有存储单元,这些存储单元要被分配地址,才能使用,分配地址当然也是以电信号的形式给出的,由于存储单元比较多,所以,用于地址分配的线也较多,这些线被称为地址总线.

二、数据、地址、指令:之所以将这三者放在一起,是因为这三者的本质都是一样的数字,或者说都是一串'0'和'1'组成的序列.
换言之,地址、指令也都是数据.
指令:由单片机芯片的设计者规定的一种数字,它与我们常用的指令助记符有着严格的一一对应关系,不可以由单片机的开发者更改.
地址:是寻找单片机内部、外部的存储单元、输入输出口的依据,内部单元的地址值已由芯片设计者规定好,不可更改,外部的单元可以由单片机开发者自行决定,但有一些地址单元是一定要有的(详见程序的执行过程).
数据:这是由微处理机处理的对象,在各种不同的应用电路中各不相同,一般而言,被处理的数据可能有这么几种情况:1·地址(如MOVDPTR,#1000H),即地址1000H送入DPTR.
2·方式字或控制字(如MOVTMOD,#3),3即是控制字.
3·常数(如MOVTH0,#10H)10H即定时常数.
4·实际输出值(如P1口接彩灯,要灯全亮,则执行指令:MOVP1,#0FFH,要灯全暗,则执行指令:MOVP1,#00H)这里0FFH和00H都是实际输出值.
又如用于LED的字形码,也是实际输出的值.
理解了地址、指令的本质,就不难理解程序运行过程中为什么会跑飞,会把数据当成指令来执行了.

三、P0口、P2口和P3的第二功能用法初学时往往对P0口、P2口和P3口的第二功能用法迷惑不解,认为平凡的单片机教程自学单片机第60页共62页60第二功能和原功能之间要有一个切换的过程,或者说要有一条指令,事实上,各端口的第二功能完全是自动的,不需要用指令来转换.
如P3.
6、P3.
7分别是WR、RD信号,当微片理机外接RAM或有外部I/O口时,它们被用作第二功能,不能作为通用I/O口使用,只要一微处理机一执行到MOVX指令,就会有相应的信号从P3.
6或P3.
7送出,不需要事先用指令说明.
事实上'不能作为通用I/O口使用'也并不是'不能'而是(使用者)'不会'将其作为通用I/O口使用.
你完全可以在指令中按排一条SETBP3.
7的指令,并且当单片机执行到这条指令时,也会使P3.
7变为高电平,但使用者不会这么去做,因为这通常这会导致系统的崩溃(即死机).

四、程序的执行过程单片机在通电复位后8051内的程序计数器(PC)中的值为'0000',所以程序总是从'0000'单元开始执行,也就是说:在系统的ROM中一定要存在'0000'这个单元,并且在'0000'单元中存放的一定是一条指令.
五、堆栈堆栈是一个区域,是用来存放数据的,这个区域本身没有任何特殊之处,就是内部RAM的一部份,特殊的是它存放和取用数据的方式,即所谓的'先进后出,后进先出',并且堆栈有特殊的数据传输指令,即'PUSH'和'POP',有一个特殊的专为其服务的单元,即堆栈指针SP,每当执一次PUSH指令时,SP就(在原来值的基础上)自动加1,每当执行一次POP指令,SP就(在原来值的基础上)自动减1.
由于SP中的值可以用指令加以改变,所以只要在程序开始阶段更改了SP的值,就可以把堆栈设置在规定的内存单元中,如在程序开始时,用一条MOVSP,#5FH指令,就时把堆栈设置在从内存单元60H开始的单元中.
一般程序的开头总有这么一条设置堆栈指针的指令,因为开机时,SP的初始值为07H,这样就使堆栈从08H单元开始往后,而08H到1FH这个区域正是8031的第二、三、四工作寄存器区,经常要被使用,这会造成数据的浑乱.
不同作者编写程序时,初始化堆栈指令也不完全相同,这是作者的习惯问题.
当设置好堆栈区后,并不意味着该区域成为一种专用内存,它还是可以象普通内存区域一样使用,只是一般情况下编程者不会把它当成普通内存用了.

六、单片机的开发过程这里所说的开发过程并不是一般书中所说的从任务分析开始,我们假设已设计并制作好硬件,下面就是编写软件的工作.
在编写软件之前,首先要确定一些常数、地址,事实上这些常数、地址在设计阶段已被直接或间接地确定下来了.
如当某器件的连线设计好后,其地址也就被确定了,当器件的功能被确定下来后,其控制字也就被确定了.
然后用文本编缉器(如EDIT、CCED等)编写软件,编写好后,用编译器对源程序文件编译,查错,直到没有语法错误,除了极简单的程序外,一般应用仿真机对软件进行调试,直到程序运行正确为止.
运行正确后,就可以写片(将程序固化在EPROM中).
在源程序被编译后,生成了扩展名为HEX的目标文件,一般编程器能够识别这种格式的文件,只要将此文件调入即可写片.
在此,为使大家对整个过程有个认识,举一例说明:ORG0000HLJMPSTARTORG040HSTART:MOVSP,#5FH;设堆栈LOOP:NOPLJMPLOOP;循环END;结束表1:03000000020040BB:0700400075815F000200431F表2020040FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75815F00020043表3平凡的单片机教程自学单片机第61页共62页61表1为源程序,表2是汇编后得到的HEX文件,表3是由HEX文件转换成的目标文件,也就是最终写入EPROM的文件,它由编程器转换得到,也可以由HEXBIN一类的程序转换得到.
学过手工汇编者应当不难找出表3与表1的一一对应关系,值得注意的是从020040后开始的一长串'FF',直到7581,这是由于伪指令:ORG040H造成的结果.
七、仿真、仿真机仿真是单片机开发过程中非常重要的一个环节,除了一些极简单的任务,一般产品开发过程中都要进行仿真,仿真的主要目的是进行软件调试,当然借助仿真机,也能进行一些硬件排错.
一块单片机应用电路板包括单片机部份及为达到使用目的而设计的应用电路,仿真就是利用仿真机来代替应用电路板(称目标机)的单片机部份,对应用电路部份进行测试、调试.
仿真有CPU仿真和ROM仿真两种,所谓CPU仿真是指用仿真机代替目标机的CPU,由仿真机向目标机的应用电路部份提供各种信号、数据,进行调试的方法.
这种仿真可以通过单步运行、连续运行等多种方法来运行程序,并能观察到单片机内部的变化,便于改正程序中的错误.

所谓ROM仿真,就是用仿真机代替目标机的ROM,目标机的CPU工作时,从仿真机中读取程序,并执行.
这种仿真其实就是将仿真机当成一片EPROM,只是省去了擦片、写片的麻烦,并没有多少调试手段可言.
通常这是二种不同类型的仿真机,也就是说,一台仿真机不能既做CPU仿真,又做ROM仿真.
可能的情况下,当然以CPU仿真好.
以上系个人对单片机的理解,如有不对之处,请诸位大虾多多指点.
发表您的高见.

单片机教程第28282828课:单片机音乐程序的设计与实验周振安利用单片机(或单板机)奏乐大概是无线电爱好者感兴趣的问题之一.
本文从单片机的基本发间实验出发,谈谈音乐程序的设计原理,并给出具体实例,以供参考.
单片机的基本发音实验我们知道,声音的频谱范围约在几十到几千赫兹,若能利用程序来控制单处机某个口线的"高"电平或低电平,则在该口线上就能产生一定频率的矩形波,接上喇叭就能发出一定频率的声音,若再利用延时程序控制"高""低"电平的持续时间,就能改变输出频率,从而改变音调.
例如,要产生200HZ的音频信号,按图1接入喇叭(若属临时实验,也可将喇叭直接接在P1口线上),实验程序为:其中子程序DEL为延时子程序,当R3为1时,延时时间约为20us,R3中存放延时常数,对200HZ音频,其周期为1/200秒,即5ms.
这样,当P1.
4的高电平或低电平的持续时间为2.
5ms,即R3的时间常数取2500/20=125(7DH)时,就能发出200HZ的音调.
将上述程序键入学习机,并不断修改R3的常数可以感到音调的变化.
乐曲中,每一音符对应着确定的频率,表1给出C调时各音符频率及其相应的时间常数.
读者可以根据表1所提供的常数,将其16进制代码送入R3,反复练习体会.
根据表1可以奏出音符.
仅这还不够,要准确奏出一首曲子,必须准确地控制乐曲节奏,即一音符的持续时间.
音符的节拍我们可以用定时器T0来控制,送入不同的初值,就可以产生不同的定时时间.
便如某歌曲的节奏为每分钟94拍,即一拍为0.
64秒.
其它节拍与时间的对应关系见表2.
但时,由于T0的最大定时时间只能为131毫秒,因此不可能直接用改变T0的时间初值来实现不同节拍.
我们可以用T0来产生10毫秒的时间基准,然后设置一个中断计数器,通过判别中断计数器的值来控制节拍时间的长短.
表2中也给出了各种节拍所对应的时间常数.
例如对1/4拍音符,定时时间为0.
16秒,相应的时间常数为16(即10H);对3拍音符,定时时间为1.
92秒,相应时间长数为192(即C0H).
我们将每一音符的时间常数和其相应的节拍常数作为一组,按顺序将乐曲中的所有常数排列成一个表,然后由查表程序依次取出,产生音符并控制节奏,就可以实现演奏效果.
此外,结束符和体止符可以分别用代码00H和FFH来表示,若查表结果为00H,则表示曲子终了;若查表结果为FFH,则产生相应的停顿效果.
为了产生手弹的节奏感,在某些音符(例如两个相同音符)音插入一个时间单位的频率略有不同的音符.

程序框图如图2所示.
平凡的单片机教程自学单片机第62页共62页62下面给出程序序请单,可直接在TD-III型学习机上演奏,对其它不同型号的学习机,只需相应地改变一下地址即可.
本程序演奏的是民歌"八月桂花遍地开",C调,节奏为94拍/分.
读者也可以自行找出一首歌,按表1和表2给定的常数,将乐曲翻译成码表输入机器,而程序不变.
本实验方法简便,即使不懂音乐的人,将一首陌生的曲子翻译成代码也是易事,和着机器的演奏学唱一首歌曲,其趣味无穷.

Digital-VM:服务器,$80/月;挪威/丹麦英国/Digital-VM:日本/新加坡/digital-vm:日本VPS仅$2.4/月

digital-vm怎么样?digital-vm在今年1月份就新增了日本、新加坡独立服务器业务,但是不知为何,期间终止了销售日本服务器和新加坡服务器,今天无意中在webhostingtalk论坛看到Digital-VM在发日本和新加坡独立服务器销售信息。服务器硬件是 Supermicro、采用最新一代 Intel CPU、DDR4 RAM 和 Enterprise Samsung SSD内存,默认...

提速啦(24元/月)河南BGP云服务器活动 买一年送一年4核 4G 5M

提速啦的来历提速啦是 网站 本着“良心 便宜 稳定”的初衷 为小白用户避免被坑 由赣州王成璟网络科技有限公司旗下赣州提速啦网络科技有限公司运营 投资1000万人民币 在美国Cera 香港CTG 香港Cera 国内 杭州 宿迁 浙江 赣州 南昌 大连 辽宁 扬州 等地区建立数据中心 正规持有IDC ISP CDN 云牌照 公司。公司购买产品支持3天内退款 超过3天步退款政策。提速啦的市场定位提速啦主...

搬瓦工VPS:高端线路,助力企业运营,10Gbps美国 cn2 gia,1Gbps香港cn2 gia,10Gbps日本软银

搬瓦工vps(bandwagonhost)现在面向中国大陆有3条顶级线路:美国 cn2 gia,香港 cn2 gia,日本软银(softbank)。详细带宽是:美国cn2 gia、日本软银,都是2.5Gbps~10Gbps带宽,香港 cn2 gia为1Gbps带宽,搬瓦工是目前为止,全球所有提供这三种带宽的VPS(云服务器)商家里面带宽最大的,成本最高的,没有第二家了! 官方网站:https...

push什么意思为你推荐
盗版win8.1升级win10电脑预装正版win8,后来装成盗版win8.1了,请问还能不能升级win10呀美团月付怎么关闭美团打车免密去付关掉了,怎么回复美国10次啦导航美国GPS导航卫星涡轮增压和自然吸气哪个好涡轮增压与自然吸气哪个好无纺布和熔喷布口罩哪个好一次性口罩的熔喷布和KN95的熔喷布有什么不同?qq空间登录网址如何查询QQ空间登入地址?YunOSYunOS怎么样,有用过的吗?铁通dns服务器地址adsl铁通要设置dns服务器地址吗360云盘网页版360云盘电脑版与网页版有什么区别?如果资料全存在360云盘电脑版里面,那网页版会是空的还是自动同步?假如电脑坏了,或重装了,那电脑版里面的资料如何才能找回不?360云盘关闭360云盘关闭了 里面的东西怎么办
我的世界服务器租用 pccw 网络星期一 云主机51web 华为4核 ca4249 cdn联盟 hktv 网站加速软件 中国域名 江苏双线 开心online 蓝队云 cx域名 globalsign blaze 遨游论坛 vpsaa stealthy 隐士ddos 更多