首页 百科知识 软件延时子程序的设计

软件延时子程序的设计

时间:2022-10-22 百科知识 版权反馈
【摘要】:本章重点是要求读者掌握汇编程序的设计方法,并结合具体的设计案例进行实际程序的编写。子程序又称为过程,它相当于高级语言中的过程或函数。我们首先来看中断子程序的定义方式。由于CPU在延迟过程中,大量耗在了空闲指令上,降低了CPU处理任务的效率,如果延迟的时间要求比较长,建议采用定时器的方式来进行延迟程序的设计。

第9章 单片机应用设计

【教学目的】

本章以简单实用的例子剖析51单片机的应用方式,结合单片机实验开发板,编写读者实用的汇编程序,加深对单片机理论知识的理解。

【教学要求】

通过本章学习,要求能够掌握51单片机汇编程序的编写方法和调试方法,理解单片机实验板的使用方法,熟练编写51单片机各功能模块的应用程序,并掌握单片机的应用方式。

【重点难点】

本章重点是要求读者掌握汇编程序的设计方法,并结合具体的设计案例进行实际程序的编写。本章难点是对每个设计案例的应用方法。

【知识要点】

本章的重要知识点有汇编程序设计的方法,子程序设计,中断程序的设计,定时器/计数器的设计和应用,LED应用设计,键盘应用设计,看门狗应用设计等。

9.1 子程序设计

子程序又称为过程,它相当于高级语言中的过程或函数。在一个程序的不同部分,往往要用到类似的程序段,这些程序段的功能和结构形式都相同,只是某些变量的赋值不同,此时就可以把这些程序段写成子程序形式,以便需要时可以调用它。有些程序段可能会被经常用到。例如:将十进制数转换为二进制数、二进制数转换为十六进制数并显示输出、软件延时等。对于这些常用的特定功能的程序段,也经常被编制成子程序的形式以供用户使用。

模块化程序的设计方法是按照各部分程序所实现的不同功能把程序划分为多个模块,各个模块在明确各自的功能和相互间的连接约定后,就可以分别编制和调试程序,最后把它们连接起来,形成一个大程序。这是一种很好的设计方法,子程序结构就是模块化程序的基础。

9.1.1 子程序的定义

在这一小节里,我们给出了在Keil A51下定义子程序的一般方法。子程序在Keil A51环境下分为两种:一种是普通的子程序,另一种是中断子程序。下面我们就这两种子程序分别说明。

(1)普通子程序:普通子程序是指通常意义上的子函数。我们通过ACALL或LCALL指令调用这个子程序,子程序的最后一条指令必须是RET。子程序基本的定义方式如下所示,其中Sub_proc_xxx为子程序的标识符。

(2)中断服务子程序:中断服务子程序是专门为中断服务的一种子程序,其定义以及调用的方式都与普通子程序有一定的区别。我们首先来看中断子程序的定义方式。

从上面的定义可以看出,中断服务子程序的最后一条指令是RETI,这与普通子程序是不一样的,大家写程序的时候一定要注意。中断函数返回时所进行的操作也和普通子程序返回时不一样,它们的区别已经在前面的章节中讲过,这里不再赘述。

9.1.2 子程序的调用

对于普通子程序的调用在前面的章节中已经讲过,我们这里重点讨论中断服务子程序的调用。一般来说中断子程序不应直接用ACALL或LCALL指令加以调用,因为中断服务子程序是用来处理中断服务的。当中断发生的时候,若全局的中断被开启(EA=1),处理器将自动查询中断向量表,并转入相应的中断服务子程序执行。

假设在程序中要使用外部中断0,下面的示例代码给出怎样设置中断向量表,关于中断向量表的内容请参考中断程序编写有关章节。

从上面的例子中可以看出,使用中断服务子程序的一般流程如图9-1所示。

图9-1 中断服务子程序的一般流程

9.1.3 子程序的参数传递

调用程序在调用子程序时,经常要传送一些参数给子程序;子程序运行完成后也经常要返回一些信息给调用程序。这种调用程序与子程序之间的信息传递称为参数的传递(或称变量传送或过程信息)。参数传送有以下几种方式:

(1)通过寄存器传送参数。这是最常用的一种方式,使用方便,但是参数很多时不能用这种方式。

(2)直接访问RAM区传送参数。可以在RAM区中开辟一个空间用于调用程序与子程序的参数传递。当函数的参数很多时可以采用这种方式。

(3)通过堆栈来传送参数。当调用某子程序之前,可以将子程序所需要的参数逐个入栈,进入子程序后再将参数出栈。子程序的返回值也可以通过这种方式传送。当程序有多重嵌套调用时,这种方式可能会使堆栈所占的内存区过大。

由于在MCS-51系列单片机上可以利用的RAM区很少,在具体使用的时候可以选择适当的参数传递方式。

9.2 宏定义

在上一小节中介绍了子程序,并了解到使用子程序结构具有很多优点:可以节省存储空间以及程序设计所花的时间,可以提供模块化程序设计的条件,便于程序的调试与修改等。但是,在使用子程序的时候也有一些缺点:为子程序以及返回、保存及恢复寄存器以及参数的传送都要增加程序的开销,这些操作所消耗的时间以及它们所占用的存储空间,都是为了取得子程序结构使程序模块化的优点而增加的额外的开销。因此,有时特别在子程序本身较短或者是需要参数较多的情况下,使用宏汇编就更加有利。

9.2.1 定义标准宏

下面我们来介绍在AX51下怎样定义一个标准的宏。

定义一个标准宏的语法如下:

macro-name MACRO [ parameter-list]

[ LOCAL local-labels ]

.

macro-body

.

ENDM

宏定义中各参数的意义如下:

macro-name为宏的名称,当需要的时候可以用该名称来展开该宏。

parameter-list为宏所用到的参数列表,该参数是可选的。当参数的个数大于1时,各个参数之间应该用逗号分隔开。在Ax51中,宏汇编的参数最多为16个。若要传送一个NULL值为宏的参数,可以简单地省略该参数就可以了,但是逗号分隔符不能省略。

local-labels为宏定义内部所用到的标识符列表,该参数也是可选的。

macro-body:宏的主体部分。在该部分中也可以调用其他宏,调用时这些宏也将被展开。当定义一个宏时,宏并没有立即被展开,它们直到被调用时才被展开。

下面是一个宏定义的例子:

LOAD_R0 MACRO R0_Val

     MOV R0,#R0_Val

     ENDM

上面定义了一个名为LOAD_R0的宏,它的作用是将第一个参数传送到寄存器R0中。宏定义必须注意的几个问题:

◆ 标准宏一旦被定义了,该宏就不能被重新定义。

◆ 标准宏可以有参数,也可以没有参数。

◆ 标准宏的嵌套深度最高不能超过9层。

◆ 标准宏不能被递归地调用超过9次。

1. 标准宏的调用

标准宏的调用方式很简单,调用时只需给出宏名以及宏所需要的参数列表。语法如下:

<MCRONAME> PARAMETER-LIST

对于上面所定义的宏LOAD_R0,其调用方式为:

LOAD_R0 200

2. Ax51预定义宏

在Ax51编译器中内建了三种宏(见表9-1),它们可以在程序中被独立调用,也可以在宏定义中使用。

表9-1 A51预定义宏

(1)REPT宏

REPT宏的定义如下:

REPT count

macro-body

ENDM

(2)REPT宏的调用方式为:

9.3 软件延时子程序的设计

CPU执行一条指令需要一定的时间,软件延时的方法就是利用CPU执行一定数量的指令的方式来实现的,其中执行的指令大多是空闲指令,软件延时是建立在精确的计算基础上的,所以在设计上需要花费大量的工作去分析程序在怎么执行,以及指令执行了多少条。由于CPU在延迟过程中,大量耗在了空闲指令上,降低了CPU处理任务的效率,如果延迟的时间要求比较长,建议采用定时器的方式来进行延迟程序的设计。但如果延迟的时间要求比较短,采用软件延迟的方式,将会有更高的执行效率,例如:要产生一个尖峰脉冲,这时采用软件延迟的方式将会更容易实现。

51单片机的内部所有时钟来源由外部晶振提供,外部晶振接在单片机的XTAL1和XTAL2引脚上,构成自激振荡器,为CPU提供稳定的时钟信号,频率为Fosc。振荡器的输出经过2分频后构成内部时钟信号,用作单片机内部功能模块的控制信号,如定时器、串口等,频率为Fpclk;其周期为时钟周期,表示为Tpclk,6个时钟周期构成一个机器周期Tpclk,频率为Fclk,如图9-2所示。

图9-2 51单片机内部时钟结构图

CPU执行一条指令的时间称为指令周期,指令周期以机器周期为单位,单周期指令只需要1个机器周期就可以执行完成,双周期指令需要2个机器周期才能执行完成,51单片机的乘法和除法是4周期指令,即需要4个机器周期才能执行完成。对于单周期指令,其执行一条指令的时间为:

Tc=(1/Fosc)* 12

如果我们采用22.1184MHz的晶体,那么单周期指令执行一条指令的时间可表示为:

Tc=(1/22.1184*1000000)*12 s

Tc=(1/22.1184*1000000)*12 *1000 ms

Tc=0.5425µs

例1:利用单片机软件延迟程序,实现给定时间的延迟。

解:首先进行段定义,这是编辑一个程序所必需的,主要进行段的定义、堆栈设置、必要的初始化。

ORG 0000H

 JMP START

该段程序表示在程序区的0地址定义程序执行的开始,CPU复位以后,PC指针指向0地址开始执行,如果没有该段定义,CPU复位后将会导致程序的不正常执行。

ORG 0030H

START

主程序段的开始,主程序段必须跳过0000H~0030H的地址段,因为该段定义了中断的向量地址,只能根据中断的设定在特定的位置存放中断向量入口程序。如图9-3所示。

图9-3 程序段地址关系

MOV SP,#30H;

表示初始化堆栈指针,CPU复位后,堆栈指针SP为07H,而数据缓冲区从30H开始,所以,把堆栈指针初始化为30H以后的地址。如下图9-4所示。

图9-4 数据存储器地址分配

Delay程序段,采用了三重循环来实现延迟功能,分别对R0、R1、R2赋值实现不同的延迟长度。

从上面的程序代码来进行分析:

L3:DJNZ R2,L3程序段:表示第一层循环,执行的条数为:

N1=R2 条

第二层循环的代码为:

L2: MOV R2,#0FFH

L3: DJNZ R2,L3

   DJNZ R1,L2

很显然,第二层循环包含了第一层循环,执行指令的条数为:

N2=N1*R1+2*R1

以此类推,第三层循环所执行的指令的条数为:

N3=N2*R0+2*R0

执行的总条数为:

N=N1+N2+N3

N=R2+R1R2+2R1+R0R1R2+2R0R1+2R0

N=R2+2R0+2R1+R1R2+2R0R1+R0R1R2

当然,调用该延迟函数段还要执行入栈指令,返回也要调用出栈指令,如果延迟时间相对比较长,可以忽略不计。

由于以上循环程序里面所有的指令均为双周期指令,所以执行以上循环所实现的延迟时间为:

T=2*N*Tc

在上述程序段中,给R0赋值10H,R1赋值FFH,R2赋值FFH,在22.1184MHz晶振下算得:T=1.14s

程序的完整代码如下:

9.4 存储器读写程序的设计

存储器在计算机系统中是一种最基本的存储介质。一个完整的计算机系统,必须包括程序存储器,数据存储器,运算存储器。在单片机系统中,程序存储器用于存储编译后的指令代码,一般指可直接运行的机器代码。该存储器一般采用只读存储器的方式,以防止程序代码的破坏,如果要更新程序,需要采用专用的设备或专门的时序进行程序的刷新(烧录),在AT89S52单片机内部,系统已经集成了8KB的Flash程序存储区,支持掉电保存,数据存储器用于存储运算过程中产生的数据,如在C语言中定义的变量。该段存储器可读可写,读写速度快,但不支持掉电保存,在AT89S52内部,集成了256B的数据存储器,完全能够满足AT89S52基本应用的需要。运算存储器用于在运算过程中暂存数据,通常称为寄存器。

在51系列单片机中,不同存储器编址方式采用同一编址,即地址是可以重叠的,系统通过不同的指令来区别所选择的存储器,如图9-5所示为单片机系统中不同的存储器的编址方式。

图9-5 51系列存储器编址方式

从图9-5中可以看到,单片机系统主要包括程序存储器和数据存储器,程序存储器包含片内Flash存储器或片外存储器,两块存储器地址都从0000H开始,AT89S52片内存储器容量为8KB,即最大地址为1FFFH,片外扩展存储器最大可达64KB,进行存储器访问的指令为MOVC,例如:

MOVC A,@A+DPTR

当DPTR的内容小于1FFFH时,如何判断读取的数据到底是在片内FLASH中还是外扩存储器中呢?51系列单片器通过/Vpp引脚来区别,当/Vpp为高电平时,系统选择片内Flash存储器,MOVC指令操作的是片内Flash存储器指令;当/Vpp为低电平时,系统选择片外扩展存储器。MOVC指令操作的是片外扩展存储器。

在单片机系统中包含三种类型的数据存储器,分别是片内数据RAM区,片内特殊功能寄存器区,片外数据扩展区,均采用同一编址,他们采用不同的指令和寻址方式来区别,其中,特殊功能寄存器采用直接寻址的方式实现数据的传输,例如:

MOV A,P0

P0的地址是80H,这条指令表示将特殊功能寄存器的地址80H的数据送到A寄存器。访问片内数据存储器和片外数据存储器均采用间接寻址的方式,而它们又采用不同的指令来区别。例如读取片内数据存储器中的80H地址的内容,使用如下语句:

MOV R1,#80H

MOV A,@R1

该语句首先把地址送入R1寄存器中,然后通过R1寄存器进行间接获取80H地址上的数据。如果是要读取片外数据存储器中的数据,采用如下指令:

MOVX R1,#80H

MOVX A,@R1

该语句和访问片内RAM存储器中的数据类似,但使用的是不同的汇编指令,通过寻址方式的不同和数据访问指令的不同,在不同存储器同一编址的情况下,进行数据访问的时候就可以明确区别具体访问哪个存储器中的数据。

例2:编写程序,将片内RAM区0080H~008FH分别置数为00H~0FH。利用Keil集成开发环境的Debug调试功能单步运行程序,在数据存储区中查看数据的正确性。

解:程序设计如下:

例3:在例2的基础上,编写程序,将片内RAM区0080H~008FH中的数据依次移到0090H~009FH中,并采用上述同样的办法查看程序运行的正确性。

解:程序设计如下:

例4:编写程序,将片内程序存储区0100H~010FH地址分别预置数据00H~0FH,并将区间的数据移到片内RAM区的0A0H~0AFH中,并验证数据的正确性。

解:程序设计如下:

9.5 八段数码管显示程序

八段数码管在工业控制中有着很广泛的应用,例如用来显示温度、数量、重量、日期、时间,还可以用来显示比赛的比分等,具有显示醒目、直观的优点。我们本节就来讨论LED数码管的应用设计以及对它进行编程控制。

9.5.1 LED数码管分类

LED数码管按其引脚的连接方式可以分为共阴管、共阳管;按数码转换为笔画信息的方式不同,可以分为软件译码、硬件译码;按扫描的方式不同,分为动态扫描和静态扫描。

采用共阴极数码管还是共阳极数码管没有太明显的优缺点。然而与同一数码管对应的笔画信息码往往相互是自反的关系。例如0、1、2、3......的笔画信息码在共阴极数码管下是3FH、06H、5BH、4FH......,而在共阳极数码管下是C0H、F9H、A4H、B0H…。图9-6为共阳极与共阴极数码管的区别。

图9-6 共阳与共阴数码管的区别

9.5.2 数码管的译码

数码管译码有软件译码方式和硬件译码方式,软件译码是将各数码管的笔画信息构成一个表格预先存储在RAM中,以后根据要显示的每一数码执行一段查表程序,查得相应的笔画信息再送数码管显示;硬件译码则采用CD4511、74LS46、74LS47、74LS48、74LS49 等BCD码——7段锁存、译码、驱动芯片直接译出笔画信息。

多个数码管采用动态扫描的方式工作,动态扫描时各个数码管是轮流点亮的,由于视觉的暂留现象,给人的感觉是数码管一直都是亮着的。而实际上控制数码管点亮的位选信号是逐一送出的。而每个数码管应显示数码的笔画信息则与位选信号同时送出的,于是各数码管按次序一一显示出自己的数码;待各管都轮到以后,又从头开始显示。对于动态扫描,轮到某管、等待该管点亮必须留给一段恰当的时间。时间过短,数码管来不及点亮;时间过长,其他的数码管将熄灭、不能显示。静态扫描五位选信号,各数码管是同时点亮的;每数码管应显示数码的笔画信息也分路同时送给,其原理比较简单。静态扫描编程比较容易,显示比较清晰,亮度一般较高,但是占用很多I/O接口和增加硬件芯片,成本较高。因此,实际使用中多用动态扫描。

硬件译码是指利用硬件芯片来实现自动译码,这里我们用到一块译码芯片CD4511,它的功能是将二进制码译成十进制数字符的器件。CD4511是一个BCD码七段译码器,并兼有驱动功能,内部没有限流电阻,与数码管相连接时,需要在每段输出接上限流电阻,引脚排列如图9-7所示。

图9-7 CD4511的引脚图及功能方框图

表9-1是CD4511功能表,CD4511只能对0~9的数字译码,超出范围将无显示。

表9-1 CD4511功能表

使用硬件译码的典型电路原理图如图9-8所示,硬件译码时的显示程序相当简单,只需要在单片机的相应的端口给出要显示的数值的BCD码就可以了。如下的程序可以实现在数码管上显示数8:

MOV P1,#8

图9-8 硬件译码典型电路图

接下来我们为大家讲解用软件译码的LED数码管的驱动以及编程。这种方式下,LED数码管的显示电路不再需要硬件译码器,它可以由74LS244、74LS245等驱动器接数码管构成,其典型的应用电路如图9-9所示。

图9-9 软件译码典型电路图

LED上显示某个数值的程序如下:

_LedDisp:

  MOV A,R7

  MOV DPTR,#LED_DISP_NUM

  MOVC A,@A+DPTR

  MOV P0,A

  RET

例5:利用P1.0控制数码管的位选,P0口控制数码管的显示内容,采用共阴极数码管,编写程序,控制数码管显示 “H” 字符。

解:程序设计如下:

例6:在例5基础上编写程序,使数码管轮流显示“0”,“1”,“2”,……,“E”,“F”,这16个字符。

解:程序设计如下:

9.6 数码管扫描程序设计

在前面的章节,读者学习了数码管的显示功能,知道了数码管进行数据显示的原理和方法,但通常情况下数码管显示的数据不止一位,是由多个独立的数码管组合成的数码显示器,数码管的个数越多,显示的数据量就越大,当然,控制难度也加大。本节中,大家将学到多个数码管的连接原理和显示的方法。重点学习数码管的扫描方法。

9.6.1 数码管的硬件结构

每个数码管都包含有8根数据线,分别对应数码管上的8个发光二极管,同时,发光二极管还包含一个公共线路,该引脚用于对数码管进行选择,通常情况下,数码管的数据线连接在一起,这样,就可以减少I/O端口使用的数量。

如图9-10所示的电路中,每个数码管模块包含4个独立的8段数码管,每个数码管都有一个公共端,即CS1、CS2、CS3、CS4,每个数码管模块引出了一组数据线,A、B、C、D、E、F、G、H,所有8段数码管的数据线采用并联的方式进行连接,于是,利用P0端口的8位I/O端口连接数码管的数据端,利用P1端口的8位I/O端口分别连接数码管的公共端。由于数码管的数据线是并联的,所以,每增加一个数码管,将增加一位数码管位选信号。

图9-10 数码管与单片机的硬件连接图

9.6.2 数码管的软件扫描

正如前面所讲的一样,数码管的所有数据线都并联在一起,当在P0口输出信号时,所有数码管的数据信号都相同,此时,位选信号决定数码管的显示与不显示,如果位选信号将所有数码管都选中,那么所有数码管显示的内容必定相同。而在实际应用中,每个数码管显示的内容不可能完全相同,为实现这样的目的,就要对数码管进行扫描。所谓扫描,就是通过软件控制P1端口,即数码管的位选信号,依次仅选择一个数码管,同时在P0口输出对应的显示段码,持续一段时间后再选择下一个数码管,并再在P0口输出对应的显示段码,再这样周而复始。于是,8个数码管被依次点亮,并显示本位的显示内容,当每个数码管被点亮的时间比较短时,由于视觉的“暂留”现象,我们就可以看到8个数码管稳定地显示一序列数据。通常情况下,对数码管的扫描频率达到50Hz的时候,显示效果最佳。

例7:利用定时器1采用中断的方式进行扫描,每中断一次选择一个数码管,并在P0口送出显示代码,并观察数码管的显示效果。调节中断一次的时间,使数码管上显示的数据比较稳定。

解:程序设计如下:

例8:上例实现的动态扫描程序为基础,在数码管上循环显示0~255之间的数字。

解:程序设计如下。

9.7 秒表程序设计

秒表的逻辑结构比较简单,它主要由数码显示器、十进制计数器、报警器、六进制计数器和按键组成。通常,秒表有六个输出显示,分别为百分之一秒、十分之一秒、秒、十秒、分、十分,所以共有6个计数值与之对应,稍微粗略一点的秒表可能只能精确到十分之一秒,精度高一点的秒表可以做到千分之一秒甚至更高。

9.7.1 秒表系统硬件结构分析

由于单片机内部自带定时器,因此在时间的计算上就显得非常简单,AT89S52内部自带3个定时器/计数器,定时器2通常用作串口的波特率产生。定时器0和定时器1可工作在8位、13位和16位模式,因此,通常可以采用定时器0或定时器1作为定时计算。

秒表的显示部分采用8段数码管进行显示,需要显示的位数有十分、分、十秒、秒、十分之一秒和百分之一秒,共需要六个八段数码管进行显示,有启动、停止、清零三个按键,如果有需要,还需要蜂鸣器进行蜂鸣提示。电路如图9-11所示。

图9-11 秒表结构原理框图

9.7.2 定时器计数值的计算

定时器的计数的基准时钟频率来源于外部时钟源Fosc,将Fosc通过12分频后提供给定时器模块,因此,定时器模块的计数频率为Ftimer=Fosc/12或Fpclk/6,由于秒表的计数精度在0.01秒,因此,定时器内的数据寄存器的计数次数为:

Tcount=Ftimer×0.01

Tcount=(Fosc×0.01)/12

如果系统时钟源频率为22.1184MHz,计算出定时器需要计数的次数Tcount=18 432次。因此,当定时器工作在16位定时/计数器模式时,我们对定时器的初值应设置为:

TH TL = 65 536-18 432=47 104

TH TL=0B800H

即设置初值时设定TH=0B8H,TL=00H。如果要考虑秒表工作的精确性,还应该考虑程序执行过程中进入中断时所调用的指令条数,包括入栈和初栈、中断返回等。

例9:设计一个秒表计数的功能,系统启动后立即从0.00秒开始计算,并显示在数码管上,并测试秒表计数值的准确性(可以统计几分钟,测试偏差度)。

解:程序设计如下:

9.8 键盘接口

单片机键盘接口方式有三种。

1.独立接口方式

独立式是指将每个独立按键以一对一的方式直接接到I/O口的输入线上,如图9-12(a)所示。读键值时直接读I/O口,每一个键的状态通过读入键值的一位(二进制)来反映,所以这种方式称为一维直读方式,按习惯称为独立式。这种方式的好处就是软件编写简单,缺点是占用的I/O线较多,一般只在按键数量较少的时候采用。

2.硬件编码方式

硬件编码方式是指先将独立式的键信号通过硬件编码,再由I/O线读入,如图9-12(b)所示。这种方式克服了独立式占用接口多的缺点,但是需要增加硬件编码电路。

3.行列方式

行列方式是用n条I/O线组成行输入口,m条I/O线组成列输出口,在行列线的每一个交点上,设置一个按键,如图9-12(c)所示。读键值方法一般采用扫描方式,即输出口按位轮换输出低电平,再从输入口读入键信息,最后通过软件方法获得键码。这种方式占用的I/O线较少,因此,在单片机应用系统中最为常用。

4.二维直读方式

二维直读方式的键盘排布采用了双行列方式,读入键值采用了直读式,每一个键的状态通过读入键值的二位(二进制位)来反映,所以这种方式成为二维直读式,如图9-12(d)所示。这种方式具有独立式和行列方式的优点,缺点是要求安装含有两个触点。在部分触摸键盘成品中,采用了这种方式。

图9-12 键盘接口方式

5.交互方式

如图9-12(e)所示的键盘是一种交互式键盘接口。这种方式中,N位I/O线既作输入行又作输出列,输入输出交互使用,构成N行N列,在行列线每一个独立(不重复)的交点上,设置一个键,即任意I/O线之间均接一个按键。这种接线方式在键数相同的情况下,占用I/O线比行列式要少。键盘读键方式与行列方式相似,所不同的是在某一端口线输出为低电平时,其他I/O线均读入键信息。但这种方式要求I/O线必须是可位控的两向或准两向口,如8031的P1口;一般I/O线则不能使用,如8255I/O接口。该方式不能用中断方式读键。

9.8.1 几种键盘接口方式的最大容量

表9-2给出了最大键位容量与I/O位数的关系。

表9-2 最大键位容量与I/O位数的关系

表9-3列出了1~10条I/O线各种接口方式的最大键位容量。

表9-3 I/O线各种接口方式的最大键位容量

9.8.2 单片机行列键盘的设计

由于单片机I/O端口数量较多,在进行键盘的设计时,往往直接利用I/O端口与按键构成扫描矩阵,通过软件扫描的方式获取按键值。其硬件连接如图9-13所示。

图9-13 行键盘的设计硬件连接图

为了确定键盘中有无按键按下以及是哪一个键被按下,通常可以采用扫描的方式。有两种扫描方式可以选择,一种是逐行扫描,也是使用最普遍的方法,结合图9-13,P2_0、P2_1、P2_2、P2_3作为输出信号,并依次输出低电平信号,P2_4、P2_5、P2_6、P2_7作为输入信号,如果没有按键按下,将检测到P2_4、P2_5、P2_6、P2_7全为高电平,如果此时P2_1输出低电平信号,P2_6检测到低电平信号,那么由此可以判断S6被按下。另一种扫描方式称为全扫描,其扫描的方式为先将P2_0、P_1、P2_2、P2_3全部置低,如果没有按键按下,则P2_4、P2_5、P2_6、P2_7引脚上全为高电平,如果检测到P2_6引脚为低电平时,则可以判断第三列有按键按下,但具体是哪个按键按下还不能清楚,于是,又将P2_4、P2_5、P2_6、P2_7转为输出,P2_0、P2_1、P2_2、P2_3作为输入,检测哪个按键为低电平,于是就可以判断出具体的某行有按键按下,行列组合就可以判断具体某个按键按下。

例10:利用一位数码管进行按键值的显示,利用定时器0进行延迟程序的设计,采用逐行扫描的方式实现按键的扫描程序的设计。

解:程序设计如下:

9.9 看门狗程序设计

在由单片机构成的微型计算机系统中,单片机的工作常常会受到来自外界电磁场的干扰,从而造成程序的“跑飞”,陷入死循环,程序的正常运行就会被打断。当单片机控制的系统无法继续工作时,整个系统会陷入停滞状态,从而发生不可预料的后果。所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的芯片,俗称“看门狗”,看门狗又叫做watchdog timer(WDT)。

9.9.1 单片机看门狗的特点

看门狗电路的应用,使单片机可以在无人状态下实现连续工作。其工作原理是:看门狗芯片和单片机的一个I/O引脚相连,该I/O引脚通过程序控制定时地往看门狗送入高电平(或低电平),这一程序语句是分散地放在单片机其他控制语句中的,一旦单片机由于干扰造成程序跑飞,就会陷入某一程序段,进入死循环状态,从而写看门狗引脚的程序便不能被执行。这个时候,看门狗电路会由于得不到单片机送来的信号,在它和单片机复位引脚相连的引脚上送出一个复位信号,使单片机发生复位,即程序从程序存储器的起始位置开始执行,这样便实现了单片机的自动复位。

常见的专用看门狗芯片有MAX813、5045、IMP813等。他们的价格为4~10元不等,在实际应用中可以根据需要选择。

9.9.2 单片机看门狗的原理

MCS-51系列有专门的看门狗定时器,对系统频率进行分频计数,定时器溢出时,将引起复位。看门狗可以由编程设定其溢出速率,也可单独用来作为定时器使用。C8051Fxxx单片机内部也有一个21位的使用系统时钟的定时器,该定时器检测对其控制寄存器的两次特定写操作的时间间隔。如果这个时间间隔超过了编程的极限值,将产生一个WDT复位。

在AT89S52中,看门狗定时器复位寄存器(WDTRST)的地址为0A6H,在程序中若要使用看门狗,可以用下面的指令初始化看门狗定时器:

MOV 0A6H,#01E MOV 0A6H,#0E1H

看门狗一旦初始化之后,就要在看门狗复位系统之前向0A6H地址再次写入01EH、0E1H序列,这个过程就叫做喂狗。

使用51内部的看门狗时,有下面几个问题是需要注意的:

(1)AT89S52的看门狗必须由程序激活后才开始工作。所以必须保证CPU有可靠的上电复位。否则看门狗也无法工作。

(2)看门狗使用的是CPU的晶振。在晶振停振的时候看门狗也无效。

(3)AT89S52只有14位计数器。在16 383个机器周期内必须至少喂狗一次。而且这个时间是固定的,无法更改。当晶振为12MHz时每16ms需喂狗一次。

软件看门狗技术的原理和这差不多,只不过是用软件的方法实现,我们还是以51系列来讲,我们知道在51单片机中有两个定时器,我们就可以用这两个定时器来对主程序的运行进行监控。我们可以对T0设定一定的定时时间,当产生定时中断的时候对一个变量进行赋值,而这个变量在主程序运行的开始已经有了一个初值,在这里我们设定的定时值要小于主程序的运行时间,这样在主程序的尾部对变量的值进行判断,如果值发生了预期的变化,就说明T0中断正常,如果没有发生变化则使程序复位。对于T1我们用来监控主程序的运行,我们给T1设定一定的定时时间,在主程序中对其进行复位,如果不能在一定的时间里对其进行复位,T1的定时中断就会使单片机复位。在这里T1的定时时间要设得大于主程序的运行时间,给主程序留有一定的余量。而T1的中断正常与否我们再由T0定时中断子程序来监视。这样就构成了一个循环,T0监视T1,T1监视主程序,主程序又来监视T0,从而保证系统的稳定运行。

9.9.3 看门狗使用注意

大多数51系列单片机都有看门狗,当看门狗没有被定时清零时,将引起复位。这可防止程序跑飞。设计者必须清楚看门狗的溢出时间以决定在合适的时候清零看门狗。清零看门狗也不能太过频繁,否则会造成资源浪费。程序正常运行时,软件每隔一定的时间(小于定时器的溢出周期)给定时器置数,即可预防溢出中断而引起的误复位。

9.9.4 看门狗使用方法

看门狗可恢复系统的正常运行以及有效的监视管理器(具有锁定光驱,锁定任何指定程序的作用,可用在家庭中防止小孩无节制地玩游戏、上网、看录像)等,具有很好的应用价值。系统软件“看门狗”的设计思路如下:

(1)看门狗定时器T0的设置。在初始化程序块中设置T0的工作方式,并开启中断和计数功能。系统Fosc=12MHz,T0为16位计数器,最大计数值为216-1=65 535,T0输入计数频率是:Fosc/12,溢出周期为(65 535+1)/ 1=65 536(µs)。

(2)计算主控程序循环一次的耗时。考虑系统各功能模块及其循环次数,本系统主控制程序的运行时间约为16.6ms。系统设置“看门狗”定时器T0定时30ms(T0的初值为65 536-30 000=35 536)。主控程序的每次循环都将刷新T0的初值。如程序进入“死循环”而T0的初值在30ms内未被刷新,这时“看门狗”定时器T0将溢出并申请中断。

(3)设计T0溢出所对应的中断服务程序。此子程序只需一条指令,即在T0对应的中断向量地址(000BH)写入“无条件转移”命令,把计算机拖回整个程序的第一行,对单片机重新进行初始化并获得正确的执行顺序。

【本章小结】

在学习了单片机的基本原理和知识后,本章重点学习单片机的汇编程序设计。在单片机应用过程中,汇编语言具有重要的应用价值。汇编语言最接近机器语言,具有设计灵活,指令执行效率高,占用空间较小的有点。本章的学习要求能够熟练在Keil 平台上编写简单的汇编语言程序,并能够查看存储器、I/O接口、寄存器等的数据变化,深刻理解单片机的基本原理。

9.10 习题

1. 设振荡频率为12MHz,请设计一软件延时程序,延时时间为1ms。

2. 如何判断读取的数据到底是在片内FLASH中还是外扩存储器中?

3. LED的静态显示方式与动态显示方式有何区别?各有什么优缺点?

4. 单片机键盘接口方式有哪几种?

5. 单片机看门狗的原理是什么?

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈