第二十课 定时与中断实验

前面我们用定时器和中断做了延时的实验 现在再来看一看外部计数和外部中断的实验 在实际

的工程应用中计数器通常会有两种要求 第一种 将计数的值显示出来 象录音机上的计数器 汽车上 的里程表等等 第二种 计数值到一定值后即中断报警 如前面提到的生产线上的计数 定长定量仪等

接下来我们先来做一个外部计数器的实验 要将外部计数的值显示出来 最好是用数码管 可我 们还没有讲到这一部份 为了避免把问题复杂化 我们用 P1 口的 8 个 LED 来显示计到的数据 为了完 成这个实验 我们需要用到一套附件 它的作用就是输出一个宽度为 500mS 的方波 这套附件不在我 们的实验板上 为了节约大家的学习费用 我特地做了几套供大家借用 各位可以到我这儿来借 免费 使用 不过请各位爱惜哦 不要搞坏了 有了这套附件就可以完成我们的实验了 我们把附件的两根线 分别连接到实验板的电源接口和单片机的 15 脚也就是 T1 的输入端实验的程序如下

一 计数器实验一 程序如下

ORG 0000H ; AJMP START ; ORG 0030H ;

START:MOV SP,#5FH ;

MOV
TMOD,#40H
;定时/计数器 1 作计数用
T0 不用全置
0

SETB
TR1
;启动 T1 开始运行


LOOP:MOV A,TL0 ; MOV P1,A ; AJMP LOOP ; END

运行这个程序 看到了什么 随着 LED 的闪烁 实验板上的 8 个 LED 也在不断地变化 注意观

察 是不是按二进制 00000000 00000001 00000010 00000011 这样的顺序在变呢 对了 这 就是 TL0 中的数据 不过这个实验还看不出什么名堂 接着做第二个实验

二 计数器实验二 程序如下

ORG 0000H ; AJMP START ; ORG 001BH ;

AJMP TIMER1 ;定时器 1 的中断处理

ORG 0030H ; START:MOV SP,#5FH ;

MOV TMOD,#40H ;定时/计数器 1 作计数用 工作方式 1 T0 不用置 0

MOV
TH1,#0FFH
;


MOV
TL1,#0FAH
;预置值
每计到 6 个脉冲即为一个事件

SETB
EA
;


SETB
ET1
;开总中断和定时器 1 中断允许


SETB
TR1
;启动定时/计数器 1 开始运行


AJMP
$
;


 

TIMER1:PUSH ACC ;

78

----------------

 

PUSH PSW ;

CPL P1.0 ;计数值到 即取反 P1.0

MOV
TH1,#0FFH
;

MOV
TL1,#0FAH
;重置计数初值

POP PSW ;

POP ACC ; RETI ; END

这段程序完成的工作其实很简单 就是每 6 个计数脉冲到来后取反一次 P1.0 因此实验的结果应

当是 15 脚接的 LED 亮 灭 6 次 则 P1.0 口所接的 LED 就亮或灭一次 这就是我们对输入脉冲的计

数 也就是每 6 个计数产生一次中断 这段程序中有一个符号以前没见过 需要给大家解释一下 AJMP

$$ 我们称为标识符 它的作用是指这条指令的开始处 在这里 其实就是循环执行 AJMP $这条 指令 执行这么多次干什么 实际上它是在等待中断的产生

这两个实验需要附件 如果您觉得做起来不大方便 没关系 我们接着来做第三个实验 采用两 个定时/计数器合用 一个作为定时器用 一个作为计数器用 来实现 P1.1 的延时 这可以直接在我们 的实验板上完成

三 两个定时/计数器合用的延时实验

采用两个定时/计数器 其中 T0 作为定时器用 工作方式为方式 1 T1 作为计数器用 计数次数

为 1000 次 T0 溢出时 产生一个间隔为 60mS 的方波也就是让 LED3 各亮灭 60mS然后把 P1.2

的输出作为 T1 的计数脉冲 T1 计数溢出时满 1000 次取反一次 P1.1 产生一个周期为 2 秒的方波

即 LED2 每 2 秒闪烁一次

程序如下

ORG 0000H ; AJMP MAIN ;

ORG 000BH ;定时器 T0 的中断入口

AJMP T_0 ;转 T0 中断服务程序 ORG 001BH ;定时器 T1 的中断入口 AJMP T_1 ;转 T1 中断服务程序 ORG 0030H ;

MAIN:MOV TMOD,#51H ;T1 为计数器方式 1 T0 为定时器方式 1

MOV
TH0,#15H
;设置 T0 初值

MOV
TL0,#0A0H
;

MOV TH1,#0FCH ;设置 T1 初值

MOV
TL1,#18H
;



MOV
IE,#8AH
;允许
T0
T1 中断

SETB
TR0
;启动定时器 T0


SETB
TR1
;启动定时器 T1


LL:SJMP LL ;循环

T_0:MOV TH0,#15H ;给 T0 重新赋值

MOV TL0,#0A0H ;


 

T_1:MOV TH1,#0FCH ;给 T1 重新赋值

MOV TL1,#18H ;

CPL P1.1 ;计数到 取反 P1.1

 

79

----------------

 

RETI ; END

把程序下载到单片机 看到什么 LED3 在不断的闪烁 这就是 T0 的作用 闪烁的周期是多少

请大家计算一下 接下来 把 P1.2也就是 3 脚和 P3.5也就是 T1的输入端相连接 是不是接在

P1.1 上的 LED2 每 2 秒闪烁一次 对了 这就是 T1 作计数器的结果

在这段程序里 有一点请大家注意 第四条—AJMP T_0,为什么要在 T 和 0 之间加上一条横线

而不直接用 T0 呢 原来在 MCS—51 系列单片机中 是不能用 T0 T1 INT RET IP PSW 等等内 部名称作为标号的 如果这样做的话 编译软件会出错 这点我们好象很早以前曾经提到过 接下来我 们再来做一个外部中断的实验

四 外部中断实验 程序如下

ORG 0000H ; AJMP START ;

ORG 0003H ;外部中断 0 地址入口

AJMP INTO ; ORG 30H ;

START: MOV SP,#5FH ;

MOV
P1,#0FFH
;灯全灭

MOV
P3,#0FFH
;P3 口置高电平

SETB
EA
;

SETB
EX0
;

AJMP $ ;

INTO:PUSH ACC ; PUSH PSW ; CPL P1.0 ;

POP
PSW
;

POP
ACC
;

RETI ;

END

本程序的功能就是按一次按键 S1接在 P3.2 引脚上的就引发一次外部中断INT0=0取反 一次 P1.0 因此理论上按一下灯亮 再按一下灯灭 有点象我们工程应用中的自锁开关 不过这段程

序在实际的实验中 可能会发觉有时不很 灵按了它没反应 但在大部份时候还是对的 这是怎么 回事呢 其实这是因为按键没有作 去抖动 处理 也就是说 理论上我们是按了一次键 但由于计算

机的处理速度很快 计算机实际上却认为已经按了好多次了 如何来解决这个问题呢 这就需要我们对 按键作去抖动处理 什么是按键的去抖动处理 我们下一课讨论键盘接口时再作详细解释

五 本课总结

通过这两节课的实验 我们对定时/计数器和中断的使用方法已经有了一个基本的了解 希望大家 继续多做实验 本来嘛 学会单片机靠的就是不断的实践和总结

 

80

----------------

 

第二十一课 键盘接口与编程 一

键盘接口和数码管接口是构成单片机人机界面的主要方法 对于一个初学者来说 这部分的内容

也是较难的 我们将用四节课的时间来学习这方面的知识 这一课先来讨论键盘的接口原理与编程方法

键盘是单片机应用系统不可缺少的重要输入设备 主要负责向计算机传递信息 我们可以通过键 盘向计算机输入各种指令 地址和数据 它一般由若干个按键组合成开关矩阵 按照其接线方式的不同

可分为两种 一种是独立式接法 一种是矩阵式接法如下面的图这一课先来讲解独立式键盘的工 作原理和编程方法

一 独立式键盘的工作原理和编程方法

独立式键盘具有结构简单 使用灵活等特点 因此被广泛应用于单片机系统中 那么它是如何来 工作的呢 我们慢慢往下看

1 独立式键盘的接线原理

独立式键盘是由若干个机械触点开关构成的 把它与单片机的 I/O 口线连起来 通过读 I/O 口的 电平状态 即可识别出相应的按键是否被按下 下面的左图就是我们实验板的按键连接图

如果按键不被按下 其端口就为高电平 如果相应的按键被按下 则端口就变为低电平 在这种

键盘的连接方法中 我们通常采用下拉电平接法 即各按键开关一端接低电平 另一端接单片机 I/O

口线 如上左图所示 这是为了保证在按键断开时 各 I/O 口线有确定的高电平

通常我们用来做键盘的按键有触点式和非触点式两种 单片机中应用的一般是由机械触点构成的 触点式微动开关 这种开关具有结构简单 使用可靠的优点 但当我们按下按键或释放按键的时候它有 一个特点 就是会产生抖动 看上图的按键脉冲波形 这种抖动对于人来说是感觉不到的 但对单片机 来说 则是完全可以感应到的 因为单片机处理的速度是在微秒级的 而机械抖动的时间至少是毫秒级

对计算机而言 这已是一个很 漫长 的过程了 下面我们通过一个实验来验证一下 实验程序如下

ORG 0000H ; AJMP START ; ORG 0030H ; START:MOV SP,5FH ;

 

81

----------------

 

MOV
P1,#0FFH
;

MOV
P3,#0FFH
;

L1:JNB P3.4,L2 ;按下按键开关 取反一次 P1.0灯亮再按一下灯灭

JNB P3.5,L3 ;按下按键开关 取反一次 P1.1灯亮再按一下灯灭

LJMP
L1
;

L2:CPL
P1.0
;

LJMP
L1
;

L3:CPL
P1.1
;

LJMP
L1
;

END

把这个程序下载到单片机 我们会发现 当按下相应的按键时 灯并不是想象中的按一下亮 再 按一下就灭 而是有时灵 有时不灵 为什么会这样呢 原来 当你按了一次按键 可是单片机却早已 执行了好多次 如果执行的次数正好是奇数次 那么结果正如你所料 如果执行的次数是偶数次 那结 果就不对了 为了使 CPU 能正确地读出端口的状态 对每一次按键只作一次响应 就必须考虑如何去除 按键的抖动

2 按键的去抖动原则和方法

常用的去抖动的方法有两种 硬件方法和软件方法 硬件去抖动的方法很多 好多书都有介绍

这不在我们的讨论范围 单片机中常用软件去抖动法 软件法其实也很简单 就是在单片机获得端口为 低电平的信息后 不是立即认定按键已被按下 而是延时 10 毫秒或更长一些时间后再次检测该端口

如果仍为低 说明此键的确被按下了 这实际上是避开了按键按下时的抖动时间 而在检测到按键释放

后端口为高电平时再延时 5-10 毫秒 消除后沿的抖动 然后再对按键进行处理 不过一般情况下

我们通常不对按键释放的后沿进行处理 实践证明 也能满足通常的要求 下面我们把前面的程序改一

下 看看按键的去抖动是如何实现的 看下面的程序

ORG 0000H ; AJMP START ; ORG 0030H ; START:MOV SP,#5FH ; MOV P1,#0FFH ; MOV P3,#0FFH ;

L1:JB P3.4,L2 ;P3.4 为 1不做处理 转 P3.5 否则说明有键按下

LCALL D10mS ; 调用延时程序 去除抖动

JB P3.4,L1 ; P3.4 为 0说明此键确实被按下了

CPL P1.0 ;取反 P1.0

L3:JNB P3.4,L3 ;直到 P3.4 释放后转去判断第二个键

L2:JB P3.5,L1 ;P3.5 为 1返回去继续处理 P3.4 否则说明有键按下

LCALL D10mS ; 调用延时程序 去除抖动

JB P3.5,L2 ; P3.5 为 0说明此键确实被按下了

CPL P1.1 ; 取反 P1.1

L4:JNB
P3.5,L4
;直到 P3.5 释放为止

LJMP
L1
;返回

D10mS:MOV R7,#50 ;延时的时间一般为 5-20mS

D1:MOV R6,#100 ; D2:DJNZ R6,D2 ; DJNZ R7,D1 ;

 

82

----------------

 

RET ; END

把这段程序写入单片机 试试看 是不是行了 这就是独立式按键去抖动的基本方法 不过这个

程序在实际应用中并没有多大的意义 因为如果按键数量比较多的话 程序就会变得很长 为什么会这 样呢 因为这里我们采用了直接寻址的方式 如果我们把键值放入一个表格中 再通过查表程序来判断 到底是哪个按键被按下了 再去处理相应的程序就会很简单 想想看 该怎么做

二 独立式键盘的编程方法

我们刚才的程序演示了按键的去抖动原理和基本方法 接下来让我们做一个按键使用的实验来验 证一下 大家看附图的电路图 我们的实验板上有 4 个按键分别接到了 P3 口的 P3.2,P3.3,P3.4,P3.5 引脚上 现在我们用 P3.2,P3.3,P3.4 和 P3.5 这四个按键来做一个实验 实验之前 先定义各个按键的 功能

 

 


 

 

 

实验程序如下

按此键则灯开始流动(由左向右)

按此键则停止流动 所有灯为灭

按此键则灯反向流动由右向左

按此键则灯正向流动由左向右

UpDown EQU 00H ; 上下行标志

StartEnd
EQU
01H;
起动及停止标志

LampCode
EQU
21H;
存放流动的数据代码

ORG 0000H ;

AJMP MAIN ; ORG 30H ; MAIN:MOV SP,#5FH ;

MOV
P1,#0FFH
;

CLR
UpDown
;启动时处于向上的状态

CLR
StartEnd
;启动时处于停止状态

MOV LampCode,#0FEH;单灯流动的代码

LOOP:ACALL KEY ;调用键盘程序

JNB F0,LNEXT ;如果无键按下 则继续 ACALL KEYPROC ;否则调用键盘处理程序 LNEXT:ACALL LAMP ;调用灯显示程序

AJMP LOOP ;反复循环 主程序到此结束

DELAY:MOV R7,#100 ; D1:MOV R6,#100 ;

DJNZ
R6,$
;

DJNZ
R7,D1
;

RET ;延时程序 键盘处理中调用

KEYPROC:MOV A,B ;从 B 寄存器中获取键值

JB ACC.2,KeyStart ;分析键的代码 某位被按下 则该位为”1”在键盘程序中已取反

JB ACC.3,KeyOver ;

JB
ACC.4,KeyUp
;

JB
ACC.5,KeyDown
;

AJMP KEY_RET ;

KeyStart:SETB StartEnd ;第一个键按下后的处理

AJMP KEY_RET ;

 

83

----------------

 

KeyOver:CLR StartEnd;第二个键按下后的处理

AJMP KEY_RET ;

KeyUp:SETB UpDown ;第三个键按下后的处理

AJMP KEY_RET ;

KeyDown:CLR
UpDown
;第四个键按下后的处理

KEY_RET:RET

;

KEY:CLR F0 ;清 F0 表示无键按下

ORL
P3,#01111000B
;将 P3 口接有四个键的位置
1


 

JZ K_RET ;如果为 0 则无键按下

ACALL DELAY ;否则延时去键抖

ORL
P3,#01111000B
;


MOV
A,P3
;


ORL
A,#10000111B
;


CPL
A
;


JZ K_RET
;


MOV B,A
;确实有键按下
将键值存入 B 中

SETB F0 ;设置有键按下的标志

K_RET:ORL P3,#01111000B ;此处循环等待键的释放

MOV
A,P3
;

ORL
A,#10000111B
;

CPL
A
;

JZ K_RET1 ;直到读取的数据取反后为”0”说明键释放了 才从键盘处理程序返回

AJMP K_RET ;

K_RET1:RET ;

D500mS: ;流水灯的延迟时间

PUSH
PSW
;

SETB
RS0
;

MOV R7,#200 ;

D51:MOV R6,#250 ; D52:NOP

NOP

NOP

NOP



DJNZ
R6,D52
;

DJNZ
R7,D51
;

POP PSW ;

RET ;

LAMP:JB StartEnd,LampStart ;如果 StartEnd=1 则启动

MOV P1,#0FFH
;


AJMP LAMPRET
;否则关闭所有显示
返回

LampStart:JB UpDown,LAMPUP ;如果 UpDown=1 则向上流动

MOV A,LAMPCODE ;

 

84

http://www.51hei.com 单片机学习中心,为您免费发布大量的学习资源以及商业代码

 

RL A ;实际就是左移位

MOV
LAMPCODE,A
;

MOV
P1,A
;

LCALL D500mS ;

AJMP LAMPRET ; LAMPUP:MOV A,LAMPCODE ;

RR A ;向下流动实际就是右移

MOV
LAMPCODE,A
;

MOV
P1,A
;

LCALL D500mS ;

LAMPRET:RET ; END

这段程序是我们到目前为止最长的程序 相信大多数指令大家应该能看懂 开始三条 UpDown EQU

00H StartEnd EQU 01H LampCode EQU 21H 给大家解释一下 EQU 叫做等值伪指令 它的功能是 将一个常数或者特定的符号赋予规定的字符串 什么意思呢 举个例子

ORG 200H

ABC EQU R6

MOV A ABC

这里将 ABC 等值为寄存器 R6 也就是说 在指令中 R6 这个寄存器可以用字符串 ABC 来代替 为 什么要这样写呢 当然是为了增加程序的可读性 不过有一点大家要记住了 这里使用的字符串不是标

号 不能用来做分隔符 比如这样写 ABC:EQU R6 如果加上汇编程序会出错 当然 用 EQU

指令除了可以赋值数据和地址外 还可以赋值直接地址或者直接当作一个立即数来使用 例如

ABC EQU 10H

DELAY EQU 05AFH

MOV A ABC

LCALL DELAY

这里 ABC 赋值以后被当作了直接地址使用 而 DELAY 被赋值以后则成了一个 16 位的地址

如此一来 上面的三条指令也就很清楚了 这里有一个问题大家需注意 使用 EQU 伪指令必须 先赋值 后使用 所以一般的程序都把赋值指令放在程序的开头部分

既然讲到了赋值伪指令 我们再讲一下另外三条赋值伪指令.

A 位地址定义伪指令 BIT

它的功能是将一个可直接寻址的位地址赋予所规定的字符名称 例如

ABC BIT P1.0 把 P1.0 赋值给 ABC 即字符串 ABC 就是直接寻址位 P1.0

这里注意 与 EQU 不同的是 这条指令只能对位地址赋值 而不能对寄存器或直接地址和立即 数赋值 相反 EQU 指令却可以用来定义位地址变量 不过这时所赋的值应当是具体的位地址值 比如 P1.0 要用 90H 来代替 P2.0 要用 AOH 来代替等等

B 内部 RAM 定义伪指令 DATA

它的功能是给一个 8 位的内部 RAM 起一个名称 例如

ABC DATA 20H 把内部 RAM 的 20H 定义为 ABC

C 外部 RAM 定义伪指令 XDADT

给一个 8 位的外部 RAM 起一个名称 例如

ABC XDATA 0ACH 由于 89C51 的内部 RAM 寻址范围为 00H-FFH 所以这个地址必然大于 FFH

讲了赋值伪指令 再回到上面的按键处理程序 这段程序的功能虽然很简单 但它演示了一个键盘 处理程序的基本思路 程序本身很简单 也不很实用 实际工作中还会有好多要考虑的因素 比如主循

环每次都调用了灯的循环程序 会造成按键反应 迟钝而如果一直按着键不放 则灯不会再流动

 

85

----------------

 

一直要到松开手为止 大家可以仔细考虑一下这些问题 想想有什么好的解决办法

独立式键盘除了上面介绍的这种连接方法 我们还可以采用上图右边所示的连接方法 用一个 与

非 门把四个输入端连接起来 当有任何一个按键按下时 都会使 与非 门输出为低电平 从而引起 单片机的中断 它的好处是不用在主程序中不断地循环查询了 如果有键按下 单片机就去作相应的处


 

 

这一课通过两个实验讲解了独立式键盘的工作原理和基本的去抖动方法 由于键盘的实际使用是 千差万别的 所以工程中您还得根据实际情况灵活应用 这里只能给大家一个基本的认识

四 第 21 课习题

1
什么是键盘的去抖动问题
为什么要对键盘进行去抖动处理

2
找一个硬件的去抖动电路
并自行分析其工作原理

86----------------

版权所有:单片机教程网 2007
Email:erd51@163.com qq:58565254