51单片机应用开发从入门到精通

978-7-115-34003-0
作者: 张华杰
译者:
编辑: 傅道坤
分类: 8051

图书目录:

详情

本书首先介绍单片机开发环境的构建方法、应用程序开发流程、指令系统和单片机C语言基础等单片机入门知识, 然后详细讲解实际开发中常用的单片机汇编语言开发实例和单片机C语言开发实例。通过这些实例的学习,读者可以迅速上手单片机的开发工作。

图书摘要

“十二五“国家重点图书出版规划项目

51单片机应用开发从入门到精通
张华杰 编著
人民邮电出版社

北京

前言

随着微处理器技术和SoC技术的发展,从较早的4位机到8位机,一直发展到现在的32位机,单片机的应用逐渐渗透到生产、生活的各个领域。本书主要针对MCS-51体系单片机,从硬件、软件两个方面的基础知识开始进行介绍。MCS-51 体系单片机由于具有简单易学、稳定性好、成本低等优点,在工业控制、家用电器、汽车电子乃至航天电子等许多领域都有着重要的应用。

本书主要针对初、中级读者群,通过众多的实例介绍单片机开发的基础知识、基本工具、基本过程及简单应用,既方便初学者入门,也对中级读者的开发进阶有很大帮助。希望读者在阅读本书的时候,能够按照书中的讲解自己动手进行实例开发,只有学以致用才能真正掌握书中的内容。

本书共分7章,各部分的具体内容介绍如下。

第1章主要介绍了单片机开发的基础知识,通过概述性的介绍,讲解了单片机开发的过程、所用的工具、最小系统以及相关的名词,方便初学者对单片机的开发有个整体印象,以便进一步的学习。

第2章、第3章介绍了单片机开发的基础实例。这两章精选20多个最基础且常用的实例,介绍单片机开发的基本过程,以及单片机与输入输出设备之间的连接。初学者通过对这些实例的学习,能基本掌握单片机的工作方式和基本的设计过程,具备初步的单片机使用能力。

第4章~第6章在前两章的基础上,介绍了如何通过单片机进行数据采集、数据通信、数据存储。这是单片机开发的进阶实例,通过这十几个实例的学习,使读者掌握单片机通过外部设备的配合完成复杂的功能的技术。

第7章详细的介绍了一个综合实例(简易GPS系统),通过这一实例的学习,使读者初步具备开发较复杂的系统的能力。

本书大量使用实例并带有详细的分析解释,特别适合初学者进行单片机技术的学习。需要说明的是,实践是学习好单片机技术的最佳方式,所以建议读者在阅读本书的基础上,根据本书提供的实例进行一定的硬件设计,并在开发板上进行相应的编程操作。这样能够使您对单片机技术的学习事半功倍。

编者

2013年12月

第1章 单片机开发预备知识

单片机的应用十分广泛,从工业控制到家用电器,许多地方都有单片机的身影。掌握了单片机开发技术,不仅能够根据自己的需要进行电子设计,实现自己的一些想法,而且也为进一步学习嵌入式技术打下一定的基础。

本章将对单片机应用系统的软、硬件设计以及开发和调试等各个方面加以介绍,以便读者能通过对本章的学习较快地完成单片机应用系统的研制工作。

1.1 单片机开发流程

单片机的开发主要分为硬件开发和软件开发,在系统设计上又有最小系统和扩展系统两种。下面将通过系统硬件设计、软件设计进行开发流程的介绍,并以最小系统及一些典型的扩展系统为例进行简单的说明。

1.1.1 单片机应用系统设计步骤

单片机应用系统是指以单片机为核心,配以一定的外围电路和软件,能实现某种或几种功能的应用系统。它由硬件部分和软件部分组成。一般来说,应用系统所要完成的任务不同,相应的硬件配置和软件配置也就不同。因此,单片机应用系统的设计应包括硬件设计和软件设计两大部分。为保证系统能可靠工作,在软、硬件的设计中,还要考虑其抗干扰能力。

在应用系统的设计中,软件、硬件和抗干扰设计是紧密相关、不可分离的。在有些情况下硬件的任务可由软件来完成(如某些滤波、校准功能等);而在另一些要求系统实时性强、响应速度快的场合,则往往用硬件代替软件来完成某些功能。设计者应根据实际情况,合理地安排软、硬件的比例,选取最佳的设计方案,使系统具有最佳的性能价格比。

设计一个单片机测控系统,一般可分为4个步骤。

● 需求分析、方案论证和总体设计阶段

需求分析、方案论证是单片机测控系统设计工作的开始,也是工作的基础。只有经过深入细致的需求分析和周密而科学的方案论证才能使系统设计工作顺利完成。

需求分析的内容主要包括:被测控参数的形式(电量、非电量、模拟量、数字量等)、被测控参数的范围、性能指标、系统功能、工作环境、显示、报警、打印要求等。

方案论证是根据用户要求设计出符合现场条件的软硬件方案,在选择测量结果输出方式上,既要满足用户要求,又要使系统简单、经济、可靠,这是进行方案论证与总体设计一贯坚持的原则。

● 器件选择、电路设计制作、数据处理、软件编写阶段

器件选择和电路设计主要根据方案中所确定的指标进行,同时需要考虑器件的电气特性。

数据处理、软件编写是单片机系统实现功能最重要的步骤。首先,设计数据处理的算法,如果是简单的控制可以跳过这一步。然后,设计程序的结构,得到流程图。最后进行程序的编写工作。

● 整个系统的设计与性能测定阶段

编制好的程序或焊接好的线路,不能按预计的那样正确工作是常有的事,这就需要查错和调试。查错和调试有时是很费时间的。

调试时,应将硬件和软件分成几部分,逐个部分调试,各部分都调试通过后再进行联调。调试完成后,应在实验室模拟现场条件,对所设计的硬件、软件进行性能测定。

● 文件编制阶段

文件不仅是设计工作的结果,而且是以后使用、维修以及进一步再设计的依据。因此,一定要精心编写,描述清楚,使数据及资料齐全。

文件应包括:任务描述;设计的指导思想及设计方案论证;性能测定及现场试用报告与说明;使用指南;软件资料(流程图、子程序使用说明、地址分配、程序清单);硬件资料(电原理图、元件布置图及接线图、接插件引脚图、线路板图、注意事项)。

一个项目定下来后,经过详细调研、方案论证后,就进入正式研制阶段。从总体上来看,设计任务可以分为硬件设计和软件设计,这两者互相结合,不可分离。从时间上来看,硬件设计的绝大部分工作量是在最初阶段,到后期往往还要作一些修改。软件设计任务贯彻始终,到中后期基本上都是软件设计任务。

1.1.2 系统硬件设计

单片机应用系统的硬件设计包括3大部分内容:单片机系统的扩展部分设计、各功能模块的设计和工艺设计。

1.单片机系统的扩展部分设计

系统扩展设计包括存储器扩展和接口扩展。存储器的扩展指 EPROM、EEPROM 和RAM的扩展,接口扩展是指8255、8155、8279以及其他功能器件的扩展。它们都属于单片机系统扩展的内容。

2.各功能模块的设计

如信号测量功能模块、信号控制功能模块、人机对话功能模块、通信功能模块等,根据系统功能要求配置相应的A/D、D/A、键盘、显示器、打印机等外围设备。

为使硬件设计尽可能合理,应重点考虑以下几点:

(1)尽可能采用功能强的芯片,以简化电路。

(2)留有余地。在设计硬件电路时,要考虑到将来修改、扩展方便。

(3)ROM空间。目前EPROM容量越来越大,一般选用2764以上的EPROM,它们都是28脚,要升级很方便。

(4)RAM空间。8031内部RAM不多,当要增强软件数据处理功能时,往往觉得不足。这就要求系统配置外部RAM,如6264、62256等。

(5)I/O端口。在样机研制出来后进行现场试用时,往往会发现一些被忽视的问题,而这些问题是不能单靠软件措施来解决的。如有些新的信号需要采集,就必须增加输入检测端;有些物理量需要控制,就必须增加输出端。如果硬件设计之初就多设计出一些I/O端口,这问题就会迎刃而解。

(6)A/D和D/A通道。和I/O端口同样的原因,留出一些A/D和D/A通道将来可能会解决大问题。

3.工艺设计

工艺设计包括机箱、面板、配线、接插件等。设计时必须考虑到安装、调试、维修的方便。另外,硬件抗干扰措施也必须在硬件设计时一并考虑进去。

1.1.3 系统软件设计

在进行应用系统的总体设计时,软件设计和硬件设计应综合考虑,同时进行。当系统的电路设计定型后,软件的任务也就明确了。

系统中的应用软件是根据系统功能要求设计的。一般地讲,软件的功能可分为两大类:一类是执行软件,它能完成各种实质性的功能,如测量、计算、显示、打印、输出控制等;另一类是监控软件,它专门用来协调各执行模块和操作者的关系,在系统软件中充当组织调度角色。

设计人员在进行程序设计时应从以下几个方面加以考虑:

(1)根据软件功能要求,将系统软件分成若干个相对独立的部分。设计出合理的软件总体结构,使其清晰、简捷,流程合理。

(2)各功能程序实行模块化、子程序化。既便于调试、链接,又便于移植、修改。

(3)在编写应用软件之前,应绘制出程序流程图。这不仅是程序设计的一个重要组成部分,而且是决定成败的关键部分。从某种意义上讲,多花些时间来设计程序流程图,就可以节约几倍源程序编辑调试时间。

(4)要合理分配系统资源,包括 ROM、RAM、定时器/计数器、中断源等。其中最关键的是片内RAM分配。对8031来讲,片内RAM指00H~7FH单元,这128个字节的功能不完全相同,分配时应充分发挥其特长,做到物尽其用。例如在工作寄存器的 8 个单元中,R0和R1具有指针功能,是编程的重要角色,避免作为他用;20H~2FH这16个字节具有位寻址功能,用来存放各种标志位、逻辑变量、状态变量等;设置堆栈区时应事先估算出子程序和中断嵌套的级数及程序中栈操作指令使用情况,其大小应留有余量。若系统中扩展了RAM存储器,应把使用频率最高的数据缓冲器安排在片内RAM中,以提高处理速度。当RAM资源规划好后,应列出一张RAM资源详细分配表,以备编程查用方便。

(5)注意在程序的有关位置处写上功能注释,提高程序的可读性。

1.1.4 最小应用系统举例

单片机的最小应用系统是单片机最简单的应用,这样的系统虽然不能完成实际的任务,但是已经能够让单片机正常地工作起来。只需要进一步加入外设就能完成一定的任务。下面将以89C51和8031两种单片机为例进行最小系统的介绍。

1.89C51最小应用系统

89C51 内部有 4KB 闪存,芯片本身就是一个最小系统。在能满足系统的性能要求的情况下,可优先考虑采用此种方案。用这种芯片构成的最小系统简单、可靠。用 89C5l单片机构成最小应用系统时,只要将单片机接时钟电路和复位电路即可,如图1-1所示。由于集成度的限制,最小应用系统只能用作一些小型的测控单元。

2.8031最小应用系统

8031无片内程序存储器,因此,其最小应用系统必须在片外扩展EPROM。图1-2所示为外接程序存储器的最小应用系统。

片外EPROM或RAM的地址线由P0口(低8位地址线)和P2口(高8位地址线)组成。地址锁存器的锁存信号为ALE。

程序存储器的取指令控制信号为PSEN。当程序存储器只有一片时,可将其片选端直接接地。数据存储器的读/写控制信号为RD、WR,其片选线与译码器输出端相连。

8031芯片必须EA直接接地,其他与89C51最小应用系统一样,也必须有复位及时钟电路。

图1-1 AT89C51 最小系统
图1-2 8031 最小系统

1.1.5 典型应用系统

在实际应用中,单片机需要在最小应用系统的基础上增加一定的外部设备。外部设备的管理主要通过外部地址进行,这里将介绍地址空间的分配以及总线的驱动。

1.地址空间分配

对于RAM和I/O容量较大的应用系统,主要考虑如何把64KB程序存储器和64KB数据存储器的空间分配给各个芯片,主要有两种方法:线选法和译码器法。

线选法的优点是硬件电路结构简单,但由于所用片选线都是高位地址线,它们的权值较大,地址空间没有充分利用,芯片之间的地址不连续。当芯片所需的片选信号多于可利用的地址线时,常采取全地址译码法。它将低位地址线作为芯片的片内地址(取外部电路中最大的地址线位数),用译码器对高位地址线进行译码,译出的信号作为片选线。一般采用74LS138作地址译码器。图1-3所示是一个全地址译码的系统实例。图中各器件芯片所对应的地址如表1-1所示。

图1-3 8031 全地址译码器实例
表1-1 各扩展芯片地址

因为6264是8K字节RAM,所以需要13根低位地址线(A12~A0)进行片内寻址,其他三根高位地址线A15~A13经3-8译码后作为外围芯片的片选线。图中尚剩余三根地址选择线,可供扩展三片8K字节RAM芯片或三个外围接口电路芯片。

2.总线的驱动

在应用系统中,所有系统扩展的外围芯片都通过总线驱动,外围芯片工作时有一个输入电流,不工作时也有漏电流存在,因此总线只能带动一定数量的电路。MCS-51系列单片机作为数据总线和低8位地址总线的P0口可驱动8个LSTFL电路,而其他口只能驱动4个13TFL电路。当应用系统规模过大时,可能造成负载过重,致使驱动能力不够,系统不能可靠地工作。

(1)总线的驱动扩展

多芯片应用系统,首先要估计总线的负载情况,以确定是否需要对总线的驱动能力进行扩展。图1-4为MCS-51单片机总线驱动扩展原理图。

图1-4 MCS-51 单片机总线驱动原理图

地址总线和控制总线的驱动器为单向驱动器,并具有三态输出功能。驱动器有一个控制端,以控制驱动器开通或处于高阻状态。通常,在单片应用系统中没有DMA功能时,地址总线及控制总线可一直处于开通状态,这时控制端接地即可。

常用的单向总线驱动器为74LS244。图1-5为74LS244引脚和逻辑图。8个三态线驱动器分成两组,分别由来控制。

图1-5 74LS244 引脚和逻辑图

数据总线的驱动器应为双向驱动、三态输出,并由两个控制端来控制数据传送方向。如图1-4所示,数据输出允许控制端DBEO有效时,数据总线输入高阻状态,输出为开通状态;数据输入允许控制端DBEI有效时则状态与以上相反。

常用的双向驱动器为74LS245,图1-6为其引脚和逻辑图,16个三态门中每两个三态门组成一路双向驱动。驱动方向由、DIR 两个控制端控制,控制端控制驱动器有效或高阻态,在控制端有效(=0)时,DIR 控制端控制驱动器的驱动方向,DIR =0 时驱动方向为从 B 至 A,DIR =1 则相反。

图1-6 74LS245 引脚和逻辑图

MCS-51 单片机应用系统总线驱动扩展电路中,P0 口的双向驱动采用双向驱动器741S245;P2口的单向驱动器采用74LS244。

对于 P0 口的双线驱动器 74LS245,使接地保证芯片一直处于工作状态,而输入/输出的方向控制由单片机的数据存储器的“读”控制引脚()和程序存储器的取指控制引脚()通与门控制DIR引脚实现。这种连接方法保证无论是“读”数据存储器中数据(有效)还是从程序存储器中取指令(有效)时,都能保证 P0 口的输入驱动;除此以外的时间里(均无效)保证P0的输出驱动。

对于P2口,因为只作地址输出口,所以74LS244的驱动门控制端接地。

上面介绍了如何在总线上扩展驱动器,下面简单介绍一下如何来估算驱动器的驱动能力。

总线驱动器驱动能力是以驱动同类门个数度量的。驱动器驱动能力和驱动器负载性质有关。由于驱动器负载有交流和直流之分,因此总线驱动器驱动能力估算时应同时考虑交流和直流负载两方面的影响。

(1)直流负载下驱动能力的估算

在直流负载下,驱动器驱动能力主要取决于高电平输出时驱动器能提供的最大电流和低电平输出时驱动器所能吸收的最大电流,如图 1-7 所示。图中,IOH为驱动器在高电平输出时的最大输出电流,IIH为每个同类负载所吸收的电流。IOL为驱动器在低电平输出时的最大吸入电流,IIL为驱动器需要为每个同类门提供的吸入电流。显然,下面两个式子满足时才能使驱动器可靠工作。

若设:IOH=15mA,IOL=24mA,IIH=0.1mA和IIL=0.2mA,

则根据上述二式求得 N1=150 和 N2=120。因此,驱动器的实际驱动能力应为 120 个同类门。

(2)交流负载下驱动能力的估算

总线上传送的数据是脉冲型信号,在同类门负载为容性(分布电容造成)时就必须考虑电容的影响。驱动器驱动容性负载时的关系如图1-8所示。若设:CP为驱动器的最大驱动电容,Ci(i =1, 2, …, N)为每个同类门的分布电容。为了满足同类门电容的交流效应,驱动器负载电路应满足如下关系:

若取:CP=15μF,Ci不大于 0.3μF,则根据上式可求得 N3=50。

综上所述,驱动器驱动负载门的能力应从交流和直流负载两方面加以考虑。通常,对于TTL负载,主要应考虑直流负载特性,因为TTL电流大,分布电容小;对于MOS型负载,主要考虑交流特性,因为MOS型负载的输入电流很小,分布电容是不容忽视的。

图1-7 驱动器驱动直流负载示意图
图1-8 负载门的交流效应

例如,74LS245驱动器常可驱动100多个74LS系列门电路,若把驱动负载的种种因素也考虑在内,起码也能可靠驱动50个同类门。但为了保险起见,74LS245输出线上一般也能挂接30个左右同类门。因此,驱动器不仅可以减轻主机负担,增强单片机驱动负载的能力,为负载电阻和分布电容提供较大的驱动电流,而且也能够消除驱动器后面负载电路对主机芯片的干扰和影响,较好地保证总线上信号波形的完整性。

(3)总线的负载平衡和上拉电阻的配置

在进行单片机应用系统设计时,都是将I/O芯片挂在相关总线上。进行设计时往往注意负载的数量,不使总线过载,但往往忽视总线负载的平衡问题。

所谓总线负载的平衡,主要发生在数据总线DB上。一般来说,I/O部件的数据都以D0为起点往DB上挂,但由于各种接口部件的数据宽度不一致,就极易造成DB的负载失衡。图1-9所示为有2个8位数据的部件、2个4位数据的部件及1个l位数据的接口。按照图 1-9 的接线,DB各位的负载显然是不平衡的,D4~D7只挂了2个负载,而D0连接的负载数达5个之多。

图1-9 DB 总线失调

当总的负载较轻时,这种失衡不会引起太大的问题。但若负载接近总线的驱动能力,就有可能影响总线信号的逻辑电平。以图1-9为例,负载不同的各位数据线上,高低电平的数值有明显差异。如高电平有的达4.3V,而有的只有3.7V。图1-9中I/O5的信号位传送很不可靠,常常发生错误,与负载失衡有密切关系。若将I/O4接数据位D7~D4,将I/O5接数据位D7,这样就能改善总线的不平衡程度,提高系统的可靠性。

除了配置总线驱动器,注意总线负载平衡配置之外,在总线上适当安装上拉电阻也可以提高总线信号传输的可靠性,加上拉电阻给总线带来的好处有以下几点。

① 提高信号电平

提高集成电路输入信号的噪声容限,是提高抗干扰能力的一个重要措施。提高信号的高电平可以提高噪声容限,其方法之一是提高芯片的电源电压,方法之二是在总线输出口配置上拉电阻。以8031单片机P0口数据为例,当不加上拉电阻时,P0.0口输出电流为I,端口的高电平为µ=VCC-IR。当加入上拉电阻后,P0.0口输出电流变为I1,由于负载恒定,则I1<I,因此端口电平µ=VCC-I1R,µ将要有所提高。

② 提高总线的抗电磁干扰能力

当总线处于高阻状态时是处于悬高状态,比较容易接受外界的电磁干扰。

例如,当程序存储器的地址空间小于 64K 字节时,由于受到外界干扰而引起程序乱飞,当乱飞空间超出系统程序存储器的地址空间时,程序存储器全部关断,致使数据总线处于高阻状态。外界的电磁干扰信号就很容易通过数据总线进入 CPU,引入虚假的程序指令,对程序运行造成更加严重的破坏。若数据总线上配有上拉电阻,总线具有稳定的高电平,这时的指令仅是“FFH”,相当于“MOV R7,A”指令,这比总线上出现的随机指令所造成的后果要好得多。

③ 抑制静电干扰

当总线的负载是CMOS芯片时,由于CMOS芯片的输入阻抗很高,容易积累静电电荷而形成静电放电干扰,严重时会损坏芯片。若在总线上配置上拉电阻,则降低了芯片的输入阻抗,为静电感应电荷提供泄荷通路,提高了芯片使用的可靠性。

④ 有助于削弱反射波干扰

由于总线负载的输入阻抗往往很高,对于变化速度很快的传输信息,当传输线较长时容易引起反射波干扰。若在总线的终端配置上拉电阻,降低了负载的输入阻抗,可有效抑制反射波干扰。

图1-10 数据总线配置上拉电阻

数据总线配置上拉电阻如图1-10所示。上拉电阻一般取2~10kΩ,典型值为10kΩ。实际应用中负载电阻可选用电阻排,其引脚间距与集成芯片标准间距一致,应用起来十分方便。

1.1.6 数据采集系统

在任何单片机测控制系统中,都是从尽量快速、准确、完整地获得数字形式的数据开始的,因此,数据采集系统作为沟通模拟域与数字域的必不可少的桥梁起着非常重要的作用。

数据采集系统一般由信号调理电路、多路切换电路、采样保持电路、A/D、CPU、RAM、EPROM组成。其原理框图如图1-11所示。

图1-11 数据采集系统原理图

1.信号调理电路

信号调理电路是传感器与 A/D 之间的桥梁,也是测控系统中重要组成部分。信号调理的主要功能是:

(1)目前标准化:正业仪表通常采用0~10mA和4~20mA的信号,为了和A/D的输入形式相适应,经I/V变换成电压信号。

(2)某些测量信号可能是非电压量,如热电阻等,这些非电压量信号必须变为电压信号,还有些信号是弱电压信号,如热电偶信号,必须放大滤波,这些处理包括信号形式的变换、量程调整、环境补偿、线性化等。

(3)某些恶劣条件下,共模电压干扰很强,如共模电压高达 220V,不采用隔离的办法无法完成数据采集任务,因此,必须根据现场环境,考虑共模干扰的抑制,甚至采用隔离措施,包括地线隔离、路间隔离等。

综上所述,非电量的转换、信号形式的变换、放大、滤波、共模抑制及隔离等,都是信号调理的主要功能。

信号调理电路包括电桥、放大、滤波、隔离等电路。根据不同的调理对象,采用不同的电路。电桥电路的典型应用之一就是热阻测温。用热电阻测温时,工业设备距离计算机较远,引线将很长,这就容易引进干扰,并在热电阻的电桥中产生长引线误差。解决办法有:采用热电阻温度变换器;智能传感器加通讯方式连接;采用三线制连接方法。

信号放大电路通常由运放承担,运放的选择主要考虑精度要求(失调及失调温漂)、速度要求(带宽和上升率)、幅度要求(工作电压范围及增益)及共模抑制要求。常用于前置放大器的有µA741、LF347(低精度),OP-07、OP-27(中等精度)及 ICL7650(高精度)等。

滤波和限幅电路通常采用二极管、稳压管、电容等器件。用二极管和稳压管的限幅方法会产生一定的非线性且灵敏度下降,这可以通过后级增益调整和非线性校正补偿。此外,由于限幅值比最大输入值高,当使用多路开关时,某一路超限可能影响其他路,需选用优质的多路模拟开关,如AD7501。

共模电压的存在对模拟信号的处理有影响,高的共模电压会击穿器件,即使没有损坏器件,也会影响测量精度。隔离是克服共模干扰影响的有效措施。常用的隔离方法有光电隔离或采用隔离放大器等。

2.多路切换电路

通常被检测的物理量有多个,如果每一通道都设有放大、采样保持(S/H)和 ADC几个环节就很不经济,而且电路也复杂。采用模拟多路开关就可以使多个通道共用一个放大器、S/H 和 ADC,采用时间分割法使几个模拟通道轮流接通,这样既经济,又使电路简单。模拟多路开关的选择主要考虑导通电阻的要求、截止电阻的要求和速度要求,常用的模拟多路开关有 CD4501、CD4066、AD7501、AD7507 等。为了降低截止通道的负载影响,提高开关速度,降低通道串扰,采用多级模拟多路开关来完成通道切换。

3.采样保持电路(S/H)

采样保持电路是为了保证模拟信号高精度转换为数字信号的电路。在模拟数字变换电路中,如果变换期间输入电压是变化的,那么就可能产生错误的数字信号输出。采样保持电路就是将快速变化的模拟信号进行“采样”与“保持”,使模拟信号在ADC转换过程中保持不变。采样保持器的选择要综合考虑捕获时间、孔隙时间、保持时间、下降率等参数。常用的采样保持器有AD582、AD583、13398等。

4.模—数转换(ADC)

ADC 是计算机同外界交换信息所必需的接口器件,因为它能将描述自然现象和生产过程的模拟量转换成便于计算机存储和处理的数字量。因此,从某种意义上说,没有 ADC 的广泛应用,就没有计算机应用技术的发展。选择 ADC 需主要考虑的指标有分辨率、转换时间、精度、电源、输入电压范围、工作环境、数字输出特性、价格等。

5.基本的单片机系统

基本的单片机系统除了采用前面介绍的外,电可采用片内带有多路转换开关、采样保持电路、ADC的高集成度的单片机。例如可采用Philips公司的8XC552型,也可采用Intel公司的MCS-96单片机(16位)。由于数据采集系统许多单元电路都集成在单片机内,这给硬件设计工作带来了极大的方便。

1.2 学习单片机的必备工具

多动手做实验能够帮助初学者很快理解单片机的原理和开发技巧。单片机开发之前都要创建一个开发环境。所以,学习前应准备好实验所需的器材和单片机开发的相关软件。

1.2.1 计算机

单片机开发对计算机的要求不高,只要能正常运行Windows操作系统的计算机即可。

1.2.2 单片机集成开发系统软件

单片机集成开发系统软件,是指用来在计算机上编写、汇编和仿真、调试单片机程序的软件。

目前用来开发单片机的应用软件比较多,如Keil 公司的 Keil C51,是比较好的 51 单片机集成开发系统软件。单片机的程序开发流程如图 1-12所示。

图1-12 单片机的程序开发流程

单片机的程序开发流程是:编写程序→编译→连接→(软件仿真)→烧写→测试。

(1)用编辑软件编写程序,注意,使用汇编语言编写的程序文件名后缀(即扩展名)是.ASM,编写的程序称为源程序。例如,汇编语言源程序Test.ASM,其中,Test是文件名(可任意),.ASM是扩展名(必须用)。

(2)将源程序用编译软件进行编译,生成扩展名为.OBJ的文件(如Test.OBJ)。

(3)用连接软件进行连接,生成扩展名为.HEX的文件(如 Test. HEX)。

(4)通过编程器将扩展名为.HEX的可执行文件烧写到单片机内。在写入单片机之前还可以进行软件仿真,即在软件上模拟单片机程序运行情况,以便进行调试和修改。

上述4步工作既可以通过4个分立工具软件来完成,也可以用一个单片机集成开发系统软件来完成。所谓集成,就是指将源程序编写、编译、连接、调试等开发单片机所要用到的工具集成到一个软件中。Keil C51 功能强大,使用广泛,并支持 80C51 的 C 语言编程,是单片机初学者的理想选择。

1.2.3 51 编程器

51编程器是用来将编好的程序烧写到51单片机内的一个设备。

用集成开发系统软件(如 Keil C51)编写并生成单片机目标代码后,需要用编程器将目标代码(即扩展名为.HEX的可执行文件)烧写到单片机中。编程器是一个设备,上面有单片机插座及与计算机的连线等,一般需要购买。

编程器按功能可分单一型和万能型。单一型编程器只能对单一系列的某些型号的单片机芯片进行写入操作;万能型编程器能对多种系列的多种型号的单片机芯片进行写入操作。前者结构简单、价格便宜,很适合初学者使用;后者功能强大,但价格较贵。

编程器按与计算机的连接方式可分为串口编程器和并口编程器两种。串口编程器通过连线接在计算机的串行端口,即通信端口上;并口编程器通过连线接在计算机的并行端口,即打印机端口上。

购买时一般选择串口编程器,串口编程器还可以很方便地进行通信程序实验。

1.2.4 实验板

实验板实际上是一个小的单片机实验系统。

写入程序的单片机需要装到实验板上运行后才能验证编写的程序正确与否。实验板上带有单片机插座、发光二极管、数码管、蜂鸣器等器件。实验板可以自制,也可以购买。初学者最好先买一块装好的实验板,待学习深入后,再进行性能扩展或自己组装。

一般销售编程器的厂家也都销售实验板,有的是把实验板部分与编程器部分合装在一起,有的是分立的。

1.3 Keil uVision2 集成开发环境

uVision2 IDE 是 Keil software 公司的产品,它集项目管理、编译工具、代码编写工具、代码调试以及完全仿真于一体,特别适用于个人开发或者人数少、对开发过程的管理还不成熟的开发团体。这一功能强大的软件提供简单易用的开发平台,可以让开发者在开发过程中集中精力于项目本身,加快开发速度。

1.3.1 uVision2 介绍

uVision2 的界面窗口如图 1-13 所示,该软件提供了丰富的工具,常用命令都具有快捷工具栏。除了代码窗口外,软件还具有多种观察窗口,这些窗口使开发者在调试过程中随时掌握代码所实现的功能。屏幕界面和VC类似,提供菜单命令栏、快捷工具栏、项目窗口、代码窗口、目标文件窗口、存储器窗口、输出窗口、信息窗口和大量的对话框,在uVision中可以打开多个项目文件进行编辑。本节主要介绍uVision2的使用方法,其安装方法等不做介绍。

菜单项包括 File/Edit/View/Project/Debug/Flash/Peripherals/Tools/SVSC/Windows/Help共11个选项,提供了文本操作、项目管理、开发工具配置、仿真等功能,下面将详细介绍这些项。

图1-13 Keil uVision 开发环境界面

1.3.2 uVision2 开发流程

uVision2本身自带项目管理器,这对于开发者来说非常方便。用户只需遵循下面的步骤就可以很方便地建立自己的项目。

● 开启 uVision2,建立工程文件并且从器件数据库里挑选出项目实际使用的器件。

● 建立一个新的源文件,并且把这个源文件添加到工程中去。

● 为单片机添加并且设置启动代码。

● 设置硬件相关的选项。

● 编译整个工程并且生成下载到存储器用的 HEX 文件。

下面,将逐条介绍建立一个简单工程的各个步骤,使用户有一个更加清楚的认识。

(1)建立新工程文件

首先建立一个新的工程文件,步骤如下。

● 菜单 Project/New Project,输入工程名称并且保存文件。

● 在弹出的对话框选择单片机的型号,如图 1-14 所示。

图1-14 选择单片机型号

● 选择文件之后,出现对话框询问是否自动加入 8051 启动文件,单击“是”,该文件用于初始化单片机内部存储器等,添加完成之后在项目管理窗口中可以看到startup.A51文件已经被加入,如图1-15所示。

(2)建立和加入文件

在工程文件内就可以建立自己的项目的文件了,然后将其加入,步骤如下:

● 打开菜单 File/New,建立一个 txt 文件。

图1-15 加入启动文件
图1-16 保存文件

● 保存该文件,注意文件名称必须带上后缀名.c,否则会保存为一个.txt 文件,如图1-16所示。

● 右键单击项目管理窗口的 Source Group1,在菜单中选择“Add Files to SourceGroup1”,在出现的对话框选择hello.c文件,如图1-17所示。

图1-17 加入.c 文件

● 此时可以在项目管理窗口里面看到 hello.c 文件已经被加入,如果需要加入.h 文件则在重复建立文件操作之后再次加入文件,需要注意的是新建文件保存的时 候也需要带上.h 扩展名,并且在选择加入文件的时候选择 Text File,如图 1-18所示。

图1-18 加入.h 文件

在完成以上步骤之后程序员就可以进行程序的编写了,如果需要加入更多的.c或者.h文件可以重复以上操作。

(3)项目配置管理

在程序编写完成之后要对项目进行配置,使得编译器能按照当前项目的情况对代码进行编译,步骤如下:

● 菜单 Project/Targets,Groups,Files,选择使用的项目目标平台,一般来说,使用默认的设置就好,直接确定,如图1-19所示。

● 在设置完目标之后可以在 Project 菜单下看到 Option for Target“Target 1”,选择后出现当前项目的配置选项,如图1-20所示。图中所示有很多选项,在这个过程中程序员只需要将 output 选项单中的 Create Hex File 选中即可,这是为了能生成供单片机运行的HEX文件。

● 选择 Project/Built Target 对项目进行编译并且生成对应的 HEX 文件。如果是修改之后的编译,选择 Rebuilt all Target Files 即可,如图 1-21 所示。

(4)出错处理

以上是建立一个项目并且编译生成对应文件的过程,如果在编译中出现错误则会在output窗中看到对应的出错信息,如图1-22所示。

图1-19 配置编译目标
图1-20 配置 Target
图1-21 生成 HEX 文件

双击output窗口中对应的错误信息,则在编辑窗口光标会跳到出错的对应语句,并且在左边出现一个蓝色箭头,方便程序员修改。

图1-22 出错信息

1.4 编程器的安装与使用

除了上一小节uVision2自带的编程器,也可以采用硬件编程器将单片机集成开发系统上生成的HEX文件使用此编程器写入到单片机内,不过需要安装编程器。编程器的安装非常简单,主要有两个步骤:一是将编程器连接到计算机上;二是在计算机上安装编程器程序软件。

下面以一款单一型串口方式连接的AT51S编程器为例介绍编程器安装过程,如图1-23所示。

图1-23 AT51S 编程器

1.4.1 编程器的连接

编程器采用串口与计算机连接,端口自动识别,无需设置,安装十分简便。先将配套的串口电缆一端与编程器的9芯座连接,另一端接到计算机的COM1口或COM2口上,电缆两端插头相同,不需区分。

再将配套的直流电源调到 12V 挡,将插头插到编程器电源座上,编程器指示灯大约每隔一秒闪烁一次,表示编程器工作正常,等待服务软件的指令。若接上电源后编程器指示灯常亮或常灭,则说明编程器工作异常,需要将电源断开数秒后重新连接。

将需要写入程序的单片机插入编程器IC锁紧座内,单片机1脚朝向锁紧座手柄的方向。请注意,不是所有编程器都是将单片机1脚朝向锁紧座手柄的方向,要依据说明书中的说明进行操作。

1.4.2 安装编程器软件

当完成编程器与计算机的连接,并且编程器处在正常的工作状态时,就可以安装编程器软件,编程器程序软件可以工作在Windows95/98/XP/2000操作系统上。

打开存有编程器软件的目录,双击安装文件 Setup.exe,当出现对话框时,只要不断地单击“下一步”按钮就可完成自动安装,并自动完成端口的识别。图 1-24 所示为编程器软件的窗口。

图1-24 编程器软件窗口

1.4.3 编程器的使用

接通编程器电源,然后双击编程器软件启动图标,在出现编程器工作窗口的同时完成编程器软件与编程器的连接。如果连接失败,可根据故障原因提示进行检查,排除后再重试。当连接成功后可按下面步骤把编好的程序写入单片机。

(1)在编程器上插入单片机

将单片机插入编程器插座并锁紧,注意使单片机1脚朝向锁紧座手柄的方向。

(2)选择单片机型号

单击编程器软件工作窗口上方“器件”按钮,在出现的下拉菜单中选择相应的单片机型号。

(3)读出与擦除

对于已经写过数据的单片机,应先使用软件的擦除功能对单片机进行擦除,擦除后从单片机读出的数据是全 FF,也可以在擦除前先将数据读出并将数据保存到磁盘中,然后再进行擦除。

(4)打开HEX文件

单击软件窗口左上方“文件”菜单选项,选择“打开HEX文件”选项,在存放HEX文件的目录里找到需要写入的文件,单击该文件名后软件工作窗口中就会出现十六进制的HEX文件。

(5)写入文件

单击“写入”按钮,被选中的文件就会被写入到单片机内。

1.5 测试方法和工具

完成一个用户样机,首先要完成硬件组装工作,然后进入软件设计、调试和硬件调试阶段。硬件组装就是在设计、制作完毕的印制板上焊好元件与插座,然后就可用仿真开发工具进行软件设计、调试和硬件调试工作。

1.用户样机软件的设计、调试

用户样机软件设计、调试的过程如图1-25所示,可为以下几个步骤。

第一步,建立用户源程序。用户通过开发系统的键盘、CRT 显示器及开发系统的编辑软件,按照所要求的格式、语法规定,把源程序输入到开发系统中,并存在磁盘上。

第二步,在开发系统机上,利用汇编软件对第一步输入的用户源程序进行汇编,变为可执行的目标代码。在汇编过程中,如果用户源程序有语法错误,则在CRT上显示出来,然后返回到第一步进行修改,再进行汇编,直至语法错误全部纠正为止。如无语法错误,则进入下一个步骤。

第三步,动态在线调试。这一步对用户源程序进行调试。上述的第一步、第二步是一个纯粹的软件运行过程,而在这一步,必须要有在线仿真器配合,才能对用户源程序进行调试。用户程序中分为与用户样机硬件无联系的程序以及与及样机硬件紧密关联的程序。

图1-25 用户样机软件设计、调试原理图

对于与用户样机硬件无联系的用户程序,例如计算机程序,虽然已经没有语法错误,但可能有逻辑错误,使计算结果不正确,这样必须借助于动态在线调试手段,如单步运行、设置断点等,发现逻辑错误,然后返回到第一步修改,直至逻辑错误纠正为止。

对于与用户样机硬件紧密相关的用户程序,如接口驱动程序,一定要先把硬件故障排除以后,再与硬件配合,对用户程序进行动态在线调试,如果有逻辑错误,则返回到第一步进行修改,直至逻辑错误消除为止。在调试这一类程序时,硬件调试与软件调试是不能完全分开的。许多硬件错误是通过对软件的调试而发现并纠正的。

第四步,将调试完毕的用户程序通过EPROM编程器(也称EPROM写入器)固化在EPROM中。

2.用户样机硬件调试

对用户样机进行调试,首先要进行静态调试,静态调试的目的是排除明显的硬件故障。

(1)静态调试

静态调试工作分为两步。

第一步是在样机加电之前,先用万用表等工具,根据硬件逻辑设计图,仔细检查样机线路是否连接正确,并核对元器件的型号、规格和安装是否符合要求,应特别注意电源系统的检查,以防止电源的短路和极性错误,并重点检查系统总线(地址总线、数据总线和控制总线)是否存在相互之间短路或与其他信号线的短路。

第二步是加电后检查各插件上引脚的电位,仔细测量各点电位是否正常,尤其应注意8031插座的各点电位,若有高压,与仿真器联机调试时将会损坏仿真器的器件。

具体步骤如下:

① 电源检查。

当用户样机板连接或焊接完成之后,先不插主要元器件,通上电源。通常用 + 5V直流电源(这是TTL电源),用万用表电压挡测试各元器件插座上相应电源引脚电压数值是否正确,极性是否符合。如有错误,要及时检查、排除,使每个电源引脚的数值都符合要求。

② 各元器件电源检查。

断开电源,按正确的元器件方向插上元器件。最好是分别插入,分别通电,并逐一检查每个元器件上的电源是否正确,以至最后全部插上元器件,通上电源后,每个元器件上电源正确无误。

③ 检查相应芯片的逻辑关系。

检查相应芯片逻辑关系通常采用静态电平检查法。即在一个芯片信号输入端加入一个相应电平,检查输出电平是否正确。单片机系统大都是数字逻辑电路,使用电平检查法可首先检查出逻辑设计是否正确。选用的元器件是否符合要求,逻辑关系是否匹配,元器件连接关系是否符合要求等。

(2)联机仿真、在线动态调试

在静态调试中,对目标样机硬件进行初步调试,只是排除了一些明显的静态故障。

用户样机中的硬件故障(如各个部件内部存在的故障和部件之间连接的逻辑错误)主要是靠联机仿真来排除的。

在断电情况下,除8031外,插上所有的元器件,并把仿真器的仿真插头插入样机上8031 的插座,然后与开发系统的仿真器连接,分别打开样机和仿真器电源后,便可开始联机仿真调试。

前面已经讲到,硬件调试和软件调试是不能完全分开的,许多硬件错误是在软件调试中发现和被纠正的。所以说,在上面介绍的软件设计过程中的第三步动态在线调试中,也包括联机仿真、硬件在线动态调试以及硬件故障的排除。

开发系统的仿真器是一个与被开发的用户样机具有相同单片机芯片的系统,它是借助开发系统的资源来模拟用户样机的单片机,对用户样机系统的资源(如存储器、I/O接口)进行管理。同时仿真开发机还具有跟踪功能,它可将程序执行过程中的有关数据和状态在屏幕上显示出来,这给查找错误和调试程序带来了方便。同时,其程序运行的断点功能、单步功能可直接发现硬件和软件的问题。

下面介绍在仿真开发机上如何利用简单调试程序检查用户样机电路。

利用仿真开发机对用户样机的硬件检查,常常按其功能及I/O通道分别编写相应简短的实验程序,来检查各部分功能及逻辑是否正确,下面作以简单介绍。

① 检查各地址译码输出

通常,地址译码输出是一个低电平有效信号。因此在选到某一个芯片时(无论是内存还是外设)其片选信号用示波器检查应该是一个负脉冲信号。由于使用的时钟频率不同,其负脉冲的宽度和频率也有所不同。注意在使用示波器测量用户板的某些信号时,要将示波器电源插头上的地线断开,这是由于示波器测量探头一端连到外壳,在有些电源系统中,保护地和电源地连在一起,有时会将电源插座插反,将交流220V直接引到测量端而将用户样机板全部烧毁,并且会殃及开发机。

下面来讨论如何检查地址译码器的输出,例如,一片 6116 存储芯片地址为 2000~27FFH,则可在开发机上执行如下程序:

程序执行后,就应该从6116存储器芯片的片选端看到等间隔的一串负脉冲,就说明该芯片片选信号连接是正确的,即使不插入该存储器芯片,只测量插座相应片选引脚也会有上述结果。

用同样的方法,可将各内存及外设接口芯片的片选信号都逐一进行检查。如出现不正确现象,就要检查片选线连线是否正确,有无接触不良或错线、断线的现象。

② 检查 RAM 存储器

检查RAM存储器可编一程序,将RAM存储器进行写入,再读出,将写入和读出的数据进行比较,如发现错误则立即停止。将存储器芯片插上,执行如下程序:

如一片RAM芯片的每个单元都出现问题,则有可能某些控制信号连接不正确,如一片RAM芯片中一个或几个单元出现问题,则有可能这一芯片本身是不好的,可换一片再测试一下。

③ 检查 I/O 扩展接口

对可编程接口芯片(如 8155、8255),要首先对该接口芯片进行初始化,再对其 I/O端口进行I/O操作。初始化要按系统设计要求进行,这个初始化程序调试好后就可作为正式编程的相应内容。程序初始化后,就可对其端口进行读写。对开关量I/O来讲,在用户样机板可利用钮子开关和发光二极管进行模拟,也可直接接上驱动板进行检查。一般情况下,用户样机板先调试,驱动板单独进行调试,这样排除故障更方便些。

如用自动程序检查端口状态不易观察时,就可用开发系统的单步功能单步执行程序,检查内部寄存器的有关内容或外部相应信号的状态,以确定开关量输入输出通道连接是否正确。

若外设端口连接一片 8255,端口地址为 B000~B003H,A 口为方式 0 输入,B 口、C口都为方式0输出,则可用下述程序进行检查:

如使用同步I/O口,例如常用的锁存器和缓冲器,可直接对端口进行读写,不存在初始化过程。

通过上面介绍的开发系统调试用户样机的过程,可以体会到离开了开发系统就根本不可能进行用户样机的调试,而调试的关键步骤——动态在线仿真调试,又完全依赖于开发系统中的在线仿真器。所以说开发系统性能的优劣,主要取决于在线仿真器性能的优劣,在线仿真器所能提供仿真开发的手段,直接影响设计者的设计、调试工作的效率。所以,它对于一个设计者来说,在了解了目前的开发系统的种类和性能之后,选择一个性能/价格比高的开发系统,并能够熟练的使用它调试用户样机是十分重要的。

第2章 51单片机基础实例

本章主要介绍以下几个单片机的基础实例程序:

● 简单点亮发光二极管实例

● 跑马灯实例

● 流水灯实例

● 查 0~9 平方表实例

● 受控输出实例

● 比较输入数大小实例

● 交通灯控制器实例

● 蜂鸣器发音实例

● 单片机演奏音乐实例

● 软件陷阱实例

2.1 简单点亮发光二极管实例

单片机最简单的实例之一就是点亮发光二极管(LED)闪烁。本节通过点亮1只发光二极管的实例来介绍单片机的I/O口操作。

本节首先介绍单片机I/O口结构的基础知识,接着介绍实例的硬件电路,然后给出了实例程序,最后将分析操作单片机的I/O口的技巧与注意点。

2.1.1 基础知识

8051单片机有4个并行I/O口:P0~P3。这4个I/O口中只有P0口是真正的双向口,P1~P3是准双向口,P3口还具有第二功能,为系统提供一些控制信号。4个并行I/O口的结构如表2-1所示。由I/O口结构可知,P0口的输出三态门由两只场效应管(FET)组成,所以是一个真正的双向口,同时可以提供较强的输出高电平(拉电流)和低电平(灌电流)驱动能力;P1~P3 口中,上拉电阻代替了场效应管,输出缓冲器不是三态的,因此不是真正的双向口,只能称为准双向口,同时这些I/O口的输出高电平驱动能力较弱,只能提供较强的输出低电平驱动能力。因此,在使用I/O口直接驱动LED等电流负载时,需要使用输出低电平驱动的接法。

表2-1 q并行I/O口的结构
续表

2.1.2 硬件电路图

本实例中,硬件电路主要包括8051单片机最小系统电路(包括5V电源,正极接40引脚,负极(地)接20引脚;振荡电路,使用晶体振荡器,接18、19脚;复位电路;EA引脚接到正电源端)和发光二极管LED电路(由1只LED和1只限流电阻串联而成),具体的连接原理电路图如图2-1所示。当P1.0引脚输出低电平时,点亮LED;当P1.0引脚输出高电平时,LED熄灭。

图2-1 点亮发光二极管原理电路图

2.1.3 软件程序设计

要实现LED的闪烁效果需要通过程序控制单片机连接到LED的I/O引脚,点亮LED,延时一段时间后,再控制I/O引脚使LED熄灭,如此循环就可以实现LED的闪烁。下面,着重介绍延时程序,并给出完整的软件程序实例。

1.延时程序

首先,通过下面的代码演示单片机如何通过程序实现延时一段时间。

程序(1)为数据传送指令,该指令的作用为将数据10H传送到寄存器R7中。8051单片机执行程序(1)~程序(3)后,寄存器R5、R6、R7的值分别为7FH、7FH和10H。程序(4)为减1不为0转移指令,该指令的作用为将寄存器R5中的数据减1,如果结果不为0,则程序跳转。其中“$”符号表示该指令的地址,所以若减1不为0则程序发生跳转,再次执行程序(4)。同理,程序(5)为将寄存器R6中的数据减1,如果结果不为0,则程序跳转到标号“DELAY1”处继续执行。程序(7)为子程序返回指令,执行该指令使程序返回调用子程序时的断点,继续执行断点之后的程序。

以上代码实例使用了数据传送指令和控制转移指令,8051 单片机的数据传送指令及控制转移指令如表2-2、表2-3所示。表2-2、表2-3中的其他一些数据传送指令及控制转移指令将在后面的实例中陆续详细介绍。

表2-2 数据传送指令
续表
表2-3 控制转移指令

以上代码实例是一个完整的子程序。所谓子程序,就是完成确定任务,并能被其他程序反复调用的程序代码段,子程序在结构上具有通用性和独立性。子程序的编写必须注意以下几点:

● 子程序调用使用 LCALL 指令。

● 子程序必须通过 RET 指令返回。

● 子程序第一条必须有标号,以实现 LCALL 指令的调用。

● 正确传递入口参数和出口参数。

● 子程序需要注意现场的保护和恢复。

● 如果需要子程序嵌套,应注意嵌套深度与堆栈区大小的问题。

该延时子程序,可以实现约0.5秒的延时,下面看看如何来计算延时子程序的延时时间。8051单片机的一个机器周期包括12个时钟周期,本实例中单片机晶振为12MHz,则它的1个时钟周期是1/12微秒,1个机器周期是1微秒。DJNZ指令是双周期指令,执行1次要2个微秒,延时子程序一共执行258064次DJNZ指令,一共为516128微秒,约为0.5秒。

2.程序全貌

本实例的软件程序全貌如下:

程序(1)为伪指令,伪指令是在汇编程序中起某种控制作用的特殊命令,其格式与通常的操作指令一样,并可加在汇编程序的任何地方,但它们并不产生实际的机器指令。程序(1)的作用是定义常数,也就是说,在该伪指令以下程序中出现的LED_PIN都是指代P1.0。8051单片机编程中常用的伪指令如表2-4所示。

表2-4 常用伪指令

8051单片机在上电复位后程序计数器PC中的值为0000H,也就是说程序是从0000H单元开始执行的。通过伪指令 ORG 0000H 指定程序从 0000H 单元开始存放,并在该单元写入跳转指令 LJMP MAIN,使程序跳转到 MAIN 处开始执行。“MAIN:”称为标号,它代表了当前行的程序存储器地址。

程序(2)为给堆栈指针赋初值指令。所谓堆栈是一个RAM区域,堆栈存放和取用数据的方式比较特殊:先进后出,后进先出。堆栈的数据传输指令有两条,即PUSH和POP 指令,堆栈的操作还涉及一个寄存器:堆栈指针 SP。每当执一次 PUSH 指令时,SP的值就自动加1,然后数据被放到SP所指示的RAM单元中。每当执行一次POP指令,数据从SP所指示的RAM单元中取出,然后SP的值自动减1。由于上电复位后,SP的初始值为07H,这样就使堆栈数据从08H单元开始往后存放,而08H到1FH这个区域正是 8051 单片机的第二、三、四工作寄存器区,经常要被使用,这会造成数据的混乱。所以使用 MOV SP,#60H 指令设置堆栈指针,把堆栈设置在从 60H 开始的 RAM单元中。

程序(3)、程序(5)为位操作指令,8051单片机的位操作指令见表2-5。

表2-5 位操作指令

通过直接寻址位取反指令CPL,程序(3)~(7)也可写作如下:

3.运行结果

本实例的运行结果为LED不停闪烁。

2.1.4 技巧总结

本实例从单片机点亮1只发光二极管出发,演示8051单片机是如何操作I/O口的。在单片机I/O口操作中应注意以下的技巧。

● 硬件连接过程中应注意各 I/O 口的内部结构,选择合适的引脚及连接方式。一般来说,对于I/O口驱动电流负载的情况,应采用输出低电平驱动电路的形式。对于许多新型的51内核的单片机,引脚提供了输出20mA电流的LED驱动能力,输出高电平时也可以直接驱动LED。

● 操作 I/O 口的指令主要是 SETB bit、CLR bit、CPL bit。

2.2 跑马灯实例

在上一节点亮1只LED的基础上,让我们来看看如何控制多只LED实现跑马灯效果。

2.2.1 硬件电路图

本实例中,硬件电路和上一节的硬件电路相似,为了实现跑马灯,本实例电路连接了8只LED,原理电路图如图2-2所示。

图2-2 跑马灯原理电路图

2.2.2 软件程序设计

控制多只LED实现跑马灯效果可以有多种实现方式,下面给出其中两种,请对比这两种实现方式的优劣。

1.程序全貌

首先,使用上节学习的直接寻址位置 1 指令 SETB bit 和直接寻址位清 0 指令 CLR bit控制P1口实现跑马灯效果,以下为程序实例。

本实例也可使用循环左移指令和循环右移指令来实现跑马灯效果,循环左移指令和循环右移指令的功能见表2-6。

表2-6 循环左移指令和循环右移指令

(1)~(26)程序替换为如下程序,运行结果不变。

2.运行结果

该实例的运行结果为8只LED循环轮流点亮。

2.2.3 技巧总结

本实例技巧总结如下:

● 同样的运行结果,使用合适的指令可以大大减少代码量,节省程序空间。

● 循环移位指令的应用——乘除法运算:左移 1 次 =无符号数乘 2 运算,右移 1次 =有符号数除 2 运算。

2.3 流水灯实例

本节基于上一节的硬件电路,首先介绍关于寻址方式的基础知识,然后给出了运用查表法实现的流水灯实例程序。

2.3.1 基础知识

寻址方式是指寻找、确定参与操作的数据地址的方式,8051 单片机的寻址方式包括寄存器寻址、直接寻址、寄存器间接寻址、立即寻址以及基址寄存器加变址寄存器间接寻址,这5种寻址方式的功能见表2-7。下面将详细介绍基址寄存器加变址寄存器间接寻址的寻址方式。

表2-7 5种寻址方式的功能

基址寄存器加变址寄存器间接寻址方式一般用于访问程序存储器中的数据表格,这种寻址方式,是以DPTR或PC作基址寄存器,以累加器A作变址寄存器,并以两者内容相加形成的16位地址作为操作数的地址,以达到访问数据表格的目的。若累加器A中原有内容为05H,程序存储器0405H单元的内容为0C4H,执行如下指令后,程序存储器0405H单元的内容将被传送到累加器A,即执行后累加器A中的内容为0C4H。

2.3.2 软件程序设计

本实例直接给出完整的流水灯实例,并解释程序实例中新出现的指令。

1.程序全貌

以下为流水灯实例的软件程序。

程序(1)为带进位的减法指令,该指令从累加器A中减去源操作数内容和进位标志,结果存入A。如果位7有借位,则PSW寄存器的Cy位置1,否则清0;如果位3有借位,则PSW寄存器的Ac位置1,否则清0;如果位6有借位而位7没有借位,或者位7有借位而位6没有借位,则PSW寄存器的溢出标志位OV置1,否则溢出标志位清0。

2.运行结果

程序运行后,将依次循环出现8只LED依次逐个点亮、依次逐个叠加、依次逐个递减、从两边靠拢后分开、从两边叠加后递减的流水灯效果。

2.3.3 技巧总结

通过本实例可以看到:

● 查表指令可用于复杂代码转换显示,通过查表指令实现复杂的显示效果,并可以减少程序代码。

2.4 延时时间计算实例

在单片机的实时控制系统中,常常需要用到延时操作,所以,延时子程序往往是编写单片机程序中不可缺少的一部分。延时方法有硬件延时和软件延时,硬件延时将在后面有关章节中介绍,本节将介绍软件延时方法。

所谓软件延时,就是让计算机重复执行一些无具体任务的程序,利用执行程序的时间来达到延时的目的。

2.4.1 机器周期和指令周期

单片机读、写操作都需要消耗一定的时间,机器周期是指单片机完成一个基本操作所用的时间,如读操作、写操作等。当石英晶体为12MHz时,1个机器周期为1μs。

指令周期是指单片机执行一条指令所需要的时间,一个指令周期通常含有1~4个机器周期,其中常用的DJNZ指令周期为两个机器周期,即执行DJNZ指令需要2μs;MOV指令周期为一个机器周期,即执行MOV指令需要1μs。

2.4.2 单重循环短暂延时

短暂的时间延时可采取简单的单重循环结构来实现,例如,下面程序为延时 540μs的短暂延时子程序,程序中采取了单重循环。

540μs延时子程序:

此子程序中,由第 3、4 行代码构成单重循环结构,其中,DJNZ 指令为控制转移指令,该指令每执行一次,寄存器R值减1,只要R值减1后不为0,就会转移到第3行标号DE1处去执行。

每循环一次需要的时间为3μs,由于R值为180,所以要循环180次,循环花费的时间为540μs。该延时子程序总的延时时间还要包括执行MOV指令时间1μs和执行RET返回指令时间2μs,但由于这个时间比循环时间要短很多,所以,延时时间的长短主要是由循环次数来控制。

2.4.3 多重循环较长时间延时

如果需要较长时间的延时,则需采用多重循环结构。例如下面程序为1s延时子程序,程序中采取了多重循环。

1s延时子程序:

此子程序采用了3重循环结构,先运行第5行代码操作,每次减1,减到0为止;再运行第6行代码对R6进行减操作,每次减1,减1后不为0,则转移到标号DLY2处运行,此时将R7赋值为100,并再对R7进行减法内循环。

当R6减为0时,程序运行到第7行,开始外循环,R5减1不为0时转移到标号DLY1处运行。为了计算上的简便,可以忽略赋值语句的时间,只计算“DJNZ R7, $”语句的执行时间,该指令执行一次需2μs,执行的次数为R5、R6和R7值的乘积,即:

总延时 =2μs×R7×R6×R5

=2μs×100×100×50 =1 000 000μs

=1s

2.4.4 延时程序改进

从上节程序可以看出,只要改变寄存器R5、R6和R7的值,就可以获得不同延时时间。在上例中假设 R6和 R7值不变,只改变 R5值(R5取值范围为 1~255),可获得不同的延时。由于忽略了赋值语句的执行时间,实际延时时间要比计算的时间略大一些。

为了使一个延时子程序能产生不同的延时,可以将2.4.3节中的程序改进为以下的形式。其中R7、R6值不变,通过改进R5值,即改变外循环次数来改变延时时间,延时子程序的延时时间为0.02s×R5。

使用时,在调用DELAY延时子程序之前,要根据对延时时间的要求,先对R5赋值,假如延时为0.5s,需将R5值赋值为25;延时为1s,需将R5值为50,R5最大值为255,这是因为R0~R7都是8位寄存器,最大存放数据为二进制数11111111,即255,在使用时注意不要超出其有效范围。改进后的延时程序使用形式如下:

延时时间为0.02s×R5的延时程序如下:

2.5 用取表方式实现灯移动

功能说明:单片机端口接8只LED,编程时利用取表的方法,使端口P1的LED先一次点亮1只跳跃左移,点亮顺序为P1.0、P1.2、P1.4、P1.6;接着一次点亮2只,从左向右移动2次;一次点亮3只,左右移3次;然后4只灯P1.0、P1.2、P1.4、P1.6与P1.1、P1.3、P1.5、P1.7交互点亮4次;最后8只灯闪烁6次,不断循环。

2.5.1 程序设计

1.流程图

程序设计流程如图2-3所示。

图2-3 程序流程

2.程序

汇编语言编写的用取表方式实现灯移动源程序代码如下:

2.5.2 代码详解

1.标号说明

START:起始程序的进入点。

LOOP:循环操作的进入点。

LOOP1:输出循环的进入点。

DELAY:延时子程序的进入点。

DEL1:延时重复递减判断的进入点1。

DEL2:延时重复递减判断的进入点2。

TABLE:代码表的进入点。

2.寄存器使用分配情况

程序中 A、P、R5、R6、R7、DPTR 为寄存器,其中 A、P、R5、R6、R7 寄存器的作用与前节相同。

DPTR是新用到的寄存器,也是唯一的16位专用寄存器,它是由两个8位寄存器DPH和DPL拼装而成,其中DPH为DPTR的高8位,DPL为DPTR的低8位。它既可作为一个16位寄存器来使用,也可以作为两个独立的8位寄存器(DPH和DPL)来使用。

我们把代码表存入DPTR,确切地说是将代码表的首地址TABLE送到DPTR中作基地址,A 作为变址寄存器,基址寄存器和变址寄存器的内容相加(@A + DPTR)形成 16位地址,该地址既是操作数的地址,也是存入地址指针(也叫数据指针)。知道了地址指针,按照地址指针所指,自然就能取到代码表中的控制码了。

3.程序分析解释

本程序利用取表方式实现亮灯的移动。

利用取表的方式将控制码建成一个表,利用“MOV DPTR, #TABLE”语句存表,再利用“MOVC A, @A + DPTR”语句从表中读取出控制码,这种方法可方便地处理一些复杂的控制动作,下面重点介绍这一部分。

第01~09行代码的作用是存表、读取表并输出,是程序的主要部分。

01:存入TABLE表,用来控制灯的一组组十六进制数控制码建成一个表,取表名为TABLE。将TABLE表存放的地址存入特殊功能寄存器DPTR中,以便查找。

所谓存表是指存入表的首地址,即表的地址指针或数据指针。地址指针指向第1行第1位控制码的位置。总之,地址指针指向哪个位置就到哪个位置读取控制码。

02:将寄存器A中的内容清除为0。

03:查表读取控制码。“MOVC A, @A + DPTR”是一条很重要的查表语句,它将A + DPTR 指定的表地址的数据送入寄存器 A 中。当程序第一次运行时,地址指针指向的位置是表中首位放置的控制码0FEH,将0FEH取出并存入寄存器A中。

04:判断读取出的控制码是否为结束码 01H,如果不是则跳转到 LOOP1 处。CJNE指令的功能是比较两个操作数,若不相等就跳转,只有相等时才继续向下运行。01H是结束标志码,放在表中的最后位置。

如果寄存器A中的数不是01H,即不相等,说明表还没有查完,程序跳转到LOOP1处继续查表读取出控制码并输出。

若寄存器 A 中的数是 01H,即两个操作数相等了,说明表已经查完一遍,程序执行下一条语句。

05:程序跳转到标号START处,即跳转到程序开始处,重新设定显示值。

06:将取来的控制码送入P1并输出。

07:调用0.5s延时子程序。

08:数据指针加1,指向下一个操作码。INC指令的功能是使寄存器A中的数据加1,即地址指针向下移动一个位置,指向第二个控制码,为下一次取码做准备。

09:跳转到LOOP处,重新开始,不断循环。

10~16:0.5s延时子程序。

第18~32行代码为控制码编制表,表名为TABLE。

18:单灯跳跃左移控制码。

20~23:两只灯从左向右移动控制码。

25:3只灯左右移控制码。

27:4只灯交互点亮控制码。

亮灯的移动是由控制码控制的,控制码采用十六进制数,如果将十六进制码换成二进制码就可以清楚地看出灯是怎样被控制的。

29~31:闪烁6次控制码。控制码00H表示8个灯都亮(0表示亮);控制码0FFH表示8个灯都灭(1表示灭)。一亮一灭形成一次闪烁,共6对控制码,闪烁6次。

32:结束控制码。结束码可以是任意的数,存放的位置要在表的最后,标志控制码已经结束。此时,由于第04行语句的作用,当寄存器A中的数是01H,即两个操作数相等,程序将执行下一条语句,即跳转到标号LOOP处,程序重新开始。

如果没有结束码,当取表码结束后,程序运行下一行END指令,使整个程序结束。

此外,制表时要注意,每一行控制码的前边都要加伪指令DB,DB是定义字节的指令。

33:程序结束。

4.边用边学指令

本节使用新的指令有:MOVC、INC、CLR、CJNE和DB。

● MOVC:MOVC 和前边经常用到的 MOV 指令都属于数据传送类指令,基本功能是传送数据,但使用上有所区别。MOVC的功能是将表中查到的一组数据送到寄存器 A。常用形式如“MOVC A, @A + DPTR”,意思是将 A + DPTR 指定的内存地址的数据传送到寄存器A,所以也称查表指令MOVC。

● INC:是算术运算类指令。算术运算类指令包括有加、减、乘和除法指令,其中INC指令的功能是使累加器加1。

● CLR:属于位移类指令。CLR 的功能是将累加器 A 清 0。

● CJNE:属于控制转移类指令。CJNE 与前边介绍的 DJNZ 指令都是有条件转移指令,DJNZ的条件是寄存器减1不为0转移,一般是与寄存器配合使用,用在控制已知循环次数的循环结构程序中。而CJNE的条件是对两数进行比较,不相等则转移,多用在分支结构的程序中。

● DB:是伪指令。其功能是从指定的地址单元开始,定义若干个字节作为内存单元的内容。

5.本章用过的指令归类

● 数据传送类指令:MOV、MOVC。

● 算术运算类指令:INC。

● 逻辑运算及位移类指令:RL、RR、CLR。

● 控制转移类指令:JMP、DJNZ、ACALL、RET、CJNE。

● 位操作类指令:CPL。

● 伪指令:END、DB。

2.5.3 模拟仿真

本节模拟仿真时主要注意观察取控制码的过程。

运行程序第1行语句时,把表TABLE的首地址存入到DPTR寄存器。

注意,在特殊功能寄存器窗口中看不到DPTR,而是看到两个独立的8位寄存器DPH和 DPL,DPTR 寄存器是由 DPH 和 DPL 组成的。此时 DPL =1CH,此地址是表的开头第1个控制码的地址,也称地址指针或数据指针,即该指针指向表中的第1个控制码0FEH。

运行程序第2行语句时将寄存器A清0。

运行程序第3行语句时,由于在第3行语句中是将A和DPTR两个寄存器内容相加,此时A为0,相加后仍为原DPTR的地址,此地址指针指向的是第1个控制码0FEH,所以,送入寄存器A的控制码是0FEH。

接下来再运行第6行语句时将控制码0FEH送入P1并输出。

运行到第 8 行语句“INC DPTR”时,地址指针加 1 即 1CH + 01H =1DH,所以DPL =1DH,此地址指针指向的是第 2 个控制码 0FBH,依次不断循环。

2.5.4 实例测试

将写入程序的单片机插入实验板接通电源后,会看到单灯向左跳移1次,双灯向右移动2次,3只灯左右移动3次,4只灯交互点亮,8只灯闪烁6次,不断循环。

如果将程序改动一下,把表TABLE中的结束码取消,再在实验板上运行程序,会看到亮灯在进行闪烁后,程序马上停止,不再循环。

这是因为当表查完后,程序要执行下一个语句“END”,END是程序结束指令,所以程序就此结束。

如果想查完一遍表后程序不立即结束,需要在表中最后一行设置一个结束标志码,当程序取到该结束标志码时,通过判断语句(如“CJNE A,#01H,LOOP1”)的作用,使程序不运行结束语句END,而跳转执行其他程序段。

2.5.5 经验总结

利用查表方法编写程序,可以很方便地完成一些复杂的控制功能。

首先通过DB伪指令把控制码制成一个表TABLE,然后再通过查表的指令查表取码。用于查表的指令有两条:

其中使用DPTR作为基地址来查表比较简单,可以通过两步操作来完成。

(1)利用“MOV DPTR,#TABLE”语句,将所查表的首地址存入 DPTR 数据指针寄存器。

(2)再利用“MOVC A,@A + DPTR”查表指令,就可以到数据指针所指的表格内读取出数据。

2.6 中断控制功能的作用

2.6.1 什么是中断

所谓中断,就是打断正在进行的工作,转而去做另外一件事情。

比如说,会计正在记账,桌面上摆着摊开的账本和正在使用的计数器,这时候有人敲门叫他去办另一件事情,他暂时放下手头工作,并保存好账本和计数器以免被弄乱或丢失(在中断中称“保护现场”);等处理完事情后返回办公桌前再拿出帐本和计数器(在中断中称“恢复现场”),继续记账。这一过程就是中断以及中断处理的过程。

单片机的中断过程与上述过程类似,如图2-4所示。

图2-4 中断过程示意图

2.6.2 实现中断的好处

1.提高了CPU的效率

CPU 是计算机系统的指挥中心,它与外围设备(如按键、显示器等)联系沟通的方法有轮询和中断两种。

轮询的方法是指无论外围设备I/O是否需要服务,CPU每隔一段时间都要依次询问一遍,此种方法CPU需要花费一些时间来做询问服务工作。

而中断方法是指当外围设备需要服务时,外设会主动告诉CPU,CPU得知有外围设备需要服务时,才去执行中断处理子程序,所以省去了依次查询的时间,提高了CPU的利用率。

2.可实现实时处理

在实时控制中,利用中断技术,外围设备在任何时刻都可以发出中断请求,CPU 接到请求后就能及时处理,以满足实时控制要求。

3.可及时处理故障

计算机系统在运行过程中难免会出现一些事先无法预料的故障,如电源掉电、存储器出错、外围设备工作不正常等,这时可以通过中断系统由故障源向CPU发送中断请求,由CPU及时转到相应的故障处理程序进行处理,提高了计算机的可靠性。

2.6.3 中断处理过程

中断处理过程如图2-5所示。

图2-5 中断处理流程图

1.中断当前程序并保护断点

CPU响应中断后,把被中断程序的断点(即PC值)压入堆栈保存,以备中断处理完毕后能返回被中断的程序。

2.转入中断服务入口

给出中断入口地址,转入被响应的中断处理程序。

3.保护现场

为了使被中断的程序及其所使用的寄存器内容不被破坏,以免在中断返回后影响被中断程序的执行,要将被中断程序的有关信息及其所使用的寄存器内容保护起来,压入堆栈保存,这就是保护现场。

4.执行中断服务程序

中断服务程序是中断处理的具体内容。

5.恢复现场

把保护现场时所保存的中断程序的有关信息及其所使用的寄存器内容恢复原样,以便返回被中断的程序后能够正常执行。

6.中断返回

中断返回是把程序运行从中断处理程序转回到被中断的程序中去。中断返回是通过中断返回指令RETI完成的。

2.6.4 中断源及入口地址

1.中断源

80C51是一个多中断源的单片机,共有3类5个中断源,分别是两个外部中断、两个定时中断和一个串行中断,如表2-8所示。

表2-8 中断源

在3类中断源中,外部中断类是指由外部原因引起的中断,共有两个中断源,即外部中断0(INT0)和外部中断1(INT1)。它们的中断请求信号分别由引脚INT0(P3.2)和INT1(P3.3)引入。

外部中断请求有两种信号方式:电平方式和脉冲方式。

电平方式是低电平有效。只要单片机在中断请求引入端(INT0和INT1)上采样到有效的低电平时,就激活外部中断。

脉冲方式是脉冲的下降沿有效。如在中断请求引入端采样到前一次为高,后一次为低,即为有效中断请求。上述两种信号方式可通过有关控制位进行设置。

定时中断类和串行中断类将在有关章节中介绍。

2.中断源入口地址

每一个中断源都有一个固定的中断处理程序入口,它们的地址如表2-9所示。

表2-9 中断源的入口地址

从表2-9可见,各中断源入口地址间只相隔8个单元。一些简单的中断处理程序可以直接安排在这些单元之内;复杂的中断处理程,则可在各中断入口地址处设置一条无条件转移指令,跳转到其他地址安排中断处理程序。

2.7 中断的控制及设置

中断系统结构如图2-6所示,从图可见,中断控制的实质是对4个特殊功能寄存器 TCON、SCON、IE 和 IP 进行管理和控制。只要按照人们的要求对这些寄存器的相应位进行设置(存入 0 或 1,一般设置 1 为开通),就能完成对中断的有效管理和控制。

2.7.1 中断允许控制寄存器IE

1.IE的格式及位的含义

寄存器IE的地址为A8H,作用是控制各中断源的开放与关闭。它实行两级控制,IE中有个总的控制位 EA,当 EA =0 时,屏蔽所有的中断请求;而当 EA =1 时,CPU 开放总中断。

图2-6 中断系统结构示意图

各个中断源的请求是否被开放,还要看IE中各个中断源自己的中断允许控制位的状态。IE的格式及各位的含义如图2-7所示。

图2-7 中断允许控制寄存器 IE 示意图

● EA:中断允许总控制位。EA =0 时,中断总禁止,关闭所有中断;EA =1,中断总允许。中断总允许后各中断的禁止或允许则由中断源的中断允许控制位进行设置。它们之间的关系类似于电表控制盘上的总开关及各户的分开关一样。

● EX0:外部中断 0(INT0)允许控制位。EX0 =0 时,禁止外部中断 0 中断;EX0 =1时,允许外部中断0中断。

● EX1:外部中断 1(INT1)允许控制位,其功能与 EX0 类同。

● ET0:定时器/计数器 T0 的溢出中断允许控制位。ET0 =0 时,禁止定时器/计数器T0 中断;ET0 =1 时,允许定时器/计数器 T0 中断。

● ET1:定时器/计数器 T1 的溢出中断允许控制位,其功能与 ET0 类同。

● ES:串行中断允许控制位。ES =0 时,禁止串行中断;ES =1 时,允许串行中断。

2.IE的设置举例

假如程序需要使用外部中断0(INT0),只要将寄存器IE的外部中断允许控制位和中断允许总控制位设置为1即可,可通过下面的语句来实现:

2.7.2 中断优先级控制寄存器IP

1.IP的格式及位的含义

寄存器 IP 的地址为 B8H,作用是控制中断的优先级,其格式及各位的含义如图 2-8所示。

图2-8 中断优先级控制寄存器 IP 示意图

● PX0:外部中断 0 优先级设置位。

● PX1:外部中断 1 优先级设置位。

● PT0:定时器/计数器中断 0 优先级设置位。

● PT1:定时器/计数器中断 1 优先级设置位。

● PS:串行中断优先级设置位。

中断优先级只分高与低两个级别,各位为0时,为低优先级;各位为1时,为高优先级。

2.IP的设置举例

假如程序需要使外部中断0为高优先级,只要将IP的第一位设置为1即可,可通过下面的语句来实现:

3.中断优先级控制原则

(1)低优先级中断请求不能打断高优先级的中断处理;反之,则可以。

(2)如果一个中断请求已被响应,则同级的其他中断响应被禁止。

(3)如果同级的多个中断请求同时出现,则按次序从高到低依次为:外部中断0、定时/计数中断0、外部中断1、定时/计数中断1、串行中断。

2.7.3 定时器控制寄存器TCON

1.TCON的格式及位的含义

寄存器TCON的地址为88H。TCON既有定时器/计数器的控制功能,又有中断控制功能。其中,与中断有关的控制位共6位,其格式及各位的含义如图2-9所示。

图2-9 定时器控制寄存器 TCON 示意图

● IT0:选择外部中断 INT0 的中断触发方式,IT0 =0 时采用电平触发方式;IT0 =1时采用脉冲触发方式。

● IT1:选择外部中断 INT1 的中断触发方式,其功能与 IT0 类同。

● IE0:外部中断 INT0 的中断请求标志位。当检测到 INT0 引脚有中断请求信号时,此位由硬件置1;在中断响应完成后转向中断处理子程序时,再由硬件自动清0。

● IE1:外部中断 INT1 的中断请求标志位,其功能与 IE0 类似。

● TF0:片内定时器/计数器 0 溢出中断请求标志位。当定时器/计数器 0 溢出发生时,此位由硬件置1;在中断响应完成后转向中断处理子程序时,再由硬件自动清0。

● TF1:片内定时器/计数器 1 溢出中断请求标志位,其功能与 TF0 类同。

2.TCON的设置举例

从上面寄存器各位的含意可以看出,在对寄存器 TCON 的设置上只要注意设置触发方式即可。

例如,程序需要使用外部中断INT0,并采用电平触发方式。此时只要将TCON的第一位设置为0即可,通过下面语句来实现:

2.7.4 串行口控制寄存器SCON

寄存器 SCON 的地址为 98H,其中与中断有关的控制位共两位,其格式及各位的含义如图2-10所示。

图2-10 串行口控制寄存器 SCON

● TI:串行口发送中断请求标志位。当发送完一帧串行数据后,由硬件置 1;在转向中断处理程序后,用软件清0。

● RI:串行口接收中断请求标志位。当接收完一帧串行数据后,由硬件置 1;在转向中断处理程序后,用软件清0。

2.8 用外部中断控制灯闪烁

功能说明:单片机P1端口的8只LED作左移右移,不断循环。当按外部中断INT0开关K1时,循环停止,转而使8只LED闪烁4次,然后再恢复灯的左右移循环。

2.8.1 硬件设计

外部中断INT0电路如图2-11所示。

单片机的P3端口除当一般I/O使用外,还有第二功能,其P3.2(INT0)脚是外部中断0的输入脚,其P3.3(INT1)脚是外部中断1的输入脚。

本电路图中,在P3端口的P3.2引脚上接有外部中断INT0控制开关K1,作为外部中断的输入信号。输出部分由P1端口接的8只LED作输出显示。

图2-11 外部中断 INT0 电路

2.8.2 程序设计

本程序在灯左右移程序的基础上,增加了外部中断INT0。

1.流程图

程序设计流程如图2-12所示。

图2-12 程序流程图

2.程序

汇编语言编写的用外部中断控制灯闪烁源程序ZD01.ASM代码如下:

2.8.3 代码详解

1.标号说明

START:起始程序的进入点。

LOOP:左右移循环执行的进入点。

LOOP1:左移循环的进入点。

LOOP2:右移循环的进入点。

LOOP3:闪烁程序循环的进入点。

EXT0:中断子程序的进入点。

DELAY:延时子程序的进入点。

2.寄存器使用分配情况

寄存器P1和A(ACC)的作用与之前相同。R0作计数器,在左移、右移和闪烁程序中都由R0负责计数,R5、R6和R7是延时子程序中的计数器。

PSW、SP、IE、IP、TCON是新用到的特殊功能寄存器,其中PSW是一个8位的专用寄存器,称为程序状态字寄存器,用于存储程序运行中的各种状态信息。其中有两位名为RS0和RS1,改变其值就能选择CPU当前工作的寄存器组。

寄存器组是CPU工作时临时存储数据的地方,共有0~3组,每组8个单元R0~R7,RS0、RS1与寄存器组的对应关系见表2-10。

表2-10 RS0、RS1与寄存器组的对应关系

单片机开始工作时,RS0 =0、RS1 =0,CPU 选用第 0 组的 8 个单元为当前工作寄存器。当主程序要调用中断子程序时,通过“CLR RS1”语句使 RS1 =0,通过“SETB RS0”语句使 RS0 =1,即将 RS1 置为 0、RS0 置为 1,则中断子程序就可以使用第 1 组 8 个单元为当前工作寄存器R0~R7。

这样,使主程序使用的第0组R0~R7的内容就能保持不变,即主程序与子程序各自使用各自的寄存器,以免内容混淆。

SP也是一个8位的专用寄存器,称为堆栈指针寄存器,用于暂存堆栈顶部的地址。堆栈是按先进后出、后进先出的原则存取数据的一个专用存储区。数据的进栈出栈由指针SP统一管理。

TCON、SCON、IE和IP是4个特殊功能寄存器,它的作用是对中断进行管理和控制。

3.程序分析解释

01~04:规定起始地址。通过“ORG 00H”语句,规定了标号 START 所在的地址为 00H,即第一条指令从 00H 开始存放;通过“ORG 03H”语句规定了下面标号 EXT0的中断子程序起始地址为03H,03H是外部中断0的入口地址。

05~08:中断设置。首先是对中断允许控制寄存器IE的设置。允许中断是由两层控制,第一层为全面控制(EA),它是总开关,第二层(EX0)是对个别中断的控制,它是分开关。

通过“MOV IE, #10000001B”语句,使中断允许控制寄存器 IE 中的允许总控制位EA 值为 1,即中断总允许。使 EX0 =1,即外中断 0 允许使用。

接着对优先级控制寄存器 IP 进行设置。中断有优先级,各中断源的优先级由优先级控制寄存器 IP 进行管理,分低优先级和高优先级:PX0 =0 时,为低优先级;PX0 =1 时,为高优先级。“MOV IP, #00000001B”语句使 PX0 =1,即使外部中断 0 为高优先级。

中断触发有两种方式:脉冲方式和电平触发方式。两种方式的选择是通过定时寄存器TCON 来设定的。通过“MOV TCON, #00000000B”语句将 IT0 位设置为 0,则使外中断0选择了电平触发方式。

语句“MOV SP, #70H”设置堆栈指针地址,当程序中执行保护现场指令 PUSH 或恢复现场指令POP时,SP会自动加1或减1,然后将数据压入或弹出堆栈,目的是使中断结束后能正确地回到程序调动点继续执行。

09~20:使LED左移和右移。

21~33:中断子程序。

中断子程序由3部分组成:保护现场部分(21~24行语句)、闪烁功能部分(25~30行语句)和恢复现场部分(31~32行语句)。

其中闪烁功能部分是使用中断的目的。而闪烁功能部分前边的保护现场和后边的恢复现场部分是为了使中断结束后能正确地回到原程序调用点继续执行。

21~22行语句是将累加器A和程序状态字寄存器PSW中的值压栈保存;23~24行语句是设置 RS1 =0、RS0 =1,目的是使中断子程序使用第 1 组工作寄存器。这样,第 0组R0~R7的内容就能保持不变

34~40:延时1s子程序。

41:程序结束。

4.边用边学指令

本节程序新用到的指令有:PUSH、POP、RETI和ORG。

• PUSH 和 POP 是两条专用堆栈操作指令,属于数据传送类。堆栈操作的特点是按先进后出、后进先出的原则存取数据。PUSH和POP是成对出现的,PUSH是进栈指令,POP是出栈指令,一般用于在中断子程序中保护现场和恢复现场。

• RETI 是控制转移类指令,RETI 的功能是使中断服务程序结束返回。RETI 除具有子程序返回指令RET所具有的全部功能之外,还有清除中断响应时被置位的优先级状态、开放较低级的中断和恢复中断逻辑等功能。

• ORG 是伪指令,ORG 的功能是规定下面目标程序存放的起始地址。

2.8.4 模拟仿真

1.模拟仿真前注意事项

(1)将第 12、18、28 行调用延时语句前加上分号,如“ACALL DELAY”改为“;ACALL DELAY”。目的是使程序跳过调用延时的语句,节省模拟运行的时间。

(2)将第 20 行语句“JMP LOOP”改为“JMP EXT0”,使程序能运行标号为 EXT0的中断子程序。

(3)重新将程序“编译/汇编”和“产生代码并装入”,然后再调试。

2.模拟仿真中注意事项

(1)注意观察中断设定中特殊功能寄存器IE、IP、TCON和SP值的变化。其中IE、IP、TCON在程序中给出的是二进制值,但在模拟仿真特殊功能寄存器窗口中看到的是十六进制数。

(2)在运行20行语句前,ACC的值是FEH,PSW的值是01H,即PSW中控制工作寄存器组的两个位 RS0 =0、RS1 =0,所以此时 CPU 选中的是第 0 组工作寄存器。

当运行21~24行语句之后,PSW的值改变为09H,即PSW中控制工作寄存器组的两个位 RS0 =1、RS1 =0,所以此时 CPU 使用的工作寄存器由第 0 组改为第 1 组。使原来第0组工作寄存器的内容能保持不变,起到保护现场的作用。

(3)当程序运行到31~32行之后,PSW的值为01H,ACC的值为FEH,这是通过POP指令取回了PSW和A在进入中断子程序前的值(即原来值),起到恢复现场的作用。

2.8.5 实例测试

将写入程序的单片机插入实验板插座上,检查无误后接通电源,此时将看到亮灯从右向左移动,移到最左端后再从左向右移动,不断循环。

当按开关K1产生中断信号时,灯移动循环停止,转而执行中断服务程序,8只LED闪烁4次。中断服务程序结束之后,亮灯再恢复左右移动循环。

在实例测试时要仔细观察,程序从什么地方发生中断,将从什么地方开始恢复。

亮灯从右端向左移动到第3个时,下一个将是第4个位置灯亮,可以在此时按K1按钮发生中断,灯开始闪烁,当灯闪烁停止后,亮灯会接着从第4个位置开始向左移动。中断后程序能准确地从原中断点开始恢复,主要是因为在中断服务程序中采用了保护现场和恢复现场的措施。

2.8.6 经验总结

编写中断程序要注意以下几步。

(1)确定中断入口地址。

(2)中断设置,包括开通中断、确定优先级和触发方式等,即对4个特殊功能寄存器TCON、SCON、IE和IP进行设置。

(3)在中断处理子程序里,要有保护现场和恢复现场部分,这是中断处理子程序与前面讲的子程序的不同点。

2.9 交通灯实例

定时器/计数器是单片机中最常用、最重要的功能模块之一,本节通过交通灯控制器实例来演示定时器的使用方法,并复习如何使用上节学习的散转程序。

本节首先介绍交通灯的基础知识以及定时器/计数器的基础知识,接着介绍本实例的硬件电路构成,然后逐步分析定时器的编程以及程序的全貌,最后将总结一下本实例的技巧与注意点。

2.9.1 基础知识

交通灯控制器实例主要使用了 8051 单片机的定时器/计数器,本实例的基础知识主要包括:交通灯的变化规律、定时器/计数器的概念、定时器/计数器的相关寄存器、定时器/计数器的 4 种工作方式以及定时器/计数器的编程。下面就从这几个方面进行介绍。

1.交通灯的变化规律

本实例是交通灯控制器,所以先介绍交通灯的变化规律。

假设一个十字路口为东西南北走向。初始状态为状态1南北方向绿灯通车,东西方向红灯。经过过一段时间(20 秒)转换到状态 2,南北方向绿灯闪几次转亮黄灯,延时 3秒,东西方向仍然红灯。再转换到状态3,东西方向绿灯通车,南北方向红灯。过一段时间(20秒)转换到状态4,东西方向绿灯闪几次转亮黄灯,延时3秒,南北方向仍然红灯。最后循环至状态1。

2.定时器/计数器的概念

8051单片机内有两个可编程的定时器/计数器T0、T1。

当定时器/计数器用作“定时器”功能时,每经过1个机器周期(12个时钟周期),计数器加1。

当定时器/计数器用作“计数器”功能时,计数器在对应的外部输入管脚(T0为P3.4引脚,T1为P3.5引脚)上每发生一次1到0的跳变时加1。使用“计数器”功能时,外部输入每个机器周期被采样一次。当某一周期管脚状态采样为高而下一周期采样为低时,计数器加1。由于检测下降沿跳变需要两个机器周期(24个时钟周期)的时间,所以计数频率最大值只能为时钟周期的1/24。计数器对外部输入信号的占空比并无限制,但为了保证给定的电平信号在其改变之前至少被采样一次,外部输入信号必须至少保持一个完整的机器周期。

3.定时器/计数器的相关寄存器

与定时器/计数器相关的寄存器有定时器/计数器工作方式寄存器(TMOD)、定时器/计数器控制寄存器(TCON)。TCON已经在2.5节受控输出实例中介绍过,在本例中主要介绍TMOD寄存器。

定时器/计数器工作方式寄存器(TMOD),字节地址89H,不可进行位寻址。TMOD的格式如图2-13所示。

图2-13 TMOD 寄存器的格式

定时器/计数器工作方式寄存器(TMOD)的8位分为两组,高4位控制T1,低4位控制T0。TMOD每一位的功能如下:

● GATE:门控位。

GATE =0,仅由运行控制位 TRx(x =0,1) =1 来启动定时器/计数器运行;

GATE =1,由运行控制位 TRx(x =0,1) =1 和外部中断引脚上的高电平共同来启动定时器/计数器运行。

● C/T:定时器模式和计数器模式选择位。

C/T =0,为定时器模式;

C/T =1,为计数器模式。

● M1、M0:工作方式选择位。M1、M0 的 4 中编码对应 4 种工作方式,对应关系见表2-11。

表2-11 定时器/计数器工作方式选择

4.定时器/计数器的4种工作方式

定时器/计数器的4种工作方式下的逻辑结构如表2-12所示。

表2-12 定时器/计数器的逻辑结构
续表
续表

(1)方式0

定时器/计数器的工作方式0称为13位定时器/计数器。它是由TLx的低5位和THx的8位构成13位的计数器,此时TLx的高3位未使用。该工作方式是为了和48系列单片机兼容而设计的一种工作方式,一般情况下一般不使用方式 0 进行定时/计数。方式 0的控制方式与方式1完全相同,下面重点介绍方式1的控制方式。

(2)方式1

定时器/计数器的工作方式1称为16位定时器/计数器。它由TLx和THx构成,TLx计数溢出向THx进位,THx计数溢出置位TCON中的溢出标志位TFx。

GATE位的状态决定定时器/计数器运行控制取决于TRx一个条件还是TRx和INTx引脚这两个条件。当GATE =0时,则只要TRx被置为1,定时器/计数器即被允许计数(定时器/计数器的计数控制仅由 TRx 的状态确定,TRx =1 计数,TRx =0 停止计数)。当 GATE =1时,定时器/计数器是否计数由INTx输入的电平和TRx的状态共同确定:当TRx =1,且INTx =1时,才允许定时器/计数器计数(定时器/计数器的计数控制由TRx和INTx两个条件控制)。

(3)方式2

定时器/计数器的工作方式0和方式1在计数溢出后,计数器的值为0,需要通过程序重新装入计数初值。

定时器/计数器的工作方式1称为初值自动重装的8位定时器/计数器。在该工作方式下,TLx作为计数器,当TLx计数溢出时,在置1溢出标志TFx的同时,还自动地将THx中的常数送至TLx,使TLx从该常数开始重新计数。这种工作方式可以省去用户软件中重装常数的程序,简化定时常数的计算方法(确定计数初值),可以相当精确地确定定时时间。

(4)方式3

工作方式3仅对定时器/计数器0有效,在该工作方式下,定时器/计数器0被拆成2个独立的定时/计数器:TL0、TH0。TL0使用T0的状态控制位C/T、GATE、TR0、INT0,而TH0被固定为一个8位定时器(不能用作外部计数方式),并使用定时器/计数器1的状态控制位TR1和TF1,同时占用定时器T1的中断源。此时,定时器/计数器1可设定为方式0、方式1和方式2,作为串行口的波特率发生器。

注意:此时,定时器/计数器1也可作为定时器,用于不需要中断的场合。

5.定时器/计数器的编程

(1)初始化

定时器/计数器的初始化编程包括以下几个部分:

● 根据要求给定时器/计数器方式寄存器(TMOD)送一个方式控制字,以设定定时器/计数器的工作方式。

● 根据需要给 TH 和 TL 寄存器送初值,以确定需要的定时时间或计数的初值。

● 根据需要给中断允许寄存器(IE)送中断控制字,以开放相应的中断和设定中断优先级。

注意:也可用查询方式来响应定时器。

● 给 TCON 寄存器送命令字以启动或禁止定时/计数器的运行。

(2)定时器/计数器初值的计算

● 计数器初值:

设计数器的模值为M,所需的计数值为C,计数初值设定为TC,则

TC =M-C(M =213、216或28)。

● 定时器初值:

设定时器的模值为M,需要的定时时间为T,定时器的初值设定为TC,则

TC =M-T/t机器周期(M =213、216或 28)。

2.9.2 硬件电路图

本实例硬件电路如图2-14所示,使用6只LED模拟两组红绿灯:VD1、VD2、VD3分别模拟南北方向的红灯、黄灯、绿灯,VD4、VD5、VD6分别模拟东西方向的红灯、黄灯、绿灯。

图2-14 交通灯控制器电路图

2.9.3 软件程序设计

交通灯控制器实例使用了8051单片机的定时器/计数器,软件程序设计部分首先分定时器初始化、定时器中断服务程序两个部分介绍定时器/计数器的软件编程。然后介绍如何在画出程序流程图的基础上编写软件程序,并给出了完整的交通灯控制器程序实例。

1.定时器初始化

为了使定时时间准确,不出现因为定时器重装而引起的累计误差,将定时器设置为初值自动重装的8位定时器/计数器,即定时器工作在工作方式2。在12MHz晶振条件下,8位定时器的最长定时时间为0.256毫秒,为了方便计算取定时时间为0.25毫秒,这样,定时0.5秒钟需要定时器中断2000次。

下面计算定时器的初值。定时器初值 TC =M-T/t机器周期=28-250/1 =6,因此 TH0 =06H,TL0 =06H。

以下为定时器初始化程序实例,定时器T0设定为工作方式2,初始值为06H,自动重装入值为06H。

2.定时器中断服务程序

定时器中断服务程序实例如下:

每0.25毫秒定时器中断发生,程序跳转到中断服务程序T0_INT开始执行。中断服务程序每次将定时器中断计数变量减1,当定时器中断计数变量为0时,0.5秒定时时间到,将位变量SECOND_FLAG置1。定时器中断服务程序通过RETI指令返回,程序将跳转到进入中断前的断点继续执行。

3.程序流程图

在前面几节中,程序较为简单,可以直接进行程序的编写,但本实例的程序流程比较复杂,在编写程序之前,应当先画出程序流程图。在编写复杂的程序之前画出程序流程图,有助于理清思路,方便编程,应当养成编写程序前画程序流程图的良好习惯。

程序流程图是描述程序运行流程的一种图表。它不仅描绘程序从头至尾的运行顺序,也描述了程序运行过程中的所有可能的状况。一般程序流程图包含的基本元素如表 2-13所示。

表2-13 程序流程图的基本元素

本实例的程序流程图如图2-15所示。

图2-15 交通灯控制器程序流程图

4.程序全貌

2.9.4 技巧总结

本实例以交通灯控制器为例介绍了8051单片机定时器/计数器的使用方法,通过本实例应注意以下的几个技巧。

● 定时器的初始化以及定时时间的计算。

● 定时器的中断服务程序的编写应注意现场保护和现场恢复的操作;中断服务程序通过RETI指令返回;中断服务程序应当尽量简短。

● 在编写复杂的程序前,画程序流程图可以理清思路,使编程事半功倍。

2.10 变频报警实例

本实例是用软件延时方法实现变频振荡报警,即用P3.4端口输出1kHz和2kHz的变频信号以示报警,每隔1s交替变换1次。本节硬件设计与上节相同。

2.10.1 程序设计

本程序利用软件延时方法,使P3.4端口输出1kHz和2kHz的变频信号,每隔1s交替变换1次。

1.流程图

程序设计流程如图2-16所示。

图2-16 程序流程

2.程序

汇编语言编写的变频报警源程序FS02.ASM代码如下:

2.10.2 代码详解

1.标号说明

START:程序开始的进入点。

LOOP1和LOOP2:输出1kHz方波,持续1s时间是采用双重循环来实现的。LOOP2是内循环的进入点,LOOP1是外循环的进入点。

LOOP3和LOOP4:输出2kHz方波,持续1s时间也是采用双重循环来实现的。LOOP4是内循环的进入点,LOOP3是外循环的进入点。

DELAY1:延时250μs子程序的进入点。

DELAY2:延时500μs子程序的进入点。

2.寄存器使用分配情况

在程序中,P3与R1~R6都是寄存器。P3是特殊寄存器,对外是输出端口;R1~R6是普通寄存器,用作计数器。

P3.4对内是寄存器P3的一个位,对外是P3端口的第5个引脚。

R1和R2:输出1kHz方波时,采用双重循环来实现持续1s所使用的两个计数器。

R3和R4:输出2kHz方波时,采用双重循环来实现持续1s所使用的两个计数器。

R5:延时250μs子程序中所用的计数器。

R6:延时500μs子程序中所用的计数器。

3.程序分析解释

本程序使P3.4端口输出1kHz和2kHz的变频信号,每隔1s交替变换1次。

01~06:完成1kHz的输出,并持续1s。

07~12:完成2kHz的输出,并持续1s。

14~16:输出1kHz时的延时子程序;17~19行语句为输出2kHz时的延时子程序。

第01~06行语句中,03行语句使P3.4端口作反相输出,每次反相输出后,都要调用延时子程序延时500μs,该延时的时间决定着P3.4端口输出的频率为1kHz。

02和05行语句组成内循环,01和06行语句组成外循环。双重循环使输出1kHz方波时间持续为1s,如图2-17所示。

图2-17 双重循环路线示意图

第07~12行语句的结构与第01~06行语句完全相同,只是调用的延时子程序的延时时间缩短了,输出2kHz方波,并持续1s。

第14~16行语句与第17~19行语句组成两个延时子程序,延时的时间不一样,但是程序结构一样,都属于单重循环结构的延时程序。

2.10.3 模拟仿真

该程序语句行数不多,但循环较多。因此,在模拟仿真时,主要是观察程序的运行路线,弄清每次循环的意图,以便更好地理解程序。

为了节省时间,在模拟仿真之前,可以先将寄存器R2、R4、R5和R6的数值减小。

2.10.4 实例测试

将写入变频报警程序的单片机插入实验板插座内,并检查实验板上的峰鸣器是否接在P3.4端口上,当检查无误后接通电源,会听到变频报警声。

实例测试时注意听声音的频率变化及频率变化的时间。该程序开始时先发出1kHz的声音,持续1s,接着发出2kHz的声音,也持续1s。不断循环,便产生变频报警声。

可以改变程序中延时子程序DELAY1或DELAY2的延时时间,观察声音的变化;再改变一下程序中的R1或R3的数值,观察发声长度的变化,从而加深对变频报警声产生原理的理解。

2.10.5 经验总结

本节程序利用两个频率声音交替变化,产生报警声,其中延时子程序中的延时时间决定输出声音的频率,双重循环延时的时间决定声音的长短。

2.11 歌曲演奏实例

本实例是利用单片机演奏一首生日快乐歌。

2.11.1 编程演奏器原理

1.演奏器原理

(1)通过控制单片机定时器的定时时间产生不同频率的音频脉冲,经放大后驱动蜂鸣器发出不同音节的声音。

(2)用软件延时来控制发音时间的长短,控制节拍,表2-14是各调1/4节拍的时间表。

表2-14 各调1/4节拍的时间

(3)把乐谱中的音符和相应的节拍变换为定时常数和延时常数,作为数据表格存放在存储器中。由程序查表得到定时常数和延时常数,分别用来控制定时器产生的脉冲频率和发出该音频脉冲的持续时间。

(4)表2-15为单片机晶振频率为12MHz时,乐曲中的音符、频率及定时常数之间的对应关系表。

表2-15 音符、频率及定时常数对应关系
续表

2.歌谱

3.建立步骤

(1)先把乐谱的音符找出,然后根据表 2-15 给出的定时值按乐谱的音符顺序建立编码表TABLE。

定时值为十六进制4位数,拆开分为两组,如5对应的定时值为FD80H,拆分为FDH和80H两组。前组装入定时器的高位TH0,后组装入定时器的低位TL0。程序中将进行两次查表来完成一个音符对应的定时初值的装入。

(2)在程序中使用定时器 T0 方式 1 来产生歌谱中各音符对应频率的音频脉冲,由P3.4输出,再经三极管将信号放大后驱动蜂鸣器发出不同音节的声音。

(3)程序中节拍的控制是通过调用延时子程序 DELAY 的次数来实现的,1 拍为 748 ms,即需要调用4次DELAY;3/4拍需要调用3次DELAY;2/4拍需要调用2次DELAY。

(4)节拍的控制码在表 TABLE 中位于音符码的后面。如第 1 行“DB 0FDH,80H,03H,…”中,0FDH和80H是音符5的音符码,其后边的03H是节拍码,即3/4拍的时间。

(5)当一个音符的发音时间到时,再查下一个音符的定时常数和延时常数。依此进行下去,就可演奏出悦耳动听的乐曲。

2.11.2 程序设计

1.流程图

程序设计流程如图2-18所示。

图2-18 程序流程

2.程序

汇编语言编写的歌曲演奏源程序FS03.ASM代码如下:

2.11.3 代码详解

1.标号说明

START:程序开始的进入点。

LOOP:处理下一个音符的进入点。

NEXT:装入定时初值的进入点。

NEXT0:关闭定时器、停止发音的进入点。

NEXT1:查找延时常数的进入点。

LOOP1:处理节拍时间的进入点。

EXT0:中断子程序的进入点。

DELAY:延时180ms子程序的进入点。

2.寄存器使用分配情况

R0、R1、R2、R5、R6、R7:普通寄存器。其中R0存放低位定时器初值;R1存放高位定时器初值;R2存放延时常数值;R5、R6和R7在延时子程序中作计数器用。

A和DPTR:特殊功能寄存器,其中A又称累加器,DPTR为数据指针。在程序中把表TABLE的首地址存入DPTR作基础地址,A作为变址寄存器。将基址寄存器和变址寄存器的内容相加(@A + DPTR)形成操作数的地址。

TMOD:定时器工作模式控制寄存器。

TL0和TH0:定时器0的计数器,TL0为低8位,TH0为高8位。

TR0:定时器0控制寄存器TCON的一个控制位。

IE:中断允许控制寄存器。

P3.4:对内是P3寄存器的一个位,对外是输入/输出端口的一个引脚,作音频信号输出端口。

3.程序分析解释

01~04:设定入口地址。其中定时器中断是从标号EXT0处进入中断子程序。

05~07:设置定时器T0为方式1,并允许T0中断。其中07行语句将表TABLE的首地址存入数据指针寄存器DPTR中。

08~10:第一次查表,取出表中的第一个码,即乐谱中的第一个音符5的定时常数(定时初值)FD80H中的高位部分FDH,并存入寄存器R1中。

11~14:第二次查表,取出表中的第二个码,即乐谱中的第一个音符 5 的定时常数FD80H中的低位部分(即80H),并存入寄存器R0中。

15~20:判断、转移语句。对二次查取到的码,检查是否有休止符码,该判断是通过第 15 行语句“ORL A,R1”对 A 和 R1 寄存器的内容进行或运算实现的。

如果累加器A中的值是00H,R1寄存器的值也是00H,进行或运算后再存入累加器A 中,此时累加器 A 中的值是全 0,说明取到的是休止符码。通过第 16 行语句“JZNEXT0”,跳转至标号NEXT0处,关闭定时器,停止发音,完成乐谱中休止符的作用。

取码时是否取到了结束码是通过第18行语句指令ANL对A和R1进行与运算来判断的。如果累加器A中的值是FFH,R1寄存器的值也是FFH,进行与运算后再存入累加器A中,此时累加器A中的值是全1,说明取到的是结束码。

第 19 行语句“CJNE A,#0FFH,NEXT”,如果累加器 A 中的值与#0FFH 不相等,则跳转移到NEXT处,开始向定时器装入定时常数;如果A与#0FFH相等,说明取到的码是结束码,程序向下执行,即从头开始循环演奏。

21~23:开始向定时器装入定时常数并启动定时器工作。

24~25:处理拍节。

26~29:查取延时码并存入R2。

30~31:决定拍节时间。如果取来的码是02H,程序调用两次DELAY延时,即为2/4拍节时间;如果取来的码是04H,程序调用4次DRLAY延时,即为1拍节时间。

32~33:每次查表取码后,数据指针都要加 1,以便指向下一个待查的码。第 33 行语句将程序运行跳转到标号LOOP处,处理下一个音符。

34~37:中断子程序。当定时器计时满后将产生中断,由标号EXT0处进入中断子程序。在中断子程序中重装定时值,并通过第36行语句在P3.4端口输出音频信号。

38~44:延时187ms子程序。

45~61:编码表TABLE。在表中每个音节由3个码组成,前两个为音符码,后一个为节拍码。如表中第1行“0FDH,80H,03H”,其中0FDH和80H即为FD80H,是音符5的发音编码;03H是节拍码,3/4节拍。

表中的最后一行“0FFH,0FFH”是结束码,表示乐曲演奏结束。

4.边用边学指令

本程序用到新的指令有ORL和JZ。

● ORL:逻辑运算及位移类指令中的按位或操作指令。该指令将累加器 A 中的内容与源操作数所指出的内容按位进行逻辑或运算,结果存入A中。

● JZ:控制转移类指令,功能是当累加器 A 为 0 时跳转。

2.11.4 模拟仿真

1.模拟仿真前注意事项

在 23 与 24 行语句之间,加一条“SETB TF0”语句,该语句使定时器 T0 的溢出标志位TF0为1,模拟产生中断,使程序能进入中断子程序中运行。

在39~43行语句的前边加分号,使延时子程序的延时时间缩短为2ms,缩短模拟仿真时间。

2.模拟仿真中注意事项

观察程序主要运行路线:第一次查表→第二次查表→中断子程序及返回→第三次查表→进入延时子程序及返回→处理下一个音符(跳转到LOOP处)。

观察累加器 A 中值的变化。第一次查表执行第 09 行语句“MOVC A,@A + DPTR”后,A 值为 FDH。第二次查表,执行第 13 行语句“MOVC A,@A + DPTR”后,A 值为80H。两次查表取回音符5的对应码FD80H。第三次查表,执行第28行语句“MOVC A,@A + DPTR”后,A 值为 03H,取回的是音符 5 的对应拍节码。

2.11.5 实例测试

将写入歌曲演奏程序的单片机插入实验板插座内,并检查实验板上蜂鸣器接口是否与程序中声音输出端口一致,当检查无误后接通电源,就会听到生日快乐歌。

2.11.6 经验总结

在用单片机作可编程乐曲演奏器的程序里,一般用定时器T0方式1来控制音符的频率,调用延时子程序DELAY来控制节拍。

程序采用查表的方法,将乐谱转换成控制码并制成表 TABLE,然后进行三次查表。第一、第二次查表完成对音符的控制,第三次查表读取节拍码,完成对节拍的控制。

在完成第一、第二次查取音符的控制码后,程序中还设有判断语句,如果判断出取来的是休止符码,就将定时器关闭一段时间;如果判断出取来的是结束码,程序就从头开始循环。

2.12 电子琴

本实例是采用4×4矩阵式键盘设计出16个音符,随意弹奏。

2.12.1 硬件设计

电子琴电路设计如图2-19所示。

图2-19 电子琴电路

单片机P3为输入端口,接有4×4矩阵式键盘,键盘上标出16个音符。键盘的4条行线的一端与单片机 P3 端口的 P3.0、P3.1、P3.2 和 P3.3 口相接,另一端通过上拉电阻接到 + 5V 上;4 条列线的一端与 P3 端口的 P3.4、P3.5、P3.6 和 P3.7 口相接。单片机的 P2.4端口为输出端,通过限流电阻R1与三极管基极相接,三极管的集电极接有蜂鸣器。

2.12.2 程序设计

电子琴程序设计的思路是:设计16个音符,与4×4矩阵式键盘16个按键一一对应。音符通过定时器T0产生,然后通过键盘扫描法,将按下的键转换成相对应的音符。

1.流程图

程序设计流程如图2-20所示。

图2-20 程序流程

2.程序

汇编语言编写的电子琴源程序JP04.ASM代码如下:

2.12.3 代码详解

1.程序分析解释

01~04:设置程序开始地址和中断T0起始地址。

05~07:设置T0工作在模式1下;开通中断并启动T0工作。

08:调用扫描子程序KEY开始扫描,判断是否有键按下。

09~22:存表(第13行语句),取码(第14和第20行语句),向定时器装初始值(第15和第21行语句)。与此同时,还将取到的高位字节和低位字节分别存入地址21H(第16行语句)和20H(第22行语句)处,以便在中断子程序中对定时器重装初始值用。地址22H用于暂存取码指针。整个过程由于已将中断关闭,所以定时器不能产生中断信号,输出端P2.4没有输出。

第10行语句是判断F0是0还是1。F0是状态字寄存器PSW的一个自由使用的标志位。程序规定 F0 =1 表示没有键按下,F0 =0 表示有键按下。

23~26:再调用扫描程序,确认是否有键按下。当有键按下时,由于此时已经开通了中断,定时器会产生中断信号,使程序进入到中断服务子程序运行,于是输出端 P2.4 便有输出。

27~44:扫描子程序。

45~47:有键按下后,将取码指针存入地址 22H,并令 F0 =0,用来标志有键按下。

48~55:中断服务子程序。其中第48~49行为进栈保护现场,第53~54行为出栈恢复现场。50~51行重装定时器初始值。第52行语句是输出语句。第55行语句是中断服务子程序返回主程序的指令。

56~59:编码表。表中的16个数是定时器发出16个音的定时器初始值。

60:程序结束。

2.至本章用过的指令归类

● 数据传送类指令:MOV、MOVC、PUSH、POP。

● 算术运算类指令:INC、DIV。

● 逻辑运算及位移类指令:RL、RR、RLC、RRC、CLR、ANL、ORL、XRL。

● 控制转移类指令:JMP、DJNZ、ACALL、RET、CJNE、RETI、JZ。

● 位操作类指令:CPL、CLR、SETB、JB、JNB、JBC、JNC、JC。

● 伪指令:END、DB、ORG。

2.12.4 模拟仿真

1.模拟仿真前注意事项

在第30行语句与31行语句之间,增加一条模拟按键按下的语句。

在第24行语句与25行语句之间,增加一条模拟中断溢出的语句,使程序能进入到中断服务程序里运行。

2.模拟仿真中注意事项

(1)注意观察程序运行路线。当第一次扫描检测到有键按下时,由于第09行语句将中断屏蔽,所以程序运行不能进入到中断服务子程序,输出端P2.4没有输出。

在第二次扫描再次确认有键按下时,由于第 24 行语句将中断开启,所以定时器 T0将产生中断信号,通过程序标号TIM0进入中断服务子程序,使P2.4端口产生输出。

(2)去掉模拟按键和模拟中断溢出语句,观察程序运行情况,由于没有按键按下,所以P2.4没有输出。

2.12.5 实例测试

将写入程序的单片机插入实验板插座内,并检查实验板上的蜂鸣器接口是否与程序中声音输出端口一致,当检查无误后接通电源,即可按图2-19所示进行音乐弹奏。

2.12.6 经验总结

电子琴程序从功能设计上可分为两大部分。

● 利用单片机内定时器 T0 产生 16 个音符,16 个音符代码在表中的排列顺序与 16个按键上所标出的音符是对应的。

● 通过对键盘不断进行扫描,当确定有按键操作后,将所按下的键转换成相对应的音符,并通过输出端口使蜂鸣器发出相对应的声音,声音的长短由按键的时间决定。

2.13 软件陷阱实例

在单片机系统的实际应用中,系统的抗干扰能力显得非常重要。提高单片机系统的抗干扰能力除了采取硬件抗干扰手段外,也可采用软件抗干扰的手段,并且软件抗干扰具有设计灵活、节省硬件资源、成本低的优点,加以合理运用也能起到很好的抗干扰效果。

软件抗干扰的措施主要有指令冗余、软件陷阱、软件“看门狗”等。本节通过软件陷阱实例介绍如何编写软件抗干扰程序。

本节首先介绍什么是程序跑飞以及软件陷阱技术,接着讲述软件陷阱在程序中的位置,最后将给出软件陷阱程序实例并对软件陷阱程序进行分析。

2.13.1 基础知识

单片机程序正常运行时,程序计数器PC始终指向正在执行指令的下一条指令第一个字节的程序存储器单元地址,这样就保证了单片机能够正确地读取每一条指令的各个字节,即CPU先读取操作码,再读取操作数(如果有操作数字节的话)。在8051单片机中,程序计数器PC的寻址范围是0000H~FFFFH,共64KB。用户应用程序中,根据系统要求,规定了程序运行的唯一路径。这体现在系统上电后,程序计数器PC有唯一的变化历程,保证了程序正常、有序地运行。程序跑飞是指系统受到某种干扰后,程序计数器 PC的值偏离了给定的唯一变化历程,导致程序运行偏离正常的运行路径。

程序跑飞的因素及后果往往是不可预计的。但在很多情况下,程序跑飞后PC指向未写入数据的Flash程序存储器。由于没有写入数据的Flash程序存储器的内容为0FFH,机器码 0FFH 对应的指令为 MOV R7,A,所以系统不断地执行 MOV R7,A 指令,并很快会执行到程序存储器的最末尾,进入死循环而导致死机。这时,采用软件陷阱技术能有效引导跑飞的程序尽快退出死循环并迅速复位。

2.13.2 软件程序设计

软件陷阱程序主要包括程序跑飞的捕获和复位处理两部分,下面对这两部分分别给出程序实例并对程序做详细解释。

1.软件陷阱

软件陷阱程序段如下:

当单片机系统工作正常时,单片机不会执行软件陷阱程序段。但是当单片机系统受到干扰而程序跑飞后,由于程序计数器PC值错误,破坏了正常的指令格式,导致执行非正常指令,从而执行软件陷阱程序段,落入软件陷阱,将跑飞的程序引导到软件陷阱复位入口地址ERR。

软件陷阱程序段中的连续2条NOP指令实际上也就是指令冗余的抗干扰方法,目的是为了增强 LJMP ERR 被执行的能力,即 LJMP ERR 指令不会被冲散,当程序跑飞后会得到完整地执行,从而使跑飞的程序纳入正常轨道。

2.软件陷阱在程序中的位置

下面将根据软件陷阱在程序中的不同位置,给出软件陷阱程序的具体实例。

(1)未使用的中断向量区

在未使用的中断向量区放上软件陷阱,就可以及时捕捉到错误的中断。在未使用的中断向量区放置软件陷阱的实例如下:

(2)子程序后面

当用户应用程序正常运行时,这些软件陷阱程序段并不会执行,但是当单片机受干扰而使程序失控时,程序计数器PC指针一旦落入这些陷阱区,就可以马上将跑飞的程序拉回到正确的轨道。

(3)中断服务程序后面

(4)在表格区

(5)未使用的程序存储器空间

如上文所述,程序一旦跑飞到未使用的程序存储器空间,将会飞流直下,一般在一些固定的地址加入软件陷阱,捕获跑飞的程序。

3.复位处理程序

跑飞的程序被软件陷阱捕捉后,可以直接使用 LJMP 0000H 指令使程序回到正常的执行状态。但是由于程序跑飞前的单片机状态不确定,一些情况下直接使用 LJMP 0000H 指令使程序返回正常状态并不可靠,因此最好加入一段错误复位处理程序,将单片机恢复到正常状态。错误复位处理程序实例如下:

使用以上的错误复位处理程序主要是由于程序受到干扰而跑飞是随机发生的,如果程序跑飞前正处于中断状态,直接跳转到程序起始位置 0000H 并不能使单片机回到正常工作状态(中断激活标志未清除)。在 8051 单片机中,能够清除中断激活标志的指令只有RETI 指令,因而可以利用 RETI 指令使单片机回到正常状态。而在最严重的情况下,程序跑飞时,单片机正处于两级中断嵌套的状态,此时需要通过两个RETI指令才能使单片机回到正常状态。下面我们看看上面的错误复位处理程序实例是如何使单片机回到正常状态的。

程序(1)关闭中断,使错误复位处理程序不受到中断信号的影响。程序(2)~(4)将 ERR1 的地址压入堆栈中。程序(5)使用 RETI 指令清除高级中断激活标志,同时使程序跳转到ERR1处继续执行。程序(6)~(8)将地址0000H压入堆栈中。程序(9)再次使用RETI指令清除低级中断激活标志,同时使程序跳转到起始位置0000H。通过这一系列操作,才能真正可靠地将单片机恢复到正常状态,并从程序起始位置重新开始执行。

2.13.3 技巧总结

本实例详细解释了软件陷阱的作用、原理,并给出了软件陷阱的程序实例。本实例需要注意以下两点。

● 在程序的恰当位置放置软件陷阱,可以在一定程度上加强抗干扰能力。但是软件陷阱的作用是有限的,在抗干扰要求高的情况下,还应当和“看门狗”等硬件抗干扰手段结合使用。

● 软件陷阱最好不要将程序直接引导向启动地址 0000H,而应当加入一段如上文所示的错误复位处理程序。在错误复位处理程序中,除通过本实例给出的这些指令操作使单片机的状态恢复正常外,还可加入一些保存关键数据的操作。

图书在版编目(CIP)数据

51单片机应用开发从入门到精通/张华杰编著.--北京:人民邮电出版社,2014.4

ISBN 978-7-115-34003-0

Ⅰ.①5… Ⅱ.①张… Ⅲ.①单片微型计算机 Ⅳ.①TP368.1

中国版本图书馆CIP数据核字(2013)第292942号

内容简介

本书遵循由浅入深、循序渐进的原则,讲解单片机开发的必备知识以及开发经典案例。第 1 章是单片机开发的必备知识,主要讲解单片机开发的过程、所用的工具、最小系统以及相关的名词等;第 2 章和第 3 章是单片机开发的基础实例讲解,通过精选的20多个单片机开发最基础、最常用的实例,介绍单片机开发的基本过程,使初学者能够轻松入门;第 4 章到第 7 章着重讲解单片机开发的进阶实例,通过分析数据采集、数据通信、综合实例等众多实用经典实例,来讲解单片机开发的技巧,提高读者的单片机开发能力。

本书内容丰富、实例详尽、图文并茂,主要通过实例讲解单片机开发的基本过程,适于初、中级读者掌握单片机开发的过程和技巧,对工程技术人员也有一定的参考价值。

◆编著 张华杰

责任编辑 傅道坤

责任印制 程彦红 杨林杰

◆人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

北京艺辉印刷有限公司印刷

◆开本:800×1000 1/16

印张:21.75

字数:434千字  2014年4月第1版

印数:1-3000册  2014年4月北京第1次印刷

定价:49.00元

读者服务热线:(010)81055410 印装质量热线:(010)81055316

反盗版热线:(010)81055315

广告经营许可证:京崇工商广字第0021号

相关图书

51单片机应用开发范例大全(第3版)
51单片机应用开发范例大全(第3版)
例说8051:单片机程序设计案例教程
例说8051:单片机程序设计案例教程
例说51单片机(C语言版)(第3版)
例说51单片机(C语言版)(第3版)

相关文章

相关课程