单片机详解.docx
- 文档编号:17648756
- 上传时间:2023-07-27
- 格式:DOCX
- 页数:24
- 大小:377.42KB
单片机详解.docx
《单片机详解.docx》由会员分享,可在线阅读,更多相关《单片机详解.docx(24页珍藏版)》请在冰点文库上搜索。
单片机详解
(第六课)上两次我们做过两个实验,都是让P1.0这个管脚使灯亮,我们能设想:
既然P1.0能让灯亮,那么其它的管脚可不能呢?
看一下,它是8031单片机管脚的说明,在P1.0旁边有P1.1,P1.2….P1.7,它们是否都能让灯亮呢?
除了以P1开头的外,还有以P0,P2,P3开头的,数一下,一共是32个管脚,前面我们以学过7个管脚,加上这32个这39个了。
它们都以P字开头,只是后面的数字不一样,它们是否有什么联系呢?
它们能不能都让灯亮呢?
在我们的实验板上,除了P10之外,还有P11->P17都与LED相连,下面让我们来做一个实验,程序如下:
MAIN:
MOVP1,#0FFH
LCALLDELAY
MOVP1,#00H
LCALLDELAY
LJMPMAIN
DELAY:
MOVR7,#250
D1:
MOVR6,#250
D2:
DJNZR6,D2
DJNZR7,D1
RET
END
将这段程序转为机器码,用编程器写入单片机中,结果如何?
通电以后我们能看到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,#0FFH
LOOP:
MOVA,P3
MOVP1,A
LJMPLOOP
先看一下这个实验的结果:
所有灯全部不亮,然后我按下一个按钮,第
(1)个灯亮了,再按下另一个按钮,第
(2)个灯亮了,松开按钮灯就灭了。
从这个实验现象结合电路来分析一下程序。
从硬件电路的连线能看出,有四个按钮被接入到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个管脚就称之为并行口,下面我们就对并行口的结构作一个分析,看一下它是怎样实现输入和输出的。
并行口结构分析:
1、输出结构
<并行口结构图>
先看P1口的一位的结构示意图(只画出了输出部份):
从图中能看出,开关的打开和合上代表了管脚输出的高和低,如果开关合上了,则管脚输出就是低,如果开关打开了,则输出高电平,这个开关是由一根线来控制的,这根数据总线是出自于CPU,让我们回想一下,数据总线是一根大家公用的线,很多的器件和它连在一起,在不一样的时候,不一样的器件当然需要不一样的信号,如某一时刻我们让这个管脚输出高电平,并要求保持若干时间,在这段时间里,计算机当然在忙个不停,在与其它器件进行联络,这根控制线上的电平未必能保持原来的值不变,输出就会发生变化了。
怎么解决这个问题呢?
我们在存储器一节中学过,存储器中是能存放电荷的,我们不妨也加一个小的存储器的单元,并在它的前面加一个开关,要让这一位输出时,就把开关打开,信号就进入存储器的单元,然后马上关闭开关,这样这一位的状态就被保存下来,直到下一次命令让它把开关再打开为止。
这样就能使这一位的状态与别的器件无关了,这么一个小单元,我们给它一个很形象的名字,称之为“锁存器”。
2、输入结构
这是并行口的一位的输出结构示意图,再看,除了输出之外,还有两根线,一根从外部管脚接入,另一根从锁存器的输出接出,分别标明读管脚和读锁存器。
这两根线是用于从外部接收信号的,为什么要两根呢?
原来,在51单片机中输入有两种方式,分别称为‘读管脚’和‘读锁存器’,第一种方式是将管脚作为输入,那是真正地从外部管脚读进输入的值,第二种方式是该管脚处于输出状态时,有时需要改变这一位的状态,则并不需要真正地读管脚状态,而只是读入锁存器的状态,然后作某种变换后再输出。
请注意输入结构图,如果将这一根引线作为输入口使用,我们并不能保证在任何时刻都能得到正确的结果(为什么?
)参考图2输入示意图。
接在外部的开关如果打开,则应当是输入1,而如果闭合开关,则输入0,但是,如果单片机内部的开关是闭合的,那么不管外部的开关是开还是闭,单片机接受到的数据都是0。
可见,要让这一端口作为输入使用,要先做一个‘准备工作’,就是先让内部的开关断开,也就是让端口输出‘1’才行。
正因为要先做这么一个准备工作,所以我们称之为“准双向I/O口”。
以上是P1口的一位的结构,P1口其它各位的结构与之相同,而其它三个口:
P0、P2、P3则除入作为输入输出口之外还有其它用途,所以结构要稍复杂一些,但其用于输入、输出的结构是相同的。
看图()。
对我们来说,这些附加的功能不必由我们来控制,所以我们就不去关心它了。
(第七课)(通过前面的学习,我们已知单片机的内部有ROM、有RAM、有并行I/O口,那么,除了这些东西之外,单片机内部究竟还有些什么,这些个零碎的东西怎么连在一起的,让我们来对单片机内部的寄存器作一个完整的功能分析吧!
下图中我们能看出,在51单片机内部有一个CPU用来运算、控制,有四个并行I/O口,分别是P0、P1、P2、P3,有ROM,用来存放程序,有RAM,用来存放中间结果,此外还有定时/计数器,串行I/O口,中断系统,以及一个内部的时钟电路。
在一个51单片机的内部包含了这么多的东西。
<单片机内部结构图>
对上面的图进行进一步的分析,我们已知,对并行I/O口的读写只要将数据送入到对应I/O口的锁存器就能了,那么对于定时/计数器,串行I/O口等怎么用呢?
在单片机中有一些独立的存储单元是用来控制这些器件的,被称之为特殊功能寄存器(SFR)。
事实上,我们已接触过P1这个特殊功能寄存器了,还有哪些呢?
看下表1
符号
地址
功能介绍
B
F0H
B寄存器
ACC
E0H
累加器
PSW
D0H
程序状态字
IP
B8H
中断优先级控制寄存器
P3
B0H
P3口锁存器
IE
A8H
中断允许控制寄存器
P2
A0H
P2口锁存器
SBUF
99H
串行口锁存器
SCON
98H
串行口控制寄存器
P1
90H
P1口锁存器
TH1
8DH
定时器/计数器1(高8位)
TH0
8CH
定时器/计数器1(低8位)
TL1
8BH
定时器/计数器0(高8位)
TL0
8AH
定时器/计数器0(低8位)
TMOD
89A
定时器/计数器方式控制寄存器
TCON
88H
定时器/计数器控制寄存器
DPH
83H
数据地址指针(高8位)
DPL
82H
数据地址指针(低8位)
SP
81H
堆栈指针
P0
80H
P0口锁存器
PCON
87H
电源控制寄存器
表1
<特殊功能寄存器地址映象表
(一)>
<特殊功能寄存器地址映象表
(二)>
<特殊功能寄存器地址映象表(三)>
下面,我们介绍一下几个常用的SFR,看图2。
ACC:
累加器,常常用A表示。
这是个什么东西,可不能从名字上理解,它是一个寄存器,而不是一个做加法的东西,为什么给它这么一个名字呢?
或许是因为在运算器做运算时其中一个数一定是在ACC中的缘故吧。
它的名字特殊,身份也特殊,稍后我们将学到指令,能发现,所有的运算类指令都离不开它。
2、B:
一个寄存器。
在做乘、除法时放乘数或除数,不做乘除法时,随你怎么用。
3、PSW:
程序状态字。
这是一个很重要的东西,里面放了CPU工作时的很多状态,借此,我们能了解CPU的当前状态,并作出对应的处理。
它的各位功能请看表2
D7
D6
D5
D4
D3
D2
D1
D0
CY
AC
F0
RS1
RS0
OV
P
表2
PSW也称为标志寄存器,了解这个对于了解单片机原理非常的重要,存放各有关标志。
其结构和定义如下:
下面我们逐一介绍sfr各位的用途
(1)CY:
进位标志。
用于表示Acc.7有否向更高位进位。
8051中的运算器是一种8位的运算器,我们知道,8位运算器只能表示到0-255,如果做加法的话,两数相加可能会超过255,这样最高位就会丢失,造成运算的错误,怎么办?
最高位就进到这里来。
这样就没事了。
例:
78H+97H(01111000+10010111)
(2)AC:
辅助进位标志也叫半进位标志。
用于表示Acc.3有否向Acc.4进位
例:
57H+3AH(01010111+00111010)
(3)F0:
用户标志位,由我们(编程人员)决定什么时候用,什么时候不用。
(4)RS1、RS0:
工作寄存器组选择位。
这个我们已知了。
RS1、RS0=00——0区(00H~07H)
RS1、RS0=01——1区(08H~0FH)
RS1、RS0=10——2区(10H~17H)
RS1、RS0=11——3区(18H~1FH)
(5)0V:
溢出标志位。
表示Acc在有符号数算术运算中的溢出,什么是溢出我们稍后再谈吧。
(6)P:
奇偶校验位:
它用来表示ALU运算结果中二进制数位“1”的个数的奇偶性。
若为奇数,则P=1,不然为0。
例:
某运算结果是78H(01111000),显然1的个数为偶数,所以P=0。
4、DPTR(DPH、DPL):
数据指针,能用它来访问外部数据存储器中的任一单元,如果不用,也能作为通用寄存器来用,由我们自已决定如何使用。
16位,由两个8位寄存器DPH、DPL组成。
主要用于存放一个16位地址,作为访问外部存储器(外RAM和ROM)的地址指针。
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,我们在用到时再介绍。
(第八课)通过前面的学习,我们已经了解了单片机内部的结构,并且也已经知道,要控制单片机,让它为我们干学,要用指令,我们已学了几条指令,但很零散,从现在开始,我们将要系统地学习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
-----------------------------------------------------
MAIN:
SETBP1.0 ;(1)
MOV30H,#255
LCALLDELAY;
CLRP1.0 ;(3)
MOV30H,#200
LCALLDELAY ;(4)
AJMPMAIN ;(5)
;以下子程序
DELAY:
MOVR7,30H ;(6)
D1:
MOVR6,#250 ;(7)
D2:
DJNZR6,D2 ;(8)
DJNZR7,D1 ;(9)
RET ;(10)
END ;(11)
表2
这样一来,我每次调用延时程序延时的时间都是相同的(大致都是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,#20
MOVR0,#30H
LOOP:
MOVA,@R0
INCR0
DJNZR7,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.(第九课)单片机数据传递类指令
(3)以直接地址为目的操作数的指令
MOVdirect,A例:
MOV20H,A
MOVdirect,RnMOV20H,R1
MOVdirect1,direct2MOV20H,30H
MOVdirect,@RiMOV20H,@R1
MOVdirect,#dataMOV20H,#34H
(4)以间接地址为目的操作数的指令
MOV@Ri,A例:
MOV@R0,A
MOV@Ri,directMOV@R1,20H
MOV@Ri,#dataMOV@R0,#34H
(5)十六位数的传递指令
MOVDPTR,#data16
8051是一种8位机,这是唯一的一条16位立即数传递指令,其功能是将一个16位的立即数送入DPTR中去。
其中高8位送入DPH,低8位送入DPL。
例:
MOVDPTR,#1234H,则执行完了之后DPH中的值为12H,DPL中的值为34H。
反之,如果我们分别向DPH,DPL送数,则结果也一样。
如有下面两条指令:
MOVDPH,#35H,MOVDPL,#12H。
则就相当于执行了MOVDPTR,#3512H。
数据传递类指令综合练习:
1.给出每条指令执行后的结果
2.上机练习:
MOV23H,#30H
MOV12H,#34H
MOVR0,#23H
MOVR7,#22H
MOVR1,12H
MOVA,@R0
MOV34H,@R1
(23h)=30h
(12h)=34h
(R0)=23H
(R7)=22H
(R1)=12H
(A)=30H
(34H)=34H
MOV45H,34H
MOVDPTR,#6712H
MOV12H,DPH
MOVR0,DPL
MOVA,@R0
(45H)=34H
(DPTR)=6712H
(12H)=67H
(R0)=12H
(A)=67
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 单片机 详解