1、单片微型计算机原理摘要:单片机,89C51,动态扫描显示接口,键盘接口。我们应首先了解何谓单片机:一台能够工作的计算机要有这样几个部份构成:CPU(进行运算、控制) 、RAM(数据存储) 、ROM(程序存储) 、输入/输出设备(例如:串行口、并行输出口等) 。在个人计算机上这些部份被分成若干块芯片,安装一个称之为主板的印刷线路板上。而在单片机中,这些部份,全部被做到一块集成电路芯片中了,所以就称为单片(单芯片)机,而且有一些单片机中除了上述部份外,还集成了其它部份如 A/D,D/A 等。 PC 中的 CPU 一块就要卖几千块钱,这么多东西做在一起,还不得买个天价!再说这块芯片也得非常大了。 不
2、,价格并不高,从几元人民币到几十元人民币,体积也不大,一般用 40 脚封装,当然功能多一些单片机也有引脚比较多的,如68 引脚,功能少的只有 10 多个或 20 多个引脚,有的甚至只 8 只引脚。为什么会这样呢? 功能有强弱,打个比方,市场上面有的组合音响一套才卖几百块钱,可是有的一台功放机就要卖好几千。另外这种芯片的生产量很大,技术也很成熟,51 系列的单片机已经做了十几年,所以价格就低了。 既然如此,单片机的功能肯定不强,干吗要生产它呢? 话不能这样说,实际工作中并不是任何需要计算机的场合都要求计算机有很高的性能,一个控制电冰箱温度的计算机难道要用酷睿 2 吗?应用的关键是看是否够用,是否
3、有很好的性能价格比。所以8051 出来十多年,依然没有被淘汰,还在不断的发展中。 MCS51 单片机和 8051、8031、89C51 等的关系我们平常老是讲 8051,又有什么 8031,现在又有 89C51,它们之间究竟是什么关系? MCS51 是指由美国INTEL 公司生产的一系列单片机的总称,这一系列单片机包括了好些品种,如8031,8051,8751,8032,8052,8752 等,其中 8051 是最早最典型的产品,该系列其它单片机都是在 8051 的基础上进行功能的增、减、改变而来的,所以人们习惯于用 8051 来称呼 MCS51 系列单片机,而 8031 是前些年在我国最流行
4、的单片机,所以很多场合会看到 8031 的名称。INTEL 公司将 MCS51 的核心技术授权给了很多其它公司,所以有很多公司在做以 8051 为核心的单片机,当然,功能或多或少有些改变,以满足不同的需求,其中 89C51 就是这几年在我国非常流行的单片机,它是由美国 ATMEL 公司开发生产的。所以我们将用 89C51 来做几个实例。首先我们知道动态扫描显示接口是单片机中应用最为广泛的一种显示方式之一。其接口电路是把所有显示器的 8 个笔划段 a-h 同名端连在一起,而每一个显示器的公共极 COM 是各自独立地受 I/O 线控制。CPU 向字段输出口送出字形码时,所有显示器接收到相同的字形码
5、,但究竟是那个显示器亮,则取决于COM 端,而这一端是由 I/O 控制的,所以我们就可以自行决定何时显示哪一位了。而所谓动态扫描就是指我们采用分时的方法,轮流控制各个显示器的 COM端,使各个显示器轮流点亮。在轮流点亮扫描过程中,每位显示器的点亮时间是极为短暂的(约 1ms),但由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位显示器并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感。下面就是动态扫描接口。由 89C51 的 P0 口能灌入较大的电流,所以我们采用共阳的数码管,并且不用限流电阻,而只是用两只 1N4004 进行降压后给数码管供电,这里仅
6、用了两只,实际上还可以扩充。它们的公共端则由 PNP 型三极管 8550 控制,显然,如果 8550 导通,则相应的数码管就可以亮,而如果 8550截止,则对应的数码管就不可能亮,8550 是由 P27,P26 控制的。这样我们就可以通过控制 P27、P26 达到控制某个数码管亮或灭的目的。下面的这个程序,就是用实验板上的数码管显示 0 和 1。FIRST EQU P2.7 ;第一位数码管的位控制SECOND EQU P2.6 ;第二位数码管的位控制DISPBUFF EQU 5AH ;显示缓冲区为 5AH 和 5BHORG 0000HAJMP STARTORG 30HSTART:MOV SP,
7、#5FH ;设置堆栈MOV P1,#0FFHMOV P0,#0FFHMOV P2,#0FFH ;初始化,所显示器,LED 灭MOV DISPBUFF,#0 ;第一位显示 0MOV DISPBUFF+1,#1 ;第二握显示 1LOOP:LCALL DISP ;调用显示程序AJMP LOOP;主程序到此结束DISP:PUSH ACC ;ACC 入栈PUSH PSW ;PSW 入栈MOV A,DISPBUFF ;取第一个待显示数MOV DPTR,#DISPTAB ;字形表首地址MOVC A,A+DPTR ;取字形码MOV P0,A ;将字形码送 P0 位(段口)CLR FIRST ;开第一位显示器位
8、口LCALL DELAY ;延时 1 毫秒SETB FIRST ;关闭第一位显示器(开始准备第二位的数据)MOV A,DISPBUFF+1 ;取显示缓冲区的第二位MOV DPTR,#DISPTAB MOVC A,A+DPTRMOV P0,A ;将第二个字形码送 P0 口CLR SECOND ;开第二位显示器LCALL DELAY ;延时SETB SECOND ;关第二位显示POP PSWPOP ACCRETDELAY: ;延时 1 毫秒PUSH PSWSETB RS0MOV R7,#50D1: MOV R6,#10D2: DJNZ R6,$DJNZ R7,D1POP PSWRETDISPTAB
9、:DB 28H,7EH,0a4H,64H,72H,61H,21H,7CH,20H,60H END从上面的例子中可以看出,动态扫描显示必须由 CPU 不断地调用显示程序,才能保证持续不断的显示。上面的这个程序可以实现数字的显示,这里仅是显示两个数字,并没有做其他的工作,因此,两个数码管轮流显示 1 毫秒,实际的工作中,当然不可能只显示两个数字,还是要做其他的事情的,这样在二次调用显示程序之间的时间间隔就不一不定了,如果时间间隔比较长,就会使显示不连续。而实际工作中是很难保证所有工作都能在很短时间内完成的。况且这个显示程序也有点“浪费”,每个数码管显示都要占用 1 个毫秒的时间,这在很多合是不允许
10、的,怎么办呢?我们可以借助于定时器,定时时间一到,产生中断,点亮一个数码管,然后马上返回,这个数码管就会一直亮到下一次定时时间到,而不用调用延时程序了,这段时间可以留给主程序干其他的事。到下一次定时时间到则显示下一个数码管,这样就很少浪费了。Counter EQU 59H ;计数器,显示程序通过它得知现正显示哪个数码管FIRST EQU P2.7 ;第一位数码管的位控制SECOND EQU P2.6 ;第二位数码管的位控制DISPBUFF EQU 5AH ;显示缓冲区为 5AH 和 5BHORG 0000HAJMP STARTORG 000BH ;定时器 T0 的入口AJMP DISP ;显示
11、程序ORG 30HSTART:MOV SP,#5FH ;设置堆栈MOV P1,#0FFHMOV P0,#0FFHMOV P2,#0FFH ;初始化,所显示器,LED 灭MOV TMOD,#00000001B ;定时器 T0 工作于模式 1(16 位定时/计数模式)MOV TH0,#HIGH(65536-2000)MOV TL0,#LOW(65536-2000)SETB TR0SETB EASETB ET0MOV Counter,#0 ;计数器初始化MOV DISPBUFF,#0 ;第一位始终显示 0MOV A,#0LOOP: MOV DISPBUFF+1,A ;第二位轮流显示 0-9INC A
12、LCALL DELAYCJNE A,#10,LOOPMOV A,#0AJMP LOOP ;在此中间可以按排任意程序,这里仅作示范。;主程序到此结束DISP: ;定时器 T0 的中断响应程序PUSH ACC ;ACC 入栈PUSH PSW ;PSW 入栈MOV TH0,#HIGH(65536-2000) ;定时时间为 2000 个周期MOV TL0,#LOW(65536-2000)SETB FIRSTSETB SECOND ;关显示MOV A,#DISPBUFF ;显示缓冲区首地址ADD A,Counter MOV R0,AMOV A,R0 ;根据计数器的值取相应的显示缓冲区的值MOV DPTR
13、,#DISPTAB ;字形表首地址MOVC A,A+DPTR ;取字形码MOV P0,A ;将字形码送 P0 位MOV A,Counter ;取计数器的值JZ DISPFIRST ;如果是 0 则显示第一位CLR SECOND ;否则显示第二位AJMP DISPNEXTDISPFIRST:CLR FIRST ;显示第一位 DISPNEXT:INC Counter ;计数器加 1MOV A,Counter DEC A ;如果计数器计到 2,则让它回 0DEC A JZ RSTCOUNT AJMP DISPEXITRSTCOUNT:MOV Counter,#0 ;计数器的值只能是 0 或 1DIS
14、PEXIT:POP PSWPOP ACCRETIDELAY: ;延时 130 毫秒PUSH PSWSETB RS0MOV R7,#255D1: MOV R6,#255D2: NOPNOPNOPNOPDJNZ R6,D2DJNZ R7,D1POP PSWRETDISPTAB:DB 28H,7EH,0a4H,64H,72H,61H,21H,7CH,20H,60H END从上面的程序可以看出,和静态显示相比,动态扫描的程序稍有点复杂,不过,这是值得的。这个程序有一定的通用性,只要改变端口的值及计数器的值就可以显示更多位数了。下面给出显示程序的流程图。键盘是由若干按键组成的开关矩阵,它是微型计算机最常
15、用的输入设备,用户可以通过键盘向计算机输入指令、地址和数据。一般单片机系统中采和非编码键盘,非编码键盘是由软件来识别键盘上的闭合键,它具有结构简单,使用灵活等特点,因此被广泛应用于单片机系统。按键开关的抖动问题 组成键盘的按键有触点式和非触点式两种,单片机中应用的一般是由机械触点构成的。在下图中,当开图 1 图 2关 S 未被按下时,P1。0 输入为高电平,S 闭合后,P1。0 输入为低电平。由于按键是机械触点,当机械触点断开、闭合时,会有抖动动,P1。0 输入端的波形如图 2 所示。这种抖动对于人来说是感觉不到的,但对计算机来说,则是完全可以感应到的,因为计算机处理的速度是在微秒级,而机械抖
16、动的时间至少是毫秒级,对计算机而言,这已是一个“漫长”的时间了。前面我们讲到中断时曾有个问题,就是说按键有时灵,有时不灵,其实就是这个原因,你只按了一次按键,可是计算机却已执行了多次中断的过程,如果执行的次数正好是奇数次,那么结果正如你所料,如果执行的次数是偶数次,那就不对了。为使 CPU 能正确地读出 P1 口的状态,对每一次按键只作一次响应,就必须考虑如何去除抖动,常用的去抖动的方法有两种:硬件方法和软件方法。单片机中常用软件法,因此,对于硬件方法我们不介绍。软件法其实很简单,就是在单片机获得 P1。0 口为低的信息后,不是立即认定 S1 已被按下,而是延时10 毫秒或更长一些时间后再次检
17、测 P1。0 口,如果仍为低,说明 S1 的确按下了,这实际上是避开了按键按下时的抖动时间。而在检测到按键释放后(P1。0 为高)再延时 5-10 个毫秒,消除后沿的抖动,然后再对键值处理。不过一般情况下,我们通常不对按键释放的后沿进行处理,实践证明,也能满足一定的要求。当然,实际应用中,对按键的要求也是千差万别,要根据不同的需要来编制处理程序,但以上是消除键抖动的原则。键盘与单片机的连接 图 3 图 4通过 1/0 口连接。将每个按键的一端接到单片机的 I/O 口,另一端接地,这是最简单的方法,如图 3 所示是实验板上按键的接法,四个按键分别接到P3.2 、P3.3 、P3.4 和 P3.5
18、。对于这种键各程序可以采用不断查询的方法,功能就是:检测是否有键闭合,如有键闭合,则去除键抖动,判断键号并转入相应的键处理。下面给出一个例程。其功能很简单,四个键定义如下: P3.2:开始,按此键则灯开始流动(由上而下)P3.3:停止,按此键则停止流动,所有灯为暗P3.4:上,按此键则灯由上向下流动P3.5:下,按此键则灯由下向上流动UpDown EQU 00H ;上下行标志StartEnd EQU 01H ;起动及停止标志LAMPCODE EQU 21H ;存放流动的数据代码ORG 0000HAJMP MAINORG 30HMAIN:MOV SP,#5FHMOV P1,#0FFHCLR Up
19、Down ;启动时处于向上的状态CLR StartEnd ;启动时处于停止状态MOV LAMPCODE,#0FEH ;单灯流动的代码 LOOP:ACALL KEY ;调用键盘程序JNB F0,LNEXT ;如果无键按下,则继续ACALL KEYPROC ;否则调用键盘处理程序LNEXT:ACALL LAMP ;调用灯显示程序AJMP LOOP ;反复循环,主程序到此结束DELAY:MOV R7,#100D1: MOV R6,#100DJNZ R6,$DJNZ R7,D1RET;-延时程序,键盘处理中调用KEYPROC:MOV A,B ;从 B 寄存器中获取键值JB ACC.2,KeyStart
20、 ;分析键的代码,某位被按下,则该位为 1(因为在键盘程序中已取反)JB ACC.3,KeyOverJB ACC.4,KeyUpJB ACC.5,KeyDownAJMP KEY_RETKeyStart:SETB StartEnd ;第一个键按下后的处理AJMP KEY_RETKeyOver:CLR StartEnd ;第二个键按下后的处理AJMP KEY_RETKeyUp: SETB UpDown ;第三个键按下后的处理AJMP KEY_RETKeyDown:CLR UpDown ;第四个键按下后的处理KEY_RET:RETKEY:CLR F0 ;清 F0,表示无键按下。ORL P3,#001
21、11100B ;将 P3 口的接有键的四位置 1MOV A,P3 ;取 P3 的值ORL A,#11000011B ;将其余 4 位置 1CPL A ;取反JZ K_RET ;如果为 0 则一定无键按下ACALL DELAY ;否则延时去键抖ORL P3,#00111100BMOV A,P3ORL A,#11000011BCPL AJZ K_RETMOV B,A ;确实有键按下,将键值存入 B 中SETB F0 ;设置有键按下的标志K_RET: ORL P3,#00111100B ;此处循环等待键的释放MOV A,P3ORL A,#11000011BCPL AJZ K_RET1 ;直到读取的数
22、据取反后为 0 说明键释放了,才从键盘处理程序中返回AJMP K_RETK_RET1: RETD500MS: ;流水灯的延迟时间PUSH PSWSETB RS0MOV R7,#200D51: MOV R6,#250D52: NOPNOPNOPNOPDJNZ R6,D52DJNZ R7,D51POP PSWRET LAMP:JB StartEnd,LampStart ;如果 StartEnd=1,则启动MOV P1,#0FFHAJMP LAMPRET ;否则关闭所有显示,返回LampStart:JB UpDown,LAMPUP ;如果 UpDown=1,则向上流动MOV A,LAMPCODERL
23、 A ;实际就是左移位而已MOV LAMPCODE,A MOV P1,ALCALL D500MSAJMP LAMPRETLAMPUP:MOV A,LAMPCODERR A ;向下流动实际就是右移MOV LAMPCODE,AMOV P1,ALCALL D500MSLAMPRET: RETEND以上程序功能很简单,但它演示了一个键盘处理程序的基本思路,程序本身很简单,也不很实用,实际工作中还会有好多要考虑的因素,比如主循环每次都调用灯的循环程序,会造成按键反应“迟钝” ,而如果一直按着键不放,则灯不会再流动,一直要到松开手为止,等等,我们可以仔细考虑一下这些问题,再进行程序扩展。采用中断方式:如图 4 所示。各个按键都接到一个与非上,当有任何一个按键按下时,都会使与门输出为低电平,从而引起单片机的中断,它的好处是不用在主程序中不断地循环查询,如果有键按下,单片机再去做相应的处理。在两个例子中我们学会了 89C51 单片机的部分功能,也让我们对单片机的运用有了更深层次的了解。