本文以利用AT89C51来实现对28路灯光变化的控制为例,介绍单片机I/O口基本功能的应用及汇编语言中的部分转移、数据传送、位操作和子程序调用及返回等指令。
一、 28路灯光控制电路功能简介
本系统的主要功能是控制28路灯光的两种变化:2灯流动发光和4灯流动发光,并且能实现人为的灯光变化形式选择。电路采用人机交互方式,选择和确认由按键来完成,并有“嘀嗒”声作为应答。系统上电后,先完成初始化,然后送出声音表示准备好,并点亮VD1至VD3作默认指示,然后进行按键判断。当直接按下确认键(SB2),系统便选择默认变化形式,进入2灯流动形式。如果首先按下选择键(SB1),系统则认为选择第二种变化形式,即4灯流动发光形式,并点亮VD1、VD2,同时送出“嘀嗒”声以作响应。如果此时再次按动选择键,系统会认为改为选择第一种变化形式,并点亮VD,同时送出“嘀嗒声”响应。不管进入上述哪种状态,只要再次按下确认键,28路灯光就会进行相应形式的变化。
28路灯光控制能满足一般控制需要,如各种固定广告语、音乐喷泉、模拟礼花等。下面我们就来了解AT89C51是怎样在电路中起控制作用的。
二、 AT89C51对28路灯光变化的控制实现原理
28路灯光控制电路图如图1所示。
AT89C51是通过改变其片外引脚电位的高低来实现对这28路灯光各种闪烁状态的控制。
VD1至VD28的正极分别通过限流电阻与AT89C51中的28个片外引脚相连接,而负极皆与电源负极(GND)相连接,这样,当这28条引脚皆呈低电位时,VD1至VD28皆因得不到正向导通电压而熄灭;如果我们使这其中的某几个引脚呈高电位,与之相连接的发光二极管就会被点亮。比如,当AT89C51的第28脚和第27脚呈高电位时,就相当于将VD9、VD10的正极通过R9和R10与VCC相连接,这样,VCC就会通过R9、R10使VD9、VD10导通而发光;若使AT89C51的第28脚和第27脚呈低电位,也就相当于上述两个引脚对地短路,VD9、VD10就会因截止而熄灭,然后使AT89C51的第26脚和第25脚再呈高电位,VD11、VD12同样会被点亮,经过延时后,以同样的方式依次点亮后面的发光二极管,28路灯光就会呈现出2灯流动的状态。如果每次点亮的发光二极管增加至4只,就会呈现出4灯流动的状态。所以,只要依次向这些I/O口中送入不同的数据,或说是向这些口中送入不同的控制字,灯光就会呈现不同的闪烁状态,达到变化控制的目的。
在图1中,P1口的P1.4~P1.7是用作对VD25~VD28进行控制的,作用同其它3个I/O口一样,而P1.0~P1.2是用作人机对话的。P1.2接有电陶瓷片B2,由于B2的一端与P1.2相连接,另一端与VCC相连接,所以,当P1.2呈低电位时,B2的两端电压为5V,如果P1.2呈高电位时,B2两端电压为零。这样,我们利用不断改变P1.2电位高低的办法,就会使B2以一定的频率振动起来,就能发出一定声调的声音来。为了在改变P1.2的电位时不至于影响P1口的其它口线输出电位的状态,采用修改整个P1口的数据的方式是不方便的。事实上,CPU对这些I/O口的任一条口线都是可以单独操作的。因为51系列单片机内部有一个布尔处理器,它相当于一个完整的1位计算机,能完成各种位操作。改变P1.2高低电位的快慢,决定着B2的振动频率,因此,我们可以用延时的方法来确定P1.2呈高低电位的持续时间,从而使B2发出一定频率的声音来。
3. 按键原理
参看图1,按键SB1表示选择键,按键SB2表示确认键。P1.0通过按键SB1与地相连接。若刚开始时P1.0呈高电位,如果SB1是断开的,P1.0就保持高电位不变,如果SB1被按下,P1.0对地短路,就呈低电位。因此,系统可以采用读取P1.0电位的办法来判断SB1的开关状态。如果CPU读得P1.0的数据为“1”,则说明SB1为断开状态,即SB1未被按下,如果CPU读得P1.0的数据为“0”,则说明SB1被按下。同理可判断SB2的开断状态。
三、 程序编写思想和相关指令介绍
程序是为实现系统功能服务的,因此,程序设计思想必须符合系统功能的要求。本系统主程序流程图如图2所示。
由图可知,主程序的执行就是一个按键判断过程。程序在初始化后即进行按键判断。若有键被按下,先调用延时子程序消除按键抖动,再进行判断,继而转向执行相关程序。
在按键判断程序的执行过程中,将调用到2灯/4灯发光子程序、延时子程序、发声子程序。以下就上述程序向读者进行说明并介绍相关指令的使用。
1. 按键状态的判断和条件转移指令
对按键状态的判断就是对选择键和确认键所连接的I/O口的电位高低的判断。故主程序中判断按键状态采用位操作中的条件转移指令:JB bit,rel,其执行结果是:如果读取某一位的数据为“1”则转移。
实际上,在按键按下的过程中,不管是手抖动还是键本身的接触不良,都会使P1.1出现不规则脉冲,这就容易出现读取错误。所以本例采用了延时程序来消除抖动。比如执行JB P1.1,005F,如果读得P1.1的电位为高,则说明选择键SB2未被按下,程序就跳到程序存储器中地址为005F的地方执行该处的程序。若发现键被按下,此时并不确认,而是先调用延时子程序,经过延时后再进行判断。如果经延时后P1.1恢复为高电位,说明此次P1.1跳低电位为误触动或其它干扰所致,就转向后一条程序指令去判断SB1的状态。若经过延时后P1.1仍为低电位,则选择键SB1确实已被按下,就调用发声子程序发出“嘀嗒”声作为对按键确认的应答,直到键SB1释放,再继续执行相应的程序。
2. 2灯/4灯发光子程序及数据传送指令
2灯/4灯发光子程序采用向P0~P3口写数据的方法,控制各I/O口的电位高低来改变二极管被点亮或熄灭的状态。此子程序中使用了向某一地址写一个数(或从某一地址读一个数)时专用的数据传送指令MOV。该指令分成3个部分,第一部分可泛指送、写、取或读数,第二部分指明被送数的来源或目的地,第三部分是传送的立即数。比如MOV P0,#03H:MOV就是传送或移动的意思,P0指的就是移动数到达的目的地是P0口,#03H(00000011)就是被移动的数,它可使P0口中的P0.7和P0.8口呈高电位,其余呈低电位。由上述可知,如果我们使VD1~VD8从VD1、VD2开始两盏两盏的依次轮流发光至VD7、VD8,那么只要依次向P0口送出立即数#03H、#0CH、#30H、#C0H即可。如果以此形式继续流动至VD27、VD28,就以同样的指令,不断改变P2口、P3口和P1口的数据即可。
在每次送数之后,都要进行一段时间的延时(调用延时子程序),这是因为只有灯光在某种状态下停留一定的时间,我们才能感觉出灯光流动变化的效果,这是人的视觉暂留的缘故。
3. 延时子程序和子程序调用及返回指令
延时子程序在本例中主要用于维持二极管发光时间、改变应答发声频率和判断按键时间。我们常常采用让CPU在内部进行循环计数的办法来实现延时。举一个例子:如果幼儿园的小朋友完成了一个作品,需放在展台上展示。而这期间需让这位小朋友回避30分钟再来取回,假如这个小朋友只认得1个数,只掌握有和没有的概念,那完全可以采用下述的办法来实现。你可以在一个小盒子里放6个蜡笔头,然后让她顺着花园的甬道转圈,每转回一圈就从小盒子里取出一个蜡笔头,直到小盒子空了,就回来找老师拿回自己的作品。当小朋友转回一圈后,她就会从盒子里取出一个蜡笔头扔掉,并一定会看一下盒子是否是空的,如果不是空的,继续走,如果是空的,就知道时间到了。如果小朋友转一圈的时间为5分钟,自开始转圈到盒子空了的时候恰好过了30分钟。延时子程序的设计思想,与上例无二。当主程序或其它子程序要用到延时时,常常通过调用指令LCALL来调用延时子程序。使用了LCALL指令后,程序回转到指令中所指地址执行该处的子程序。图3为延时子程序的流程图,在设置了时间常数后将执行n次空操作,再判断延时到否,若延时已到,则必须返回到主程序的调用处,即LCALL指令执行处,否则继续执行循环内的指令。因此延时子程序的最后要放一条子程序返回指令RET。
4. 发声子程序及位操作指令
发声子程序用于在人机交互时控制硬件发出应答声。根据前述发声原理,子程序中用SETB和CLR两条位操作指令,就可灵活地改变P0~P3口的任意一条引脚输出电位的高低。同向寄存器送整个字节的立即数一样,对位进行操作,必须指出位的地址,这里说明一点,虽然位地址与有些寄存器地址使用相同的数字,但并不冲突,因为位操作指令中的操作码与向寄存器送数指令中的操作码是不同的,是可以区别的。比如我们执行SETB P1.2和CLR P1.2就能改变P1.2引脚电位的高低。若我们在SETB P1.2和CLR P1.2之间调用了500μs延时子程序,就会使P1.2呈高低电位的时间各为500μs,那么,B1在此方波作用下完成一次全振动的时间就是1ms,因此,B2就发出1000Hz的声音来。同理,若我们在P1.2呈高低电位状态之间2次调用500μs延时子程序,B2就能发出频率为500Hz的声音。以此为基本应答单元,通过不同的排列组合,就可表达不同的含义,实现对按键确认或其它命令的应答。
以下列举出本例所用的几条主要指令的含义、对应的机器码和格式及其说明。
(1)条件长转移指令LJMP addr16。此指令为三字节指令,第一字节是操作码,机器码为02H,后两个字节是操作数,指出转移的目的地址,高8位在前,低8位在后。跳转范围64K。
(2)位高电位转移指令JB bit,rel。此指令有三个字节构成,第一字节是操作码,机器码为20H,第二字节是操作数指定被操作位的地址,第三字节确定了转移的偏移量。跳转范围-128至+127。
(3) 低电位转移指令JNB bit,rel。该指令操作码为30H,第二字节是操作数,指定被操作位的地址,第三字节确定了转移的偏移量,跳转范围同上。
(4) 寄存器减1不为零转移指令DJNZ Rn,rel。该指令为两字节指令,第一字节指明了被操作寄存器的地址,机器码为78H至7FH,依次对应的被操作寄存器是R0至R7。第二字节为偏移量,转移范围为-128至+127。
(5) 位置“1”指令SETB bit。该指令为两字节指令,操作码为D2H,操作数指出被操作位的地址,是范围在00H至FFH之中可进行位操作的位。
(6) 位清“0”指令CLR bit。操作码为C2H,其它同SETB bit。
(7) 立即数传送指令MOV direct,#data。此指令为三字节指令,第一字节为操作码75H,第二字节为操作数,指出送数的目的地址,第三节数是传送的立即数。
(8) 长调用指令LCALL addr16,机器码为12H。可调用放在64K空间任何地方的子程序。此指令为三字节指令,第一字节是操作码,后两个字节是操作数,指出所调用子程序的存放地址。
(9) 子程序返回指令RET。机器码为22H。
限于篇幅,本例程序清单略。若读者需要源程序,可到本刊网址www.radio.com.cn上查找。
另外,AT89C51的详细技术资料,可在www.atmel.com/atmel/products/ prod71.htm下载 ,有多种文件格式。
(滕世进)

