51单片机教程.docx
- 文档编号:27083135
- 上传时间:2023-06-26
- 格式:DOCX
- 页数:49
- 大小:145.58KB
51单片机教程.docx
《51单片机教程.docx》由会员分享,可在线阅读,更多相关《51单片机教程.docx(49页珍藏版)》请在冰豆网上搜索。
51单片机教程
图1
图2
图3
一个实验,程序如下:
MAIN:
MOVP3,#0FFH
LOOP:
MOVA,P3
MOVP1,A
LJMPLOOP
需要真正地读引脚状态,而只是读入锁存器的状态,然后作某种变换后再输出。
请注意输入结构图,如果将这一根引线作为输入口使用,我们并不能保证在任何时刻都能得到正确的结果(为什么?
)参考图2输入示意图。
接在外部的开关如果打开,则应当是输入1,而如果闭合开关,则输入0,但是,如果单片机内部的开关是闭合的,那么不管外部的开关是开还是闭,单片机接受到的数据都是0。
可见,要让这一端口作为输入使用,要先做一个‘准备工作’,就是先让内部的开关断开,也就是让端口输出‘1’才行。
正因为要先做这么一个准备工作,所以我们称之为“准双向I/O口”。
符号
地址
功能介绍
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
定时器/计数器0(高8位)
TL1
8BH
定时器/计数器1(低8位)
TL0
8AH
定时器/计数器0(低8位)
TMOD
89A
定时器/计数器方式控制寄存器
TCON
88H
定时器/计数器控制寄存器
DPH
83H
数据地址指针(高8位)
DPL
82H
数据地址指针(低8位)
SP
81H
堆栈指针
P0
80H
P0口锁存器
PCON
87H
电源控制寄存器
D7
D6
D5
D4
D3
D2
D1
D0
CY
AC
F0
RS1
RS0
OV
P
上机练习:
进入DOS状态,进入WAVE所在的目录,例D:
\WAVE
键入MCS51,出现如下画面
图1
用ALT+A汇编通过。
用F8即可单步执行,可以用CTRL+F2功能键复位PC值。
请用ALT+4或ALT+5等来切换。
当然以上操作也可以菜单进行。
CTRL+F2是程序复位,用RUN菜单。
窗口用WINDOWS菜单。
。
在乘积大于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指令
INCA
INCRn
INCdirect
INC@Ri
INCDPTR
用途很简单,就是将后面目标中的值加1。
例:
(A)=12H,(R0)=33H,(21H)=32H,(34H)=22H,DPTR=1234H。
执行下面的指令:
INCA(A)=13H
INCR2(R0)=34H
INC21H(21H)=33H
INC@R0(34H)=23H
INCDPTR(DPTR)=1235H
后结果如上所示。
说明:
从结果上看INCA和ADDA,#1差不多,但INCA是单字节,单周期指令,而ADD#1则是双字节,双周期指令,而且INCA不会影响PSW位,如(A)=0FFH,INCA后(A)=00H,而CY依然保持不变。
如果
实验五:
ORG0000H
LJMPSTART
ORG30H
START:
MOVSP,#5FH
MOVA,#80H
LOOP:
MOVP1,A
RLA
LCALLDELAY
LJMPLOOP
delay:
movr7,#255
d1:
movr6,#255
d2:
nop
nop
nop
nop
djnzr6,d2
djnzr7,d1
ret
END
先让我们将程序写入片中,装进实验板,看一看现象。
前而的ORG0000H、LJMPSTART、ORG30H等我们稍后分析。
从START开始,MOVSP,#5FH,这是初始化堆栈,在本程序中有无此句无关紧要,不过我们慢慢开始接触正规的编程,我也就慢慢给大家培养习惯吧。
MOVDPTR,#TAB;将TAB所代表的地址送入DPTR
MOVA,R0;从R0中取数(详见下面说明)
MOVB,#2
MULA,B;A中的值乘2(详见下面的说明)
JMP@A+DPTR;跳转
TAB:
AJMPS1;跳转表格
AJMPS2
AJMPS3
取得键值后要乘2?
如果例程下面的所有指令换成LJMP,即:
LJMPS1,LJMPS2……这段程序还能正确地执行吗?
如果不能,应该怎么改?
8031中有一些SFR是可以进行位寻址的,这些SFR的特点是其字节地址均可被8整除,如A累加器,B寄存器、PSW、IP(中断优先级控制寄存器)、IE(中断允许控制寄存器)、SCON(串行口控制寄存器)、TCON(定时器/计数器控制寄存器)、P0-P3(I/O端口锁存器)。
以上的一些SFR我们还不熟,等我们讲解相关内容时再作详细解释。
。
原来TF1(0)在这儿!
那么TR0、TR1又是什么呢?
看上节课的图。
计数脉冲要进入计数器还真不容易,有层层关要通过,最起码,就是TR0
(1)要为1,开关才能合上,脉冲才能过来。
因此,TR0
(1)称之为运行控制位,可用指令SETB来置位以启动计数器/定时器运行,用指令CLR来
一般情况处,只有在T1以工作方式2运行(当波特率发生器用)时,才让T0工作于方式3的。
由与中断有关的特殊功能寄存器、中断入口、顺序查询逻辑电路等组成,一个完整的主程序看起来应该是这样的:
ORG0000HLJMPSTART
ORG0003H
LJMPINT0;转外中断0ORG000BH
RETI;没有用定时器0中断,在此放一条RETI,万一“不小心“产生了中断,也不会有太大的后果。
。
例1:
查询方式
ORG0000H
AJMPSTART
ORG30H
START:
MOVP1,#0FFH;关所灯
MOVTMOD,#00000001B;定时/计数器0工作于方式1
MOVTH0,#15H
MOVTL0,#0A0H;即数5536
SETBTR0;定时/计数器0开始运行
LOOP:
JBCTF0,NEXT;如果TF0等于1,则清TF0并转NEXT处
AJMPLOOP;否则跳转到LOOP处运行
NEXT:
CPLP1.0
MOVTH0,#15H
MOVTL0,#9FH;重置定时/计数器的初值
AJMPLOOP
ENDAJMPLOOP
END
键入程序,看到了什么?
灯在闪烁了,这可是用定时器做的,不再是主程序的循环了。
简单地分析一下程序,为什么用JBC呢?
TF0是定时/计数器0的溢出标记位,当定时器产生溢出后,该位由0变1,所以查询该位就可知宇时时间是否已到。
该位为1后,要用软件将标记位清0,以便下一次定时是间到时该位由0变1,所以用了JBC指令,该指位在判1转移的同时,还将该位清0。
以上程序是可以实现灯的闪烁了,可是主程序除了让灯闪烁外,还是不能做其他的事啊!
不,不对,我们可以在LOOP:
……和AJMPLOOP指令之间插入一些指令来做其他的事情,只要保证执行这些指令的时间少于定时时间就行了。
那我们在用软件延时程序的时候不是也可以用一些指令来替代DJNZ吗?
是的,但是那就要求你精确计算所用指令的时间,然后再减去相应的DJNZ循环次数,很不方便,而现在只要求所用指令的时间少于定时时间就行,显然要求低了。
当然,这样的方法还是不好,所以我们常用以下的方法来实现。
程序2:
用中断实现
ORG0000H
AJMPSTART
ORG000BH;定时器0的中断向量地址
AJMPTIME0;跳转到真正的定时器程序处
ORG30H
START:
MOVP1,#0FFH;关所灯
MOVTMOD,#00000001B;定时/计数器0工作于方式1
MOVTH0,#15H
MOVTL0,#0A0H;即数5536
SETBEA;开总中断允许
SETBET0;开定时/计数器0允许
SETBTR0;定时/计数器0开始运行
LOOP:
AJMPLOOP;真正工作时,这里可写任意程序
TIME0:
;定时器0的中断处理程序
PUSHACC
PUSHPSW;将PSW和ACC推入堆栈保护
CPLP1.0
MOVTH0,#15H
MOVTL0,#0A0H;重置定时常数
POPPSW
POPACC
RETI
END
想实现一个1S的定时,该怎么办呢?
ORG0000H
AJMPSTART
ORG000BH;定时器0的中断向量地址
AJMPTIME0;跳转到真正的定时器程序处
ORG30H
START:
MOVP1,#0FFH;关所灯
MOV30H,#00H;软件计数器预清0
MOVTMOD,#00000001B;定时/计数器0工作于方式1
MOVTH0,#3CH
MOVTL0,#B0H;即数15536
SETBEA;开总中断允许
SETBET0;开定时/计数器0允许
SETBTR0;定时/计数器0开始运行
LOOP:
AJMPLOOP;真正工作时,这里可写任意程序
TIME0:
;定时器0的中断处理程序
PUSHACC
PUSHPSW;将PSW和ACC推入堆栈保护
INC30H
MOVA,30H
CJNEA,#20,T_RET;30H单元中的值到了20了吗?
T_L1:
CPLP1.0;到了,取反P10
MOV30H,#0;清软件计数器
T_RET:
MOVTH0,#15H
MOVTL0,#9FH;重置定时常数
POPPSW
POPACC
RETI
END
先自己分析一下,看看是怎么实现的?
这里采用了软件计数器的概念,思路是这样的,先用定时/计数器0做一个50毫秒的定时器,定时是间到了以后并不是立即取反P10,而是将软件计数器中的值加1,如果软件计数器计到了20,就取反P10,并清掉软件计数器中的值,否则直接返回,这样,就变成了20次定时中断才取反一次P10,因此定时时间就延长了成了20*50即1000毫秒了。
这个思路在工程中是非常有用的,有的时候我们需要若干个定时器,可51中总共才有2个,怎么办呢?
其实,只要这几个定时的时间有一定的公约数,我们就可以用软件定时器加以实现,如我要实现P10口所接灯按1S每次,而P11口所接灯按2S每次闪烁,怎么实现呢?
对了我们用两个计数器,一个在它计到20时,取反P10,并清零,就如上面所示,另一个计到40取反P11,然后清0,不就行了吗?
这部份的程序如下
ORG0000H
AJMPSTART
ORG000BH;定时器0的中断向量地址
AJMPTIME0;跳转到真正的定时器程序处
ORG30H
START:
MOVP1,#0FFH;关所灯
MOV30H,#00H;软件计数器预清0
MOV31H,#00H;软件计数器预清0
MOVTMOD,#00000001B;定时/计数器0工作于方式1
MOVTH0,#3CH
MOVTL0,#0B0H;即数15536
SETBEA;开总中断允许
SETBET0;开定时/计数器0允许
SETBTR0;定时/计数器0开始运行
LOOP:
AJMPLOOP;真正工作时,这里可写任意程序
TIME0:
;定时器0的中断处理程序
PUSHACC
PUSHPSW;将PSW和ACC推入堆栈保护
INC30H
INC31H;两个计数器都加1
MOVA,30H
CJNEA,#20,T_NEXT;30H单元中的值到了20了吗?
T_L1:
CPLP1.0;到了,取反P10
MOV30H,#0;清软件计数器
T_NEXT:
MOVA,31H
CJNEA,#40,T_RET;31h单元中的值到40了吗?
T_L2:
CPLP1.1
MOV31H,#0;到了,取反P11,清计数器,返回
T_RET:
MOVTH0,#15H
MOVTL0,#9FH;重置定时常数
POPPSW
POPACC
RETI
END
324构成的振荡器连到定时/计数器1的外部引脚T1上面,我们就利用这个来做一个计数实验,要将计数的值显示出来,当然最好用数码管了,可我们还没讲到这一部份,为了避免把问题复杂化,我们用P1口的8个LED来显示计到的数据。
程序如下:
ORG0000H
AJMPSTART
ORG30H
START:
MOVSP,#5FH
MOVTMOD,#01000000B;定时/计数器1作计数用,0不用全置0
SETBTR1;启动计数器1开始运行.
LOOP:
MOVA,TL0
MOVP1,A
AJMPLOOP
END
在硬件上用线将324的输出与T1连通(印板上有焊盘)运行这种程序,注意将板按正确的位置放置(LM324放在左手边,LED排列是按从高位到低们排列)看到什么?
随着324后接的LED的闪烁,单片机的8只LED也在不断变化,注意观察,是不是按二进制:
00000000
00000001
00000010
00000011
这样的顺序在变呢?
这就对了,这就是TL0中的数据。
程序二:
ORG0000H
AJMPSTART
ORG001BH
AJMPTIMER1;定时器1的中断处理
ORG30H
START:
MOVSP,#5FH
MOVTMOD,#01010000B;定时/计数器1作计数用,模式1,0不用全置0
MOVTH1,#0FFH
MOVTL1,#0FAH;预置值,要求每计到6个脉冲即为一个事件
SETBEA
SETBET1;开总中断和定时器1中断允许
SETBTR1;启动计数器1开始运行.
AJMP$
TIMER1:
PUSHACC
PUSHPSW
CPLP1.0;计数值到,即取反P1.0
MOVTH1,#0FFH
MOVTL1,#0FAH;重置计数初值
POPPSW
POPACC
RETI
END
上面这个程序完成的工作很简单,就是在每6个脉冲到来后取反一次P1。
0,因此实验的结果应当是:
LM324后接的LED亮、灭6次,则P1。
0口所接LED亮或灭一次。
这实际就是我们上面讲的计数器的第二种应用。
程序三:
外部中断实验
ORG0000H
AJMPSTART
ORG0003H;外部中断地直入口
AJMPINT0
ORG30H
START:
MOVSP,#5FH
MOVP1,#0FFH;灯全灭
MOVP3,#0FFH;P3口置高电平
SETBEA
SETBEX0
AJMP$
INT0:
PUSHACC
PUSHPSW
CPLP1.0
POPPSW
POPACC
RETI
END
本程序的功能很简单,按一次按键1(接在12引脚上的)就引发一次中断0,取反一次P1。
0,因此理论上按一下灯亮,按一下灯灭,但在实际做实验时,可能会发觉有时不“灵”,按了它没反应,但在大部份时候是对的,这是怎么回事呢?
我们在讲解键盘时再作解释,这个程序本身是没有问题的。
单片机教程第二十一课:
串行接口
概述
串行接口的一般概念单片机与外界进行信息交换称之为通讯。
8051单片机的通讯方式有两种:
并行通讯:
数据的各位同时发送或接收。
串行通讯:
数据一位一位顺序发送或接收。
参看下图:
串行通讯的方式:
异步通讯:
它用一个起始位表示字符的开始,用停止位表示字符的结束。
其每帧的格式如下:
在一帧格式中,先是一个起始位0,然后是8个数据位,规定低位在前,高位在后,接下来是奇偶校验位(可以省略),最后是停止位1。
用这种格式表示字符,则字符可以一个接一个地传送。
在异步通讯中,CPU与外设之间必须有两项规定,即字符格式和波特率。
字符格式的规定是双方能够在对同一种0和1的串理解成同一种意义。
原则上字符格式可以由通讯的双方自由制定,但从通用、方便的角度出发,一般还是使用一些标准为好,如采用ASCII标准。
波特率即数据传送的速率,其定义是每秒钟传送的二进制数的位数。
例如,数据传送的速率是120字符/s,而每个字符如上述规定包含10数位,则传送波特率为1200波特。
同步通讯:
在异步通讯中,每个字符要用起始位和停止位作为字符开始和结束的标志,占用了时间;所以在数据块传递时,为了提高速度,常去掉这些标志,采用同步传送。
由于数据块传递开始要用同步字符来指示,同时要求由时钟来实现发送端与接收端之间的同步,故硬件较复杂。
D7
D6
D5
D4
D3
D2
D1
D0
SM0
SM1
SM2
REN
TB8
RB8
TI
RI
SM0、SM1:
串行口工作方式选择位,其定义如下:
SM0、SM1
工作方式
功能描述
波特率
00
方式0
8位移位寄存器
Fosc/12
01
方式1
10位UART
可变
10
方式2
11位UART
Fosc/64或fosc/32
11
方式3
11位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。
特殊功能寄存器PCON
PCON是为了在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接收时,应先用软件清零RI和SM2标志。
方式2
方式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相同。
波特率选择
如下:
波特率=
定时器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)
SMOD
TH1初值
19200
11.0592
1
FDH
9600
11.0592
0
FDH
4800
11.0592
0
FAH
2400
11.0592
0
F4h
1200
11.0592
0
E8h
单片机教程第二十二课:
串行口应用编程实例
1.串口方式0应用编程8051单片
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 51 单片机 教程