337
康耘电子 单片机培训教材 (C51 版) www.canwinelec.com

单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

  • Upload
    others

  • View
    13

  • Download
    0

Embed Size (px)

Citation preview

Page 1: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

康耘电子

单片机培训教材(C51 版)

www.canwinelec.com

Page 2: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 2 .

康耘电子单片机开发板配套教材

单片机中级培训教材

MCS-51

单片机培训教材

(C51版)

西安康耘电子有限责任公司

Xi’an Canwin Electronic Co.,Ltd.

http://www.canwinelec.com

Page 3: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 3 .

版权声明

本手册版权归西安康耘电子有限责任公司所有,未经康耘电子

同意,任何单位和个人不得擅自抄录本手册或全部以任何形式用于商

业目的,但可以自由传播。本手册所介绍相关软件版权均归相关公司

所有,这里只供学习使用,若进行实际商业性开发使用请与相关公司

联系购买正版软件。

本手册编制过程中个别电路及程序参考了相关资料,在手册中都

给出了说明,感谢这些资料的提供者!

版权所有 Copyright©2007 西安康耘电子有限责任公司

Copyright©2007 Xi’an Canwin Electronics Co.,Ltd. All Rights Reserved

Page 4: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 4 .

目 录

第1章 单片机开发概述.................................................................................................................91.1 单片机的由来.................................................................................................................. 91.2 主要单片机的分类.......................................................................................................... 91.3 单片机项目开发过程.................................................................................................... 10

第2章 C 语言程序设计................................................................................................................112.1 程序的初步.....................................................................................................................112.2 C语言的数据类型........................................................................................................ 14

2.2.1 整型量..................................................................................................................152.2.2 实型量..................................................................................................................162.2.3 字符型量..............................................................................................................172.2.4 变量的类型转换..................................................................................................18

2.3 基本运算符和表达式................................................................................................... 192.4 输入、输出语句............................................................................................................ 22

2.4.1 数据输出语句......................................................................................................222.4.2 数据输入语句......................................................................................................24

2.5 分支结构程序................................................................................................................ 252.5.1 关系运算符和关系表达式..................................................................................252.5.2 逻辑运算符和表达式..........................................................................................262.5.3 if语句...................................................................................................................262.5.4 条件运算符和条件表达式..................................................................................292.5.5 switch­case 语句.................................................................................................. 30

2.6 循环结构程序................................................................................................................ 312.6.1 while循环语句....................................................................................................312.6.2 do­while循环语句 332.6.3 for循环语句 342.6.4 转移语句 36

2.7 数组 382.7.1 一维数组 382.7.2 二维数组 42

2.8 函数(FUNCTION) 442.8.1 函数概述 442.8.2 函数定义与调用 452.8.3 变量的作用域 48

2.9 位运算 512.10 指针 53

2.10.1 指针的基本概念 532.10.2 指针变量的类型说明 54

Page 5: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 5 .

2.10.3 指针变量的运算................................................................................................542.10.4 数组指针变量的说明和使用............................................................................56

2.11 预处理命令.................................................................................................................. 582.12 VC++开发环境简单应用.............................................................................................. 59

第3章 Keil C 与 ANSI C...........................................................................................................643.1 数据类型........................................................................................................................ 643.2 特殊功能寄存器............................................................................................................ 643.3 存储类型........................................................................................................................ 653.4 指针................................................................................................................................ 683.5 绝对地址访问................................................................................................................ 693.6 使用 Keil C 时应做的和应该避免的.........................................................................693.7 keil C程序举例......................................................................................................... 71

第4章 KEIL 开发环境与ISP编程..............................................................................................734.1 Keil μVision2集成开发环境的简单使用...............................................................73

4.1.1 Keil μVision2 中建立项目的方法...................................................................... 734.1.2 Keil μVision2 中软件调试的方法...................................................................... 78

4.2 STC89C51RC系列单片机的 ISP 编程...........................................................................814.2.1 ISP编程硬件电路...............................................................................................824.2.2 STC_ISP下载软件..............................................................................................82

第5章 STC89C51 系列单片机的结构和原理................................................................................865.1 STC89C51系列单片机的主要性能特点.......................................................................865.2 STC89C51系列单片机的内部结构...............................................................................875.3 STC89C51系列单片机的引脚功能...............................................................................885.4 STC89C51系列单片机的主要组成部分.......................................................................89

5.4.1 CPU...................................................................................................................... 895.4.2 存储器..................................................................................................................905.4.3 I/O接口 92

5.5 时钟电路与时序 955.5.1 时钟电路 955.5.2 有关时序的概念 955.5.3 CPU时序 96

5.6 单片机的复位 975.6.1 复位电路 985.6.2 复位后的状态 98

5.7 低功耗设计 995.8 最小系统设计 100

第6章 单片机简单应用 1016.1 蜂鸣器的驱动 101

6.2 继电器的应用 1026.3 LED指示灯应用 103

Page 6: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 6 .

第7章 LED 显示器及键盘接口技术..........................................................................................1077.1 LED显示器的接口技术............................................................................................... 107

7.1.1 LED显示原理................................................................................................... 1077.1.2 LED显示器的动态扫描驱动方式...................................................................1087.1.3 LED显示器串行驱动方式...............................................................................115

7.2 键盘接口技术.............................................................................................................. 1177.2.1 独立式键盘接口................................................................................................1177.2.2 矩阵式键盘接口................................................................................................122

7.3 键盘显示接口芯片 HD7279......................................................................................... 1377.3.1 HD7279的特点及引脚.....................................................................................1377.3.2 控制指令............................................................................................................1387.3.3 HD7279与单片机的接口及程序设计.............................................................141

第8章 定时/计数器.....................................................................................................................1488.1 定时/计数器 T0和 T1................................................................................................. 148

8.1.1 定时/计数器 T0和 T1的结构及功能..............................................................1488.1.2 定时/计数器 T0和 T1的功能寄存器..............................................................1498.1.3 定时/计数器 T0和 T1的工作模式..................................................................1508.1.4 定时/计数器 T0和 T1应用举例......................................................................152

8.2 定时/计数器 T2............................................................................................................. 1578.2.1 T2控制寄存器 T2CON 和 T2MOD.................................................................1578.2.2 T2的操作模式...................................................................................................158

第9章 中断系统......................................................................................................................... 1639.1 中断控制方式.............................................................................................................. 163

9.1.1 中断的概念........................................................................................................1639.1.2 中断处理过程....................................................................................................164

9.2 52系列单片机的中断系统........................................................................................... 1659.2.1 中断源类型 1659.2.2 中断请求标志 1669.2.3 中断请求控制 1679.2.4 中断处理过程 169

9.3 中断的 C51编程 1719.4 外部中断的扩充 176

第10章 串行通信接口 17710.1 串行通信的基础知识 177

10.1.1 串行通信的基本原理 17710.1.2 RS­232C 串行总线 17810.1.3 串并转换和串行接口 180

10.2 51系列单片机的串行接口 18010.2.1 标准UART 操作基础 18010.2.2 标准UART 工作模式 183

Page 7: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 7 .

10.3 串口通信程序编制.................................................................................................... 18710.3.1 单片机与 PC机通信.......................................................................................18710.3.2 单片机双机通信..............................................................................................195

第11章 A/D 转换器与D/A转换器应用......................................................................................20011.1 A/D转换器接口........................................................................................................... 200

11.1.1 A/D转换器概述..............................................................................................20011.1.2 8 位并行A/D 转换器ADC0809.................................................................... 20111.1.3 12 位A/D 转换器MAX197............................................................................20411.1.4 串行模数转换芯片 TLC0832......................................................................... 207

11.2 D/A转换器接口........................................................................................................... 21211.2.1 D/A转换器的主要性能指标..........................................................................21311.2.2 8位 D/A转换器 DAC0832............................................................................ 21311.2.3 12 位D/A 转换器MAX508............................................................................217

第12章 常用外围芯片资料与编程.............................................................................................22012.1 实时日历/时钟芯片 DS12887...................................................................................22012.2 单总线温度传感器 DS18B20.......................................................................................22412.3 语音芯片 ISD4004及其应用......................................................................................230

12.3.1 引脚功能描述..................................................................................................23012.3.2 工作原理与功能特性......................................................................................23212.3.3 放音应用..........................................................................................................233

第13章 I2C总线.........................................................................................................................23713.1 I2C 总线简介................................................................................................................237

13.1.1 I2C总线的原理................................................................................................23713.1.2 I2C总线上的数据传送....................................................................................23813.1.3 I2C总线竞争的仲裁........................................................................................23913.1.4 模拟 I2C总线...................................................................................................240

13.2 EEPROM存储器 24C02 的应用 24013.2.1 24C02的功能 24013.2.2 24C02的总线特性 24213.2.3 硬件连接及程序 242

13.3 I2C 接口的日历时钟芯片 PCF8563 24713.3.1 PCF8563概述 24713.3.2 PCF8563工作原理 24813.3.3 PCF8563的操作方式 25113.3.4 PCF8563的应用电路与编程 252

第14章 LCD 显示器原理及应用 26014.1 液晶显示模块的原理 26014.2 字符型液晶显示器 RT1602C 261

14.2.1 RT1602C引脚及原理 26114.2.2 RT1602C控制指令及显示地址 262

Page 8: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 8 .

14.2.3 RT1602C与单片机接口及程序.....................................................................26314.3 汉字字模提取............................................................................................................ 26814.4 RT12232B汉字液晶显示器.......................................................................................270

14.4.1 RT12232B引脚说明....................................................................................... 27114.4.2 指令描述........................................................................................................27114.4.3 RT12232B与单片机接口及程序...................................................................273

14.5 KS0108B液晶控制器................................................................................................. 28514.5.1 KS0108B液晶控制器介绍.............................................................................28514.5.2 KS0108B液晶控制器指令系统.....................................................................28614.5.3 与内置KS0108B的液晶模块的接口与编程................................................287

第15章 步进电机控制...............................................................................................................29315.1 步进电机概述............................................................................................................ 29315.2 步进电机原理............................................................................................................ 293

15.2.1 反应式步进电机原理......................................................................................29315.2.2 感应子式步进电机..........................................................................................294

15.3 步进电机应用............................................................................................................ 29515.4 驱动控制系统............................................................................................................ 296

15.4.1 基于ULN2003的驱动方式............................................................................29715.4.2 步进电机细分控制..........................................................................................298

第16章 单片机综合应用实例...................................................................................................30216.1 投篮游戏机控制系统................................................................................................ 302

16.1.1 系统组成及功能..............................................................................................30216.1.2 硬件设计..........................................................................................................30216.1.3 软件设计..........................................................................................................309

16.2 低压侧功率因数补偿系统........................................................................................31816.2.1 系统的硬件组成与功能描述..........................................................................31816.2.2 系统的软件编制 319

附 录 326附录1 基本知识简介 326附录2 计算机总线知识 328附录3 印制电路板设计原则和抗干扰措施 330附录4 快速识别色环电阻 332附录5 ASCII码表与常用库函数 333附录6 单片机课题选编 334

Page 9: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 9 .

第 1章 单片机开发概述

1.1 单片机的由来

单片机专业名称—Micro Controller Unit(微控制器件,MCU), 它是由大名鼎鼎的

INTEL 公司发明的最早的,系列是 MCS-48, 后来有了 MCS-51 ,我们经常说的 51 系列单

片机就是 MCS-51 (micro controller system),它是一种 8 位的单片机。

后来 INTEL 公司把它的核心技术转让给了世界上很多其他公司, 所以世界上就有许

多公司生产 51 系列兼容单片机,比如飞利浦的 87LPC 系列、P89C51系列,STC89C51系列、

华邦的 W78 系列、Cygnal公司的C8051系列等。

目前在我国比较流行的就是美国 ATMEL 公司的 89C51,它是一种带 Flash ROM 的单

片机。讲到这里也许有的人会问:我平时在各种书上看到全是讲解 8031、8051 等型号的

单片机,它们又有什么不同呢?其实它们同属于一个系列,只是 89C51 的单片机更新型一

点,这里随便说一下,目前国内的单片机教材都是以 8051 为蓝本的。

本书主要以 STC89C51 单片机为例进行讲解,该单片机的内核也是8051,但支持教新

的技术,如内部有可用程序读写的 FLASH 存储器、支持 6 时钟、内部含看门狗定时器、

支持ISP程序下载方式等。

1.2 主要单片机的分类

下面介绍一下我们经常在各种刊物上看到的AVR 系列和 PIC 系列单片机是怎么回

事,以便让大家对单片机的发展有一个较全面的认识。在没有学习单片机之前,这是一个

令很多初学者非常困惑的问题,这么多的单片机我该先学哪一种呢?

AVR 系列单片机也是 ATMEL 公司生产的一种 8 位单片机,它采用的是一种叫 RISC

精简指令集单片机的结构,所以它的技术和 51 系列有所不同,开发设备也和 51 系列是

不通用的。它的一条指令的运行速度可以达到纳秒级,即每秒 1000000000 次,是 8 位单

片机中的高端产品。 由于它的出色性能目前应用范围越来越广。

说完了 AVR 的 再来说说另一种--PIC 系列单片机,它是美国 MICROCHIP 公司(微芯

公司)生产的另一种 8 位单片机, 它采用的也是 RISC 的指令集。它的指令系统和开发

工具与 51 系列更是不同,但由于它的低价格和出色性能 ,目前国内使用的人越来越多 ,

国内也有很多的公司在推广它,不过它的影响力远没有 51 系列的大,所以作为初学者,

51 系列当然是首选。

单片机除了以上介绍的 8 位机外,还有16位单片机和32单片机。16位单片机以MCS96

系列为主流,而32位单片机以现在的 ARM 系列处理器为主。

有很多初学者认为,既然有了32位的单片机,8位的单片机就该淘汰了。这是一种错误

的看法,因为不同的单片机有不同的应用领域,比如32位单片机,价格教高,一般用在语

音、图象及大规模数据处理等高端嵌入式系统中(如掌上电脑、MP4、机顶盒等),也就是

Page 10: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 10 .

速度要求教高的场合;而8位的单片机价格低廉,速度适中,多用于性价比教高的场合,如

家用电器、工业控制、仪器仪表、信号采集等。还有,在实际项目开发时,所选的控制器

性能够用就行,不应一味追求高速度与高性能,因为高速度与高性能势必会引起高成本!

就目前行情来看,8位的单片机所占市场份额仍在总数的80%以上,还是微控制器应用

的主力军。而且32位单片机的学习应该在8位单片机的基础上进行,否则很难直接进入高端

开发领域!

1.3 单片机项目开发过程

单片机项目开发是一个复杂的过程,需要的知识很多,不是只学某一门课程或技术就

能够实现的。下面先介绍一下单片机项目开发的一般步骤:

首先根据项目需求选择硬件,比如说根据估算的程序量、速度、内部资源等选择单片

机,因为单片机有很多种类型可供选择,有内部不带存储器的,有内部带 A/D和D/A转换器

的,有带PWM输出的,有内部带FLASH存储器的,有带CAN总线控制器的,数不胜数。选择硬

件还要根据项目需求选择外围器件,比如说想做温度控制,那么温度检测就必不可少,检

测温度的传感器很多,有模拟的,有数字的,要根据所测的温度范围及所需精度选择合适

的温度传感器。

硬件选择完毕,则应根据项目需要做原理图,也就是把单片机及外围器件合理连接以

达到所需目的。做原理图要用专门的电子线路绘图软件,这里我们推荐用PROTEL DXP软件,

该软件是目前主流的电子线路设计软件。绘制完原理图,审查无误就可以将原理图转化为

PCB图,所谓PCB图就是直接可以做线路板的元器件布局及走线图。PCB图制作完毕,即可发

送到厂家生产线路板,线路板完成后即可购买所需元器件进行焊接,将元器件焊接到线路

板上。

焊接完毕,即可进行程序编制。我们推荐用C语言编程,C语言编程可读性好,适合初

学者,也适合做大型项目。编制好程序即可将程序下载到单片机内部存储器进行运行。如

果有问题就分析问题,看是硬件的问题还是软件的问题,是硬件的问题就要借助万用表和

示波器等工具进行分析,如果是软件的问题就更改程序。当所有调试完毕,你的项目就完

成了!!

到现在你应该清楚单片机项目的开发过程了,可以看出,需要的知识很多,选择硬件

需要电路及电子技术知识,选择单片机需要很清楚单片机的种类及各自特点。制作线路板

需要电子线路绘图能力,需要学习PROTEL软件。编制程序需要熟悉开发环境、程序下载方

法,熟悉C语言。还要对计算机操作应该熟练。

Page 11: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 11 .

第 2章 C语言程序设计

当设计一个小的嵌入式系统时一般我们都用汇编语言,在很多工程中这是一个很好的

方法,因为代码一般都不超过8K,而且都比较简单。使用汇编的麻烦在于它的可读性和可

维护性,特别当程序没有很好的标注的时候,代码的可重用性也比较低。如果使用C 的话

可以很好的解决这些问题。

用C 编写的程序,因为C 语言很好的结构性和模块化,更容易阅读和维护。而且由于

模块化,用C 语言编写的程序有很好的可移植性,功能化的代码能够很方便的从一个工程

移植到另一个工程,从而减少了开发时间。

用C 编写程序比汇编更符合人们的思考习惯,开发者可以更专心的考虑算法而不是考

虑一些细节问题,这样就减少了开发和调试的时间。使用像C 这样的语言,程序员不必十

分熟悉处理器的运算过程,这意味着对新的处理器也能很快上手。不必知道处理器的具体

内部结构,使得用C 编写的程序比汇编程序有更好的可移植性,很多处理器支持C 编译器。

所有这些并不说明汇编语言就没了立足之地,很多系统特别是实时时钟系统都是用C和

汇编语言联合编程。对时钟要求很严格时使用汇编语言成了唯一的方法,除此之外,包括

硬件接口的操作都应该用C来编程。C的特点就是可以使你尽量少地对硬件进行操作,是一

种功能性和结构性很强的语言。

2.1 程序的初步

1、void main(void)函数

C程序从main开始执行,前一个void表示无返回值;后一个void表示不传参数。下面给

出一个C语言的完整例子:

# include <stdio.h> /*头文件包含语句*/

int max(int a,int b); /*函数说明*/

void main(void) /*主函数*/

int x,y,z; /*变量说明*/

printf("input two numbers:\n");

scanf("%d%d",&x,&y); /*输入x,y值*/

z=max(x,y); /*调用max函数*/

printf("maxmum=%d",z); /*输出*/

int max(int a,int b) /*定义max函数*/

if(a>b)return a;

else return b; /*把结果返回主调函数*/

Page 12: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 12 .

此函数的功能是输入两个整数,输出其中的大数。

上面例中程序的功能是由用户输入两个整数,程序执行后输出其中较大的数。本程序

由两个函数组成,主函数和max 函数。函数之间是并列关系。可从主函数中调用其它函数。

max 函数的功能是比较两个数,然后把较大的数返回给主函数。max 函数是一个用户自定

义函数。因此在主函数中要给出说明(程序第三行)。可见,在程序的说明部分中,不仅可

以有变量说明,还可以有函数说明。关于函数的详细内容将在以后介绍。在程序的每行后

用/*和*/括起来的内容为注释部分,程序不执行注释部分。

上例中程序的执行过程是,首先在屏幕上显示提示串,请用户输入两个数,回车后由

scanf函数语句接收这两个数送入变量x,y中,然后调用max函数,并把x,y 的值传送给max

函数的参数a,b。在max函数中比较a,b的大小,把大者返回给主函数的变量z,最后在屏幕

上输出z的值。

2、C源程序的结构特点

1)一个C语言源程序可以由一个或多个源文件组成。

2)每个源文件可由一个或多个函数组成。

3)一个源程序不论由多少个文件组成,都有一个且只能有一个main函数,即主函数。

4)源程序中可以有预处理命令(include 命令仅为其中的一种),预处理命令通常应放

在源文件或源程序的最前面。

5)每一个说明,每一个语句都必须以分号结尾。但预处理命令,函数头和花括号“”

之后不能加分号。

6)标识符,关键字之间必须至少加一个空格以示间隔。若已有明显的间隔符,也可不

再加空格来间隔。

3、书写程序时应遵循的规则

从书写清晰,便于阅读,理解,维护的角度出发,在书写程序时 应遵循以下规则:

1)一个说明或一个语句占一行。

2)用 括起来的部分,通常表示了程序的某一层次结构。一般与该结构语句的第

一个字母对齐,并单独占一行。

3)低一层次的语句或说明可比高一层次的语句或说明缩进若干格后书写。以便看起来

更加清晰,增加程序的可读性。在编程时应力求遵循这些规则,以养成良好的编程风格。

4、C语言的字符集

字符是组成语言的最基本的元素。C语言字符集由字母,数字,空格,标点和特殊字

符组成。在字符常量,字符串常量和注释中还可以使用汉字或其它可表示的图形符号。

1)字母 小写字母a~z共26个,大写字母A~Z共26个

2)数字 0~9共10个

3)空白符 空格符、制表符、换行符等统称为空白符。空白符只在字符常量和字符串

常量中起作用。在其它地方出现时,只起间隔作用, 编译程序对它们忽略。因此在程序中

使用空白符与否,对程序的编译不发生影响,但在程序中适当的地方使用空白符将增加程

序的清晰性和可读性。

Page 13: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 13 .

4)标点和特殊字符

5、C语言词汇

在C语言中使用的词汇分为六类:标识符,关键字,运算符,分隔符,常量,注释符

等。

1)标识符

在程序中使用的变量名、函数名、标号等统称为标识符。除库函数的函数名由系统定

义外,其余都由用户自定义。C 规定,标识符只能是字母(A~Z,a~z)、数字(0~9)、下

划线(_)组成的字符串,并且其第一个字符必须是字母或下划线。

以下标识符是合法的:

a,x, 3x,BOOK_1,sum5

以下标识符是非法的:

3s 以数字开头;

s*T 出现非法字符*;

-3x 以减号开头;

bowy-1 出现非法字符-(减号);

在使用标识符时还必须注意以下几点:

(1)标准C不限制标识符的长度,但它受各种版本的C 语言编译系统限制,同时也受到

具体机器的限制。例如在某版本C 中规定标识符前八位有效,当两个标识符前八位相同时,

则被认为是同一个标识符。

(2)在标识符中,大小写是有区别的。例如BOOK和book 是两个不同的标识符。

(3)标识符虽然可由程序员随意定义,但标识符是用于标识某个量的符号。因此,命名

应尽量有相应的意义,以便阅读理解,作到“顾名思义”。

2)关键字

关键字是由C语言规定的具有特定意义的字符串,通常也称为保留字。用户定义的标

识符不应与关键字相同。C语言的关键字分为以下几类:

(1)类型说明符

用于定义、说明变量、函数或其它数据结构的类型。如前面例题中用到的int等。

(2)语句定义符

用于表示一个语句的功能。如前面例中用到的if else就是条件语句的语句定义符。

(3)预处理命令字

用于表示一个预处理命令。如前面例中用到的include。

3)运算符

C语言中含有相当丰富的运算符。运算符与变量,函数一起组成表达式,表示各种运

算功能。运算符由一个或多个字符组成。

4)分隔符

在C语言中采用的分隔符有逗号和空格两种。逗号主要用在类型说明和函数参数表中,

分隔各个变量。空格多用于语句各单词之间,作间隔符。在关键字,标识符之间必须要有

一个以上的空格符作间隔, 否则将会出现语法错误,例如把int a;写成 inta;C编译器会

把inta当成一个标识符处理,其结果必然出错。

Page 14: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 14 .

5)常量

C 语言中使用的常量可分为数字常量、字符常量、字符串常量、符号常量、转义字符

等多种。在第二章中将专门给予介绍。

6)注释符

C 语言的注释符是以“/*”开头并以“*/”结尾的串。在“/*”和“*/”之间的即为

注释。程序编译时,不对注释作任何处理。注释可出现在程序中的任何位置。注释用来向

用户提示或解释程序的意义。在调试程序中对暂不使用的语句也可用注释符括起来,使翻

译跳过不作处理,待调试结束后再去掉注释符。

2.2 C语言的数据类型

在前面,我们已经看到程序中使用的各种变量都应预先加以说明,即先说明,后使用。

本节说明变量的数据类型。

所谓数据类型是按被说明量的性质,表示形式,占据存储空间的多少,构造特点来划

分的。在C语言中,数据类型可分为:基本数据类型,构造数据类型,指针类型,空类型

四大类。

1、基本数据类型

基本数据类型最主要的特点是,其值不可以再分解为其它类型。也就是说,基本数据

类型是自我说明的。

2、构造数据类型构造数据类型

是根据已定义的一个或多个数据类型用构造的方法来定义的。也就是说,一个构造类

型的值可以分解成若干个“成员”或“元素”。每个“成员”都是一个基本数据类型或又

是一个构造类型。在C语言中,构造类型有以下几种:

数组类型

结构类型

联合类型

3、指针类型

指针是一种特殊的,同时又是具有重要作用的数据类型。其值用来表示某个量在内存

储器中的地址。虽然指针变量的取值类似于整型量,但这是两个类型完全不同的量,因此

不能混为一谈。

4、空类型

在调用函数值时,通常应向调用者返回一个函数值。这个返回的函数值是具有一定的

数据类型的,应在函数定义及函数说明中给以说明,例如在例题中给出的 max函数定义中,

函数头为: int max(int a,int b);其中“int ”类型说明符即表示该函数的返回值为整

型量。但是,也有一类函数,调用后并不需要向调用者返回函数值, 这种函数可以定义为

Page 15: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 15 .

“空类型”。其类型说明符为void。在本节中,我们先介绍基本数据类型中的整型、浮点

型和字符型。其余类型在以后各节中陆续介绍。

对于基本数据类型量,按其取值是否可改变又分为常量和变量两种。在程序执行过程

中,其值不发生改变的量称为常量,取值可变的量称为变量。它们可与数据类型结合起来

分类。例如,可分为整型常量、整型变量、浮点常量、浮点变量、字符常量、字符变量、

枚举常量、枚举变量。在程序中,常量是可以不经说明而直接引用的,而变量则必须先说

明后使用。

2.2.1 整型量

整型量包括整型常量、整型变量。整型常量就是整常数。在C语言中,使用的整常数

有八进制、十六进制和十进制三种。

1、整型常量

1)八进制整常数

八进制整常数必须以0开头,即以0作为八进制数的前缀。数码取值为0~7。八进制数

通常是无符号数。

以下各数是合法的八进制数:

015(十进制为13) 0101(十进制为65) 0177777(十进制为65535)

以下各数不是合法的八进制数:

256(无前缀0) 03A2(包含了非八进制数码) -0127(出现了负号)

2)十六进制整常数

十六进制整常数的前缀为0X或0x。其数码取值为0~9,A~F或a~f。

以下各数是合法的十六进制整常数:

0X2A(十进制为42) 0XA0 (十进制为160) 0XFFFF (十进制为65535)

以下各数不是合法的十六进制整常数:

5A (无前缀0X) 0X3H (含有非十六进制数码)

3)十进制整常数

十进制整常数没有前缀。其数码为0~9。

以下各数是合法的十进制整常数:

237 -568 65535 1627

以下各数不是合法的十进制整常数:

023 (不能有前导0) 23D (含有非十进制数码)

在程序中是根据前缀来区分各种进制数的。因此在书写常数时不要把前缀弄错造成结

果不正确。

2、整型变量

整型变量可分为以下几类:

1)基本型

Page 16: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 16 .

类型说明符为int,在内存中占2个字节,其取值为基本整常数。

2)短整量

类型说明符为short int或short'C110F1。所占字节和取值范围均与基本型相同。

3)长整型

类型说明符为long int或long ,在内存中占4个字节,其取值为长整常数。

4)无符号型

类型说明符为unsigned。

无符号型又可与上述三种类型匹配而构成:

无符号基本型 类型说明符为unsigned int或unsigned。

无符号短整型 类型说明符为unsigned short

无符号长整型 类型说明符为unsigned long

各种无符号类型量所占的内存空间字节数与相应的有符号类型量相同。但由于省

去了符号位,故不能表示负数。下表列出了Turbo C中各类整型量所分配的内存字节数

及数的表示范围。

变量说明的一般形式为: 类型说明符 变量名标识符,变量名标识符,...; 例如:

int a,b,c; (a,b,c为整型变量)

long x,y; (x,y为长整型变量)

unsigned p,q; (p,q为无符号整型变量)

在书写变量说明时,应注意以下几点:

1)允许在一个类型说明符后,说明多个相同类型的变量。各变量名之间用逗号间隔。

类型说明符与变量名之间至少用一个空格间隔。

2)最后一个变量名之后必须以“;”号结尾。

3) 变量说明必须放在变量使用之前。一般放在函数体的开头部分。

2.2.2 实型量

1、实型常量

实型也称为浮点型。实型常量也称为实数或者浮点数。在C语言中,实数只采用十进

制。由数码0~9和小数点组成。例如:0.0,.25,5.789,0.13,5.0,300.,-267.8230等

均为合法的实数。

类型说明符 数的范围 分配字节数

int ­32768~32767 2

short int ­32768~32767 2

signed int ­32768~32767 2

unsigned int 0~65535 2

long int ­2147483648~2147483647 4

unsigned long 0~4294967295 4

Page 17: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 17 .

标准C允许浮点数使用后缀。后缀为“f”或“F”即表示该数为浮点数。如356f和

356.0是等价的。

2、实型变量

实型变量分为两类:单精度型和双精度型,其类型说明符为float 单精度说明符,

double 双精度说明符。在Turbo C中单精度型占4个字节(32位)内存空间,其数值范围为

3.4E-38~3.4E+38,只能提供七位有效数字。双精度型占8 个字节(64位)内存空间,其

数值范围为1.7E-308~1.7E+308,可提供16位有效数字。

实型变量说明的格式和书写规则与整型相同。

例如: float x,y; (x,y为单精度实型量)

double a,b,c; (a,b,c为双精度实型量)

实型常数不分单、双精度,都按双精度double型处理。

2.2.3 字符型量

1、字符常量

字符常量是用单引号括起来的一个字符。例如'a','b','=','+','?'都是合法字符常量。

在C语言中,字符常量有以下特点:

1)字符常量只能用单引号括起来,不能用双引号或其它括号。

2)字符常量只能是单个字符,不能是字符串。

3)字符可以是字符集中任意字符。但数字被定义为字符型之后就不能参与数值运算。

如'5'和5 是不同的。'5'是字符常量,不能参与运算。

2、字符变量

字符变量的取值是字符常量,即单个字符。字符变量的类型说明符是char。字符变量

类型说明的格式和书写规则都与整型变量相同。

例如:char a,b; 每个字符变量被分配一个字节的内存空间,因此只能存放一个字符。

字符值是以ASCII码的形式存放在变量的内存单元之中的。如x的十进制ASCII码是120,y的

十进制ASCII码是121。对字符变量a,b赋予'x'和'y'值: a='x';b='y';实际上是在a,b两个

单元内存放120和121的二进制代码: a 0 1 1 1 1 0 0 0

b 0 1 1 1 1 0 0 1

所以也可以把它们看成是整型量。 C语言允许对整型变量赋以字符值,也允许对字符

变量赋以整型值。在输出时, 允许把字符变量按整型量输出,也允许把整型量按字符量输

出。 整型量为二字节量,字符量为单字节量,当整型量按字符型量处理时, 只有低八位

字节参与处理。

main()

char a,b;

a=120;

Page 18: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 18 .

程序的结果为:

a,b

a=120,b=121;

本程序中说明a,b为字符型,但在赋值语句中赋以整型值。从结果看,a,b值的输出

形式取决于printf函数格式串中的格式符,当格式符为"c"时,对应输出的变量值为字符,

当格式符为"d"时,对应输出的变量值为整数。

2.2.4 变量的类型转换

变量的数据类型是可以转换的。转换的方法有两种, 一种是自动转换,一种是强制转

换。

1、自动转换

自动转换发生在不同数据类型的量混合运算时,由编译系统自动完成。自动转换遵循

以下规则:

1)若参与运算量的类型不同,则先转换成同一类型,然后进行运算。

2) 转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,

先把int量转成long型后再进行运算。

3) 所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也

要先转换成double型,再作运算。

4)char型和short型参与运算时,必须先转换成int型。

5)在赋值运算中,赋值号两边量的数据类型不同时, 赋值号右边量的类型将转换为

左边量的类型。 如果右边量的数据类型长度左边长时,将丢失一部分数据,这样会降低精

度, 丢失的部分按四舍五入向前舍入。

本例程序中,PI为实型;s,r为整型。在执行s=r*r*PI语句时,r和PI都转换成double

型计算,结果也为double型。但由于s为整型,故赋值结果仍为整型,舍去了小数部分。

2、强制类型转换

强制类型转换是通过类型转换运算来实现的。其一般形式为:(类型说明符) (表达式)

其功能是把表达式的运算结果强制转换成类型说明符所表示的类型。例如:(float) a

b=121;

printf("("%c,%c\na=%d,b=%d\n",a,b,a,b);

)

void main()

float PI=3.14159;

int s,r=5;

s=r*r*PI;

printf("s=%d\n",s);

Page 19: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 19 .

把a转换为实型,(int)(x+y) 把x+y的结果转换为整型。在使用强制转换时应注意以下问题:

1)类型说明符和表达式都必须加括号(单个变量可以不加括号),如把(int)(x+y)写成

(int)x+y则成了把x转换成int型之后再与y相加了。

2)无论是强制转换或是自动转换,都只是为了本次运算的需要而对变量的数据长度进

行的临时性转换,而不改变数据说明时对该变量定义的类型。

本例表明,f虽强制转为int型,但只在运算中起作用, 是临时的,而f本身的类型并

不改变。因此,(int)f的值为 5(删去了小数)而f的值仍为5.75。

2.3 基本运算符和表达式

C语言中运算符和表达式数量之多,在高级语言中是少见的。正是丰富的运算符和表

达式使C语言功能十分完善。 这也是C语言的主要特点之一。

1、运算符的种类

C语言的运算符可分为以下几类:

1)算术运算符

用于各类数值运算。包括加(+)、减(-)、乘(*)、除(/)、求余(或称模运算,%)、自增

(++)、自减(--)共七种。

2)关系运算符

用于比较运算。包括大于(>)、小于(<)、等于(= =)、 大于等于(>=)、小于等于(<=)

和不等于(!=)六种。

3)逻辑运算符

用于逻辑运算。包括与(&&)、或(||)、非(!)三种。

4)位操作运算符

参与运算的量,按二进制位进行运算。包括位与(&)、位或(|)、位非(~)、位异或(^)、

左移(<<)、右移(>>)六种。

5)赋值运算符

用于赋值运算,分为简单赋值(=)、复合算术赋值(+=,-=,*=,/=,%=)和复合位运算赋值

(&=,|=,^=,>>=,<<=)三类共十一种。

6)条件运算符

这是一个三目运算符,用于条件求值(?:)。

7)逗号运算符

用于把若干表达式组合成一个表达式(,)。

8)指针运算符

用于取内容(*)和取地址(&)二种运算。

main()

float f=5.75;

printf("(int)f=%d,f=%f\n",(int)f,f);

Page 20: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 20 .

9)求字节数运算符

用于计算数据类型所占的字节数(sizeof)。

10)特殊运算符

有括号(),下标[],成员(→,.)等几种。

2、优先级和结合性

在表达式中,优先级较高的先于优先级较低的进行运算。 而在一个运算量两侧的运算

符优先级相同时, 则按运算符的结合性所规定的结合方向处理。

C语言中各运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左)。

例如算术运算符的结合性是自左至右,即先左后右。如有表达式 x-y+z则 y应先与“-”号

结合,执行x-y运算,然后再执行+z的运算。这种自左至右的结合方向就称为“左结合性”。

而自右至左的结合方向称为“右结合性”。 最典型的右结合性运算符是赋值运算符。如

x=y=z,由于“=”的右结合性,应先执行y=z 再执行x=(y=z)运算。

3、算术运算符

1)加法运算符“+”加法运算符为双目运算符,即应有两个量参与加法运算。如a+b,4+8

等。具有右结合性。

2)减法运算符“-”减法运算符为双目运算符。但“-”也可作负值运算符,此时为单

目运算,如-x,-5等具有左结合性。

3)乘法运算符“*”双目运算,具有左结合性。

4)除法运算符“/”双目运算具有左结合性。参与运算量均为整型时, 结果也为整型,

舍去小数。如果运算量中有一个是实型,则结果为双精度实型。

双目运算具有左结合性。参与运算量均为整型时, 结果也为整型,舍去小数。如果运

算量中有一个是实型,则结果为双精度实型。

20/7,-20/7 的结果均为整型,小数全部舍去。而 20.0/7 和-20.0/7 由于有实数参与

运算,因此结果也为实型。

5)求余运算符(模运算符)“%”,双目运算,具有左结合性。要求参与运算的量均为

整型。 求余运算的结果等于两数相除后的余数。

6)自增1,自减1运算符

自增1运算符记为“++”,其功能是使变量的值自增1。自减1 运算符记为“--”,其

功能是使变量值自减1。自增1,自减1 运算符均为单目运算,都具有右结合性。可有以下

几种形式:

++i i 自增 1后再参与其它运算。--i i 自减 1 后再参与其它运算。

i++ i参与运算后,i的值再自增 1。

i-- i参与运算后,i的值再自减 1。

在理解和使用上容易出错的是 i++和 i--。 特别是当它们出在较复杂的表达式或语句

中时,常常难于弄清,因此应仔细分析。

例1:a=1;

b=++a;

其运算过程是a 值加1变为 2,然后再将2 赋值给b,所以 b=2,a=2。

Page 21: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 21 .

例2:a=1;

b=a++;

其运算过程是a原先的值 1,先赋值给b,然后 a再加 1变为 2,所以b=1,a=2。

4、算术表达式

表达式是由常量、变量、函数和运算符组合起来的式子。 一个表达式有一个值及其类

型, 它们等于计算表达式所得结果的值和类型。表达式求值按运算符的优先级和结合性规

定的顺序进行。 单个的常量、变量、函数可以看作是表达式的特例。

算术表达式是由算术运算符和括号连接起来的式子, 以下是算术表达式的例子:

a+b (a*2)/c (x+r)*8-(a+b)/7 ++i sin(x)+sin(y) (++i)-(j++)+(k--)

5、赋值运算符和赋值表达式

简单赋值运算符和表达式,简单赋值运算符记为“=”。由“= ”连接的式子称为赋值

表达式。其一般形式为: 变量=表达式 例如:

x=a+b

w=sin(a)+sin(b)

赋值表达式的功能是计算表达式的值再赋予左边的变量。赋值运算符具有右结合性。

因此a=b=c=5 可理解为a=(b=(c=5))。

在 C 中,把“=”定义为运算符,从而组成赋值表达式。 凡是表达式可以出现的地方

均可出现赋值表达式。例如,式子 x=(a=5)+(b=8)是合法的。它的意义是把 5 赋予a,8赋

予 b,再把 a,b相加,和赋予x ,故 x应等于 13。

如果赋值运算符两边的数据类型不相同, 系统将自动进行类型转换,即把赋值号右边

的类型换成左边的类型。具体规定如下:

1)实型赋予整型,舍去小数部分。

2)整型赋予实型,数值不变,但将以浮点形式存放, 即增加小数部分(小数部分的值

为0)

3)字符型赋予整型,由于字符型为一个字节, 而整型为二个字节,故将字符的ASCII

码值放到整型量的低八位中,高八位为0。

4)整型赋予字符型,只把低八位赋予字符量。

本例表明了上述赋值运算中类型转换的规则。a 为整型,赋予实型量 y值 8.88后只取

void main()

int a,b=322;

float x,y=8.88;

char c1='k',c2;

a=y;

x=b;

a=c1;

c2=b;

printf("%d,%f,%d,%c",a,x,a,c2);

Page 22: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 22 .

整数8。x 为实型,赋予整型量 b值 322, 后增加了小数部分。字符型量 c1赋予 a变为整

型,整型量b赋予 c2 后取其低八位成为字符型(b的低八位为 01000010,即十进制66,按

ASCII码对应于字符 B)。

6、复合赋值符及表达式

在赋值符“=”之前加上其它二目运算符可构成复合赋值符。如 +=,-=,*=,/

=,%=,<<=,>>=,&=,^=,|=。 构成复合赋值表达式的一般形式为: 变量 双目运算符=表达式

它等效于 变量=变量 运算符 表达式 例如: a+=5 等价于 a=a+5 ; x*=y+7 等价于

x=x*(y+7) ; r%=p 等价于r=r%p

复合赋值符这种写法,对初学者可能不习惯, 但十分有利于编译处理,能提高编译效

率并产生质量较高的目标代码。

7、逗号运算符

C语言中逗号“,”也是一种运算符,称为逗号运算符。 其功能是把两个表达式连接

起来组成一个表达式, 称为逗号表达式。

其一般形式为: 表达式1,表达式 2 其求值过程是分别求两个表达式的值,并以表达

式2 的值作为整个逗号表达式的值。

本例中,y 等于整个逗号表达式的值,也就是表达式 2的值,x是第一个表达式的值。

对于逗号表达式还要说明几点:

1)逗号表达式一般形式中的表达式1 和表达式2 也可以又是逗号表达式。例如:

表达式1,(表达式 2,表达式3) 形成了嵌套情形。因此可以把逗号表达式扩展为以下形式:

表达式1,表达式 2,…表达式n。整个逗号表达式的值等于表达式n 的值。

2)程序中使用逗号表达式,通常是要分别求逗号表达式内各表达式的值,并不一定要

求整个逗号表达式的值。

3)并不是在所有出现逗号的地方都组成逗号表达式,如在变量说明中,函数参数表中

逗号只是用作各变量之间的间隔符。

2.4 输入、输出语句

2.4.1 数据输出语句

本小节介绍的是向标准输出设备显示器输出数据的语句。在C语言中,所有的数据输

入/输出都是由库函数完成的。 因此都是函数语句。本小节先介绍 printf 函数。printf

函数称为格式输出函数,其功能是按用户指定的格式,把指定的数据显示到显示器屏幕上。

void main()

int a=2,b=4,c=6,x,y;

y=(x=a+b),(b+c);

printf("y=%d,x=%d",y,x);

Page 23: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 23 .

在前面的例题中我们已多次使用过这个函数。

1、printf函数调用的一般形式

printf函数是一个标准库函数,它的函数原型在头文件“stdio.h”中。但作为一个特

例,不要求在使用 printf 函数之前必须包含 stdio.h 文件。printf 函数调用的一般形式

为: printf(“格式控制字符串”,输出表列)其中格式控制字符串用于指定输出格式。 格

式控制串可由格式字符串和非格式字符串两种组成。格式字符串是以 %开头的字符串,在%

后面跟有各种格式字符,以说明输出数据的类型、形式、长度、小数位数等。如 “%d”表

示按十进制整型输出,“%ld”表示按十进制长整型输出,“%c”表示按字符型输出等。后

面将专门给予讨论。

非格式字符串在输出时原样照印,在显示中起提示作用。 输出表列中给出了各个输出

项, 要求格式字符串和各输出项在数量和类型上应该一一对应。

本例中四次输出了 a,b 的值,但由于格式控制串不同,输出的结果也不相同。第四行

的输出语句格式控制串中,两格式串%d 之间加了一个空格(非格式字符),所以输出的a,b

值之间有一个空格。第五行的 printf语句格式控制串中加入的是非格式字符逗号, 因此

输出的a,b 值之间加了一个逗号。第六行的格式串要求按字符型输出 a,b值。第七行中为

了提示输出结果又增加了非格式字符串。

2、格式字符串

在Turbo C 中格式字符串的一般形式为: [标志][输出最小宽度][.精度][长度]类型

其中方括号[]中的项为可选项。各项的意义介绍如下:

1)类型类型字符用以表示输出数据的类型,其格式符和意义如下表所示:

void main()

int a=88,b=89;

printf("%d %d\n",a,b);

printf("%d,%d\n",a,b);

printf("%c,%c\n",a,b);

printf("a=%d,b=%d",a,b);

表示输出类型的格式字符 格式字符意义

d 以十进制形式输出带符号整数(正数不输出符号)

o 以八进制形式输出无符号整数(不输出前缀 O)

x 以十六进制形式输出无符号整数(不输出前缀 OX)

u 以十进制形式输出无符号整数

f 以小数形式输出单、双精度实数

e 以指数形式输出单、双精度实数

g 以%f%e 中较短的输出宽度输出单、双精度实数

c 输出单个字符

s 输出字符串

Page 24: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 24 .

2)标志

标志字符为-、+、#、空格四种,其意义下表所示:

3)输出最小宽度

用十进制整数来表示输出的最少位数。 若实际位数多于定义的宽度,则按实际位数输

出, 若实际位数少于定义的宽度则补以空格或0。

4)精度

精度格式符以“.”开头,后跟十进制整数。本项的意义是:如果输出数字,则表示小

数的位数;如果输出的是字符, 则表示输出字符的个数;若实际位数大于所定义的精度数,

则截去超过的部分。

5)长度

长度格式符为h,l 两种,h表示按短整型量输出,l表示按长整型量输出。

本例第七行中以四种格式输出整型变量a 的值,其中“%5d ”要求输出宽度为5,而 a

值为15 只有两位故补三个空格。 第八行中以四种格式输出实型量 b的值。其中“%f”和

“%lf ”格式的输出相同,说明“l”符对“f”类型无影响。“%5.4lf”指定输出宽度为5,

精度为4,由于实际长度超过 5故应该按实际位数输出,小数位数超过 4 位部分被截去。第

九行输出双精度实数,“%8.4lf ”由于指定精度为 4位故截去了超过4 位的部分。第十行

输出字符量d,其中“%bc ”指定输出宽度为 8故在输出字符p 之前补加7 个空格。

2.4.2 数据输入语句

C语言的数据输入也是由函数语句完成的。 本节介绍从标准输入设备—键盘上输入数

据的函数scanf。scanf函数称为格式输入函数,即按用户指定的格式从键盘上把数据输入

到指定的变量之中。

scanf函数是一个标准库函数,它的函数原型在头文件“stdio.h”中,与 printf函数

标志格式字符 含 义

- 结果左对齐,右边填空格

+ 输出符号(正号或负号)空格输出值为正时冠以空格,为负时冠以负号

# 对 c,s,d,u类无影响;对 o类,在输出时加前缀 o;对 x类,在输出时加前缀

0x;对 e,g,f 类当结果有小数时才给出小数点

void main()

int a=15;

float b=138.3576278;

double c=35648256.3645687;

char d='p';

printf("a=%d,%5d,%o,%x\n",a,a,a,a);

printf("b=%f,%lf,%5.4lf,%e\n",b,b,b,b);

printf("c=%lf,%f,%8.4lf\n",c,c,c);

printf("d=%c,%8c\n",d,d);

Page 25: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 25 .

相同,C语言也允许在使用scanf函数之前不必包含 stdio.h文件。scanf函数的一般形式

为: scanf(“格式控制字符串”,地址表列); 其中,格式控制字符串的作用与printf函

数相同,但不能显示非格式字符串, 也就是不能显示提示字符串。地址表列中给出各变量

的地址。 地址是由地址运算符“&”后跟变量名组成的。例如,&a,&b分别表示变量 a和变

量b 的地址。这个地址就是编译系统在内存中给 a,b变量分配的地址。

在C语言中,使用了地址这个概念,这是与其它语言不同的。 应该把变量的值和变量

的地址这两个不同的概念区别开来。变量的地址是 C 编译系统分配的,用户不必关心具体

的地址是多少。在赋值表达式中给变量赋值,如: a=567 在赋值号左边是变量名,不能写

地址,而scanf函数在本质上也是给变量赋值,但要求写变量的地址,如&a。 这两者在形

式上是不同的。&是一个取地址运算符,&a是一个表达式,其功能是求变量的地址。

在本例中,由于 scanf函数本身不能显示提示串,故先用 printf语句在屏幕上输出提

示,请用户输入a、b、c 的值。执行scanf 语句,则退出TC屏幕进入用户屏幕等待用户输

入。用户输入7、8、9 后按下回车键,此时,系统又将返回TC屏幕。在 scanf语句的格式

串中由于没有非格式字符在“%d%d%d”之间作输入时的间隔, 因此在输入时要用一个以上

的空格或回车键作为每两个输入数之间的间隔。

如: 7 8 9

7

8

9

2.5 分支结构程序

2.5.1 关系运算符和关系表达式

在程序中经常需要比较两个量的大小关系, 以决定程序下一步的工作。比较两个量的

运算符称为关系运算符。 在C语言中有以下关系运算符:

< 小于

<= 小于或等于

> 大于

>= 大于或等于

== 等于

!= 不等于

void main()

int a,b,c;

printf("input a,b,c\n");

scanf("%d%d%d",&a,&b,&c);

printf("a=%d,b=%d,c=%d",a,b,c);

Page 26: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 26 .

关系运算符都是双目运算符,其结合性均为左结合。 关系运算符的优先级低于算术运

算符,高于赋值运算符。 在六个关系运算符中,<,<=,>,>=的优先级相同,高于==和!=,

==和!=的优先级相同。

关系表达式的一般形式为: 表达式 关系运算符 表达式

例如:a+b>c-d,x>3/2,'a'+1<c,-i-5*j==k+1;都是合法的关系表达式。由于表达式也

可以又是关系表达式。 因此也允许出现嵌套的情况,例如:a>(b>c),a!=(c==d)等。关系

表达式的值是“真”和“假”,用“1”和“0”表示。

如: 5>0的值为“真”,即为 1。(a=3)>(b=5)由于 3>5 不成立,故其值为假,即为0。

2.5.2 逻辑运算符和表达式

C语言中提供了三种逻辑运算符: && 与运算 || 或运算 ! 非运算

与运算符&&和或运算符||均为双目运算符。具有左结合性。 非运算符!为单目运算符,

具有右结合性。逻辑运算符和其它运算符优先级的关系可表示如下:

a>b && c>d等价于(a>b) && (c>d)

!b==c||d<a等价于((!b)==c)||(d<a)

a+b>c && x+y<b等价于((a+b)>c) && ((x+y)<b)

1、逻辑运算的值

逻辑运算的值也为“真”和“假”两种,用“1”和“0 ”来表示。其求值规则如下

1)与运算&&参与运算的两个量都为真时,结果才为真,否则为假。例如,5>0 && 4>2,

由于 5>0为真,4>2 也为真,相与的结果也为真。

2)或运算||参与运算的两个量只要有一个为真,结果就为真。 两个量都为假时,结果

为假。例如:5>0||5>8,由于 5>0为真,相或的结果也就为真

3)非运算!参与运算量为真时,结果为假;参与运算量为假时,结果为真。

例如:!(5>0)的结果为假。

2、逻辑表达式

逻辑表达式的一般形式为: 表达式 逻辑运算符 表达式 其中的表达式可以又是逻辑表

达式,从而组成了嵌套的情形。例如:(a&&b)&&c 根据逻辑运算符的左结合性,上式也可写为:

a&&b&&c 逻辑表达式的值是式中各种逻辑运算的最后值,以“1”和“0”分别代表“真”和

“假”。

2.5.3 if语句

1、if (条件表达式)

动作

如果条件表达式的值为真(非零的数),则执行 内的动作,如果条件表达式为假(值

为0),则略过该动作而继续往下执行。

Page 27: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 27 .

2、 if(条件表达式)

动作 1

else

动作 2

如果条件表达式的值为真(非零的数),则执行动作1,略过else 的部分,接着向下执

行;如果条件表达式为假(值为 0),则略过 if 的部分而执行 else 的动作 2,然后往下执

行。

范例1:(判断从键盘输入的数是奇数还是偶数,是偶数则显示“是偶数”,否则显示“是

奇数”)

3、if (条件表达式1)

if (条件表达式2)

if (条件表达式3)

动作 A

else

动作 B

else

动作 C

else

动作 D

动作 A:是条件表达式1、2、3 都成立时才执行;

动作B:是条件表达式1、2 成立,但条件表达式3 不成立时才执行;

动作C:是条件表达式1 成立,条件表达式2 不成立时才执行;

动作D:是条件表达式1 不成立时才执行。

范例2:输入3 个整数,输出最大数。

void main(void)

unsigned char dat;

scanf(“%d”,&dat);

if(dat%2==0)

printf(“是偶数”);

else

printf(“是奇数”);

main()

int a,b,c;

Scanf(“%d,%d,%d”,&a,&b,&c);

if(a<b)

Page 28: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 28 .

4、if (条件表达式1)

动作 A

else if (条件表达式2)

动作 B

else if (条件表达式3)

动作 C

else

动作 D

动作 A:是条件表达式1 成立时立即执行;

动作B:是条件表达式1 不成立,但条件表达式2 成立时才执行;

动作C:是条件表达式1、2 不成立,条件表达式3 成立时才执行;

动作D:是条件表达式1、2、3 都不成立时才执行。

范例3:(输入三个整数,输出最大数和最小数)

本程序中,首先比较输入的 a,b的大小,并把大数装入 max, 小数装入min 中,然后

再与c 比较,若max 小于 c,则把 c赋予 max;如果c 小于min,则把 c赋予 min。因此max

if(b<c)

printf(“%d”,c);

else

printf(“%d”,b);

else

if(a<c)

printf(“%d”,c);

else

printf(“%d”,a);

void main()

int a,b,c,max,min;

printf("input three numbers: ");

scanf("%d%d%d",&a,&b,&c);

if(a>b)

max=a;min=b;

else

max=b;min=a;

if(max<c)

max=c;

else if(min>c)

min=c;

printf("max=%d\nmin=%d",max,min);

Page 29: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 29 .

内总是最大数,而min 内总是最小数。最后输出max和 min的值即可。

在使用if语句中还应注意以下问题:

(1) 在各种形式的 if 语句中,在 if关键字之后均为表达式。 该表达式通常是逻辑

表达式或关系表达式, 但也可以是其它表达式,如赋值表达式等,甚至也可以是一个变量。

例如: if(a=5) 语句;if(b) 语句; 都是允许的。只要表达式的值为非 0,即为“真”。

如在if(a=5)…;中表达式的值永远为非0,所以其后的语句总是要执行的,当然这种情况

在程序中不一定会出现,但在语法上是合法的。

又如,有程序段:

本语句的语义是,把b值赋予 a,如为非0则输出该值,否则输出“a=0”字符串。这

种用法在程序中是经常出现的。

(2) 在 if语句中,条件判断表达式必须用括号括起来, 在语句之后必须加分号。

(3) 在 if 语句的三种形式中,所有的语句应为单个语句,如果要想在满足条件时执

行一组(多个)语句,则必须把这一组语句用 括起来组成一个复合语句。但要注意的是在

之后不能再加分号。

例如:

2.5.4 条件运算符和条件表达式

如果在条件语句中,只执行单个的赋值语句时, 常可使用条件表达式来实现。不但使

程序简洁,也提高了运行效率。

条件运算符为?和:,它是一个三目运算符,即有三个参与运算的量。由条件运算符组成

条件表达式的一般形式为:

表达式1? 表达式 2: 表达式3

其求值规则为:如果表达式 1的值为真,则以表达式 2 的值作为条件表达式的值,否

则以表达式2 的值作为整个条件表达式的值。 条件表达式通常用于赋值语句之中。例如条

件语句:

if(a>b) max=a;

if(a=b)

printf("%d",a);

else

printf("a=0");

if(a>b)

a++;

b++;

else

a=0;

b=10;

Page 30: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 30 .

else max=b;

可用条件表达式写为 max=(a>b)?a:b; 执行该语句的语义是:如 a>b为真,则把 a赋

予max,否则把 b 赋予max。

使用条件表达式时,还应注意以下几点:

1)条件运算符的运算优先级低于关系运算符和算术运算符,但高于赋值符。因此

max=(a>b)?a:b可以去掉括号而写为 max=a>b?a:b

2) 条件运算符?和:是一对运算符,不能分开单独使用。

3) 条件运算符的结合方向是自右至左。

例如:a>b?a:c>d?c:d应理解为a>b?a:(c>d?c:d)。

这也就是条件表达式嵌套的情形,即其中的表达式3又是一个条件表达式。

2.5.5 switch­case 语句

switch ( 条件表达式 )

case 条件值1:

动作1

break;

case 条件值2:

动作2

break;

case 条件值3:

动作3

break;

default:

动作4

break;

switch内的条件表达式的结果必须为整数或字符。 Switch 以条件表达式的值来与各

case的条件值对比,如果与某个条件值相符合,则执行该 case 的动作。如果所有的条件值

都不符合,则执行 default的动作。每个动作之后一定要写 break,否则会有错误。另外,

case之后的条件值必须是数据常数,不能是变量,而且不可以重复,即条件值必须各不同,

如果有数种case 所做的动作一样时,也可以写在一起,即上下并列。一般程序必须做多选

一时,可以采用switch 语句。

Break:是跳出循环的指令,任何由 switch、for、while、do-while 构成的循环,都

可以用break 来跳出,必须注意的是break一次只能跳出一层循环,通常都和if 连用,当

某些条件成立后就跳出循环。

Default:当所有的 case 的条件值都不成立时,就执行 default 所指定的动作,做完

后也要使用break 指令跳出switch循环。

Page 31: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 31 .

在使用switch语句时还应注意以下几点:

1.在 case后的各常量表达式的值不能相同,否则会出现错误。

2.在 case后,允许有多个语句,可以不用括起来。

3.各 case和 default子句的先后顺序可以变动,而不会影响程序执行结果。

4.default子句可以省略不用。程序举例

练习题:

1、给出一个不多于 5 位的正整数,求出它是几位数,分别打印出每一位数字,然后再

按逆序打印出各位数字。

2、有分段函数:y=

)0()0(0)0(12

xxxxx

编写程序,输入一个x值,计算输出相应的 y值。

2.6 循环结构程序

循环结构是程序中一种很重要的结构。其特点是, 在给定条件成立时,反复执行某

程序段,直到条件不成立为止。 给定的条件称为循环条件,反复执行的程序段称为循环体。

C语言提供了多种循环语句,可以组成各种不同形式的循环结构。

2.6.1 while 循环语句

while ( 条件表达式 )

动作

void main() //功能:输入一个数字,输出该数字对应的星期

int a;

scanf("%d",&a);

switch (a)

case 1:printf("Monday\n");break;

case 2:printf("Tuesday\n"); break;

case 3:printf("Wednesday\n");break;

case 4:printf("Thursday\n");break;

case 5:printf("Friday\n");break;

case 6:printf("Saturday\n");break;

case 7:printf("Sunday\n");break;

default:printf("error\n");

Page 32: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 32 .

先测试条件表达式是否成立,当条件表达式为真时,则执行循环内动作,做完后又继

续跳到条件表达式做测试。如此反复直到条件表达式为假为止。使用时要避免条件永真,

造成死循环。

范例1:(求 1~100之间所有整数的和,结果放入sum 变量中)

范例2:用公式 ,求 的近似值,直到最后一项的绝对值小于71

51

31

14

为止。610

使用while 语句应注意以下几点:

1)while语句中的表达式一般是关系表达或逻辑表达式,只要表达式的值为真 (非 0)

即可继续循环。

main()

unsigned int dat , sum;

sum=0;

dat=1;

while(dat<=100)

sum+=dat;

dat++;

#include<math.h>

main()

double s=0,x=1;

long k=1;

int sign=1;

while(fabs(x)>1e-6) /*当 X 的绝对值小于 时,退出循环*/610

s+=x;

k+=2;

sign*=-1;

x=(double)sign/k;

s*=4;

prinf(“%f\n”,s);

void main()

int a=0,n;

printf("\n input n: ");

Page 33: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 33 .

本例程序将执行n 次循环,每执行一次,n 值减1。循环体输出表达式 a++*2 的值。该

表达式等效于(a*2;a++)

2) 循环体如包括有一个以上的语句,则必须用括起来, 组成复合语句。

3) 应注意循环条件的选择以避免死循环。

本例中while 语句的循环条件为赋值表达式a=5, 因此该表达式的值永远为真,而循

环体中又没有其它中止循环的手段, 因此该循环将无休止地进行下去,形成死循环。

4)允许while 语句的循环体又是while语句,从而形成双重循环。

2.6.2 do­while 循环语句

do 动作

while ( 条件表达式 );

先执行动作后,再测试条件表达式是否成立。当条件表达式为真,则继续回到前面执

行动作,如此反复直到条件表达式为假为止。不论条件表达式的结果为何,至少会做一次

动作,使用时要避免条件永真,造成死循环。

范例:计算正整数n的各位上的数字之和。

对于do-while语句还应注意以下几点:

1)在 if语句,while语句中, 表达式后面都不能加分号, 而在 do-while语句的表

达式后面则必须加分号。

scanf("%d",&n);

while (n--)

printf("%d ",a++*2);

void main()

int a,n=0;

while(a=5)

printf("%d ",n++);

main()

long n,k=1;

scanf(“%ld”,&n);

do

k=k*(n%10);

k=n/10;

while(n);

printf(“%ld\n”,k);

Page 34: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 34 .

2)do-while语句也可以组成多重循环,而且也可以和 while语句相互嵌套。

3)在do 和 while之间的循环体由多个语句组成时,也必须用括起来组成一个复合

语句。

4)do-while和 while语句相互替换时,要注意修改循环控制条件。

2.6.3 for循环语句

for (表达式1;表达式 2;表达式3)

循环体

for语句的语义是:

1)首先计算表达式1 的值(通常是设定起始值)。

2)再计算表达式2(通常是条件判断式)的值,若值为真(非 0)则执行循环体一次,否

则跳出循环。

3)然后再计算表达式 3(通常是步长表达式)的值,转回第 2 步重复执行。在整个

for循环过程中,表达式1 只计算一次,表达式2和表达式3 则可能计算多次。循环体可能

多次执行,也可能一次都不执行。

范例1:(求 1~100之间所有整数的和,结果放入sum 变量中)

范例2:打印出所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字

立方和等于该数本身。例如:153是一个“水仙花数”,因为 153=1*1*1+5*5*5+3*3*3。

void main()

unsigned char dat , sum;

sum=0;

for(dat=1;dat<=10;dat++)

sum+=dat;

main()

int i,j,k,n;

for(n=100;n<1000;n++)

i=n/100;

j=(n-i*100)/10;

k=n%10;

if(i*i*i+j*j*j+k*k*k= =n)

printf("%d\n",n);

Page 35: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 35 .

范例3:用for 语句实现:从0 开始,输出n 个连续的偶数。

本例的for 语句中,表达式 1 已省去,循环变量的初值在 for 语句之前由 scanf语句

取得,表达式3是一个逗号表达式,由a++,n-- 两个表达式组成。每循环一次a 自增1,n

自减 1。a 的变化使输出的偶数递增,n的变化控制循次数。

范例4:输出 9*9口诀。(分行与列考虑,共9 行 9 列,i控制行,j 控制列)

在使用for 语句中要注意以下几点

1)for语句中的各表达式都可省略,但分号间隔符不能少。如:for(;表达式;表达

式)省去了表达式1。for(表达式;;表达式)省去了表达式 2。

for(表达式;表达式;)省去了表达式3。for(;;)省去了全部表达式。

2)在循环变量已赋初值时,可省去表达式 1,如上例即属于这种情形。如省去表达式2

或表达式3 则将造成无限循环,这时应在循环体内设法结束循环。

for语句也可与 while,do-while语句相互嵌套,构成多重循环。以下形成都合法

的嵌套。

(1)for()…

while()

void main(void)

int a=0,n;

printf("\n input n: ");

scanf("%d",&n);

for( ;n>0;a++,n--)

printf("%d ",a*2);

#include "stdio.h"

main()

int i,j,result;

printf("\n");

for (i=1;i<10;i++)

for(j=1;j<10;j++)

result=i*j;

printf("%d*%d=%-3d",i,j,result);/*-3d 表示左对齐,占 3位*/

printf("\n");/*每一行后换行*/

Page 36: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 36 .

(2)do

for()

while();

(3)while()

for()

(4)for()

for()

2.6.4 转移语句

程序中的语句通常总是按顺序方向, 或按语句功能所定义的方向执行的。如果需要改

变程序的正常流向, 可以使用本小节介绍的转移语句。在C语言中提供了4种转移语句:

gotu, break, continue和return。

其中的return语句只能出现在被调函数中, 用于返回主调函数,我们将在函数一节中具体

介绍。 本小节介绍前三种转移语句。

1、goto语句

goto语句也称为无条件转移语句,其一般格式如下: goto 语句标号; 其中语句标号

是按标识符规定书写的符号, 放在某一语句行的前面,标号后加冒号(:)。语句标号起标

识语句的作用,与goto 语句配合使用。

goto语句的语义是改变程序流向, 转去执行语句标号所标识的语句。goto语句通常与

条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。但是,在结构

化程序设计中一般不主张使用goto语句, 以免造成程序流程的混乱,使理解和调试程序都

产生困难。

范例:统计100以内能被7整除的数据个数。

#include"stdio.h"

Page 37: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 37 .

2、break语句

break语句只能用在switch 语句或循环语句中, 其作用是跳出switch语句或跳出本层

循环,转去执行后面的程序。由于break语句的转移方向是明确的,所以不需要语句标号与

之配合。break语句的一般形式为: break; 上面例题中分别在switch语句和for语句中使

用了break 语句作为跳转。使用break语句可以使循环语句有多个出口,在一些场合下使编

程更加灵活、方便。

范例:已知sum=1+2+3+…+i, 求sum大于100时,i的最小值。

3、continue语句

continue语句只能用在循环体中,其一般格式是:

continue;

其语义是:结束本次循环,即不再执行循环体中continue 语句之后的语句,转入下一

次循环条件的判断与执行。应注意的是, 本语句只结束本层本次的循环,并不跳出循环。

void main()

int n=1,num=0;

loop: if(n<100)

if(n%7==0)

num++;

n++;

goto loop;

printf("%d",num);

main()

int i=1,sum=0;

while(i<10)

sum+=i++;

if(sum>20)

break;

printf(“%d”,i);

void main() // 输出100以内能被7整除的数。

int n;

for(n=7;n<=100;n++)

if (n%7!=0)

Page 38: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 38 .

本例中,对7~100的每一个数进行测试,如该数不能被7整除,即模运算不为0,则由

continus语句转去下一次循环。只有模运算为0时,才能执行后面的printf语句,输出能被

7整除的数。

练习题:

1、输出菲波那契数列的前20项。即1,1,2,3,5,8,13,21…

2、给定一个整数m,判断其是否为素数。所谓素数是指除了1和它本身再没有其它因子

的数,如3,5,7,11,13,17等。

3、求下面分数序列的前n项之和:1/2,2/3,3/5,5/8,8/13,…

4、计算正整数n所有因子(1和n除外)之和。

5、给定n的值,计算1+(1+2)+(1+2+3)+(1+2+3+4)+…+(1+2+…+n)的和。

6、编写程序,找出1000以内所有的“完数”,所谓“完数”是指一个数是其所有因子

之和,例如6=1+2+3,所以6是一个“完数”。

2.7 数组

在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来。

这些按序排列的同类数据元素的集合称为数组。一个数组可以分解为多个数组元素,这些

数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不同,数组又可分为

数值数组、字符数组、指针数组、结构数组等各种类别。本节主要介绍数值数组。

2.7.1 一维数组

1、数组类型说明

在C语言中使用数组必须先进行类型说明。 数组说明的一般形式为:

类型说明符 数组名 [常量表达式],……;

其中,类型说明符是任一种基本数据类型或构造数据类型。 数组名是用户定义的数组

标识符。 方括号中的常量表达式表示数据元素的个数,也称为数组的长度。

例如:

int a[10]; 说明整型数组a,有 10个元素。

float b[10],c[20]; 说明实型数组b,有 10个元素,实型数组c,有 20个元素。

对于数组类型说明应注意以下几点:

1)数组的类型实际上是指数组元素的取值类型。对于同一个数组,其所有元素的数据

类型都是相同的。

continue;

printf("%d ",n);

Page 39: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 39 .

2)数组名的书写规则应符合标识符的书写规定。

3)数组名不能与其它变量名相同,例如:

void main()

int a;

float a[10];

……

是错误的。

4)方括号中常量表达式表示数组元素的个数,如a[5]表示数组 a有 5个元素。但是其

下标从0 开始计算。因此5个元素分别为 a[0],a[1],a[2],a[3],a[4]。

5)不能在方括号中用变量来表示元素的个数, 但是可以是符号常数或常量表达式。

例如:

#define FD 5

void main()

int a[3+2],b[7+FD];

……

是合法的。但是下述说明方式是错误的。

void main()

int n=5;

int a[n];

……

6)允许在同一个类型说明中,说明多个数组和多个变量。

例如: int a,b,c,d,k1[10],k2[20];

2、数组元素的表示方法

数组元素是组成数组的基本单元。数组元素也是一种变量, 其标识方法为数组名后跟

一个下标。下标表示了元素在数组中的顺序号。数组元素的一般形式为:数组名[下标] 其

中的下标只能为整型常量或整型表达式。如为小数时, C 编译将自动取整。例如,

a[5],a[i+j],a[i++]都是合法的数组元素。 数组元素通常也称为下标变量。必须先定义数

组, 才能使用下标变量。在C语言中只能逐个地使用下标变量, 而不能一次引用整个数

组。 例如,输出有10 个元素的数组必须使用循环语句逐个输出各下标变量:

for(i=0; i<10; i++)

printf("%d",a[i]);

范例 1:

void main()

Page 40: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 40 .

本例中用一个循环语句给 a数组各元素送入奇数值,然后用第二个循环语句从大到小

输出各个奇数。在第一个 for 语句中,表达式3省略了。在下标变量中使用了表达式i++,

用以修改循环变量。当然第二个for 语句也可以这样作, C语言允许用表达式表示下标。

给数组赋值的方法除了用赋值语句对数组元素逐个赋值外, 还可采用初始化赋值和动

态赋值的方法。组初始化赋值是指在数组说明时给数组元素赋予初值。 数组初始化是在编

译阶段进行的。这样将减少运行时间,提高效率。

初始化赋值的一般形式为: 类型说明符 数组名[常量表达式]=值,值……值;在

中的各数据值即为各元素的初值, 各值之间用逗号间隔。例如:

int a[10]= 0,1,2,3,4,5,6,7,8,9 ; 相当于a[0]=0;a[1]=1...a[9]=9;

C语言对数组的初始赋值还有以下几点规定:

1)可以只给部分元素赋初值。当 中值的个数少于元素个数时,只给前面部分元素

赋值。例如: static int a[10]=0,1,2,3,4;表示只给a[0]~a[4]5个元素赋值,而后 5

个元素自动赋0 值。

2)只能给元素逐个赋值,不能给数组整体赋值。 例如给十个元素全部赋 1 值,只能

写为: int a[10]=1,1,1,1,1,1,1,1,1,1;而不能写为: int a[10]=1;

3)如不给可初始化的数组赋初值,则全部元素均为0 值。

4)如给全部元素赋值,则在数组说明中, 可以不给出数组元素的个数。例如:static

int a[5]=1,2,3,4,5;可写为: static int a[]=1,2,3,4,5;

范例 2:对 10个数进行排序

程序分析:可以利用选择法,即从后 9 个比较过程中,选择一个最小的与第一个元素

交换,

下次类推,即用第二个元素与后8 个进行比较,并进行交换。

int i,a[10];

for(i=0;i<10;)

a[i++]=2*i+1;

for(i=9;i>=0;i--)

printf("%d",a[i]);

#define N 10

main()

int i,j,min,tem,a[N];

printf("please input ten num:\n");

for(i=0;i<N;i++) //循环输入 10 个数

scanf("%d",&a[i]);

for(i=0;i<N-1;i++) //排序

min=i;

for(j=i+1;j<N;j++)

if(a[min]>a[j])

Page 41: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 41 .

范例3:输出数组中最大的数

本例程序中第一个 for语句逐个输入 10 个数到数组 a中。 然后把 a[0]送入max 中。在

第二个 for语句中,从 a[1]到 a[9]逐个与 max中的内容比较,若比 max的值大,则把该下标

变量送入max 中,因此max 总是在已比较过的下标变量中为最大者。比较结束,输出max的值。

范例4:将一个数组逆序排列后再存入该数组,并输出。

程序分析:用第一个与最后一个交换,第二个与倒数第二个交换,依次类推。

min=j;

tem=a[i];

a[i]=a[min];

a[min]=tem;

printf("After sorted \n"); //输出排序后的数据

for(i=0;i<N;i++)

printf("%5d",a[i]);

void main( )

int i,max,a[10];

printf("input 10 numbers:\n");

for(i=0;i<10;i++)

scanf("%d",&a[i]);

max=a[0];

for(i=1;i<10;i++)

if(a[i]>max)

max=a[i];

printf("maxmum=%d\n",max);

#define N 5

main( )

int a[N]=9,6,5,4,1,i,temp;

printf("\n original array:\n"); //显示原数组

for(i=0;i<N;i++)

printf("%4d",a[i]);

for(i=0;i<N/2;i++) //逆序排列

temp=a[i];

a[i]=a[N-i-1];

a[N-i-1]=temp;

Page 42: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 42 .

2.7.2 二维数组

前面介绍的数组只有一个下标,称为一维数组, 其数组元素也称为单下标变量。在实

际问题中有很多量是二维的或多维的, 因此C语言允许构造多维数组。多维数组元素有多

个下标, 以标识它在数组中的位置,所以也称为多下标变量。 本小节只介绍二维数组,

多维数组可由二维数组类推而得到。二维数组类型说明二维数组类型说明的一般形式是:

类型说明符 数组名[常量表达式1][常量表达式 2]…;

其中常量表达式 1表示第一维下标的长度,常量表达式 2 表示第二维下标的长度。例

如:

int a[3][4]; 说明了一个三行四列的数组,数组名为 a,其下标变量的类型为整型。该数

组的下标变量共有3×4 个,即: a[0][0],a[0][1],a[0][2],a[0][3]

a[1][0],a[1][1],a[1][2],a[1][3],

a[2][0],a[2][1],a[2][2],a[2][3]

二维数组在概念上是二维的,即是说其下标在两个方向上变化, 下标变量在数组中的

位置也处于一个平面之中, 而不是象一维数组只是一个向量。但是,实际的硬件存储器却

是连续编址的, 也就是说存储器单元是按一维线性排列的。 如何在一维存储器中存放二

维数组,可有两种方式:一种是按行排列, 即放完一行之后顺次放入第二行。另一种是按

列排列, 即放完一列之后再顺次放入第二列。在C语言中,二维数组是按行排列的

1、二维数组元素的表示方法

二维数组的元素也称为双下标变量,其表示的形式为: 数组名[下标][下标] 其中下

标应为整型常量或整型表达式。例如: a[3][4] 表示 a 数组三行四列的元素。下标变量和

数组说明在形式中有些相似,但这两者具有完全不同的含义。 数组说明的方括号中给出的

是某一维的长度,即可取下标的最大值; 而数组元素中的下标是该元素在数组中的位置标

识。前者只能是常量, 后者可以是常量,变量或表达式。

2、二维数组的初始化

二维数组初始化也是在类型说明时给各下标变量赋以初值。 二维数组可按行分段赋

值,也可按行连续赋值。 例如对数组a[5][3]:

1)按行分段赋值可写为

int a[5][3]= 80,75,92,61,65,71,59,63,70,85,87,90,76,77,85 ;

2)按行连续赋值可写为

int a[5][3]= 80,75,92,61,65,71,59,63,70,85,87,90,76,77,85 ;

printf("\n sorted array:\n"); //输出逆序的数组

for(i=0;i<N;i++)

printf("%4d",a[i]);

Page 43: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 43 .

这两种赋初值的结果是完全相同的。

对于二维数组初始化赋值还有以下说明:

1)可以只对部分元素赋初值,未赋初值的元素自动取0值。

例如: int a[3][3]=1,2,3; 是对每一行的第一列元素赋值,未赋值的元素取 0

值。 赋值后各元素的值为: 1 0 0 2 0 0 3 0 0

int a [3][3]=0,1,0,0,2,3; 赋值后的元素值为 0 1 0 0 0 2 3 0 0

2)如对全部元素赋初值,则第一维的长度可以不给出。

例 如 : int a[3][3]=1,2,3,4,5,6,7,8,9; 可 以 写 为 : int

a[][3]=1,2,3,4,5,6,7,8,9;

数组是一种构造类型的数据。 二维数组可以看作是由一维数组的嵌套而构成的。设一

维数组的每个元素都又是一个数组, 就组成了二维数组。当然,前提是各元素类型必须相

同。根据这样的分析,一个二维数组也可以分解为多个一维数组。 C语言允许这种分解有

二维数组 a[3][4],可分解为三个一维数组,其数组名分别为 a[0],a[1],a[2]。对这三个

一维数组不需另作说明即可使用。这三个一维数组都有 4个元素,例如:一维数组 a[0]的

元素为 a[0][0],a[0][1],a[0][2],a[0][3]。

必须强调的是,a[0],a[1],a[2]不能当作下标变量使用,它们是数组名,不是一个单

纯的下标变量。

范例:将一个二维数组行和列元素互换,存到另外一个二维数组中。

例如a 是原数组,b是变换之后的数组。a= 则 b=

654321

635241

main()

int a[2][3]=1,2,3,4,5,6;

int b[3][2],i,j;

printf(“array a:\n”);

for(i=0;i<2;i++)

for(j=0;j<3;j++)

printf(“%5d”,a[i][j]); /*输出数组 a*/

b[j][i]=a[i][j]; /*行列交换*/

printf(“\n”); /*输出一行后换行*/

printf(“array b:\n”);

for(i=0;i<3;i++)

for(j=0;j<2;j++)

Page 44: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 44 .

练习题:

1、输入一个数组,输出其最大值及该元素下标、最小值及该元素下标,并输出该数组

各元素的平均值。

2、输入一个数组,输出该数组中的两位数及两位数的个数。

3、求 1000之内的“水仙花数”并将其存入一个数组中,并显示该数组。

4、输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。

5、有 n个整数,使其前面各数顺序向后移 1个位置,最后那个数变成最前面的数。

6、输入一个数组,该数组元素个数为奇数,求该数组中大小为中间的值及该元素的下

标。(注:先把数组排序,则数组中间的那个元素则为中间值)

2.8 函数(FUNCTION)

2.8.1 函数概述

虽然在前面各章的程序中都只有一个主函数 main(), 但实用程序往往由多个函数组

成。函数是C源程序的基本模块, 通过对函数模块的调用实现特定的功能。C语言中的函

数相当于其它高级语言的子程序。 C语言不仅提供了极为丰富的库函数(如 Turbo C,MS C

都提供了三百多个库函数),还允许用户建立自己定义的函数。用户可把自己的算法编成一

个个相对独立的函数模块,然后用调用的方法来使用函数。

可以说C程序的全部工作都是由各式各样的函数完成的, 所以也把C语言称为函数式

语言。 由于采用了函数模块式的结构,C语言易于实现结构化程序设计。使程序的层次结

构清晰,便于程序的编写、阅读、调试。

在C语言中可从不同的角度对函数分类。

1. 从函数定义的角度看,函数可分为库函数和用户定义函数两种。

(1)库函数

由C系统提供,用户无须定义, 也不必在程序中作类型说明,只需在程序前包含有该

函数原型的头文件即可在程序中直接调用。在前面各章的例题中反复用到 printf 、scanf

等函数均属此类。

(2)用户定义函数

由用户按需要写的函数。对于用户自定义函数, 不仅要在程序中定义函数本身, 而

且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。

2. C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数

分为有返回值函数和无返回值函数两种。

(1)有返回值函数

printf(“%5d”,b[i][j]); /*输出数组 b*/

printf(“\n”); /*输出一行后换行*/

Page 45: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 45 .

此类函数被调用执行完后将向调用者返回一个执行结果, 称为函数返回值。如数学函

数即属于此类函数。 由用户定义的这种要返回函数值的函数,必须在函数定义和函数说明

中明确返回值的类型。

(2)无返回值函数

此类函数用于完成某项特定的处理任务, 执行完成后不向调用者返回函数值。这类函

数类似于其它语言的过程。 由于函数无须返回值,用户在定义此类函数时可指定它的返回

为“空类型”, 空类型的说明符为“void”。

3. 从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种。

(1)无参函数

函数定义、函数说明及函数调用中均不带参数。 主调函数和被调函数之间不进行参数

传送。 此类函数通常用来完成一组指定的功能,可以返回或不返回函数值。

(2)有参函数

也称为带参函数。在函数定义及函数说明时都有参数, 称为形式参数(简称为形参)。

在函数调用时也必须给出参数, 称为实际参数(简称为实参)。 进行函数调用时,主调函

数将把实参的值传送给形参,供被调函数使用。

还应该指出的是,在C语言中,所有的函数定义,包括主函数main 在内,都是平行的。

也就是说,在一个函数的函数体内, 不能再定义另一个函数, 即不能嵌套定义。但是函

数之间允许相互调用,也允许嵌套调用。习惯上把调用者称为主调函数。 函数还可以自己

调用自己,称为递归调用。main 函数是主函数,它可以调用其它函数,而不允许被其它函

数调用。 因此,C程序的执行总是从main 函数开始, 完成对其它函数的调用后再返回到

main函数,最后由 main 函数结束整个程序。一个C源程序必须有,也只能有一个主函数

main。下面主要介绍用户定义函数。

2.8.2 函数定义与调用

1、函数定义的一般形式

类型 函数名称(类型 参数1,类型 参数2,类型 参数 3……)

其中,最左边的“类型”为函数返回值的类型,“函数名称”为用户自己定义的标识符,

括号中的“类型”为函数参数的类型。

例如,一个计算圆柱体积的函数定义如下:

/*类型为 double,即函数运行结束会返回给调用它的函数一个 double 型的数据(体积 v);参数为两

个 double 型的变量,r是半径,h是高;所谓参数是指要完成函数的功能必须要给函数的已知数据*/

double tiji(double r,double h)

double v;

v=3.1415926*r*r*h;

return v;

Page 46: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 46 .

注意:函数本身是无法运行的,必须在其它函数中被调用才能运行

使用函数时的注意事项:

1、函数定义时要同时声明其类型;

2、调用函数前要先声明该函数;

3、传给函数的参数值,其类型要与函数原定义一致;

4、接受函数返回值的变量,其类型也要与函数一致。

2、函数的声明

void function1(void);

此函数无返回值,也不传参数;

void function2(unsigned char i,int j);

此函数无返回值,但需要unsigned char类型的 i参数和 int类型的 j参数;

unsigned char function3(unsigned char i);

此函数有unsigned char类型的返回值给原调用程序。

3、函数的返回值

return语句;

return语句是用来使函数立即结束以返回原调用程序的指令,而且可以把函数内的最

后结果数据传回给原调用程序。

/*调用以上函数的主程序编制如下*/

main()

double r,h,v;

scanf(“%f,%f”,&r,&h);

v=tiji(r,h); /*函数调用*/

printf(“%f\n”,v);

unsigned char add(unsigned char , unsigned char );//函数声明

void main(void ) //主调函数

unsigned char a ,b ,c;

a=5;

b=6;

c=add(a,b); //函数调用

unsigned char add(unsigned char x, unsigned char y) //加法函数的定义

unsigned char z;

z=x+y;

Page 47: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 47 .

4、函数的参数

函数的参数分为形参和实参两种。形参出现在函数定义中,在整个函数体内都可以使

用, 离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能

使用。 形参和实参的功能是作数据传送。发生函数调用时, 主调函数把实参的值传送给

被调函数的形参从而实现主调函数向被调函数的数据传送。

函数的形参和实参具有以下特点:

1)形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内

存单元。因此,形参只有在函数内部有效。 函数调用结束返回主调函数后则不能再使用该

形参变量。

2)实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函

数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,

输入等办法使实参获得确定值。

3)实参和形参在数量上,类型上,顺序上应严格一致, 否则会发生“类型不匹配”

的错误。

4)函数调用中发生的数据传送是单向的。 即只能把实参的值传送给形参,而不能把

形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值

不会变化。

下例可以说明这个问题:

本程序中定义了一个函数s,该函数的功能是求1+2+3+….+n的值。在主函数中输入n

值,并作为实参,在调用时传送给 s 函数的形参量 n( 注意,本例的形参变量和实参变量

的标识符都为 n, 但这是两个不同的量,各自的作用域不同)。 在主函数中用 printf 语

句输出一次n 值,这个 n值是实参 n的值。在函数 s 中也用 printf 语句输出了一次 n值,

return(z); //返回给调用函数一个 unsigned char 型的数据

int s(int) ;

void main()

int n;

scanf("%d",&n);

s(n);

printf("n=%d\n",n);

int s(int n)

int i;

for(i=n-1;i>=1;i--)

n=n+i;

printf("n=%d\n",n);

Page 48: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 48 .

这个n 值是形参最后取得的n值 0。从运行情况看,输入n 值为100。即实参n 的值为100。

把此值传给函数s 时,形参 n 的初值也为100,在执行函数过程中,形参 n 的值变为5050。

返回主函数之后,输出实参n 的值仍为100。可见实参的值不随形参的变化而变化。

2.8.3 变量的作用域

函数的形参变量只在被调用期间才分配内存单元,调用结束立即释放。 这一点表明形

参变量只有在函数内才是有效的, 离开该函数就不能再使用了。这种变量有效性的范围称

变量的作用域。不仅对于形参变量, C语言中所有的量都有自己的作用域。变量说明的方

式不同,其作用域也不同。 C语言中的变量,按作用域范围可分为两种, 即局部变量和

全局变量。

1、局部变量

局部变量也称为内部变量。局部变量是在函数内作定义说明的。其作用域仅限于函数

内, 离开该函数后再使用这种变量是非法的。

例如:

在函数f1 内定义了三个变量,a为形参,b,c 为一般变量。在 f1 的范围内a,b,c有效,

或者说a,b,c 变量的作用域限于 f1内。同理,x,y,z的作用域限于 f2内。 m,n的作用域

限于main 函数内。

关于局部变量的作用域还要说明以下几点:

1) 主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用。同时,主

函数中也不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平

行关系。这一点是与其它语言不同的,应予以注意。 2. 形参变量是属于被调函数的局部

变量,实参变量是属于主调函数的局部变量。

int f1(int a) /*函数 f1*/

int b,c;

……

//a,b,c 作用域

int f2(int x) /*函数 f2*/

int y,z;

……

// x,y,z 作用域

main()

int m,n;

……

//m,n 作用域

Page 49: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 49 .

2)允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,

互不干扰,也不会发生混淆。如在上例中,形参和实参的变量名都为n,是完全允许的。

3)在复合语句中也可定义变量,其作用域只在复合语句范围内。例如:

2、全局变量

全局变量也称为外部变量,它是在函数外部定义的变量。 它不属于哪一个函数,它属

于一个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量

说明。 只有在函数内经过说明的全局变量才能使用。全局变量的说明符为extern。 但在

一个函数之前定义的全局变量,在该函数内使用可不再加以说明。 例如:

从上例可以看出a、b、x、y 都是在函数外部定义的外部变量,都是全局变量。但x,y

定义在函数f1 之后,而在f1内又无对x,y 的说明,所以它们在f1内无效。 a,b定义在源

程序最前面,因此在f1,f2 及 main内不加说明也可使用。

范例:输入正方体的长宽高l,w,h。求体积及三个面x*y,x*z,y*z 的面积。

main()

int s,a;

……

int b;

s=a+b;

……b作用域

……s,a 作用域

int a,b; /*外部变量*/

void f1() /*函数 f1*/

……

float x,y; /*外部变量*/

int fz() /*函数 fz*/

……

main() /*主函数*/

……

/*全局变量 x,y 作用域 全局变量 a,b 作用域*/

int s1,s2,s3;

int vs( int a,int b,int c)

Page 50: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 50 .

本程序中定义了三个外部变量 s1,s2,s3, 用来存放三个面积,其作用域为整个程序。

函数vs 用来求正方体体积和三个面积, 函数的返回值为体积 v。由主函数完成长宽高的输

入及结果输出。由于C语言规定函数返回值只有一个, 当需要增加函数的返回数据时,用

外部变量是一种很好的方式。本例中,如不使用外部变量,在主函数中就不可能取得

v,s1,s2,s3四个值。而采用了外部变量, 在函数 vs中求得的 s1,s2,s3值在 main 中仍然

有效。因此外部变量是实现函数之间数据通讯的有效手段。

在同一源文件中,允许全局变量和局部变量同名。在局部变量的作用域内,全局变量

不起作用。

练习题:

1、编写一个函数,功能为判断一个年份是否为闰年,若是闰年则打印“YES”,不是则

打印“NO”。并在主函数中调用该函数,判断键盘输入的年份是否为闰年。

2、编写一个函数,求一个数的阶乘并返回该结果。并在主函数中调用该函数,求键盘

输入的数的阶乘并显示。

3、编写函数,在该函数中调用上一个函数,求 1+2!+3!+...+20!的和。并在主函数中调

用该函数。

4、编写一个函数,功能为判断一个数是不是“水仙花数”,是则返回 1,不是则返回 0。并在主函数中调用该函数,判断键盘输入的数是否为“水仙花数”。

所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身。例如:153是一个

“水仙花数”,因为 153=1 的三次方+5的三次方+3 的三次方。

5、编写函数,在该函数中调用上一个函数,用来找出变量 a 和 b(a和 b均为 3位数且

a<b)之间所有的“水仙花数”并显示出来。并在主函数中调用该函数,a和 b从键盘输入。

6、编写一个函数,功能为判断一个数是否为 “完数”, 是则返回 1,不是则返回 0。并在主函数中调用该函数,判断键盘输入的数是否为“完数”。

int v;

v=a*b*c;

s1=a*b;

s2=b*c;

s3=a*c;

return v;

main()

int v,l,w,h;

printf("\ninput length,width and height\n");

scanf("%d%d%d",&l,&w,&h);

v=vs(l,w,h);

printf("v=%d s1=%d s2=%d s3=%d\n",v,s1,s2,s3);

Page 51: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 51 .

一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如 6=1+2+3.7、编写函数,在该函数中调用上一个函数,用来找出变量 a 和 b(且 a<b)之间所有

的“完数”并显示出来。并在主函数中调用该函数,a和 b从键盘输入。

2.9 位运算

前面介绍的各种运算都是以字节作为最基本位进行的。 但在很多系统程序中常要求在

位(bit)一级进行运算或处理。C语言提供了位运算的功能, 这使得C语言也能像汇编语言

一样用来编写系统程序。

C语言提供了六种位运算符:

& 按位与

| 按位或

^ 按位异或

~ 取反

<< 左移

>> 右移

1、按位与运算

按位与运算符"&"是双目运算符。其功能是参与运算的两数各对应的二进位相与。只有

对应的两个二进位均为 1 时,结果位才为 1 ,否则为 0。参与运算的数以补码方式出现。

例如:9&5 可写算式如下: 00001001 (9 的二进制补码)&00000101 (5 的二进制补码)00000001 (1的二进制补码)可见 9&5=1。

按位与运算通常用来对某些位清0或保留某些位。例如把字符型变量 a 的高 4位清 0 ,

保留低 4 位,可作 a&0x0f 运算。

2、按位或运算

按位或运算符"|"是双目运算符。其功能是参与运算的两数各对应的二进位相或。只要

对应的二个二进位有一个为 1 时,结果位就为 1。参与运算的两个数均以补码出现。

例如:9|5可写算式如下: 00001001|0000010100001101 (十进制为 13)可见 9|5=13

main()

char a=9,b=5,c;

c=a&b;

printf("a=%d\nb=%d\nc=%d\n",a,b,c);

main()

char a=9,b=5,c;

c=a|b;

printf("a=%d\nb=%d\nc=%d\n",a,b,c);

Page 52: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 52 .

按位或运算通常用来对某些位置 1。例如把字符型变量 a 的高 4 位置 1,保留低 4位,

可作 a|0xf0 运算。

3、 按位异或运算

按位异或运算符"^"是双目运算符。其功能是参与运算的两数各对应的二进位相异或,

当两对应的二进位相异时,结果为 1。参与运算数仍以补码出现,例如 9^5可写成算式如下:

00001001^00000101 00001100 (十进制为 12)。

4、求反运算

求反运算符"~"为单目运算符,具有右结合性。其功能是对参与运算的数的各二进位按

位求反。例如~9的运算为: ~(0000000000001001)结果为:1111111111110110

5、左移运算

左移运算符“<<”是双目运算符。其功能把“<< ”左边的运算数的各二进位全部左移若干

位,由“<<”右边的数指定移动的位数,

高位丢弃,低位补 0。例如: a<<4 指把 a 的各二进位向左移动 4位。如 a=00000011(十进

制 3),左移 4 位后为 00110000(十进制 48)。

6、右移运算

右移运算符“>>”是双目运算符。其功能是把“>> ”左边的运算数的各二进位全部右移若

干位,“>>”右边的数指定移动的位数。

例如:设 a=15,a>>2 表示把 000001111 右移为 00000011(十进制 3)。应该说明的是,

对于有符号数,在右移时,符号位将随同移动。当为正数时, 最高位补 0,而为负数时,

符号位为 1,最高位是补 0或是补 1 取决于编译系统的规定。Turbo C和很多系统规定为补

1。

main()

char a=9;

a=a^15;

printf("a=%d\n",a);

main()

unsigned char a,b;

printf("input a number: ");

scanf("%d",&a);

b=a>>5;

b=b&15;

printf("a=%d\tb=%d\n",a,b);

Page 53: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 53 .

范例:(显示一个无符号字符型变量 a 中的 8 个位)

练习题:

1、取一个从键盘输入的无符号字符型数据的后四位并输出显示;

2、编写一个函数,功能为将一个无符号字符型数据中的8 个位首尾互换,并在主函数

中调用该函数,从键盘输入数据对该函数进行测试;

3、编写一个函数,使一个无符号字符型数据中的前四位置“1”。并在主函数中调用该

函数。

4、编写一个函数,功能为读取一个无符号字符型数据中的任意一个位,该位的值从键

盘输入,并在主函数中调用;

5、编写程序,将键盘输入的 8个位的值按从高到低的顺序组成一个无符号字符型数据,

并显示该数据。

6、编写程序,将一个无符号字符型数据的高 4位与低 4 位为互换,数据从键盘输入,

结果输出显示。

2.10 指针

指针是C语言中广泛使用的一种数据类型。利用指针变量可以表示各种数据结构; 能

很方便地使用数组和字符串; 并能象汇编语言一样处理内存地址,从而编出精练而高效的

程序。

2.10.1 指针的基本概念

在计算机中,所有的数据都是存放在存储器中的。 一般把存储器中的一个字节称为一

个内存单元, 不同的数据类型所占用的内存单元数不等,如整型量占2 个单元,字符量占

1个单元等。为了正确地访问这些内存单元, 必须为每个内存单元编上号。 根据一个内存

单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址。 既然根据内存单元

main()

unsigned char a, i;

scanf(“%d”, &a);

printf(“a=”);

for(i=0;i<8;i++)

if(a&0x80>0)

printf(“1”);

else

Printf(“0”);

a<<=1;

Page 54: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 54 .

的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。

内存单元的指针和内存单元的内容是两个不同的概念。一个指针变量的值就是某个内

存单元的地址或称为某内存单元的指针。

既然指针变量的值是一个地址, 那么这个地址不仅可以是变量的地址, 也可以是其

它数据结构的地址。在一个指针变量中存放一个数组或一个函数的首地址有何意义呢? 因

为数组或函数都是连续存放的。通过访问指针变量取得了数组或函数的首地址, 也就找到

了该数组或函数。这样一来,凡是出现数组,函数的地方都可以用一个指针变量来表示,只

要该指针变量中赋予数组或函数的首地址即可。

2.10.2 指针变量的类型说明

对指针变量的类型说明的一般形式为: 类型说明符 * 变量名;

其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指

针变量所指向的变量的数据类型。

例如: int *p;表示 p是一个指针变量,它的值是某个整型变量的地址。 或者说p指

向一个整型变量。至于p 究竟指向哪一个整型变量, 应由向p赋予的地址来决定。

指针变量同普通变量一样,使用之前不仅要定义说明, 而且必须赋予具体的值。未经

赋值的指针变量不能使用。指针变量的赋值只能赋予地址, 决不能赋予任何其它数据,否

则将引起错误。C语言中提供了地址运算符&来表示变量的地址。其一般形式为: & 变量

名;

如&a变示变量 a 的地址,&b 表示变量 b 的地址。 变量本身必须预先说明。设有指向

整型变量的指针变量p,如要把整型变量 a 的地址赋予p可以有以下两种方式:

(1)指针变量初始化的方法 int a;

int *p=&a;

(2)赋值语句的方法 int a;

int *p;

p=&a;

不允许把一个数赋予指针变量,故下面的赋值是错误的: int *p;p=1000; 被赋值的

指针变量前不能再加“*”说明符,如写为*p=&a 也是错误的。

2.10.3 指针变量的运算

指针变量可以进行某些运算,但其运算的种类是有限的。 它只能进行赋值运算和部分

算术运算及关系运算。

1、指针运算符

(1)取地址运算符&

取地址运算符&是单目运算符,其结合性为自右至左,其功能是取变量的地址。在scanf

函数及前面介绍指针变量赋值中,我们已经了解并使用了&运算符。

Page 55: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 55 .

(2)取内容运算符*

取内容运算符*是单目运算符,其结合性为自右至左,用来表示指针变量所指的变量。

在*运算符之后跟的变量必须是指针变量。需要注意的是指针运算符 *和指针变量说明中的

指针说明符* 不是一回事。在指针变量说明中,“*”是类型说明符,表示其后的变量是指

针类型。而表达式中出现的“*”则是一个运算符用以表示指针变量所指的变量。

main()

int a=5,*p=&a;

printf ("%d",*p); //指针变量p 取得了整型变量a的地址,本语句输出变量 a的值。

2.指针变量的运算

(1)赋值运算

指针变量的赋值运算有以下几种形式:

①指针变量初始化赋值,前面已作介绍。

②把一个变量的地址赋予指向相同数据类型的指针变量。例如:

int a,*pa;

pa=&a; /*把整型变量a的地址赋予整型指针变量 pa*/

③把一个指针变量的值赋予指向相同类型变量的另一个指针变量。如:

int a,*pa=&a,*pb;

pb=pa; /*把 a的地址赋予指针变量 pb*/

由于 pa,pb均为指向整型变量的指针变量,因此可以相互赋值。

④把数组的首地址赋予指向数组的指针变量。

例如: int a[5],*pa;

pa=a; (数组名表示数组的首地址,故可赋予指向数组的指针变量pa)

也可写为:

pa=&a[0]; /*数组第一个元素的地址也是整个数组的首地址,也可赋予pa*/

当然也可采取初始化赋值的方法:

int a[5],*pa=a;

(2)加减算术运算

对于指向数组的指针变量,可以加上或减去一个整数 n。设pa 是指向数组 a的指针变

量,则pa+n,pa-n,pa++,++pa,pa--,--pa 运算都是合法的。指针变量加或减一个整数 n 的

意义是把指针指向的当前位置(指向某数组元素)向前或向后移动 n 个位置。应该注意,数

组指针变量向前或向后移动一个位置和地址加1 或减1 在概念上是不同的。因为数组可以

有不同的类型, 各种类型的数组元素所占的字节长度是不同的。如指针变量加1,即向后

移动1 个位置表示指针变量指向下一个数据元素的首地址。而不是在原地址基础上加 1。

例如:

int a[5],*pa;

pa=a; /*pa指向数组 a,也是指向a[0]*/

pa=pa+2; /*pa指向 a[2],即 pa 的值为&pa[2]*/ 指针变量的加减运算只能对数组指

Page 56: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 56 .

针变量进行, 对指向其它类型变量的指针变量作加减运算是毫无意义的。

(3)两个指针变量之间的运算只有指向同一数组的两个指针变量之间才能进行运算,

否则运算毫无意义。

①两指针变量相减

两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。实际上是两

个指针值(地址) 相减之差再除以该数组元素的长度(字节数)。例如 pf1和 pf2 是指向同一

浮点数组的两个指针变量,设 pf1的值为 2010H,pf2 的值为2000H,而浮点数组每个元素

占4 个字节,所以pf1-pf2的结果为(2000H-2010H)/4=4,表示 pf1和 pf2之间相差 4个元

素。两个指针变量不能进行加法运算。 例如, pf1+pf2是什么意思呢?毫无实际意义。

②两指针变量进行关系运算

指向同一数组的两指针变量进行关系运算可表示它们所指数组元素之间的关系。例如:

pf1==pf2表示 pf1 和 pf2 指向同一数组元素

pf1>pf2表示 pf1 处于高地址位置

pf1<pf2表示 pf2 处于低地址位置

2.10.4 数组指针变量的说明和使用

指向数组的指针变量称为数组指针变量。 在讨论数组指针变量的说明和使用之前,我

们先明确几个关系。

一个数组是由连续的一块内存单元组成的。 数组名就是这块连续内存单元的首地址。

一个数组也是由各个数组元素(下标变量) 组成的。每个数组元素按其类型不同占有几个连

续的内存单元。 一个数组元素的首地址也是指它所占有的几个内存单元的首地址。 一个

指针变量既可以指向一个数组,也可以指向一个数组元素, 可把数组名或第一个元素的地

址赋予它。如要使指针变量指向第 i 号元素可以把 i 元素的首地址赋予它或把数组名加 i

赋予它。

设有实数组a,指向 a 的指针变量为 pa, pa,a,&a[0]均指向同一单元,它们是数组 a

的首地址,也是 0 号元素a[0]的首地址。pa+1,a+1,&a[1]均指向 1号元素 a[1]。类推可知

pa+i,a+i,&a[i]指向 i 号元素a[i]。应该说明的是pa 是变量,而a,&a[i]都是常量。在编

程时应予以注意。

main()

int a=10,b=20,s,t,*pa,*pb; //说明 pa,pb 为整型指针变量

pa=&a; //给指针变量 pa 赋值,pa 指向变量 a。

pb=&b; //给指针变量 pb 赋值,pb 指向变量 b。

s=*pa+*pb; //求 a+b 之和,(*pa 就是 a,*pb 就是 b)。

t=*pa**pb; //求 a*b 之积。

printf("a=%d\nb=%d\na+b=%d\na*b=%d\n",a,b,a+b,a*b); //输出结果

printf("s=%d\nt=%d\n",s,t);// 输出结果

Page 57: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 57 .

数组指针变量说明的一般形式为:

类型说明符 * 指针变量名

其中类型说明符表示所指数组的类型。 从一般形式可以看出指向数组的指针变量和指

向普通变量的指针变量的说明是相同的。

引入指针变量后,就可以用两种方法来访问数组元素了。

第一种方法为下标法,即用a[i]形式访问数组元素。 在第四章中介绍数组时都是采用

这种方法。

第二种方法为指针法,即采用*(pa+i)形式,用间接访问的方法来访问数组元素。

范例:编写一个函数,功能为将一个数组中的所有元素按相反顺序存放,并在主程序中

调用该函数。

/*本程序以指针的形式给数组 a赋值并输出该数组*/

main() //主函数

int a[5],i,*pa; //定义整型数组和指针

pa=a; //将指针 pa 指向数组 a

for(i=0;i<5;i++) //循环

*pa=i; //将变量 i的值赋给由指针 pa 指向的 a[]的数组单元

pa++; //将指针 pa 指向 a[]的下一个单元

pa=a; //指针 pa 重新取得数组 a的首地址

for(i=0;i<5;i++) //循环

printf("a[%d]=%d\n",i,*pa); //用数组方式输出数组 a中的所有元素

pa++; //将指针 pa 指向 a[]的下一个单元

void rev(int *x,int n)

int *p,t,*i,*j,m=(n-1)/2;

i=x; //指向数组第一个元素

j=x+n-1; //指向数组最后一个元素

p=x+m; //指向数组中间一个元素

for( ;i<=p;i++,j--)

t=*i;

*i=*j;

*j=t;

main()

int i,a[5]=1,2,3,4,5;

Page 58: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 58 .

2.11 预处理命令

1、#define宏定义

#define 宏名 字符串

以一个宏名称代表一个字符串,即当程序任何地方使用到宏名时,则将以所代表的字

符串来替换。宏的定义可以是一个常数、表达式或含有参数的表达式都可以。在程序中如

果多次使用宏,则会占用较多的内存,但执行速度较快。

范例:

#define DATA data

#define pi 3.1415926

#define CLOCK_4096us (65536—4096*CLOCK_BASE)

2、typedef

typedef:可以用来定制自己的类型名称,如下所示:

typedef unsigned char uchar; 可以用uchar 作为数据类型名作变量声明

typedef unsigned long Long; 可以用Long 作为数据类型名作变量声明

范例:

printf(“The original array:\n”);

for(i=0;i<5;i++)

printf(“%d”,a[i]);

rev(a,5);

printf(“The reversed array:\n”);

for(i=0;i<5;i++)

printf(“%d”,a[i]);

typedef unsigned char uchar;

uchar a ,b ,c;

main()

if(a>0)

c=a+b;

else

c=a-b;

Page 59: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 59 .

2.12 VC++开发环境简单应用

本节介绍 C 语言源程序在 VC++6.0 集成开发环境中的编辑、编译及运行的方法。在

VC++6.0开发环境中可以进行 C、C++及基于 MFC 的程序开发,该环境是可视化的,程序的

编辑、查错、运行等都非常直观、方便,是TC 开发环境最好的替代品。请注意,编辑基于

单片机的程序不是在VC++6.0环境中,而是后续章节要讲述的Keil 开发环境,但是,VC++6.0

环境中编辑的程序粘贴到Keil 中是依然可以运行的。

下面介绍在VC++6.0 开发环境中编制C语言程序的方法与步骤。当然要先安装VC++6.0

软件,然后才能在其中进行编程,这里假设已经安装了该软件。

首先,进入VC++6.0 开发环境,方法是单击“开始”菜单,然后单击“程序”中的

“Microsoft Visual Studio 6.0”,在出现的菜单中单击“Microsoft Visual C++ 6.0”

即可进入该开发环境,如图2-1 所示。

图 2-1 打开 VC++软件的方法

图 2-2 VC++6.0 开发环境主界面

Page 60: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 60 .

当进入VC++开发环境后,则显示图 2-2所示的主界面。

然后单击“文件”菜单中的“新建”命令,则显示图 2-3 所示的新建对话框。选择上

面的“工程”标签,并选中下面的“Win32 Console Application”,此时在右边“C 位置”

下面的文件保存位置处输入工程所在的文件夹名,如图2-3所示的“练习”,则所建的工程

就保存在 D盘的“练习”文件夹中。此时在右边“工程”下面输入所建的工程的名字,如

“test”,然后单击“确定”就完成了新建任务。

注意,如上所述建立工程则工程的名字为“test”,该工程保存在保存在 D盘的“练习”

文件夹中。

图 2-3 新建对话框

图 2-4 刚建立好的空工程

Page 61: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 61 .

当单击“确定”后,在紧接着出现的对话框中单击“完成”, 在紧接着出现的对话框

中单击“确定”,就出现了图 2-4所示的空工程界面。单击中间的“FileView”标签,并在

上面白色区域中单击“test files”左侧的“+”号,就会出现图2-5 所示的界面。

图 2-5 “FileView”标签界面

单击工具栏中的 图标,新建一个空白文档,则界面中右侧工作区变为白色,该区域

就是C 语言编辑区。单击工具栏中的 图标, 以保存刚建立好的空白文档,则会出现图

2-6所示的文件保存对话框。在该对话框中选择工程路径,即D 盘的“练习”文件夹的“test”

文件夹,在下面输入 C 文件名,如“Text1.c”,如图 2-6所示。注意:由于刚建立好的文

件是C 文件,所以必须在文件名后面加上后缀“.c”。

图 2-5 文件保存对话框

单击“保存”按钮,就把刚建立好的空白文档保存在了工程路径下。 注意:最好把文

本文档与工程保存在同一个文件夹中,因为 VC++环境中不能单独运行 C 程序,C 程序必须

Page 62: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 62 .

在工程中才可以运行。

下面将建立好的 C 文件添加到工程中,方法是:右键单击左边的“Source Files”标

签,如图2-6所示,在弹出的界面中单击“Add Files to Folder”标签,则出现图2-7所

示的插入文件对话框。在该对话框中选中“Text1.c”,然后单击“确定”按钮,则刚建立

的空的C 文档就加入到了工程中。

图 2-6 添加文件界面

此时,在左边“Source Files”标签左侧就出现了一个“+”号,表明该文件夹中有了

内容,单击该“+”号,则会显示“Text1.c”文件。此时就可以在右边程序编辑区输入 C

程序了,输入完C 程序后的界面入图2-8所示。

程序编辑完成后,可单击工具栏上 图标编译工程,以检查是否有错误发生,如果发

生错误或警告,则会在最下面空白区以英文形式给出提示,双击该提示信息行则上面程序

区会出现箭头,指示该错误发生在该行。如果无错误和警告信息,则下面空白区显示 “0

error(s), 0 warning(s)”。

图 2-7 插入文件对话框

Page 63: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 63 .

编译无错后,可单击工具栏上 图标链接工程,然后单击工具栏上 图标即可运行

所编写的程序了。

图 2-8 程序编辑完成的界面

以上就是在VC++环境中编辑及运行 C语言程序的方法,下面介绍以下如何打开已编写

好的程序。注意,当要打开已编写好的程序时,首先要找到该程序所在的文件夹,然后双

击其中以“.dsw”为后缀的文件,则整个工程就打开了。不要双击“.c”文件,那样只会

打开C 程序,但无法运行该程序,因为VC++环境中只能运行整个工程。

Page 64: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 64 .

第 3章 Keil C 与 ANSI C

Keil C是我们所用的编程软件,下面将介绍Keil C 的主要特点和它与ANSI C 的不同

之处。Keil 编译器除了少数一些关键地方外,基本类似于ANSI C。差异主要是Keil 可以

让用户针对8051的结构进行程序设计,其它差异主要是8051的一些局限引起的。

3.1 数据类型

Keil C 有ANSI C 的所有标准数据类型,除此之外,为了更加有利的利用89C52的结构,

还加入了一些特殊的数据类型。C51(Keil C)中用到的数据类型及长度见下表。

除了ANSI C支持的标准数据类型外,编译器还支持一种位数据类型。一个位变量存在

于内部RAM的可位寻址区中,可像操作其它变量那样对位变量进行操作,但位数组和位指针

是违法的。

3.2 特殊功能寄存器

特殊功能寄存器用sfr 来定义,而sfr16 用来定义16 位的特殊功能寄存器如DPTR。通

过名字或地址来引用特殊功能寄存器,地址必须高于80H。可位寻址的特殊功能寄存器的位

变量定义用关键字sbit。SFR 的定义如下所示,对于大多数8051成员,Keil提供了一个包

含了所有特殊功能寄存器和他们的位的定义的头文件。通过包含头文件可以很容易的进行

新的扩展。

数据类型 位数 字节数 值 域

bit 1 0~1

signed char 8 1 -128~+127

unsigned char 9 1 0~255

enum 16 2 -32768~+32767

signed short 16 2 -32768~+32767

unsigned short 16 2 0~65535

signed int 16 2 -32768~+32767

unsigned int 16 2 0~65535

signed long 32 4 -2147483648~+2147483647

unsigned long 32 4 0~4294967295

float 32 4 0.175494e-38~0.402823e+38

sbit 1 0~1

sfr 8 1 0~255

sfr16 16 2 0~65536

Page 65: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 65 .

3.3 存储类型

Keil 允许使用者指定程序变量的存储区。这使使用者可以控制存储区的使用。编译器

可识别以下存储区。

1、DATA 区

对DATA 区的寻址是最快的,所以应该把使用频率高的变量放在DATA 区。由于空间有

限,必须注意使用。DATA 区除了包含程序变量外,还包含了堆栈和寄存器组,DATA 区的

声明如下所示。

标准变量和用户自定义变量都可存储在DATA 区中,只要不超过DATA 区的范围。因为

C51 使用默认的寄存器组来传递参数,你至少失去了8 个字节。另外,要定义足够大的堆

栈空间。当你的内部堆栈溢出的时候,你的程序会莫名其妙的复位。实际原因是8051 系列

微处理器没有硬件报错机制,堆栈溢出只能以这种方式表示出来

2、BDATA 区

你可以在BDATA 区的位寻址区定义变量,这个变量就可进行位寻址,并且声明位变量。

这对状态寄存器来说是十分有用的,因为它需要单独的使用变量的每一位。不一定要用位

sfr SCON=0X98; //定义 SCON

sbit SM0=0X9F; //定义 SCON 的各位

sbit SM1=0X9E;

sbit SM2=0X9D;

sbit REN=0x9C;

sbit TB8=0X9B;

sbit RB8=0X9A;

sbit TI=0X99;

sbit RI=0X98;

存储区 描 述

DATA RAM 的低128 个字节,可在一个周期内直接寻址

BDATA DATA 区的16 个字节的可位寻址区

IDATA RAM 区的高128 个字节,必须采用间接寻址

PDATA 外部存储区的256 个字节,通过P0 口的地址对其寻址,使用指令

MOVX @Rn,需要两个指令周期。

XDATA 外部存储区使用DPTR 寻址

CODE 程序存储区使用DPTR 寻址

unsigned char data system_status=0;

unsigned int data unit_id[2];

char data inp_string[16];

float data outp_value;

Page 66: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 66 .

变量名来引用位变量。下面是一些在BDATA 段中声明变量和使用位变量的例子:

编译器不允许在BDATA 段中定义float 和double 类型的变量。

下面的代码访问状态寄存器的特定位,把访问定义在DATA 段中的一个字节和通过位名

和位号访问同样的可位寻址字节的位的代码对比。注意,对变量位进行寻址产生的汇编代

码比检测定义在DATA 段的状态字节位所产生的汇编代码要好。如果你对定义在BDATA 段中

的状态字节中的位采用偏移量进行寻址,而不是用先前定义的位变量名时,编译后的代码

是错误的。

unsigned char bdata status_byte;

unsigned int bdata status_word;

unsigned long bdata status_dword;

sbit stat_flag=status_byte^4;

if(status_word^15)

stat_flag=1;

unsigned char data byte_status=0x43; //定义一个字节宽状态寄存器

unsigned char bdata bit_status=0x43; //定义一个可位寻址状态寄存器

sbit status_3=bit_status^3; //把bit_status 的第3 位设为位变量

bit use_bit_status(void);

bit use_bitnum_status(void);

bit use_byte_status(void);

void main(void)

unsigned char temp=0;

if (use_bit_status()) //如果第3 位置位temp 加1

temp++;

if (use_byte_status()) //如果第3 位置位temp 再加1

temp++;

if (use_bitnum_status()) //如果第3 位置位temp 再加1

temp++;

bit use_bit_status(void)

return(bit)(status_3);

bit use_bitnum_status(void)

return(bit)(bit_status^3);

Page 67: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 67 .

3、IDATA 段

IDATA 段也可存放使用比较频繁的变量,使用寄存器作为指针进行寻址。在寄存器中

设置8 位地址进行间接寻址。和外部存储器寻址比较,它的指令执行周期和代码长度都比

较短。

4、PDATA 和XDATA 段

在这两个段声明变量和在其它段的语法是一样的。PDATA 段只有256 个字节,而XDATA

段可达65536 个字节。下面是一些例子:

对PDATA 和XDATA 的操作是相似的。对PDATA 段寻址比对XDATA 段寻址要快,因为对

PDATA 段寻址只需要装入8 位地址,而对XDATA 段寻址需装入16 位地址。所以尽量把外部

数据存储在PDATA 段中。对PDATA 和XDATA 寻址要使用MOVX 指令,需要两个处理周期。

5、CODE 段

代码段的数据是不可改变的,89C52的代码段不可重写。一般,代码段中可存放数据表、

跳转向量和状态表。对CODE 段的访问和对XDATA 段的访问的时间是一样的。代码段中的对

象在编译的时候初始化,否则,你就得不到你想要的值。下面是代码段的声明例子:

bit use_byte_status(void)

return byte _status&0x04;

unsigned char idata system_status=0;

unsigned int idata unit_id[2];

char idata inp_string[16];

float idata outp_value;

unsigned char xdata system_status=0;

unsigned int pdata unit_id[2];

char xdata inp_string[16];

float pdata outp_value;

#include <reg51.h>

unisgned char pdata inp_reg1;

unsigned char xdata inp_reg2;

void main(void)

inp_reg1=P1;

inp_reg2=P3;

unsigned char code data[ ]=

0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,

0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15

Page 68: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 68 .

3.4 指针

1、通用指针

C51(51系列的单片机,是典型;我们在下面将用这种型号来说明) 提供一个3 字节的

通用存储器指针,通用指针的头一个字节表明指针所指的存储区空间,另外两个字节存储

16 位偏移量。对于DATA、IDATA 和PDATA 段,只需要8 位偏移量。

例如:

long * state; 是一个指向long型整数的指针,而state本身则根据存储模式存放

在不同的RAM区。

char * xdata ptr;是一个指向char数据的指针,而ptr本身存放在外部RAM区。

以上的long、char等指针指向的数据可存放于任何存储器中。通用指针产生的代码比

具体指针代码的执行速度要慢,因为存储区在运行前是未知的,编译器不能优化存储区访

问,必须产生可以访问任何存储区的通用代码。如果优先考虑执行速度,应该尽可能地用

具体指针,而不用通用指针。

2、具体指针

Keil 允许使用者规定指针指向的存储段,这种指针叫具体指针。例如:

char data *str; //str指向data区中char型数据

Int xdata *ptr; //ptr指向外部RAM的int型整数。

使用具体指针的好处是节省了存储空间。编译器不用为存储器选择和决定正确的存储

器操作指令产生代码,这样就使代码更加简短,但必须保证指针不指向所声明的存储区以

外的地方,否则会产生错误,而且很难调试。

由于使用具体指针能够节省不少时间所以我们一般都不使用通用指针。

以下是具体指针与通用指针对照表:

下面的例子反映出使用具体指针比使用通用指针更加高效。

;

指针类型 通用指针 XDATA 指针 CODE 指针 IDATA 指针 DATA 指针 PDATA 指针

大小 3 字节 2 字节 2 字节 1 字节 1 字节 1 字节

#include <absacc.h>

char *generic_ptr;

char data *xd_ptr;

char mystring[]="Test output";

main()

generic_ptr=mystring;

while (*generic_ptr)

XBYTE[0x0000]=*generic_ptr;

generic_ptr++;

Page 69: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 69 .

3.5 绝对地址访问

C51提供了几种访问绝对地址的方法,下面介绍常用的两种方法。

1、绝对宏

在程序中,用“#include<absacc.h>”即可使用其中声明的宏来访问绝对地址,包括

CBYTE、XBYTE、PWORD、DBYTE、CWORD、XWORD、PBYTE和DWORD等,具体使用方法请参考absacc.h

头文件,例如:

2、_at_关键字

它的用法比较简单,直接在数据声明后加上_at_ const即可,但是需要注意:

1)绝对变量不能被初始化。

2) bit型函数及变量不能用_at_指定。

例如:

3.6 使用 Keil C 时应做的和应该避免的

Keil 编译器能从你的C 程序源代码中产生高度优化的代码。但你可以帮助编译器产生

更好的代码。下面将讨论这方面的一些问题。

xd_ptr=mystring;

while (*xd_ptr)

XBYTE[0x0000]=*xd_ptr;

xd_ptr++;

xval=XBYTE[0x0002]; //把外部存储区地址0x0002的数据存入变量xval中

XWORD[0x0002]=0x2000; //把0x2000送到外部存储区地址为0x0002的单元

#define DAC0832 XBYTE[0x7fff] //定义DAC0832的端口地址

DAC0832=0x80; //启动一次D/A转换

struct dat

struct dat idata *next;

char code *test;

;

idata struct dat num _at_ 0x42;//结构变量num定位于idata空间地址0x42

xdata int val _at_ 0x8000; // int变量val定位于xdata空间地址0x8000

Page 70: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 70 .

1、采用短变量

一个提高代码效率的最基本的方式就是减小变量的长度。使用C 编程时,我们都习惯

于对循环控制变量使用int 类型,这对8 位的单片机来说,是一种极大的浪费。应该仔细

考虑你所声明的变量值可能的范围,然后选择合适的变量类型。很明显,经常使用的变量

应该是unsigned char,只占用一个字节。

2、使用无符号类型

为什么要使用无符号类型呢?原因是8051 不支持符号运算,程序中也不要使用含有带

符号变量的外部代码。除了根据变量长度来选择变量类型以外,你还要考虑变量是否会用

于负数的场合。如果你的程序中可以不需要负数,那么把变量都定义成无符号类型的。

3、避免使用浮点指针

在8 位操作系统上使用32 位浮点数是得不偿失的,你可以这样做,但会浪费大量的时

间。所以当你要在系统中使用浮点数的时候,你要问问自己这是否一定需要。可以通过提

高数值数量级和使用整型运算来消除浮点指针。处理int和long比处理double和float要方

便得多。

4、使用位变量

对于某些标志位,应使用位变量而不是unsigned char。这将节省你的内存,你不用多

浪费7 位存储区,而且位变量在RAM 中,访问他们只需要一个处理周期。

5、用局部变量代替全局变量

把变量定义成局部变量比全局变量更有效率。编译器为局部变量在内部存储区中分配

存储空间,而为全局变量在外部存储区中分配存储空间,这会降低你的访问速度。另一个

避免使用全局变量的原因是你必须在你系统的处理过程中调节使用全局变量,因为在中断

系统和多任务系统中,不止一个过程会使用全局变量。

6、为变量分配内部存储区

局部变量和全局变量可被定义在你想要的存储区中。根据先前的讨论,当你把经常使

用的变量放在内部RAM 中时,可使你的程序的速度得到提高,除此之外,你还缩短了你的

代码,因为外部存储区寻址的指令相对要麻烦一些。考虑到存储速度,按下面的顺序使用

存储器:DATA,IDATA,PDATA,XDATA,当然你要记得留出足够的堆栈空间。

7、使用特定指针

当你在程序中使用指针时,你应指定指针的类型,确定它们指向哪个区域,如XDATA 或

CODE 区。这样你的代码会更加紧凑,因为编译器不必去确定指针所指向的存储区,因为你

已经进行了说明。

8、使用宏替代函数

Page 71: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 71 .

对于小段代码,像使能某些电路或从锁存器中读取数据,你可通过使用宏来替代函数,

使得程序有更好的可读性。你可把代码定义在宏中,这样看上去更像函数。编译器在碰到

宏时,按照事先定义的代码去替代宏。宏的名字应能够描述宏的操作。当需要改变宏时,

你只要修改宏定义处。

宏能够使得访问多层结构和数组更加容易,可以用宏来替代程序中经常使用的复杂语

句,以减少你打字的工作量,且有更好的可读性和可维护性。

3.7 keil C 程序举例

切记,Keil C 是对单片机进行程序设计的,也就是对硬件进行编程的,在程序设计之

前一定要搞清楚硬件的连接方式,因为不同的硬件连接方式下程序的编写方法是不同的。

1、延时程序

实际应用中经常会用到延时程序,如键盘去抖动程序。一般可用程序执行循环来作为

短暂延迟。

注意,以上是函数,不能单独运行,要在其它函数中调用它才能运行。

2、让单片机的P2.0口线以大约100ms的频率输出方波

说明:若想让P2.0口线以大约 100ms的频率输出方波,则P2.0口线应该保持 50ms高

电平,然后保持50ms低电平,不断循环。还要注意,所谓高电平就是对应数字“1”,低电

平就是对应数字“0”。 程序如下(利用上述的延时函数,以下程序为完整程序,可直接

运行):

#define led_on()

led_state=LED_ON;

XBYTE[LED_CNTRL] = 0x01;

#define led_off()

led_state=LED_OFF;

XBYTE[LED_CNTRL] = 0x00;

delay1ms(count)

/* 参数 count 为次数,总共延迟时间为 1ms 乘以 count。程序如下:*/

void delay1ms(unsigned char count)

unsigned char i,j;

for(i=0;i<count;i++)

for(j=0;j<120;j++); //在时钟频率为 12M 赫兹时,循环 120 次大约为 1ms

//具体应用应考虑系统的实际时钟频率

/* reg51.h 是 51 单片机头文件,该文件中定义了所有普通 51 单片机内部的寄存器,包含了该

头文件,则可以直接使用这些寄存器,如 P1,TCON 等*/

#include<reg51.h>

Page 72: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 72 .

任何控制 I/O 都是最基本的,而且大部分的应用程序也都需要用到 I/O,8051 共有 4

个 I/O 口,分别为 P0、P1、P2 和 P3,此 4个接口都可作为单独的输入或输出使用(P0口

不带有锁存功能)。当作为输出时,则每一支脚的外部电路可以高电平 “1”(+5V)驱动或

低电平“0”( 0V) 驱动。P3口又同时做其它功能接口使用,当用于其它功能时,则不能作

为I/O 口使用,当作为I/O使用, 则就无法作为其它功能使用了。

sbit p2_0=P2^0; //定义特殊位变量 p2_0 指向 P2.0 口线

delay1ms(unsigned char); //声明延时函数

void main()

while(1) //死循环,不断产生方波。

p2_0=!p2_0; //P2.0 口线取反,实现高低电平的跳变

delay1ms(50); //延时 50 毫秒

void delay1ms(count)

unsigned char i,j;

for(i=0;i<count;i++)

for(j=0;j<120;j++);//在时钟频率为 12M 赫兹时,循环 120 次大约为 1ms

Page 73: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 73 .

第 4章 KEIL 开发环境与 ISP 编程

前面已经介绍了单片机的和C语言编程的基本知识,读者已经可以进行一些基本的开发

了,但是在什么地方编制程序,又怎样把编制好的程序下载到单片机里呢?本章讲述单片

机C语言程序开发环境及程序下载的方法。

任何单片机的C语言软件开发都要经过如下步骤:在计算机的开发环境中编制源程序

(*.C文件),然后把该源程序编译和链接,最终生成十六进制文件(*.HEX文件),最后

把该十六进制文件用某种编程工具(如编程器)下载到单片机的程序存储器中,就完成了

软件编制过程。于是,当计算机上电复位后就可以运行用户编制的程序了。当然,其间要

经过查找错误、改正错误的过程。

4.1 Keil μVision2集成开发环境的简单使用

目前,单片机C 语言的开发环境主要是Keil μVision2,该软件是用于 8051 系列及其衍

生的单片机的开发工具,可以支持汇编和 C语言。μVision2 集成开发环境集成了项目管理

器、功能完善的编辑器、仿真器、各种选项设置工具以及在线帮助。Keil μVision2 是 51 系

列单片机最佳的软件开发工具。该软件可以在网络上免费下载。

4.1.1 Keil μVision2 中建立项目的方法

Keil μVision2 软件界面如图 4­1所示,从图中可以看出,该界面与 Visual C++环境相似。

Keil μVision2 使用方便,下面介绍在该开发环境中编制程序的方法。

图 4-1 Keil μVision2 软件界面

Page 74: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 74 .

要想从头开始编制一个 C语言程序,首先要打开Keil μVision2,然后从 Project下拉菜

单中左键单击 New Project... 选项,如图 4­2所示。马上会弹出图 4­3所示的对话框,在该

对话框中选择合适的路径(如桌面main文件夹)、输入项目名称(如 text),然后保存。

图 4-2 新建工程

图4-3 新建工程对话框

图 4­4 单片机型号选择对话框

Page 75: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 75 .

然后会出现一个如图 4­4所示的对话框,该对话框用来选择系统要使用的单片机的信

号,假设是针对 AT89C52RC芯片编程,在左边的树形目录中单击 Atmel左边的“+”符号,

下面就会显示Atmel公司所有的单片机型号,然后选择 AT89C52,然后单击“确定”。在紧

接着出现的对话框中单击“是”(如图 4­5所示),系统就会自动加入启动代码。这时集成开

发环境左边的 Project Workspace 区就出现了一个 Target 1的目标项目。左键点击 Target 1左边的“+”号,可以看到展开的目录有个 Source Group 1,再单击 Source Group 1左边的“+”号,可以看到项目中已经加入了 STARTUP.A51 启动代码,如图 4­6所示。这时就可以向工

程里添加程序文件了。

图 4­5 选择添加文件到工程

图4-6 工程初步建立界面

从 File下拉菜单选择 New...或者左键点击工具栏上的 New File按钮,就可以打开一个

文本编辑区,此时就可以在该编辑区中输入所需的代码了,假设所输入的代码功能为从单

片机的串口输出一行字符“You are OK!”,如图 4­7所示。输入结束后,从 File下拉菜单选

择 Save 或者左键点击工具栏上的 Save 按钮,在弹出的对话框里输入文件名,如 text.c(注

意一定要加后缀.c),然后保存。

Page 76: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 76 .

图 4-7 代码编辑界面

图 4-8 源程序加载对话框

然后从Project Workspace区里右键单击Source Group 1,在弹出的菜单中选择Add File togroup ‘Source group 1’,在弹出的对话框里用左键选中刚才建立的源程序文件(此处为 text.c),然后单击 add 按钮,即可把该源程序文件加入到工程。如图 4­8所示。

此时项目已经建立完毕,但是在实际应用之前要进行设置。 从 Project下拉菜单选择

Option for target ‘target 1’。在弹出的标签页对话框中选择 Target,这里有很多选项可选,

把存储器模式选为 Large,则程序中用到的变量就优先存放到外部数据存储区,一般这里选

择 Small,则程序中用到的变量就优先存放到内部数据存储区,执行速度较快;程序段大小

选为 Large;并选中右侧三个单选框(使用片内 ROM、使用片内XRAM、使用多 DPTR寄

存器)。如果外部扩展了程序存储器或数据存储器,则需在下面填入存储器的起始地址及存

储区大小。注意:必须选择Use on­chip ROM(0x0~0x1FFF)选择完毕后如图 4­9 所示。

Page 77: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 77 .

图 4­9 设置对话框

然后单击该对话框上面的Output标签,选上 Create HEX File 单选框,如图 4­10所示,

于是编译项目后就可以生成十六进制文件了。单击该对话框下面的“确定”,刚才的设置就

有效了。

图 4­10 生成 16进制文件设置

单击File下拉菜单中的Save All或单击工具栏上的该按钮,以保存全部工程。然后单

Page 78: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 78 .

击工具栏中的 (Translate current file) 图标(Buid targed) (Rebuild all

targed file) 以编译该工程,此时在所建的工程文件夹中就会生成十六进制文件

text.hex。把该十六进制文件用编程器等下载到单片机的程序存储器中,上电后程序即可

运行。

4.1.2 Keil μVision2 中软件调试的方法

Keil μVision2提供了在计算机中运行和调试单片机源程序的机制,软件的调试可以在计

算机中脱离单片机进行。

1、软件调试

软件调试要通过Debug下拉菜单中的工具进行。Debug菜单如图4­11所示,共分为5块。

下面就其中主要部分进行分别介绍。

Start/StopDebug Session 命令用于启动或停止调试功能。启动调试功能后,Keil μVision2的项目窗口自动切换到寄存器标签页,显示CPU内部各寄存器的当前状态,寄存器的状态

将随着程序的执行而不断变化。

Go 命令用于启动用户程序从当前地址处开始全速运行,遇到断点或是执行“Stop

Running”命令时停止。

Step 命令用于单步执行程序。单击一次该选项则执行一条C语言指令,遇到函数调用

则进入被调用函数执行。

Step Over 命令用于启动用户程序从当前地址处开始执行一条语句。遇到函数调用时

不进入函数执行,而是将整个函数一起一次执行。

图4­11 debug 菜单

Step Out of current Function 命令用于在调用函数的过程中,启动函数从当前地址

Page 79: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 79 .

处开始执行并返回到调用该函数的下一条语句。

Runto Cursor Line 命令用于启动用户程序从当前地址处开始执行到光标所在行。

Stop Running 命令用于停止运行用户程序。

以上是Debug菜单中前两块的内容,其第三块的内容用于调试用户程序过程中的断点管

理。断点功能可在某种特定条件下暂停程序的运行,以便于观察程序的运行情况、查找和

排除错误。

Insert/Remove Breakpoint 命令用来在当前光标所在行插入或删除一个断点。

Enable/Disable Breakpoint 命令可激活或禁止当前光标所指向的一个断点。

Disble All Breakpoints 命令用来禁止所有已经设置的断点。

Show Next Statement 命令用来在编辑窗口中显示下一条将要被执行的用户程序语

句。

2、观察仿真结果

Keil μVision2可以通过内部集成的器件库实现对单片机外围集成功能的模拟仿真,在调

试状态下可以通过Peripherals菜单来观察仿真的结果。Peripherals菜单在调试状态下的

内容如图4-12所示。下面对该菜单中各选项进行分别介绍。

图4-12 Peripherals菜单

Reset CPU选项用来对模拟仿真的单片机进行复位。

Interrupt选项用来显示单片机中断系统状态。单击该选项会弹出图4-13所示的窗口。

在窗口中选中某一个中断源,窗口下面一栏将显示于该中断相对应的中断允许和中断标志

位的复选框,通过对这些状态位的置位或复位操作,可以实现对单片机系统的仿真。单片

机的中断系统在后续章节中作介绍。

I/O-Ports选项用来对单片机的并行I/O口P0~P3进行仿真,选中Port1后会弹出图4-14

所示的窗口。窗口中P1栏显示单片机P1口各位锁存器的状态;Pins栏显示P1口各个引脚的

状态。在进行仿真时以上各位可以根据需要进行修改。

Page 80: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 80 .

图4-13 中断系统状态窗口

图4-14 Port1窗口

Serial选项用来对单片机的串行口进行仿真。单击该选项会显示出图4-15所示的窗口。

该窗口中Mode栏用来选择串行口的工作方式。选定工作方式后相应的特殊功能寄存器 SCON

和SBUF等的控制字也显示在窗口中。通过对SM2、REN、TB8、RB8、TI和RI等特殊控制位复

选框置位或复位就可以实现对单片机串行口的仿真。窗口中的Baudrate栏用来显示串行口

工作时的波特率,SMOD位置位时将使波特率加倍。窗口中的IRQ栏显示串行口的发送和接收

中断标志。单片机的串行口在后续章节中作介绍。

Timer选项用来对单片机内部的定时器/计数器、PCA定时器、PCA模块和内部看门狗进

行仿真,对不同类型的单片机,该选项内容不尽相同。选中Timer0后将弹出图4-16所示的

窗口。该窗口中Mode栏用来选择定时器的工作方式以及是定时器方式或计数器方式。选定

好工作方式后,相应的特殊功能寄存器TCON和TMOD的控制字也显示在窗口中,TH0和TL0用

来显示计数初值,T0 Pin和TF0复选框用来显示T0引脚和定时器/计数器的溢出状态。TR0、

GATE、INT0#是启动控制位,通过对这些位置位或复位就可以实现对单片机内部定时器/计

数器的仿真。单片机的定时器/计数器等内容在后续章节中作介绍。

图4-15 串行口窗口 图4-16 定时器0窗口

3、 Keil μVision2中的常用工具栏

在进行仿真调试的时候,要经常用到Keil μVision2开发环境中的工具栏。常用的工具栏

图标如图4­17所示,此图中的状态为启动仿真的状态,从左到右共分为四栏。

Page 81: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 81 .

图标为运行控制,相当于Debug菜单中Go。当启动仿真(单击Debug菜单中的Start/Stop

Debug Session选项)后,系统处于待运行状态。这时单击该图标,程序就开始从头运行。

这时图4­17中第三个图标变为 ,该图标为停止运行控制,程序运行的时候,单击该图标

则停止运行。

图4-17 Keil μVision2中的工具栏

图标用来模拟复位单片机。

第二栏和第三栏中的工具在Debug菜单中有介绍,这里不再赘述。

用来显示反汇编窗口,单击该图标后,在弹出的窗口中会显示已经装入到Keil

μVision2的用户程序汇编语言指令、反汇编代码及地址。

用来在调试状态下观察窗口的显示 /隐藏切换。观察窗口有四个标签页,分别是

Locals、Watch #1、Watch #2和Call Stack。Locals用来显示用户程序调试过程中当前局部变

量的使用情况;Watch #1用来显示程序中已经设置了的观察点在调试过程中的当前值;CallStack用来显示程序在执行过程中对函数的调用情况。

用于串行窗口1的显示/隐藏切换。单击该图标会弹出一个窗口,该窗口在进行用户

程序调试时非常有用,如果用户程序中调用了C51的库函数scanf( )或printf( ),则必须利用该

窗口来完成scanf( )函数的输入操作,printf( )函数的输出结果也将显示在该窗口中。利用该

串行窗口,用户可在仿真调试时实现人机交互,或者对程序的运行结果进行实时显示。

用于系统存储器空间的显示/隐藏切换。单击该图标会弹出显示存储器空间的窗口,

在该窗口中Address处键入存储器地址,将立即显示对应存储器空间的内容。

4.2 STC89C51RC系列单片机的 ISP 编程

上一节介绍了在计算机中开发程序的方法,相信读者可以很容易地输入自己的源程序

并将其编译链接成十六进制文件。那么如何才能把该十六进制文件下载到单片机的程序存

储器里呢?本节就来讨论这个问题。

单片机程序的下载有以下几种方式:

1、用商用编程器编程。这是最古老的编程方式,该方式的适用范围广,既可对 EPROM

型单片机编程,也可对FLASH型单片机编程。只是该方式比较麻烦,必须把单片机或外围存

储器脱离单片机系统,并将其置入编程器才能实现程序的下载,因此应用比较费力,目前

只在一些低价位的传统单片机编程时使用。

2、利用仿真器编程。现在很多FLASH型单片机都支持JTAG边界扫描方式,也都配有JTAG

Page 82: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 82 .

接口,这种单片机就可以使用带有JTAG接口的仿真器进行程序的下载,如Cygnal公司的

C8051F系列单片机。这种编程方式无需把单片机脱离系统,可以实现单片机在系统编程,

因此不必频繁的插/拔单片机,使用非常方便。

3、ISP编程。目前很多公司的单片机都支持ISP编程,如STC系列单片机、飞利浦公司

的P89C51RX2系列单片机。所谓ISP是指在系统编程(In-System Programming),只需三根

线来连接上位计算机和单片机即可实现对单片机的编程。该编程方式无需昂贵的编程器或

仿真器,因此应用方便,且编程时无需把单片机脱离系统。

STC89C51RC 系列单片机片内程序存储器才用了最先进的FLASH存储器结构,比EPROM存

储器的改进之处是不仅可读,而且可以用软件快速地擦除和写入,从而引出了在系统编程

(ISP)技术。STC89C51RC系列单片机也可用商用编程器进行并行地编程。

4.2.1 ISP编程硬件电路

ISP编程需要使用单片机的5个引脚TxD、RxD、Vcc、Vss(GND)和Vpp。为了和主机进

行RS232串口通信,还需要少量的器件和DB9型小插座。STC89C52 系列单片机ISP编程的硬

件电路如图4-18所示。

图4-18中,DB9插座与IBM PC及其兼容机标准串口连接,与主机中的ISP编程软件(STC-

ISP)配合使用。单片机直接置于系统中,系统只要留出ISP编程接口即可。此时就可以在

PC机上运行ISP编程软件进行写入、擦除、校验等操作。具体ISP编程方法见下一小节描述。

594837261

VCC

31

DB9

13 R1IN

14 T1OUT

R1OUT 12

11T1IN

RXD

TXD

10

11

C1+C1­C2+C2­

13

5

104

104

8

10R2IN

T2IN

VCC

V+V­

GND

16

2

615

104

104

VCC

+100μ

MAX232

9 RESET

1819

XTAL2XTAL1

复位电路

接晶体

20 GND

40VCC

EA/VPP

4

RxdPC

TxdPC

STC89C52

图4-18 ISP编程电路原理图

4.2.2 STC_ISP 下载软件

按照图4-18所示的连接方式,把单片机与主机连接好以后,就可以使用ISP下载软件进

行程序的写入了。

STC_ISP是专门针对STC公司支持ISP编程的单片机进行ISP写入的软件。该软件的界面

如图4-19所示。

Page 83: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 83 .

图4-19 STC下载软件

在使用STC软件下载程序时,一般遵循以下步骤:

(1)选择单片机型号。用鼠标左键单击界面中步骤1中的MCU type下面的选择对话框

右侧的向下箭头,选择所使用的单片机型号。如图4-20所示(假设所用的是STC89C52RC系

列)。

图4-20 选择单片机型号

(2)选择串口。在步骤3中的“COM”右侧用鼠标选择主机所使用的串行口,有COM1~

COM16个选项,实际连接了哪个串口就选择对应的串口号。如图4-21(一般选择COM1)

图4-21 选择串口

(3)选择波特率。在步骤3中的“Max buad”右侧用鼠标左键选择通信所需的波特率(可

Page 84: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 84 .

选择115200)。如图4-21

(4)在步骤4中,可以根据需要选择速度。STC89C52单片机支持6时钟模式,若选择了

6T/双倍速,则单片机运行速度是12时钟时的2倍。当速度够用时,我们选用12T/单倍速。

选择OSCDN中的full gain。其它按默认设置。如图4-22所示。

图4-22 选择波特率

(5)选择要下载的文件。在步骤2中点击Open File,选择想要下载的程序文件。如图

4-23所示。

图4-23 打开下载文件

(6)单击Open File按钮后,会出现图4-24所示的文件打开对话框。单击“查找范围”

右侧的下拉箭头,选择我们工程文件所保存的文件夹,点击选择已经编译好的十六进制文

件(.HEX文件),然后单击“打开”。就把该十六进制文件载入了STC缓冲区,例如我们选

择了显示文件程序,如图4-24所示。

图4-24 选中十六进制文件

Page 85: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 85 .

(7) 在STC下载软件的右侧文件缓冲区中有相应的十六进制的数据生成,如图4-25所

示。此时点击图4-26所示的“Download/下载”按钮就可以把程序下载到单片机中,注意:

在下载程序时,单片机应该处于断电状态,当“Download/下载”下面的对话框中提示:“请

给MCU上电”,此时方可通电,否则下载程序会失败。如图4-26所示。

图4-25 打开的16进制文件

图4-26 单片机上电提示

到这里相信读者已经掌握了单片机ISP编程的方法了,现在就可以把编制好的程序下载

到单片机中进行实际应用了。如果读者使用的是不支持ISP编程的51系列单片机,则可用通

用编程器进行程序的写入。

Page 86: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 86 .

第 5章 STC89C51 系列单片机的结构和原理

5.1 STC89C51系列单片机的主要性能特点

STC89C51系列单片机是宏晶科技推出的新一代超强抗干扰、高速、低功耗的单片机,

指令代码与传统 8051 单片机完全兼容,12时钟/机器周期和 6 时钟/机器周期可任意选择。

特点:

1、增强型 12时钟/机器周期和 6时钟/机器周期的 8051 CPU;2、工作电压:5.5V­ 3.4V(5V单片机)/ 3.8V – 2.0V(3V单片机);

3、工作频率范围:0­40MHz,相当于普通 8051 的 0­80MHz,实际工作频率可达 48MHz;4、用户应用程序空间 4K / 8K / 13K / 16K / 20K / 32K / 64K 字节;

5、片上集成 1280 字节/512字节 RAM;

6、ISP(在系统编程)/IAP(在应用编程),无需专用编程器、仿真器,可直接通过串

口直接下载用户程序;

7、含有片上 FLASH,可直接用程序进行读写;

8、带有内部看门狗定时器,可有效防止程序死锁;

表5-1 STC89C51系列单片机型号及详细特性

9、共 3 个 16位的定时/计数器,其中定时器 0还可以当成 2个 8位的定时器使用;

Page 87: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 87 .

10、外部中断 4 路,可选择下降沿触发中断或低电平触发中断;

11、通用异步串行口(UART),还可通过定时器软件实现多个串口;

12、封装:PDIP­40、PLCC­44、PQFP­44。STC89C51系列单片机型号及详细特性见表 5­1。

5.2 STC89C51系列单片机的内部结构

如图5-1所示为STC89C51系列单片机的基本结构,它由以下几大部件组成:中央处理

器(CPU),片内数据存储器(RAM),片内程序存储器(FLASH),输入输出接口(I/O口,

分为P0口、P1口、P2口和P3口),可编程全双工串行口,定时/计数器,中断系统和特殊功

能寄存器(SFR)。此外,还在基本的8051基础上增加了内部看门狗定时器。各部分通过内

部总线相连。其基本结构依然是采用CPU加上外围芯片的结构模式,但在功能单元的控制上,

却采用了特殊功能寄存器(SFR)的集中控制方法。

硬件结构各部分的功能与原理将在后续章节中分别介绍。

增强的80C51CPU12时钟模式 ,6时钟模式

4K/ 8 K/ 64KB程序FLASH

512B/1280B数据RAM

端口3可配置的 I/O口

端口0可配置的 I /O口

端口1可配置的 I/O口

端口2可配置的 I/O口

振 荡器晶体 或谐振器

看门狗定时器

定时器2

定时器0定时器1

全双工增强型

UART

...

图 5-1 STC89C51 系列单片机的结构框图

Page 88: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 88 .

5.3 STC89C51系列单片机的引脚功能

图5-2 所示为STC89C51系列单片机的 DIP封装引脚图。其中有 2条为电源引脚,2条

外接晶体引脚,4条控制引脚,32 条 I/O引脚。下面分别介绍这些引脚的功能。

1.电源引脚

VSS(20脚):接地,0V 参考点。

VCC(40脚):电源,提供掉电、空闲、正常工作电压。

2.外接晶体引脚

XTAL1(19 脚):接外部晶体的一端,振荡反向放大器的输入端和内部时钟电路输入端。

XTAL2(18脚):接外部晶体的另一端,振荡反向放大器的输出端。

T2/P1.0T2EX/P1.1ECI /P1.2

CEX0/P1.3CEX1/P1.4CEX2/P1.5CEX3/P1.6CEX4/P1.7

RSTRxD/P3.0TxD /P3.1INT0/P3.2INT1/P3.3T0/P3.4 P2.6/A14

P2.7/A15PSENALE/PROG

P0 .2/AD 2P0 .3/AD 3P0 .4/AD 4P0 .5/AD 5P0 .6/AD 6P0 .7/AD 7EA/Vpp

P0 .1/AD 1P0 .0/AD 0Vcc1

234567891011121314 27

28293031323334353637383940

T1/P3.5WR /P3.6RD/P3.7XTAL 1XTAL 2

Vss P2.0/A8P2.1/A9P2.2/A10P2.3/A11P2.4/A12P2.5/A1315

1617181920 21

2223242526

STC89C51

图5-2 DIP 管脚功能

3.控制引脚

RST(9脚):复位端。当晶体在运行时,只要此引脚上出现2个机器周期高电平即可复

位,内部有扩散电阻连接到Vss,仅需要外接一个电容到Vcc即可实现上电复位。

ALE(30脚):地址锁存使能。在访问外部存储器时,输出脉冲锁存地址的低字节,在

正常情况下,ALE输出信号恒定为1/6振荡频率。并可用作外部时钟或定时,注意每次访问

外部数据时,一个ALE脉冲将被忽略。

PSEN(29脚):程序存储使能。读外部程序存储。当从外部读取程序时,PSEN每个机

器周期被激活两次,在访问外部数据存储器时PSEN无效,访问内部程序存储器时PSEN无效。

EA/Vpp(31脚):外部寻址使能/编程电压。在访问整个外部程序存储器时,EA必须

外部置低。如果EA为高时,将执行内部程序。当RST释放后EA脚的值被锁存,任何时序的

改变都将无效。该引脚在对FLASH编程时用于输入编程电压(Vpp)。4.输入/输出引脚

P0口(P0.0-P0.7,39-32脚):是双向8位三态I/O口。可向其写入1 使其状态为悬浮,

用作高阻输入。P0口也可以在访问外部程序存储器时作地址的低字节,在访问外部数据存

储器时作数据总线,此时通过内部强上拉传送1。

P1口(P1.0-P1.7,1-8脚):是带内部上拉的双向I/O口。向P1口写入1时,P1口被内

Page 89: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 89 .

部上拉为高电平,可用作输入口;当作为输入脚时,被外部拉低的P1口会因为内部上拉而

输出电流。

P2口(P2.0-P2.7,21-28脚):是带内部上拉的双向I/O口。向P2口写入1时,P2口被

内部上拉为高电平,可用作输入口。当作为输入脚时,被外部拉低的 P2口会因为内部上拉

而输出电流。在访问外部程序存储器和外部数据时分别作为地址高位字节和16 位地址,此

时通过内部强上拉传送1。当使用8位寻址方式访问外部数据存储器时,P2口发送P2 特殊功

能寄存器的内容。

表5-2 P3口的第二功能

P3口(P3.0-P3.7,10-17脚):是带内部上拉的双向I/O 口。向P3 口写入 1时,P3

口被内部上拉为高电平,可用作输入口。当作为输入脚时,被外部拉低的 P3 口会因为内部

上拉而输出电流。P3口脚具有第二功能,表 5-2介绍了 P3口的第二功能。

5.4 STC89C51系列单片机的主要组成部分

STC89C51系列单片机由中央处理器(CPU)、存储器和 I/O接口三大主要部分组成。

5.4.1 CPU

8位高性能的 CPU是单片机的核心部分,它的作用是读入并分析没每条指令,根据各指

令的功能,控制单片机的各功能部件执行指定的操作。它由以下两部分组成:

1.运算器

运算器由算术逻辑部件ALU、累加器A、寄存器B、暂存寄存器、程序状态字寄存器PSW

组成。它的任务是完成算术和逻辑运算、位变量处理和数据传送等操作。

ALU的功能强大,既可实现 8 位数据的加、减、乘除算术运算和与、或、异或、循环、

求补等逻辑运算,同时还具有位处理功能。

累加器A 用于向 ALU 提供操作数和存放运算的结果。在运算时将一个操作数经暂存器

送到ALU,与另一个来自暂存器的操作数在ALU 中进行运算,运算后的结果又送回累加器A。

寄存器B 在乘、除运算时用来存放一个操作数,也用来存放运算后的一部分结果。在

不进行乘、除运算时,可以作为通用的寄存器使用。

口 线 第二功能 类型 名 称

P3.0 RxD I 串行输入口

P3.1 TxD 0 串行输出口

P3.2 INT0 I 外部中断0

P3.3 INT1 I 外部中断1

P3.4 T0 I 定时器0 外部输入

P3.5 T1 I 定时器1 外部输入

P3.6 WR O 外部数据存储器写信号

P3.7 RD O 外部数据存储器读信号

Page 90: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 90 .

暂存寄存器用来暂时存放数据总线或其它寄存器送来的操作数。它作为 ALU 的数据输

入源,向ALU 提供操作数。

程序状态字寄存器PSW 用来保存ALU运算结果的特征(如:结果是否为 0,是否溢出等)

和处理器状态。这些特征和状态可以作为控制程序转移的条件。

2.控制器

控制器由指令寄存器IR、指令译码器 ID、定时及控制逻辑电路和程序计数器 PC组成。

程序计数器PC 是一个 16位的计数器。它总是存放着下一个要取的指令的存储单元的

16位地址。也就是说,CPU 总是把PC 的内容作为地址,按该地址从内存中取出指令码或含

在指令中的操作数。因此,每取完一个字节后,PC 的内容自动加 1,为取下一个字节作好

准备。只有在程序转移、子程序调用和中断响应时例外,那时的PC 不再加1,而是由指令

或中断响应过程自动给PC 置入新的地址。单片机上电或复位时,PC自动清 0,即装入地址

0000H,这就保证了单片机上电或复位后,程序从0000H 地址开始执行。

指令寄存器IR 用来保存当前正在执行的一条指令。若要执行一条指令,首先要把它从

程序存储器取到指令寄存器中。指令的内容包括操作码和地址码两部分,操作码送往指令

译码器ID,经其译码后便确定了所要执行的操作,地址码送往操作数地址形成电路以便形

成实际的操作数地址。

定时与控制逻辑是中央处理器的核心部件,它的任务是控制取指令、执行指令、存取

操作数或运算结果等操作,向其它部件发出各种微操作控制信号,协调各部件的工作。

5.4.2 存储器

1.数据存储器(RAM)

STC89C51系列单片机内部有512B/1280B RAM,片外最多可扩展64KB RAM。

内部数据存储器分为四部分:RAM低128字节、高128字节、128字节特殊功能寄存器(SFR)

和256字节扩展RAM(ERAM)。

1)内部RAM低128字节(地址00H到7FH)

这部分RAM存储器应用最为灵活,可用于暂存运算结果及标志位等,可直接或间接寻址。

按其用途还可以分为3个区域。

① 工作寄存器区。从地址00H-1FH安排了4组工作寄存器,每组占用8个RAM字节,记为

RO-R7。在某一时刻,CPU只能使用其中的一组工作寄存器,工作寄存器组的选择则由程序

状态字寄存器PSW中两位来确定。

② 位寻址区。占用地址20H-2FH,共16B,128位。这个区域除了可以作为一般RAM单元

进行读写外,还可以对每个字节的每一位进行操作,并且对这些位都规定了固定的位地址,

从20H单元的第0位起到2FH的第7位止共128位,用为地址00H-FFH分别与之对应。对于需要

进行按位操作的数据,可以存放到这个区域。

③ 用户RAM区。地址为30H-7FH,共80B。这是真正给用户使用的一般RAM区,在一般应

用中常把堆栈开辟在此区中。

2)内部RAM高128字节(地址80H到FFH)

这128字节与特殊功能寄存器(SFR)占用相同的地址空间,不过它们在物理上是分开

Page 91: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 91 .

的。当指令访问高于7FH的内部地址时,CPU通过使用的寻址方式确定是对高128字节RAM还

是对SFR进行访问。

STC89C51系列单片机内部有多个特殊功能寄存器(SFR),表5-3列出了这些特殊功能

寄存器(SFR)的符号、名称、地址和复位后的值。

表5-3 STC89C51系列单片机特殊功能寄存器(SFR)

注:带*号的SFR可位寻址;带#号的SFR表示从80C51的SFR修改而来或新增加的

访问这些特殊功能寄存器只允许使用直接寻址方式,在指令中,既可以使用特殊功能

寄存器的符号,也可以使用它们的地址,使用寄存器符号可以提高程序的可读性。

寄存器符号 寄存器名称 地址 复位值

ACC* 累加器 E0H 00H

B* 寄存器B F0H 00H

DPH 数据存储器指针高字节 83H 00H

DPL 数据存储器指针低字节 82H 00H

IE* 中断使能 A8H 00H

IP* 中断优先级 B8H x0000000B

IPH# 中断优先级高字节 B7H x0000000B

P0* P0 口 80H FFH

P1* P1 口 90H FFH

P2* P2 口 A0H FFH

P3* P3 口 B0H FFH

PCON#1 电源控制寄存器 87H 00xxx000B

PSW* 程序状态字 D0H 00000000B

RACAP2H# 定时器2 捕获高字节 CBH 00H

RACAP2L# 定时器2 捕获低字节 CAH 00H

SADEN# 从地址屏蔽 B9H 00H

SADDR# 从地址 A9H 00H

SBUF 串口数据缓冲区 99H xxxxxxxxB

SCON* 串行口控制 98H 00H

SP 堆栈指针 81H 07H

TCON* 定时器控制 88H 00H

T2CON* 定时器2 控制 C8H 00H

T2MOD# 定时器2 模式控制 C9H xxxxxx00B

TH0 定时器0 高字节 8CH 00H

TH1 定时器1 高字节 8DH 00H

TH2# 定时器2 高字节 CDH 00H

TL0 定时器0 低字节 8AH 00H

TL1 定时器1 低字节 8BH 00H

TL2# 定时器2 低字节 CCH 00H

TMOD 定时器模式 89H 00H

Page 92: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 92 .

可位寻址的特殊功能寄存器的具体介绍见后续章节。访问可位寻址的寄存器的各位时,

既可以使用它的位符号,也可以使用它的位地址,还可以用“寄存器名.位”来表示,如ACC.1

表示ACC寄存器的第一位。使用位符号比较方便,而且可以增加程序的可读性。

3) 256/1024字节扩展RAM

这些片内RAM是系列单片机新增加的,有了这些新增RAM,使得该系列单片机可以应用

于较复杂的场合而无须外扩RAM,大大增加了适用范围。这部分RAM在物理上位于片内,在

逻辑上占用了外部数据存储器开始的若干字节。

2.程序存储器(FLASH)

程序存储器用于存放编制好的程序、表格和程序中用到的常量。FLASH存储器增加了

EPROM 所没有的电可擦除和编程特性。对芯片的擦除操作将整个程序存储区都擦除。块擦

除功能可实现对任意Flash块进行擦除。在系统编程和标准的并行编程都是可行的。片内产

生的擦除和写入时序为用户提供了良好的编程接口。

有三种方法可实现对Flash 的编程或擦除。第一,在应用程序中调用 Boot ROM中的擦

除/写入等子程序进行串行的在运行中编程(IAP);第二,用 Boot ROM固件由主机进行串行

的在系统中编程(ISP);第三,使用商用编程器进行并行编程。

由于STC89C51 系列单片机有较大的程序存储器空间,在一般的应用领域无须扩展程序

存储器,因此在实际应用中比较方便。

5.4.3 I/O接口

STC89C51系列单片机有 4 个 8 位的并行I/O 端口,分别记为 P0、P1、P2 和 P3,共 32

根线。每个端口都包含一个锁存器、一个输出驱动器和一个输入缓冲器。

四个I/O 口在结构和特性上是基本相同的,但又各具特色,下面详细的介绍它们的结

构,相信会对设计单片机外围逻辑电路有所启发。

1.P0 口

图 5-3 画出了P0口的某一位结构,它由一个输出锁存器、两个三态输入缓冲器和输出

驱动电路几控制电路组成。

I/O口的每位锁存器均由 D 触发器组成,用来锁存输入/输出的信息。在 CPU的“写锁

存器”信号驱动下,将内部总线上的数据写入锁存器中。

两个三态缓冲器,一个用来 “读引脚”信息,即将 I/O 引脚上的信息读到内部总线,

送CPU 处理;另一个用来“读锁存器”,即把锁存器内容读到内部总线上,送CPU 处理。因

此,对某些 I/O指令可读取锁存器的内容,而另外一些指令则是读取引脚上的信息,应注

意两者之间的区别。

输出控制电路由一个与门、一个反向器和一个模拟转换开关(MUX)组成。多路转换开

关用于在对外部存储器进行读/写时要进行地址/数据的切换。

输出驱动电路由两个串联的场效应管(VT1和 VT2)组成。当 P0 口作为一般 I/O 口使

用时,CPU 送来的控制信号为低电平,此时模拟开关处于图 5-3所示的位置,/Q 端与输出

驱动电路下面的 VT2 栅极接通。因控制信号为低,与门输出为 0,使VT1 截止。这时,当

CPU向 P0口输出数据时,即CPU 对 P0 口进行写操作时,写脉冲加到锁存器的时钟端CP上,

Page 93: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 93 .

锁存器的状态取决于 D 端的状态。当 Q 端为高,/Q 为低,而/Q 与 VT2 的栅极连通,故 P0

端口的状态刚好与内部总线的状态一致。

当输入数据时,由于外部输入信号既加在缓冲输入端上,又加在驱动电路的漏极上,

如果这时VT2 是导通的,则引脚上的电位被钳位在0 电平上,输入数据不可能正确地读入。

因此,在输入数据时,应先把 P0口置 1,使两个场效应管均关断,使引脚成为高阻状态,

这样才能正确地输入数据。这就是所谓的准双向口。

在有外部扩展存储器时,P0 口必须作为地址/数据总线用,这时就不能把它作为通用的

I/O口使用了。当从 P0口输出地址/数据时,控制信号为高,使 MUX向上与反向器输出端接

通,同时与门打开,地址/数据便通过与门及 VT1传送到P0 口,当从P0 口输入数据时,则

通过下面的缓冲器进入内部总线。

Q

QD

CP

1

2

1&读锁存器

内部总线

写入

读引脚

锁存器MUX

VT1

VT2

Vcc控制C地址/数据

图 5-3 P0 口某一位的结构

2.P1口

P1口也是一个 8位的准双向并行I/O 口,与基本的8051 单片机不同,它是一个多功能

端口,除作普通的 I/O 口外,它还有第二功能,具体定义前面以介绍,这里不再重复,第

二功能的具体应用请参考相关章节。

由于与P3 口类似,P1 口的某一位结构不再画出,请参图5-5。在电路结构上,P1口的

输出驱动部分与P0 口不同,内部有上拉电阻与电源相连,与场效应管共同组成输出驱动电

路。当P1口输出高电平时,可以向外提供拉电流负载,所以不必再接上拉电阻,当输入时,

与P0 口一样,必须先向锁存器写1,使场效应管截止。由于片内负载电阻较大,所以不会

对输入数据产生影响。

3.P2口

P2口在结构上比 P1口多了一个输出转换控制部分,多路开关MUX 的倒向由CPU命令控

制,内部也有固定的上拉电阻。P2口的某一位结构见图 5-4。

QD

CP

1

2

读锁存器

内部总线

写入

读引脚

锁存器

MUX

控制地址

1

Vcc

P2.X

图 5-4 P2 口某一位的结构

P2口既可作为通用 I/O口使用,又可作为地址总线口,传送地址高8 位。

Page 94: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 94 .

当P2 口用来作为通用 I/O口时,是一个准双向的 I/O 口。此时,CPU 送来的控制信号

为低电平,使转换开关 MUX 与锁存器的 Q 端接通。当输出信息时,引脚上的状态即为 Q端

的状态。当输入信息时,也要先用软件使输出锁存器置1,然后再进行输入操作。

当单片机外部扩展有存储器时,P2口可用于输出高 8 位地址,这时CPU 送来的控制信

号应为高电平,使MUX 与地址接通,此时引脚上得到的信息为地址。

在外接程序存储器的系统中,由于访问外部程序存储器的操作连续不断,P2口将不断

输出高8 位地址,所以这时P2口不再作通用 I/O口使用。

4.P3口

P3口是一个多功能端口,其某一位的结构见图 5-5。

当 P3 口作为通用I/O口使用时,第二输出功能端应保持高电平,打开与非门,是锁存

器输出Q 端的状态能顺利通过与非门送到引脚上。输入时,先置输出锁存器为 1,使场效应

管截止,再公共三态缓冲器读引脚信息。

QD

CP

1

2

读锁存器

内部总线

写入

读引脚

锁存器

Vcc

P3.X

&

3

第二输出控制

图 5-5 P3 口某一位的结构

当P3 口作为第二功能使用时,应先将输出锁存器置 1,使与非门畅通。输出时,第二

输出功能端的信息通过与非门送到引脚上。输入时,也先置输出锁存器为 1,使场效应管截

止,引脚上的第二输入功能信号经第一个缓冲器输入。不论作为输入口使用还是第二功能

信号输入,图5-5 中的锁存器输出和第二输出功能端都应保持高电平。

5.I/O口的读-修改-写操作

每个I/O 口都有两种读入方法:读锁存器和读引脚,并有相应的指令。读锁存器指令

是从锁存器中读取数据,送 CPU处理,再把处理后的数据重新写入锁存器中,这类指令称

为读-修改-写指令。

在读-修改-写指令中,目的操作数必须是一个I/O口或 I/O口的某一位。例如:P2++,

P1.0=0,P1=0xff等。

读引脚指令一般是以 I/O 端口为源操作数的指令,执行读引脚指令时,三态门打开,

输入口状态。例如,读P1 口引脚指令为:dat=P1,dat为字符型变量。

对读-修改-写指令,直接读锁存器 Q 端而不是读引脚的原因是为了避免错读引脚上电

平的可能性。例如,若用一口位去驱动一个晶体管基极,当向此口位写1 时,晶体管导通,

并把引脚上的电平拉低,这时若从引脚上读取数据,则读的是晶体管的基极电平 0,与口锁

存器的状态1 不一样。而从锁存器Q端读取,就能避免这样的错误,得到正确的数据。

6.I/O口的负载能力

P0口的每位输出可驱动 8 个 LSTTL 输入,但把它作为通用 I/O使用时,输出级是开漏

Page 95: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 95 .

电路,故用它驱动 NMOS 输入时需外接上拉电阻;而把它作为地址/数据总线用时,则无需

外接上拉电阻。

P1-P3口的输出级均接有内部上拉电阻,它们的每一位输出可驱动3 个 LSTTL 输入。不

论是什么类型的单片机,它们的 P1-P3 口的输出端都可以被集电极开漏电路所驱动,而不

必再外加上拉电阻。

5.5 时钟电路与时序

5.5.1 时钟电路

单片机内部有一个用于构成振荡器的高增益反向放大器,引脚 XTAL1和 XTAL2分别是

反向放大器的输入端和输出端,由这个放大器与作为反馈元件的片外晶体或陶瓷谐振器一

起构成了一个自激振荡器,如图5-6 所示。这种方式形成的始终信号称为内部时钟方式。

至内部时钟电路

XTAL 1

XTAL 2

C1

C2

晶振

1

悬空

Vss

XTAL 2

XTAL 1带上拉电阻的

TTL门或CMOS门

外部振荡器

信号

STC89C51

STC89C51

图 5-6 时钟振荡电路 图 5-7 外部时钟接法

利用芯片内部的振荡电路,在XTAL1 和 XTAL2两端跨接晶体(或陶瓷)振荡器和两个

电容就构成了一个稳定的自激振荡器。晶体振荡频率可在 6MHz-40MHz之间选择(不能超过

单片机所允许的范围)。电容值无严格要求,但其取值对振荡器频率输出的稳定性、大小、

振荡电路起振速度稍有影响,C1、C2 可在 20pF-100pF 之间选择。一般当外接晶体时,电容

选为30pF±10pF;外接陶瓷振荡器时,电容选为 40pF±10pF。

此外,单片机的时钟还可以采用外接方式。所谓外接方式,是指利用外部振荡信号源

直接接入XTAL1 或 XTAL2。由于HMOS 和 CHMOS 单片机内部时钟进入的引脚不同,CHMOS型

由 XTAL1 进入,HMOS型由 XTAL2 进入,因此外部振荡信号源接入的方法也不同。图5-7 示

出了P89C51RX2 系列单片机时钟电路的外接方式。

外接振荡信号源方式常用于多块芯片同时工作,以便于同步。

5.5.2 有关时序的概念

计算机在执行指令时,一条指令经译码后产生若干个基本的微操作,这些微操作所对

Page 96: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 96 .

应的脉冲信号在时间上的先后顺序称为计算机的时序。下面介绍有关CPU 时序的几个基本

概念。

1.振荡周期

指为单片机提供定时信号的振荡源的周期,若为内部产生方式,则为石英晶体的振荡

周期。

2.时钟周期

也称为状态周期,用S 表示。时钟周期是计算机中最基本的时间单位,在一个时钟周

期内,CPU完成一个最基本的操作。一个时钟周期是振荡周期的 2倍。

3.机器周期

为便于管理,常把一个指令的执行过程划分为若干个阶段,每一个阶段完成一个基本

操作,例如:取指令、存储器读、存储器写等。完成一个基本操作所需要的时间称为一个

机器周期。

4.指令周期

完成一条指令所需要的时间称为指令周期,51系列单片机的指令周期含1-4个机器周期

不等,其中多数为单周期指令,还有2周期和4周期指令。4周期指令只有乘、除两条指令。

5.5.3 CPU 时序

由于STC89C51系列单片机的时钟模式有两种:12时钟模式和6时钟模式,以下介绍均针

对12时钟模式进行的。不论是内部时钟还是外部时钟,均需经过内部时钟发生器(一个二分

频触发器)而成为内部时钟信号,它向单片机提供了一个二节拍时钟信号,在每个时钟的前

半周期,节拍1信号P1有效;后半周期,节拍2信号P2有效,如图5-8所示。

XTAL1

XTAL2

C1

C2

S1 S2P1 P2 P1 P2

P1

P2时钟

发生器

MCU

图5-8 片内振荡器及时钟发生器

每个机器周期包含六个时钟周期(用S表示),每个时钟周期由节拍信号P1和节拍信号

P2组成。每个节拍持续一个振荡周期。因此,一个机器周期包含S1P1-S6P2共6个状态的12

个振荡周期。

Page 97: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 97 .

P1 P2S1

P1 P2S1

P1 P2S6

P1 P2S4

P1 P2S5

P1 P2S3

P1 P2S2

P1 P2S6

P1 P2S5

P1 P2S4

P1 P2S3

P1 P2S2

P1 P2S1

S1 S2 S3 S4 S5 S6

S1 S2 S3 S4 S5 S6

S1 S2 S3 S4 S5 S6 S1 S2 S3 S4 S5 S6

S1 S2 S3 S4 S5 S6 S1 S2 S3 S4 S5 S6

振荡器(XTAL2)

ALE

读操作码读下一个

操作码(丢弃)再读下一个操作码

读操作码 读第二字节 读下一个操作码

读操作码读下一个操作码 (丢弃) 再读下一个

操作码

再读下一个

操作码读下一个操作码

(丢弃)读操作码无取指

无取指无ALE

地址 数据

访问外部存储器

单字节双周期指令如:dat=XBYTE[0x2000]

(d)

(c)

(b)

(a)

单字节双周期指令 ,如DPTR++

双字节单周期指令 ,如ACC+=20

单字节单周期指令 ,如ACC++

图5-9 取指令和执行指令时序

图5-9列举了几种典型指令的取指和执行时序。在每个机器周期里,地址锁存允许信号

ALE两次有效。第一次出现在S1P2和S2P1期间,第二次出现在S4P2和S5P1期间。

在操作码被锁存到指令寄存器时,单周期指令从S1P2开始执行,如果是双字节指令,

在同一个机器周期的S4期间读入第二字节;如果是单字节指令,则在S4仍有读操作,但读

入的字节(下一个操作码)是无效的,且程序计数器不加1。在任何情况下,当S6P2结束时

都会完成操作。图5-9(c)为单字节双周期指令的时序,在2个机器周期内发生4次读操作

码的操作,由于是单字节指令,后3次读操作都是无效的。图5-9(d)给出了访问外部数据

存储器指令的时序,它是一条单字节双周期指令,在第一个机器周期的S5开始时,送出外

部数据存储器地址,在S6P2开始读/写数据。读写期间ALE端不输出有效信号。在第二机器

周期(即外部数据存储器已被寻址和选通后)也不产生取指操作。

5.6 单片机的复位

复位是使单片机的CPU或系统中的其它部件处于某一确定的初始状态,并从这一状态开

始工作。单片机的工作就是从复位开始的。

Page 98: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 98 .

5.6.1 复位电路

在振荡器工作时,将RST脚保持至少两个机器周期高电平(6 时钟模式时为12个振荡器

周期,12时钟模式时为24个振荡器周期)可实现复位。为了保证上电复位的可靠,RST 必

须保持足够长时间的高电平,该时间至少为振荡器的稳定时间(通常为几个毫秒)加上两

个机器周期。上电时VCC 和RST 必须同时上升以实现正确的启动。如果RST引脚持续保持高

电平,单片机就处于循环复位状态。

实际应用中,复位操作有两种基本形式:一种是上电复位,另一种是上电与按键均有

效的复位。如图5-10所示。

C1

R1

RSTC1

R2R1

S

RST

MCU MCU

VCCVCC

图5-10 单片机复位电路

1、上电复位

上电复位要求接通电源后,单片机自动实现复位操作。常用的上电复位电路如图5-10

左图所示。上电瞬间 RST引脚获得高电平,随着电容 C1 的充电,RST 引脚的高电平将逐渐

下降。该电路典型的电阻和电容参数为:晶振为12MHz 时,C1为 10µF,R1 为 8.2KΩ。

2、上电与按键均有效的复位

上电与按键均有效的复位电路如图5-10 右图所示。上电复位与前述相同。单片机运行

期间,利用按键也可以完成复位操作。

3、看门狗定时器复位

在实际应用系统中,为了保证单片机系统正常工作,一般都加入看门狗复位电路,这

样可以保证在程序跑飞的情况下系统能够自动复位。STC89C51 系列单片机内部带有硬件看

门狗定时器(WDT),无须外加看门狗电路,可以通过软件的设置而启动看门狗定时器。WDTRST

是一个只写寄存器,具体使用方法见后续章节。

5.6.2 复位后的状态

单片机复位操作使单片机进入初始化状态。初始化后,程序计数器PC=0000H,所以程

序从0000H 地址单元开始执行。单片机冷启动后,片内 RAM 为随机值,运行中的复位操作

不改变片内RAM 的内容。

特殊功能寄存器复位后的内容是确定的。见表5-3。某些寄存器复位后的意义为:

P0--P3=FFH,相当于各端口锁存器已写入1,此时不但可用于输出,也可以用于输入。

SP=07H,表明堆栈指针指向片内RAM 的 07单元(即第一个被压入的内容将写入到 08H

单元)。

Page 99: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 99 .

IP、IE 和 PCON的有效位为 0,分别表明各中断源处于低优先级、各中断均被关断、串

行通信的波特率不加倍。

PSW=00H,表明当前工作寄存器选为0 组。

5.7 低功耗设计

在很多情况下,单片机要工作在供点点困难的场合,如野外、空中等。对于便携式仪

器要求用电池供电,这时都希望单片机应用系统能低功耗运行。STC89C51系列单片机提供

了如下几种低功耗模式。

1、时钟停止模式

静态设计使时钟频率可以降至0MHz(停止)。当振荡器停振时,RAM 和SFR 的值保持不

变。该模式允许逐步应用并可将时钟频率降至任意值以实现系统功耗的降低。如要实现最

低功耗则建议使用掉电模式。

2、空闲模式

空闲模式(通常也指待机工作方式)是指CPU在不需要执行程序时停止工作,以取代不

停的执行空操作或原地踏步等待操作,达到减小功耗的目的。在空闲模式时, CPU 进入睡

眠状态,但片内的外围电路仍然保持工作状态。

空闲方式是通过设置电源控制寄存器PCON中的IDL位来实现的。用软件将IDL位置1,系

统就进入空闲模式。这时,送往 CPU的时钟信号被封锁,CPU停止工作,但中断控制电路、

定时/计数器和串行口继续工作,CPU 内容、片内RAM 和所有SFR 保持原来的值。

电源控制寄存器PCON的各位定义如下。

其中,SMOD1是波特率倍增位,SMOD0是SCON.7选择位,在串行通信中使用。

GF1、GF0:通用标志位,由软件置位、复位。

PD:掉电方式控制位,PD=1,则进入掉电方式。

IDL:空闲方式控制位,IDL=1,则进入空闲方式。

终止空闲模式有如下两种方法:任何被使能的中断(此时程序从中断服务程序处恢复

并继续执行)和硬件复位(与上电复位使用相同的方式启动处理器)均可。

3、掉电模式

为了进一步降低功耗,通过软件(将电源控制寄存器PCON中的PD位置1)可实现掉电模

式。该模式中,振荡器停振并且在最后一条指令执行进入掉电模式。降到2.0V 时,片内RAM

和SFR保持原值,在退出掉电模式之前Vcc 必须升至规定的最低操作电压。

硬件复位或外部中断均可结束掉电模式。硬件复位使SFR 值重新设置,但不改变片内

RAM 的值。外部中断允许SFR和片内RAM 都保持原值。

要正确退出掉电模式,在Vcc恢复到正常操作电压范围之后,复位或外部中断开始执行

D7 D6 D5 D4 D3 D2 D1 D0

SMOD1 SMOD0 - POF GF1 GF0 PD IDL

Page 100: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 100 .

并且要保持足够长的时间(通常小于10ms)以使振荡器重新启动并稳定下来。

使用外部中断时,INT0 和INT1 必须使能且配置为电平触发。将管脚电平拉低使振荡

器重新启动,退出掉电模式后将管脚恢复为高电平。一旦中断被响应, RETI(汇编语言中

的中断返回指令)之后所执行的是进入掉电模式指令的后一条指令。

当Vcc电压从0V上升到5V时,片内电路将上电标志位POF(电源控制寄存器中的第4位)

置位。上电标志位可通过软件置位或清零。用户可确定复位是由上电引起的还是由掉电唤

醒引起的。当Vcc电压在3V以上时POF不受影响

5.8 最小系统设计

所谓单片机最小系统是指能够让单片机正常运行所需的最基本的硬件配置,对于 8051单片机,由于其内部没有程序存储器,所以必须外扩程序存储器才能够正常工作。而80C51单片机内部含有程序存储器,所以不需外扩程序存储器即可正常工作。

图5­11给出了STC89C51单片机的最小系统原理图,一定要注意,任何电子线路要正常

工作必须有电源供应,而大部分教材上在画原理图的时候并没有给出电源,只给出信号线

的连接方式。

C1

R2R1

S

RSTVCC

XTAL 1

XTAL 2

C1

C2

晶振

9

18

19

40

20

31

VCC

VCC

GND

STC89C51

EA

图5­11 STC89C51单片机最小应用系统

可以看出,单片机要正常工作,必须给出晶振电路,以提供时钟信号;还要有复位电

路,因为单片机必须可靠复位后才能运行程序;必须有电源供应,以提供电能;要想使用

内部程序存储器,还必须将 引脚(31脚)接高电平。EA

按照图5­11的连接方式,如果单片机内部已经有了程序,则单片机就可以运行该程序了,

但是单片机还不能够干任何事情,因为单片机没有接任何外围电路。所以一定要注意,想

用单片机实现任何功能,必须配备相应的外围电路。

Page 101: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 101 .

第 6章 单片机简单应用

6.1 蜂鸣器的驱动

蜂鸣器是一种常用的提示元件,在工业及民用设备中经常用到,比如说全自动洗衣机

利用蜂鸣器的音乐提示衣服洗涤完毕、电子闹钟用蜂鸣器提示人设定的时间已到、工业上

用蜂鸣器发出警报提示系统故障或发生紧急事件等。

蜂鸣器分为直流和交流两种,直流蜂鸣器只要电源接通就会发出固定不变的声音,使

用简单,但无法实现动听的音乐;交流蜂鸣器需要给其提供频率信号才能使其发声,也就

是不断让交流蜂鸣器的电源通断,才会发出声音,控制电源通断的频率即可改变发出的声

音,频率高,则声音尖,频率低,则声音粗。

蜂鸣器驱动非常简单,因为单片机本身无法提供较大的驱动电流,所以一般用三极管

对其进行驱动。本节以交流蜂鸣器为例介绍其硬件连接方式与程序编制方法。

蜂鸣器的驱动电路如图 6­1所示。我公司 MCU2006 型开发板上蜂鸣器就采用该驱动方

式,三极管的基极用单片机的 P2.5口线控制。

图 6­1 蜂鸣器驱动电路

蜂鸣器的发声主要是靠单片机发送的不同频率而产生的,所以单片机要不断的发送

“1”、“0”信号;即单片机与蜂鸣器的接口要不断的 ON/OFF,从而根据所给的通断时间的

长短而发出不同的声音。

1、固定频率声音的蜂鸣器驱动程序

# include<reg52.h> //包含头文件,在该头文件中定义了 52 系列单片机所有的特殊功能

//寄存器,不包含该头文件,则特殊功能寄存器不可用

#define uint unsigned int

uint i; //定义循环变量

sbit fengming=P2^5; //定义特殊位变量 fengming 指向 P2.5 口线,以后对 fengming 变量

//操作就是对 P2.5 口线操作

void main( )

while(1) //无穷循环,蜂鸣器才能不断发声

Page 102: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 102 .

2、可变频率声音的蜂鸣器驱动程序

以上程序只能让蜂鸣器发出恒定频率的声音,若想让蜂鸣器发出比较好听的音乐,可

以通过改变蜂鸣器的频率来实现。以下为变频率的例子:

以上是简单的变音调的程序举例,若想让蜂鸣器发出真正的音乐,则需用定时器精确

控制蜂鸣器的频率,让其符合音乐的频率及节奏,就会真正演奏出动听的音乐,请读者自

己查阅相关资料。

注:我公司MCU2007­I 型单片机开发板上的蜂鸣器用 P1.7口线控制,若用该开发板,

只需将上述程序中的 sbit fengming=P2^5语句改为 sbit fengming=P1^7 即可。

6.2 继电器的应用

继电器是一种用弱电来控制强电通断的元件,在我们的日常生活及工业中应用极为广

泛,例如饮水机,当水烧开后,电源就被切断,当水温降低后,电源又被接通,这种通断

控制就是利用继电器来实现的。其原理特别的简单,当继电器的线圈通电,就会产生电磁

吸力使触点闭合,于是与触点相连的用电设备就被接入了电源。

用单片机控制继电器通断非常方便,就是用单片机的某一 I/O口进行控制,原理和上面

的蜂鸣器一样,不过我们的单片机驱动能力有限,不能直接驱动继电器,所以在设计电路

fengming=1; //蜂鸣器的关断;

for(i=0;i<100;i++); //空循环,延时。此延时长短可控制声音的频率

fengming=0; //蜂鸣器的开通;

for(i=0;i<100;i++); //空循环,延时。此延时长短可控制声音的频率

# include<reg52.h>

#define uint unsigned int

sbit fengming=P2^5;

void main()

uint i, j;

while(1) //无穷循环,蜂鸣器才能不断发声

for(j=50;j<120;j++)

fengming=!fengming; //蜂鸣器的通断;

for(i=0;i<j;i++); //空循环,延时。

Page 103: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 103 .

时,应该加入三极管作为驱动。在用到单片机控制强电时,就可能用到这个。我公司

MCU2007­I型单片机开发板上的继电器驱动电路如图 6­2所示。

图 6­2 继电器驱动原理图

图 6­2 中继电器线圈并联了一个续流二极管 D5,因为当继电器线圈从通电到断电时,

线圈储存的磁场能将释放,如果没有放电回路,则会产生瞬时高压。因此二极管D5用来提

供放电回路,防止放电产生的瞬时高压对电路产生不良影响。

继电器的驱动程序也很简单,如下所示,以下程序实现继电器的循环通断。

6.3 LED指示灯应用

LED(发光二极管)的应用非常广泛,常用来指示一些信息,如电源接通、系统故障、

#include<reg52.h>

sbit p10=P1^0;

void delay(unsigned int i) //定义延时函数

while(i­­);

void main()

while(1)

p10=1; //继电器的断开;

delay(30000); //调用延时函数实现一段延时

p10=0; //继电器的接通;

delay(30000);

Page 104: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 104 .

功能指示等;有时 LED还用于装饰,如各种灯饰;LED组成点阵,还可以作为显示器,如

公共场所的大屏幕显示器。

图 6­3 LED指示灯驱动原理图

我公司MCU2007­I 型单片机开发板上有 8 个 LED指示灯,可以实现流水灯的功能。8个发光二极管是采用共阳极接入,与 P0 口相连接,所以是低电平导通,我们可以通过控制

P0口的通断让它显示不同的形式,就像大街上的霓虹灯一样。LED指示灯的电路连接如图

6­3所示。

为了防止电流过大烧坏 LED,所以每个 LED上串联一个 560 欧姆的电阻进行限流,因

为普通 LED的工作电流大约为 10mA。流水灯的程序如下:

#include<reg52.h>

sbit cs_373=P1^5; // 定义 373 芯片片选线,以后会在 LED 显示器一节详细介绍

void delay(unsigned int i) //定义延时函数

while(i--);

void main( )

unsigned char k, a, b;

cs_373=0; //使 373 无效,以后会在 LED 显示器一节详细介绍

while(1)

P0=0xff; //8 个 LED 全部熄灭

for(k=0;k<8;k++) //以下程序实现 8个 LED 指示灯由左向右循环亮;

P0<<=1;

delay(20000);//延时,此延时时间决定循环亮的速度

Page 105: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 105 .

P0=0x00; //全部亮;

delay(20000); //延时,此延时时间决定全部亮的时间

P0=~P0; //全部不亮;

for(k=0;k<8;k++) //由右向左循环亮;

P0>>=1;

delay(20000);

P0=0xff; //全部闪动 3次;

for(k=0;k<6;k++)

P0=~P0;

delay(20000);

P0=0x00; //全部亮;

for(k=0;k<8;k++) //由左向右熄灭;

P0<<=1;

P0=P0|0x01;

delay(20000);

P0=0x00; //全部亮;

for(k=0;k<8;k++) //由右向左熄灭;

P0>>=1;

P0=P0|0x80;

delay(20000);

P0=0xff; //全部不亮;

a=0xf0; b=0x0f;

for(k=0;k<4;k++) //由内向外依次点亮;

P0= a|b;

delay(20000);

Page 106: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 106 .

注:我公司MCU2006 型单片机开发板上 LED指示灯与 P1口相连,所以使用该开发板

编制流水灯程序只需将以上程序中的 P0改为 P1 即可。

练习题:

1、让8个LED指示灯只有一个灯从左向右循环点亮。

提示,某个时刻P0口只有一个位为“0”,其余位均为“1”,为“0”的位循环移

动。

2、让8个LED指示灯从左到右依次循环点亮,同时蜂鸣器不断发声。

a<<=1;

b>>=1;

P0=0xff; //全部不亮;

a=0xf0; b=0x0f;

for(k=0;k<4;k++) //由外向内依次点亮;

a>>=1;

b<<=1;

P0= (a&0xf0 )| (b&0x0f );

delay(20000);

P0=0x01; //从左向右只有一个不亮

for(k=0;k<7;k++)

P0<<=1;

delay(20000);

P0=0x80; //从右向左只有一个不亮

for(k=0;k<7;k++)

P0>>=1;

delay(20000);

Page 107: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 107 .

第 7章 LED 显示器及键盘接口技术

键盘与显示器是单片机应用系统不可缺少的部分,它们共同组成了人机交互系统(HMI:

Humen Machine Interface)。一般通过键盘向系统输入命令或数据等,通过显示器显示系

统状态及参数等。本章介绍键盘与显示器接口的搭建与驱动程序的编制。

7.1 LED显示器的接口技术

LED显示器是单片机系统中常用的显示装置,由于它价格低廉、亮度高、控制方便,

因此应用比较广泛。LED(Light Emiting Diode)是发光二极管的缩写,LED显示器是由发

光二极管构成的。下面说明LED 显示器的原理及应用。

7.1.1 LED 显示原理

每位LED 显示器由7个发光二极管组成,也称为数码管,由于显示数字和简单字母只需

7段 LED,所以也称为七段LED显示器。其排列形状如图7-1 所示。此外,显示器中还有一

个圆点型发光二极管(在图中以 dp 表示),用于显示小数点。通过七个发光二极管亮暗的

不同组合,可以显示多种数字、子母以及其它符号。

LED显示器中的发光二极管共有两种连接方法:共阳极接法和共阴极接法。共阳极接法

是把发光二极管的阳极连在一起构成公共阳极,使用时公共阳极接+5V,这样阴极端输入低

电平的段发光二极管就导通发光,而输入高电平的则不发光;共阴极接法是把发光二极管

的阴极连在一起构成公共阴极,使用时公共阴极接地,这样阳极端输入高电平的段发光二

极管就导通发光,而输入低电平的则不发光。g f GND a b

e d GND c dp

a

b

c

de

fg

dp

a

b

c

d

e

f

g

dp

a

b

c

d

e

f

g

dp

+5V

符号与引脚 共阴极 共阳极

图 7-1 LED 显示器

使用LED 显示器时要注意区分这两种不同的接法,因为不同的接法驱动方式是不一样

的。为了显示数字或符号,要为 LED 显示器提供代码,因为这些代码是为显示字形的,因

此称之为字形代码。七段发光二极管,再加上一个小数点位,共计八段。因此提供给 LED

Page 108: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 108 .

显示器的字形代码正好是一个字节。

七段LED 显示器与单片机的接口很简单,只需将单片机的一个 8位并行 I/O 口经某种

驱动电路与 LED数码管的发光二极管的引脚相连即可。因为单片机引脚的直接驱动能力有

限,所以与 LED 显示器接口时须采用外围驱动电路,具体连接见下几小节。根据 8位 I/O

口输出的不同数据,LED就可以显示不同的数字或字符。

用LED 显示器显示十六进制的字形代码在表 7-1 中给出。显然,共阳极和共阴极数码

管的字形代码是不同的。表 7-1给出的字形代码是假设“a”段为低位、“dp”段为高位计

算出来的,具体的字形代码计算还要看电路的实际连接方式。

表 7-1 LED 字形代码表

7.1.2 LED 显示器的动态扫描驱动方式

LED显示器的驱动有静态锁存和动态扫描方式:静态锁存方式也称直流驱动,是指每个

数码管都用一个译码器(如4511芯片)进行译码驱动,这种方式下的显示内容可保持,无

须CPU 进行动态刷新,可提高 CPU 效率,但是要求硬件资源多,接口复杂,而且功耗大,

一般不采用。

动态扫描方式是所有数码管共同使用一个译码驱动器,使各位数码管逐个轮流受控显

显 示

字 符

段 符 号 十六进制代码

dp g f e d c b a 共阴极 共阳极

0 0 0 1 1 1 1 1 1 3FH C0H

1 0 0 0 0 0 1 1 0 06H F9H

2 0 1 0 1 1 0 1 1 5BH A4H

3 0 1 0 0 1 1 1 1 4FH B0H

4 0 1 1 0 0 1 1 0 66H 99H

5 0 1 1 0 1 1 0 1 6DH 92H

6 0 1 1 1 1 1 0 1 7DH 82H

7 0 0 0 0 0 1 1 1 07H F8H

8 0 1 1 1 1 1 1 1 7FH 80H

9 0 1 1 0 1 1 1 1 6FH 90H

A 0 1 1 1 0 1 1 1 77H 88H

B 0 1 1 1 1 1 0 0 7CH 83H

C 0 0 1 1 1 0 0 1 39H C6H

D 0 1 0 1 1 1 1 0 5EH A1H

E 0 1 1 1 1 0 0 1 79H 86H

F 0 1 1 1 0 0 0 1 71H 8EH

H 0 1 1 1 0 1 0 1 76H 89H

P 0 1 1 1 0 0 1 1 73H 8CH

U 0 0 1 1 1 1 1 0 3EH C1H

熄灭 0 0 0 0 0 0 0 0 00H FFH

Page 109: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 109 .

示,当扫描频率很高的时候,其显示效果也非常良好。这种方式功耗小,硬件资源要求少,

所以应用较多。

1、用非门作驱动

下面以动态扫描驱动方式为例介绍LED数码管驱动程序的编制方法。硬件连接见图7-2。

图中只用一个译码器 74LS373 作为驱动芯片,该芯片连成直通方式,共阴极数码管的阴极

用74LS04 芯片(反向器)驱动,字形代码由P1 口提供,数码管的选择由P3口的低 5位控

制。

在扫描显示中,每位显示器的点亮时间是极为短暂的(约 1ms),由于人的视觉暂留现

象及发光二极管的余辉效应,尽管实际上各位显示器并非同时点亮,但只要扫描的速度足

够快,给人的印象就是一组稳定的显示数据,不会有闪烁感。如果数码管要显示 12345 这

么 5 个数,在单片机中实际的工作流程如下:先打开最左边的数码管,送 1,然后关掉数

码管,打开第二个数码管送 2,再关掉数码管,打开第三个数码管送3 ,依次向下,由于

速度足够快,那么我们将连续的看到 12345 这 5 个数。

首先对 74LS373 芯片进行简单说明:

1)74LS373是 20引脚的锁存器,8入 8出,输入与单片机的 8个 I/O 口相连接,输出

至数码管传送数据。

2)管脚说明:

74LS373引脚见图 7­3。OE: 为芯片的输出允许引脚,低电平有效,可以直接接地;

LE(G): 为芯片的片选引脚,高电平有效;

D0~D7: 为数据输入端,与单片机连接;

GND: 接地;

VCC: 接+5V 电源;

Q0~Q7: 为数据输出端,与数码管连接。

1 1 11P3.0P3.1P3.2P3.3P3.4

P1.0

P1.3P1.2

P1.4P1.5P1.6

P1.1

MCU

D0D1D2D3D4D5D6

Q0Q1Q2Q3Q4Q5Q6

G OE

+5V

ag

74LS373

图 7-2 LED 动态扫描驱动接口

Page 110: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 110 .

12345678910 11

121314151617181920OE

Q0

D0D1Q1

Q2D2D3Q3

GND LE (G)Q4

D4D5Q5

Q6D6D7

Q7

VCC

74LS373

图 7­3 74LS373引脚图

参考程序如下:程序的功能是首先用5个是 LED显示器显示1~5 这 5 个数字,然后显

示全局整型变量para 的值,在显示整数之前,要把该整数的各位的值算出来,然后一位一

位的进行显示,从中可以领会求取整数的各位的值的方法。

# include<reg51.h>

unsigned int para; /*定义全局变量*/

void main() /*主程序*/

unsigned char code zixing1[10]=0x3f,0x06,0x5b,0x4f,0x66, /*共阴极字形代码*/

0x6d,0x7d,0x07,0x7f,0x6f; /*(0~9)*/

unsigned char j,k,zixing2[5]; /* zixing2[5]用来存放 para 各位的字形代码*/

unsigned int i;

P3=0x01; /* P3 赋初值,指向最左侧的数码管*/

for(i=0;i<5000;i++) /*外循环控制显示 12345 的时间*/

for(j=1;j<6;j++)

P1=zixing1[j]; /* LED 显示 12345*/

P3<<=1; /* P3 口移位,指向下一个数码管*/

while(1)

zixing2[0]= zixing1[para/10000]; /* 以下 5行用来把 para 各位的字形*/

zixing2[1]= zixing1[(para/1000)%10]; /*代码存入 zixng2 数组中*/

zixing2[2]= zixing1[(para/100)%10];

zixing2[3]= zixing1[(para/10)%10];

zixing2[4]= zixing1[para%10];

P3=0x01; /* P3 赋初值,指向最左侧的数码管*/

for(j=0;j<5;j++)

P1=zixing2[j]; /* LED 显示 para 的值*/

P3<<=1;

Page 111: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 111 .

2、用三极管作驱动

下面介绍MCU2007-I 型单片机开发板上LED显示器的连接及驱动方式。

在MCU2007-I型开发板中采用了 74LS138译码芯片和 74LS373地址锁存器以及 PNP型三极管来驱动显示,属于扫描驱动方式,数码管为共阳极。驱动原理图如图 7­5所示。

在图 7­5中,所使用的 LED显示器(U5 和U4)与前面介绍的有所不同,这里使用的

是 4 位一体的 LED显示器。在U5或 U4中,4 个 LED显示器共用 8 个段 a、b、c、d、e、f、g和 dp,而Q1、Q2、Q3 和Q4 是对应 4 个 LED显示器的控制端。当Q1与电源 VCC接通,

则 a、b、c、d、e、f、g和 dp 对应 4 位 LED显示器的第一位,当 Q2与电源 VCC接通,则

a、b、c、d、e、f、g和 dp对应 4位 LED显示器的第二位,依次类推。

用扫描方式控制 4位一体的 LED显示器的方法就是接通Q1,然后给 a、b、c、d、e、f、g和 dp送字形码;然后对Q2、Q3、Q4 作同样操作,并不断循环,则 LED显示器处于点亮

状态。要注意扫描速度应足够快,否则 LED显示器会比较暗或不亮。

由于用到了 74LS138译码器对 LED显示器进行扫描控制,所以首先对 74LS138芯片

进行简单说明:

1)74LS138是 16 引脚的 3 输入—8输出的译码器,在应用到数码管上可以节省单片机

的 I/O的接口;它是一种低电平输出有效器件,当输入的地址选择某通道时,此通道即为低

电平,而其余通道输出高电平。在应用芯片时,应该先仔细查看其资料。

2)图 7­4为 74LS138的引脚图。

、 、E3:片选端。 、 低电平有效,所以把它们直接接地;E3高电平有1E 2E 1E 2E

效,所以用单片机的 I/0口线直接控制 E3,其低电平转为高电平时芯片有效。

A0、A1、A2:地址输入端,由此来选择输出通道Y0~Y7;当 、 和 E3均为有效1E 2E

状态时,A0、A1、A2与输出有效的对应关系见表 7­2。Y0~Y7:地址输出端,与三极管相连接,驱动数码管;

GND: 接地;

VCC: 接+5V 电源;

12345678 9

10111213141516A0

A1

A2E1E2E3Y7

GND Y6Y5Y4

Y3Y2Y1

Y0VCC

74LS138

图 7­4 74LS138引脚图

for(k=0;k<20;k++); /* 延时,控制扫描频率*/

Page 112: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 112 .

图 7­5 三极管驱动方式原理图

Page 113: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 113 .

表 7­2 74LS138真值表

在MCU2007-I型开发板(图 7­4)中,译码器 74LS138的 、 接地,E3接 P26口1E 2E

线,地址输入端由 P14、P13 和 P12口线控制。因此 P26口线为高电平就选通 74LS138,此

时 P14、P13和 P12口线的状态就决定译码器的输出,也就决定了某一个 LED显示器被点

亮。以U5 的第一位 LED为例,若想点亮该位,则 U5 的Q1 应接通 VCC,此时三极管 Q7应导通,则译码器的输出端 Y7 应为低电平,于是 P14、P13 和 P12口线都为“1”。所以只

要 P1=0xbf(计算此值时要考虑 P1口其它引脚的电平不应引起其它元件误动作),就可保证

P14、P13 和 P12口线都为“1”,则 U5 的第一个 LED显示器就被点亮。

根据以上方法就可以计算出 P1口的数据与 LED显示器的一一对应关系,具体参以下

程序中的 display 函数。

锁存器74LS373的片选线接单片机P1.5口线,因此P1.5口线输出高电平则选通74LS373锁存器。

驱动程序如下:(程序功能为从左到右依次显示0~7共 8个数字)

A2 A1 A0 输 出 状 态

0 0 0 Y0=0,其余为 1

0 0 1 Y1=0,其余为 1

0 1 0 Y2=0,其余为 1

0 1 1 Y3=0,其余为 1

1 0 0 Y4=0,其余为 1

1 0 1 Y5=0,其余为 1

1 1 0 Y6=0,其余为 1

1 1 1 Y7=0,其余为 1

#include<reg52.h>

#define uchar unsigned char

sbit a_138=P1^2;

sbit b_138=P1^3;

sbit c_138=P1^4; //以上定义 74LS138 的 A0、A1 和 A2 的口线

sbit cs_138=P2^6; //138 片选,高电平有效;

sbit cs_373=P1^5; //373 片选,高电平有效;

uchar code zima[10]=0xc0,0xf9,0xa4,0xb0,0x99, //共阳极字形代码(0~9)

0x92,0x82,0xf8,0x80,0x90;

void display(uchar pos,uchar num,bit dp); //声明显示函数

void main()

uchar i,j;

cs_138=1; //选通 74LS138

cs_373=1; //选通 74LS373

while(1)

Page 114: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 114 .

以上程序重点是display()函数,该函数有三个参数:第一个参数pos是数据显示的

位置,0为最左边数码管,7 位最右边数码管;第二个参数 num是要显示的数据(0~9);

第三个参数表示小数点是否点亮,为1 则显示小数点,为0则不显示小数点。

可以在需要显示的场合调用display()函数进行显示,但要注意,该函数调用一次只

能显示一位数据,因此显示多位数据需先把该数据的各个位求出来,然后分别调用该函数

进行显示即可。还要注意,扫描显示方式必须时刻对数码管进行刷新,否则数码管会变暗

或不显示。在后续章节还会用到此显示方式,请读者仔细体会。

练习题:

1、让数字3在 8个数码管上从左到右依次显示,某个时刻只有一个数码管显示,不断

循环。

2、编写程序,在数码管上分时显示英文“HELLO”和下划线“_ _ _ _ _”。

3、编写程序,在数码管上显示“12-34-56”。

for(i=0;i<8;i++)

display(i,i,0);

for(j=0;j<200;j++); //控制扫描频率

void display(uchar pos,uchar num,bit dp) //显示函数定义

if(dp==1)

P0=zima1[num]+0x80; //显示小数点

else

P0=zima[num]; //不显示小数点

switch(pos)

case 0:P1=0xbf;break; //位置 0,对应最左边数码管

case 1:P1=0xbb;break; //位置 1

case 2:P1=0xb7;break; //位置 2

case 3:P1=0xb3;break; //位置 3

case 4:P1=0xaf;break; //位置 4

case 5:P1=0xab;break; //位置 5

case 6:P1=0xa7;break; //位置 6

case 7:P1=0xa3;break; //位置 7,对应最右边数码管

default:break;

Page 115: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 115 .

7.1.3 LED 显示器串行驱动方式

从上一小节中可以看出,该种 LED扫描驱动方式比较多地占用 I/O 口,在系统较大、

I/O口紧张的情况下,不适宜应用,一般采用带有串行接口的译码驱动芯片来实现LED的接

口。下面介绍采用串行/并行转换芯片74LS164 进行 LED显示器驱动的方法。

74LS164是一款高速的 8位串入并出的移位寄存器。当脉冲输入端有一个从低到高的跳

变时,串行的数据就会通过 2输入的与门进入。当复位端接收到一个有效的低电平信号,

74LS164的所有输出端都会复位为低电平。74LS164 的引脚图如图7-6所示。

1234567

AB

Q2Q1Q0

Q3GND 8

91011121314

CPMR

74LS164

Q4Q5Q6Q7Vcc

图 7-6 74LS164 的引脚图

其中A、B为串行数据输入端;CP为脉冲输入端,从低到高的跳变有效; 为复位端,MR

低电平有效;Q7~Q0为并行数据输出端。

74LS164工作过程如下:先把串行数据的最低位送到 A、B 端,然后使 CP端有一个从低

到高的跳变,则 A、B 端数据位就送到了输出 Q0端,再把串行数据的次低位送到 A、B端,

然后再使 CP端有一个从低到高的跳变,则 A、B端数据位就送到了输出 Q0 端,原来 Q0端

的数据就移位到了Q1 端,依次类推,进行八次上述操作就把一个字节的数据以串行方式送

到了74LS164 的输出端。

当没有新的数据从输入端到来之前,74LS164 的输出端保持不变,所以用此种驱动方

式实质上就是静态锁存方式。

单片机与 74LS164 之间只需两条线即可实现对 LED显示器的驱动,将前一个 74LS164

的 Q7 接到下一个 74LS164 的 A、B 上,就可以实现多个 74LS164的串行连接,这样就可以

很方便地实现用单片机的两个I/O 口对多个LED显示器进行驱动。

我公司MCU2006 型单片机开发板上的LED显示器驱动就采用 74LS164芯片,一共接了6

个数码管(共阳极),当然就需要6个 74LS164芯片。具体连接方式见图7-7(注意,在 74LS164

的输出端与LED 显示器之间需加限流电阻,此图未给出)。

AB Q

1Q2

Q3Q0

74LS164

Q4

Q5

Q6

Q7

dpgfedcba

CPAB Q

1Q2

Q3Q0

74LS164

Q4

Q5

Q6

Q7

dpgfe

AB Q

1Q2

Q3Q0

74LS164

Q4

Q5

Q6

Q7

dpgfe...

CP CP

P2.7

P2.6

1# 2# 6#

dcba dcba

图 7­7 74LS164驱动原理图

Page 116: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 116 .

注意,因为 74LS164是用移位的方式将数据向后传递的,因此要改变后边数码管的显

示内容,前边数码管的内容也应同时刷新,也就是说,改变任意一个数码管的显示,必须

将所有数码管的内容全部重写。

参考程序编制如下:

#include <reg52.h>

sbit clk_164=P2^7; //定义 164 的数据引脚

sbit data_164=P2^6; //定义 164 的时钟脉冲引脚

void led2(unsigned char); //2 位数码管驱动函数

void led6(unsigned char); //6 位数码管驱动函数

unsigned char code zixing[10]=0xc0,0xf9,0xa4,0xb0,0x99,

0x92,0x82,0x8f,0x80,0x90;//0~9 的字形代码

void main()

led6(12,34,56); //显示数字 1、2、3、4、5、6

while(1);

void led2(unsigned char aa) // 2 位数码管驱动程序,aa 为要显示的整数

unsigned char j,x[2],bb;

x[1]=aa/10;

x[0]=aa%10;

for(j=0;j<2;j++)

bb=zixing[x[j]];

for(i=0;i<8;i++) //分 8 次送出一个字节,每次送出一位

clk_164=0;

data_164=bb&0x01;

bb=bb>>1;

clk_164=1;

void led6(unsigned char x,unsigned char y, unsigned char z)

led2(z); //先显示的数据在最右边

led2(y);

led2(x); //最后显示的数据在最左边

Page 117: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 117 .

练习题:

1、让1~6这六个数字在数码管上从左到右不断循环移动;

2、在数码管上显示“OPEN”字符,并让该字符不断向右移动,从右边移出,再从左

边移入。

3、编写一个函数,该函数的参数有两个,第一个是含有六个元素的数组,该数组中

存放的是要显示的数据;第二个参数是一个字符型数据,该数据的低六位对应 6个数码管

的小数点,当某一位的值为1,则对应数码管的小数点就亮。

7.2 键盘接口技术

单片机使用的键盘一般分为两种:独立式键盘与矩阵式键盘。独立式键盘实际上就是

一组相互独立的按键,这些按键可直接与单片机的 I/O口连接,即每个按键独占一条口线,

接口简单,在按键数目不多的场合使用方便。矩阵式键盘也叫行列式键盘,因为按键的个

数较多,所以按键组成行列式的矩阵。

键盘在应用的时候包括两项工作:一个是按键的识别,即在键盘中找出被按下的是哪

一个键,一般使用接口电路来实现;另一个是按键功能的实现,要通过键盘服务程序来完

成。接口电路可以采用如下4 种方式:使用单片机本身的并行口;使用单片机本身的串行

口;使用通用接口芯片(如8255);使用专用接口芯片(如 HD7279,8279,ZLG7289等)。

键盘处理的过程如下:

键盘扫描,用查询或中断方式判断是否有按键被按下。

去抖动,用硬件或软件方法去除按键抖动。

键码的识别,用来区分不同的按键。

根据键码编制程序来处理该按键操作。

所谓去抖动是指消除按键闭合或断开时引起的瞬间电压抖动,这样才能保证键扫描的

正确性。常用的去抖动方法为软件方法,其方法为判别到有键按下后,软件延时一段时间

(一般为 10ms左右)后,再判断键盘状态,如果仍为有键按下状态,则认为有一个确定的

键被按下,否则把按键当作抖动处理。

7.2.1 独立式键盘接口

在单片机的运行过程中,键盘的扫描和处理有两种方式:查询方式和中断方式。查询

方式是当CPU 空闲时就执行键盘扫描程序,这种方式浪费CPU时间,在 CPU处理任务较重

的场合容易产生延迟;中断方式是每当有按键闭合就向CPU 发出中断申请,中断响应后执

行键盘扫描程序,这种方式响应速度快,CPU利用率高。

1、查询方式示例

图7-8为一种典型的独立式键盘接口电路,可以看出,各按键接到了单片机的 P1口,

且无键按下时P1.0-P1.3 为高电平,当某个键被按下时,与该键相连的口线被拉成低电平,

利用某个口线电平的变化即可判断出哪个键被按下了。

Page 118: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 118 .

MCU+5V

4×10K KEY1

KEY2

KEY3

KEY4

P1.0

P1.1

P1.2

P1.3

图 7-8 查询方式键盘接口

以下程序功能为:通过按键设置全局变量para的值(初值为 100)。其中KEY1 按下表

示可以设置,KEY2 按下 para 的值增加,KEY3按下 para的值减少,KEY4按下表示确定。本

例使用if 语句来实现,程序编制如下:

# include<reg51.h>

sbit key1=P1^0;

sbit key2=P1^1;

sbit key3=P1^2;

sbit key4=P1^3;

bit key1_stat; /*记录键 KEY1 的状态,KEY1 被按下过则为 1,否则为 0*/

unsinged char para,j; /*定义全局变量*/

void keyscan(void); /*键盘扫描与处理函数的声明*/

void main( ) /*主函数*/

para=100; /*全局参数 para 赋初值*/

key1_stat=0; /*KEY1 的状态赋初值,KEY1 未被按下*/

while(1)

keyscan( ); /*调用键盘扫描与处理函数*/

void keyscan(void) /*键盘扫描与处理函数的定义*/

if(key1==0) /*按键 KEY1 的处理*/

key1_stat=1; /*如果 KEY1 被按下,置位状态标记*/

if(key2==0) /*按键 KEY2 的处理*/

for(j=0;j<200;j++);/*延时,防止键被按下一次时引起多次处理*/

if((key2==0)&&(key1_stat==1))

para++; /*若键 KEY1 被按下过且 KEY2 也被按下,则 para 加 1*/

Page 119: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 119 .

以上方式在同一时间只能按下一个键,如果同时按下复合键或多个键时,则会以KEY1、

KEY2、KEY3、KEY4为检测的顺序。

2、中断方式示例

在我公司MCU2006开发板上,接有4个按键,具体连接电路如图 7-10所示。

由于电路中用到了CD4078芯片,因此首先对该器件进行简单介绍:CD4078芯片是4输

入或非门,其引脚如图7-9 所示。

其中, A、B、C、D为4或非门的输入端,J为与其对应的4或非门输出端,当A、B、

C、D 这4个输入端有一个为高电平,则输出端J为低电平;E、F、G、H是另外一个4或非

门的输入端,K为与其对应的4或非门输出端。

1234567

KA

DCB

NCGND 8

91011121314

NCEFGHJVcc

CD4078

图 7-9 CD4078 引脚图

从图7-10中可以看出,各按键接到了单片机的 P2口,同时,四个按键还接到了4输

入或非门CD4078 芯片的输入端A、B、C、D,当无键按下时P2.0-P2.3 和 A、B、C、D 均为

低电平,CD4078 输出端J为高电平,此时不申请中断;当某个键被按下时,CD4078输出端

J就会变为低电平,向单片机申请中断,同时与该键相连的单片机口线被拉成高电平,在

单片机响应中断时,通过判断单片机口线电平的变化即可判断出哪个键被按下了,因此这

种方式响应速度快。

下面介绍中断方式键盘的程序编制方法。

这里所有中断信号都接外部中断 INT1(P3.3 引脚)。当某一个键被按下时,P3.3会变

为低电平,产生有效的中断信号。当中断产生的时候,单片机会在中断处理函数中采用查

询的方式来判断到底是哪个键被按下并给予相应的处理,这样可以减少处理延迟,大大提

高CPU 的利用率。中断的触发方式采用下降沿触发。下面给出中断方式的处理程序:

本程序功能为通过按键设置全局变量 para 的值(初值为 100),并用 74LS164 驱动的

LED显示器显示该变量的值及状态。要求如下:

if(key3==0) /*按键 KEY3 的处理*/

for(j=0;j<200;j++);

if((key3==0)&&(key1_stat==1))

para--; /*若键 KEY1 被按下过且 KEY3 也被按下,则 para 减 1*/

if(key4==0) /*按键 KEY4 的处理*/

key1_stat=0; /*如果 KEY4 被按下,复位状态标记*/

Page 120: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 120 .

MCU

+5V

4×10KKEY1

KEY 2

KEY 3KEY 4

P2.0P2.1

P2.2P2.3

P3.3A

DCBJ

CD4078INT1

图 7-10 中断方式键盘接口

程序上电即在显示器左边两位显示 “00”,最右边三位数码管显示 para的值(100);

当KEY1 按下表示可以设置,显示器左边两位显示 “11”;KEY2 按下 para的值增加;KEY3

按下 para 的值减少;KEY4 按下表示确定,显示器左边两位显示“00”;数码管第三位始终

显示“-”。

# include<reg51.h>

bit key1_stat; //记录键 KEY1 的状态,KEY1 被按下过则为 1,否则为 0

sbit clk_164=P2^7; //定义 164 的数据引脚

sbit data_164=P2^6; //定义 164 的时钟脉冲引脚

unsinged char para,j,keynum; //定义全局变量

void led3(unsigned char,bit); //3 位数码管驱动函数

void delay(unsigned int); //延时函数

unsigned char code zixing[11]=0xc0,0xf9,0xa4,0xb0,0x99, 0x92, 0x82,

0x8f,0x80,0x90,0xbf;//0~9 及“-”的字形代码

void main( ) //主函数

EA=1; //开中断

IT1=0; //外部中断 0,下降沿触发

EX1=1; //外部中断 0

para=100; //全局参数 para 赋初值

key1_stat=0; //KEY1 的状态赋初值,KEY1 未被按下

while(1) //空循环,等待中断信号。实际应用时做需要的工作

if(key1_stat==1)

led3(para,0);

led3(11,1);

else

led3(para,0);

led3(00,1);

Page 121: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 121 .

delay(10000); //延时,防止显示频率过高,数码管闪烁严重

void keyscan() interrupt 2 using 2//键盘处理中断函数

keynum= P2&0x0f; //读取 P2 口低 4位的值,以判断按键号 keynum

switch (keynum) //根据不同的按键号 keynum 进入相应的处理入口

case 1: //按键 KEY1 的处理

key1_stat=1; //如果 KEY1 被按下,置位状态标记

break;

case 2: //按键 KEY2 的处理*/

for(j=0;j<20;j++);//延时,防止键被按下一次时引起多次处理

if((key2==0)&&(key1_stat==1))

para++; //若键 KEY1 被按下过且 KEY2 也被按下,则 para 加 1

break;

case 4: //按键 KEY3 的处理

for(j=0;j<20;j++);

if((key3==0)&&(key1_stat==1))

para--; //若键 KEY1 被按下过且 KEY3 也被按下,则 para 减 1

break;

case 8: //按键 KEY4 的处理

key1_stat=0; //如果 KEY4 被按下,复位状态标记

break;

default:

break;

void led3(unsigned char aa,bit flag) // aa 为要显示的整数,flag 为1,

//则显示“-”和两位数;flag 为0,则显示三位数

unsigned char j,x[3],bb;

if(flag==1)

x[2]= aa/10;

x[1]= aa%10;

x[0]=10;

Page 122: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 122 .

在中断处理函数中,对各键的查询使用switch语句来处理,与上例中的 if语句功能

相同。读者可以将其进行对比,以更好地掌握条件语句的使用。以上为单个按键的应用,

应用场合较多,若想对复合键进行处理,读者可根据需要仿照上述方式自行编写。

练习题:

1、用键盘控制蜂鸣器发声:按任意键一下,蜂鸣器就发声;再按任意键一下,蜂鸣器

就停止发声。

2、用键盘控制流水灯的方向。按下S1,流水灯从左向右;按下S2,流水灯从右向左;

按下S3,所有LED 指示灯闪烁;按下S4 ,所有LED 指示灯全亮,然后从左到右逐个熄灭。

3、实现以下功能:按下S1,数码管第一位显示 1;按下 S2,数码管第二位显示 2;按

下S3,数码管第三位显示 3,按下 S4,数码管第四位显示 4;同时按下 S1 和 S2 ,数码管

第五位显示5;同时按下 S3和 S4 ,数码管第六位显示 6;

7.2.2 矩阵式键盘接口

一、矩阵式键盘的结构与原理

else

x[2]=aa/100;

x[1]=aa/10%10;

x[0]=aa%10;

for(j=0;j<3;j++)

bb=zixing[x[j]];

for(i=0;i<8;i++) //分 8 次送出一个字节,每次送出一位

clk_164=0;

data_164=bb&0x01;

bb=bb>>1;

clk_164=1;

void delay(unsigned int x); //延时函数

while(x--);

Page 123: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 123 .

矩阵式键盘由行线和列线组成,按键位于行、列的交叉点上,行线与列线分别连接到

按键的两端。如图7-11 所示。矩阵式键盘适用于按键数量较多的场合。

MCU+5V

P1.0P1.1P1.2P1.3P1.4P1.5P1.6

4×10K

1234

5678

9101112

图 7-11 矩阵式键盘接口电路

图7-11 为 4 行 3列键盘接法,共 12个按键。行线通过上拉电阻接+5V,平时无按键

动作时,行线处于高电平状态,而当有按键按下时,则对应的行线和列线短接,行线电平

状态将由与此行线相连的列线电平决定。

在单片机的控制下,判别键盘中究竟哪一个按键被按下其方法是:先令列线 P1.4为低

电平,其余2 根列线P1.5、P1.6都为高电平,读行线状态。如果 P1.0、P1.1、P1.2、P1.3

都为高电平,则P1.4这一列上没有键闭合,如果读出的行线状态不全为高电平,则为低电

平的行线和P1.4 相交的键处于闭合状态;如果P1.4 这一列上没有键闭合,接着使列线P1.5

为低电平,其余列线为高电平。用同样的方法检查 P1.5 这一列上有无键闭合,依此类推。

这种逐行逐列地检查键盘状态的过程称为对键盘进行扫描。

可以看出,如果采用独立式键盘,12个按键需要12 个 I/O 口与之相连,而矩阵式键盘

只需占用7 个 I/O口。这说明矩阵式键盘在按键数量较多的场合,可以节省很多I/O口线。

1、矩阵式键盘的工作方式

与独立式键盘相类似,矩阵式键盘的扫描也有程序查询方式和中断处理方式两种,在

实际应用中应根据具体情况进行选择。一般CPU 比较繁忙的场合应采用中断处理方式。

程序查询方式是利用CPU 在完成其它工作的空闲,调用键盘扫描子程序,来处理按键

输人。键盘扫描程序一般应具备下述4 个功能:

①判别键盘上有无键按下。其方法为列线输出全“0”,然后读取行线的状态,若行线

为全“1”,则表明键盘无键按下,若不全为“1”,则表明有键按下。

②去除键的抖动。方法与独立式键盘中采用的方法相同。

③求按键位置。根据前面介绍的键盘扫描方法,逐行逐列进行扫描,最后确定被按下

键的键号。具体方法参考示例程序。

④调用功能处理程序,对该键号的按键进行处理。

中断处理方式中,CPU 响应中断请求后,转去执行中断服务程序,在中断服务程序中判

别键盘上闭合键的键号,并做相应的处理,方法与程序查询方式相同。

查询方式的程序示例如下,该程序的功能只是识别按键的号,并把该键号(1~12)存

入全局变量 keynum 中,并根据键号分别对按键进行处理。实际应用中可加入根据键号进行

相应处理的程序。

Page 124: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 124 .

# include<reg51.h>

sbit col_1=P1^4;

sbit col_2=P1^5;

sbit col_3=P1^6;

unsigned char keynum;

void keyscan(void); /*键盘扫描函数声明*/

void keyfind(unsigned char); /*键值分析函数声明*/

void keyprocess(void); /*按键功能实现函数声明*/

void main(void) /*主程序*/

while(1)

keynum=0; /*键值赋为无效值*/

keyscan( ); /*键盘扫描函数调用*/

keyprocess( ); /*按键功能实现函数调用*/

void keyscan(void) /*键盘扫描函数定义*/

col_1=0;

col_2=0;

col_3=0; /*列线输出全“0”*/

if((P1&0x0f)!=0x0f) /*判断行线是否全“1”, 不是则处理*/

col_1=0; /*第一列(P1.4)输出“0”*/

col_2=1;

col_3=1;

keyfind(0); /*判断第一列是否有键按下,并计算键号*/

col_1=1;

col_2=0; /*第二列(P1.5)输出“0”*/

col_3=1;

keyfind(4); /*判断第二列是否有键按下,并计算键号*/

col_1=1;

col_2=1;

col_3=0; /*第三列(P1.6)输出“0”*/

keyfind(8); /*判断第三列是否有键按下,并计算键号*/

col_3=1; /*第三列输出无效电平*/

Page 125: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 125 .

void keyfind(unsigned char col_dat) /*键值分析函数定义*/

unsigned char temp; /*定义临时变量*/

temp=(~P1)&0x0f; /*求取各行状态*/

switch (temp) /*键值计算*/

case 1:

keynum=1+col_dat;

break;

case 2:

keynum=2+col_dat;

break;

case 4:

keynum=3+col_dat;

break;

case 8:

keynum=4+col_dat;

break;

default:

break;

void keyprocess(void) /*按键功能实现函数*/

switch(keynum)

case 1:

/*按键 1的处理*/

break;

case 2:

/*按键 2的处理*/

break;

case 3:

/*按键 3的处理*/

break;

case 4:

/*按键 4的处理*/

Page 126: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 126 .

上例中,由于在keyscan 函数中要经常用到keyfind函数中的代码段,因此把该代码

段在函数keyfind 中实现。这样可以缩减整个程序的长度,而且程序的整体看上去更加简

明、清晰。这种方式也是在实际应用中常用的,请读者仔细体会。

2、MCU2007­I单片机开发板上矩阵式键盘的连接

MCU2007-I单片机开发板本采用矩阵式键盘,具体连接方式如图 7-12所示。

在图 7­12 中,用到了 74LS11芯片,下面对该芯片进行简单说明:

74LS11是 14 引脚的三输入的与门,我们接在键盘上与单片机的 I/O相连接,可以触发

中断,进行对键盘的操作。管脚分布如图 7­13所示。

A、B、C为输入引脚;

Y为输出引脚:Y=A&B&C;

break;

case 5:

/*按键 5的处理*/

break;

case 6:

/*按键 6的处理*/

break;

case 7:

/*按键 7的处理*/

break;

case 8:

/*按键 8的处理*/

break;

case 9:

/*按键 9的处理*/

break;

case 10:

/*按键 10 的处理*/

break;

case 11:

/*按键 11 的处理*/

break;

case 12:

/*按键 12 的处理*/

break;

default:

break;

Page 127: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 127 .

图 7-12 MCU2007-I开发板键盘接口电路

1234567

1A1B

GND 891011121314

3Y3A

74LS11

3B3C1Y1CVcc

2A2B2C2Y

图 7-13 74LS11 引脚图

GND 接地;

VCC 接+5V 电源。

图7-12为 3行 3列键盘接法,共 9个按键。行线通过上拉电阻接+5V,平时无按键动

作时,列线处于高电平状态,而当有按键按下时,则对应的行线和列线短接,列线电平状

态将由与此行线相连的行线电平决定。

在单片机的控制下,判别键盘中究竟哪一个按键被按下其方法是:先令行线 P2.0为低

电平,其余 2根行线 P2.1、P2.2 都为高电平,读列线状态。如果 P2.3、P2.4、P2.5 都为

高电平,则P2.0这一行上没有键闭合,如果读出的列线状态不全为高电平,则为低电平的

列线和P2.3 相交的键处于闭合状态;如果P2.0这一行上没有键闭合,接着使行线P2.1为

低电平,其余行线为高电平。用同样的方法检查P2.1这一行上有无键闭合,依此类推。这

种逐行逐列地检查键盘状态的过程称为对键盘进行扫描。

3、矩阵式键盘的程序设计

矩阵式键盘的扫描也有程序查询方式和中断处理方式两种,在实际应用中应根据具体

情况进行选择。一般CPU 比较繁忙的场合应采用中断处理方式。

程序查询方式是利用CPU 在完成其它工作的空闲,调用键盘扫描子程序,来处理按键

输入。

中断处理方式中,CPU 响应中断请求后,转去执行中断服务程序,在中断服务程序中判

Page 128: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 128 .

别键盘上闭合键的键号,并做相应的处理,方法与程序查询方式相同。

范例 1:当按下 S1~S7按键时,在数码管对应的位置显示按键号;当按下 S8时,蜂鸣

器发声;当按下 S9时,运行流水灯程序。

#include<reg52.h>

#define uchar unsigned char

/*以下定义 LED 显示所需的位变量*/

sbit a_138=P1^2;

sbit b_138=P1^3;

sbit c_138=P1^4;

sbit cs_138=P2^6;

sbit cs_373=P1^5;

/*以下定义键盘所需的位变量*/

sbit kh0=P2^0;

sbit kh1=P2^1;

sbit kh2=P2^2;

sbit ks3=P2^3;

sbit ks4=P2^4;

sbit ks5=P2^5;

sbit kt=P3^3;

sbit fengming=P1^7; //定义蜂鸣器的位变量

uchar code zima1[10]=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90;

uchar keydat; //定义全局变量保存按键号

/*以下是函数声明*/

void liushuideng(void); //流水灯函数

void fengmingqi(void); //蜂鸣器函数

void display(uchar pos,uchar num,bit dp);//LED 显示函数

void keyscan(void); //按键扫描函数声明;

void keyfind(unsigned char); //按键分析函数声明;

void keyprocess(void); //按键处理函数声明;

void delay(unsigned int); //延时函数声明;

void main( ) //主程序

cs_138=1;

cs_373=1;

keydat=9; //按键号赋初值,上电后先运行流水灯程序

while(1)

keyscan();

Page 129: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 129 .

keyprocess();

void display(uchar pos,uchar num,bit dp) //LED 显示函数定义

if(dp==1)

P0=zima1[num]+0x80; //显示小数点

else

P0=zima1[num]; //不显示小数点

switch(pos)

case 0:P1=0xbf;break; //位置 0

case 1:P1=0xbb;break; //位置 1

case 2:P1=0xb7;break; //位置 2

case 3:P1=0xb3;break; //位置 3

case 4:P1=0xaf;break; //位置 4

case 5:P1=0xab;break; //位置 5

case 6:P1=0xa7;break; //位置 6

case 7:P1=0xa3;break; //位置 7

default:break;

void delay(unsigned int i) //延时程序

while(i--);

void keyscan(void) //按键扫描函数

kh0=0;

kh1=0;

kh2=0; //使行线输出为 0;

if((P2&0x38)!=0x38) //判别列线是否有键被按下

kh0=0;

kh1=1;

kh2=1;

keyfind(0); //行线 p20 上有键被按下;

Page 130: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 130 .

kh0=1;

kh1=0;

kh2=1;

keyfind(3); //行线 p21 上有键被按下;

kh0=1;

kh1=1;

kh2=0;

keyfind(6); //行线 p22 上有键被按下;

void keyfind(unsigned char i) //键植分析函数

unsigned char keytmp;

keytmp=~(P2)&0x38; //判别哪个键被按下

switch(keytmp)

case 8:

keydat=1+i; //列线 p23 被按下

break;

case 16:

keydat=2+i; //列线 p24 被按下

break;

case 32:

keydat=3+i; //列线 p25 被按下

break;

default:

break;

void keyprocess(void) //按键处理函数

switch(keydat)

case 1:

case 2:

case 3:

case 4:

case 5:

case 6:

Page 131: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 131 .

case 7:

display(keydat,keydat,0);

break;

case 8:

fengmingqi();

break;

case 9:

liushuideng();

break;

default:

break;

delay(20);

void liushuideng(void) //流水灯函数定义

uchar k;

P0=0xff; //由左向右循环亮;

for(k=0;k<8;k++)

P0<<=1;

delay(10000);

keyscan(); //键盘扫描,防止该程序运行时按键反应迟钝

if(keydat!=9)

keyprocess();

P0=0xff; //全部不亮;

for(k=0;k<8;k++) //由右向左循环亮;

P0>>=1;

delay(10000);

keyscan(); //键盘扫描,防止该程序运行时按键反应迟钝

if(keydat!=9)

keyprocess();

void fengmingqi(void) //蜂鸣器函数定义

Page 132: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 132 .

范例 2:用MCU2007-I 型单片机开发板上的9 个按键及LED 显示器组成一个简易计算器!其

中:键盘 S1~S7表示数字 1~7;S8:按下一次是+,再按一次是 - ,再按一次是*,再按一次是/,S8键按下的时候用

最左边数码管显示状态,加法显示 “-|”,减法显示“-”,乘法显示“.”,除法显示“三”;

S9:该键按下得出结果 。

uchar i,j;

for(i=60;i<200;i++)

for(j=0;j<5;j++)

fengming=!fengming;//蜂鸣器不断通断;

delay(i);

keyscan(); //键盘扫描,防止该程序运行时按键反应迟钝

if(keydat!=8)

keyprocess();

#include<reg52.h>

#define uchar unsigned char

sbit cs_138=P2^6;

sbit cs_373=P1^5;

sbit kh0=P2^0;

sbit kh1=P2^1;

sbit kh2=P2^2;

sbit ks3=P2^3;

sbit ks4=P2^4;

sbit ks5=P2^5;

char key8=0,key9=0,N=0;

uchar code zima1[14]=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xB9,0xbf,0x7f,0xb6;

//表示字符0~9,+,-,*,/

unsigned int sum=0,num1=0,num2=0;

unsigned char keydat,num=0,no1,a,b,c;

void keyscan(void); //按键扫描函数声明;

void keyfind(unsigned char); //按键分析函数声明;

Page 133: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 133 .

void keyprocess(void); //按键处理函数声明;

void delay(unsigned int); //延时函数声明;

void xianshi(void); //显示函数声明;

void shuju(unsigned int i); //数据函数声明;

unsigned char flag,anxia=0;

void main()

flag=0;

cs_138=1;

cs_373=1;

while(1)

delay(100);

keyscan();

if(anxia= =1)

keyprocess();

xianshi();

/*以下是延时函数程序*/

void delay(unsigned int i)

while(i­­);

/*````````````````````以下是按键函数处理的调用程序````````````````````*/

void keyscan(void)

kh0=0;

kh1=0;

kh2=0; //使行线输出为0;

if((P2&0x38)!=0x38) //判别列线是否有键被按下

kh0=0;

kh1=1;

kh2=1; //行线p20上有键被按下;

keyfind(0);

kh0=1;

kh1=0;

Page 134: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 134 .

kh2=1; //行线p21上有键被按下;

keyfind(3);

kh0=1;

kh1=1;

kh2=0; //行线p22上有键被按下;

keyfind(6);

anxia=1;

else

anxia=0;

void keyfind(unsigned char i) //键植分析

unsigned char keytmp;

keytmp=~(P2)&0x38; //判别哪个键被按下

switch(keytmp)

case 8:

keydat=1+i; //列线p23被按下

break;

case 16:

keydat=2+i; //列线p24被按下

break;

case 32:

keydat=3+i; //列线p25被按下

break;

default:

break;

void keyprocess(void) //按键处理函数

if(keydat!=8&&flag==0)

num1=num1*10+keydat;

a=num1/100;

b=num1/10%10;

c=num1%10;

Page 135: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 135 .

if(keydat!=9&&keydat!=8&&flag>0||keydat!=9&&keydat!=8&&num1!=0&&flag>0)

num2=num2*10+keydat;

a=num2/100;

b=num2/10%10;

c=num2%10;

switch(keydat)

case 8:

key8++;

if(key8= =5)

key8=1;

flag=1;

break;

case 9:

if(key8= =1) //如果是加法

sum=num1+num2;

else if(key8= =2) //如果是减法

sum=num1­num2;

else if(key8= =3) //如果是乘法

sum=num1*num2;

else if(key8= =4) //如果是除法

sum=num1/num2;

num1=sum;

num2=0;

flag=0;

a=sum/100;

b=sum/10%10;

c=sum%10;

break;

default:

break;

void xianshi(void)

char i;

Page 136: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 136 .

for(i=0;i<50;i++)

if(key8= =1) //如果是加法

P0=zima1[10]; //显示“-|”

P1=0xbf; //位置1,第一位数码管

delay(100);

if(key8= =2) //如果是减法

P0=zima1[11]; //显示“-”

P1=0xbf; //位置1,第一位数码管

delay(100);

if(key8= =3) //如果是乘法

P0=zima1[12]; //显示“.”

P1=0xbf; //位置1,第一位数码管

delay(100);

if(key8= =4) //如果是除法

P0=zima1[13]; //显示“三”

P1=0xbf; //位置1,第一位数码管

delay(100);

P0=zima1[a]; //所要显示的数据

P1=0xab; //第六位数码管

delay(100);

P0=zima1[b]; //所要显示的数据

P1=0xa7; //第七位数码管

delay(100);

P0=zima1[c]; //所要显示的数据

P1=0xa3; //第八位数码管

delay(100);

Page 137: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 137 .

7.3 键盘显示接口芯片 HD7279

HD7279_是一片具有串行接口的可同时驱动 8 位共阴式数码管(或 64只独立 LED)的

智能显示驱动芯片,该芯片同时还可连接多达 64键的键盘矩阵,单片即可完成 LED显示、

键盘接口的全部功能。

HD7279内部含有译码器可直接接受 BCD 码或 16进制码,并同时具有 2 种译码方式,

此外还具有多种控制指令,如消隐、闪烁、左移、右移、段寻址等。HD7279 具有片选信号,

可方便地实现多于 8 位的显示或多于 64键的键盘接口。

7.3.1 HD7279 的特点及引脚

HD7279具有以下特点:

串行接口无需外围元件可直接驱动LED;

各位独立控制译码/不译码及消隐和闪烁属性;

循环左移/循环右移指令;

具有段寻址指令方便控制独立LED;

64 键键盘控制器内含去抖动电路。

VDDVDDNCVSSNCCS

CLKDATAKEYSGSFSESDSC SB

SADPDIG0

DIG7DIG6DIG5DIG4DIG3DIG2DIG1

OSC2OSC1RESET1

234567891011121314 15

16171819202122232425262728

HD7279

图 7-14 HD7279 引脚图

HD7279的封装如图7-14所示,它一共有28个引脚,说明如下:

VDD:正电源。

NC:悬空。

Vss:接地。

:片选输入端,此引脚为低电平时,可向芯片发送指令及读取键盘数据。CSCLK:同步时钟输入端,向芯片发送数据及读取键盘数据时,此引脚电平上升沿表示数

据有效。

DATA:串行数据输入/输出端,当芯片接收指令时,此引脚为输入端。当读取键盘数据

时,此引脚在“读”指令最后一个时钟的下降沿变为输出端。

:按键有效输出端,平时为高电平,当检测到有效按键时,此引脚变为低电平。KEYSG-SA:段g—段a驱动输出。

DP:小数点驱动输出。

Page 138: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 138 .

DIG0-DIG7:数字0—数字7驱动输出。

OSC2,振荡器输出端。

OSC1:振荡器输入端。

:复位端。RESET

7.3.2 控制指令

HD7279的控制指令分为二大类:纯指令和带有数据的指令。

1、纯指令

纯指令有6条。

(1) 复位(清除)指令,指令代码为A4H

当zlg7289A 收到该指令后,将所有的显示清除,所有设置的字符消隐、闪烁等属性也

被一起清除。执行该指令后,芯片所处的状态与系统上电后所处的状态一样。

(2) 测试指令,指令代码为BFH

该指令使所有的LED 全部点亮,并处于闪烁状态,主要用于测试。

(3) 左移指令,指令代码为A1H

使所有的显示自右向左(从第1 位向第8位)移动一位(包括处于消隐状态的显示位),

但对各位所设置的消隐及闪烁属性不变。移动后,最右边一位为空(无显示)。例如原显

示为:

其中第2位“2”和第4位“4“为闪烁显示,执行了左移指令后显示变为:

第二位“3”和第四位“5”为闪烁显示。

(4) 右移指令,指令代码为A0H

与左移指令类似,但所做移动为自左向右(从第8位向第1位)移动,移动后最左边一

位为空。

(5) 循环左移指令,指令代码为A3H

与左移指令类似,不同之处在于移动后原最左边一位(第8位)的内容显示于最右位(第1

D7 D6 D5 D4 D3 D2 D1 D0

1 0 1 0 0 1 0 0

D7 D6 D5 D4 D3 D2 D1 D0

1 0 1 1 1 1 1 1

D7 D6 D5 D4 D3 D2 D1 D0

1 0 1 0 0 0 0 1

1 2 3 4 5 6 7 8

2 3 4 5 6 7 8

D7 D6 D5 D4 D3 D2 D1 D0

1 0 1 0 0 0 0 0

D7 D6 D5 D4 D3 D2 D1 D0

1 0 1 0 0 0 1 1

Page 139: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 139 .

位)。在上例中,执行完循环左移指令后的显示为:

第二位“3”和第四位“5”为闪烁显示。

(6) 循环右移指令,指令代码为A2H

与循环左移指令类似,但移动方向相反。

2、带有数据的指令

(1) 下载数据且按方式0译码指令

X表示无影响。命令由二个字节组成,前半部分为指令,其中a2、a1、a0为位地址,具

体分配见表7-3。

表7-3 显示位与a2、a1、a0的对照表

d0-d3为数据,收到此指令时,HD7279按表7-4所示的规则(译码方式0)进行译码。小

数点的显示由DP位控制,DP=1时小数点显示,DP=0时小数点不显示。

表7-4 方式0译码时数据和显示的关系表

2 3 4 5 6 7 8 1

D7 D6 D5 D4 D3 D2 D1 D0

1 0 1 0 0 0 1 0

D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0

1 0 0 0 0 a2 a1 a0 DP X X X d 3 d2 d1 d0

a2 a1 a0 显示位

0 0 0 1

0 0 1 2

0 1 0 3

0 1 1 4

1 0 0 5

1 0 1 6

1 1 0 7

1 1 1 8

d0-d3 (十六进制) d3 d2 d1 d0 7段显示

00H 0 0 0 0 0

01H 0 0 0 1 1

02H 0 0 1 0 2

03H 0 0 1 1 3

04H 0 1 0 0 4

05H 0 1 0 1 5

06H 0 1 1 0 6

07H 0 1 1 1 7

08H 1 0 0 0 8

09H 1 0 0 1 9

0AH 1 0 1 0 -

Page 140: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 140 .

(2) 下载数据且按方式1译码指令

X表示无影响。此指令与上一条指令基本相同,所不同的是译码方式,不同的地方见表7-5。

表7-5 方式0译码时数据和显示的关系表

(3) 下载数据但不译码指令

其中a2、a1、a0 为位地址(参见‘下载数据且译码’指令),A-G和DP 为显示数据,分别

对应7段LED 数码管的各段。当相应的数据位为“1”时该段点亮,否则不亮。

(4) 闪烁控制指令88H

此命令控制各个数码管的消隐属性。d1-d8 分别对应数码管1-8,0=闪烁,1=不闪烁。

开机后缺省的状态为各位均不闪烁。

(5) 消隐控制指令98H

此命令控制各个数码管的消隐属性。d1-d8 分别对应数码管1-8。1=显示,0=消隐。当

某一位被赋予了消隐属性后,zlg7289A在扫描时将跳过该位,因此在这种情况下无论对该

位写入何值,均不会被显示,但写入的值将被保留,在将该位重新设为显示状态后,最后

一次写入的数据将被显示出来。当无需用到全部8个数码管显示的时候,将不用的位设为消

隐属性,可以提高显示的亮度。

注意:至少应有一位保持显示状态。如果消隐控制指令中d1-d8全部为0,该指令将不

被接受,HD7279保持原来的消隐状态不变。

0BH 1 0 1 1 E

0CH 1 1 0 0 H

0DH 1 1 0 1 L

0EH 1 1 1 0 P

0FH 1 1 1 1 空(无显示)

D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0

1 1 0 0 1 a2 a1 a0 DP X X X d 3 d2 d1 d0

d0-d3 (十六进制) d3 d2 d1 d0 7段显示

0AH 1 0 1 0 A

0BH 1 0 1 1 B

0CH 1 1 0 0 C

0DH 1 1 0 1 D

0EH 1 1 1 0 E

0FH 1 1 1 1 F

D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0

1 0 0 1 0 a2 a1 a0 DP A B C D E F G

D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0

1 0 0 0 1 0 0 0 d8 d7 d6 d5 d4 d3 d2 d1

D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0

1 0 0 1 1 0 0 0 d8 d7 d6 d5 d4 d3 d2 d1

Page 141: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 141 .

(6) 段点亮指令E0H

此为段寻址指令,作用为点亮数码管中某一指定的段或LED 矩阵中某一指定的LED。指

令中:X=无影响,d0-d5为段地址,范围从00H-3FH,具体分配为:

第1个数码管的G段地址为00H,F段为01H,… …A段为06H,小数点DP为07H,第2个数

码管的G段为08H,F 段为09H,… …,依此类推,直至第8个数码管的小数点DP地址为3FH。

(7) 段关闭指令C0H

段寻址命令,作用为关闭(熄灭)数码管中的某一段,指令结构与‘段点亮指令’相

同。

(8) 读键盘数据指令15H

该指令从HD7279读出当前的按键代码。与其它指令不同,此命令的前一个字节

0001010B 为单片机传送到HD7279的指令,而后一个字节d0-d7则为HD7279返回的按键代码,

其范围是0-3FH(无键按下时为0xFF)。各键键盘代码的定义,请参阅图7-15,其中图中对

应S0--S63号键分别对应键值的0—63(0--3FH)。

此指令的前半段,HD7279的DATA引脚处于高阻输入状态,以接受来自微处理器的指令;

在指令的后半段,DATA 引脚从输入状态转为输出状态,输出键盘代码的值。故微处理器连

接到DATA引脚的I/O口应有一从输出态到输入态的转换过程。详情请参阅本文串行接口一节

的内容。

当HD7279检测到有效的按键时,KEY 引脚从高电平变为低电平,并一直保持到按键结

束。在此期间,如果HD7279接收到“读键盘数据指令”,则输出当前按键的键盘代码;如

果在收到“读键盘指令”时没有有效按键,HD7279将输出FFH(1111,1111B)。

7.3.3 HD7279 与单片机的接口及程序设计

1、HD7279与单片机的接口

HD7279采用串行方式与微处理器通讯,串行数据从DATA引脚送入芯片,并由CLK 端同

步,当片选信号变为低电平后,DATA引脚上的数据在CLK 引脚的上升沿被写入HD7279的缓

冲寄存器。

HD7279的指令结构有三种类型:1.不带数据的纯指令,指令的宽度为8个BIT,即微处

理器需发送8个CLK脉冲;2.带有数据的指令,宽度为16个BIT,即微处理器需发送16个CLK

脉冲;3.读取键盘数据指令,宽度为16个BIT,前8个为微处理器发送到HD7279的指令,后8

个BIT为HD7279返回的键盘代码。执行此指令时,HD7279的DATA端在第9个CLK 脉冲的上升

沿变为输出状态,并与第16 个脉冲的下降沿恢复为输入状态,等待接收下一个指令。

D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0

1 1 1 0 0 0 0 0 X X d5 d4 d3 d2 d1 d0

D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0

1 1 0 0 0 0 0 0 X X d5 d4 d3 d2 d1 d0

D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 D1 D0

0 0 0 1 0 1 0 1 d7 d6 d5 d4 d3 d2 d1 d0

Page 142: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 142 .

HD7279的典型应用图如图7-15所示。

HD7279应连接共阴式数码管,应用中,无需用到的数码管和键盘可以不连接,省去数

码管和对数码管设置消隐属性均不会影响键盘的使用。

如果不用键盘,则典型电路中连接到键盘的8只10K电阻和8只100K下拉电阻均可以省

去。如果使用了键盘,则电路中的8只10K电阻和8只100K下拉电阻均不得省略。除非不接数

码管,否则串入DP及SA-SG连线的8只电阻均不能省去。

/RSTRC

CLKD

DIG7

DIG0DIG1DIG2DIG3DIG4DIG5DIG6

DP

SA

SB

SC

SD

SE

SF

SG

100K*8

10K*8220*8

10

11

12

13

14

15

16

17

1819202122232425

6

7

8

9

5

4

3

2

1

VCC

RTCC

VCC

NC

GND

NC

/CS

CLK

DIO

KEY

12345678

S00S08S16S24S32S40S48S56

S63 S07

15p262728

VCC

HD7279

S15S23S55 S47 S39 S31

1.5K

P1.2

P1.3

P1.0

P1.1

MCU

图7-15 HD7279 与单片机的接口

实际应用中,8只下拉电阻和8只键盘连接位选线DIG0-DIG7 的8只电阻(位选电阻),

应遵从一定的比例关系,下拉电阻应大于位选电阻的5 倍而小于其50倍,典型值为10倍。

下拉电阻的取值范围是10K-100K,位选电阻的取值范围是1K-10K。在不影响显示的前提下,

下拉电阻应尽可能的取较小的值,这样可以提高键盘部分的抗干扰能力。

因为采用循环扫描的工作方式,如果采用普通的数码管,亮度有可能不够,采用高亮

或超高亮的型号可以解决这个问题。数码管的尺寸也不宜选的过大,一般字符高度不超过1

英寸,如使用大型的数码管,应使用适当的驱动电路。

HD7279 需要一外接 RC 振荡电路供系统工作。其典型值分别为R=1.5K,C=15P。如果

芯片无法正常工作,请首先检查此振荡电路。在印刷电路板布线时,所有元件,尤其是振

荡电路的元件应尽量靠近HD7279 ,并尽量使电路联线最短。

HD7279的 RESET 复位端在一般应用情况下,可以直接和VCC相连,在需要较高可靠性

的情况下,可以连接一外部复位电路,或直接由MCU 控制。在上电或RESET 端由低电平变

为高电平后,HD7279大约要经过18-25ms 的时间才会进入正常工作状态。

Page 143: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 143 .

上电后,所有的显示均为空,所有显示位的显示属性均为“显示”及“不闪烁”。当

有键按下时,KEY引脚输出低电,平此时如果接收到“读键盘”指令,HD7279将输出所按下

键的代码。如果在没有按键的情况下收到“读键盘”指令,HD7279将输出0FFH(255)。

因为芯片直接驱动LED数码管显示,电流较大,且为动态扫描方式,故如果该部分电路

电源连线较细较长,可能会引入较大的电源噪声干扰,在电源的正负极并入一47µF 到220µF

的电容可以提高电路抗干扰的能力。

注意:如果有2个键同时按下,HD7279 将只能给出其中一个键的代码,因此HD7279 不

适于应用在需要2个或2个以上键同时按下的场合。

2、串行操作

HD7279采用串行方式与微处理器通信,串行数据从DATA 引脚送入芯片,并由CLK端同

步。当片选信号变为低电平后,DATA 引脚上的数据在CLK引脚的上升沿被写入 HD7279的缓

冲寄存器。

串行接口的时序分三种指令形式给出,见以下图示。

1)纯指令

2)带数据指令

3)读键盘指令

3、HD7279的程序设计

Page 144: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 144 .

下面给出HD7279 的驱动程序,以图7-15的连接方式为例。程序功能为:上电后显示1~

8这 8个数字,再将64 个段逐个点亮;然后判断有按键按下没,有按键按下则在第1、2位

数码管上显示该按键号(0 ~ 63)。

更详尽的程序请参阅比高公司的资料。

#include <reg51.h>

void long_delay(void); //长延时

void short_delay(void); //短延时

void write7279(unsigned char, unsigned char);//写入 HD7279

unsigned char read7279(unsigned char);//从 HD7279 读出

void send_byte(unsigned char); //发送一个字节

unsigned char receive_byte(void); // 接收一个字节

unsigned char key_number; //定义全局变量,保存按键号(0 ~ 63)

sbit cs=P1^0; // cs 接 P1.0

sbit clk=P1^1; // clk 接 P1.1

sbit dat=P1^2; // dat(DIO)接 P1.2

sbit key=P1^3; // key 接 P1.3

#define CMD_RESET 0xa4 //复位指令

#define DECODE1 0xc8 //方式 1译码指令,用来向 HD7279 写数据以便显示

#define CMD_READ 0x15 //读键盘数据指令

#define CMD_TEST 0xbf //测试指令

#define DECODE0 0x80 //方式0译码指令,

#define UNDECODE 0x90 //下载数据但不译码

#define RTL_CYCLE 0xa3 //循环左移指令

#define RTR_CYCLE 0xa2 //循环右移指令

#define RTL_UNCYL 0xa1 //左移指令

#define RTR_UNCYL 0xa0 //右移指令

#define ACTCTL 0x98 //消隐控制指令

#define SEGON 0xe0 //段点亮指令

#define SEGOFF 0xc0 //段关闭指令

#define BLINKCTL 0x88 //闪烁控制指令

main()

unsigned int i;

unsigned char k;

for (i=0;i<20000;i++); //上电延时

send_byte(CMD_RESET); //复位 HD7279A

for (i=0;i<8;i++)

Page 145: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 145 .

write7279(DECODE1+i,i+1);//数码管从左到右显示 1~8

for (i=0;i<50000;i++); //延时

for (k=0;k<64;k++)

write7279(SEGON,k); // 将64个显示段逐个点亮

for (i=0;i<50000;i++); //延时

while (1)

if (!key) //如果有键按下

key_number=read7279(CMD_READ); //读按键号

write7279(DECODE1,key_number/10); //在第一、二位数码管显示按键号

write7279(DECODE1+1,key_number%10);

void write7279(unsigned char cmd, unsigned char dta)//写数据函数

//cmd 为指令,dta 为写入的数据

send_byte (cmd);

send_byte (dta);

unsigned char read7279(unsigned char command)//读数据指令,一般用来读按键号

send_byte(command);

return(receive_byte());

void send_byte( unsigned char out_byte) //发送一个字节

unsigned char i;

cs=0;

long_delay();

for (i=0;i<8;i++)

if (out_byte&0x80)

dat=1;

Page 146: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 146 .

else

dat=0;

clk=1;

short_delay();

clk=0;

short_delay();

out_byte=out_byte*2;

dat=0;

unsigned char receive_byte(void) //接收一个字节

unsigned char i, in_byte;

dat=1;

long_delay();

for (i=0;i<8;i++)

clk=1;

short_delay();

in_byte=in_byte*2;

if (dat)

in_byte=in_byte|0x01;

clk=0;

short_delay();

dat=0;

return (in_byte);

void long_delay(void) //长延时

unsigned char i;

for (i=0;i<0x30;i++);

void short_delay(void) //短延时

unsigned char i;

Page 147: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 147 .

练习题:(利用 MCU2007-I型单片机开发板实现)

1、用按键控制继电器:按下S1键,继电器吸合;按下 S2 键,继电器断开。

2、用键盘控制蜂鸣器发声:按任意键一下,蜂鸣器就发声;再按任意键一下,蜂鸣器

就停止发声。

3、用键盘控制流水灯的方向。按下S1,流水灯从左向右;按下S2,流水灯从右向左;

按下S3,所有LED 指示灯闪烁;按下S4 ,所有LED 指示灯全亮,然后从左到右逐个熄灭;

按下S5 ,所有 LED指示灯全亮,然后从右到左逐个熄灭。

for (i=0;i<8;i++);

Page 148: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 148 .

第 8章 定时/计数器

52系列单片机内部集成有三个16位的定时/计数器,分别为定时/计数器0、定时/计数

器1和定时/计数器2,简称为T0、T1和T2。利用它们可以完成一些定时或延时控制,如定时

输出、定时检测等;还可以实现对外部事件的计数功能。

8.1 定时/计数器 T0和 T1

在51 系列单片机内部只有两个定时/计数器T0 和 T1,定时/计数器T2 是 52 系列单片

机所独有的,本节首先介绍常用的两个定时/计数器T0 和 T1。

8.1.1 定时/计数器 T0和 T1的结构及功能

1、定时/计数器的结构

定时/计数器的结构如图8-1所示。CPU是通过内部总线与定时/计数器交换信息的。16

位的分别由两个8位的专用寄存器组成,即定时/计数器T0由TH0和TL0组成,T1由TH1和TL1

组成。此外,其内部还有2个8位的特殊功能寄存器TMOD和TCON,TMOD负责控制和确定T0和

T1的功能和工作模式,TCON用来控制T0和T1启动或停止计数,同时包含定时/计数器的状态。

这两个寄存器的内容可通过软件设置,系统复位时,寄存器的所有位都被清零。

CPU

INT1 INT0 (P3.2)(P3.0)

TH1 TL1

定时器T1 定时器T0

TH0 TL0

T1(P3.5) T0(P3.4)

TMODTCON

启动

内部总线

溢出

工作方式启

中断

图8-1 定时/计数器的结构

定时/计数器T0和T1都是加法计数器,每输入一个脉冲,计数器加1,当加到计数器全

为1时,再输入一个脉冲,就使计数器发生溢出,溢出时,计数器重新回零,并置位TCON中

的TF0或TF1,以表示定时时间已到或计数器已满,向CPU发出中断申请。

2、定时/计数器的功能选择

Page 149: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 149 .

T0和T1都具有定时和计数两种功能。在TMOD中,有一个控制位C/T,分别用于选择T0和

T1是工作于定时器方式,还是计数器方式。

1)计数功能

所谓计数是指对外部事件进行计数。外部事件的发生以输入脉冲表示,因此计数功能

的实质是对外来脉冲进行计数。51系列单片机有T0(P3.4)和T1(P3.5)两个信号引脚,分

别是这两个计数器的计数输入端。外部输入的脉冲在负跳变时有效,进行计数器加1。

计数方式下,单片机在每个机器周期的S5P2(12时钟模式)期间对外部计数脉冲进行

采样。如果前一个机器周期采样为高电平,后一个机器周期采样为低电平,即为一个有效

的计数脉冲。在下一机器周期的S3P1进行计数。可见采样计数脉冲是在2个机器周期进行的。

因此,计数脉冲的频率不能高于振荡脉冲频率的1/24。

2)定时功能

定时功能也是通过计数器的计数来实现的。不过此时的计数脉冲来自单片机的内部,即

每个机器周期产生一个计数脉冲,也就是每个机器周期计数器加1。由于一个机器周期等于

12(12时钟模式)个振荡脉冲周期,因此计数频率为振荡频率的1/12。

8.1.2 定时/计数器 T0和 T1的功能寄存器

与定时/计数器T0和T1应用有关的控制寄存器有:

1、工作方式控制寄存器TMOD

TMOD寄存器是一个专用寄存器,用于设定两个定时/计数器的工作方式。但TMOD不能位

寻址,只能有用字节传送指令设置其内容。TMOD的各位定义如下:

说明如下:

GATE位:门控位。该位置位时只有在INTn引脚置高及TRn控制置位时才可打开定时器/

计数器,这时可用于测量在INTn引脚出现的正脉冲的宽度。该位清零时,置位TR1即可打开

定时器/计数器。

C/T:计数/定时功能选择位。控制定时器是用作定时器或计数器,该位清零则用作定

时器(从内部系统时钟输入);该位置位则用作计数器(计数脉冲从Tn引脚输入)。

M1和M0:定时器模式选择位。见表8-1。

表8-1 定时/计数器 T0和T1工作模式

D7 D6 D5 D4 D3 D2 D1 D0

GATE C/T M1 M0 GATE C/T M1 M0

定 时 器 1 定 时 器 0

M1 M0 定 时 器 模 式

0 0 模式0:TLn中低5位与THn中的8位构成13位计数器

0 1 模式1:16位定时器/计数器,无预分频器

1 0 模式2:8位自装载定时器,当溢出时将THn存放的值装入TLn

1 1 模式3:定时器0此时作为双8位定时/计数器。TL0作为一个8位定时器/计数器,通过标准

定时器0控制位控制。TH0仅作为一个8位定时器,由定时器1控制位控制。在这种模式下定

Page 150: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 150 .

2、定时器控制寄存器TCON

TCON用来控制T0和T1的启动和停止。TCON各位的定义如下:

说明如下:

TF1:定时器1 溢出标志。定时/计数器溢出时由硬件置位。中断处理时由硬件清除。

或用软件清除。

TR1:定时器1 运行控制位。由软件置位时将定时/计数器打开,清零时将定时/计数器

关闭。

TF0:定时器0 溢出标志。定时/计数器溢出时由硬件置位。中断处理时由硬件清除 ,

或用软件清除。

TR0:定时器0 运行控制位。由软件置位时将定时/计数器打开,清零时将定时/计数器

关闭。

TCON的低4位与外部中断有关,具体说明参第九章。

8.1.3 定时/计数器 T0和 T1的工作模式

T0和T1均有4种工作模式,其中前三种工作模式对两者都是相同的,而第四种模式不同。

下面对这四种模式分别进行介绍。

1、模式0

将定时器设置成模式0(M1和M0均设为0)时类似8048定时器,即8位计数器带32分频的

预分频器。图8-2所示为模式0工作方式,其中6时钟模式d=6,12时钟模式d=12。

此模式下定时器寄存器配置为13位寄存器。当计数从全为“1”翻转为全“0”时,定

时器中断标志位TF0置位。当TR0=1同时GATE=0或INT0=1时定时器计数,置位GATE时允许由

外部输入INT0控制定时器,这样可实现脉宽测量。TR0为TCON寄存器内的控制位。

该13位寄存器包含THn全部8个位及TLn的低5位。TLn的高3位不定,可将其忽略。置位

运行标志(TRn)不能清零此寄存器。

&

≥1

振荡器 ÷d

Tn引脚

INTn引脚

1

TRn

C/T=0

C/T=1控制

TLn(5位)

THn(8位)

TFn中断

GATE

图8-2 定时/计数器0/1 的模式0(13 位定时/计数器)

时/计数器T1关闭。

D7 D6 D5 D4 D3 D2 D1 D0

TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0

Page 151: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 151 .

模式0的操作对于定时器0及定时器1都是相同的,两个不同的GATE位(TMOD.7和TMOD.3)

分别分配给定时器0及定时器1。

在模式0下,当为计数工作方式时,计数值的范围是:1-8192(213)。当为定时器工作

方式时,定时时间的计算公式为:(213-计数初值)×晶振周期×d(6时钟模式d=6,12时

钟模式d=12)。其时间单位为微秒(µS)。

2、模式1

模式1除了使用了THn及TLn全部16位外,其它与模式0相同。模式1情况下定时时间的计

算公式为:(216-计数初值)×晶振周期×d(6时钟模式d=6,12时钟模式d=12)。其时间

单位为微秒(µS)。

3、模式2

此模式下定时器寄存器作为可自动重装的8位计数器(TLn),如图8-3所示,其中6时

钟模式d=6,12时钟模式d=12。TLn 的溢出不仅置位TFn,而且将THn 内容重新装入TLn,THn

内容由软件预置。重装时THn内容不变。模式2的操作对于定时器0及定时器1是相同的,定

时时间的计算公式为:(28-计数初值)×晶振周期×d(6时钟模式d=6,12时钟模式d=12)。

其时间单位为微秒(µS)。

&

≥1

振荡器 ÷d

Tn引脚

INTn引脚

1

TRn

C/T=0

C/T=1控制

TLn(8位)

TH1(8位)

TFn中断

GATE重装载

图8-3 定时/计数器0/1 的模式2(8 位自动重装)

4、模式3

&

≥1

振荡器 ÷d

T0引脚

INT0引脚

1

TR0

C/T=0

C/T=1控制

TL0(8位) TF0

中断

GATE

÷d

TR1

控制

TH0(8位 )

TF1振荡器中断

图8-4 定时/计数器0 的模式3(双8 位计数器)

Page 152: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 152 .

在模式3中定时器1停止计数,效果与将TR1设置为0相同。此模式下定时器0的TL0及TH0

作为两个独立的8位计数器。图8-4为模式3时的定时器0逻辑,其中6时钟模式d=6,12时钟

模式d=12。TL0占用定时器0的控制位:C/T、GATE、TR0、INT0及TF0。TH0限定为定时器功

能(计数器周期),占用定时器1的TR1及TF1,此时TH0控制“定时器1”中断。

模式3可用于需要一个额外的8位定时器的场合。定时器0工作于模式3时,80C51看似有

3个定时器/计数器,当定时器0工作于模式3时,定时器1可通过开关进入/退出模式3,它仍

可用作串行端口的波特率发生器,或者应用于任何不要求中断的场合。

8.1.4 定时/计数器 T0和 T1应用举例

1.设单片机系统时钟频率为12MHz,工作于12时钟模式,试编程使P1.2引脚输出周期为

5ms的方波。

当系统时钟为12MHz时,若定时 /计数器工作于模式 0,则最大定时时间为213=8192µS,满足周期为5ms的要求,可选用定时器T0,工作于模式1,定时时间为2.5ms。首先计算定时

器的初值,根据下式:

(213-计数初值)×晶振周期×12=2500µS

可计算出定时器的初值为5692,由于模式0用到了TH0的8位和TL0的低5位,于是可计算

出TH0=B1H,TL0=1CH。程序编制如下:

2.利用定时器T0测量正脉冲的宽度(时间),脉冲从P3.2引脚输入,设脉冲宽度不超过

定时器的定时范围,且系统时钟为12MHz,单片机工作于12时钟模式。要求把该脉冲宽度值

存入变量pul_width中。

在模式0的介绍中提到过,利用门控位 GATE可以测量外部脉冲的宽度。具体方法是:

令GATE=1,然后软件置位TR0(或TR1),这时当INT0(或INT1)为1时(外部脉冲的上升

#include<reg52.h>

sbit p1_2=P1^2;

void main()

TMOD=0x00;

TH0=0xb1;

TL0=0x1c;

TR0=1;

while(1)

while(TF0==0); //等待定时器溢出

TF0=0;

p1_2=!p1_2;

Page 153: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 153 .

沿)就会自动启动定时器,当INT0(或INT1)变为0时(外部脉冲的下降沿)关闭定时器,

于是定时器中的计数值就体现了外部脉冲的宽度。程序编制如下:

3.利用定时器实现一个时钟,时间从12时34分56秒开始走,并在MCU2007-I型开发板的

LED显示器中显示该时间,时、分、秒之间用“-”隔开。(查询方式)

利用定时器0产生50ms的定时间隔,这样计数溢出10次则可得到1s。让定时器0工作于

方式0,假定单片机为12MHz的时钟频率,设计数初值为T,则有如下等式:

(216-T)=50000µs

于是可计算出计数初值为T=15536,对应的16进制数为0x3CB0。

#include<reg52.h>

sbit p3_2=P3^2; /*定义定时器0的外部引脚*/

unsigned int pul_width; /*定义全局变量以保存脉宽结果*/

void main() /*主程序*/

unsigned char a; /*定义中间变量*/

TMOD=0x09; /*T0工作为16位计数器方式,GATE置1*/

TL0=0x00; /*计数器装入初值*/

TH0=0x00;

while(1)

while(p3_2==1); /*等待INT0变低,以检测一个完整的正脉冲*/

TR0=1; /*定时器准备好,INT0来上升沿则启动*/

while(p3_2==0); /*等待INT0的上升沿*/

while(p3_2==1); /*等待INT0的下降沿,此时为完整的正脉冲*/

TR0=0; /*关闭定时器*/

a=TH0; /*计数结果高8位存入中间变量*/

Pul_width=a*256+TL0; /*计算脉宽的最终结果*/

#include<reg52.h>

sbit cs_138=P2^6;

sbit cs_373=P1^5;

unsigned char code zima1[11]=0xc0,0xf9,0xa4,0xb0,0x99,0x92, 0x82,0xf8, 0x80,0x90,

0xbf;//0~9及“-”的字形码

unsigned char hour,minute,second; //定义全局变量保存时间

void delay(); //延时函数

void xianshi(); //显示函数

void main()

unsigned char i

Page 154: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 154 .

cs_138=1;

cs_373=1;

i=0;

hour=12;

minute=34;

second=56;

TMOD=0x01; //定时器0,模式1,

TL0=0xb0; //定时时间为50ms

TH0=0x3c;

TR0=1; //启动定时器

while(1)

if(TF0==1) //如果定时时间到

TF0=0; //清零溢出标志

TL0=0xb0; //重新赋初值

TH0=0x3c;

i++;

if(i==20) //如果1秒钟已到

second++;

i=0;

if(second==60) //如果1分钟已到

second=0;

minute++;

if(minute==60) //如果1小时已到

minute=0;

hour++;

if(hour==24)

hour=0;

xianshi(); //不断显示

Page 155: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 155 .

4.用一个计数器和一个定时器来检测一台电机的转速。设在电机主轴上安装了一个旋

转编码器,该旋转编码器随电机主轴一起旋转,每转一周编码器发出100个脉冲,该脉冲输

void delay() //延时函数

unsigned char i;

for(i=0;i<80;i++);

void xianshi(void) //LED显示的函数

P0=zima1[hour/10]; //显示小时

P1=0xbf; //在第1、2位数码管显示

delay();

P0=zima1[hour%10];

P1=0xbb;

delay();

P0=zima1[10]; //显示“-”

P1=0xb7; //在第3位数码管显示

delay();

P0=zima1[minute/10]; //显示分

P1=0xb3; //在第4、5位数码管显示

delay();

P0=zima1[minute%10];

P1=0xaf;

delay();

P0=zima1[10]; //显示“-”

P1=0xab; //在第6位数码管显示

delay();

P0=zima1[second/10]; //显示秒

P1=0xa7; //在第7、8位数码管显示

delay();

P0=zima1[second%10];

P1=0xa3;

delay();

Page 156: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 156 .

出端接到单片机T0端,通过检测每秒钟内T0端的脉冲个数即可知道该电机的转速。

让定时/计数器T0工作于16位计数器方式,定时/计数器T1工作于16位定时器方式,先

启动计数器T0对外部脉冲计数,同时启动T1开始定时,每隔1秒的时间关闭计数器T0,此时

计数器T0的计数值除以100就是电机的转速,单位是 r/s(转/秒)。

程序编制如下( 设系统时钟为12MHz):

练习题:

1、用定时器实现一个秒表,按下S1键秒表启动,最低有效时间为0.1s;按下S2键秒表

#include <reg51.h>

unsigned int num; //定义保存计数值的变量

unsigned char n; //定义保存转速的变量

void main()

unsigned char k=0,a;

TMOD=0x51; //T0工作为16位计数器方式,T1为16位定时器方式

TL1=0xb0; //T1置初值,定时时间为50ms

TH1=0x3c;

while(1)

TL0=0x00; //计数器装入初值

TH0=0x00;

TR0=1; //启动计数器T0

TR1=1; //启动定时器T1

while(k<20) //等待1秒定时到

if(TF1==1) //如果50ms定时到

TL1=0xb0; //T1重置初值

TH1=0x3c;

k++;

TR0=0; //关闭计数器T0

TR1=0; //关闭定时器T1

k=0; //中间变量清0

a=TH0; //计数结果高8位存入中间变量

num=a*256+TL0; //计算计数结果

n=num/100; //计算转速

Page 157: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 157 .

停止;按下S3键秒表清零;每按下一次按键蜂鸣器叫一声进行提示。在LED显示器上显示该

时间,并在秒与0.1秒之间显示小数点。

2、用MCU2006型单片机开发板编写程序,实现时钟功能,只显示分和秒信息,时间从

12分34秒开始走,在分和秒之间显示“-”,且“-”每隔一秒显示一次。

8.2 定时/计数器 T2

定时器2是一个16位定时/计数器,通过设置特殊功能寄存器T2CON中的C/T2位,可将其

作为定时器或计数器。

8.2.1 T2控制寄存器T2CON 和 T2MOD

1、状态控制寄存器T2CON

T2CON是定时/计数器T2的状态控制寄存器 ,用于确定T2的工作方式、各种功能选择及

有关状态信息。T2CON可位寻址,因此所有标志或控制位都可以用位操作指令来置位或清零。

T2CON的格式如下:

说明如下:

TF2:定时器2 溢出标志。定时器2 溢出时置位,必须由软件清除。当RCLK或TCLK 1 时,

TF2 将不会置位。

EXF2:定时器2 外部标志。当EXEN2 1 且T2EX 的负跳变产生捕获或重装时,EXF2置位。

定时器2 中断使能时,EXF2=1 将使CPU 从中断向量处执行定时器2 中断子程序。EXF2 位

必须用软件清零,在递增/递减计数器模式DCEN 1 中,EXF2 不会引起中断。

RCLK:接收时钟标志。RCLK 置位时,定时器2 的溢出脉冲作为串行口模式1 和模式3 的

接收时钟。RCLK=0 时,将定时器1 的溢出脉冲作为接收时钟。

TCLK:发送时钟标志。TCLK 置位时,定时器2 的溢出脉冲作为串行口模式1 和模式3 的

发送时钟。TCLK=0 时,将定时器1 的溢出脉冲作为发送时钟

EXEN2:定时器2 外部使能标志。当其置位且定时器2 未作为串行口时钟时,允许T2EX

的负跳变产生捕获或重装。EXEN2=0 时,T2EX 的跳变对定时器2 无效。

TR2:定时器2 启动/停止控制位。置1 时启动定时器。

C/T2:定时器/计数器选择(定时器2)。

0:内部定时器(OSC/12 或OSC/6);

1:外部事件计数器(下降沿触发)。

CP/RL2:捕获/重装标志。置位:EXEN2=1 时T2EX 的负跳变产生捕获。清零:EXEN2=1

时定时器2 溢出或T2EX 的负跳变都可使定时器自动重装。当RCLK=1 或TCLK=1 时,该位无

效且定时器强制为溢出时自动重装。

D7 D6 D5 D4 D3 D2 D1 D0

TF2 EXF2 RCLK TCLK EXEN2 TR2 C/T2 CP/RL2

Page 158: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 158 .

2、模式控制寄存器T2MOD

T2MOD是定时/计数器T2的模式控制寄存器,不可位寻址,格式如下:

说明如下:

“-”:不可用,保留将来之用。但不可将其置1,这些位在将来8051 系列产品中用来

实现新的特性,这种情况下,以后用到保留位,复位时或非有效状态时,它的值应为0,而

这些位为有效状态时,它的值为1,从保留读到的值是不确定的。

T2OE:定时器2 输出使能位。

DCEN:向下计数使能位。定时器2 可配置成向上/向下计数器。

8.2.2 T2 的操作模式

定时器2 有三种操作模式:捕获、自动重新装载(递增或递减计数)和波特率发生器,

这三种模式由T2CON 中的位进行选择,见表8-2。

表8-2 定时/计数器T2工作模式

1、捕获模式

在捕获模式中,通过T2CON 中的EXEN2 设置两个选项。如果EXEN2=0,定时器2 作为一

个16 位定时器或计数器(由T2CON 中C/T2 位选择),溢出时置位TF2(定时器2 溢出标志

位)。通过使能IE 寄存器中的定时器2 中断使能位,该位可用于产生中断。如果EXEN2=1,

与以上描述相同,但增加了一个特性,即外部输入T2EX 由1 变0 时将定时器2 中TL2 和TH2

的当前值各自捕获到RCAP2L 和RCAP2H。另外,T2EX 的负跳变使T2CON 中的EXF2 置位,EXF2

也象TF2 一样能够产生中断,其向量与定时器2溢出中断地址相同,定时器2 中断服务程序

通过查询TF2 和EXF2 来确定引起中断的事件。

捕获模式如图8-4所示。在该模式中TL2 和TH2 无重新装载值。甚至当T2EX 产生捕获

事件时,计数器仍以T2EX 的负跳变或振荡频率的1/12(12 时钟模式)或1/6(6 时钟模式)

计数。

2、自动重装模式(递增/递减计数器)

16 位自动重装模式中,定时器2 可通过C/T2 配置为定时器/计数器,编程控制递增/

递减计数。计数的方向是由DCEN (递减计数使能位)确定的。DCEN 位于T2MOD 寄存器中,

D7 D6 D5 D4 D3 D2 D1 D0

- - - - - - T2OE DCEN

RCLK+TCLK CP/RL2 TR2 操 作 模 式

0 0 1 16 位自动重装

0 1 1 16 位捕获

1 X 1 波特率发生器

X X 0 (关闭)

Page 159: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 159 .

当DCEN=0 时,定时器2 默认为向上计数;当DCEN =1 时,定时器2 可通过T2EX 确定递增

或递减计数。

图8-5显示了当DCEN=0 时,定时器2 自动递增计数,在该模式中通过设置EXEN2 位进

行选择。如果EXEN2= 0, 定时器2 递增计数到0FFFFH 并在溢出后将TF2 置位,然后将

RCAP2L 和RCAP2H 中的16 位值作为重新装载值装入定时器2。RCAP2L 和RCAP2H 的值是通

过软件预设的。

振荡器 ÷d

T2引脚

C/T2=0

C/T2=1控制

TL2(8位)

TH2(8位)

TF2

定时器2中断

TR2

T2EX引脚

跳变检测

EXEN2控制

PCAP2L PCAP2H

≥1

EXF2

图8-4 定时/计数器2的捕获模式

如果EXEN2=1,16 位重新装载可通过溢出或T2EX 从1到0 的负跳变实现,此负跳变同

时将EXF2置位。如果定时器2 中断被使能,则当TF2 或EXF2 置1 时产生中断。

在图8-6中,DCEN=1 时定时器2 可递增或递减计数,此模式允许T2EX 控制计数的方向。

当T2EX置1 时,定时器2 递增计数,计数到0FFFFH 后溢出并置位TF2, 还将产生中断(如

果中断被使能)。定时器2 的溢出将使RCAP2L 和RCAP2H 中的16 位值作为重新装载值放入

TL2 和TH2。

振荡器 ÷d

T2引脚

C/T2=0

C/T2=1控制

TL2(8位)

TH2(8位)

TF2

定时器2中断

TR2

T2EX引脚

跳变检测

EXEN2

控制

RCAP2L RCAP2H

≥1

EXF2

≥1

重装

图8-5 定时/计数器2的自动重装模式(DCEN=0)

当T2EX 置零时,将使定时器2 递减计数。当TL2 和TH2 计数到等于RCAP2L 和RCAP2H

时,定时器产生溢出,定时器2 溢出置位TF2 ,并将0FFFFH 重新装入TL2 和TH2。

Page 160: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 160 .

当定时器2 递增/递减产生溢出时,外部标志位EXF2 翻转。如果需要,可将EXF2 位作

为第17 位。在此模式中,EXF2 标志不会产生中断。

3、波特率发生器模式

寄存器T2CON的位TCLK 和RCLK允许从定时器1或定时器2获得串行口发送和接收的波特

率。当TCLK=0时,定时器1 作为串行口发送波特率发生器;当TCLK=1时,定时器2作为串行

口发送波特率发生器。RCLK对串行口接收波特率有同样的作用。通过这两位,串行口能得

到不同的接收和发送波特率:一个通过定时器1产生,另一个通过定时器2产生。

当定时器2工作在波特率发生器模式时,与自动重装模式相似,当TH2溢出时,波特率

发生器模式使定时器2寄存器重新装载来自寄存器RCAP2H和RCAP2L的16位的值,寄存器

RCAP2H和RCAP2LR的值由软件预置。

振荡器 ÷d

T2引脚

C/T2=0

C/T2=1控制

TL2(8位)

TH2(8位) TF2 定时器2

中断

TR2

RCAP2L RCAP2H

EXF2

FFH FFH

溢出

T2EX引脚

计数方向

1=上0=下

向下计数重装

向上计数重装

交叉

图8-6 定时/计数器2的自动重装模式(DCEN=1)

当工作于模式1 和模式3 时,波特率由下面给出的定时器2 溢出率所决定:

定时器可配置成“定时”或“计数”方式,在许多应用上,定时器被设置在“定时”

方式(C/T2*=0)。当定时器2 作为定时器时,它的操作不同于波特率发生器。

通常定时器2 作为定时器,它会在每个机器周期递增(1/6或1/12振荡频率)。当定时

器2作为波特率发生器时,它在6时钟模式下,以振荡器频率递增(12时钟模式时为1/2振荡

频率),这样,波特率公式如下:

此处n = 16(6时钟模式)或32(12时钟模式)

(RCAP2H,RCAP2L)=RCAP2H 和RCAP2L 的内容,为16 位无符号整数。

当定时器2作为波特率发生器时,仅当寄存器T2CON中的RCLK和(或)TCLK=1时,定时

Page 161: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 161 .

器2作为波特率发生器才有效。注意TH2 溢出并不置位TF2,也不产生中断。这样当定时器2

作为波特率发生器时,定时器2中断不必被禁止。如果EXEN2(T2外部使能标志)被置位,

在T2EX中,由1到0的转换会置位EXF2(T2外部标志位),但并不导致(TH2,TL2)重装载

(RCAP2H,RCAP2L)。 因此,当定时器2用作波特率发生器时,如果需要,T2EX可用作附

加的外部中断。

当计时器工作在波特率发生器模式下,则不要对TH2和TL2进行读写,每隔一个状态时间

(0sc/2)或由T2进入的异步信号,定时器2将加1,在此情况下对TH2 和TH1 进行读写是不

准确的。可对RCAP2 寄存器进行读,但不要进行写,否则将导致自动重装错误。当对定时

器2或寄存器RCAP进行访问时,应关闭定时器(清零TR2)。

表8-3列出了常用的波特率和如何用定时器2 得到这些波特率。

表8-3 由定时器2 产生的常用波特率

4、波特率公式汇总

定时器2工作在波特率发生器模式,外部时钟信号由T2 脚进入,波特率为:

如果定时器2采用内部时钟信号,则波特率为:

此处n = 32(12时钟模式)或16(6时钟模式)。

fOSC = 振荡器频率

自动重装值可由下式得到:

RCAP2H,RCAP2L=65536-[fosc/(n 波特率)]

5、T2的设置

除了波特率发生器模式,T2CON 不包括TR2 位的设置,TR2 位需单独设置来启动定时

器。表8-4、表8-5给出了T2 作为定时器和计数器的设置。

波特率 振荡器频率 定时器 2

12 时钟模式 6时钟模式 RCAP2H RCAP2L

375K 750K 12MHz FF FF

9.6K 19.2K 12MHz FF D9

4.8K 9.6K 12MHz FF B2

2.4K 4.8K 12MHz FF 64

1.2K 2.4K 12MHz FE C8

300 600 12MHz FB 1E

110 220 12MHz F2 AF

300 600 6MHz FD 8F

110 220 6MHz F9 57

Page 162: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 162 .

表8-4 T2 作为定时器

表8-5 T2 作为计数器

注:1、仅当定时器溢出时进行捕获和重装;

2、当定时/计数器溢出并且T2EX(P1.1)发生电平负跳变时产生捕获和重装(定时器2 用于波特率

发生器模式时除外)。

6、可编程时钟输出

STC89C52系列单片机,可以设定定时/计数器2,通过P1.0输出时钟。P1.0除作为通用

I/0口外,还有两个功能可供选用:用于定时/计数器2的外部计数输入和定时/计数器2时钟

信号输出。

通过软件对T2CON.1位C/T2复位为0,对T2MOD.1位T2OE置1就可将定时/计数器2选为时钟

信号发生器,而T2CON.2位TR2控制时钟信号开始或结束(TR2为启停控制位)。由主振频率

(fosc)和定时/计数器2定时、自动再装入方式的计数初值决定时钟信号的输出频率,其

设置公式如下:

时钟信号输出频率=)]2,2(65536[ LRCAPHRCAPn

fosc

上式中,n=2,6时钟/机器周期;n=4,12时钟/机器周期。

从公式可见,主振频率(fosc)设定后,时钟信号输出频率的设定就取决于定时/计数

器2初值的设定。

在时钟输出模式下,计数器回0溢出不会产生中断请求,这种功能相当于定时/计数器2

作为波特率发生器,同时又可作时钟发生器。但必须注意,无论如何波特率发生器和时钟

发生器不能单独确定不同的频率,因为两者都同时用同一个陷阱寄存器RCAP2H和RCAP2L,

不可能出现两个计数初值。

模 式 T2CON

内部控制(注 1) 外部控制(注 2)

16 位重装 00H 08H

16 位捕获 01H 09H

波特率发生器接收和发送相同波特率 34H 36H

只接收 24H 26H

只发送 14H 16H

模 式 TMOD

内部控制(注1) 外部控制(注2)

16 位 02H 0AH

自动重装 03H 0BH

Page 163: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 163 .

第 9章 中断系统

在单片机系统中,输入/输出操作是最基本的操作,对CPU而言,就是数据的传送。常

用的数据传送方式有程序控制方式、中断控制方式和DMA方式。

程序控制方式又包括无条件传送方式和查询方式。无条件传送时,CPU无需询问外设是

否准备好,通过程序向指定的端口执行输入/输出操作。查询方式是指在执行一个输入/输

出传送之前,CPU首先要去查询I/0设备的当前状态,获得端口的状态信息,看它是否准备

就绪。对输入操作,查询是否有新的数据送入端口,即准备好否;对输出操作,查询是否

已完成上次的输出任务,即设备是否忙。如果外设未准备好,就进入循环等待;如果已经

准备好,就执行数据传送。在查询方式下,I/O操作完全由CPU控制,即由CPU执行启动、控

制以及输入/输出操作。这时CPU要不断检测外设状态,直至外设准备好后才传送数据。这

在与慢速外设进行I/O传送时,不仅浪费了CPU的时间,降低了主机的效率,而且在很多控

制过程中也是不可行的。

DMA方式是直接存储器存取方式,它适合于大批量数据高速传送的场合,而且需要专门

的硬件支持,一般在单片机控制系统中应用较少。

中断方式可使CPU不必定时查询接口状态,而由接口在数据发送完毕或接收数据准备好

后,通知CPU ,CPU通过执行一个中断服务程序来完成数据的传送。而当接口没有准备好时,

CPU可以继续执行主程序,从而可以大大提高CPU的工作效率。

9.1 中断控制方式

9.1.1 中断的概念

所谓中断,是指CPU在正常运行程序时,由于CPU以外某一事件的发生(例如一个电平

的变化、一个脉冲沿的产生或定时/计数器溢出等),使得CPU中断正在运行的程序,而转

去为预先安排好的该事件服务的程序中去执行。这些引起程序中断的事件称为中断源,中

断源要求为其服务的请求称为中断请求。中断请求信号何时发生不是预先知道的,但它们

一旦产生,便会马上通知CPU,这样CPU就无需花费大量的时间去查询这些信号是否存在。

当CPU接到中断请求信号后,便立即转去执行相应的中断服务程序,执行完以后, CPU便返

回原来所执行程序的中断处(称为断点)继续往下执行。如图9-1所示。

在实际应用中,常常会有多个中断源请求中断服务,因此CPU响应这些中断就有个先后

次序,这称为中断的优先级。对于优先级高的中断,CPU优先响应。在执行中断服务程序时,

若产生了优先权较高的中断请求,这时CPU就暂停正在处理的中断,转去响应优先权较高的

中断,等具有较高优先权的中断处理完毕后,再返回继续执行原来的中断处理程序。在执

行某一中断服务程序时,如果有低级或同级中断申请,CPU均不响应。上述这种处理过程称

为中断嵌套。二级中断嵌套的中断过程如图9-2所示。

Page 164: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 164 .

断点

返回

主程

断响

断响

断响

返 回

返 回

图9-1 中断流程示意图 图9-2 中断嵌套流程图

9.1.2 中断处理过程

一个完整的中断传送方式的处理过程应包括中断请求、中断优先权判别、中断响应、

中断处理和中断返回等几个步骤。

1.中断请求

中断请求是中断源向CPU发出的中断申请,这是引起中断的原因。不同的中断源将根据

不同的原因产生中断请求,例如,输人设备输人一个字符后向CPU发出中断申请,请求读取

数据;输出设备从端口取走一个字符后发出中断申请,请求发送下一个数据等。中断请求

信号一经产生,将一直保持着,直到CPU响应该中断后,才清除中断请求信号。

2.中断优先权判别

如果多个中断源同时向CPU发出中断请求, CPU每次只能响应其中的一个中断,CPU究

竟响应哪一个中断,实际上是根据不同中断源的中断优先级来排序的。CPU优先处理中断优

先级高的中断申请。实现中断优先级判别的方法主要是用软件来实现。

一般采用查询技术实现软件判别。当CPU响应中断后,用软件查询来确定当前提出中断

申请的优先权最高的中断源。软件判别的特点是,查询的次序就是中断源优先权的次序。

采用这种方法可节省硬件开销,但实现速度较慢。

3.中断响应

当CPU得到经中断判别送出的中断请求信号后,要等到出现适当的条件才能响应中断。

这些条件一般包括:当前的一条指令已执行完毕而不是正在执行中,且中断允许寄存器中

的有关位置1(即CPU开中断)。满足上述条件后,CPU才响应中断,进人中断响应周期。在

中断响应周期完成将程序中断处的断点地址压人堆栈保护起来,然后将对应的中断服务程

序人口地址装入程序计数器PC,控制程序转移到相应的中断服务程序去执行。

4.中断处理

中断处理是由中断服务子程序来完成的。进人中断服务子程序后,首先要做的就是保

护现场,即把断点处的寄存器及标志寄存器的有用内容压入堆栈,以便不影响主程序的运

行,然后再执行中断服务程序。在中断处理结束前又要恢复现场,即把堆栈中的内容恢复

到各寄存器。

Page 165: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 165 .

在中断处理进行的过程中可能又有新的中断请求到来,但保护现场和恢复现场的操作

是不允许打扰的,否则就会造成原有内容的破坏。为此在保护现场和恢复现场之前要先关

闭中断系统,以屏蔽其它中断请求。待保护现场和恢复现场完成后,为了使系统具有中断

嵌套的功能,再开放中断系统。

5.中断返回

每一个中断服务子程序的末尾都要使用中断返回指令,该指令的功能是恢复断点地址。

待返回指令执行完毕后,便返回到被中断了的程序,从断点地址开始继续向下执行原程序。

9.2 52 系列单片机的中断系统

因为52系列单片机比51系列单片机多了一个定时 /计数器T2,其余与51系列单片机相

同,因此以52系列单片机为例对中断系统进行介绍。

51系列单片机有6个中断源(比80C51多1个),2个中断优先级,在没有产生同级的中

断和更高级的中断情况下,中断将被执行;如果同级的中断或更高级的中断正在执行,新

的中断只有等到正在执行的中断结束才能被执行。在更低级的中断正在执行情况下产生新

的中断时,低级的中断停止,转而执行新的中断,直到新中断完成才可以执行被停 止的中

断。

6个中断源的中断请求能否得到响应,受中断允许寄存器IE中各位的控制;它们的优先

级分别由中断优先级寄存器IP和IPH的各位确定;同一优先级内的各中断源同时申请中断

时,还要通过内部的查询逻辑来确定响应的次序;不同的中断源有不同的中断矢量。

9.2.1 中断源类型

52系列单片机有6个中断源,分别是:

1.INTO:外部中断0请求,由P3.2引脚输入。它有两种触发方式,通过IT0(TCON.O)

来决定是电平触发方式还是边沿触发方式。一旦输入信号有效,则向CPU申请中断,并且将

中断标志IEO置1。

2.INT1:外部中断1请求,由P3.3引脚输人。通过IT1(TCON.2)来决定是电平触发方

式还是边沿触发方式。一旦输入信号有效,则向CPU申请中断,并将中断标志IE1置1。

3.TF0:定时器T0溢出中断请求。当定时器TO产生溢出时,TO中断请求标志TF0置1,

请求中断处理。

4.TF1:定时器T1溢出中断请求。当定时器Tl产生溢出时,T1中断请求标志TF1置1,

请求中断处理。

5.TI/RI:串行口发送/接收中断请求。当通过串行口发送或接收完一帧串行数据时,

串行口中断请求标志TI或RI置1,请求中断处理。

6.TF2:定时器T2溢出中断请求。TF2在定时器2溢出时置位。

Page 166: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 166 .

9.2.2 中断请求标志

中断请求是通过几个特殊功能寄存器中的有关位来标示的,只要判别这些位的状态就

能确定有无中断请求及中断的来源。

1、TCON的中断标志

TCON是专用寄存器,字节地址为88H,可位寻址。它锁存了外部INT0和INT1的中断请求

标志及T0和T1的溢出中断请求标志,与中断有关的位如下:

说明如下:

(1) ITO:选择外部中断O(INT0)触发方式控制位。

当ITO=O时,为电平触发方式。在这种方式下,CPU在每个机器周期的S5P2期间采样INTO

(P3. 2 )引脚输入电平,若采样为低电平时,认为有中断申请,则置IEO标志为1;若采样

为高电平,认为无中断申请或中断申请已撤除,则将IEO标志清0。注意在电平触发方式下,

CPU响应中断后不会自动清除IEO标志,也不能由软件清除IE0标志,所以在中断返回前,一

定要撤消INTO引脚上的低电平,使IE0置0,否则将再次引起中断。

当ITO=1时,为边沿触发方式。CPU在每个机器周期的S5P2期间采样INTO引脚输人电平,

如果连续两次采样,一个机器周期中采样为高电平,接着下个机器周期中采样为低电平,

则置IE0标志为1,表示外部中断0正在向CPU申请中断。当CPU响应该中断时,IEO由硬件自

动清0。由于每个机器周期采样一次外部中断输人电平,在边沿触发方式中,为保证CPU在

两个机器周期内检测到由高到低的负跳变,必须保证外部中断源输人的高电平和低电平的

持续时间在12个时钟周期以上。

(2) IEO:外部中断O(INT0)请求标志位。IEO=1,外部中断0向CPU申请中断。

(3) ITl:选择外部中断1(INT1)触发方式控制位。其操作功能与IT0相同。

(4) IEl:外部中断1(INT1)请求标志位。IEl=1时,外部中断1向CPU申请中断。

(5) TFO:片内定时器TO溢出中断请求标志。TO被启动后,从初始值开始进行加1计数,

当最高位产生溢出时置TFO = l,向CPU申请中断,直到CPU响应该中断时,才由硬件自动将

TFO清0,也可由软件查询该标志,并用软件清O。

(6) TF1:片内定时器T1溢出中断请求标志,其操作功能与TF0相同。

2.、SCON的中断标志

SCON也是专用寄存器,字节地址为98H,可位寻址。其低2位锁存了串行口发送/接收

中断请求标志TI/RI。与中断有关的位如下:

说明如下:

(1) TI:串行口发送中断标志位。当CPU将一个数据写人发送缓冲器SBUF时,就启动发

送,每发送完一个串行帧,由硬件置TI=1,向CPU发中断申请。因CPU响应中断时并不清除

D7 D6 D5 D4 D3 D2 D1 D0

TF1 TF0 IE1 IT1 IE0 IT0

D7 D6 D5 D4 D3 D2 D1 D0

TI RI

Page 167: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 167 .

TI,所以必须由软件清除。

(2) RI:串行口接收中断标志位。当允许串行口接收数据时,每接收完一个串行帧,

由硬

件置RI=1,向CPU发中断申请。CPU响应中断时也不会自动清除RI,因此必须由软件清除。

3、T2CON的中断标志

T2CON也是专用寄存器,字节地址为C8H,可位寻址。与中断有关的位如下:

说明如下:

(1)TF2:定时器2 溢出标志。定时器2 溢出时置位,必须由软件清除。当RCLK或TCLK =1

时,TF2 将不会置位。

(2)EXF2:定时器2 外部标志。当EXEN2=1且T2EX 的负跳变产生捕获或重装时,EXF2置

位。定时器2中断使能时,EXF2=1将使CPU 从中断向量处执行定时器2 中断子程序。EXF2位

必须用软件清零,在递增/递减计数器模式DCEN=1中,EXF2不会引起中断。

9.2.3 中断请求控制

52系列单片机的中断控制主要实现中断的开放或屏蔽及中断优先级的管理功能。中断

控制的设定是通过对几个特殊功能寄存器的编程来实现的。

1、中断允许寄存器IE

专用寄存器IE的字节地址为A8H,通过对IE的编程写入可以控制CPU对中断源的开放或

禁止,以及每一个中断源是否允许中断。其格式如下:

说明如下:

(1)EA: CPU中断总允许位。EA=1,CPU开放中断,这时每个中断源的中断请求被允许或

禁止,取决于各自中断允许位的置1或清O;EA=O,CPU屏蔽所有的中断请求,即关中断。

(2)EC:PCA中断使能位。EC=1,允许PCA中断;EC=0,禁止PCA中断。

(3)ET2:定时器2中断使能位。ET2=1,允许T2中断;ET2=0,禁止T2中断。

(4)ES: 串行口中断允许位。ES=1,允许串行口中断;ES=O,禁止串行口中断。

(5)ETl:定时器T1滋出中断允许位。ET1=1,允许T1中断;ET1=0,禁止T1中断。

(6)EX1:外部中断1 (INT1)中断允许位。EXI=1,允许外部中断1中断;EX1=0,禁止外

部中断1中断。

(7)ETO:定时器TO溢出中断允许位。ETO=1,允许TO中断;ETO=O,禁止T0中断。

(8)EXO:外部中断0(INT0)中断允许位。EXO=1,允许外部中断0中断;EXO=O,禁止外部

中断0中断。

单片机系统复位后,IE中各位均被清0,即处于禁止所有中断的状态。若要开放某一中

D7 D6 D5 D4 D3 D2 D1 D0

TF2 EXF2

D7 D6 D5 D4 D3 D2 D1 D0

EA EC ET2 ES ET1 EX1 ET0 EX0

Page 168: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 168 .

断,可在系统初始化程序中对IE寄存器编程。

例如要以中断方式使用T1,可以用下面的字节操作指令:

IE=0x88;

也可以用下面的位操作指令实现:

EA=1;

ET1=1;

2、中断优先级寄存器IP

52系列单片机中断系统具有四级中断优先级管理。每一个中断源均可通过对中断优先

级寄存器IP的设置,选择高优先级中断或低优先级中断,并可实现中断嵌套。

中断优先级管理遵循的基本原则是:高优先级中断源可中断正在执行的低优先级中断

服务程序,除非在执行低优先级服务程序时,设置了CPU关中断或禁止某些高优先级中断源

的中断;同级或低优先级中断源不能中断正在执行的中断服务程序。

专用寄存器IP的字节地址为B8H,通过对IP的编程,可实现将7个中断源分别设置为高优

先级中断或低优先级中断。其格式如下:

说明如下:

(1) PT2:定时器2 中断优先级控制位。PT2=1,高优先级;PT2=O,低优先级。

(2) PS:串行口中断优先级控制位。PS=1,高优先级;PS=O,低优先级。

(3) PTI:片内定时器T1中断优先级控制位。PT1=1,高优先级;PT1=0,低优先级。

(4) PX1:外部中断INTI中断优先级控制位。PXI=1,高优先级;PX1=0,低优先级。

(5) PTO:片内定时器TO中断优先级控制位。PTO=1,高优先级;PTO=O,低优先级。

(6) PXO:外部中断INTO中断优先级控制位。PXO=1,高优先级;PXO=O,低优先级。

当系统复位时,IP寄存器被清0,将6个中断源均设置为低优先级中断。

如果同一级的几个中断源同时向CPU申请中断,CPU便通过内部硬件查询逻辑按自然优

先级决定响应顺序。各中断源按自然优先级由高到低的排列顺序如表9-1所示。

表9-1 同级中断源硬件优先级顺序

3、中断优先级寄存器IPH

专用寄存器IP的字节地址为B7H,通过对IPH的编程,可实现将6个中断源分别设置为高优

先级中断或低优先级中断。其格式如下:

D7 D6 D5 D4 D3 D2 D1 D0

- - PT2 PS PT1 PX1 PT0 PX0

中 断 源 同级内的优先级

外部中断0(IE0) 1(最高)

定时器T0溢出中断(TF0) 2

外部中断1(IE1) 3

定时器T1溢出中断(TF1) 4

串行口中断(RI/TI) 5

定时器T2溢出中断(TF2/EXF2) 6(最低)

Page 169: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 169 .

说明如下:

(1) PT2H:定时器2中断优先级控制位。PT2H=1,高优先级;PT2H=O,低优先级。

(2) PSH:串行口中断优先级控制位。PSH=1,高优先级;PSH=O,低优先级。

(3) PTIH:片内定时器T1中断优先级控制位。PT1H=1,高优先级;PT1H=0,低优先级。

(4) PX1H:外部中断INTI中断优先级控制位。PXIH=1,高优先级;PX1H=0,低优先级。

(5) PTOH:片内定时器TO中断优先级控制位。PTOH=1,高优先级;PTOH=O,低优先级。

(6) PXOH:外部中断INTO中断优先级控制位。PXOH=1,高优先级;PXOH=O,低优先级。

当系统复位时,IP寄存器被清0,将6个中断源均设置为低优先级中断。IPH寄存器的功

能很简单,IPH和IP组合使用决定每一个中断的优先级,如表9-2所示。

表9-2 中断优先级的确定

9.2.4 中断处理过程

中断处理过程分为三个阶段,即中断响应、中断处理和中断返回。

1、中断响应

中断响应是指在满足CPU的中断响应条件之后,CPU对中断源中断请求的回答。在这个

阶段,CPU要完成中断服务以前的所有准备工作,包括保护断点及把程序转向中断服务程序

的人口地址。

1)中断响应的条件

CPU响应中断的基本条件如下:

有中断源发出中断申请;

中断总允许位EA=1,即CPU开放中断;

请求中断的中断源的中断允许位置1,即该中断源可以向CPU发中断申请。

CPU在每个机器周期的S5P2期间,采样中断源,而在下一个机器周期的S6期间按优先级

顺序查询各中断标志,如查询到某个中断标志为1,将在再下一个机器周期S1期间按优先级

顺序进行中断处理。但在下列任何一种情况存在时,中断响应会被阻止:

CPU正在执行同级或高一级的中断服务程序;

现行机器周期不是正在执行的指令的最后一个机器周期,即现行指令完成前,不

响应任何中断请求;

当前正在执行的是中断返回指令或访问专用寄存器IE或IP的指令。也就是说,在

D7 D6 D5 D4 D3 D2 D1 D0

- - PT2H PSH PT1H PX1H PT0H PX0H

优先级位 中断优先级

IPH.x IP.x

0 0 0 级(最低级)

0 1 1 级

1 0 2 级

1 1 3 级(最高级)

Page 170: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 170 .

执行中断返回指令或是访问IE、IP的指令后,至少需要再执行一条其他指令,才

会响应中

断请求。

中断查询在每个机器周期都要重复执行。如果CPU响应中断的基本条件已满足,但由于

上述三个封锁条件之一而未被及时响应,待封锁中断的条件撤消后,若中断标志也已消失,

则本次被拖延的中断申请就不会再被响应。

2)中断响应过程

如果中断响应的条件满足,且不存在中断封锁的情况,则CPU将响应中断,进人中断响

应周期。

CPU在中断响应周期要完成下列操作:

将相应的优先级状态触发器置1;

由硬件清除相应的中断请求标志;

执行一条由硬件生成的长调用指令。该指令将自动把断点地址(PC值)压人堆栈

保护起来,然后将对应的中断人口地址送人程序计数器PC,使程序转向该中断人

口地址去执行中断服务程序。

各中断源的中断服务程序人口地址见表9-3。

表9-3 中断服务程序人口地址

2、中断服务与返回

中断服务程序从人口地址开始执行,一直到遇到返回指令为止,这个过程称为中断服

务。

在中断服务中首先要做的事情是保护现场,即在执行中断处理程序之前,先将主程序

中已用到并存有数据的累加器、PSW寄存器及其他一些寄存器的内容压人堆栈保护起来,以

免当在中断服务程序中用到上述寄存器时,破坏了它们原来的内容。保护好现场后,再执

行中断服务程序;在返回主程序以前,再恢复现场。

中断服务程序的最后一条是返回指令RETI, RETI指令的执行标志着中断服务程序的结

束,该指令将清除响应中断时被置位的优先级状态触发器,然后自动将断点地址从栈顶弹

出,装人程序计数器PC,使程序返回到被中断的程序断点处,继续向下执行。

在用C语言编程时,中断处理程序中的现场保护是由集成开发环境(IDE)自动完成的,

返回指令也是编译系统自动处理的,用C语言编程时只要注意其书写格式即可。

3、中断请求的撤除

中 断 源 入口地址

外部中断0(IE0) 03H

定时器T0溢出中断(TF0) 0BH

外部中断1(IE1) 13H

定时器T1溢出中断(TF1) 1BH

串行口中断(RI/TI) 23H

定时器T2溢出中断(TF2/EXF2) 2BH

Page 171: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 171 .

CPU响应中断请求后,在中断返回(RETI指令执行 )前,该中断请求信号必须撤除,否

则会引起另外一次中断。

但以上几种中断被响应时,中断请求标志并非都能被清除,这一点应引起注意。采用

边沿触发的外部中断标志IE0或IE1和定时器中断标志TF0或TF1,CPU响应中断后,能用硬件

自动清除,无需采取其它措施。但在电平触发时,IE0或IE1受外部引脚中断信号(INT0或INT1)

直接控制,CPU无法控制IE0或IE1,需要另外考虑撤除中断请求信号的措施,一般通过外加

硬件电路,并配合软件来解决;串行口中断请求标志TI和RI也不能用硬件自动清除,需要

在中断服务程序中,用软件来清除相应的中断请求标志。

4、中断响应时间

中断响应时间是指从中断请求产生到CPU转向中断服务程序人口处所花费的时间。CPU

不是在任何情况下对中断请求都予以响应,且不同的情况对中断响应的时间也是不一样的。

下面以外部中断为例,说明中断响应的时间。

外部中断INTO和INT1的电平在每个机器周期的S5P2期间,经反相后锁存到IE0和IE1标

志位,CPU在下一个机器周期才会查询到新置人的IEO和IE1。如果这时满足中断响应条件,

则CPU响应中断(即执行一条由硬件生成的LCALL指令),转人中断服务程序人口。由于执

行LCALL指令要花费2个机器周期,因此,从外部中断请求有效到开始执行中断服务程序的

第一条指令,中间要隔3个完整的机器周期,这是最短的响应时间。

若中断响应条件得不到满足,则需要更长的中断响应时间。可分为以下3种情况:

(1) 若现行查询周期不是现行指令最后一个机器周期,那么增加的等待时间不会超过3

个周期,因为一条指令的最长时间为4个周期(乘法和除法指令)。

(2)若当前指令为RETI或访问IE或IP指令,执行该类指令占一个机器周期,其后需再

执行一条指令(最多占4个机器周期),才能响应中断请求,则增加的等待时间不会超过5

个周期。

在上述两种情况下,中断响应时间在3-8个机器周期之间。

(3) 若当前正在处理同级或更高级中断,则额外等待时间取决于所执行的中断服务程

序的长短。

9.3 中断的 C51 编程

8051 的中断系统十分重要,C51 使你能够很容易地用C语言来声明中断和编写中断服

务程序。中断过程通过使用interrupt关键字和中断号(0 到31)来实现。中断号告诉编译器

中断程序的入口地址,中断号对应着IE 寄存器中的使能位。也就是说IE寄存器中的0 位对

应着外部中断0,相应的外部中断0的中断号是0。这种关系示于表9-4。

表9-4 中断号与中断源的对应关系

一个中断过程并不一定带上所有参数,可以没有返回值。编译器不须要担心寄存器组

C51的中断号 0 1 2 3 4

对应的中断源 外部中断0 定时器0溢出 外部中断1 定时器1溢出 串行口中断

Page 172: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 172 .

参数的使用和对累加器、状态寄存器、B 寄存器、数据指针和默认的寄存器的保护,只要

它们在中断程序中被用到,编译的时候会把他们入栈,在中断程序结束时将他们恢复。中

断程序的入口地址被编译器放在中断向量中。C51支持所有5 个8051/8052 标准中断(从0到4)和在8051系列中多达27个中断源。

在编制中断服务程序时,可以指定所使用的寄存器组。方法是使用关键字using,后跟

一个0 到3 的数,对应着4 组工作寄存器。当指定工作寄存器组的时候,默认的工作寄存

器组就不会被推入堆栈,这将节省一些处理周期,因为入栈和出栈都需要2个处理周期。

中断服务程序的格式如下:

返回值 函数名(参数)interrupt n using m

其中,n是中断号,标准中断的对应关系见表9-4;m是所使用的寄存器组。

下面列举几个中断系统应用的实例。

1、用定时器实现一个时钟

编制一个程序,实现一个时钟,要求每隔一秒则秒变量second加1,加到60则分变量

minute加1;分变量minute加到60则小时变量hour加1。时间从0点0分0秒起始,24小时制。

程序编制如下,定时器采用中断方式。(在MCU2007-I单片机开发板上调试通过)

#include<reg52.h>

sbit cs_138=P2^6;

sbit cs_373=P1^5;

unsigned char code zima1[11]=0xc0,0xf9,0xa4,0xb0,0x99,0x92, 0x82,0xf8, 0x80,0x90,

0xbf;//0~9及“-”的字形码

unsigned char hour,minute,second; //定义全局变量保存时间

unsigned char i;

void delay(); //延时函数

void xianshi(); //显示函数

void main() //主程序只负责初始化与时间显示

//时间的计算在中断函数中实现

cs_138=1;

cs_373=1;

i=0;

hour=0;

minute=0;

second=0;

TMOD=0x01;

TL0=0xb0;

TH0=0x3c;

TR0=1;

EA=1;

ET0=1;

Page 173: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 173 .

while(1)

xianshi();

void time0() interrupt 1 using 2 //定时器0中断函数

TL0=0xb0; //重新赋初值

TH0=0x3c;

i++;

if(i==20) //1秒钟到

second++;

i=0;

if(second==60)

second=0;

minute++;

if(minute==60)

minute=0;

hour++;

if(hour==24)

hour=0;

void delay()

unsigned char i;

for(i=0;i<80;i++);

void xianshi(void) //LED显示的函数

P0=zima1[hour/10]; //显示小时

P1=0xbf; //在第1、2位数码管显示

Page 174: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 174 .

2. 外部中断的使用

外部中断一般为下降沿的触发方式,而且为最高优先权,在实际应用中时常需要及时

做出反应或异常处理,这时可以考虑用中断方式。

设有如图9-3所示电路,要求编制中断服务程序,实现以下功能:按下按键一次,则LED

指示灯闪烁一次。

I N T 0

P 1 . 0

G N D

V c cV c c

L E D80C51

图9-3 外部中断示例图

程序如下:

delay();

P0=zima1[hour%10];

P1=0xbb;

delay();

P0=zima1[10]; //显示“-”

P1=0xb7; //在第3位数码管显示

delay();

P0=zima1[minute/10]; //显示分

P1=0xb3; //在第4、5位数码管显示

delay();

P0=zima1[minute%10];

P1=0xaf;

delay();

P0=zima1[10]; //显示“-”

P1=0xab; //在第6位数码管显示

delay();

P0=zima1[second/10]; //显示秒

P1=0xa7; //在第7、8位数码管显示

delay();

P0=zima1[second%10];

P1=0xa3;

delay();

Page 175: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 175 .

练习题:编一个闹钟,可以用键盘设置闹钟时间。程序运行就在LED显示器上显示时、

分、秒,当按下S1键,表示可以设置秒,再按下S1键,表示可以设置分,再按下S1键,表

示可以设置小时;按下S2键对应的时间增加;按下S3键对应的时间减少;按下S4键表示确

定。当LED显示的时间与设定的闹钟时间相等时,蜂鸣器发声,此时按S5键可以使蜂鸣器停

止发声。

# include <reg51.h>

unsigned char j;

sbit led=P1^0;

void delay(unsigned char);//延时函数

void main(void)

IE=0; //关所有中断

IT0=1; //设置int0为下降沿触发方式

EX0=1; //开int0中断

EA=1; //开所有中断

Led=1; //熄灭LED

while(1);

void int0func(void) interrupt 0 using 3 //中断处理函数

EX0=0; //关int0中断

led=0; //点亮LED

delay(40); //延时

led=1; //熄灭LED

EX0=1; //开int0中断

void delay(unsigned char a)

unsigned int b;

for(j=0;j<a;j++)

b=500;

while(b--);

Page 176: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 176 .

9.4 外部中断的扩充

尽管8051的外部中断数不应超过两个,但有方法可以使其外部中断数增加。最简单的

方法是把定时/计数器中断做成外部中断。

这种方法就是把定时器设置为计数模式,然后把外部中断信号接到计数器的引脚上(T0

或T1)。为了使每出现一个从高到低的脉冲时都产生一个中断,可以把定时器设置为自动

重装模式,令重装值为0xff。当计数器检测到从高到低的脉冲时,定时器将溢出,这时就

会产生一个中断请求。

用定时器扩展外部中断的程序示例如下:

这种方法还是有一定限制的。首先,它只能是边沿触发,所以当需要的是一个电平触

发的中断时,就要在中断中不断地对T0或T1进行采样,直到它们变高。其次,检测到下降

沿和产生中断之间有一个指令周期的延时,这是因为在检测到下降沿一个指令周期后,计

数器才加1。

当使用定时器作为外部中断时,它以其的功能将不能使用,除非用软件对它进行复用。

#include<reg51.h>

void main()

TMOD=0x66; //两个定时/计数器都设置为8位模式

TH1=0xff; //设定重装值

THO=0xff;

TL0=0xff;

TL1=0xff;

TCON=0x50; //开始计数

IE=0x9f; //中断允许

while(1)

………

void timer0_int(void) interrupt 1

//外部中断处理

void timer1_int(void) interrupt 3

//外部中断处理

Page 177: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 177 .

第 10 章 串行通信接口

串行通信是CPU与外界进行信息交换的一种基本通信方式,本章介绍串行通信的基础知

识和80C51系列单片机串行通信接口的结构、原理及应用。

10.1 串行通信的基础知识

10.1.1 串行通信的基本原理

计算机的基本通信方式共有两种:并行通信和串行通信。

并行通信的特点是:各数据位同时传送,传送速度快、效率高。但并行通信有多少数

据位就需多少根数据线,因此传送成本高。所以并行通信适用于近距离通信的场合,通信

距离通常小于30米,在计算机内部的数据传送都是并行的。

串行通信的特点是:数据传送按位顺序进行,最少只需一根传输线即可完成,成本较

低但速度慢。所以串行通信适用于远距离通信的场合,计算机与外界的数据传送大多数是

串行的,其传送的距离可以从几米到几千公里(使用调制解调器)。

串行通信又分为异步和同步两种方式。在单片机中使用的串行通信都是异步方式,因

此,本章只介绍异步通信。

1、异步通信的字符格式

异步通信以字符为单位,即一个字符一个字符地传送。图10-1就是异步串行通信的字

符格式。

起始位 D0 D1 ... DN 奇偶校验位 停止位

图10-1 异步通信的字符格式

说明如下:

(1) 在这种格式标准中,发送器空闲时,数据线应保持在“1”状态。

(2) 起始位。发送器是远过发送起始位而开始一个字符的传送。起始位使数据线处

于“0”状态。

(3) 数据位。起始位之后就传送数据位。在数据位中,低位在前(左),高位在后

(右)。由于字符编码方式的不同,数据位可以是5,6,7或8位。

(4) 奇偶校验位。用于对字符传送作正确性检查,因此奇偶校验位是可选择的,共

有3种可能:奇校验、偶校验和无校验,可由用户根据需要选定。

(5) 停止位。停止位在最后,用以标志一个字符传送的结束。它对应于“1”状态。

停止位可能是1位、l.5位或2位,在实际应用中根据需要确定。

(6) 帧(frame)。从起始位开始到停止位结束的全邵内容称之为一帧,帧是一个字

符的完整通信格式,因此也就把串行通信的字符格式称之为帧格式。

Page 178: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 178 .

异步通信是一帧接一帧进行的,传送可以是连续的,也可以是断续的。连续的异步通

信是在一个字符格式的停止位之后立即发送下一个字符的起始位,开始一个新的字符传送,

即帧与帧之间是连续的。而断续的异步通信,则是在一帧结束之后并不一定接着传送下一

个字符,不传送时维持数据线的“1”状态,使数据线处于空闲。其后,新的字符传送可在

任何时刻开始。

2、串行通信的数据传送方式

串行通信有以下几种数据传送方式。

(1)单工(Simplex)方式

单工方式的数据传送是单向的。通信双方中一方固定为发送端,另一方则固定为接收

端。单工形式的串行通信,只需要一条数据线,如图10-2所示。

(2)半双工(Half-duplex)形式

半双工形式的数据传送是双向的。但任何时刻只能由其中的一方发送数据,另一方接

收数据。因此半双工形式既可以使用一条数据线,也可以使用两条数据线,如图10-3所示。

(3)全双工(Full-duplex)形式

全双工形式的数据传送是完全双向的,且可以同时发送和接收数据。因此全双工形式

的串行通信需要两条数据线,如图10-4所示。

发送端 接收端

发送端

发送端

发送端

发送端

接收端

接收端接收端

接收端

图10-2 单工方式图 10-3 半双工方式图 10-4 全双工方式图

3、串行通信的传输速率

传输速率用于说明数据传送的快慢。在串行通信中,数据是按位进行传送的,因此传

输速率用每秒钟传送格式位的数目来表示,称之为波特率(baud rate)。每秒传送一个格

式位就是1波特。即:

1波特=lbps(或bit/s,位/秒)

在串行通信中,格式位的发送和接收分别由发送时钟脉冲和接收时钟脉冲进行定时控制。

时钟频率高,则波特率也高,通信速度就快;反之,时钟频率低,则波特率也低,通信速

度就慢。串行通信可以使用的标准波特率在RS-232C标准中已有规定,使用时应根据速度需

要、线路质量以及设备情况等因素选定。

10.1.2 RS­232C 串行总线

串行异步通信使用RS-232C标准,它本是美国电子工业协会的推荐标准,现已在全世界

的范围广泛使用。

Page 179: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 179 .

1、RS­232C信号引脚定义

该总线标准定义了25条信号线,使用25个引脚的连接器,各信号引脚的定义如表10-1

所示。

表10-1 RS-232C信号引脚定义

25个引脚中有很多在基本的异步通信中用不到,所以经常使用的连接器为9针连接器,

而在9针中,必须要用到的信号线只有TXD、RXD和SG。

2、RS­232C主要串行通信信号

RS-232C标准中的许多信号是为通信业务联系或信息控制而定义的,在计算机串行通信

中主要使用如下信号:

(1)数据传送信号

发送数据(TXD)

接收数据(RXD)

(2)调制解调器控制信号

请求发送(RTS)

清除发送(CTS)

数据通信设备准备就绪(DSR)

数据终端设备准备就绪(DTR)

(3)定位信号

接收时钟(RXC)

发送时钟(TXC)

(4)信号地(SG)和保护地(PG)

3、 RS­232C的其它规定

除信号定义外,RS-232C标准的其它规定还有:

引脚 定 义 引脚 定 义

1 保护地(PG) 14 辅助通道发送数据

2 发送数据(TXD) 15 发送时钟(TXC)

3 接收数据(RXD) 16 辅助通道接收数据

4 请求发送(RTS) 17 接收时钟(RXC)

5 清除发送(CTS) 18 未定义

6 数据通信设备准备就绪(DSR) 19 辅助通道请求发送

7 信号地(SG) 20 数据终端准备就绪(DTR)

8 接收线路信号检测(DCD) 21 信号质量检测

9 接收线路建立检测 22 音响指示

10 线路建立检测 23 数据信号速率选择

11 未定义 24 发送时钟

12 辅助通道接收线信号检测` 25 未定义

13 辅助通道清除发送

Page 180: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 180 .

RS-232C是一种电压型总线标准,以不同极性的电压表示逻辑值:

-3V~-25V表示逻辑“1”

+3V~+25V表示逻辑“0”

标准数据传送速率有50,75,110,150,300,600,1200,2400,4800,9600和19200

波特等。

10.1.3 串并转换和串行接口

CPU通常是并行的输人/输出数据,但和某些外部设备或其他计算机交换信息时可采用

串行通信方式这就要求把从CPU来的并行数据转换为串行数据送给I/O设备,或者把I/O设备

送来的串行数据转换为并行数据送给CPU。为了实现这样的串并转换,应使用专门的串行接

口电路再加以适当的软件配合来完成。

现在市场上有各种多样的串行接口芯片,并且大多是可编程的多功能芯片,统称为通

用异步接收/发送器—UART。

UART的基本组成是接收器、发送器和控制器,它的主要功能是以下几点:

(1) 把并行数据转换为串行数据或者把串行数据转换为并行数据,这主要由发送器或

接收器来完成

(2) 完成格式信息的插入和滤除及错误检验。格式信息是指异步通信中的起始位、奇

偶位和停止位等,这部分工作由控制器完成。

10.2 51系列单片机的串行接口

51系列单片机的串口为全双工结构,表示可以同时发送和接收。

10.2.1 标准UART 操作基础

标准UART操作具有接收缓冲,在第一个字节从寄存器读出之前,可以开始接收第二个

字节。但是如果第二个字节接收完毕时第一个字节仍未读出,其中一个字节将会丢失。

1、串行口的控制

串口的发送和接收都是通过特殊功能寄存器SBUF进行访问的,写入SBUF的数据装入发

送寄存器,对SBUF的读操作是对物理上分开的接收寄存器进行访问。另外,电源控制寄存

器PCON中的一些为也与串行口的控制有关。

1)串行端口控制寄存器(SCON)

串行端口控制寄存器SCON用来设定串行口的工作模式、接收/发送控制及设置状态标

志,字节地址为98H,可位寻址,其格式为:

D7 D6 D5 D4 D3 D2 D1 D0

SM0/FE SM1 SM2 REN TB8 RB8 TI RI

Page 181: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 181 .

说明如下:

FE:帧错误位。当检测到一个无效停止位时,通过UART接收器设置该位,但它必须由

软件清零。要使该位有效,PCON 寄存器中的SMOD0位必须置1。FE与SM0的选择见电源控制

寄存器PCON的介绍。

SM0 和SM1:定义串口操作模式,要使该位有效,PCON 寄存器中的SMOD0必须置0。

SM1和SM0定义串行口操作模式见表10-2。

表10-2 串行口工作模式

SM2:在模式2和3中多处理机通信使能位。在模式2或3中,若SM2=1,且接收到的第9位

数据(RB8)是0,则接收中断标志RI不会被激活。在模式1中,若SM2=1,且没有接收到有

效的停止位,则RI不会被激活,在模式0中,SM2必须是0。

REN:允许接收位。由软件置位或清除。REN=1时,允许接收;REN=0时,禁止接收。

TB8:模式2和3中发送的第9位数据,可以按需要由软件置位或清除。

RB8:模式2和3中已接收的第9位数据,在模式1中,或sm2=0,RB8是已接收的停止位。

在模式0中,RB8未用。

TI:发送中断标志。模式0中,在发送完第8位数据时,由硬件置位。其它模式中,在

发送停止位之初,由硬件置位。在任何模式中都必须由软件来清除TI。

RI:接收中断标志。模式0中,接收第8位结束时,由硬件置位。其它模式中,在接收

停止位的中间时刻,由硬件置位。在任何模式(SM2所述情况除外)必须由软件清除RI。

2)电源控制寄存器PCON

PCON是为了实现电源控制而设置的,字节地址为87H,不可位寻址, PCON的各位定义

如下:

其中,PCON的低4位是掉电方式控制位,POF是上电标志位,在第二章已介绍。

SMOD1是波特率倍增位。在模式1、模式2、模式3时,若SMOD1=1,则波特率提高一倍;

若SMOD=0,则波特率不加倍。

SMOD0是SCON.7选择位。如果SMOD0=1,则SCON.7作为FE;若SMOD0=0,SCON.7作为SM0。

作为FE时,SCON.7只能由软件清零。

2、波特率设计

在串行通信中,收发双方对发送或接收的数据速率要有一定的约定,通过软件对串行

口编程可设置4种工作模式。

1)模式0和模式2的波特率

操作模式0的波特率是固定的,为fosc/12(12 时钟模式)或fosc/6(6 时钟模式)。

SM0 SM1 UART 模式 波特率

0 0 0:同步移位寄存器 fosc/12 12 时钟模式或fosc/6 6 时钟模式

0 1 1:8 位UART 可变(由定时器控制)

1 0 2:9 位UART fosc/64或fosc/32(12时钟);fosc/32或fosc /16(6时钟)

1 1 3:9 位UART 可变(由定时器控制)

D7 D6 D5 D4 D3 D2 D1 D0

SMOD1 SMOD0 - POF GF1 GF0 PD IDL

Page 182: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 182 .

模式2的波特率取决于PCON寄存器中的SMOD1位的值。在12时钟模式下,若SMOD1=0(复

位后的值),波特率为振荡器频率1/64,若SMOD1=1,波特率为振荡器频率1/32,在6 时钟

模式下则分别为振荡器频率1/32 或振荡器频率的1/16。

此处:n=64(12时钟模式)或32(6时钟模式)。

2)模式1和模式3的波特率

在80C51中,模式1和模式3使用定时器1作波特率发生器,所以这两种模式下的波特率

由定时器1的溢出速率决定。

当定时器1用作波特率发生器,模式1和3中波特率由定时器1的溢出速率和SMOD1的值决

定。

此处:n =32(12 时钟模式)或16(6 时钟模式)。

在此应用中定时器1不能用作中断,定时器1可以工作在定时或计数方式和3种工作模式

中任何一个。在最典型应用中,它用作定时器方式工作自动重装载模式(TMOD的高半字节

为0010B),它的波特率值由下式给出:

此处n =32(12时钟模式)或16(6时钟模式)。

可以用定时器1的中断实现非常低的波特率。将定时器配置为16 位定时器(TMOD 的高

半字节为0001B),并使用中断进行16位软件重装。表10-3列出了几个常用的波特率以及如

何从定时器1获得。

表10-3 由定时器1 产生的通用波特率

波特率(bps) Fosc(MHz) SMOD 定时器1

模式 12时钟模式 6时钟模式 C/T 模式 重装载值

模式 0 Max 1.67 M 3.34 M 20 X X X X

模式2 Max 625 k 1250 k 20 1 X X X

模式1, 3

Max

104.2 k 208.4 k 20 1 0 2 FFH

模式 1, 3

19.2 k 38.4 k 11.059 1 0 2 FDH

9.6 k 19.2 k 11.059 0 0 2 FDH

4.8 k 9.6 k 11.059 0 0 2 FAH

2.4 k 4.8 k 11.059 0 0 2 F4H

1.2 k 2.4 k 11.059 0 0 2 E8H

137.5 275 11.986 0 0 2 1DH

110 220 6 0 0 2 72H

110 220 12 0 0 1 FEEBH

Page 183: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 183 .

10.2.2 标准UART 工作模式

STC89C52系列单片机的串行通信接口有4 种操作模式,模式0---模式3,由SCON中的

SM0、SM1两位决定。

1、模式0

模式0为同步移位寄存器模式。每次发送或接收以LSB(最低位)作首位,波特率固定

为单片机时钟频率的1/12(12时钟模式)或1/6(6时钟模式)。串行数据由RxD端出入,TxD输

出同步移位时钟,发送或接收的是8位数据,低位在先。串口模式0的功能简图见图10-5。

执行任何一条把SBUF作为目的寄存器的指令时,就开始发送。在机器周期的S6P2时刻

的“写SBUF”信号将1装入发送移位寄存器的第9位,并通知发送控制部分开始发送。写SBUF

信号有效后一个完整的机器周期后SEND端有效。

SEND使能RxD(P3.0)端送出数据,TxD(P3.1)输出移位时钟。每个机器周期的S3、

S4 及S5状态内移位时钟为低电平,而S6、S1及S2 状态内为高。在SEND 有效时,每一机器

周期的S6P2 时刻发送移位寄存器的内容右移一位。

≥1

SBUF

≥1

&

80C51内部总线

写入SBUFD S Q

CLSBUF

过零检测

RXDP3.0

TXDP3.1

启动

TX时钟

TX控制移位

发送TI

RI 接收

移位RX控制

RX时钟

启动

移位

RXDP3.0

输入功能

输出功能

输出功能

输入移位寄存器

装载SBUF

读SBUF

LSB MSB

80C51内部总线

移位时钟

MSBLSB

RENRI

&

&

串口

中断

S5

1 1 1 1 1 1 1 0

TB8

图10-5 串口模式0的功能简图

Page 184: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 184 .

数据位向右移时,左边添加零。当数据字节最高位(MSB)移到移位寄存器的输出端时,

其左边是装入“1”的第9 位,再左的内容均为0, 此时通知Tx控制模块进行最后一位移位

处理后禁止SEND并置位T1, 所有这些步骤均在“写入SBUF”后第10个机器周期的S1P1 时进

行的。接收初始化条件是REN=1及R1=0,下一机器周期的S6P2时,RX控制单元向接收移位寄

存器写入1111 1110并在下一个时钟使RECEIVE端有效。

RECEIVE使能移位时钟转换P3.1功能,移位时钟在每个机器周期的S3P1及S6P1跳变,在

RECEIVE有效时每一机器周期的S6P2时刻,接收移位寄存器内容向左移一位。从右移位进来

的值是该机器周期S5P2时从P3.0脚上采样得来的。

数据从右边移入时,左边移出为“1”。当初始时置入最右端的“0”移至最左端时,

通知RX控制时钟作最后一次移位后装入SBUF。在写入SCON清除R1后第10个机器周期,

RECEIVE端被清除且置位RI。

2、模式1

串行口工作于模式1时为8位UART模式,每帧传输的是10位数据:1位起始位(0),8位

数据(低位在先)及一位停止位(1)。由RxD接收,TxD发送。接收时,停止位存入SCON内

的RB8 ,80C51波特率取决于定时器1的溢出速率。图10-6所示为串行口模式1的功能简图。

≥1

SBUF

80C51内部总线

写入SBUF

D S Q

CLSBUF

过零检测

启动

TX时钟

TX控制移位

发送TI

RI

移位RX控制

RX时钟

启动

移位

输入移位寄存器(9位 )

装载SBUF

读SBUF

80C51内部总线

&

串口中断

TB8

1FFH

位检测

RXD

1到0跳变检测

采样

÷16

÷2

定时器1溢出

数据

≥1

装载

SBUF

TXD

÷16

SMOD=1SMOD=0

图10-6 串口模式1的功能简图

Page 185: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 185 .

发送过程是由执行一条以SBUF为目的寄存器的指令启动的。“写SBUF”信号还把1(TB8)

装入发送移位寄存器的第9位,同时通知发送控制器进行发送。实际上发送过程开始于16分

频计数器下次翻转后的那个机器周期的S1P1时刻。每位的发送时序与16分频计数器同步,

而并不与“写SBUF”信号同步。

发送以激活SEND端开始,向TxD发送一起始位,一位(时间)以后DATA端有效,使输出

移位寄存器中数据得以送至TxD。再过一位,产生第一个移位脉冲。

数据向右移出,左边不断填以0。当数据字节的最高位移到移位寄存器的输出位置时,

其左边是装入“1”的第9位,再左的内容均为0。此时通知TX控制器作最后一次移位,然后

禁止SEND 端并置位TI。这都发生于“写SBUF”后16分频计时器的第10次翻转时。

接收在RxD端检测到负跳变时启动,为此单片机对RxD不断采样,采样速率为波特率的

16倍。当检测到负跳变时,16分频计数器立即复位,同时将1FFH 写入输入移位寄存器。复

位16分频计时器确保计时器翻转时位与输入数据位时间同步。

计数器的16个状态将每个位时间分为16份。在第7、8、9状态时,位检测器对RxD端的

值采样,取值为三个采样值中取多数(至少2 个)作为读入值,这样可以抑制噪声。如果

所接收的第一位不为0,说明它不是一帧数据的起始位,该位被摒弃,接收电路被复位,等

待另一个负跳变的到来。这用来防止错误的起始位。如果起始位有效,则被移入输入移位

寄存器,并开始接收这一帧中的其它位。

当数据位逐一由右边移入时,“1” 从左边被移出。当起始位0 移到最左边时(模式1

为9位寄存器),通知接收控制器进行最后一次移位,将移位寄存器内容(9)位分别装入

SBUF及RB8,并置RI=1。仅当最后一位移位脉冲产生时同时满足下述2 个条件:RI=0、SM2=0

或接收到的停止位=1,才会装载SBUF和RB8,并且置位RI。

上述两个条件任一不满足,所接收到的数据帧就会丢失,不再恢复。两者都满足时,

停止位就进入RB8,8位数据进入SBUF,RI=1。这时,无论上述条件满足与否,接收控制单

元都会重新等待RxD 的负跳变。

3、模式2

串行口工作于模式1时为9位UART模式。图10-7所示为串行口模式2的功能简图。

模式2时,TxD脚发送,RxD脚接收。每次数据为11 位,一个起始位(0),8 个数据位

(LSB 在前),一个可编程第9位数据及一个停止位(1)。

发送时,第9 个数据位(SCON 内TB8 位)可置为0或1。例如将奇偶位(PSW内P位)移

至TB8。接收时,第9位数据存入SCON的RB8 位,停止位忽略,波特率可编程为MCU时钟频率

的1/32或1/64(12时钟模式)(6 时钟模式下为1/16 或1/32)。

发送过程是由执行一条以SBUF为目的寄存器的指令启动的。“写SBUF”同时将TB8装入

发送移位寄存器的第9位位置上,并通知发送控制器进行一次发送。发送过程由16分频计数

器下一次翻转后机器周期的S1P1时刻开始。

发送过程由使能SEND有效开始,将一个起始位送到TxD端。一位时间,后DATA有效,数

据由移位寄存器送入TxD端,再过一位后产生第一个移位脉冲。第一个移位时钟将“1”(停

止位)送入移位寄存器的第9位,此后每次移位只把0送入第9位,所以当数据位向右移出时,

“0”从左边移入。当TB8移至输出位置上时,它左边就是停止位,其余位均为零,此时将

通知发送控制器作最后一次移位,然后使SEND无效并置位TI。这些均发生在“写SBUF”后

Page 186: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 186 .

第11 次计数器翻转时,MCU以16倍波特率对RxD 脚进行采样,一旦检测到负跳变,16分频

计数器立即复位同时将1FFH 写入输出移位寄存器。

≥1

SBUF

80C51内部总线

写入SBUF

D S QCL

SBUF

过零检测

启动

TX时钟TX控制

移位

发送TI

RI

移位RX控制

RX时钟

启动

移位

输入移位寄存器(9位)

装载SBUF

读SBUF

80C51内部总线

&

串口中断

TB8

1FFH

位检测

RXD

1到0跳变检测

采样

÷16

÷2

数据

≥1

装载SBUF

TXD

÷16

SMOD=1SMOD=0

停止位

二分频(fosc/2)

图10-7 串口模式2的功能简图

在每一位的第7、8、9状态时,位检测器对RxD端值进行采样。对三个采样值取多数(至

少2 次)为确定值以抑制噪声。如若所接收的第一位不为0,接收电路复位,单元等待下一

个负跳变的出现。如果起始位有效。则被移入输入移位寄存器。并开始接收这一帧中的其

它位。

数据位从右边移入,“1”从左边移出。当起始位移至寄存器(模式2~3 时为9位寄存

器)的最左端时,通知接收控制器进行最后一次移位,并装入SBUF及RB8并置位RI。仅当产

生最后一位移位脉冲时同时满足下列2个条件:RI=0、SM2=0或接收到的第9位数据为1时,

才装载SBUF和RB8并置位。

上述两个条件任一不满足,所接收到的数据帧就会丢失不再恢复,RI仍为0。当两者都

满足时,第9位数据位就装入RB8,前8位数据则装入SBUF,一个位时间后,无论上述条件满

足与否,单元都会重新等待RxD端的负跳变。

4、模式3

TxD脚发送,RxD脚接收。每次数据为11 位,一个起始位(0),8 个数据位(LSB 为

Page 187: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 187 .

首位)一个可编程的第9位数据及一个停止位(1)。事实上模式3除了波特率外均与模式2

相同,其波特率可变并由定时器1溢出率决定。

在上述4 种模式中,发送过程是以任意一条以写SBUF 作为目标寄存器的指令开始的,

模式0 时接收通过设置R1=0及REN=1初始化,其它模式下如若REN=1则通过起始位初始化。

10.3 串口通信程序编制

单片机的串口使用非常方便,使用串行通信的一般步骤为:

1、选择串口工作模式,一般选模式1,通信波特率由定时器1 溢出率控制;

2、若选择模式1,则应设定定时器 1的工作方式为8 位自动重装方式;

3、计算所需波特率对应的定时器 1 的初值,并装入初值,表 10-3列出了常用晶体频

率下不同波特率所对应的定时器初值;

4、启动定时器;

5、若选择中断传送方式还需开中断。

以上是串口编程的一般步骤,在实际使用时要做好通信双方的约定,即协议,这样通

信双方才能正常通信。下面介绍单片机与单片机双机通信及单片机与计算机(PC机)通信

的硬件连接与程序编制方法。

10.3.1 单片机与 PC 机通信

1、RS­232总线接口芯片MAX232

由于计算机的串行口输出是 RS232信号,逻辑“1”用­3V~­25V信号表示,逻辑“0”用+3V~+25V信号表示,不是 TTL信号,与单片机信号不同,因此二者进行通信必须进行

电平转换,一般选用MAX232 芯片来实现这种转换。

MAXim 公司的 MAX232/MAX232A 接收/发送器为满足 EIA/TEA2232E的标准而设计

的。具有功耗低、工作电源为单电源,外接电容仅为 0.1uF或 1uF、采用双列直插或帖片封

装形式、接收器输出为三态 TTL/CMOS等特点。

1)主要性能

MAX232 的主要性能如下。

工作是采用 5V 单电源供电。

先进的 CMOS 制造工艺制造。

两路接收器和发送器。

+/­30V输入电平。

低的工作电流,典型值为 8mA。 符合 TIA/EIA­232­F标准和 ITUV2.8建议。

2)管脚图及管脚说明

(1)管脚图,如图 10­8所示

Page 188: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 188 .

1234567

C1+V+

C2­C2+C1­

V­T2OUT

8 91011121314

T1INR1OUTR1INT1OTGNDVcc

MAX232

1516

R2IN R2OUTT2IN

图 10­8 MAX232芯片引脚图

(2)管脚定义

MAX232 的管脚分布如表 10­4所示。

表 10­4 MAX232管脚功能

3)MAX232应用电路

如图 10­9所示,TXD 与 RXD 分别连单片机的 RXD 与 TXD,右下角的DB9 插头连接

计算机的串行口。我公司 MCU2006 及 MCU2007­I 型单片机开发板均采用此连接方式,除

作串口通信实验外,本电路也是 ISP程序下载电路。

4)MAX232的应用介绍

MAX232 可以作为单片机和单片机之间、单片机和 PC机串口之间的接口电路中。只要

将待进行串行传输的设备的发送和接收端接上,即可编程。如要实现单片机和 PC机之间的

数据传输,则应在 PC机的程序中加入设立数据缓冲区的程序,数据缓冲区的大小和待传输

的数据帧有关,应该根据实际数据帧的大小通过实验方法来选择以防止数据的丢失。

注:本节以下几个程序在MCU2007­I型单片机开发板上均可调试通过。

管脚序号 名 称 功 能

1 C1+ 电容倍增充电泵电容正端

3 C1­ 电容倍增充电泵电容负端

4 C2+ 反向充电泵电容正端

5 C2­ 反向充电泵电容负端

2 V+ 充电产生的+5.5V

6 V­ 充电产生的­5.5V

13 R1IN RS­232发送器输入 1

8 R2IN RS­232发送器输入 2

14 T1OUT RS­232发送器输出 1

7 T2OUT RS­232发送器输出 2

11 T11IN TTL 发送器输入 1

10 T21IN TTL 发送器输入 2

12 R1OUT TTL 接收器输出 1

9 R2OUT TTL 接收器输出 2

16 VCC 电 源

15 GND 地

Page 189: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 189 .

图 10­9 单片机与 PC 机串行通信连接图

2、计算机发送数据、单片机接收数据程序

在进行本实验前请详细阅读本章前面所叙述的串口通信知识,熟练掌握波特率已及定

时器初值的计算方法,注意定时器初值求解时要根据所使用的晶体频率来计算,本程序中

所使用的晶体频率为11.0592MHz,还要注意串口的工作方式。一定要注意通信双方的波特

率要严格一致。

要进行串口通信必须将通信双方用传输介质连好,可以选用成品的串口线、通信电缆

或无线数传模块等。本开发板与计算机的通信只需用串口线把开发板和计算机连接起来即

可。实际通信属于RS232 通信。

下面介绍单片机通信程序的编制方法,这里以单片机与计算机通信为例进行讲述。实

际通信时,计算机方应使用VB、VC等软件编制收发程序,以便与单片机协调通信过程。本

实验并不具体讲述计算机通信程序的编制方法,请读者自己查阅其他资料,本实验中计算

机的通信程序采用STC—ISP 软件自带的串口调试助手来实现。

STC—ISP下载软件支持串口调试功能,在进行串口通信时显得非常方便,可以在串口

调试助手中接收并显示单片机传过来的数据,也可以在串口调试助手中发送数据让单片机

接收。

首先约定协议如下:通信口选择COM1,波特率选9600,无校验位,数据位8位,停止位

1位。

根据以上协议,假设计算机每次发送一个字节的数据,由单片机接收并用LED显示器现

实出来。单片机侧程序如下:

1) 扫描方式

#include<reg52.h>

Page 190: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 190 .

sbit a_138=P1^2;

sbit b_138=P1^3;

sbit c_138=P1^4;

sbit cs_138=P2^6;

sbit cs_373=P1^5;

unsigned int i;

unsigned code zixing[]=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90;

//LED显示器字形数据

unsigned char getchar(void) //串口接收函数

while(!RI); //等待RI变高,变高则表示有数据接收到

RI=0; //RI清零,为下一次接收数据做准备

return(SBUF); //返回接收到的数据

void main()

unsigned char dat=0; //用来保存接收到的数据

unsigned char a,b,c; //定义中间变量,保存数据的各个位

SCON=0x50; //设定为方式1,无校验位,数据位8位,停止位1位

TMOD|=0x20; //定时器1工作于8位自动重装方式

TH1=0xfd; //定时器1赋初值,设定波特率为9600

TL1=0xfd;

TR1=1; //启动定时器

RI=0; //清中断标志;以上为串口通信初始化

cs_138=1; //使138选通,为显示做准备

cs_373=1; //使373选通,为显示做准备

while(1)

if(RI)

dat=getchar();

a=dat/100;

b=dat/10%10;

c=dat%10; //取出dat的各个位

P1=0xbf; //使第一个数码管选通

P0=zixing[a]; //显示数据a

Page 191: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 191 .

将以上程序下载到单片机中,上电运行。然后打开STC—ISP下载软件,选择串口调试

助手标签,如图10-10所示。

图10-10 串口调试助手

for(i=0;i<200;i++); //延时

P1=0xbb; //使第二个数码管选通

P0=zixing[b]; //显示数据b

for(i=0;i<200;i++);

P1=0xb7; //使第三个数码管选通

P0=zixing[c]; //显示数据c

for(i=0;i<200;i++);

Page 192: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 192 .

在这个界面中,首先要进行串口通信的设置,在COM口中选择COM1,波特率选择9600,

校验位为None,数据位为8,停止位为1(以上设置一定要与单片机设定的串口速度一致),

还要选择“十六进制发送”。 如图10-10所示。

设置好以后,单击“打开串口”按钮,然后在“单字串发送区”输入要发送的内容,

如80,然后点击“发送字符/数据”,单片机侧就可以接收到数据了。注意,单片机侧会显

示128,因为我们在计算机中发送的数据是16进制的,16进制的80刚好等于十进制的128。

2)中断方式

#include<reg52.h>

sbit a_138=P1^2;

sbit b_138=P1^3;

sbit c_138=P1^4;

sbit cs_138=P2^6;

sbit cs_373=P1^5;

unsigned int i;

unsigned char dat=0; //用来保存接收到的数据,由于在中断处理函数中要用到该变量,

//所以声明为全局变量

unsigned code zixing[]=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90;

//LED显示器字形数据

void main()

unsigned char a,b,c;//定义中间变量,保存数据的各个位

SCON=0x50; //设定为方式1,无校验位,数据位8位,停止位1位。

TMOD|=0x20; //定时器1工作于8位自动重装方式

TH1=0xfd; //定时器1赋初值,设定波特率为9600

TL1=0xfd;

TR1=1; //启动定时器

RI=0; //清中断标志;以上为串口通信初始化过程

EA=1; //总中断允许

ES=1; //开放串口中断

cs_138=1; //使138选通,为显示做准备

cs_373=1; //使373选通,为显示做准备

while(1) //此循环只做显示,当有数据接收到中断处理函数就会自动执行

a=dat/100;

b=dat/10%10;

c=dat%10; //取出dat的各个位

P1=0xbf; //使第一个数码管选通

P0=zixing[a];//显示数据a

for(i=0;i<200;i++);//延时

Page 193: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 193 .

以上中断方式程序与扫描方式效果相同,一般中断方式应用在9600波特以上的通信中,

扫描方式应用在9600波特以下的传输中。计算机侧的操作与扫描方式相同。

3、计算机接收数据、单片机发送数据时的单片机程序

P1=0xbb; //使第二个数码管选通

P0=zixing[b];//显示数据b

for(i=0;i<200;i++);

P1=0xb7; //使第三个数码管选通

P0=zixing[c];//显示数据c

for(i=0;i<200;i++);

void get(void) interrupt 4 using 0 //串口处理中断函数

if(RI)

RI=0;

dat=SBUF;

#include<reg52.h>

sbit a_138=P1^2;

sbit b_138=P1^3;

sbit c_138=P1^4;

sbit cs_138=P2^6;

sbit cs_373=P1^5;

unsigned int i,j;

unsigned char dat; //用来保存发送的数据

unsigned code zixing[]=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90;

void putchar(unsigned char ch)

SBUF=ch; //将数据送到发送缓冲区,单片机就会自动发送数据

TI=0;

Page 194: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 194 .

将以上程序下载到单片机中,上电运行。此时计算机处于接收状态。然后打开STC—ISP

void main()

unsigned char a,b,c; //定义中间变量,保存数据的各个位

SCON=0x50; //设定串口工作方式为方式1,无校验位,数据位8位,停止位1位。

TMOD|=0x20; //定时器1工作于8位自动重装方式

TH1=0xfd; //定时器1赋初值,设定波特率为9600

TL1=0xfd;

TR1=1; //启动定时器

RI=0; //清中断标志

cs_138=1; //使138选通,为显示做准备

cs_373=1; //使373选通,为显示做准备

while(1)

for(dat=1;dat<255;dat++)//循环发送递增的数据

putchar(dat); //发送dat

/*******以下显示发送的数据,以便与计算机接收到的数据比较********/

for(j=0;j<100;j++)

a=dat/100;

b=dat/10%10;

c=dat%10; //取出dat的各个位

P1=0xbf; //使第一个数码管选通

P0=zixing[a]; //显示数据a

for(i=0;i<200;i++);//延时

P1=0xbb; //使第二个数码管选通

P0=zixing[b]; //显示数据b

for(i=0;i<200;i++);

P1=0xb7; //使第三个数码管选通

P0=zixing[c]; //显示数据c

for(i=0;i<200;i++);

Page 195: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 195 .

下载软件,选择串口调试助手标签,如图10-11所示,窗口最下方的设置与发送时相同。

图10-11 串口调试助手接收

如图10-11所示,选择“十六进制显示”, 再点击“打开串口”, 当单片机发送数

据后,“接收区/键盘发送区”就会显示出单片机发送过来的数据了,注意接收到的数据为

16进制格式。若接收到的数据过多,可以单击“清接收区”按钮以清除接收到的数据。

10.3.2 单片机双机通信

在很多场合,需要用到单片机与单片机之间进行通信,如果通信的两个单片机之间距

离很近,则可以直接将两个单片机的P0口相连,用并行方式进行通信;也可以将两个单片

机的串口直接相连,用串行方式进行通信,这时一个单片机的发送线(TXD)应与另一个单

片机的接收线(RXD)相连。

如果两个需要相互通信的计算机之间距离较远,可以采用 RS232 总线方式进行通信,

该方式的传输距离一般在12米以内,通信双方都需采用MAX232芯片将TTL信号转换为RS232

信号进行传送,此时的连接电路如图10-12 所示。

如果两个需要相互通信的计算机之间距离很远,可以采用 RS485 总线方式进行通信,

该方式的传输距离一般在 1500米以内,通信双方都需采用 MAX485 芯片将 TTL信号变为差

模信号进行传送,具体电路连接这里不再给出,读者可查阅MAX485 芯片资料。

Page 196: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 196 .

7T2OUT

89

10MAX232

R2INR2OUT

T2IN

10 RXD

11 TXD

7T2OUT

8 9

10

MAX232R2IN R2OUT

T2IN

MCU10

11

MCURXD

TXD

图 10-12 单片机之间采用 RS232 方式通信连接图

如果需要采用无线通信方式,可以在单片机串口上连接无线数传模块来实现。只要通

信双方的单片机上都连有无线数传模块,模块的通信方式设定一致(波特率、无线信号频率

等),且单片机通信程序中的波特率与模块的相同,则基于无线方式的串口通信也非常简单。

不管串口通信采用何种方式,单片机内的程序编制方法都是一样的。下面介绍单片机

双机通信的程序编制方法。

以一个单片机A 发送、另一个单片机B接收为例,首先约定通信协议如下:

单片机A 发送时的联络信号为“0xaa”,单片机B的应答信号为“0xbb”;

单片机A 每次发送10个字节的数据,最后发送校验和;

如果单片机 B接收完数据后发现数据出错,就向单片机 A 发送“0xff”,单片机 A 接

收到“0xff”后重传数据;若数据正确单片机B 就发送“0x00”。

程序示例如下(可仿照上一小节示例将显示函数包含进来,双方在发送和接收的同时

可将数据进行显示,以便比较通信是否正确):

1、单片机A 发送程序

#include<reg52.h>

# define uchar unsigned char ;

uchar dat[10]; //要发送的10个数据

uchar sum; //校验和

void init(void) //串口初始化函数

SCON=0x50; //设工作方式为方式1,无校验,数据8位,停止位1位

TMOD|=0x20; //定时器1工作于8位自动重装方式

TH1=0xfd; //定时器1赋初值,设定波特率为9600

TL1=0xfd;

TR1=1; //启动定时器

void send(void) //发送函数

uchar k;

do //此循环与单片机B握手(联络)

SBUF=0xaa; //发送联络信号0xaa

Page 197: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 197 .

2、单片机 B接收程序

while(TI==0); //等待发送结束

TI=0;

while(RI==0); //等待单片机B回答

RI=0;

while(SBU!=0xbb); //单片机B未准备好,继续联络

do //此循环发送数据与校验和

sum=0;

for(k=0;k<10;k++) //for循环发送10个数据并计算校验和

SBUF=dat[k];

sum^=dat[k]; //计算校验和

while(TI==0);

TI=0;

SBUF=sum; //发送校验和

while(TI==0);

TI=0;

while(RI==0); //等待单片机B应答

RI=0;

while(SBUF!=0); //出错则重发

void main()

init(); //串口初始化

send(); //发送

#include<reg52.h>

# define uchar unsigned char ;

uchar dat[10]; //保存接收到的10个数据

uchar sum; //校验和

void init(void) //串口初始化函数

Page 198: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 198 .

SCON=0x50; //设工作方式为方式1,无校验,数据8位,停止位1位

TMOD|=0x20; //定时器1工作于8位自动重装方式

TH1=0xfd; //定时器1赋初值,设定波特率为9600

TL1=0xfd;

TR1=1; //启动定时器

void receive(void) //接收函数

uchar k;

while(RI==0); //等待单片机A 联络

RI=0;

while(SBUF!=0xaa); //判断单片机A是否发出请求

SBUF=0xbb; //发送应答信号0xbb

while(TI==0); //等待发送结束

TI=0;

while(1)

sum=0; //清校验和

for(k=0;k<10;k++) //循环接收10个数据

while(RI==0);

RI=0;

dat[k]=SBUF; //接收一个数据

sum^=dat[k]; //求校验和

while(RI==0); //等待接收单片机A发送的校验和

RI=0;

if(SBUF==sum) //判断校验和是否相同,相同则发送0x00

SBUF=0x00;

break; //接收数据正确则跳出上面的while(1)

else //校验和不同则发送0xff,重新接收

SBUF=0xff;

while(TI==0);

TI=0;

Page 199: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 199 .

void main()

init(); //串口初始化

receive(); //接收

Page 200: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 200 .

第 11 章 A/D 转换器与 D/A 转换器应用

11.1 A/D转换器接口

在单片机应用系统中经常需要检测被测对象的一些物理参数, 如温度、压力、流量、

位移、速度等,这些参数都是随时间连续变化的模拟量。虽然这些模拟量可以通过传感器、

变送器变换成标准的电压或电流信号,但还需要通过模拟量/数字量(A/D)转换器,将其

转换成计算机能处理的相应的数字信号。本节讨论A/D 转换器及其与单片机接口技术,同

时给出相应的转换程序的编制方法。

11.1.1 A/D 转换器概述

A/D转换器就是模/数转换器(ADC),它是一种将模拟量转换成数字量的设备,简称A/D。

1、A/D转换器的技术指标

(1)分辨率

A/D转换器的分辨率是指被转换后的数字量变化一个单位所需输入模拟电压的变化量。

分辨率用位数表示。例如8 位 A/D转换器的分辨率就是 8位,也就是说该 A/D转换器可以

把输入的满量程模拟量分成28=256份,每一份所对应的模拟量的大小就是该 A/D转换器的

分辨率,其分辨率为1LSB。例如一个满输入量程为5V的 8位 A/D 转换器,它能分辨输入电

压变化的最小值是5ⅹ1/28=19.5mV。

(2)量化误差

A/D转换器把模拟量变为数字量,用数字量近似表示模拟量,这个过程称为量化。量化

误差是由于A/D 转换器的有限位数引起的。实际上,要更准确表示模拟量,A/D转换器的位

数是越大越好。量化误差理论上是一个单位分辨率,即±1/2LSB。分辨率高的A/D 转换器

具有较小的量化误差。

(3)偏移误差

偏移误差是指输入信号为0 时,输出信号不为0,所以有时又称为零值误差。这种误差

可以通过软件来消除。

(5)绝对精度

在一个A/D 转换器中,任何输出的数据所对应的实际模拟量输入与理论模拟量输入之

差的最大值,称为绝对精度,它包括了所有的误差。

(6)转换时间与转换速率

A/D转换器完成一次 A/D转换所需要的时间称为转换时间。ADC的转换速率就是能够重

复进行数据转换的速度,即每秒转换的次数。转换时间与转换速率互为倒数。一般,逐次

逼近式的A/D 转换器的转换时间可达0.4µs。

2、A/D转换器的选择原则

Page 201: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 201 .

A/D转换器是单片机系统中的一个重要环节,当要对外部模拟量的值进行采集的时候,

一般都要用到A/D 转换器。在选择A/D转换器的时候,不能随意去选,否则可能无法满足

系统的要求,一般遵循以下原则:

(1)根据系统的误差要求,合理选择A/D 转换器的精度与分辨率。分辨率并非越高越

好,而是够用就行,一般分辨率高的A/D 转换器的价格也要相对高一些。

(2)根据被采集信号的变化速度,确定A/D 转换器的转换速度。不同类型的A/D转换

器,其转换速度有很大的差异,在选择时要注意这个重要指标。

(3)根据单片机的接口特性,合理的考虑A/D 转换器的输出状态。例如,A/D转换器

是并行输出,还是串行输出;是二进制码还是BCD 码等。

(4)其它因素,如成本、供货速度等。

11.1.2 8位并行A/D转换器ADC0809

ADDC0809是 8位 8 通道的逐次逼近式并行A/D 转换器。ADC0809的主要特性如下:

分辨率为8 位。

精度:小于±1LSB。

单一+5V供电,模拟输入电压范围为 0-5V。

具有锁存控制的8 路输入模拟开关。

可锁存三态输出,输出与TTL 电平兼容。功耗为15mW。

转换速度取决与芯片外接的时钟频率。时钟频率范围:10—1280kHz,典型值为

640kHz,转换速度约为100µs。

1、ADC0809的内部结构及引脚功能

ADC0809的内部逻辑结构如图 11-1所示。ADC0808/0809 片内带有锁存功能的8路模拟

开关,可对8 路输入模拟信号中的某一路进行转换。地址锁存和译码电路完成对A、B、C

三个地址位进行锁存和译码,其译码输出用来选择某一路模拟量通道。8 位 A/D转换器由控

制与时序电路、比较器、256R 电阻 T 型网路、树状电子开关、逐次逼近寄存器SAR等组成。

输出锁存器用来存放和输出转换后的数字量。

地址锁存

与译码

控制和时序

256R T型网络

IN0IN1IN2IN3IN4IN5IN6IN7

8通道

模拟

ADDAADDBADDCALE

Vcc GND

开关树

逐次逼近寄存器

比较器

START CLOCK

EOC

D7D6D5D4D3D2D1D0

三态

输出锁存

缓冲

REF (­)REF(+) OE

数据

输出

8 路模拟开关

A/D转换器

图 11-1 ADC0809 内部结构框图

Page 202: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 202 .

ADC0809为 28 脚标准封装直插式芯片,其引脚如图11-2 所示。各引脚功能介绍如下:

1234567891011121314 15

16171819202122232425262728IN3

IN4

IN7IN6IN5

STARTEOCD3OECLKVcc

REF(+)GNDD1

IN0IN1IN2

D2REF(­)D0D4D5D6D7ALECBA

ADC0809

图 11-2 ADC0809 引脚图

(1)IN0~IN7:8路模拟量输入端。

(2)C、B、A:三根地址译码输入线,根据这三条线的不同组合,控制多路转换开关,

使8路模拟输入 IN0~IN7中的某一个与 A/D转换器接通,从而对该路模拟量进行A/D 转换,

对应关系见表11-1。

表 11-1 C、B、A 与接通的模拟输入通道的对应关系

(3)ALE:地址锁存控制端,高电平有效。在 ALE的上升沿将 C、B、A 传过来的地址

锁存到内部的地址锁存器中,并将相应的模拟量输入通道接入A/D 转换器。

(4)OE:数字量输出允许信号输入端,高电平有效。当该段有效时,允许从A/D转换

器的输出锁存器中读取数字量。

(5)START:启动信号输入端,下降沿启动A/D 进行新的一次转换。

(6)EOC:A/D转换结束标志。A/D 转换期间,EOC 为低电平,A/D转换结束,EOC为高

电平。可作为转换结束的中断请求信号。

(7)CLOCK:时钟输入端。

(8)D7~D0:数字量输出线。

(9)Vcc:电源输入端,接+5V。

(10)GND:接地端。

(11)REF(+)、REF(-):参考电源的输入端。一般REF(+)接+5V,REF(-)接 0V(GND)。

2、ADC0809与单片机的接口

ADC0809与 51系列单片机的接口电路如图11-3所示。由于ADC0809需要外部时钟信号,

所以可利用单片机的地址锁存允许信号ALE 经 D触发器 2分频获得,如果单片机使用 6M的

晶体,则单片机的 ALE可直接与 ADC0809的 CLK相连,只要注意 CLK 端的时钟频率不超过

ADC0809所要求的范围(10—1280kHz)即可。

C 0 0 0 0 1 1 1 1

B 0 0 1 1 0 0 1 1

A 0 1 0 1 0 1 0 1

模拟通道 IN0 IN1 IN2 IN3 IN4 IN5 IN6 IN7

Page 203: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 203 .

P0.0P0.1P0.2

≥1

≥1

CK QD Q

ALE

P0.3

P0.7P0.6P0.5P0.4

WRP2.7

RD

P3.3 1 EOC

OEALESTART

D7D6D5D4D3D2D1D0

CLK

CBA

IN7

IN0IN1IN2IN3IN4IN5IN6

REF(+)

REF(­)

+5V

8路

MCU

ADC0809

图 11-3 ADC0809 与单片机的接口

P0口的低 3位(P0.2~P0.0)分别与ADC0809 的 C、B、A 相连,在启动A/D 转换的时

候作为地址线输出地址以确定哪一路模拟量被接通,除图11-3所示连接方式外,也可在P0

口的低3 位与ADC0809的 C、B、A之间接入锁存器。P0口还直接与 ADC0809数据总线相连,

在转换完毕时用来输入数据。P2.7 与 WR 共同控制ADC0809 的地址锁存与转换的启动,当单

片机向ADC0809 发出写命令时,WR 变为低电平,同时P2.7也变成低电平,由于ADC0809的

ALE与 START连在一起,所以START 和 ALE 同时有效,也就是说当单片机发给ADC0809通道

地址的同时也就启动了转换。转换完毕时的数据读出通过 P2.7 和 RD共同来控制完成。转

换结束信号端EOC 通过反向器与单片机的 P3.3(INT1)引脚相连,读取数据时既可采用查

询方式,也可采用中断方式。

3、A/D转换程序的编制

在编制程序之前,首先要计算 A/D转换器的地址,也就是单片机发送什么地址才能把

A/D转换器选通。根据图11-3 可知,P0 口的低3 位决定着哪一路模拟量被接通,当确定对

哪一路模拟量进行转换以后,P0 口低 3 位的值也就确定了,P0 口其它位与地址无关。前面

分析过,要想使ADC0809的 START与 ALE有效,必须 P2.7为 0,与 P2 口其它位无关。于是

就可以确定ADC0809 的地址了,设现在要对IN2通道进行A/D 转换,则P0.2~P0.0应分别

为0、1、0,P2.7为0,P2口与P1口的其它位取1,则ADC0809的地址为0111 1111 1111 1010B,

转化为十六进制为 7FFAH。通过上述分析很容易得知 IN0~IN7的通道地址分别为 7FF8H~

7FFFH。

有了以上分析,以查询方式进行 A/D 转换的程序如下,程序的功能为分别对 IN0~IN7

通道进行一次A/D 转换,转换后的数据存入数组ad_result中。

# include<reg51.h>

# include<absacc.h> /*绝对地址访问头文件*/

sbit EOC=P3^3; /*定义查询位*/

void main(void)

Page 204: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 204 .

中断方式进行A/D 转换的程序读者可仿照中断处理函数自行编制,这里不在赘述。

11.1.3 12位 A/D转换器MAX197

MAX197是一款 12 位的多量程 A/D转换器,芯片的工作电压为 5V。既可以接收高于电

源电压的模拟信号,又可接收低于地电位的模拟信号。MAX197的主要特性如下:

12位分辨率,1/2LSB 线性度。

单5V 供电。

软件可编程选择输入量程:±10V,±5V, 0~5V,0~10V。

8路模拟输人通道。

6µs转换时间,100k SPS 采样速率。

内/外部采集控制。

内部4.096V 或外部参考电压。

两种掉电模式。

内部或外部时钟

1、MAX197的引脚与控制字

MAX197的引脚图如图 11-4所示。各引脚描述如下。

1234567891011121314 15

16171819202122232425262728CLK

CS

HBENRDWR

SHDND7D6D5D4

D3/D11D2/D10D1/D9D0/D8

REFVDDDGND

AGNDCH0CH1CH2CH3CH4CH5CH6CH7INTREFADJ

MAX197

图 11-4 MAX197 的引脚图

CLK:时钟输人。外部时钟模式时,由此脚输人时钟;内部时钟模式时,该脚与地间接

一电容,以确定内部时钟频率,当fCLK=1.56MHz时,外接电容的典型值为 100pF。

unsigned char ad_result[8],j; /*定义存放结果的数组与临时变量*/

for(j=0;j<8;j++)

XBYTE[0x7ff8+j]=0; /*向外部地址写操作(写任意)以启动转换*/

while(EOC==0); /*等待转换结束*/

ad_result[j]=XBYTE[0x7ff8]; /*读取转换后的数据存入数组中*/

/*读操作的地址只要使 OE 有效即可*/

Page 205: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 205 .

CS:片选线,低电平有效。

WR:当 CS为低时,在内部采集模式下,WR 的上升沿将锁存数据,并启动一次采集和一

次转换周期;在外部采集模式下,WR的第一个上升沿启动采集,第二个上升沿结束采集并

启动转换周期。

RD:输出允许,当CS 为低时,RD的下降沿将允许读取数据总线上的数据。

HBEN:用于切换12位转换结果。此脚为高时,数据总线上的数据为高4 位;此脚为低

时,数据总线上的数据为低8 位。

SHDN:关断控制位。此脚接低电平时,器件进人掉电模式。

D7~D4:三态数字I/O 口。

D3/Dll:三态数字I/O 口。HBEN为低时,输出D3;HBEN 为高时,输出D11。

D2/D10:三态数字I/O 口。HBEN为低时,输出D2;HBEN 为高时,输出D10。

Dl/D9:三态数字1/0 口。HBEN为低时,输出D1;HBEN 为高时,输出D9。

DO/D8:三态数字1/0 口。HBEN为低时,输出DO;HBEN 为高时,输出D8。

AGND:模拟地。

CHO~CH7:模拟输人通道。

INT:当转换完成,且数据准备就绪时,INT变低。

REFADJ:内/外部参考电压选择引脚。使用内部参考电压时,对地接 0.01µF的旁路电

容;使用外部参考时,此脚接VDD。

REF:参考电压缓冲输出或ADC参考电压输入。存内部参考电压模式下,由此脚提供一

个4.096V 的标准输出;由 REFADJ脚进行外部调节;在外部参考电压模式下,REFADJ 接至

VDD,此脚接外部参考电压。

VDD:+5V 电源,对地接0.1µF 的旁路电容。

DGND:数字地。

MAX197的转换是从写入控制字开始的,控制字的格式如下:

其中,ACQMOD决定采集控制模式,置 0 时为内部采集控制模式,置 1 时为外部采集控

制模式。

PD1和 PD0用来控制芯片的时钟模式。一旦选定了芯片的时钟模式,再进入待机或掉电

模式时,时钟模式不会改变。当 PD1 和 PD0 均为 0 时,芯片选择外部时钟模式,外部时钟

的频率应在100kHz 到 2.0 MHz之间。其具体含义见表 11-2。

表 11-2 时钟/模式选择与量程选择

BIP和 RNG用来选择模拟输入信号的电压范围,见表11-2。

D7 D6 D5 D4 D3 D2 D1 D0

PD1 PD0 ACQMOD RNG BIP A2 A1 A0

时钟与模式选择 量程选择

PD1 PD0 芯片工作模式 BIP RNG 模拟量输入量程(V)

0 0 工作状态/外部时钟模式 0 0 0~5

0 1 工作状态/内部时钟模式 0 1 0~10

1 0 待机状态/时钟模式不变 1 0 -5~+5

1 1 掉电状态/时钟模式不变 1 1 -10~+10

Page 206: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 206 .

A2、A1 和 A0 用来对8 路模拟量输入通道进行选择,利用这三位可以决定具体哪一路模

拟量被接通。具体见表11-3。

表 11-3 时钟/模式选择与量程选择

2、MAX197与单片机的接口

MAX197 与 51 系列单片机的接口如图 11-5 所示。图中, C1=100Pf,C2=4.7µF,

C3=0.1µF,C4=0.01µF。

D0/D8

D2/D10D1/D9

D3/D11D4D5D6D7

HBEN

INT

14 3913121110987

38373635343332

P0.0P0.1

P0.7P0.6P0.5P0.4P0.3P0.2

5

2

4

3

24 1216

17

28

1 P1.0

P2.7

CH0

DGNDAGND

REFADJ

SHDNVDD

REF

CLK

GND

VCC

C4

C1

C2MAX197

P1.2

C3

CS

RDWR

RD

WR

MCU

..

.

CH7

图 11-5 MAX197 与单片机的接口电路

3、A/D转换程序的编制

在编制A/D 转换程序之前,要计算MAX197 的控制字和地址。图 11-5的接法中,MAX197

使用的是内部参考电压,内部时钟方式,外部控制采集模式,模拟量输入范围是0~5V,于

是可以计算出控制字为60H~67H,分别对应CHO~CH7 通道。地址的计算仿照ADC0809的计

算方式,可以得出MAX197 的地址为7F00H。

A/D转换程序如下,程序的功能为分别对 CH0~CH7通道进行一次 A/D 转换,转换后的

数据存入数组ad_result 中。

A2 0 0 0 0 1 1 1 1

A1 0 0 1 1 0 0 1 1

A0 0 1 0 1 0 1 0 1

模拟通道 CH0 CH1 CH2 CH3 CH4 CH5 CH6 CH7

# include<reg51.h>

# include<absacc.h> /*绝对地址访问头文件*/

#define ad XBYTE[0x7f00] /*A/D 地址*/

sbit HBEN=P1^0; /*定义高/低位数据的切换位*/

sbit EOC=P1^2; /*定义查询位*/

void main(void)

unsigned int ad_result[8]; /*定义存放结果的数组与临时变量*/

Page 207: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 207 .

11.1.4 串行模数转换芯片TLC0832

TLC0832是一种 8 位分辨率的串行模数转换芯片,该芯片有两个模拟量通道,与单片

机接口简单,体积小,因此在数据采集量不大的场合有广泛的应用。在我公司 MCU2007­I型单片机开发板上就使用了一片TLC0832,可以对可调电阻的输出电压进行采集。与

TLC0832兼容的芯片还有 ADC0832,二者使用方法完全相同。

1、工作原理

首先我们要了解 TLC0832的引脚分布,如图 11­6 所示:

CS

CHO

CHI

GND DI

DO

CLK

VCC1

2

3

4 5

6

7

8

TLC0832

图 11­6 TLC0832引脚图

芯片引脚接口说明:

CS 片选使能,低电平芯片使能。

CH0 模拟输入通道0,或作为IN+/­使用。

CH1 模拟输入通道1,或作为IN+/­使用。

GND 芯片参考0 电位(地)。

DI 数据信号输入,选择通道控制。

DO 数据信号输出,转换数据输出。

CLK 芯片时钟输入。

Vcc/REF 电源输入及参考电压输入(复用)。

TLC0832 为8位分辨率A/D转换芯片,其最高分辨可达256级,可以适应一般的模拟量转换要

求;其内部电源输入与参考电压的复用,使得芯片的模拟电压输入在0~5V之间;芯片转换时间

仅为32μS;据有双数据输出,可作为数据校验,以减少数据误差;转换速度快且稳定性能强;

unsigned char data_l,data_h,j; /*定义中间变量*/

for(j=0;j<8;j++) /*注意存放 12 位转换结果的数组应为整型*/

ad=0x60+j; /*向 MAX197 写入控制字以启动转换*/

while(EOC==1); /*等待转换结束*/

HBEN=0; /*指向结果低 8位*/

data_l=ad; /*读结果低 8位*/

HBEN=1; /*指向结果高 4位*/

data_h=ad; /*读结果高 4位*/

ad_result[j]= data_h*256+data_l; /*计算最终的数据并存入数组中*/

Page 208: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 208 .

独立的芯片使能输入,使多器件挂接和处理器控制变的更加方便。通过DI 数据输入端,可以轻

易的实现通道功能的选择。

正常情况下TLC0832 与单片机的接口应为4 条数据线,分别是CS、CLK、DO、DI。但

由于DO 端与 DI端在通信时并未同时有效并与单片机的接口是双向的,所以电路设计时可

以将DO 和 DI 并联在一根数据线上使用。

图 11-7 TLC0832(ADC0832)时序图

TLC0832的工作时序如图11-7 所示。当TLC0832 未工作时其CS 输入端应为高电平,此

时芯片禁用,CLK 和 DO/DI 的电平可任意。当要进行A/D 转换时,须先将 CS使能端置于低

电平并且保持低电平直到转换完全结束。

芯片开始转换工作时,由处理器向芯片时钟输入端 CLK 输入时钟脉冲,DO/DI 端则使

用DI 端输入通道功能选择的数据信号。在第1 个时钟脉冲的下沉之前DI端必须是高电平,

表示启始信号。在第2、3 个脉冲下沉之前DI 端应输入2 位数据用于选择通道功能,在第2、

3个脉冲下沉之前 DI端应输入2 位数据用于选择通道功能,当前2 位数据为"1""0"时,只

对CH0 进行单通道转换。当2 位数据为"1""1"时,只对 CH1进行单通道转换。当2 位数据

为"0""0"时,将CH0作为正输入端IN+,CH1作为负输入端IN-进行输入。当2位数据为"0""1"

时,将 CH0 作为负输入端 IN-,CH1 作为正输入端 IN+进行输入。到第 3 个脉冲的下沉之后

DI端的输入电平就失去输入作用,此后DO/DI 端则开始利用数据输出DO进行转换数据的读

取。从第4个脉冲下沉开始由 DO端输出转换数据最高位 DATA7,随后每一个脉冲下沉DO端

输出下一位数据。直到第 11 个脉冲时发出最低位数据 DATA0,一个字节的数据输出完成。

也正是从此位开始输出下一个相反字节的数据,即从第 11个字节的下沉输出 DATD0。随后

输出8 位数据,到第 19 个脉冲时数据输出完成,也标志着一次 A/D 转换的结束。最后将

CS置高电平禁用芯片,直接将转换后的数据进行处理就可以了。

作为单通道模拟信号输入时 TLC0832的输入电压是 0~5V且 8 位分辨率时的电压精度

为19.53mV。如果作为由 IN+与 IN-输入的输入时,可是将电压值设定在某一个较大范围之

内,从而提高转换的宽度。但值得注意的是,在进行 IN+与 IN-的输入时,如果 IN-的电压

Page 209: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 209 .

大于IN+的电压则转换后的数据结果始终为 00H。

2、ADC0832的编程方式及应用程序

MCU2007-I型开发板中TLC0832与单片机的连接方式如图11-8所示。

TLC0832具有双通道功能,CHI、CHO 为模拟输入通道,可以分别使用;CHI通道接可

调电阻R25 的输出端,调节R25的阻值即可改变 CHI通道的模拟电压值,ADC0832转换后的

数据就会发生改变;CHO 通道未使用,与插针JP7相连,用户可以在需要的时候将需要A/D

转换的模拟电压信号接至JP7 插针,注意,JP7插针左边引脚接地。

图 11­8 TLC0832与单片机接口

我们根据图11-7所给出的CLK和SDA的信号,然后参考上面的工作原理可以写出TLC0832

的程序,此程序是选用CH1通道,对可调电阻的输出电压进行采集,并将采集到的数据在 LED

显示器上进行显示。

#include<reg52.h>

#include<absacc.h>

#include <intrins.h>

#define uchar unsigned char

sbit adc_cs=P1^1; //TLC0832的片选信号端;

sbit adc_clk=P3^4; //时钟端口;

sbit adc_sda=P3^5; //数据端口;

sbit a_138=P1^2; //138地址片选端口;

sbit b_138=P1^3;

sbit c_138=P1^4;

sbit cs_138=P2^6; //138片选端口;

sbit cs_373=P1^5; //373片选端口;

unsigned code zima1[10]=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90;

//共阳极字码0~9;

unsigned char zima2[10]=10,10;

void delay(void);

Page 210: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 210 .

void delay_adc(unsigned int i);

unsigned char adc_change(void); //模拟数字转换函数声明;

void display(uchar pos,uchar num,bit dp); //显示函数声明;

void led3(uchar pos,uchar num); //显示字符数据的函数

main() //主函数;

unsigned char adc_dat,x,y;

bit flag=0;

cs_138=1; //使数码管开通;

cs_373=1;

while(1)

adc_cs=0; //选通TLC0832;

adc_dat=adc_change(); //A/D转换,并将结果保存在adc_dat中

led3(0,adc_dat); //在第一个数码管开始的3位显示转换结果

void delay()

unsigned char i;

for(i=0;i<20;i++);

void delay_adc(unsigned char i) //延时

while(i--);

unsigned char adc_change(void) //ADC初始化

unsigned char i;

unsigned char dat1,dat2;

loop:

dat1=0;

dat2=0;

adc_cs=0; //当TLC0832未工作时CS输入端应为高电平,此时芯片禁用

delay_adc(2);

Page 211: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 211 .

adc_clk=1;

adc_sda=1; //在第1 个时钟脉冲的下沉之前DI端必须是高电平.

adc_clk=0;

delay_adc(2);

adc_clk=1; //形成一次下降沿

adc_sda=1;

adc_clk=0;

adc_clk=1; //形成二次下降沿

adc_sda=1; //确定取值

adc_clk=0; //形成三次下降沿

for(i=0;i<8;i++)

dat1<<=1;

adc_clk=1;

_nop_();

adc_clk=0;

if(adc_sda)

dat1=dat1|0x01;

dat2=0x80;

for(i=0;i<7;i++)

dat2>>=1;

adc_clk=1;

_nop_();

adc_clk=0;

if(adc_sda)

dat2=dat2|0x80;

adc_cs=1;

if (dat1==dat2)

return(dat2);

else

Page 212: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 212 .

11.2 D/A转换器接口

在单片机控制系统中,经常将检测到的物理量转换成数字量,经计算机进行数据处理

后,再将结果的数字量转换成模拟量输出,以实现对被控对象(如电动调节阀、模拟量输

入的变频器等)的控制。把数字量到模拟量的变换称为数/模转换,简称D/A 转换。把数字

goto loop;

void display(uchar pos,uchar num,bit dp) //显示函数定义

if(dp==1)

P0=zima1[num]+0x80; //显示小数点

else

P0=zima[num]; //不显示小数点

switch(pos)

case 0:P1=0xbf;break; //位置 0,对应最左边数码管

case 1:P1=0xbb;break; //位置 1

case 2:P1=0xb7;break; //位置 2

case 3:P1=0xb3;break; //位置 3

case 4:P1=0xaf;break; //位置 4

case 5:P1=0xab;break; //位置 5

case 6:P1=0xa7;break; //位置 6

case 7:P1=0xa3;break; //位置 7,对应最右边数码管

default:break;

void led3(uchar pos,uchar num) //显示字符数据的函数

//从第pos+1个数码管开始显示3位

uchar k;

for(k=pos+2;k>=pos;k--)

display(k,num%10,0);

num/=10;

Page 213: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 213 .

信号转换成模拟信号的器件称为数/模转换器(DAC),也叫D/A 转换器。

11.2.1 D/A 转换器的主要性能指标

为了更好地设计 D/A 转换器的接口电路,应该对 D/A转换器的性能指标有所了解,这

有助于对D/A 转换器的选择。D/A 转换器的性能指标主要有静态指标、动态指标以及环境和

工作条件指标。

(1)分辨率

分辨率是指输入数字量的最低有效位 LSB 产生一次变化时,所对应的输出模拟量(电

压或电流 )的变化量。分辨率等于满量程的 1/2n(n 为输入数字量的位数),它反映了输

出模拟量的最小变化值。

在实际使用中,常用输入数字量的位数来表示分辨率。例如,8 位的D/A 转换器,其分

辨率为8 位,即模拟量输出的最小变化值为满量程的1/256。显然,位数越多,分辨率就越

高。

(2)线性度

线性度也称为非线性误差,它定义为转换后的实际值与理想值之间的最大偏差,并以

该偏差相对于满量程的百分数表示。例如,±1%是指实际输出值与理论值之差在满刻度的

±1%以内。

(3)绝对精度和相对精度

绝对精度是指在整个刻度范围内,任一输入所对应的模拟量实际输出值与理论值之间

的最大误差。绝对精度应小于 1/2n,即 1LSB。相对精度与绝对精度表示的意义相似,所不

同的是相对精度用最大误差相对满刻度的百分比表示,或者用数字量的最低有效位 LSB表

示。

(4)建立时间

建立时间是描述D/A转换速率快慢的一个重要参数,一般是指输入数字量从全 0变为

全1 后,输出的模拟信号稳定在满刻度值的±1/2LSB所需的时间。

11.2.2 8位D/A 转换器DAC0832

DAC0832是具有 8位分辨率的电流输出型D/A 转换器,由于其片内有输入数据寄存器,

故可以直接与单片机相连。在电流输出需要转换为电压输出的场合,可外接运算放大器来

实现。

DAC0832的电流建立时间为 1µs;数据输入可采用双缓冲、单缓冲或直通方式;逻辑电

平输入与TTL 电平兼容;单一电源供电(+5~+15V);功耗低,约为20mW。由于它价格低廉,

与单片机接口简单,控制容易,所以在实际系统中被广泛应用。

1、DAC0832的结构与引脚

Page 214: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 214 .

DAC0832的内部结构如图 11-9所示。它由一个8 位输入锁存器、一个8 位 DAC 寄存器

和一个8 位 D/A转换器及逻辑控制电路组成。

数据输入通道由输入锁存器和DAC 寄存器构成两级数据的输入缓存,当LE1(LE2)为0

时,数据进入寄存器被锁存;当LE1(LE2)为 1时,锁存的数据进行输出。这样,在使用

时就可根据需要对数据输入采用两级锁存(双缓冲)、一级锁存(单缓冲)或无锁存(直通

方式)形式。

双缓冲形式,可使D/A 转换器在转换前一个数据的同时,就可以将下一个待转换的数

据预先送入输入锁存器,以提高转换速度。此外,在使用多个D/A 转换器分时输入数据的

情况下,双缓冲可以保证同时输出模拟信号。

8位输入

锁存器

&

&

&

8位DAC寄存器

8位D/A转换器

DI7 DI0

输入数据

ILE

CSWR1

WR2 XFER

R

RFB

AGND

IOUT1

IOUT2

VREF

FB

图 11-9 DAC0832 内部结构图

DAC0832的引脚如图 11-10所示,它采用20 脚双列直插式封装,各引脚功能如下:

(1)DI7~DI0:数据输入线。

(2) :片选信号,低平有效。CS

(3)ILE:输入锁存器锁存允许信号,高电平有效。

(4) :写信号1,低电平有效。当ILE=1, =0, =0时,可将数据写入输入1WRCS

1WR

锁存器。

(4) :写信号 2,低电平输入有效。当其有效时,在传送控制信号 的作用下,2WRXFER

可将锁存在输入锁存器的8 位数据送到DAC寄存器。

(5) :数据传送控制信号,输入低电平有效。当 为低电平, 也为低电XFER XFER

2WR

平时将DAC 寄存器的内容打入DAC寄存器。

(6) :基准电压输入端,可在-10V~+10V范围内调节。REFV

(7) 、 :电流输出引脚。电流 和 的和为常数。1OUTI 2OUTI 1OUTI 2OUTI

(8) :反馈信号输入端,与DAC0832 内部反馈电阻相连。可作为外部运算放大器的FBR

反馈电阻用。

(9) :工作电源引脚。一般 的范围是+5V~+10V。CCV CCV

(10)DGND:数字信号地。

(11)AGND:模拟信号地。

Page 215: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 215 .

12345678910 11

121314151617181920CS

WR1

DAC0832

AGNDDI3DI2DI1DI0

VREFRFB

DGND IOUT1IOUT2DI7DI6DI5DI4

VccILEWR2XFER

图 11-10 DAC0832 引脚图

2.DAC0832的工作方式及与单片机的接口

前面提到过,DAC0832 有三种工作方式,分别是双缓冲工作方式、单缓冲工作方式和直

通方式。不同的工作方式适用于不同的场合,大多数情况下使用的是单缓冲工作方式。

1)单缓冲工作方式

单缓冲工作方式时的接口电路如图11-11所示。该方式适用于只有一路模拟量输出或

虽有多路输出但不要求同步的系统。DAC0832的两级寄存器的写信号 和 都由单片机1WR 2WR的 引脚控制。当单片机的地址线选择好DAC0832后,只要输出 控制信号(向DAC0832WR WR

写任意数据),就可以同时完成数字量的输入锁存和D/A 转换输出。

CSXFERDI7~DI0

WR1WR2 DGND

ILEVcc

+5V

MCU

P2.7

P0

WR

DAC0832

­+

10K

10K

­+IOUT2

IOUT1RFB

VOUT

8

图 11-11 DAC0832 单缓冲工作方式接口电路

由于DAC0832 是电流输出型,所以为了得到电压信号,需在 DAC0832 的输出端接运算

放大器。接入一级运算放大器可以得到负的电压信号,为了得到正的电压信号,在图 11-11

中,接入了两级运算放大器。

单缓冲工作方式下的程序编制如下,程序的功能是在运算放大器的输出端(VOUT端)输

出锯齿波信号。

#include<reg51.h>

#define dac XBYTE[0x7fff]; /* DAC0832 的地址*/

void main()

unsigned char j; /*变量定义*/

while(1)

for(j=0;j<256;j++) /* 循环输出 0~255 之间的数据*/

dac=j;

Page 216: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 216 .

2)双缓冲工作方式

在一个单片机系统中,如果有多个D/A 转换接口,而且要求同步进行D/A转换输出,

则必须采用双缓冲工作方式。DAC0832 工作于双缓冲工作方式时,数字量的输入锁存和D/A

转换是分两步完成的。首先CPU 的数据总线分时地向各路D/A转换器输入要转换的数字量

并锁存在各自的输入锁存器中,然后CPU 对所有的D/A转换器发出控制信号,将各个 D/A

转换器输入锁存器中的数据打入DAC 寄存器,实现同步转换输出。

图11-12为一个二路同步输出的 D/A转换接口电路。单片机的 P2.5和 P2.6分别选择

两路D/A 转换器的输入锁存器,P2.7连接到两路D/A 转换器的 端控制同步转换输出。XFER

CSXFERDI7~DI0WR1WR2 DGND

ILEVcc

+5V

CSXFERDI7~DI0WR1WR2 DGND

ILEVcc

+5V

MCU

8

P2.5P2.7P0WR

P2.6

DAC0832(2)

DAC0832(1)

图 11-12 DAC0832 双缓冲工作方式接口电路

完成两路D/A 的同步转换输出的程序如下,设 DAC0832(1)转换的数据在变量 data1中,

DAC0832(2)转换的数据在变量data2 中。

3)直通工作方式

当DAC0832 芯片的片选信号 、写信号 、 及传送控制信号 的引脚全部接CS

1WR 2WR XFER

地,允许输入所存信号ILE引脚接+5V 时,DAC0832 芯片就处于直通工作方式,数字量一旦

输入,就直接进入 DAC 寄存器,进行 D/A 转换。直通工作方式适用于单片机不对外部进行

其它写操作的场合,由于该方式不利于控制,所以实际系统中很少应用。

#include<reg51.h>

#include<absacc.h> /* 绝对地址操作头文件*/

#define dac1 XBYTE[0xdfff]; /* DAC0832(1)的输入锁存器地址*/

#define dac2 XBYTE[0xbfff]; /* DAC0832(2)的输入锁存器地址*/

#define dac_total XBYTE[0x7fff];/* 两个 D/A 转换器的 DAC 寄存器地址*/

void main()

unsigned char data1,data2,random; /*变量定义*/

dac1=data1; /* data1 送入 DAC0832(1)的输入锁存器*/

dac2=data2; /* data2 送入 DAC0832(2)的输入锁存器*/

dac=random; /* 向两个 D/A 转换器写入任意数据启动 D/A 转换*/

Page 217: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 217 .

11.2.3 12位 D/A转换器MAX508

MAX508是 12位的电压输出型D/A 转换器,使用比较方便,精度高。其内部带参考电压,

而且参考电压与转换后的输出电压有相同的极性,允许单电源供电。

1、 MAX508概述

MAX508的内部结构如图 11-13所示。MAX508内部集成了 D/A转换器、电压输出放大器。

双缓冲的逻辑输入很容易与单片机接口,数据以(8+4)位的格式被传送到输入寄存器。该

D/A转换器既可单电源供电,也可双电源供电。电源电压值可以是+12V 或±15V。该芯片内

部设有增益放大器,可以把输出电压设定为三个范围,在单/双电源供电时可为0V~+5V、

0V~+10V,在双电源供电时为-5V~+5V。在输出为 10V时,输出放大器可驱动 2kΩ的负载。

高4位输入锁存

低8位输入锁存

DAC锁存器

≥1

D0

D7...

≥1

_

+

RFB

VOUT

VSS

D/A转换器

ROFS

AGNDVDDREFOUTLDAC

CSMSB

WR

CSLSB

图 11-13 MAX508 的内部结构框图

MAX508的引脚如图 11-14所示。引脚说明如下。

VSS:电源负极。单电源供电时接地,双电源供电时接-15V。

ROFS:满量程输出设置端。输出为0V~+5V时,ROFS 与 RFB 同时接VOUT;输出为0V~

+10V时,ROFS接 AGND,而 RFB 接 VOUT;输出为-5V~+5V时,ROFS 接 REFOUT,而 RFB 接

VOUT。

REFOUT:内部参考电压输出(+5V)。

AGND:模拟地。

D7~D4:数字输入的D7~D4 位。

DGND:数字地。

D3/Dll:数字输入口。 =0时,输入 D3; =0 时,输入D11。参表 10-7。CSLSB CSMSB

D2/D10:数字输入口。 =0时,输入 D2; =0 时,输入D10。参表 10-7。CSLSB CSMSBDl/D9:数字输入口。 =0时,输入 D1; =0 时,输入D9。参表 10-7。CSLSB CSMSBDO/D8:数字输入口。 =0时,输入 D0; =0 时,输入D8。参表 10-7。CSLSB CSMSB

:数据输入高低位选择。 =0 时,装载数据的高4位。CSMSB CSMSB

:数据输入高低位选择。 =0 时,装载数据的低8 位。CSLSB CSLSB

:写允许信号。 信号的上升沿将锁存数据。WR WR:当 =1时,装载数据到内部输入锁存器,在 上升沿时,将数据锁存到LDAC LDAC LDAC

DAC锁存器。

VDD:正电源输入端,接+12V 或+15V。

RFB:反馈电阻引脚。与ROFS 共同控制输出量程。

Page 218: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 218 .

VOUT:转换后的电压输出端。

12345678910 11

121314151617181920VSS

ROFS

MAX508

REFOUTAGND

D7D6D5D4

D3/D11DGND D2/D10

D1/D9D0/D8CSMSBCSLSBWR

VOUTRFBVDDLDAC

图 11-14 MAX508 的引脚图

表11-4 列出了MAX508的控制引脚的组合及其功能,在编制程序时要严格遵守。表 11-5

列出了MAX508 的模拟电压输出范围与输入数据之间的关系,在实际应用时要注意不同的电

压输出情况下的输入数据有很大的差异。

表 11-4 MAX508 的控制引脚组合及其功能

注:↑表示上升沿

表 11-5 MAX508 的输出电压范围与输入数据的关系

CSLSB CSMSB WR LDAC 功 能

0 1 0 1 装载低 8位数据到输入锁存器

0 1 ↑ 1 把低 8位数据锁存到输入锁存器

↑ 1 0 1 把低 8位数据锁存到输入锁存器

1 0 0 1 装载高 4位数据到输入锁存器

1 0 ↑ 1 将高 4位数据锁存到输入锁存器

1 ↑ 0 1 将高 4位数据锁存到输入锁存器

1 1 1 0 装载输入数据到 DAC 锁存器

1 1 1 ↑ 把输入数据锁存到 DAC 锁存器

1 0 0 0 装载高 4位到输入锁存器并将输入数据送入DAC 锁存器

1 1 1 1 无数据传送

输 入 0V~+5V 输出 0V~+10V 输出 -5V~+5V 输出

1111 1111

1111

VREF×4095/4096 2×VREF×4095/4096 +VREF×2027/2048

1000 0000

0001

VREF×2049/4096 2×VREF×2049/4096 +VREF×1/2048

1000 0000

0000

VREF×2048/4096 2×VREF×2048/4096 0V

0111 1111

1111

VREF×2047/4096 2×VREF×2047/4096 -VREF×1/2048

0000 0000

0001

VREF×1/4096 2×VREF×1/4096 -VREF×2027/2048

0000 0000

0000

0V 0V -VREF

Page 219: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 219 .

2. MAX508与单片机的接口与编程

在MAX508 与单片机接口时,首先要确定输出电压的极性范围,因为不同的输出电压范

围其接口形式式不一样的,具体请参阅引脚介绍。

MAX508与单片机的接口电路如图 11-15 所示。图中所示接法的输出电压范围是 0V~

+10V。

P2.6

P0.0P0.1

P0.7P0.6P0.5P0.4P0.3P0.2

P2.5

D0/D8

D2/D10D1/D9

D3/D11D4D5D6D7

ROFSAGNDDGND

VCCVOUTRFB

GND

+12V

电压

输出

MAX508

VSS

MCUCSMSBCSLSB

LDAC

WRWR

P1.1

图 11-15 MAX508 与单片机的接口电路

程序编制如下,程序的功能是从 MAX508 的 VOUT 引脚输出三角波,若想改变三角波的

频率,可在向MAX508 输出一个数据后适当延时,然后在输出下一个数据。

#include<reg51.h>

#include<absacc.h>

#define da_h XBYTE[0xbfff]

#define da_l XBYTE[0xdfff]

sbit p1_1=P1^1;

void main()

unsigned char i,j;

while(1)

for(i=0;i<16;i++) /*循环输出递增的数据*/

for(j=0;j<256;j++)

da_h=j; /*送出数据的低 8位*/

da_l=i; /*送出数据的高 4位*/

p1_1=0; /*将 12 位数据送入 DAC 锁存器*/

p1_1=1; /*启动转换*/

for(i=15;i>=0;i--) /*循环输出递减的数据*/

for(j=254;j>=0;j--)

da_h=j; /*送出数据的低 8位*/

da_l=i; /*送出数据的高 4位*/

Page 220: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 220 .

第 12 章 常用外围芯片资料与编程

12.1 实时日历/时钟芯片 DS12887

DS12887是 DALLAS 公司推出的新型日历芯片,它能够自动产生年、月、日、时、分、

秒等时间信息。芯片内部带有锂电池,外部掉电时,其内部时间信息能够保持 10年左右。

有12 小时制和 24小时制两种工作模式。时间的表示也有两种,一种用二进制表示,一种

用BCD 码表示。此外,用户还可对 DS12887编程以实现多种方波输出,并对其内部的三路

中断通过软件进行屏蔽。DS12887引脚如图 12-1(a)。

0

1

2

3

4

5

6

7

8

9

10

11

12

13

秒报警

分报警

时报警

星期

寄存器A

寄存器D

寄存器C

寄存器B

00H

0DH

.

.

.

1

2

3

4

5

6

7

8

9

10

11

12 13

14

15

16

17

18

19

20

21

22

23

24MOT

NC

NC

AD0

AD1

AD2

AD3

AD4

AD5

AD6

AD7

GND

Vcc

SQW

NC

NC

NC

/IRQ

/RESET

DS

NC

R/W

AS

/CS

图a 图b

图 12-1 DS12887 引脚及内部寄存器

各引脚功能如下:

Vcc:直流+5V 输入。当Vcc输入为+5V 时,用户可对DS12887 内的 RAM 进行访问,并可

对其进行读、写操作;当Vcc的输入小于+4.25V 时,禁止用户对内部RAM 进行读、写操作,

此时用户不能正确获取芯片内的时间信息;当 Vcc 的输入小于 3V时,DS12887 会自动将电

源切换到内部自带的锂电池上,以保证内部的电路能够正常工作。

GND:地。

MOT:总线模式选择,当MOT接 Vcc时,选用MOTOROLA 总线模式,当MOT 接 GND时,选

用Intel 总线模式。

p1_1=0; /*将 12 位数据送入 DAC 锁存器*/

p1_1=1; /*启动转换*/

Page 221: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 221 .

SQW;方波输出,当供电电压Vcc大于 4.25V时,SQW 脚可输出方波,此时用户可通过对

控制寄存器编程来得到13 种方波信号。

AD0-AD7:双向地址/数据总线。

AS:地址有效输入。

DS/RD:数据选通/读允许,当 MOT 接 Vcc时作为数据选通;当 MOT接 GND 时,作为读

允许输入(即RD)。

R/W:读/写允许输入,若 MOT接 Vcc,该引脚为高电平时为读操作,为低电平时为写操

作;若MOT 接 GND,该引脚作为写允许输入(即WR)。

CS:片选输入,低电平有效。

IRQ:中断请求输出,低电平有效。

RESET:复位端,可以直接接 Vcc,这样可以保证 DS12887在掉电时,其内部控制寄存

器不受影响。

NC:空引脚。

DS12887带有 128 字节片内 RAM,其中 10 字节用来存储时间信息,称为时标寄存器,

地址为00H-09H,CPU可以通过读取时标寄存器获得时间与日历值,也可以编程设置其初始

值,时标寄存器的值可以用二进制或 BCD码表示。4 字节用来存储控制信息,称为控制寄存

器,地址为0AH-0DH,用户可通过对控制寄存器编程实现从SQW引脚输出多种不同频率的方

波,并可对其内部的三路中断通过软件进行屏蔽。114字节作为通用RAM 供用户使用,地址

为0EH-7FH。DS12887 寄存器地址分配见图12-1(b)。

寄存器A主要用于选择时钟频率、中断周期和 SQW输出频率,格式如下:

UIP:更新周期标志。UIP=1 时,芯片正处于或即将开始更新周期,在此期间不允许读

写时标寄存器。

DV2-DV0:芯片内部振荡器RTC控制位。当芯片解除复位状态,并将010写入 DV0、DV1、DV2

后,另一个更新周期将在 500ms后开始。因此,在程序初始化时,可用这三位精确地使芯

片在设定的时间开始工作。

RS3-RS0:周期中断可编程方波输出速率选择位。着 3位不同的组合可以产生不同的方

波输出,程序可以通过设置寄存器B 的 SQWF和 PIE位控制是否允许周期中断方波输出。

寄存器B主要用于设置芯片的工作状态,格式如下:

SET:当该位为“0”时,芯片处于正常工作状态,每秒产生一个更新周期来更新时标

寄存器,当该位为“1”时,芯片停止工作,程序在此期间可初始化芯片的各个时标寄存器。

PIE、AIE、UIE:分别为周期中断、报警中断、更新周期结束中断允许位。各位分别为

“1”时允许相应的中断。

SQWE:方波输出允许位。为“1”时,按寄存器A输出速率选择位所确定的频率输出方

波;为“0”时,SQW脚保持高电平。

DM:时标寄存器用BCD 码表示或用二进制表示的格式选择位。为“0”时,BCD码格式;

为“1”时,二进制码。

D7 D6 D5 D4 D3 D2 D1 D0

UIP DV2 DV1 DV0 RS3 RS2 RS1 RS0

D7 D6 D5 D4 D3 D2 D1 D0

SET PIE AIE UIE AQWE DM 24/12 DSE

Page 222: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 222 .

24/12:24小时或 12小时模式设置位。为“1”时,选择 24小时工作模式;为“0”时,

选择12 小时工作模式。

DSE:夏令时服务位。为“1”时,夏时制设置有效,夏时制结束可自动刷新恢复时间;

为“0”时无效。

寄存器C 为中断标志寄存器,该寄存器的特点是程序访问读寄存器后,该寄存器的内

容将自动清零,从而使 IRQF 标志位变为高电平,否则,芯片无法向 CPU申请下一次中断。

格式如下:

IRQF:中断申请标志位。当IRQF=1时,IRQ 引脚将变低电平引发中断申请。

PF、AF、UF:这三位分别为周期中断、报警中断、更新周期结束中断标志位。只要满

足各中断的条件,相应的中断标志位将置“1”。

寄存器D为状态标志寄存器,只有一个标志位 VRT(D7),其余各位均为0。VRT是芯片

内部RAM 与寄存器内容有效标志位。该位为“1”时,表示芯片内部 RAM和寄存器内容有效。

读该寄存器后,该位将自动置“1”。

DS12887处于正常工作状态时,每秒钟将产生一个更新周期,芯片处于更新周期的标志

是寄存器 A 中的 UIP 位为“1”。在更新周期内,芯片内部时标寄存器数据处于更新阶段,

故在该周期内,处理器不能读出时标寄存器的内容,否则将得到不确定的数据。更新周期

的基本功能主要是刷新各个时标寄存器中的内容,同时秒时标寄存器内容加1,并检查其它

时标寄存器内容是否溢出。如果有溢出则相应进位日、月、年。另外一个功能是检查三个

时、分、秒报警时标寄存器的内容是否与对应时标寄存器的内容相符,如果相符则寄存器 C

中的AF 位置“1”。如果报警时标寄存器的内容C0H到 FFH之间的数据,则为不关心状态。

为了采样时标寄存器中的数据,DS12887提供了两种避开更新周期内访问时标寄存器的

方法:一种方法是利用更新周期结束发出的中断。它可以编程允许在每次更新周期结束后

发中断申请,提醒 CPU 将有 998ms 左右的时间去获取有效的数据,在中断之后的 998ms 时

间内,程序可先将时标数据读入芯片内部不掉电静态 RAM 中,因为芯片内部的静态 RAM和

状态寄存器是可随时读写的,在离开中断服务子程序前应清除寄存器 C中的IRQF 位。另一

种方法是利用寄存器 A 中的 UIP 位来指示芯片是否处于更新周期。在 UIP位从低变高 244

微秒后,芯片将开始更新周期,所以检测到 UIP位为低电平时,利用 244微秒的时间去读

取时标信息。如检测到 UIP 位为“1”,则可暂缓读数据,等到 UIP变成低电平后再去读数

据。

DS12887采取连续工作方式,一般无须每次都进行初始化,即使是系统复位时也是如此。

如果必须要进行初始化,首先应禁止芯片内部的更新周期操作,即先将状态寄存器B 中的

SET位置“1”,然后初始化00H-09H时标参数寄存器和状态寄存器 A,此后再通过读状态寄

存器C,清除寄存器 C中的周期中断标志 PF,报警中断标志 AF,更新周期结束中断标志UF。

通过读寄存器 D 中的 VRT位,读状态寄存器后 VRT位将自动置“1”,最后将状态寄存器 B

中的SET 位置“0”,芯片开始计时工作。

D7 D6 D5 D4 D3 D2 D1 D0

IRQF PF AF UF 0 0 0 0

Page 223: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 223 .

/RD

/WR

/INT0

/RESET

ALE

P2.7

P0

DS

R/W

/IRQ

/RESET

AS

/CE

AD0-AD7

8051 DS12887

Vcc

GND

+5V

图 12-2 DS12887 与单片机接口电路

图12-2 为 DS12887 与 51单片机的一种接口电路,按图示的接线方式,相应的驱动程

序如下:

#include <reg51.h>

#include <absacc.h>

#define mca XBYTE[0x7f0a] //寄存器 A

#define mcb XBYTE[0x7f0b] //寄存器 B

#define mcc XBYTE[0x7f0c] //寄存器 C

#define mcd XBYTE[0x7f0d] //寄存器 D

void initstat(void); //函数声明

void write_mc(void);

void rread_mc(void);

unsigned char date[6]=06,3,8,18,11,15; //06 年 3 月 8 日 18 点 11 分 15 秒

unsigned char ymd[6]=9,8,7,4,2,0; //年、月、日、时、分、秒的地址

unsigned char xdata *addr=0x7f00; //片选地址

void main(void)

write_mc();

initstat();

while(1)

read_mc();

//此处可加显示代码,把时间信息显示出来

void initstat(void) //初始化函数

mca=0x2f; //时钟频率为 32.768kHz,中断周期为 500ms

ACC=mcc; //读寄存器 C,清零中断请求标志

ACC=mcd; //读寄存器 D,使 VRT=1,数据有效

mcb=0x76; //允许中断,时标为二进制码,24 小时方式

EA=1; //开中断

EX0=1;

Page 224: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 224 .

12.2 单总线温度传感器 DS18B20

DS18B20是 DALLAS公司生产的一种“单总线”温度传感器,它采用独特的单线接口方

式,仅需一个端口引脚来发送或接收信息;每个 DS18B20都有一个唯一的 ROM序列号,所

以可以将多个DS18B20 同时连在一根单总线上,进行简单的多点分布应用。DS18B20引脚排

列下见图12-3(a)。

VDD

8051 VDD

温度 低字节

温度 高字节

TH/用户字节1

TL/用户字节2

保留(10H)

保留(FFH)

CRC

配置寄存器

保留(0CH)

DS18B20

P1.6

GND

DQ

+5V

4.7K

GND DQ

DALLAS18B20

0

1

2

3

4

5

6

7

8

图a 图b

图c

图 12-3 DS18B20 引脚、与单片机接口及内部寄存器

各引脚功能如下:

GND:地。

DQ:单线应用的数据输入/输出引脚。

VDD:可选的外部供电电源引脚。

DS18B20内部有三个主要数字部件:64位激光ROM,温度传感器,非易失性温度报警触

发器TH 和 TL。DS18B20可用外部 3-5.5V电源供电,接线方式如图 12-3(b)。

void write_mc(void) //写时标函数

unsigned char j;

mcb=0x80; //使 SET=1

for(j=0;j<10;j++) //设时标初值

*(addr+ymd[j])=date[j];

void read_mc(void) //读时标函数

unsigned char j;

do ACC=mca; while(ACC>=0x80) //判断 UIP 位

for(j=0;j<6;j++) //读时标寄存器的值

date[j]=*(addr+ymd[j];

Page 225: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 225 .

DS18B20依靠一个单线端口通信,必须先建立ROM 操作协议,才能进行存储器和控制操

作。因此,MCU必须首先提供下面 5个 ROM 操作命令之一:

读出ROM,代码为 33H,用于读出DS18B20的序列号,即 64位激光 ROM代码;

匹配ROM,代码为 55H,用于辨识(或选中)某一特定的DS18B20进行操作;

搜索ROM,代码为 F0H,用于确定总线上的节点数以及所有节点的序列号;

跳过ROM,代码为 CCH,命令发出后系统将对所有 DS18B20进行操作,通常用于启

动DS18B20 转换之前,或系统中只有一个DS18B20时;

报警搜索,代码为 ECH,主要用于鉴别和定位系统中超出程序设定的报警温度界限

的节点。

这些命令对每个器件的激光 ROM部分进行操作,在总线挂有多个器件时,可以区分出

单个器件,同时可以向总线上的MCU 指明有多少个器件或是什么型号的器件。

DS18B20内部存储器映像见图 12-3(c),存储器由一个高速暂存器和一个存储高低报

警触发值 TH和 TL的非易失性电可擦除 EERAM 组成。当在单总线上通信时,暂存器帮助确

保数据的完整性。数据先被写入暂存器,并可被读回。数据经过校验后,用一个拷贝暂存

器命令会把数据传到非易失性EERAM 中。这一过程确保更改存储器时数据的完整性。

DS18B20提供了如下存储器操作命令:

温度转换,代码为44H,用于启动 DS18B20进行温度测量,温度转换命令被执行后,

DS18B20保持等待状态。如果 MCU在这条命令之后跟着发出读时间隙,而 DS18B20

又忙于做温度转换的话,DS18B20 将在总线上输出“0”,若温度转换完成,则输出

“1”。

读暂存器,代码为BEH,用于读取暂存器中的内容,从字节0开始最多可以读取9

个字节,如果不想读完所有字节,主机可以在任何时间发出复位命令来终止读取。

写暂存器,代码为 4EH,用于将数据写入到 DS18B20 暂存器的地址 2 和 3(TH 和

TL字节)。可以在任何时刻发出复位命令来终止写入。

复制暂存器,代码为 48H,用于将暂存器的内容复制到DS18B20 的非易失性EERAM

中。即把温度报警触发字节存入非易失性存储器里。如果主机在这条命令之后跟

着发出读时间隙,而 DS18B20又正在忙于把暂存器拷贝到EERAM 存储器,DS18B20

就会输出一个“0”,如果拷贝结束的话,DS18B20则输出“1”。

重读EERAM,代码为 B8H,用于将存储在非易失性 EERAM中的内容重新读入到暂存

器(温度触发器中)。这种复制操作在 DS18B20上电时自动执行,这样器件一上电,

暂存器里马上就存在有效的数据了。若在这条命令发出之后发出读时间隙,器件

会输出温度转换忙的标志:“0”=忙,“1”=完成。

一条温度转换命令启动DS18B20完成一次温度测量,测量结果以 16为带符号位扩展的

二进制补码形式存放在高速暂存器中,占用暂存器的字节 0(LSB)和字节 1(MSB)。用一

条读暂存器内容的存储器操作命令可以把暂存器中的数据读出。温度报警触发器 TH 和 TL

各由一个EEPROM 字节构成,可以用一条写存储器操作命令对TH和 TL 进行写入,对这些寄

存器的读出需要通过暂存器。所有数据都是以最低有效位(LSB)在前的方式进行读写。

DS18B20的温度分辨率为0.125,其温度数据格式见下表:

D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0

S S S S S 26 25 24 23 22 21 20 2-1 2-2 2- 2-4

Page 226: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 226 .

表12-1为 DS18B20的温度-数据关系。

表 12-1 DS18B20 的温度-数据关系

*上电复位后温度寄存器中的值为+85°C

DS18B20完成一次温度转换后,将温度值和存储在TH 和 TL 中的值进行比较,如果测得

的温度高于TH 过低于TL,将置位器件内部的报警标志,每进行一次测温对该标志进行一次

更新。当报警标志置位时,DS18B20会对报警搜索命令有反应。这样就允许多个 DS18B20并

联在一起同时测温,如果某个地方的温度超过了限定值,报警的器件就会被立即识别出来

并读取起温度值,而不用读未报警的器件。

通过单总线端口访问DS18B20的过程如下:

初始化

ROM操作命令

存储器操作命令

数据处理

DS18B20需要严格的时序协议以确保数据的完整性。协议包括几种单线信号类型:复位

脉冲、存在脉冲、写0、写 1、读0和读 1。所有这些信号,除存在脉冲外,都是由主机发

出的。与 DS18B20之间的任何通信都需要以初始化开始,初始化包括一个由主机发出的复

位脉冲和一个紧跟其后由从机发出的存在脉冲,存在脉冲通知主机 DS18B20 在总线上且已

准备好进行发送和接收数据(适当的ROM 命令和存储器操作命令)。

DS18B20的数据读写是通过时间隙处理位和命令字来完成信息交换的。说明如下:

写时间隙:当主机把数据线从逻辑高电平拉到逻辑低电平的时候,写时间隙开始。

有两种写时间隙,写1 时间隙和写0时间隙。所有写时间隙必须最少持续 60微秒,

包括两个写周期间至少 1 微秒的恢复时间。DQ 引脚上的电平变低后,DS18B20 在

一个15 微秒到60微秒的时间内对 DQ引脚采样。如果 DQ 引脚是高电平,就是写1,

如果DQ 引脚是低电平,就是写 0。主机要生成一个写 1 时间隙,必须把数据线拉

到低电平然后释放,在写时间隙开始后的 15微秒内允许数据线拉到高电平。主机

要生成一个写0 时间隙,必须把数据线拉到低电平并保持60微秒。

读时间隙:从DS18B20 读取数据时,主机生成读时间隙。当主机把数据线从逻辑

3

温度() 数据(二进制) 数据(十六进制)

+125°C 0000 0111 1101 0000 07D0h

+85°C* 0000 0101 0101 0000 0550h

+25.0625°C 0000 0001 1001 0001 0191h

+10.125°C 0000 0000 1010 0010 00A2h

+0.5°C 0000 0000 0000 1000 0008h

0°C 0000 0000 0000 0000 0000h

-0.5°C 1111 1111 1111 1000 FFF8h

-10.125°C 1111 1111 0101 1110 FF5Eh

-25.0625°C 1111 1110 0110 1111 FE6Fh

-55°C 1111 1100 1001 0000 FC90h

Page 227: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 227 .

高电平拉到逻辑低电平的时候,读时间隙开始。数据线必须保持至少 1 微秒。从

DS18B20输出的数据在读时间隙的下降沿出现后 15微秒内有效,。因此,主机在读

时间隙开始后必须停止把 DQ引脚驱动为低电平 15微秒,以读取 I/O脚状态。在

读时间隙的结尾,DQ 引脚将被外部上拉电阻拉到高电平。所有读时间隙最少必须

为60 微秒,包括两个读周期时间和至少1微秒的恢复时间。

下面给出DS18B20的温度检测驱动程序,单片机的P1.6引脚用作单总线接口(MCU2007-

I型点片机开发板就采用图 12-3b接法,适用此程序)。

#include<reg52.h>

#include <intrins.h>

sbit a_138=P1^2;

sbit b_138=P1^3;

sbit c_138=P1^4;

sbit cs_138=P2^6;

sbit cs_373=P1^5;

unsigned code zima1[10]=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90;

sbit DQ =P1^6; //定义单总线通信端口

void delay(void);

void delay1(unsigned char a);

void longdelay(unsigned char ms);

void xianshi(void);

void Init_DS18B20(void);

void ReadOneChar(void);

void WriteOneChar(unsigned char dat);

unsigned int ReadTemperature(void);

void write(unsigned int i);

void display(unsigned char pos, unsigned char num,bit dp) //显示函数声明

void led3(unsigned char ); //显示字符型数据函数声明

void main()

cs_138=1;

cs_373=1;

P1=0xbf;

while(1)

unsigned char i;

i=ReadTemperature(); //读温度

led3(i); // 把读取的温度值显示出来。

Page 228: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 228 .

void longdelay(unsigned char ms) // 延时子程序

unsigned char i ;

while(ms--)

for(i = 0;i<250;i++) ;

void delay1(unsigned char a) //延时函数

while(a--);

void delay(void)

unsigned char i;

for(i=0;i<20;i++);

void Init_DS18B20(void) //初始化函数

unsigned char x=0;

DQ = 1; //DQ 复位

delay1(8); //稍做延时

DQ = 0; //单片机将 DQ 拉低

delay1(80); //精确延时 大于 480us

DQ = 1; //拉高总线

delay1(14);

x=DQ; //如果 x=0 则初始化成功 x=1 则初始化失败

delay1(20);

ReadOneChar(void) //读一个字节

unsigned char i=0;

unsigned char dat=0;

Page 229: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 229 .

for (i=8;i>0;i--)

DQ = 0; // 给脉冲信号

dat>>=1;

DQ = 1; // 给脉冲信号

if(DQ)

dat|=0x80;

delay1(4);

return(dat);

void WriteOneChar(unsigned char dat) //写一个字节

unsigned char i=0;

for (i=8; i>0; i--)

DQ = 0;

DQ = dat&0x01;

delay1(5);

DQ = 1;

dat>>=1;

unsigned int ReadTemperature(void) //读取温度

unsigned char a=0;

unsigned char b=0;

unsigned int t=0;

float tt=0;

Init_DS18B20();

WriteOneChar(0xCC); // 跳过读序号列号的操作

WriteOneChar(0x44); // 启动温度转换

Init_DS18B20();

WriteOneChar(0xCC); //跳过读序号列号的操作

WriteOneChar(0xBE); //读取温度寄存器等(共可读 9个寄存器)

a=ReadOneChar(); //前两个就是温度

b=ReadOneChar();

t=b;

Page 230: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 230 .

12.3 语音芯片 ISD4004及其应用

美国 ISD公司生产的语音录放电路系列品种齐全,不仅有单片6~20s、32~120s和 60~

240s的,还有非单片时间长达 1h(外接存储器)的语音电路。ISD4000系列中的4002、4003

和 4004 单片录放时间分别为2~4min、4~8min和 8~16min。

t<<=8;

t=t|a;

t=t/16;

return(t);

void display(uchar pos,uchar num,bit dp) //显示函数定义

if(dp==1)

P0=zima1[num]+0x80; //显示小数点

else

P0=zima[num]; //不显示小数点

switch(pos)

case 0:P1=0xbf;break; //位置 0,对应最左边数码管

case 1:P1=0xbb;break; //位置 1

case 2:P1=0xb7;break; //位置 2

case 3:P1=0xb3;break; //位置 3

case 4:P1=0xaf;break; //位置 4

case 5:P1=0xab;break; //位置 5

case 6:P1=0xa7;break; //位置 6

case 7:P1=0xa3;break; //位置 7,对应最右边数码管

default:break;

void led3(unsigned char num) //从位置 0开始显示三位数

display(2,num%10,0);

display(1,num/10%10);

display(0,num/100);

Page 231: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 231 .

ISD4004采用“直接模拟量存储(DAST)”专利技术,信号无需经过 D/A、A/D转换、数

字压缩和语音合成等复杂的数字信号处理过程,这样减少了失真、音质好。由于 ISD4004

内部含有大容量的闪存,一个芯片就能实现长达 16min的录音和放音。外围电路简单,体

积小。3V 单电源供电,耗电低,维持电流仅 1µA,可以和微控制器接口。根据采样频率的

不同,相应的录放时间有8min、10min、12min、16min 几种。

12.3.1 引脚功能描述

ISD4004引脚排列如图 12­4所示,各引脚功能如下 。

1234567891011121314

2728

151617181920212223242526

SSMOSIMISOVSSDNC

NC

NCNCNC

NCVSSAVSSA

AUD OUTAM CAP NC

ANA IN-

ANA IN+VCCANC

NCNCNCVSSA

RACINT

XCLKVCCDSCLK

ISD4004

图 12­4 ISD4004引脚图

VCCA、VCCD:电源。为使噪声最小,芯片的模拟和数字电路使用不同的电源总线,

并且分别引到外封装的不同管脚上。模拟和数字电源端最好分别走线,尽可能在靠近供电

端处相连,而去耦电容应尽量靠近器件。

VSSA、VSSD:地线。芯片内部的模拟和数字电路也使用不同的地线。

ANA IN+:同相模拟输入。这是录音信号的同相输入端。输入放大器可用单端或差分

驱动。单端输入时,信号由耦合电容输入,最大幅度为峰峰值 32mV,耦合电容和本端的

3k欧电阻的输入阻抗决定了芯片频带的低端截止频率。差分驱动时,信号最大幅度为峰峰

值 16mV。ANA IN­:反相模拟输入。差分驱动时,这是录音信号的反相输入端。信号通过耦合电

容输入,最大幅度为峰峰值 16mV。AUD OUT:音频输出。可驱动 5k欧的负载。

SS:片选信号。此端为低,即向该 ISD4004芯片发送指令,两条指令之间为高电平。

MOSI:串行输入。主控制器应在串行时钟上升沿之前半个周期将数据放到本端,供 ISD输入。

MISO:串行输出。ISD未选中时,本端呈高阻态。

SCLK:ISD的时钟输入端,由主控制器产生,用于同步 MOSI 和 MISO的数据传输。

数据在 SCLK上升沿锁存到 ISD,在下降沿移出 ISD。

Page 232: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 232 .

INT:中断。本端为漏极开路输出。ISD在任何操作中检测到 EOM或OVF 时,本端变

低并保持。中断状态在下一个 SPI周期开始时清除。OVF标志:指示 ISD的录、放操作已

到达存储器的末尾。EOM标志:只在放音中检测到内部的 EOM标志时,此状态位才置 1。RAC:行地址时钟。漏极开路输出。每个 RAC周期表示 ISD存储器的操作进行了一行

(ISD4004系列中的存储器共 2400行)。该信号保持电平为 175ms,低电平为 25ms。快进

模式下,RAC的高电平为 218.75µs,低电平为 31.25µs。该端可用于存储管理技术。

XCLK:外部时钟。内部有下拉元件。芯片内部的采样时钟在出厂前已调校好,误差在

+1%内。若要求更高精度,可从本端输入外部时钟。在不外接时钟时,本端必须接地。

AMCAP:自动静噪。当录音信号电平下降到内部设定的某一阈值以下时,自动静噪功

能使信号衰弱,这样有助于消除无信号(静音)时的噪声。通常本端对地接 1mF的电容,

构成内部信号电平峰值检测电路的一部分。本端接VCCA 则禁止自动静噪。

12.3.2 工作原理与功能特性

1、SPI协议

ISD4004工作于 SPI串行接口。SPI协议是一个同步串行数据传输协议,该协议假定微

控制器的 SPI移位寄存器在 SCLK的下降沿动作,因此对 ISD4004而言,在时钟上升沿锁

存MOSI 引脚的数据,在下降沿将数据送至MIDSO 引脚。协议的具体内容是:

1)所有串行数据传输开始于 SS下降沿。

2) SS在传输期间必须保持为低电平,在两条指令之间则保持为高电平。

3)数据在时钟上升沿移入,在下降沿移出。

4) SS变低,输入指令和地址后,ISD才能开始录放操作。

5)指令格式是:5位控制码+11 位地址码。

6) ISD的任何操作如果遇到 EOM或OVF,则产生一个中断,该中断状态在下一个

SPI周期开始时被清除。

7)使用“读”指令使中断状态位移出 ISD的MISO 引脚时,控制及地址数据也应同

步从MOSI 移入。因此要注意移入的数据是否与器件当前进行的操作兼容。当然,

也允许在一个 SPI周期里,同时执行读状态和开始新的操作。

8)所有操作在运行位(RUN)置 1时开始,置 0时结束。

9)所有指令都在 SS端的上升沿开始执行。

2、SPI接口指令

SPI接口指令见表 12­2。

表 12­2 SPI指令接口表

指 令 8位控制码(16位地址) 说 明

POWERUP 00100XXX(XXXXXXXXXXXXXXXX) 上电:等待 TPUD后器件可以工作

Page 233: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 233 .

需要注意的是,快进只能在放音操作开始时选择。

3、SPI端口的控制位

MISO

MOSI

OVF EOM P0 P1 P2 P3 P4 ... ... P15 0 0 0 0 0 0

C4 C3 C2 C1 C0 X X A15 ... ... A4 A3 A2 A1 A0X

C0:信息检索。

C1:忽略地址位。

C2:上电。

C3:录/放。

C4:运行。

4、操作顺序

1)信息快进

用户不必知道信息的确切地址,就能快进跳过一条信息。信息快进只用于放音模式。

放音速度是正常速度的 1600 倍,遇到 EOM后停止,然后内部地址计数器加 1,指向下条

信息的开始处。

2)放音的上电顺序

器件延时 TPUD(8kHz采样时,约为 25ms)后才能开始操作。因此,用户发完上电指令

后,必须等待 TPUD,才能发出一条操作指令。例如,从 00 处放音,应遵循以下时序:

发 POWER UP指令;

等待 TPUD(上电延时);

发地址值为 00 的 SETPLAY指令;

发 PLAY命令。

器件会从 00 地址开始放音,当出现 EOM时,立即中断,停止放音。

3)录音的上电顺序

如果从 00 处录音,则按以下时序进行:

发 POWER UP指令;

等待 TPUD(上电延时);

发 POWER UP指令;

等待 2 倍 TPUD;

SET PLAY 11100XXX(A15­­­­A0) 从指定地址放音,必须后跟 PLAY指令使放音继续

PLAY 11110XXX(XXXXXXXXXXXXXXXX) 从当前地址放音(直至 EOM或 OVF)

SET REC 10100XXX(A15­­­­A0) 从指定地址录音,必须后跟 REC指令使录音继续

REC 10110XXX(XXXXXXXXXXXXXXXX) 从当前地址录音(直至 OVF或停止)

SETMC 11101XXX(A15 ­­­­A0) 从指定地址快进,必须后跟MC指令使快进继续

MC 11111XXX(XXXXXXXXXXXXXXXX) 执行快进直至 EOM,若再无信息则进入 OVF状态

STOP 0X110XXX(XXXXXXXXXXXXXXXX) 停止当前操作

STOPWRDN 0X01XXXX(XXXXXXXXXXXXXXXX) 停止当前操作并掉电

RINT 0X110XXX(XXXXXXXXXXXXXXXX) 读状态:OVF和 EOM

Page 234: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 234 .

发地址值为 00 的 SETREC指令;

发 REC命令。

器件便从 00 地址开始录音,一直到出现OVF(存储器末尾)时,录音停止。

12.3.3 放音应用

由于在实际应用领域大多是播放声音,如公共汽车的到站提示音、儿童玩具的音乐、

电话查询系统的提示音等,所以这里只介绍用ISD4004芯片组成的放音系统。

用ISD4004组成具有声音提示或音乐播放系统非常简单,只要事先用ISD4004配套的下

载软件及编程器将录制好的声音文件下载到ISD4004芯片中,并记录各声音存放的地址,就

可以轻松的用程序控制任意地址处的语音进行播放。

ISD4004的放音电路如图12­5所示。当然要播放声音必须在 ISD4004的输出端外接功放

电路和扬声器,功放电路有很多可以参考,取决于需要接多大功率的扬声器,这里不详细

给出,LM386芯片是一款功率放大芯片。

注意,ISD4004芯片的电源电压应该是3V,所以请勿直接将其接至5V电源,否则无法

正常工作。可以使用1117(3.3v)电压转换芯片将5V电压降至3V来给ISD4004供电,还要注

意单片机系统与ISD4004应共地。

GND XCLKVSSA

VSSAVSSA

VSSDIN+ AUD OUT

MCU

ISD4004

SSSCLKMOSIMISOINT

P1.0P1.1P1.2P1.3P3.4

3.3V

VCCA

VCCD

5V

LM386

3

2

4

6

5

图12­5 ISD4004应用电路

设要实现的功能为:系统上电就播放地址0x0000处的提示音,播放完毕自动播放地址

为0x0030处的音乐,播放完毕后停止。

程序编制如下:

#include<reg52.h>

#include <intrins.h>

sbit SS=P1^0; //ISD4004 片选

sbit SCLK=P1^1; //ISD4004 时钟输入端

sbit MOSI=P1^2; //ISD4004 串行输入

sbit yuyin_int =P3^4; //语音芯片中断引脚

void DSTOP(void); //停止当前操作并掉电

void STOPP(void); //停止当前操作

Page 235: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 235 .

void UP(void); //上电

void ISDX(unsigned char d); //SPI 写入函数

void YS50(void); //延时函数

void main(void)

UP(); //ISD4004 上电

YS50(); //延时等待

bofang(0x0000); //播放提示音

while(yuyin_int); //等待播放完毕

bofang(0x0030); //播放音乐

while(yuyin_int); //等待播放完毕

DSTOP(); //ISD4004 停止并掉电

while(1);

void bofang(unsigned int bf) //播放声音函数,bf 为要播放的声音地址

unsigned char isdl, isdh;

isdl=bf%256;

isdh=bf/256;

SS=1;

ISDX(isdl);

ISDX(isdh); //发送声音地址

ISDX(0xe0); //SET PLAY 指令

SS=1;

ISDX(0xf0); //PLAY 指令

SS=1;

void YS50(void) //延时函数

unsigned int a;

a=10000;

while(a--);

void ISDX(unsigned char d) //SPI 写入函数

unsigned char i,j;

Page 236: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 236 .

SS=0;

SCLK=0;

j=d;

for(i=0;i<8;i++)

if(j&0x01)

MOSI=1;

else

MOSI=0;

_nop_();

_nop_();

SCLK=1;

_nop_();

_nop_();

_nop_();

j=j>>1;

SCLK=0;

void UP() //上电

ISDX(0x20); //POWERUP 指令

SS=1;

YS50();

YS50();

void STOPP() //停止当前操作

ISDX(30); //STOP 指令

SS=1;

YS50();

YS50();

void DSTOP() //停止当前操作并掉电

ISDX(10); //STOP WRDN 指令

SS=1;

Page 237: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 237 .

第 13 章 I2C 总线

13.1 I2C 总线简介

I2C (Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,

用于连接微控制器及其外围设备。I2C总线产生于20世纪80年代,目前在视频处理、移动通

信等领域采用12C总线接口器件已经比较普遍。另外,通用的I2C总线接口器件,如带I2C总

线的单片机、RAM、ROM、A/D、D/A、LCD驱动器等器件,也越来越多地应用于计算机及自动

控制系统中。

I2C总线通过SDA(串行数据线)及SCL(串行时钟线)两根线在连到总线上的器件之间

传送信息,并根据地址识别每个器件。

13.1.1 I2C总线的原理

采用I2C总线标准的单片机或IC器件,其内部不仅有I2C接口电路,而且将内部各单元电

路按功能划分为若干相对独立的模块,通过软件寻址实现片选,减少了器件片选线的连接。

CPU不仅能通过指令将某个功能单元电路挂靠或摘离总线,还可对该单元的工作状况进行检

测,从而实现对硬件系统的既简单又灵活的扩展与控制。I2C总线接口电路结构如图13-1所

示。

YS50();

YS50();

Page 238: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 238 .

SDA

SCL

RRVCC

SDA出

SDA入 SCL入

SCL出 SDA出

SDA入

SCL出

SCL入器件1 器件n

...

图13-1 I2C总线接口电路结构

传统的单片机串行接口的发送和接收一般都各用一条线,如MCS51系列的TXD和RXD。而

I2C总线则根据器件的功能通过软件程序使其可工作于发送或接收方式。当某个器件向总线

上发送信息时,它就是发送器(也叫主器件),而当其从总线上接收信息时,又成为接收

器(也叫从器件)。主器件用于启动总线上传送数据并产生时钟以开放传送的器件,此时

任何被寻址的器件均被认为是从器件。I2C总线的控制完全由挂接在总线上的主器件送出的

地址和数据决定,在总线上,既没有中心机,也没有优先级。

总线上主和从(即发送和接收)的关系不是一成不变的,取决于此时数据传送的方向。

SDA和SCL均为双向I/O线,通过上拉电阻接正电源。当总线空闲时,两根线都是高电平。连

接总线的器件的输出级必须是集电极或漏极开路,以具有线“与”功能。I2C总线的数据传

送速率在标准工作方式下为100Kbit/s,在快速方式下,最高传送速率可达400Kbit/s。

I2C总线最主要的特点是其简单性和有效性。由于接口直接在组件之上,因此I2C总线占

用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互连成本。总线的长度

可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件。I2C总线的另一个优点是,

它支持多主控(multimastering),其中任何能够进行发送和接收的设备都可以成为主总线。

一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。

13.1.2 I2C总线上的数据传送

在I2C总线上传送信息时的时钟同步信号是由挂接在SCL时钟线上的所有器件的逻辑

“与”完成的。SCL线上由高电平到低电平的跳变将影响到这些器件,一旦某个器件的时钟

信号变为低电平,将使SCL线一直保持低电平,使SCL线上的所有器件开始低电平期。此时,

低电平周期短的器件的时钟由低至高的跳变并不能影响SCL线的状态,于是这些器件将进入

高电平等待的状态。

当所有器件的时钟信号都变为高电平时,低电平期结束,SCL线被释放返回高电平,即

所有的器件都同时开始它们的高电平期。其后,第一个结束高电平期的器件又将SCL线拉成

低电平。这样就在SCL线上产生一个同步时钟。可见,时钟低电平时间由时钟低电平期最长

的器件确定,而时钟高电平时间由时钟高电平期最短的器件确定。

在I2C总线上,就像电话机一样只有拨通各自的号码才能工作,所以每个电路和模块都

有惟一的地址,在信息的传输过程中,I2C总线上并接的每一模块电路既是主控器(或被控

Page 239: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 239 .

器),又是发送器(或接收器),这取决于它所要完成的功能。CPU发出的控制信号分为地

址码和控制量两部分,地址码用来选址,即接通需要控制的电路,确定控制的种类;控制

量决定该调整的类别(如对比度、亮度等)及需要调整的量。这样,各控制电路虽然挂在

同一条总线上,却彼此独立,互不相关。

I2C总线在传送数据过程中共有三种类型信号:开始信号、结束信号和应答信号。

开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。

结束信号:SCL为低电平时,SDA由低电平向高电平跳变,结束传送数据。

应答信号:接收数据的IC在接收到8b数据后,向发送数据的IC发出特定的低电平

脉冲表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,

CPU接收到应答信号后,根据实际情况做出是否继续传递信号的判断。若未收到应答信号,

由判断为受控单元出现故障。

在数据传送过程中,必须确认数据传送的开始和结束。在I2C总线技术规范中,开始和

结束信号(也称启动和停止信号)的定义如图13-2所示。开始和结束信号都是由主器件产

生。在开始信号以后,总线即被认为处于忙状态。在结束信号以后的一段时间内,总线被

认为是空闲的。

SDA

SCL

SDA

SCL

启动 停止

图13-2 I2C总线开始和结束信号定义

I2C总线的数据传送格式是在IZC总线开始信号后,送出的第一字节数据是用来选择从器

件地址的,其中前7位为地址码,第8位为方向位(R/W)。方向位为“0”表示发送,即主器

件把信息写到所选择的从器件中;方向位为“1”表示主器件将从从器件读信息。开始信号

后,系统中的各个器件将自己的地址和主器件送到总线上的地址进行比较,如果与主器件

发送到总线上的地址一致,则该器件即为被主器件寻址的器件,其接收信息还是发送信息

则由第8位(R/W)确定。

在I2C总线上每次传送的数据字节数不限,但每一字节必须为8位,而且每个传送的字节

后面必须跟一个认可位(第9位),也叫应答位(ACK)。数据的传送过程如图13-3所示。

每次都是先传最高位,通常从器件在接收到每字节后都会做出响应,即释放SCL线返回

高电平,准备接收下一个数据字节,主器件可继续传送。如果从器件正在处理一个实时事

件而不能接收数据时(例如正在处理一个内部中断,在这个中断处理完之前就不能接收I2C

总线上的数据字节),可以使时钟SCL线保持低电平,从器件必须使SDA保持高电平。此时

主器件产生1个结束信号,使传送异常结束,迫使主器件处于等待状态。当从器件处理完毕

时释放SCL线,主器件继续传送。

Page 240: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 240 .

SDA

SCL数据

稳定、有效数据变换

图13-3 I2C总线的数据传送过程

当主器件发送完一字节的数据后,接着发出对应于SCL线上的一个时钟(ACK)认可位,

此时钟内主器件释放SDA线,一字节传送结束,而从器件的响应信号将 SDA线拉成低电平,

使SDA在该时钟的高电平期间为稳定的低电平。从器件的响应信号结束后, SDA线返回高电

平,进入下一个传送周期。

I2C总线还具有广播呼叫地址用于寻址总线上所有器件的功能。若一个器件不需要广播

呼叫寻址中所提供的任何数据,则可以忽略该地址不作响应。如果该器件需要广播呼叫寻

址中提供的数据,则应对地址做出响应,其表现为一个接收器。

13.1.3 I2C总线竞争的仲裁

I2C总线上可能挂接多个器件,有时会发生两个或多个主器件同时想占用总线的情况。

例如,

多单片机系统中,可能在某一时刻有两个单片机要同时向总线发送数据,这种情况叫做总

线竞争。I2C总线具有多主控能力,可以对发生在SDA线上的总线竞争进行仲裁,其仲裁原则

是这样的:当多个主器件同时想占用总线时,如果某个主器件发送高电平,而另一个主器

件发送低电平,则发送电平与此时SDA总线电平不符的那个器件将自动关闭其输出级。总线

竞争的仲裁是在两个层次上进行的。首先是地址位的比较,如果主器件寻址同一个从器件,

则进入数据位的比较,从而确保了竞争仲裁的可靠性。由于利用I2C总线上的信息进行仲裁,

因此不会造成信息的丢失。

13.1.4 模拟 I2C总线

目前在仪器仪表、移动通信、密码控制等领域采用I2C 总线接口器件已经比较普遍。另

外,通用的I2C总线接口器件,如带 I2C 总线的单片机、RAM、ROM、A/D、D/A、LCD驱

动器等器件,也越来越多地应用于计算机及自动控制系统中。

具有I2C接口的单片机在使用 I2C 总线时非常方便,只需将数据发送到I2C接口,数据

就自动以I2C数据形式传送出去了。但是大多数单片机是没有集成I2C总线接口的,所以一

般用单片机的普通 I/O口来模拟 I2C 总线,也就是用两个 I/O 口来模拟SDA 和 SCL。I2C 数

据的形成、发送与接收都用软件来实现,因此程序编制就比较复杂。这里不单独给出模拟

I2C 总线的程序,具体的程序实现参下两个小节 。

Page 241: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 241 .

13.2 EEPROM存储器 24C02的应用

串行EEPROM 中,较为典型的有ATMEL 公司的AT24CXX 系列和AT93CXX 等系列产品,简称I2C

总线式串行器件。串行器件不仅占用很少的资源和 I/O 线,而且体积大大缩小,同时具有

工作电源宽、抗干扰能力强、功耗低、数据不易丢失和支持在线编程等特点。 I2C总线是

一种用于IC 器件之间连接的二线制总线。它通过SDA(串行数据线)及 SCL(串行时钟线)

两根线在连到总线上的器件之间传送信息,并根据地址识别每个器件:不管是单片机、存

储器、LCD驱动器还是键盘接口。 串行 E2PROM 是基于I2C-BUS 的存储器件,遵循二线制

协议,由于其具有接口方便,体积小,数据掉电不挥发等特点,在仪器仪表及工业自动化

控制中得到大量的应用。

13.2.1 24C02 的功能

1、24C02的性能

24C02与单片机的接口非常简单。A0,A1,A2 为器件地址线,WP 为写保护引脚,SCL,

SDA为二线串行接口,符合 I2C总线协议。在一般单片机系统中,24C02 数据受到干扰的情

况是很少的,以前通过简单的器件之间替换比较,发现不同牌号的 24C02 其抗干扰性能是

不一样的,于是就认定24C02器件存在"质量"好坏的问题。发现有些24C02 的 WP引脚并不

起到保护作用,也就是说将 WP引脚与 CPU 输出引脚断开并保持高电平的情况下,CPU仍然

能够对24C02 中的数据进行修改写入!

2、 24C02 器件的应用

无论是智能仪器仪表还是单片机工业控制系统都要求其数据能够安全可靠而不受干

扰,特别是一些重要的设定参数(如温度控制设定值)受到干扰后变成一个很大的数字,

那么就有可能发生烧箱毁物的破坏性后果,给生产和经济带来损失,因此必须选用可靠的

24C02器件作为数据储存单元。

对于只用一片 24C02器件的系统,因为不需要分辨不同的地址(我们采用的 I2C总线

控制的ADC0832 及 PCF8563,因此需要区分地址),只要 WP 保护功能正常就可以了,这只要

断开WP 与 CPU连线且保持高电平,再试一下系统数据读写功能是否正常就可以了。而这一

点对软件抗干扰技术也是至关重要的。一般来说,同种牌号的 24C02 器件性能是一样的;

对于有2 片 24C02 以上的系统,必须严格检查其器件寻址功能,这可以轮流拨下其中一片

24C02器件,检查相应的数据存取功能,若没有交叉出错现象则可以选用。

24C02的 1、2、3脚是三条地址线,用于确定芯片的硬件地址(MCU2007-I型开发板中

直接接地,因只有一个器件);第 8脚和第4 脚分别为正、负电源。第5脚 SDA为串行数据

输入/输出,数据通过这条双向 I2C 总线串行传送,SDA 和 SCL 都需要和正电源间各接一个

5.1K的电阻上拉。第 7脚为 WP写保护端,接地时允许芯片执行一般的读写操作。接电源端

时不允许对器件写。

Page 242: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 242 .

3. 引脚说明

图 13­4是 24C02芯片的引脚排列。

A0

A1A2

VSS SDASCL

WP

VCC1

23

4 5

67

8

24C02

图13-4 24C02引脚

管脚说明:

(1) SDA:串行地址/数据输入/输出端

这是一个双向传输端,用于传送地址和数据进入器件或从器件发出数据。它是一个漏

路端,因此需要一个上拉电阻(一般为 4.7K)到 VCC端。对于一般的数据传输 SCL为低电

平期间,SDA才允许变化。在 SCL为高电平期间SDA 变化,留给指示开始和停止条件。

(2) SCL:串行时钟端

此输入端用于同步传输进入和发出器件的数据。

(3) WP端

此端必须接到 VCC或 GND 端,当接到 VCC时,EEPROM 只能用于读数据,写操作禁止;

当接到GND 时,为可读可写存储器。我们在应用时通常接在GND端。

(4) A0,A1,A2:器件/页面寻址

这些端没有被 24C02 应用,因此可以接在 VCC 或 GND 上,我们的开发板是接在 GND 上

的。24C02中带有片内地址寄存器。每写入或读出一个数据字节后,该地址寄存器自动加 1,

以实现对下一个存储单元的读写。所有字节均以单一操作方式读取。为降低总的写入时间,

一次操作可写入多达8 个字节的数据。

(5)VCC和 VSS:分别为正电源(一般为+5V)和地。

13.2.2 24C02 的总线特性

1、24C02的总线规程

仅当总线不忙时,数据才能开始传送;

在数据传送期间,无论何时时钟线为高,数据线必须保持稳定。当时钟线为高时,数

据线的变化将认为是传送的开始或停止。

2、总线条件的工作过程

(1)数据线(SDA)和时钟线(SCL)保持高时总线不忙;

(2)开始传送数据:时钟(SCL)为高时,数据线(SDA)由高到低的变化为开始条件;所有

的命令在开始之后进行操作;

(3)停止传送条件:时钟(SCL)为高时,数据线(SDA)由低到高的变化为停止条件;所有

的命令在停止之前必须结束;

Page 243: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 243 .

(4)有效数据:在开始条件以后,时钟信号高电平周期期间,当数据线稳定时,数据线

的状态为有效状态;在时钟信号为低电平期间,数据线的状态必须改变,每位数据需要一

个时钟脉冲。

(5)确认:在接收每个字节后,当寻址时,每个接收器必须产生一个确认位,主器件必

须产生一个与此相关的额外时钟脉冲。注意:如果内部的编程周期正在进行,不能产生确

认位。

6)器件寻址和操作:当I2C 总线工作时,必须根据相应的地址进行操作,24C02的读地

址为0xA1;写地址为 0xA0。

3、器件寻址

24C02在开始状态后需紧跟一个 8位器件地址,以进行应读写操作。设备寻址码的高 4

位为1010,高 4位后面的三位是器件寻址码,与 A2、A1、A0的管脚电平相对应。最低位是

读写选择位,置0 时刻激发读操作。

13.2.3 硬件连接及程序

24C02与单片机的连接方式,我们采用图 13-5所示电路,也就是 MCU2007-I 型开发板

所采用的方式。

24C02的应用程序如下:(程序功能为:先向 24C02写入一些数据,然后再将这些数据

读出,仅为示例程序。具体应用参第16 章的“投篮游戏机程序”)

图 13-5 24C02 接口电路

#include<reg52.h>

#include <intrins.h>

#define write 0xA0; //24C02 的写地址

#define read 0xA1; //24C02 的读地址;

sbit SCL=P3^4;

sbit SDA=P3^5;

unsigned char dat_wr[8],dat_rd[8];//要写入的数据数组及保存读出数据的数组

Page 244: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 244 .

int WriteEE(unsigned char SubAdr,unsigned char Num,unsigned char *Wbuf);//24C02 写入函

int ReadEE(unsigned char SubAdr,unsigned char Num,unsigned char *Rbuf);//24C02 读出函数

/***以下函数是任何没有 IIC 总线接口的单片机在与 IIC 总线接口器件进行数据传送时都必须有的

函数,即模拟 IIC 总线程序***/

int SendByte(unsigned char a); //IIC 的字节发送程序

unsigned char ReadByte(void); //IIC 的字节读取程序

void SendACK(void); //发送应答

void SendNoACK(void); //应答

void Start(void); //IIC 总线开始

void Stop(void); //IIC 总线停止

void Delay2(unsigned int x); //延时函数

main() //主程序

unsigned int i;

for(i=0;i<8;i++)

dat_wr[i]=i+1; //将要写入的数据赋初值 1~8

WriteEE(0,8,dat_wr); //将 dat_wr 数组中的数据写入 24C02

ReadEE(0,8,dat_rd); //从 24C02 中读出 8个数据送入 dat_rd 数组

while(1);

/*以下是写入函数。其中 SubAdr 为写入的地址;Num 为要写入的数据个数;*Wbuf 为存放要写入的

数据的数组。此函数功能为:把*Wbuf 数组中的 Num 个数写入到 04C02 中从 SubAdr 地址开始的连续

单元*/

int WriteEE(unsigned char SubAdr,unsigned char Num,unsigned char *Wbuf)

unsigned char data i;

Start(); //发送 I2C 总线起始条件

if(SendByte(0xA0)!=0) return -1; //发送被控器总线地址

if(SendByte(SubAdr)!=0) return -1; //发送写的地址

for(i=0;i<Num;i++) //重复操作直到发送完最后一个数据

if(SendByte(*(Wbuf+i))!=0) return -1;

Stop(); //发送 I2C 总线停止条件

Delay2(10); //延时 10ms,等待数据写完

return 0;

Page 245: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 245 .

/*以下是读出函数。其中 SubAdr 为读出的地址;Num 为要读出的数据个数;*Rbuf 为存放要读出的

数据的数组。此函数功能为:把 04C02 中从 SubAdr 地址开始的连续 Num 个数读入到*Rbuf 数组中*/

int ReadEE(unsigned char SubAdr,unsigned char Num,unsigned char *Rbuf)

unsigned char data i;

Start(); // 发送 I2C 总线起始条件

if(SendByte(0xA0)!=0) return -1; //发送被控器总线写地址

if(SendByte(SubAdr)!=0) return -1; //发送读首地址*/

Start(); //发送 I2C 总线重复起始条件

if(SendByte(0xA1)!=0) return -1; //发送被控器总线读地址

if(Num!=1)

for(i=0;i<Num-1;i++)

*(Rbuf+i)=ReadByte();

SendACK(); //发送应答信号

*(Rbuf+Num-1)=ReadByte();

else

*Rbuf=ReadByte();

SendNoACK(); //最后一个字节,发送非应答信号

Stop(); //发送 I2C 总线停止条件

return 0;

/*字节数据传送子程序发送一个字节数据或地址给被控器*/

int SendByte(unsigned char a)

unsigned char data i,j;

unsigned char bdata Sin;

Sin=a;

for(i=0;i<8;i++)

if((Sin & 0x80)==0)

SDA=0;

else

SDA=1;

for(j=0;j<2;j++);

SCL=1; //置时钟线为高通知被控器开始

for(j=0;j<4;j++); //保证时钟高周期大于 4 s

Page 246: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 246 .

SCL=0;

Sin=Sin<<1;

for(j=0;j<2;j++);

SDA=1;

for(j=0;j<2;j++);

SCL=1;

for(j=0;j<2;j++);

if (SDA==0) SCL=0;return 0; //成功,返回 0

for(j=0;j<1;j++);

SCL=0;

return -1; //未收到应答,返回-1

/*数据接收子程序从被控器接收一个字节数据*/

unsigned char ReadByte(void)

char i,j;

unsigned char bdata Sin;

SDA=1;

for (i=0;i<7;i++)

SCL=0;

for(j=0;j<2;j++);

SCL=1;

if(SDA==1)

Sin|=0x01;

else

Sin&=0xfe;

for(j=0;j<2;j++);

Sin<<=1;

SCL=0;

for(j=0;j<2;j++);

SCL=1;

if(SDA==1)

Sin|=0x01;

else

Sin&=0xfe;

for(j=0;j<2;j++);

Page 247: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 247 .

return(Sin);

/*发送应答位*/

void SendACK(void)

unsigned char data j;

SCL=0;

for(j=0;j<2;j++);

SDA=0;

for(j=0;j<2;j++);

SCL=1;

for(j=0;j<4;j++);

SCL=0;

/*发送非应答位*/

void SendNoACK(void)

unsigned char data j;

SCL=0;

for(j=0;j<2;j++);

SDA=1;

for(j=0;j<2;j++);

SCL=1;

for(j=0;j<4;j++);

SCL=0;

/*START 启动 I2C 总线子程序发送 I2C 起始条件*/

void Start(void)

unsigned char data i;

SDA=1; // 发送起始条件的数据信号

for (i=0;i<2;i++);

SCL=1; //发送起始条件的时钟信号

for(i=0;i<4;i++) ; //起始条件建立时间大于 4.7 s

SDA=0; //发送起始信号*/

for(i=0;i<4;i++); //起始条件锁定时间大于 4 s

SCL=0; //钳住 I2C 总线准备发送或接收数据

Page 248: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 248 .

13.3 I2C 接口的日历时钟芯片 PCF8563

13.3.1 PCF8563 概述

PCF8563是飞利浦公司生产的一款内含I2C总线接口功能的时钟/日历芯片。PCF8563具有

多种报警功能、定时器功能、时钟输出功能以及中断输出功能,能完成各种复杂的定时工

作。因其内部具有时钟电路、振荡电路、低电压检测电路及两线制I2C总线通讯方式,所以

外围电路比较简单。每次读写数据后内部的字地址寄存器会自动产生增量,所以使用非常

方便。

PCF8563内部资源包括:16个8位寄存器,一个可自动增量的地址寄存器,一个内置

32.768KHz 的振荡器,一个分频器(用于给实时时钟RTC提供源时钟),一个可编程时钟输

出,一个定时器,一个报警器,一个掉电检测器和一个最大速率400Kbits/s的I2C总线接口。

PCF8563具有如下特性:

宽电压范围1.0V~5.5V,复位电压标准值Vlow=0.9V。

超低功耗,典型值为0.25µA(VDD=3.0V,Tamb=25)。

可编程时钟输出频率为32.768KHz、1024Hz、32Hz、1Hz。

四种报警功能和定时器功能。

内部具有复位电路、振荡器电容和掉电检测电路。

具有400kHz的I2C总线,其从地址为:读,0A3H;写,0A2H。

PCF8563的引脚排列如图13-6所示,各引脚功能说明如下:

1234 5

678OSCI

OSCOINTVSS SDA

SCLCLKOUTVDD

PCF

8563

图13-6 PCF8563引脚图

/*STOP 停止 I2C 总线子程序发送 I2C 总线停止条件*/

void Stop(void)

unsigned char data i;

SDA=0;

for(i=0;i<2;i++) ;

SCL=1;

for(i=0;i<4;i++)

SDA=1;

for(i=0;i<4;i++);

Page 249: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 249 .

OSCI:振荡器输入。

OSCO:振荡器输出。

:中断输出,低电平有效。IN T

VSS:接地。

SDA:串行数据I/O。

SCL:串行时钟输入。

CLKOUT:时钟输出(开漏)。

VDD:电源正极。

13.3.2 PCF8563 工作原理

PCF8563的所有16个寄存器设计成可寻址的8位并行寄存器,但不是所有位都有用。其

中00H和01H为控制方式寄存器,02H~08H为秒~年的时间寄存器,09H~0CH为报警功能寄

存器,0DH为时钟输出寄存器,0EH和0FH为定时器功能寄存器,各寄存器的位描述见表13-1。

现对一些重要的寄存器位进行介绍。

(1)控制/状态寄存器1

TEST1:模式选择位。TEST1=0为普通模式;TEST1=1为EXT_CLK 测试模式。

STOP:时钟运行控制位。STOP=0,芯片时钟运行;STOP=1,芯片时钟停止运行,但CLKOUT

在32.768kHz时可用。

TESTC:TESTC=0时,电源复位功能失效,普通模式时置逻辑0;TESTC=1时,电源复位

功能有效。

其它位缺省值置逻辑0

表13-1 PCF8563内部寄存器

地址 寄存器名称 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0

00H 控制/状态寄存器1 TEST1 0 STOP 0 TESTC 0 0 0

01H 控制/状态寄存器 2 0 0 0 TI/TP AF TF AIE TIE

02h 秒 VL 00~59 BCD码格式数

03h 分钟 - 00~59 BCD码格式数

04h 小时 - - 00~59 BCD码格式数

05h 日 - - 01~31 BCD码格式数

06h 星期 - - - - - 0~6 对应周日~周六

07h 月/世纪 C - - 01~12 BCD码格式数

08h 年 00~99 BCD码格式数

09h 分钟报警 AE 00~59 BCD码格式数

0Ah 小时报警 AE - 00~23 BCD码格式数

0BH 日报警 AE - 01~31 BCD码格式数

0CH 星期报警 AE - - - - 0~6 对应周日~周六

0DH CLKOUT 输出寄存器 FE - - - - - FD1 FD0

Page 250: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 250 .

注:表明“-”的位为无效位,无“BCD 码格式数”字样的寄存器为二进制格式。

(2)控制/状态寄存器2

TI/TP:TI/TP=0时,当TF有效时INT有效; TI/TP=1时,INT脉冲有效(均取决于TIE 的

状态)。当AF和AIE都有效时则INT一直有效。

AF和TF:当报警发生时,AF被置逻辑1;在定时器倒计数结束时,TF被置逻辑1,它们在被

软件重写前一直保持原有值,若定时器和报警中断都请求时,中断源由 AF和TF决定。若要

使清除一个标志位而防止另一标志位被重写,应运用逻辑指令AND。

AIE和TIE:标志位AIE和TIE决定一个中断的请求有效或无效。当AF或TF中一个为1时,

中断是AIE和TIE都置1时的逻辑或。AIE=0时,报警中断无效;AIE=1时,报警中断有效。TIE=0

时,定时器中断无效;TIE=1时,定时器中断有效。其它位缺省值置逻辑0

(3)秒寄存器

VL:VL=0,保证准确的时钟/日历数据;VL=1,不保证准确的时钟/日历数据。

Bit6~Bit0:代表BCD 格式的当前秒数值,值为00~59。例如1011001 代表 59 秒。

(4)分钟寄存器

Bit7:无效。

Bit6~Bit0:代表BCD格式的当前分钟数值,值为00~59。例如1011000代表58分。

(5)小时寄存器

Bit7和Bit6:无效。

Bit5~Bit0:代表BCD格式的当前小时数值,值为00~23。例如010010代表12点。

(6)日寄存器

Bit7和 Bit6:无效。

Bit5~Bit0:代表BCD 格式的当前日数值,值为 01~31。当年计数器的值是闰年时,

PCF8563 自动给二月增加一个值,使其成为29 天。

(7)星期寄存器

Bit7~Bit3:无效。

Bit2~Bit0:代表当前星期数值,值为0~6。这些位也可由用户重新分配,为二进制

编码格式。Bit2~Bit0编码为000时,表示星期日;Bit2~Bit0编码为001~110时,分别对

应星期一~星期六。

(8)月/世纪寄存器

C:世纪位。C=0,指定世纪数为20XX;C=1,指定世纪数为19XX。“XX”为年寄存器中

的值当年寄存器中的值由99变为00时,世纪位会改变。

Bit6和Bit5:无效。

Bit2~Bit0:代表BCD格式的当前月份值,值为01~12。例如010010代表12月。

(9)年寄存器

Bit7~Bit0:代表BCD格式的当前年数值,值为00~99。例如10010110代表96年。

(10)分钟报警寄存器

0EH 定时器控制寄存器 TE - - - - - TD1 TD0

0FH 定时器倒计数

数值寄存器

定时器倒计数数值(二进制)

Page 251: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 251 .

向一个或多个报警寄存器写入合法的分钟、小时、日或星期数值,并且它们相应的 AE

位为逻辑0,当这些数值与当前的分钟、小时、日或星期数值相等,标志位 AF就会被设置。

AF保存设置值直到被软件清除为止,AF被清除后,只有在时间增量与报警条件再次相匹配

时才可再被设置。报警寄存器在它们相应位AE 置为逻辑时将被忽略。

AE:AE=0,分钟报警有效;AE=1,分钟报警无效。

Bit6~Bit0:代表BCD格式的分钟报警数值,值为00~59。

(11)小时报警寄存器

AE:AE=0,小时报警有效;AE=1,小时报警无效。

Bit6~Bit0:代表BCD格式的小时报警数值,值为00~23。

(12)日报警寄存器

AE:AE=0,日报警有效;AE=1,日报警无效。

Bit6~Bit0:代表BCD格式的日报警数值,值为00~31。

(13)星期报警寄存器

AE:AE=0,星期报警有效;AE=1,星期报警无效。

Bit6~Bit0:代表BCD格式的星期报警数值,值为0~6。

(14)CLKOUT 频率寄存器

FE:FE=0,CLKOUT输出被禁止并设成高阻抗;FE=1,CLKOUT输出有效。

Bit6~Bit2:无效。

FD1和FD0:用于控制CLKOUT的频率输出管脚CLKOUT。具体见表13-2。

表13-2 CLKOUT频率选择表

(15)倒计数定时器寄存器

定时器寄存器是一个8位的倒计数定时器,它由定时器控制器中位TE决定有效或无效。

定时器的时钟也可以由定时器控制器选择,其它定时器功能,如中断产生,由控制状态寄

存器控制。为了能精确读回倒计数的数值,I2C 总线时钟SCL 的频率应至少为所选定定时器

时钟频率的两倍。

TE:TE=0,定时器无效;TE=1,定时器有效。

Bit6~Bit2:无效。

TD1和TD0:定时器时钟频率选择位,决定倒计数定时器的时钟频率。不用时 TD1和TD0

应设为“11”,以降低电源损耗。具体描述见表13-3。

表13-3 定时器时钟频率选择

(16)定时器倒计数数值寄存器

Bit7~Bit0:定时器倒计数数值。若倒计数数值为n,则倒计数周期为n/时钟频率。

FD1 0 0 1 1

FD0 0 1 0 1

CLKOUT 32.768kHz 1024Hz 32Hz 1Hz

TD1 0 0 1 1

TD0 0 1 0 1

时钟频率 4096Hz 64Hz 1Hz 1/60Hz

Page 252: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 252 .

13.3.3 PCF8563 的操作方式

1. 报警功能模式

一个或多个报警寄存器的AE位清时,相应的报警条件有效,这样,一个报警将在每分钟至

每星期范围内产生一次。设置报警标志位AF(控制/状态寄存器2的位3)用于产生中断,AF

只可以用软件清除。

2. 定时器

8位的倒计数器(地址0FH)由定时器控制寄存器(地址0EH)控制,定时器控制寄存器

用于设定定时器的频率,以及设定定时器有效或无效。定时器从软件设置的8位二进制数倒

计数,每次倒计数结束,定时器设置标志位TF。定时器标志位TF只可以用软件清除,TF用

于产生一个中断(INT),每个倒计数周期产生一个脉冲作为中断信号。TI/TP控制中断产

生的条件。当读定时器时,返回当前倒计数的数值。

3. CLKOUT 输出

管脚CLKOUT可以输出可编程的方波。CLKOUT 频率寄存器(地址0DH)决定方波的频率,

CLKOUT为开漏输出管脚,上电时输出有效,无效时输出为高阻抗。

4. 复位

PCF8563包含一个片内复位电路,当振荡器停止工作时,复位电路开始工作。在复位状

态下,I2C总线初始化,寄存器TF、VL、TD1、TD0、TESTC和AE被置逻辑1,其它的寄存器和

地址指针被清0。

5. 掉电检测器和时钟监控

PCF8563内嵌掉电检测器,当 VDD 低于Vlow时,位VL (秒寄存器的位7)被置1,用于

指明可能产生不准确的时钟日历信息。VL标志位只可以用软件清除。当VDD慢速降低(例如

以电池供电)达到Vlow时,标志位VL被设置,这时可能会产生中断。

6. EXT_CLK 测试模式

测试模式用于在线测试\建立测试模式和控制RTC(实时时钟)的操作。测试模式由控

制/状态寄存器1的位TEST1设定,这时CLKOUT管脚成为输入管脚。在测试模式状态下,通过

CLKOUT管脚输入的频率信号代替片内的64Hz频率信号,每64个上升沿将产生秒的时间增量。

进入EXT_CLK 测试模式时,时钟不与片内64Hz时钟同步,也确定不出预分频的状态。

EXT_CLK 测试模式操作举例:

(1) 进入EXT_CLK测试模式;使控制/状态寄存器的位TEST=1。

(2) 使控制/状态寄存器的位STOP=1。

(3) 使控制/状态寄存器的位STOP=0。

(4) 设置时间寄存器(秒、分钟、小时、日、星期、月/世纪和年)为期望值。

(5) 提供32个时钟脉冲给CLKOUT引脚。

(6) 读时间寄存器观察第一次变化。

(7) 提供64 个时钟脉冲给CLKOUT引脚。

(8) 读时间寄存器观察第二次变化。需要读时间寄存器的附加增量时,请重复步骤(7)

和(8)。

7.石英晶片频率调整的方法

Page 253: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 253 .

(1) 定值OSCI 电容。计算所需的电容平均值,用此值的定值电容,通电后在CLKOUT管

脚上测出的频率应为32.768kHz,测出的频率值偏差取决于石英晶片本身。平均偏差可达5

分钟/年。

(2) OSCI微调电容。可通过调整OSCI管脚的微调电容使振荡器频率达到精确值,这时

可测出通电时管脚CLKOUT上的32.768kHz信号。

(3) OSCI输出。直接测量管脚OSCI的输出。

13.3.4 PCF8563 的应用电路与编程

PCF8563的典型应用电路如图 13-7所示,MCU2007-I型开发板也采用此电路。图中,

用单片机的两个普通 I/O 口 P3.4 和 P3.5来模拟 I2C总线。PCF8563的 1、2 两个引脚需外

加晶振(Y2),频率为32.768KHz。

下面给出PCF8563 的驱动程序,默认11.0592Mhz的晶振。

程序中给出了对PCF8563操作的多个函数,需要的时候可以调用这些函数。

主程序完成如下功能:单片机向PCF8563发送9个字节的数据,发送的初始数据在

rom_sed[9]中,rom_sed[9]定义了寄存器中当前发送的值:控制/状态寄存器1为0,控制/

状态寄存器2为0,秒寄存器为0,分钟寄存器05,小时寄存器为11,日寄存器为15,星期寄

存器为4,月/世纪寄存器为0x03,年寄存器为0x07(即2007年03月15日11点05分0秒)。当

程序运行一段时间后,从地址寄存器02H 开始读数据,时间数据存放在rom_rec[7]中。

图 13-7 PCF8563 与单片机接口原理图

下面是我们的带有数码管显示的程序:

#include<reg52.h>

#include <intrins.h>

#define uchar unsigned char

#define uint unsigned int

Page 254: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 254 .

#define true 1

#define false 0

sbit SDA=P3^5; //数据端口;

sbit SCL=P3^4; //时钟端口;

sbit write=0xA2; //8563 的写地址;

sbit read=0xA3; //8563 的读地址;

sbit a_138=P1^2; //数码管的地址片选;

sbit b_138=P1^3;

sbit c_138=P1^4;

sbit cs_138=P2^6; //138 的片选,高电平有效;

sbit cs_373=P1^5; //373 的片选,高电平有效;

unsigned code zima1[10]=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90;//共阳极字

码 0~9;

unsigned char zima2[6];//保存时间信息对应的各个位

/*以下是显示函数的声明*/

void delay();

void xianshi();

void shuju(unsigned int i);

/*---------------以下是 pcf8563 的函数声明---------------------------*/

void delay_iic(void);

void I2CStart(void);

void I2CStop(void);

bit WaitAck(void);

void SendAck(void);

void SendNotAck(void);

void SendNotAck(void);

void I2CSendByte(uchar);

uchar I2CReceiveByte(void);

void GetPCF8563(uchar ,uchar ,uchar *);

void SetPCF8563(uchar ,uchar );

void SetAlarm(uchar alarmtype,uchar count);

void CleanAlarm(void);

void I2CInit(void);

Page 255: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 255 .

main() //主程序

idata uchar rom_sed[9]; //定义保存时间初值的数组

idata uchar rom_rec[7]; //定义保存读取时间的数组

idata uint i;

cs_138=1;

cs_373=1;

rom_sed[0]=0x00; rom_sed[1]=0x00; //要发送的数据初始化

rom_sed[2]=0x00; rom_sed[3]=0x05;

rom_sed[4]=0x11; rom_sed[5]=0x15;

rom_sed[6]=0x04; rom_sed[7]=0x03;

rom_sed[8]=0x07;

for (i=0;i<9;i++)

SetPCF8563(i,rom_sed[i]); //设定时间初值,2007 年 03 月 15 日 11 点 05 分 0 秒

while(1)

GetPCF8563(0x02,0x07, rom_rec); //读取时间信息,并保存在 rom_rec 中

for(i=0;i<500;i++)

xianshi();

zima2[4]=(rom_rec[0]&0xf0)/16; //BCD 码取各个位

zima2[5]=rom_rec[0]&0x0f; // rom_rec[0]中存放的是“秒”数据

xianshi();

zima2[2]=(rom_rec[1]&0xf0)/16; //BCD 码取各个位

zima2[3]=rom_rec[1]&0x0f; // rom_rec[1]中存放的是“分”数据

xianshi();

zima2[0]=(rom_rec[2]&0x30)/16; //BCD 码取各个位

zima2[1]=rom_rec[2]&0x0f; // rom_rec[2]中存放的是“小时”数据

xianshi();

void delay()

Page 256: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 256 .

unsigned char i;

for(i=0;i<20;i++);

void xianshi(void) //LED 显示时间数据

P0=zima1[zima2[0]];

P1=0xb7;

delay();

P0=zima1[zima2[1]];

P1=0xb3;

delay();

P0=zima1[zima2[2]];

P1=0xaf;

delay();

P0=zima1[zima2[3]];

P1=0xab;

delay();

P0=zima1[zima2[4]];

P1=0xa7;

delay();

P0=zima1[zima2[5]];

P1=0xa3;

delay();

/***以下是 8563 的初始化***/

void delay_iic(void) /*延时子程序*/

_nop_();

void I2CInit()

SCL=0;

I2CStop();

void I2CStart(void) /*启动 I2C 总线*/

Page 257: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 257 .

EA=0;

SDA=1;

delay_iic();

SCL=1;

delay_iic();

SDA=0;

delay_iic();

SCL=0;

delay_iic();

void I2CStop(void) /*停止 I2C 总线*/

SDA=0;

delay_iic();

SCL=1;

delay_iic();

SDA=1;

delay_iic();

SCL=0;

delay_iic();

EA=1;

bit WaitAck(void)

uchar errtime=255; //因故障接收方无 ACK,超时值为 255。

SDA=1;

delay_iic();

SCL=1;

delay_iic();

while(SDA)

errtime--;

if (!errtime)

I2CStop();

//SystemError=0x11;

return false;

Page 258: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 258 .

SCL=0;

return true;

void SendAck(void)

SDA=0;

delay_iic();

SCL=1;

delay_iic();

SCL=0;

void SendNotAck(void)

SDA=1;

delay_iic();

SCL=1;

delay_iic();

SCL=0;

void I2CSendByte(uchar ch) /*发送一个字节*/

uchar i=8;

while (i--)

SCL=0;

delay_iic();

SDA=(bit)(ch&0x80);

ch<<=1;

delay_iic();

SCL=1;

delay_iic();

SCL=0;

uchar I2CReceiveByte(void) /*接收一个字节*/

Page 259: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 259 .

uchar i=8;

uchar ddata=0;

SDA=1;

while (i--)

ddata<<=1;

SCL=0;

delay_iic();

SCL=1;

delay_iic();

ddata|=SDA;

SCL=0;

return ddata;

/* 以下函数的功能为:读取时钟芯片 PCF8563 的时间,设置要读的第一个时间类型 firsttype

。顺序是: 0x02:秒/0x03:分/0x04:小时/0x05:日/0x06:星期/0x07:月(世纪)/0x08:年*/

void GetPCF8563(uchar firsttype,uchar count,uchar *buff)

uchar i;

I2CStart();

I2CSendByte(0xA2);

WaitAck();

I2CSendByte(firsttype);

WaitAck();

I2CStart();

I2CSendByte(0xA3);

WaitAck();

for(i=0;i<count;i++)

buff[i]=I2CReceiveByte();

if (i!=count-1)

SendAck(); //除最后一个字节外,其他都要从 MASTER 发应答。

SendNotAck();

I2CStop();

Page 260: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 260 .

第 14 章 LCD 显示器原理及应用

液晶显示器(LCD)具有体积小、重量轻、功耗低、可以显示图形图像、便于用集成

电路直接驱动、易于实现全彩色等特点,因此在仪器仪表、便携工具、监控系统及消费电

子等领域有着广泛的应用。

液晶显示器分为段式、字符式、点阵式等。段式只能显示固定的一些符号或字符,价

格低廉;字符式可以显示数字和字符,驱动方便,但无法显示汉字。点阵式可以显示汉字,

价格较高。汉字液晶显示器又分为带字库和不带字库的两种。

14.1 液晶显示模块的原理

图14-1 是字符型液晶显示器(不能显示汉字)原理,字框中显示了字码 ABC;我们现

在看到的图形和实际见到的有些相似,但是有很大的差别,这些字码的显示是通过在方格

中依次连接所得,拼凑成一个个字符;液晶显示屏的原理和数码管的相同,即这些方格在

这里的作用相当于一个个 LED灯相互拥簇在一起;在这里可以称方格为“数码管”;通过

控制“数码管”的通断就可以显示出不同的字符或数字。每个字符占据 8 个方格的宽度,

11个方格的高度,即 11 行 8列,但是请注意,并不是显示一个字符一定要用 11 行 8 列,

/*以下函数的功能为:调整时钟。timetype 是要改的时间类型,value 是新设置的时间值(BCD 格式)。

x02:秒/0x03:分/0x04:小时/0x05:日/0x06:星期/0x07:月(世纪)/0x08:年 */

void SetPCF8563(uchar timetype,uchar value)

I2CStart();

I2CSendByte(0xA2);

WaitAck();

I2CSendByte(timetype);

WaitAck();

I2CSendByte(value);

WaitAck();

I2CStop();

Page 261: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 261 .

也可以是7 行 5列等。当然,数字液晶屏不能够显示汉字,因为所占据的位数不同。

图 14-1 字符液晶显示器原理

同理,汉字液晶屏也是,如图14-2的“大”字。大部分汉字液晶屏的驱动芯片是与液

晶屏集成的,集成在一起叫做液晶模块,不需要另加驱动,但不同驱动芯片的驱动方式不

同,程序也不同,所以在使用液晶模块之前首先要看它采用了什么样的驱动。

汉字屏的占据空间要大于数字屏,每个汉字的显示由 16 行 16 列组成。以汉字液晶显

示器为例,描述一个汉字的点阵数据要32个字节,大家知道,一个字节占8 个位,而一个

位刚好描述液晶屏上一个点是否点亮。举个例子,假设“1”表示某个点是亮的,“0”表

示某个点是不亮的,则表示上图中“大”字的第一行二进制信息应为:0000,0001,0000,

0000,即 0x01和 0x00两个字节。同理,可以计算出“大”字的其他行数据表示。很明显,

要现实一个汉字,需要用32 个字节的数据描述。

图 14-2 汉字液晶屏显示原理

在实际应用中,要根据所要显示的内容、成本及信息的多少来选择液晶显示器。因为

字符型液晶成本低,但只能显示固定的一些字符;汉字液晶又分为点阵型和字库型,点阵

型既可以显示汉字、又可以显示图形,而字库型只能显示汉字及字符,无法显示图形,但

驱动方便。液晶屏的大小也有很大差异,典型的汉字液晶一般有 12232、12864、19264 和

240128等。

Page 262: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 262 .

14.2 字符型液晶显示器 RT1602C

字符型液晶显示器是一种用5×7 点阵图形来显示字符的液晶显示器,根据显示的容量

可以分为1 行 16个字、2 行 16 个字、2 行 20 个字等,最常用的为2 行 16 个字,即我们马

上要学习的RT1602C 液晶模块。

14.2.1 RT1602C 引脚及原理

MCU2007-I 开发板使用带背光的液晶模块 RT1602C,RT1602C 采用标准的 16 脚接口,

其引脚功能如下:

第1 脚:VSS为电源地,接GND。

第 2 脚:VDD接 5V 正电源。

第3 脚:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度

最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K 的电位器调整对比度。

第4 脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。

第5 脚:RW为读写信号线,高电平时进行读操作,低电平时进行写操作。当RS 和 RW

共同为低电平时可以写入指令或者显示地址,当RS 为低电平RW为高电平时可以读忙信号,

当RS 为高电平RW为低电平时可以写入数据。

第6 脚:E端为使能端,当E 端由高电平跳变成低电平时,液晶模块执行命令。

第7~14 脚:D0~D7为 8 位双向数据线。

第15 脚:BLA背光电源正极(+5V)输入引脚。

第16 脚:BLK背光电源负极,接GND。

RT1602C液晶模块内带标准字库,内部的字符发生存储器已经存储了192 个 5×7点阵

字符,32个 5×10 点阵字符。另外还有字符生成 RAM 512字节,供用户自定义字符。这些

字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等。每一个字符都有

一个固定的代码,比如大写的英文字母“A”的代码是 01000001B(41H,也就是其 ASCII码),

显示时模块把地址41H 中的点阵字符图形显示出来,我们就能看到字母“A”。

14.2.2 RT1602C 控制指令及显示地址

RT1602C液晶模块内部的控制器共有 11条控制指令,如表 14-1所示。它的读写操作、

屏幕和光标的操作都是通过指令编程来实现的。(说明:1为高电平、0 为低电平)

表 14-1 1602 液晶模块控制指令

指 令 RS R/W D7 D6 D5 D4 D3 D2 D1 D0

1 清显示 0 0 0 0 0 0 0 0 0 1

2 光标返回 0 0 0 0 0 0 0 0 1 *

3 置输入模式 0 0 0 0 0 0 0 1 I/D S

Page 263: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 263 .

指令1:清显示,指令码01H,光标复位到地址 00H位置;

指令2:光标复位,光标返回到地址 00H;

指令3:光标和显示模式设置。I/D:光标移动方向,高电平右移,低电平左移;S:屏

幕上所有文字是否左移或者右移,高电平表示有效,低电平则无效;

指令4:显示开关控制。D:控制整体显示的开与关,高电平表示开显示,低电平表示

关显示 C:控制光标的开与关,高电平表示有光标,低电平表示无光标 B:控制光标是否

闪烁,高电平闪烁,低电平不闪烁;

指令5:光标或显示移位。S/C:高电平时移动显示的文字,低电平时移动光标;

指令6:功能设置命令。DL:高电平时为 4位总线,低电平时为 8 位总线。N:低电平

时为单行显示,高电平时双行显示。F: 低电平时显示5x7的点阵字符,高电平时显示 5x10

的点阵字符;

指令7:字符发生器 RAM地址设置;

指令8:DDRAM 地址设置;

指令9:读忙信号和光标地址。BF:为忙标志位,高电平表示忙,此时模块不能接收命

令或者数据,如果为低电平表示不忙;

指令10:写数据;

指令11:读数据。

液晶显示模块是一个慢显示器件,所以在执行每条指令之前一定要确认模块的忙标志

为低电平,表示不忙,否则此指令失效。要显示字符时要先输入显示字符地址,也就是告

诉模块在哪里显示字符,表14-2 是 TC1602EL液晶模块的内部显示地址。

表 14-2 1602 液晶模块的内部显示地址

比如第二行第一个字符的地址是40H,那么是否直接写入 40H就可以将光标定位在第二

行第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位 D7 恒定为高电平 1,

所以实际写入的数据应该是01000000B(40H)+10000000B(80H)=11000000B(C0H)。

4 显示开/关控制 0 0 0 0 0 0 1 D C B

5 光标或字符移位 0 0 0 0 0 1 S/C R/L * *

6 置功能 0 0 0 0 1 DL N F * *

7 置字符发生存储器地址 0 0 0 1 字符发生存储器地址

8 置数据存储器地址 0 0 1 显示数据存储器地址

9 读忙标志或地址 0 1 BF 计数器地址

10 写数据到存储器 1 0 要写入的数据

11 从存储器读数据 1 1 要读出的数据

位 置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

第一行 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

第二行 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F

Page 264: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 264 .

14.2.3 RT1602C 与单片机接口及程序

在MCU2007-I 型单片机开发板上,RT1602C 液晶连接插槽位JP4,具体连接方式见图14-

3。在使用液晶时,在第三引脚连接了一个10K 的电位器用来调整对比度,若程序运行后液

晶无显示,则可调整此电位器。

图 14-3 1602 与单片机接口

1602液晶模块的驱动程序编制如下:(功能为:用定时器实现一个时钟,并用 1602液

晶模块显示时间及提示信息,从中读者可以体会液晶驱动程序的编制方法)

#include<reg51.h>

#define uchar unsigned char

#define uint unsigned int

sbit rs = P3^6 ; //定义寄存器选择位

sbit rw = P3^7 ; //定义读写控制线

sbit ep = P2^7 ; //定义使能端

code uchar mayuan[ ]='0','1','2','3','4','5','6','7','8','9';

code uchar aa[ ]="The total times is: ";

uchar i,hour,minute,second,k=0;

void putchar(uchar weizhi,uchar da);//显示一个字符的函数

void delay(uchar) ; //延时函数

void lcd_wcmd(uchar) ; // 写入指令数据到 LCD

bit lcd_bz(void) ; // 测试 LCD 忙碌状态

void lcd_pos(uchar) ; //设定显示位置

void lcd_wdat(uchar) ; //写入字符显示数据到 LCD

Page 265: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 265 .

void display(uchar,uchar *) ; //显示一个字符串的函数

void lcd_init(void); //液晶初始化函数

void showtime(uchar,uchar,uchar);//显示时间函数

void time_init(void); //定时时间初始化函数

main()

delay(10) ;

lcd_init() ; // 初始化 LCD

time_init(); //定时器初始化

display(0x00,aa); //第一行显示"The total times is: "

while(1)

if(k!=second) //控制每一秒显示一次

k=second;

showtime(hour,minute,second);

void delay(uchar ms) // 延时子程序

uchar i ;

while(ms--)

for(i = 0 ; i<250;i++) ;

bit lcd_bz() // 测试 LCD 忙碌状态

bit result ;

rs = 0 ;

rw = 1 ;

ep = 1 ;

result = (bit)(P0 & 0x80) ;

ep = 0 ;

return result ;

Page 266: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 266 .

void lcd_wcmd(uchar cmd) // 写入指令数据到 LCD

while(lcd_bz()) ; //等待液晶空闲

rs = 0 ;

rw = 0 ;

ep = 0 ;

P0 = cmd ;

ep = 1 ;

ep = 0 ;

void lcd_pos(uchar pos) //设定显示位置

lcd_wcmd(pos | 0x80) ;

void lcd_wdat(uchar dat) //写入字符显示数据到 LCD

while(lcd_bz()) ; //等待液晶空闲

rs = 1 ;

rw = 0 ;

ep = 0 ;

P0 = dat ;

ep = 1 ;

ep = 0 ;

void lcd_init() //LCD 初始化设定

lcd_wcmd(0x38) ; //功能设置:4位总线,双行显示,5x7 的点阵字符(参指令 6)

delay(1) ;

lcd_wcmd(0x01) ; //清除 LCD 的显示内容(参指令 1)

delay(1) ;

lcd_wcmd(0x06) ; //入口模式设置:光标右移,文字不移(参指令 3)

delay(1) ;

lcd_wcmd(0x0c) ; //入口模式设置:开显示,无光标,光标不闪烁(参指令 4)

delay(1) ;

Page 267: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 267 .

/*以下是在 LCD 上显示数组数据的函数。先写显示地址,后写显示数据。其中 pos 为写入的位置,q

指向要写入的数据所在的数组*/

void display(uchar pos, uchar *q)

uchar i ;

lcd_pos(pos) ;

for(i=0 ;i<16;i++)

lcd_wdat(*q) ;

q++ ;

void putchar(uchar weizhi,uchar da) //在 weizhi 处显示字符 da

delay(1);

lcd_pos(weizhi);

lcd_wdat(da);

void showtime(uchar h,uchar m,uchar s)//显示时间(h:小时,m:分,s:秒)

putchar(0x40,mayuan[h/10]); //在位置 0x40 显示小时的高位

putchar(0x41,mayuan[h%10]); //在位置 0x41 显示小时的低位

putchar(0x42,':'); //在位置 0x42 显示“:”

putchar(0x43,mayuan[m/10]); //在位置 0x43 显示分的高位

putchar(0x44,mayuan[m%10]); //在位置 0x44 显示分的低位

putchar(0x45,':'); //在位置 0x45 显示“:”

putchar(0x46,mayuan[s/10]); //在位置 0x46 显示秒的高位

putchar(0x47,mayuan[s%10]); //在位置 0x47 显示秒的低位

void time_init(void) //定时器初始化函数

i=0;

hour=12;

minute=34;

second=56; //时间初值为 12 时 34 分 56 秒

TMOD=0x01;

TL0=0xb0; //赋初值,定时 50ms

Page 268: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 268 .

14.3 汉字字模提取

一般,在进行液晶显示器编程以前,首先应根据要显示的汉字计算其点阵数据,然后

将点阵数据存储在单片机的程序存储器中,需要的时候将这些数据发送到液晶显示器即可

显示所需的汉字。将汉字转换为对应的点阵数据叫做对汉字取模,取模有横向取模和纵向

取模两种。横向取模就象上一段例子中描述的,逐行取点阵数据;而纵向取模则按列取点

阵数据,但纵向取模把汉字的16行分成上下两部分,首先对上面8 行逐列进行点阵数据提

取,然后再对下8 行进行点阵数据提取。可以看出,同样的汉字横向取模和纵向取模的数

据是不同的,而且不同的液晶显示器取模方式不同, 使用前应考虑好。

TH0=0x3c;

TR0=1; //启动定时器

EA=1;

ET0=1; //相关开中断

void time0() interrupt 1 using 2 //定时器 0中断函数,实现定时 1秒及时间的计算

TL0=0xb0; //重新赋初值

TH0=0x3c;

i++;

if(i==20) //1 秒钟到

second++;

i=0;

if(second==60)

second=0;

minute++;

if(minute==60)

minute=0;

hour++;

if(hour==24)

hour=0;

Page 269: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 269 .

下面介绍一款字模提取软件“字模提取V2.1”,该软件可以从网络上下载。双击该软

件启动后,可以根据需要对汉字取所需的模。举例说明如下:启动该软件后,界面如图14-4

所示。

图 14-4 汉字字模提取软件

首先要进行取模设置,是横向取模还是纵向取模,是否反显等。所谓“反显”是指用

“0”表示某个点是亮的,而用“1”表示某个点是不亮的。单击图14-4界面左侧“参数设

置”标签,出现的界面如图14-5 所示。

Page 270: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 270 .

图 14-5 参数设置界面

图 14-6 取模方式设置

单击图14-5所示的左侧“其他选项”,就会弹出图14-6所示的对话框。在图14-6中,

可以选择是横向取模或纵向取模,是否字节倒序等。MCU2007-I 型开发板所支持的汉字液晶

显示器是RT12232B 型,采用横向取模方式,使用时请注意。

Page 271: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 271 .

设置好以后单击确定,单击图 14-4左侧的“取模方式”标签。然后在图 14-4 下面的

“文字输入区”输入要取模的汉字,如“大”,然后按键盘的Ctrl+Enter键,界面就会变

成图14-7 所示样子,上方点阵区就会显示所输入汉字在液晶显示器上的形状。

图 14-7 取模界面

这时单击图14-7左侧“C51 格式”图标,界面就会变成图14-8 所示样子。

图 14-8 汉字字模生成区

在图14-8 中,下方点阵生成区中的 32个字节的数据就是“大”字对应的横向取模数

据了。把该数据存放在一个数组中,需要的时候将数组中的数据发送显示,就会在汉字液

晶显示器上显示“大”字了。

14.4 RT12232B汉字液晶显示器

RT12232B是一款点阵型液晶显示模块,既可以显示字符和汉字,也可以显示图形。

显示效果为黄底黑字。逻辑电压范围为 2.4V~6V,功耗低。

Page 272: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 272 .

14.4.1 RT12232B 引脚说明

RT12232B液晶显示模块有 18个引脚,各引脚功能如下:

第1脚:VDD 为逻辑正电源;

第2脚:VSS 为逻辑电源地;

第3脚:V0 为 LCD 驱动电源;

第4脚:RES 为复位端,下降沿复位,且复位后电平须保持为低电平;

第5脚:E1 为读写使能,低电平有效;

第6脚:E2:同 E1引脚;

第7脚:R/W 为读写选择,低电平时允许数据传输,上升沿时锁定数据;

第8脚:A0 为数据/指令选择。高电平:数据D0-D7 将送入显示RAM;低电平:数据D0-D7

将送入指令执行器执行;

第9~16脚:DB0-DB7:数据输入输出引脚;

第 17 脚:A为背光正电源;

第 18 脚:K为背光电源地。

14.4.2 指令描述

RT12232B液晶模块有 13条指令,见表 14­2。表 14­2 RT12232B 液晶模块指令

1、显示模式设置

开/关屏幕显示,不改变显示RAM(DD RAM)中的内容,也不影响内部状态。D=0,开显示;

D=1,关显示。如果在显示关闭的状态下选择静态驱动模式,那么内部电路将处于安全模式。

指 令 A0 R/W D7 D6 D5 D4 D3 D2 D1 D0

1 显示模式设置 0 0 1 0 1 0 1 1 1 0

2 设置显示起始行 0 0 1 1 0 A4 A3 A2 A1 A0

3 页地址设置 0 0 1 0 1 1 1 0 A1 A0

4 列地址设置 0 0 0 A6 A5 A4 A3 A2 A1 A0

5 读状态指令 0 1 BUSY ADC ON/OFF RESET 0 0 0 0

6 写显示数据 1 0 要写入的数据

7 读显示数据 1 1 要读出的数据

8 设置显示方向 0 0 1 0 1 0 0 0 0 D

9 开/关静态驱动模式设置 0 0 1 0 1 0 0 1 0 D

10 占空比(DUTY)选择 0 0 1 0 1 0 1 0 0 D

11 “读-修改-写”模式设

0 0 1 1 1 0 0 0 0 0

12 END 指令 0 0 1 1 1 0 1 1 1 0

13 复位指令 0 0 1 1 1 0 0 0 1 0

Page 273: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 273 .

2、设置显示起始行

执行该命令后,所设置的行将显示在屏幕的第一行。起始地址可以是0-31(由 A4~A0

的编码决定)范围内任意一行。行地址计数器具有循环计数功能,用于显示行扫描同步,

当扫描完一行后自动加一。

3、页地址设置

设置页地址。当单片机要对 DDRAM进行读写操作时,首先要设置页地址和列地址。本

指令不影响显示。 A1和 A0与相应的页地址对应关系见下表。

4、列地址设置

设置DD RAM中的列地址。当 MPU要对 DD RAM 进行读写操作前,首先要设置页地址和

列地址。执行读写命令后,列地址会自动加 1,直到达到 50H才会停止,但页地址不变。A6~A0

与相应的列地址对应关系见下表。

5、读状态指令

检测内部状态。 BUSY 为忙信号位,BUSY =1:内部正在执行操作;BUSY =0:空闲状态;

ADC为显示方向位,ADC=0:反向显示;ADC=1:正向显示;

ON/OFF显示开关状态,ON/OFF=0:显示打开,ON/OFF=1:显示关闭;

RESET复位状态,RESET=0:正常,RESET=1:内部正处于复位初始化状态。

6、写显示数据

将8 位数据写入DD RAM,该指令执行后,列地址自动加 1,所以可以连续将数据写入

DD RAM而不用重新设置列地址。

7、读显示数据

读出页地址和列地址限定的DD RAM 地址内的数据。当“读-修改-写模式”关闭时,每

执行一次读指令,列地址自动加1,所以可以连续从 DD RAM读出数据而不用设置列地址。

注意:再设置完列地址后,首次读显示数据前必须执行一次空的“读显示数据”。这

是因为设置完列地址后,第一次读数据时,出现在数据总线上的数据是列地址而不是所要

读出的数据。

8、设置显示方向

A1 A0 页地址

0 0 0

0 1 1

1 0 2

1 1 3

A6 A5 A4 A3 A2 A1 A0 列地址

0 0 0 0 0 0 0 0

0 0 0 0 0 0 1 1

… …

1 0 0 1 1 1 0 4E

1 0 0 1 1 1 1 4F

Page 274: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 274 .

该指令设置DD RAM 中的列地址与段驱动输出的对应关系。当设置D=0 时,反向;当D=1

时,正向。

9、开/关静态驱动模式设置

D=0表示关闭静态显示,D=1 表示打开静态显示。 如果在打开静态显示时,执行关闭

显示指令,内部电路将被置为安全模式。

10、DUTY选择

占空比设置。当D=0,表示 1/16DUTY;D=1,表示1/32DUTY。

11、“读-修改-写”模式设置

执行该指令以后,每执行一次写数据指令列地址自动加1;但执行读数据指令时列地址

不会改变。这个状态一直持续到执行“END”指令。

注意:在“读-修改-写”模式下,除列地址设置指令之外,其他指令照常执行。

12、END指令

关闭“读-修改-写”模式,并把列地址指针恢复到打开“读-修改-写”模式前的位置。

13、复位指令

使模块内部初始化。初始化内容:设置显示初始行为第一行;页地址设置为第三页。复

位指令对显示RAM 没有影响。

14.4.3 RT12232B 与单片机接口及程序

RT12232B与单片机连接非常方便,图 14-9 是 MCU2007-I 型单片机开发板与 RT12232B

的连接方式,JP6 插槽连接 RT12232B,背光灯控制引脚与电源直连,所以一上电背光灯就

亮。1脚与 3脚之间的可调电阻用来调整对比度,如果液晶模块不显示,可调节该电阻。

图 14­9 RT12232B 与单片机连接方式

以下是程序:

/*============================================================

Page 275: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 275 .

功能:使用 RT12232B 液晶显示文字演示

=============================================================*/

#include <reg52.H>

#define uchar unsigned char

#define uint unsigned int

sbit E1=P3^6; //块 1 左边

sbit E2=P3^7; //块 2 右边

sbit RW=P2^7;

sbitA0=P3^0; //A0 为 1 时是数据,0时为指令数据

sbit cs_138=P2^6;

#define DATA P0 //数据

unsigned char code bmp001[]=

/*­­ 文字: 康 ­­*/

/*­­ 隶书 12; 此字体下对应的点阵为:宽 x高=16x16 ­­*/

0x00,0x00,0x00,0xE0,0x20,0x60,0x60,0x70,0xF0,0x60,0x60,0xE0,0x20,0x00,0x00,0x00,

0x00,0x18,0x0C,0x07,0x19,0x0B,0x0F,0x2B,0x3F,0x07,0x0B,0x0F,0x19,0x10,0x10,0x00,

/*­­ 文字: 耘 ­­*/

/*­­ 隶书 12; 此字体下对应的点阵为:宽 x高=16x16 ­­*/

0x00,0x00,0x80,0x40,0xF0,0xF0,0xA0,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x00,

0x08,0x08,0x05,0x05,0x3F,0x1F,0x05,0x09,0x19,0x0D,0x13,0x11,0x1D,0x09,0x19,0x01,

/*­­ 文字: 科 ­­*/

/*­­ 隶书 12; 此字体下对应的点阵为:宽 x高=16x16 ­­*/

0x00,0x80,0x40,0x60,0xE0,0xF0,0x50,0x80,0xA0,0x20,0x40,0xF0,0x00,0x00,0x00,0x00,

0x00,0x04,0x06,0x03,0x3F,0x3F,0x03,0x04,0x02,0x02,0x02,0x3F,0x02,0x02,0x06,0x02,

/*­­ 文字: 技 ­­*/

/*­­ 隶书 12; 此字体下对应的点阵为:宽 x高=16x16 ­­*/

0x00,0x00,0x40,0x40,0xF0,0xD0,0x40,0x40,0xC0,0xF0,0xF0,0xC0,0x40,0x00,0x00,0x00,

0x00,0x10,0x12,0x11,0x1F,0x0F,0x11,0x12,0x0A,0x0A,0x04,0x0B,0x18,0x18,0x10,0x00,

/*­­ 文字: 前 ­­*/

/*­­ 隶书 12; 此字体下对应的点阵为:宽 x高=16x16 ­­*/

Page 276: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 276 .

0x00,0x40,0x40,0xC0,0xC0,0xD0,0xA0,0xC0,0x40,0x20,0x50,0x40,0xC0,0x40,0x40,0x00,

0x00,0x10,0x10,0x1F,0x07,0x06,0x1F,0x1F,0x00,0x17,0x10,0x30,0x1F,0x00,0x00,0x00,

/*­­ 文字: 程 ­­*/

/*­­ 隶书 12; 此字体下对应的点阵为:宽 x高=16x16 ­­*/

0x00,0xC0,0xA0,0xA0,0xE0,0x90,0x90,0xF0,0xF0,0xA0,0xA0,0xA0,0xF0,0x00,0x00,0x00,

0x00,0x06,0x02,0x1F,0x3F,0x01,0x12,0x15,0x15,0x1F,0x1F,0x15,0x15,0x10,0x10,0x00,

/*­­ 文字: 无 ­­*/

/*­­ 隶书 12; 此字体下对应的点阵为:宽 x高=16x16 ­­*/

0x00,0x00,0x00,0xA0,0xA0,0xA0,0xE0,0xE0,0xA0,0xA0,0xA0,0xA0,0x80,0x00,0x00,0x00,

0x00,0x10,0x11,0x10,0x18,0x0C,0x07,0x00,0x07,0x0C,0x18,0x30,0x30,0x30,0x18,0x00,

/*­­ 文字: 忧 ­­*/

/*­­ 隶书 12; 此字体下对应的点阵为:宽 x高=16x16 ­­*/

0x00,0xC0,0x10,0xF0,0x00,0xE0,0x80,0x80,0xF0,0x80,0x80,0xB0,0x80,0x00,0x00,0x00,

0x01,0x00,0x30,0x3F,0x10,0x18,0x08,0x06,0x03,0x0E,0x1F,0x18,0x30,0x30,0x18,0x08,

/*­­ 文字: ! ­­*/

/*­­ 隶书 12; 此字体下对应的点阵为:宽 x高=16x16 ­­*/

0x00,0x00,0x00,0x00,0xF8,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

0x00,0x00,0x00,0x00,0x6F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

;

//延时

void delay(unsigned int i)

unsigned char k=200;

while(i>0)

i­­;

while(k>1)k­­;

//左页

//发送数据

void OUTMD(unsigned char i)

A0=1; //写数据

Page 277: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 277 .

//delay(5);

E1=1;

//delay(10);

DATA=i;

//delay(5);

E1=0;

//左页

//发送命令

void OUTMI(unsigned char i)

A0=0; //写指令

//delay(5);

E1=1;

//delay(10);

DATA=i;

//delay(5);

E1=0;

//右页

//发送数据

void OUTSD(unsigned char i)

A0=1; //写数据

//delay(5);

E2=1;

//delay(10);

DATA=i;

//delay(5);

E2=0;

//右页

//发送命令

void OUTSI(unsigned char i)

A0=0; //写指令

//delay(5);

Page 278: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 278 .

E2=1;

//delay(10);

DATA=i;

//delay(5);

E2=0;

//初始化

void lcdini(void)

RW=0;

OUTMI(0XE2);

OUTSI(0XE2);//复位

OUTMI(0XAE);

OUTSI(0XAE);//POWER SAVE

OUTMI(0XA4);

OUTSI(0XA4);//动态驱动

OUTMI(0XA9);

OUTSI(0XA9);//1/32 占空比

OUTMI(0XA0);

OUTSI(0XA0);//时钟线输出

OUTMI(0XEE);

OUTSI(0XEE);//写模式

OUTMI(0X00);

OUTMI(0XC0);

OUTSI(0X00);

OUTSI(0XC0);

OUTMI(0XAF);

OUTSI(0XAF);

/*

函数说明:同时设置主从显示页为 0-3页。(内函数,私有,用户不直接调用)

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

*/

void SetPage(uchar page0,uchar page1)

Page 279: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 279 .

OUTMI(0xB8|page1);OUTSI(0xB8|page0);

/*

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

函数说明:同时设置主从列地址为 0-121。(内函数,私有,用户不直接调用)

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

*/

void SetAddress(uchar address0,uchar address1)

OUTMI(address1);OUTSI(address0);

/*

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

调用方式:void PutChar0(uchar ch)

函数说明:在右页当前地址画一个字节 8个点。(内函数,私有,用户不直接调用)

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

-*/

void PutCharR(uchar ch)

OUTSD(ch);

/*

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

调用方式:void PutChar1(uchar ch)

函数说明:在左页当前地址画一个字节 8个点。(内函数,私有,用户不直接调用)

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

-*/

void PutCharL(uchar ch)

OUTMD(ch);

/*

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

Page 280: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 280 .

调用方式:void DrawBmp(bit layer,uchar width,uchar *bmp)

函数说明:画一个图,layer 表示上下层,width 是图形的宽,高都是 16,bmp 是

图形指针

使用 zimo3 软件,纵向取模,字节倒序/240 字节

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

-*/

void DrawBmp(bit layer,uchar width,uchar *bmp)

uchar x,address,i=0; //address 表示显存的物理地址

uchar page=0; //page 表示上下两页

bit window=0; //window 表示左右两页

for (x=width;x>1;x­­)

if (i>60) window=1;address=i%61;

else address=i;

if(layer==0) //显示一行八个字

SetPage(0,0);

SetAddress(address,address);

if(window==1)PutCharR(bmp[i]);

else PutCharL(bmp[i]);

SetPage(1,1);

SetAddress(address,address);

if(window==1)PutCharR(bmp[i+width]);

else PutCharL(bmp[i+width]);

else

//显示第二行八个汉字

SetPage(2,2);

SetAddress(address,address);

if(window==1)PutCharR(bmp[i]);

else PutCharL(bmp[i]);

SetPage(3,3);

SetAddress(address,address);

if(window==1)PutCharR(bmp[i+width]);

else PutCharL(bmp[i+width]);

i++;

Page 281: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 281 .

/*

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

调用方式:void clrscr(void)

函数说明:清屏

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

-*/

void clrscr(void)

uchar i;

uchar page;

for (page=0;page<4;page++)

SetPage(page,page);

SetAddress(0,0);

for (i=0;i<61;i++)PutCharR(0);PutCharL(0);

//-----------------------------------------------------------

//图片显示

//采用字模 2生成或字模 3生成的都可以.

//-----------------------------------------------------------

void DrawBmp1(uint x_add, uchar width,uchar *bmp)

uchar x,address,i=0; //address 表示显存的物理地址

uchar page=0; //page 表示上下两页

bit window=0; //window 表示左右两页

//putcharR //右边

//putcharL //左边

for (x=width;x>1;x­­)

if (x_add>60)

window=1;

Page 282: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 282 .

address=x_add%61;

else

address=x_add;

SetPage(0,0);

SetAddress(address,address);

if(window==1)

PutCharR(bmp[i]);

else

PutCharL(bmp[i]);

SetPage(1,1);

SetAddress(address,address);

if(window==1)

PutCharR(bmp[i+width]);

else

PutCharL(bmp[i+width]);

SetPage(2,2);

SetAddress(address,address);

if(window==1)

PutCharR(bmp[i+width+width]);

else

PutCharL(bmp[i+width+width]);

SetPage(3,3);

SetAddress(address,address);

if(window==1)

PutCharR(bmp[i+width+width+width]);

else

PutCharL(bmp[i+width+width+width]);

i++;

x_add++;

Page 283: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 283 .

//---------------------------------------------------------------

//---------------------------------------------------------------

//采用 zimo2 显示汉字,以汉字的显示方式

//void Draw_word(uchar d_where,uint x_add,bit layer,uchar width)

//d_where 表示在码表中第几个汉字,x_add 横坐标位置,layer 显示的层, width 显示的宽度。

//---------------------------------------------------------------

void Draw_word(uint d_where,uint x_add,bit layer,uchar width)

uchar x,i=0,address; //address 表示显存的物理地址

uchar page=0; //page 表示上下两页

bit window=0; //window 表示左右两页

//putcharR //右边

//putcharL //左边

d_where=d_where*32;

for (x=width;x>1;x­­)

if (x_add>60)

window=1;address=x_add%61;

else

address=x_add;

if(layer==0) //显示一行八个字

SetPage(0,0);

SetAddress(address,address);

if(window==1)

PutCharR(bmp001[d_where]);//右边

else

PutCharL(bmp001[d_where]);//左边

SetPage(1,1);

SetAddress(address,address);

Page 284: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 284 .

if(window==1)

PutCharR(bmp001[d_where+width]);

else

PutCharL(bmp001[d_where+width]);

else

//显示第二行八个汉字

SetPage(2,2);

SetAddress(address,address);

if(window==1)PutCharR(bmp001[d_where]);//右边

else PutCharL(bmp001[d_where]);//左边

SetPage(3,3);

SetAddress(address,address);

if(window==1)PutCharR(bmp001[d_where+width]);

else PutCharL(bmp001[d_where+width]);

x_add++;

d_where++;

void delay1s(unsigned char i)

while(i>1)

i­­;

delay(65530);

unsigned char getchar(void)

while(!RI);

RI=0;

return(SBUF);

Page 285: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 285 .

void putchar(unsigned char ch)

SBUF=ch;

TI=0;

//while(!TI);

void main(void)

uchar xunhuan=0;

cs_138=0;

lcdini();

clrscr();

while(1)

clrscr();

// wait1(3); //调用动态图片

// delay1s(10);

// clrscr();

while(xunhuan<56) //需要显示的字节位数;

Draw_word(xunhuan+0,1,0,16);//每次只能显示 14 个汉字,即 0~13 位排列;

Draw_word(xunhuan+1,17,0,16);

Draw_word(xunhuan+2,33,0,16);

Draw_word(xunhuan+3,49,0,16);

Draw_word(xunhuan+4,65,0,16);

Draw_word(xunhuan+5,81,0,16);

Draw_word(xunhuan+6,97,0,16);

Draw_word(xunhuan+7,1,1,16);

Draw_word(xunhuan+8,17,1,16);

Draw_word(xunhuan+9,33,1,16);

Draw_word(xunhuan+10,49,1,16);

Draw_word(xunhuan+11,65,1,16);

Page 286: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 286 .

Draw_word(xunhuan+12,81,1,16);

Draw_word(xunhuan+13,97,1,16);

delay1s(10);

// clrscr();

xunhuan=xunhuan+14; //翻页;

xunhuan=0; //回到首页;

//DrawBmp1(5,109,bmptupian);

delay1s(10);

//clrscr();

14.5 KS0108B液晶控制器

14.5.1 KS0108B 液晶控制器介绍

1.KS0108B液晶控制器的特点

KS0108B是一种带有列驱动输出的128×64图形液晶显示控制器,可直接与8 位微处理

器相联。KS0108B具有如下特点:

内藏64×64=4096 位显示RAM,RAM 中每位数据对应LCD 屏上一个点的亮暗状态;

KS0108B控制器是列驱动器,具有64 路列驱动输出;

KS0108B控制器读、写操作时序与68 系列微处理器相符,因此,它可直接与68 系

列微处理器接口相联;

KS0108B 控制器的占空比为1/32--1/64。

2.KS0108B液晶控制器的引脚功能

KS0108B与微处理器的接口信号如下:

CS1B、CS2B、CS3:芯片片选信号。CS1B 和CS2B 低电平选通,CS3 高电平选通;

E:读写控制信号。在E 下降沿,数据被锁存(写)入KS0108B;在E 高电平期间,数据

被读出;

R/W:读写选择信号。R/W=1 为读选通,R/W=0 为写选通;

RS:数据、指令选择信号。RS=1 为数据操作,RS=0 为写指令或读状态。

DB0~DB7:三态的数据总线;

RSTB:复位信号。复位信号有效时,关闭液晶显示,使显示起始行为0。RST 可与单片

机相连,由单片机控制;也可直接与Vcc相连,使之不起作用。

3. KS0108B液晶控制器的时序

KS0108B 液晶控制器具有能与68 系列微处理器直接接口的时序。单片机的写时序和

Page 287: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 287 .

读时序分别如图8-21和图8-22所示。

E

R/W

CS1B,CS2B

CS3,RS

DB0–DB7

图 8­21 单片机的写时序

E

R/WCS1B,CS2B

CS3,RS

DB0–DB7

图 8­22 单片机的读时序

14.5.2 KS0108B 液晶控制器指令系统

KS0108B液晶控制器的指令系统比较简单,总共只有七种。现分别介绍如下。

1.显示开/关指令

当DB0=1 时,LCD 显示 RAM 中的内容;DB0=0 时,关闭显示。

2.显示起始行(ROW)设置指令

该指令设置了对应液晶屏最上一行的显示 RAM 的行号,有规律地改变显示起始行,

可以使 LCD 实现显示滚屏的效果。

3.页(PAGE)设置指令

显示 RAM 共 64 行,分 8 页,每页 8 行。

4.列地址(YAddress)设置指令

设置了页地址和列地址,就唯一确定了显示 RAM 中的一个单元,这样单片机就可以

用读、写指令读出该单元中的内容或向该单元写进一个字节数据。

5.读状态指令

R/W D/I DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0

0 0 0 0 1 1 1 1 1 1/0

R/W D/I DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0

0 0 1 1 显示起始行(0~63)

R/W D/I DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0

0 0 1 0 1 1 1 页号(0~7)

R/W D/I DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0

0 0 0 1 显示列地址(0~63)

Page 288: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 288 .

该指令用来查询液晶显示模块内部控制器的状态,各参量含义如下:

BUSY : 1­内部在工作 0­正常状态

ON/OFF : 1­显示关闭 0­显示打开

RESET : 1­复位状态 0­正常状态

在 BUSY 和 RESET 状态时,除读状态指令外,其它指令均不对液晶显示模块产生作

用。

6.写数据指令

7.读数据指令

读、写数据指令每执行完一次读、写操作,列地址就自动增一。必须注意的是,进行

读操作之前,必须有一次空读操作,紧接着再读才会读出所要读的单元中的数据。

14.5.3 与内置KS0108B 的液晶模块的接口与编程

单片机与内置KS0108B控制器的128×64的液晶模块的接口电路如图8-23所示。其中,

液晶模块选用双电源(VDD/V0)供电,所以需要提供一个负电压,并设置了一个电位器,

接在液晶模块的V0 引脚上,可以调整负电压值,用以调节对比度。由于KS0108B控制器单

片只能驱动64列,所以驱动128×64的液晶模块需要两片KS0108B,片选端分别为/CSA和

/CSB,由P3.4和P3.3口线控制。

P2.7P2.6P2.5

P2.1P2.2

P2.0P3.4P3.3P3.2

P2.3P2.4

P3.0P3.1

DB7DB6DB5

DB1DB2

DB0/CSA/CSB

DB3DB4

R/WE

DI

Vcc

V0

GND

电位器

负电源

10K

GND

Vcc

80C51

液晶

显示模块

12864

RSTBP1.0

图 8­23 单片机与液晶模块接口电路

以下为128×64 的液晶模块驱动程序,主程序中调用液晶驱动程序在液晶模块上显示

“单片机”字样。

R/W D/I DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0

1 0 BUSY 0 ON/OFF REST 0 0 0 0

R/W D/I DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0

0 1 写 数 据

R/W D/I DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0

1 1 读显示数据

Page 289: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 289 .

#include <reg51.h>

#include <intrins.h>

#define DATA P2

sbit RS=P3^0;

sbit RW=P3^1;

sbit E=P3^2;

sbit CS1=P3^4;

sbit CS2=P3^3;

sbit RST=P1^0;

/********************函数定义***********************************/

void Delay_long(unsigned char); //长延时

void Delay_short(unsigned char); //短延时

void LCD_Busy1(void); //判断 LCD 控制芯片 KS0108 是否忙

void LCD_Busy2(void); //判断 LCD 控制芯片 KS0108 是否忙

void LCD_Command1(char ch); //向 LCD 控制芯片 KS0108 发送控制命令

void LCD_Command2(char ch); //向 LCD 控制芯片 KS0108 发送控制命令

void LCD_Data1(char ch); //向 LCD 控制芯片 KS0108 发送数据命令

void LCD_Data2(char ch); //向 LCD 控制芯片 KS0108 发送数据命令

void LCD_Show(char x,char y,char *Dot); //显示 16*16 点阵汉字

void LCD_Init (void); //初始化 LCD

void main(void)

/*-- 文字: 单 --*/

char code dan[]=0x00,0x00,0xF8,0x28,0x29,0x2E,0x2A,0xF8,0x28,0x2C, 0x2B,0x2A,

0xF8,0x00,0x00,0x00, 0x08,0x08,0x0B,0x09,0x09,0x09,0x09,0xFF,0x09,0x09,0x09,

0x09,0x0B,0x08,0x08,0x00;

/*-- 文字: 片 --*/

char code pian[]=0x00,0x00,0x00,0xFE,0x10,0x10,0x10,0x10,0x10, 0x1F,0x10,0x10,

0x10,0x18,0x10,0x00, 0x80,0x40,0x30,0x0F,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,

0xFF,0x00,0x00,0x00,0x00;

/*-- 文字: 机 --*/

char code ji[]=0x08,0x08,0xC8,0xFF,0x48,0x88,0x08,0x00,0xFE,0x02,0x02,0x02,

0xFE,0x00,0x00,0x00, 0x04,0x03,0x00,0xFF,0x00,0x41,0x30,0x0C,0x03,0x00, 0x00,

0x00,0x3F,0x40,0x78,0x00;

LCD_Init(); //初始化

LCD_Show(2,1,dan); //显示 16*16 点阵汉字'单'

Page 290: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 290 .

Delay_long(10);

LCD_Show(60,3,pian); //显示 16*16 点阵汉字'片'

Delay_long(10);

LCD_Show(120,5,ji); //显示 16*16 点阵汉字'机'

Delay_long(5);

void LCD_Busy1(void) //判断 LCD 控制芯片 KS0108 是否忙

DATA=0xff;

_nop_();

CS2=0;

RW=1;

RS=0;

E=1;

while (DATA&0x80);

CS2=1;

void LCD_Busy2(void) //判断 LCD 控制芯片 KS0108 是否忙

DATA=0xff;

_nop_();

CS1=0;

RW=1;

RS=0;

E=1;

while (DATA&0x80);

CS1=1;

void LCD_Command1(char ch) //向 LCD 控制芯片 KS0108 发送控制命令

LCD_Busy1();

CS2=0;

RW=0;

RS=0;

DATA=ch;

E=1;

E=0;

CS2=1;

Page 291: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 291 .

void LCD_Command2(char ch) //向 LCD 控制芯片 KS0108 发送控制命令

LCD_Busy2();

CS1=0;

RW=0;

RS=0;

DATA=ch;

E=1;

E=0;

CS1=1;

void LCD_Data1(char ch) //向 LCD 控制芯片 KS0108 发送数据命令

LCD_Busy1();

CS2=0;

RW=0;

RS=1;

DATA=ch;

E=1;

E=0;

CS2=1;

void LCD_Data2(char ch) //向 LCD 控制芯片 KS0108 发送数据命令

LCD_Busy2();

CS1=0;

RW=0;

RS=1;

DATA=ch;

E=1;

E=0;

CS1=1;

void LCD_Show(char x,char y,char *Dot) //显示 16*16 点阵汉字

Page 292: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 292 .

char i;

for (i=0;i<32;i++)

if ((x+i%16)<64)

LCD_Command1(0xB8+y+i/16);

LCD_Command1(0x40+x+i%16);

LCD_Data1(Dot[i]);

else

LCD_Command2(0xB8+y+i/16);

LCD_Command2(0x40+x-64+i%16);

LCD_Data2(Dot[i]);

void LCD_Init (void) //初始化 LCD

char i,j;

RST=0;

Delay_short(1);

RST=1;

LCD_Command1(0xc0);

LCD_Command2(0xc0);

LCD_Command1(0x3f); //开显示

LCD_Command2(0x3f);

for (j=0;j<8;j++) //清屏

LCD_Command1(0xB8+j); //清左半屏

LCD_Command1(0x40);

for (i=0;i<64;i++)

LCD_Data1(0x00);

LCD_Command2(0xB8+j); //清右半屏

LCD_Command2(0x40);

for (i=0;i<64;i++)

LCD_Data2(0x00);

Page 293: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 293 .

void Delay_long(unsigned char T)

unsigned int i;

while(T--)

Delay_short(200);

void Delay_short(unsigned char a)

unsigned char i;

while(a--)

for(i=0;i<100;i++)

_nop_();

Page 294: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 294 .

第 15 章 步进电机控制

15.1 步进电机概述

步进电机是将电脉冲信号转变为角位移或线位移的开环控制元件。在非超载的情况下,

电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数,而不受负载变化的影响,即

给电机加一个脉冲信号,电机则转过一个步距角。这一线性关系的存在,加上步进电机只

有周期性的误差而无累积误差等特点。使得在速度、位置等控制领域用步进电机来控制变

的非常的简单。

虽然步进电机已被广泛地应用,但步进电机并不能象普通的直流电机,交流电机在常

规下使用。它必须由双环形脉冲信号、功率驱动电路等组成控制系统方可使用。因此用好

步进电机却非易事,它涉及到机械、电机、电子及计算机等许多专业知识。

步进电动机具有如下特点:

1)步进电动机的角位移与输入脉冲数严格成正比。因此,当它转一圈后,没有累计误

差,具有良好的跟随性。

2)由步进电动机与驱动电路组成的开环数控系统,既简单、廉价,又非常可靠,同时,

它也可以与角度反馈环节组成高性能的闭环数控系统。

3)步进电动机的动态响应快,易于启停、正反转及变速。

4)速度可在相当宽的范围内平稳调整,低速下仍能获得较大转距,因此一般可以不用

减速器而直接驱动负载。

5)步进电机只能通过脉冲电源供电才能运行,不能直接使用交流电源和直流电源。

6)步进电机存在振荡和失步现象,必须对控制系统和机械负载采取相应措施。

15.2 步进电机原理

15.2.1 反应式步进电机原理

由于反应式步进电机工作原理比较简单。下面简单叙述三相反应式步进电机原理。

1、结构

电机转子均匀分布着很多小齿,定子齿有三个励磁绕阻,其几何轴线依次分别与转子

齿轴线错开。

设相邻两转子齿轴线间的距离为齿距以て表示,则A与齿 1相对齐,B与齿 2向右错开

1/3て,C 与齿 3向右错开 2/3て,A'与齿 5相对齐,(A'就是 A,齿 5 就是齿1)。

2、旋转

如A 相通电,B,C 相不通电时,由于磁场作用,齿 1 与 A 对齐,(转子不受任何力,

以下均同);

Page 295: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 295 .

如B相通电,A,C相不通电时,齿 2应与 B对齐,此时转子向右移过 1/3て,此时齿3

与 C 偏移为1/3て,齿 4与 A偏移(て-1/3 て)=2/3て;

如C相通电,A,B相不通电,齿 3应与 C对齐,此时转子又向右移过 1/3て,此时齿4

与 A 偏移为1/3て对齐;

如A相通电,B,C 相不通电,齿4 与 A 对齐,转子又向右移过1/3 て。

这样,经过A、B、C、A分别通电状态,齿 4(即齿 1 前一齿)移到A 相,电机转子向

右转过一个齿距,如果不断地按A,B,C,A......通电,电机就每步(每脉冲)1/3て,向

右旋转。如按A,C,B,A......通电,电机就反转。

由此可见,电机的位置和速度由导电次数(脉冲数)和频率成一一对应关系。而方向由

导电顺序决定。

不过,出于对力矩、平稳、噪音及减少角度等方面考虑。往往采用A-AB-B-BC-C-CA-A

这种导电状态,这样将原来每步 1/3 て改变为 1/6 て。甚至于通过二相电流不同的组合,

使其1/3 て变为1/12て,1/24 て,这就是电机细分驱动的基本理论依据。

15.2.2 感应子式步进电机

1、特点

感应子式步进电机与传统的反应式步进电机相比,结构上转子加有永磁体,以提供软

磁材料的工作点,而定子激磁只需提供变化的磁场而不必提供磁材料工作点的耗能,因此

该电机效率高,电流小,发热低。因永磁体的存在,该电机具有较强的反电势,其自身阻

尼作用比较好,使其在运转过程中比较平稳、噪音低、低频振动小。

感应子式步进电机某种程度上可以看作是低速同步电机。一个四相电机可以作四相运

行,也可以作二相运行。(必须采用双极电压驱动),而反应式电机则不能如此。例如:

四相,八相运行(A-AB-B-BC-C-CD-D-DA-A)完全可以采用二相八拍运行方式。

一个二相电机的内部绕组与四相电机完全一致,小功率电机一般直接接为二相,而功

率大一点的电机,为了方便使用,灵活改变电机的动态特点,往往将其外部接线为八根引

线(四相),这样使用时,既可以作四相电机使用,可以作二相电机绕组串联或并联使用。

2、分类

感应子式步进电机以相数可分为:二相电机、三相电机、四相电机、五相电机等。以

机座号(电机外径)可分为:42BYG(BYG为感应子式步进电机代号)、57BYG、86BYG、110BYG、

(国际标准),而像70BYG、90BYG、130BYG 等均为国内标准。

3、步进电机的指标术语

相数:产生不同对极N、S磁场的激磁线圈对数。常用 m表示。

拍数:完成一个磁场周期性变化所需脉冲数或导电状态用 n 表示,或指电机转过一个

齿距角所需脉冲数,以四相电机为例,有四相四拍运行方式即 AB-BC-CD-DA-AB,四相八拍

运行方式即 A-AB-B-BC-C-CD-D-DA-A。

Page 296: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 296 .

步距角:对应一个脉冲信号,电机转子转过的角位移用θ表示。θ=360 度(转子齿数

J*运行拍数),以常规二、四相,转子齿为50 齿电机为例。四拍运行时步距角为θ=360度

/(50*4)=1.8 度(俗称整步),八拍运行时步距角为θ=360 度/(50*8)=0.9 度(俗称半

步)。

定位转矩:电机在不通电状态下,电机转子自身的锁定力矩(由磁场齿形的谐波以及

机械误差造成的)。

静转矩:电机在额定静态电作用下,电机不作旋转运动时,电机转轴的锁定力矩。此

力矩是衡量电机体积(几何尺寸)的标准,与驱动电压及驱动电源等无关。

步距角精度:步进电机每转过一个步距角的实际值与理论值的误差。用百分比表示:

误差/步距角*100%。不同运行拍数其值不同,四拍运行时应在 5%之内,八拍运行时应在15%

以内。

电机正反转控制:当电机绕组通电时序为AB-BC-CD-DA时为正转,通电时序为DA-CA-BC-

AB时为反转。

15.3 步进电机应用

1、步进电机的选择

步进电机有步距角(涉及到相数)、静转矩、及电流三大要素组成。一旦三大要素确

定,步进电机的型号便确定下来了。

1)步距角的选择

电机的步距角取决于负载精度的要求,将负载的最小分辨率(当量)换算到电机轴上,

每个当量电机应走多少角度(包括减速)。电机的步距角应等于或小于此角度。目前市场

上步进电机的步距角一般有0.36 度/0.72度(五相电机)、0.9度/1.8 度(二、四相电机)、

1.5度/3 度 (三相电机)等。

2)静力矩的选择

步进电机的动态力矩一下子很难确定,我们往往先确定电机的静力矩。静力矩选择的

依据是电机工作的负载,而负载可分为惯性负载和摩擦负载二种。单一的惯性负载和单一

的摩擦负载是不存在的。直接起动时(一般由低速)时二种负载均要考虑,加速起动时主

要考虑惯性负载,恒速运行进只要考虑摩擦负载。一般情况下,静力矩应为摩擦负载的 2-3

倍内好,静力矩一旦选定,电机的机座及长度便能确定下来(几何尺寸)。

3)电流的选择

静力矩一样的电机,由于电流参数不同,其运行特性差别很大,可依据矩频特性曲线

图,判断电机的电流(参考驱动电源、及驱动电压)

4)力矩与功率换算

步进电机一般在较大范围内调速使用、其功率是变化的,一般只用力矩来衡量,力矩

与功率换算如下:

P= Ω·M

Ω=2π·n/60

Page 297: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 297 .

P=2πnM/60

其 P 为功率单位为瓦;Ω为每秒角速度,单位为弧度;n 为每分钟转速;M 为力矩单位

为牛顿∙米。P=2πfM/400(半步工作),其中 f为每秒脉冲数(简称 PPS)

2、步进电机在应用中的注意事项

1)步进电机应用于低速场合---每分钟转速不超过 1000转,(0.9度时 6666PPS),最

好在1000-3000PPS(0.9 度)间使用,可通过减速装置使其在此间工作,此时电机工作效率

高,噪音低。

2)步进电机最好不使用整步状态,整步状态时振动大。

3)由于历史原因,只有标称为12V 电压的电机使用12V 外,其他电机的电压值不是驱

动电压伏值 ,可根据驱动器选择驱动电压(建议:57BYG 采用直流24V-36V,86BYG采用直

流50V,110BYG 采用高于直流80V),当然12 伏的电压除12V恒压驱动外也可以采用其他驱

动电源,不过要考虑温升。

4)转动惯量大的负载应选择大机座号电机。

5)电机在较高速或大惯量负载时,一般不在工作速度起动,而采用逐渐升频提速,一

电机不失步,二可以减少噪音同时可以提高停止的定位精度。

6)高精度时,应通过机械减速、提高电机速度,或采用高细分数的驱动器来解决,也

可以采用5 相电机,不过其整个系统的价格较贵,生产厂家少,其被淘汰的说法是外行话。

7)电机不应在振动区内工作,如若必须可通过改变电压、电流或加一些阻尼的解决。

8)电机在600PPS(0.9度)以下工作,应采用小电流、大电感、低电压来驱动。

9)应遵循先选电机后选驱动的原则。

15.4 驱动控制系统

使用、控制步进电机必须通过环形脉冲,功率放大等组成的控制系统。

1、脉冲信号的产生。

脉冲信号一般由单片机或 CPU 产生,一般脉冲信号的占空比为 0.3-0.4 左右,电机转

速越高,占空比则越大。

2、信号分配

感应子式步进电机以二、四相电机为主,二相电机工作方式有二相四拍和二相八拍二

种,具体分配如下:二相四拍为,步距角为 1.8度;二相八拍为,步距角为 0.9 度。四相电

机工作方式也有二种,四相四拍为 AB-BC-CD-DA-AB,步距角为1.8度;四相八拍为 AB-B-BC-

C-CD-D-AB,(步距角为0.9 度)。

3、功率放大

功率放大是驱动系统最为重要的部分。步进电机在一定转速下的转矩取决于它的动态

平均电流而非静态电流(而样本上的电流均为静态电流)。平均电流越大电机力矩越大,

要达到平均电流大这就需要驱动系统尽量克服电机的反电势。因而不同的场合采取不同的

的驱动方式,到目前为止,驱动方式一般有以下几种:恒压、恒压串电阻、高低压驱动、

恒流、细分数等。

Page 298: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 298 .

下面介绍两种比较常用的步进电机控制方式。

15.4.1 基于ULN2003 的驱动方式

基于ULN2003 进行步进电机驱动的硬件连接图见图15-1。本例以驱动两相5V步进电机

进行介绍。

图15-1 中,ULN2003是达林顿管阵列,其封装为 16引脚。1~7 引脚为输入,分别与

10~16输出引脚对应,且输入与输出为反向;8脚为地线,9 脚为正电源。这里ULN2003用

来扩展单片机的驱动能力,当电压为50V 时最大可提供500mA的电流。

+5V4.7K×4

1

2

3

4

8 9

16

15

14

13

1

2

3

4

SteppingMotor

+5V

A

B

+5VULN2003MCU

P1.3

P1.2

P1.1

P1.0

图 15-1 步进电机驱动电路

单片机的四个I/O 口 P1.3到 P1.0连接到了 ULN2003的输入端。四个 4.7K的电阻帮助

单片机从电源处获得更多的驱动电流。

基于图15-1 连接方式下的步进电机驱动程序如下:(设步进电机工作于 2 相 8 拍,步

距角为0.9 度)

#include<reg51.h>

/*以下数组为步进电机控制码: 0x08 对应 1导通;0x0c 对应 1、2导通;0x04 对应 2 导通;0x06

对应 2、3导通;0x02 对应 3导通;0x03 对应 3、4导通;0x01 对应 4导通;0x09 对应 4、1导通*/

unsigned char step[8]=0x08,0x0c,0x04,0x06,0x02,0x03,0x01,0x09;

unsigned char flag=0; //记录步进电机步进状态,假设从 1导通开始

void delay(unsigned int k) //延时函数,控制步进速度

while(k--);

/*以下是控制步进电机正转的函数,pulse 为正转的脉冲数,一个脉冲对应 0.9 度*/

void zhengzhuan(unsigned int pulse)

unsigned int k;

for(k=flag;k<pulse+flag;k++)

Page 299: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 299 .

15.4.2 步进电机细分控制

步进电机在低频工作时,会有振动大、噪声大的缺点。如果使用细分方式,就能很好

的解决这个问题,步进电机的细分控制,从本质上讲是通过对步进电机励磁绕组中电流的

控制,使步进电机内部的合成磁场为均匀的圆形旋转磁场,从而实现步进电机步距角的细

分,一般情况下,合成磁场矢量的幅值决定了步进电机旋转力矩的大小,相邻两合成磁场

矢量之间的夹角大小决定了步距角的大小。

实现细分方式有多种方法,最常用的是脉宽调制式斩波驱动方式,大多数专用的步进

电机驱动芯片都采用这种驱动方式,TA8435 就是其中一款芯片。下面介绍基于TA8435芯片

的步进电机驱动电路及驱动程序。

P1=step[k%8];

delay(10); //延时,控制步进速度。可以使用定时器进行精确定时

flag=k%8; //记录步进电机停止时的步进状态,下次步进从此状态开始

/*以下是控制步进电机反转的函数,pulse 为反转的脉冲数,一个脉冲对应 0.9 度*/

void fanzhuan(unsigned int pulse)

unsigned int k;

unsigned char j;

j=8-pulse%8;

for(k=pulse+j+flag;k>j+flag;k--)

P1=step[k%8];

delay(10); //延时,控制步进速度。可以使用定时器进行精确定时

flag=k%8; //记录步进电机停止时的步进状态,下次步进从此状态开始

main()

zhengzhuan(400); //正转 1圈(400 步对应 360 度)

delay(20000);

fanzhuan(800); //反转 2圈(800 步对应 720 度)

while(1);

Page 300: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 300 .

1、TA8435 芯片特点

TA8435是东芝公司生产的单片正弦细分二相步进电机驱动专用芯片,该芯片具有以下

特点:

1)工作电压范围宽(10-40V);

2)输出电流可达1.5A(平均)和 2.5A(峰值);

3)具有整步、半步、1/4细分、1/8 细分运行方式可供选择;

4)采用脉宽调试式斩波驱动方式;

5)具有正/反转控制功能;

6)带有复位和使能引脚;

7)可选择使用单时钟输入或双时钟输入

2、TA8435 芯片引脚

TA8435芯片共有 25个引脚,其引脚如图15-2 所示。

SGRE

SET

ENAB

LE OSC

CW/CCW CK2

CK1

M1

M2

REFIN

MO B

1 2 3 4 5 6 7 8 9 10 11 12 1413 15 16 17 18 19 20 21 22 23 24

TA8435

VCC

NFB

NFAB A A

VMB

VMAGAGBNC

NC

25NC

图 15-2 TA8435 引脚图

第1 脚:SG为信号地;

第2 脚:RESET为复位端,低电平有效;

第3 脚:ENABLE为使能端,低电平有效;

第4 脚:OSC为斩波振荡器输入,由外部电容决定;

第5 脚:CW/CCW为正反转控制端,高电平正转,低电平反转;

第6 脚:CK2为时钟输入端;

第7 脚:CK1为时钟输入端。CK1、CK2时钟输入的最大频率不能超过 5KHz,控制时钟

的频率,即可控制电机转动速率;

第8 脚:M1为电机转动方式控制端,与M2 一起决定电机的转动方式。

第9 脚:M2为电机转动方式控制端,与 M1一起决定电机的转动方式。M1=0、M2=0,

电机按整步方式运转;M1=1、M2=0,电机按半步方式运转;M1=0、M2=1,电机按 1/4

细分方式运转;M1=1、M2=1,电机按1/8 步细分方式运转;

第10 脚:REFIN为 VNF的控制输入。REFIN为高电平时,NFA和 NFB的输出电压为0.8V,

REFIN为低电平时,NFA 和 NFB输出电压为 0.5V;

第 11 脚:MO为电机的输出端;

第12 脚:NC为空引脚,无连接;

第13 脚:VCC为逻辑正电源;

第14 脚:NC为空引脚,无连接;

Page 301: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 301 .

第15脚:VMB 为输出电力供应端;

第16脚:/B 为输出端;

第17脚:GB 为电源地;

第18 脚:NFB为 B通道输出电流检测端;这个引脚控制步进电机输入电流,电流大小

与NFB 端外接电阻关系式为:IO=Vref/Rnf。

第 19脚:B 为输出端;

第20脚:/A 为输出端;

第21 脚:NFA为 A通道输出电流检测端;这个引脚控制步进电机输入电流,电流大小

与NFA 端外接电阻关系式为:IO=Vref/Rnf。

第 22脚:GA 为电源地;

第23脚:A 为输出端;

第24脚:VMA 为输出电力供应端;

第25脚:NC 为空引脚,无连接。

3、TA8435驱动电路与驱动程序

图15-3 是单片机与TA8435 相连控制步进电机的原理图,图中,设 REFIN=1,选用步

进电机额定电流为 0.4A,R1,R2选用 1.6 欧姆、2W 的大功率电阻,O、C两线不接。步进

电机按二相双极性使用,四相按二相使用时可以提高步进电机的输出转矩,D1~D4快恢复

二极管用来泄放绕组电流。

VSS

ENABLE

OSC

CW/CCWCK2CK1

M1M2REFIN

BVCC

NFBNFA

B

A

A

VMBVMA

GA GB

TA8435MCU

P1.1P1.0

P1.3P1.4P1.5

P1.2

3000pF

A

AO

B O B

R1

R2

D1

D2

D3

D4

+12V

+5V

图 15-3 TA8435 与单片机连接图

以下是利用 TA8435控制步进电机的程序,实现采用 1/4细分方式控制步进电机的顺

时钟方向转动的功能,利用定时器0 向 TA8435输出脉冲,用来控制步进电机转速。

#include<reg51.h>

sbit m1=P1^2;

sbit m2=P1^1;

sbit refin=P1^0;

sbit cw=P1^3;

sbit ck2=P1^4;

sbit ck1=P1^5;

Page 302: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 302 .

使用细分方式可以提高步进电机的控制精度,降低步进电机的振动和噪声,因此,在

低频工作时,可以选用 1/4 细分或 1/8细分模式,以降低系统的振动和噪声,当系统需要

在高速工作时,细分模式就有可能达不到要求的速度,这时可以选用整步或半步方式,在

速度较高时,在整步或半步工作模式下,步进电机运行稳定,振动小、噪声也小。

void main()

TMOD=0x00; // 定时器方式 0

TH0=0x1f;

TL0=0x80; //定时器赋初值,此初值决定电机速度

TR0=0;

refin=0;

m1=0;

m2=1; //设定 1/4 细分方式

ck1=1;

cw=1; //正转

while(1);

Void stepmotor(void) interrupt 1 using 2

ck2=!ck2; //ck2 反转产生脉冲

TH0=0x1f;

TL0=0x80;

Page 303: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 303 .

第 16 章 单片机综合应用实例

16.1 投篮游戏机控制系统

16.1.1 系统组成及功能

投篮游戏机是由普通蓝球筐、蓝筐挡板控制系统、显示系统、计时系统、语音控制、

投币识别系统、投球检测系统等组成的。

投篮游戏机设计要求为:系统上电就不断播放音乐,同时 4 位数码管显示游戏机的总

使用次数;当投入游戏币(个数可设置)后,系统语音报玩家最好成绩(投篮的最高分数),

同时 4 位数码管显示该最好成绩;成绩报完后,语音播放音乐,同时投篮机蓝筐挡板上升,

当挡板上升到上限时停止;此时语音提示“游戏开始”,4位数码管左边两位显示倒计时时

间(从 60 秒倒计时,计时到 0 则游戏结束)、右边两位显示投篮分数,投中一球加 2 分;

当有球投中,语音播放鼓励语(如“太棒了”,“好极了”),整个游戏过程中不播放鼓励语

就播放音乐;当投球分数超过 40分,则延长 20秒的游戏时间;游戏结束后,语音播报玩

家分数,并根据分数报提示语(分数少于 40分则播放“请继续努力”,分数超过 40分则播

放“你真是神投手”);游戏结束后还要更新总的使用次数,使总的使用次数加 1,还要根据

玩家成绩看是否破记录,如果破记录就更新最好成绩,并将最好成绩及总的使用次数存入

非易失存储器(EEPROM)中;游戏结束后蓝筐挡板下降,将蓝筐挡住,播放完提示语后,

不断播放空闲音乐。

16.1.2 硬件设计

1、蓝筐挡板控制系统

蓝筐挡板的上下运动由一个直流电机控制,系统电路图见图 16­1。图 16­1中,接线端子 JP10 接直流电机,为电机提供电能。该直流电机的电压为 12V,

电机的通断由单片机 p2.7口线控制继电器 K2来实现。当 p2.7口线为低电平,则继电器K2吸合,电机得电;当 p2.7口线为高低电平,则继电器 K2断开,电机掉得电。

电机的正反转通过单片机的 p1.7口线控制双刀双掷继电器 K1 来实现。当 p1.7口线为

低电平,则继电器 K1 吸合,接线端子 JP10 的“1”端接通正电源(+12V),“2”端接地,

此时电机正转;当 p1.7口线为高电平,则继电器K1断开,接线端子 JP10 的“2”端接通正

电源(+12V),“1”端接地,此时电机反转。

电机正转则挡板上升,上升到上限则碰到上限位开关;电机反转则挡板下降,下降到

下限则碰到下限位开关;上下限位开关与单片机的 P0口低两位口线相连,当被碰到时则相

应口线为低电平(没碰到则为高电平)。连接电路见图 16­2,其中上限位开关接 P0.6,下限

Page 304: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 304 .

位开关接 P0.7。

图 16­1 直流电机通断及正反转控制电路

图 16­2 蓝筐上下限位开关连接方式

2、显示系统

显示系统由 4位共阳极数码管组成,在游戏进行时,其中左边两位显示游戏的剩余时

间,右边两位显示投篮分数;在系统上电时显示游戏机的总使用次数;在刚投币时,显示

玩家最好成绩。

数码管控制采用74LS164芯片来实现,具体电路连接参第七章,这里不详细给出。

74LS164的数据端连到单片机的 P1.6引脚,脉冲输入端连到单片机的 P1.5引脚。

3、投币识别系统

该系统采用成品的投币器,投币器输出端经电平变换后与单片机的 INT0口线相连。当

有币经过投币器的时候,投币器输出端就会输出一个低电平的脉冲,就可以向单片机提供

中断信号。但是投币器的输出信号平时是 12V 的,不能直接接到单片机引脚上,因此采用

图 16­3的方式进行了处理。

4、投球检测系统

当有球投中时,单片机系统应该及时获知该信息,为此选择了红外发射和接收管共同

Page 305: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 305 .

组成透球的检测系统。把两个管子头对头接到蓝筐上,平时红外发射管一直处于发射状态,

接收管处于接收状态。当有球投中,红外发射管发射的信号就被球挡住从而中断,此时红

外接收管就会发出一个低电平脉冲,将该脉冲信号输出端接到单片机的 P3.3引脚,当有球

投中时,就可以采用中断方式而获知。

图 16­3 投币器电平变换电路

5、投币数设定电路

因为不同地区的消费水平不同,导致玩游戏的时候投币个数也不同。本系统可以设置

1~4个币,当设置 4个币时,玩家必须投掷 4个币才能启动游戏机。具体电路见图 16­4。这里,JP11 是跳线接插端。当 JP11 的 1、2 和 3、4 都不短接时,单片机 P2.2和 P2.3

引脚均为高电平;当 JP11 的某个跳线短接时,单片机 P2.2和 P2.3相应的引脚被拉成低电

平,因此根据 P2.2和 P2.3引脚高低电平的不同就可以区分 4 种状态,也就可以控制投币的

个数。具体投币个数与 P2.2和 P2.3引脚状态之间的对应关系参软件设计一节。

图 16­4 投币数设定电路

6、语音控制电路

语音播放是整个游戏机必不可少的一部分。要在游戏的过程中适时播放语音,首先要

事先将语音录制好,并用语音编程器下载到某种语音芯片中,然后由单片机控制在合适的

时候将其播放出来,以达到烘托游戏氛围的效果。

语音芯片选择 ISD4004 系列中的 8 分钟型号,因为不但要播放提示语、鼓励语,还要

播放空闲音乐,所以要求时间较长。ISD4004芯片与单片机的连接如图 16­5所示。其中语

音控制引脚连接到了单片机的 P1口低 4位。

Page 306: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 306 .

图 16­5 语音芯片接口电路

7、EEPROM 存储电路

该电路选用 I2C总线接口的 24C02 芯片,用来保存玩家最好成绩与游戏机的总使用次

数。因为这两个参数要求在单片机系统掉电时仍能保存,所以选择非易失存储器 EEPROM。

24C02的两条控制引脚与单片机的 P2.0和 P2.1相连。如图 16­6 所示。

图 16­6 EEPROM 存储电路

Page 307: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 307 .

8.功率放大电路

要播放语音还需功率放大电路,这里功放电路采用 LA4440功放芯片。该芯片具体特性

请查阅其手册,这里不详细给出。功放电路连接见图 16­7。LA4440的音频输入端接 ISD4004的音频输出端。

图 16­7 LA4440功放电路

9、单片机最小系统

图 16­8给出了本控制系统的核心­­­­单片机最小系统。该系统含有复位电路、编程调试

接口、晶体振荡电路。其中编程调试接口与基于MAX232 的串口转换电路相连,采用计算

机串口下载程序(ISP)的编程方式。

10、电源设计

整个游戏机控制系统中需要三两种电压:+12V、+5V和+3V。+12V电源为直流电机和

LA4440芯片供电;+5V 电源为单片机系统供电;+3V电源为 ISD4004芯片供电。因此本系

统的电源比较复杂,具体电路见图 16­9和图 16­10。其中,图 16­9是把+5V电压降为+3V 电压的电路,电路中采用了 SPX1117 芯片,该芯

片可以将+5V左右的电压转换并稳定为+3.3V。

Page 308: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 308 .

图 16­8 单片机最小系统

图 16­9 语音芯片 ISD4004供电电路

图 16­10中,JP9插针接变压器输出端(约 15V),经整流桥后接三端稳压器 7812,输

出+12V电压,再经 7809和 7805稳压后,输出+5V电压。在 7812和 7805之间加了一个 7809,是防止从+12V直接稳压到+5V 产生太大的压差,稳压器会发热量过大,因此其间加了一个

7809来使电压过渡缓和。

Page 309: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 309 .

1 0

|

16

Page 310: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 310 .

16.1.3 软件设计

根据第一小节对游戏机功能的描述,可以得到图 16­11 所示的程序流程。

开 始

读总使用次数并显示

计算应投的币数

实投的币数赋0

播放待机音乐

语音提示“游戏开始”

是否投币

投币数够否

读出最好成绩

显示并语音播报最好成绩

篮筐挡板上升

上升完毕否

总使用次数加1并保存

播放游戏音乐

倒计时时间赋初值

1秒定时启动计时时间自动减1

时间到否

播放鼓励语,分数加2

投球命中否

播放游戏音乐

分数>40?

游戏时间加20秒

Y

N

Y

N

Y

N

N

Y

Y

N

Y

N

停止计时

语音提示“游戏结束”

语音播报玩家分数

播放“你真是神投手”

分数>40?

播放“请继续努力”

保存分数为最好成绩

分数>最好成绩?

篮筐挡板下降

下降完毕否

电机停止

Y

N

N

Y

Y

N

图 16­11 游戏机程序流程图

Page 311: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 311 .

根据以上的程序流程,可以得到以下程序:(由于篇幅有限,所以 24C02 的 I2C 读写程

序和 ISD4004 的操作程序此处没有给出,只给出了相关函数声明,具体程序请参阅相关章

节)

设语音已事先录制好并保存在 ISD4004中,具体语音对应的地址请参程序。

#include<reg52.h>

#include <intrins.h>

#define uint unsigned int

#define uchar unsigned char

/*****变量定义*****/

uchar toubi=0,bishu; //投币信号标志位

uchar score; //当前分数

sbit clk_164=P1^5; //74LS164 的脉冲输入引脚

sbit data_164=P1^6; //74LS164 的串行数据输入引脚

sbit SDA=P2^1; //24C02 的数据引脚

sbit SCL=P2^0; //24C02 的时钟引脚

sbit p25=P2^5; //投币数设定引脚

sbit p26=P2^6; //投币数设定引脚

sbit p3_2=P3^2; //是否投币

sbit ball=P3^2; //接球引脚

sbit motor=P2^7; //电机控制

sbit motor_zf=P1^7; //正反转控制,为 0则正转,为 1则反转

sbit up=P0^6; //上限位检测,蓝筐挡板上升到最高点此位为 0

sbit down=P0^7; //下限位检测,蓝筐挡板下降到最低点此位为 0

bit zhengzhuan=1; //正转上升

sbit SS=P1^3; //ISD4004 控制

sbit SCLK=P1^2;

sbit MOSI=P1^1;

sbit yuyin_int=P1^4; //语音芯片中断引脚

uchar jishi; //倒计时时间

uchar s; //定时次数

unsigned char code num1[10]=0x03, 0x9F, 0x25, 0x0D, 0x99, 0x49, 0x41, 0x1F, 0x01,

0x09;//LED 显示器字形代码

/*以下数组存放要写入 24C02 和从 24C02 读出的数据,其中前两个字节保存总的使用次数,第一、

二个字节分别为总使用次数的高字节与低字节;最后一个字节保存玩家最好成绩 */

unsigned char dat_wr[3],dat_rd[3];

Page 312: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 312 .

/*****函数声明*****/

void bofang(uint address); //播放语音函数

void bofangshuzi(uchar adress); //播放数字函数

void hanshu(uchar kk);

void DSTOP(void); //以下 5个为 ISD4004 驱动函数

void STOPP(void);

void UP(void);

void ISDX(uchar d);

void YS50(void);

void led_wr(uchar,uchar); //写数码管(LED 显示函数)

void delay1(uchar); //延时函数

void process(void); //主处理函数

/******以下是 24C02 读写函数******/

int WriteEE( uchar SubAdr, uchar Num, uchar *Wbuf); //24C02 写入函数

int ReadEE(uchar SubAdr, uchar Num, uchar *Rbuf); //24C02 读出函数

int SendByte(unsigned char a); //IIC 的字节发送程序

unsigned char ReadByte(void); //IIC 的字节读取程序

void SendACK(void); //发送应答

void SendNoACK(void); //应答

void Start(void); //IIC 总线开始

void Stop(void); //IIC 总线停止

void Delay(unsigned int x); //延时函数

void main(void) //主程序

uint j=1,a,b;

uchar num=0;

ReadEE(0,3 ,dat_rd); //从 24C02 中读出总使用次数和玩家最好成绩

a= (dat_rd[0]*256+dat_rd[1])/100; //计算总使用次数的千、百位

b= (dat_rd[0]*256+dat_rd[1])%100; //计算总使用次数的十、个位

led_wr(a,b); //显示总使用次数

if(p25==0&&p26==0) //以下 8行计算应投币个数

bishu=1;

else if(p25==1&&p26==0)

bishu=2;

Page 313: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 313 .

else if(p25==0&&p26==1)

bishu=3;

else if(p25==1&&p26==1)

bishu=4;

toubi=0; //投币个数赋初值

delay1(50); //为 ISD4004 上电

UP();

YS50();

bofang(0x003a); //播放空闲音乐

ball=0; //接球引脚拉低

while(1)

if(p3_2==0) //如果有币投入

for(num=0;num<100;num++);

if(p2_4==0)

toubi++; //投币数加 1

if(toubi>=bishu) //如果投币个数等于设定的币数

process(); //启动游戏机,进行游戏处理

if(yuyin_int==0) //如果音乐播放结束

bofang(0x003a); //重新播放空闲音乐

//main end

void process( ) //游戏进行时的处理函数

uchar delay=200,time=60;

uchar a=0; //切换鼓励语播放的变量

uint k;

ReadEE(0,3 ,dat_rd); //从 24C02 中读出总使用次数和玩家最好成绩

STOPP(); //停止 ISD4004,以便播放其它语音

Page 314: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 314 .

delay1(50);

UP();

YS50();

bofang(0x002d); //播放语音“投篮比赛开始”

led_wr(0,dat_rd[2]); // 显示最高成绩

while(up= =1) //上限位未碰到,则上升

motor_zf=0; // 电机正转,挡板上升

motor=0; // 电机启动

motor=1; //电机停止

motor_zf=1; //电机升降继电器释放

bofang(0x0012); //播放“当前最好成绩是”

while(yuyin_int); //等待语音播放结束

bofangshuzi(dat_rd[2]); //播放最好成绩的数据

if(dat_rd[1]==255) //计算总使用次数

dat_rd[0]++;

dat_rd[1]=0;

else

dat_rd[1]++;

WriteEE(0,3 ,dat_rd); //将总使用次数写入 24C02 进行保存

s=0; //定时次数

TMOD=0x01;

TH0=0x4c;

TL0=0x00; //定时 50ms

EA=1;

ET0=1;

score=0; //分数赋初值

while(yuyin_int); //等待语音播放结束

bofang(0x012c); //播放游戏音乐

TR0=1; //启动定时器

jishi=60; //倒计时时间赋初值

while(jishi>0) //游戏未结束(时间未到)

Page 315: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 315 .

led_wr(jishi,score); //显示剩余时间与得分

if(ball= =1) //有球接中

score+=2; //每投中一球加 2分

STOPP( ); //停止 ISD4004,以便播放其它语音

delay1(50);

UP();

YS50();

if(a= =0)

bofang(0x0020);//播放语音“好极了”

a=1;

else if(a= =1)

bofang(0x0010); //播放“OK”

a=2;

else

bofang(0x0037); //播放“太棒了”

a=0;

if(yuyin_int= =0) //等待语音播放结束

bofang(0x003a); //继续播放空闲音乐

for(k=0;k<1000;k++); //适当延时

if(score>40) // 分数超过 40 ,游戏实际时间加 20 秒

time=time+20;

//while(jishi >0) 结束

TR0=0; //游戏结束,关闭定时器

STOPP( ); //停止 ISD4004,以便播放其它语音

delay1(100);

UP();

YS50();

bofang(0x0033); //播放语音“游戏结束”

Page 316: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 316 .

while(yuyin_int); //等待语音播放结束

bofang(0x0022); //播放语音“你投中“

if(dat_rd[2]<score) //播放语音同时,看是否破记录,如果破了记录,

dat_rd[2]=score; //就将新记录(最好成绩)保存在 24C02 中

WriteEE(0,3,dat_rd);

while(yuyin_int= =1); //等待语音播放结束

bofangshuzi(score); //播放分数

while(yuyin_int==1); //等待语音播放结束

if(score<40)

bofang(0x001c); //播放语音“请继续努力”

else

bofang(0x0026); //播放语音“你真是神投手”

toubi=0; //投币个数初值

while(down= =1) //下限位未碰到,则挡板下降

motor_zf=1; //电机反转,挡板下降

motor=0; //电机启动

motor=1; //电机停止

void timer0() interrupt 1 using 2 //定时 1s

s++;

TH0=0x4c;

TL0=0x00; //定时 50ms

if(s= =20) //1 秒钟定时到

s=0;

jishi­­; //倒计时时间减 1

void led_wr(unsigned char cc,unsigned int aa) //4 位 LED 显示函数

// cc 为左边两位数据,aa 为右边两位数据

unsigned char i,j,x[4],bb;

x[3]=cc/10;

Page 317: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 317 .

x[2]=cc%10;

x[1]=aa/100;

x[0]=aa%10;

for(j=0;j<4;j++)

bb=num1[x[j]];

for(i=0;i<8;i++)

clk_164=0;

_nop_();

data_164=bb&0x01;

bb=bb>>1;

_nop_();

clk_164=1;

delay1(2);

void delay1(unsigned char bb) //延时函数

while(bb­­)

_nop_();

void bofang(unsigned int bf) //播放语音函数,bf 为语音所在的地址

unsigned char isdl;

unsigned char isdh;

isdl=bf%256;

isdh=bf/256;

SS=1;

ISDX(isdl);

ISDX(isdh);

ISDX(0xe0);

SS=1;

ISDX(0xf0);

SS=1;

Page 318: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 318 .

/*以下是播放得分的函数,num 为要播放的两位数据(分数),注意:播放一个两位数据的分数要分

四次,如播放“24 分”则先播“2”,再播“十”,再播“4”,再播“分” */

void bofangshuzi(uchar num)

uchar aa,bb;

aa=num /10;

bb=num %10;

if(aa>0) //如果 num 是两位数

switch(aa) //播放十位的数

case 1:bofang(0x0000);break; //播放语音“1”

case 2:bofang(0x0002);break; //播放语音“2”

case 3:bofang(0x0003);break; //播放语音“3”

case 4:bofang(0x0005);break; //播放语音“4”

case 5:bofang(0x0007);break; //播放语音“5”

case 6:bofang(0x0008);break; //播放语音“6”

case 7:bofang(0x000a);break; //播放语音“7”

case 8:bofang(0x023d);break; //播放语音“8”

case 9:bofang(0x000c);break; //播放语音“9”

default:break;

Delay(500); //延时,等待语音播放结束

bofang(0x000e); //播放语音“十”

Delay(500); //延时,等待语音播放结束

switch(bb) //播放个位的数

case 1:bofang(0x0000);break; //播放语音“1”

case 2:bofang(0x0002);break; //播放语音“2”

case 3:bofang(0x0003);break; //播放语音“3”

case 4:bofang(0x0005);break; //播放语音“4”

case 5:bofang(0x0007);break; //播放语音“5”

case 6:bofang(0x0008);break; //播放语音“6”

case 7:bofang(0x000a);break; //播放语音“7”

case 8:bofang(0x023d);break; //播放语音“8”

case 9:bofang(0x000c);break; //播放语音“9”

default:break;

Page 319: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 319 .

16.2 低压侧功率因数补偿系统

在当前的用电设备中,绝大部分为感性负载,导致了电力系统的功率因数较低,造成

了输电线路上的大量电能的浪费,同时也给用户带来了不便,因为功率因数过低会导致相

应的罚款。为此,这里设计了单片机控制的功率因数补偿系统,可以自动监测电网的功率

因数,并根据设定值进行自动补偿,可以大大提高功率因数。

所谓功率因数是指交流电压与电流之间夹角的余弦值。由于感性负载过多而引起功率

因数过低,为此可在负载端并联电容器进行补偿。所需并联的电容器的大小可根据实际的

功率因数计算出来。下面以单相功率因数补偿为例进行介绍。

16.2.1 系统的硬件组成与功能描述

整个控制系统由功率因数检测单元、单片机、显示单元、电容投切指示单元以及继电

器驱动单元组成。下面分别介绍各单元的实现。

1、功率因数检测单元

功率因数检测单元可以检测单相交流电的功率因数及电压电流的超前、滞后情况。交

流电压信号经过电压互感器变成小的电压信号 ;交流电流经电流互感器后,串连大功率uu

的小阻值电阻后,可以从电阻两端取到与电流信号同频、同相位的正弦波 。如图 16­12iu

所示, 和 经过运算放大器 LM324 构成的电压比较器后成为同频方波,这两路方波进入uu iu

与门 CT4008后,即可利用单片机的高速定时器测量两路方波公共部分的脉冲宽度 T(ms),由于工频交流电为 50Hz,其周期为 20ms,所以 10ms﹣T即为电压和电流的相位差的时间。

同时当电压超前电流时,D 触发器 74LS74的输出为“0”,反之为“1”,于是可以根据 D触

发器74LS74的输出来判断当前电网呈感性或容性,以决定应该投入电容器或是切除电容器。

2、显示单元

该单元采用HD7279 芯片,HD7279与 CPU 之间采用串行方式连接,只需三条线就可

直接驱动 8 位共阴极的 LED数码管;可方便控制独立的 LED。因此可以很容易的构成显示

系统。

这里用 LED显示器显示电网的功率因数值,于是选用了四位 LED数码管,最左边显示

Delay(500); //延时,等待语音播放结束

bofang(0x001a); // 播放语音“分”

Delay(500); //延时,等待语音播放结束

Page 320: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 320 .

超前滞后情况,分别用 L或 C表示,于是要用到 LED分段显示;后三位显示功率因数值,

可显示小数点后两位数字。

3、继电器驱动单元

该单元负责把单片机输出的控制信号放大以驱动继电器,继电器的输出再接通相应的

接触器,从而把单相电容器并联到负载回路,以达到功率因数补偿的目的。如图 16-12所

示。

驱动电路采用ULN2003芯片,该芯片驱动能力较强,单个芯片可直接驱动 7路 12VDC

的继电器。ULN2003 由单片机的P1口驱动,由于单片机在上电后会使 P1 口变为高电平,这

样会使继电器误动作,因此把P1 口经反向器CT4004后接 ULN2003的输入端,这样还可以

增加驱动能力。 图16-12 以驱动六路继电器为例,若需驱动更多的继电器,可利用8255

芯片对I/O 口进行扩展。

R1

+12V

+12V+5V

+5V+5V

R2

R3

R4R5

R6

ULN2003CT400480C5174LS74

CT4008

LM324

35

4

1

72611

127

143

23 1

4

5

7

1P1.0

P3.213591113

1424681012

7

123

54

68 9

111213141516 QD1

QD2QD3QD4QD5QD6

12234567

uuiu

P1.1

P1.5P1.6

P1.4P1.3P1.2

6

7

8

/CS

CLK

DIO

21

22

23

P2.0

P2.1

P2.2

ZLG7289

驱动

继电

.

.

.+5V

R7R8

R12

824

28 P2.7

P2.3P1.7 LED1

LED6

LED2

图 16­12 控制系统硬件原理图

4、电容投切指示单元

该单元用六个 LED指示灯(LED1~LED6)组成,当某一组电容投入,则对应的 LED指示灯点亮;当某一组电容切除,则对应的 LED指示灯熄灭。

16.2.2 系统的软件编制

系统的软件包括功率因数检测程序、电容组投切及指示程序、数码管显示程序等。其

中,为了避免某些电容频繁投切而其它电容始终未用的现象,电容投入和切除的程序采用

了循环方式,即先投入的电容先切除,后投入的电容后切除,这样可以延长电容的使用寿

命。读者可以慢慢体会电容循环投切的两个函数。

下面给出相应的程序.,由于篇幅所限,HD7279的驱动函数只给出了声明,未给出具

体定义,读者可以在相关章节找到。

#include<reg51.h>

#include<absacc.h>

Page 321: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 321 .

#include<math.h>

/******************函数定义********************/

void delay(unsigned char ); //延时函数

void gonglvbc(void); //功率补偿

unsigned char glys(void); //功率因数检测

void xhtou(void); //循环投电容

void xhqie(void); //循环切除电容

void display(unsigned char,bit); //显示功率因数及超前滞后

void led_on(unsigned char); //点亮 LED

void led_off(unsigned char); //熄灭 LED

void long_delay(void); //长延时。以下为 HD7279 读写函数

void short_delay(void); //短延时

void write7279(unsigned char, unsigned char);//写入 HD7279

unsigned char read7279(unsigned char);//从 HD7279 读出

void send_byte(unsigned char); //发送一个字节

unsigned char receive_byte(void); // 接收一个字节

/****************************变量及 I/O 接口********************************/

unsigned char gonglys; //功率因数的整形值

unsigned char dianrong[12]; //投切电容指示数组

sbit cs=P1^0; // cs 接 P2.0

sbit clk=P1^1; // clk 接 P2.1

sbit dat=P1^2; // dat(DIO)接 P2.2

sbit p1_1=P1^1; //第 1 组电容

sbit p1_2=P1^2; //第 2 组电容

sbit p1_3=P1^3; //第 3 组电容

sbit p1_4=P1^4; //第 4 组电容

sbit p1_5=P1^5; //第 5 组电容

sbit p1_6=P1^6; //第 6 组电容

sbit p3_2=P3^2; //功率因数检测输入

sbit p1_0=P1^0; //超前滞后输入,1 为感性,0 为容性

bit chaoq_zhih; //显示超前滞后的指示变量

#define CMD_RESET 0xa4 //复位指令。以下为 HD7279 用到的指令

#define DECODE1 0xc8 //方式 1译码指令,用来向 HD7279 写数据以便显示

#define SEGON 0xe0 //段点亮指令

Page 322: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 322 .

#define SEGOFF 0xc0 //段关闭指令

/***************************主程序****************************/

void main(void)

init(); //初始化

while(1)

if(p1_0==0)

chaoq_zhih =1; //超前,感性

else

chaoq_zhih =0; //滞后,容性

gonglys = glys(); //检测功率因数

display(gonglys,chaoq_zhih);//显示功率因数及超前滞后

gonglvbc(); //功率因数补偿

delay(200);

delay(200);

/**************初始化*******************/

void init(void)

unsigned char i;

P1=0xff; //断继电器

P2=0xff; //电容投切指示灯熄灭

for(i=0;i<6;i++) //以下为电容指示数组初始化

dianrong[i]=0;

for(i=6;i<12;i++)

dianrong[i]=i-5;

EA=1; //开中断

IT1=1;

EX1=1;

/***********************功率补偿******************************/

void gonglvbc(void)

if(p1_0==0&&gonglys<94) //感性且功率因数小于 0.94

Page 323: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 323 .

xhtou(); //循环投电容

delay (200);

else if(p3_1==1&&gonglys<94) //容性且功率因数小于 0.94

xhqie(); //循环切电容

delay (200);

/*****************************求功率因数*******************************/

unsigned char glys(void)

unsigned char b;

unsigned int zhouqi;

float z,fai,hudu;

TMOD=0x09; //设置定时器检测外部脉冲宽度

TL0=0x00;

TH0=0x00;

while(p3_2==1);

TR0=1;

while(p3_2==0);

while(p3_2==1);

TR0=0;

b=TH0;

zhouqi=b*256+TL0; //计算计数器中的计数值

if(zhouqi*2>=10000) //根据脉冲宽度计算电压与电流的夹角

fai=(zhouqi*2-10000)*0.018;

else

fai=(10000-zhouqi*2)*0.018;

if(fai>90)

fai=fai-90;

hudu=(fai/180)*3.14159; //把角度转化为弧度

z=cos(hudu); //计算功率因数

b=z*100; //求功率因数的整数值

return(b);

/**************************循环投电容*****************************/

void xhtou(void)

Page 324: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 324 .

unsigned char xx;

for(xx=0;xx<11;xx++)

dianrong[xx]= dianrong[xx+1];

dianrong[xx]=0;

switch(dianrong[5])

case 1:p1_1=0;led_on(1);break; //投入第 1组电容,并点亮指示灯

case 2:p1_2=0;led_on(2);break; //投入第 2组电容,并点亮指示灯

case 3:p1_3=0;led_on(3);break; //投入第 3组电容,并点亮指示灯

case 4:p1_4=0;led_on(4);break; //投入第 4组电容,并点亮指示灯

case 5:p1_5=0;led_on(5);break; //投入第 5组电容,并点亮指示灯

case 6:p1_6=0;led_on(6);break; //投入第 6组电容,并点亮指示灯

default :break;

/****************************循环切除电容*****************************/

void xhqie(void)

unsigned char xx;

for(xx=0;xx<6;xx++)

if(dianrong[xx]!=0)

dianrong[xx+6]= dianrong[xx];

switch(dianrong[xx])

case 1:p1_1=1;led_off(1);break; //切除第 1组电容,并熄灭指示灯

case 2:p1_2=1;led_off(2);break; //切除第 2组电容,并熄灭指示灯

case 3:p1_3=1;led_off(3);break; //切除第 3组电容,并熄灭指示灯

case 4:p1_4=1;led_off(4);break; //切除第 4组电容,并熄灭指示灯

case 5:p1_5=1;led_off(5);break; //切除第 5组电容,并熄灭指示灯

case 6:p1_6=1;led_off(6);break; //切除第 6组电容,并熄灭指示灯

default :break;

aaa[xx]=0;

xx=6; //跳出 for 循环

Page 325: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 325 .

/***************延时函数**************************/

void delay(unsigned char a)

unsigned char j,i;

for(j=0;j<a;j++)

for(i=0;i<200;i++);

/***************LED 指示灯点亮函数**************************/

void led_on(unsigned char led)

switch(led)

case 6:p2_7=0; break;

case 5: p2_6=0; break;

case 4: p2_5=0; break;;

case 3: p2_4=0; break;

case 2: p2_3=0; break;

case 1: p1_7=0; break;

default:break;

/***************LED 指示灯熄灭函数**************************/

void led_off(unsigned char led)

switch(led)

case 6:p2_7=1; break;

case 5: p2_6=1; break;

case 4: p2_5=1; break;;

case 3: p2_4=1; break;

case 2: p2_3=1; break;

case 1: p1_7=1; break;

default:break;

Page 326: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 326 .

/***************功率因数及超前滞后显示函数**************************/

void display(unsigned char gy,bit ca)

unsigned char a,b;

b=gy/10;

a=gy%10;

write7279(DECODE1,0);

write7279(SEGON,7); //数码管第2位显示“0.”

write7279(DECODE1,a); //数码管第 3、4位显示功率因数整数值

write7279(DECODE1,b);

if(ca= =1) //超前,数码管第 1位点亮 f,e,d,显示“L”。

for(a=0;a<8;a++) //数码管第 1位全部熄灭

write7279(SEGON,a);

write7279(SEGON,1); //点亮 f

write7279(SEGON,2); //点亮 e

write7279(SEGON,3); //点亮 d

delay(200);

else //滞后,数码管第 1位点亮 a,f,e,d,显示“C”

for(a=0;a<8;a++) //数码管第 1位全部熄灭

write7279(SEGON,a);

write7279(SEGON,6); //点亮a

write7279(SEGON,1); //点亮 f

write7279(SEGON,2); //点亮 e

write7279(SEGON,3); //点亮 d

delay(200);

Page 327: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 327 .

附 录

附录 1 基本知识简介

一、晶体二极管的种类

晶体二极管按其组成的材料分可为 : 锗二极管、硅二极管、砷化嫁二极管 ( 发光二极

管 ) 。而按用途分可为 : 整流二极管、稳压二极管、开关二极管、发光二极管、检波二极

管、变容二极管等。

常用晶体二极管的电路符号如下图所示。

二、电容

1、单位: 电容的基本单位是:F (法),此外还有μF(微法)、pF(皮法),另外还有

一个用的比较少的单位,那就是:nF(),由于电容 F 的容量非常大,所以我们看到的一

般都是μF、nF、pF 的单位,而不是 F的单位。 他们之间的具体换算如下: 1F=1000000μF1μF=1000nF=1000000pF

2、电容的耐压 单位:V(伏特)

每一个电容都有它的耐压值,这是电容的重要参数之一。普通无极性电容的标称耐压值有:

63V、100V、160V、250V、400V、600V、1000V等,有极性电容的耐压值相对要比无极性

电容的耐压要低,一般的标称耐压值有:4V、6.3V、10V、16V、25V、35V、50V、63V、

80V、100V、220V、400V 等。

3、电容的种类

电容的种类有很多,可以从原理上分为:无极性可变电容、无极性固定电容、有极性电容

等,从材料上可以分为:CBB 电容(聚乙烯),涤纶电容、瓷片电容、云母电容、独石电容、

电解电容、钽电容等。下面是各种电容的优缺点:

无感CBB电容

2层聚丙乙烯塑料和 2层金属箔交替夹杂然后捆绑而成。 无感,高频特性好,体积较小 ,

不适合做大容量,价格比较高,耐热性能较差。

CBB电容

2层聚乙烯塑料和 2层金属箔交替夹杂然后捆绑而成。 有感,其他同上。

瓷片电容

薄瓷片两面渡金属膜银而成。 体积小,耐压高,价格低,频率高(有一种是高频电容) 易

碎!容量低

云母电容

云母片上镀两层金属薄膜 容易生产,技术含量低。 体积大,容量小,(几乎没有用了)

独石电容

体积比 CBB 更小,其他同 CBB,有感

Page 328: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 328 .

电解电容

两片铝带和两层绝缘膜相互层叠,转捆后浸泡在电解液(含酸性的合成溶液)中。 容量大。

高频特性不好。

钽电容

用金属钽作为正极,在电解质外喷上金属作为负极。 稳定性好,容量大,高频特性好。 造

价高。(一般用于关键地方)

三、电阻

在选择电阻器的阻值时,应根据设计电路时理论计算电阻值,在最靠近标称值系列中

选用。普通电阻器(不包括精密电阻器)阻值标称系列值见下表,实际电阻器的阻值是表

中的数值乘以 10n(n为整数)。

电阻器额定功率标称系列值

允许偏差(%) 阻值()

±5% 1.0、1.1、1.2、1.3、1.5、1.6、1.8、2.0、2.2、2.4、2.7、3.0、3.3、3.6、3.9、4.3、4.7、

5.1、5.6、6.2、6.8、7.5、8.2、9.1

±10% 1.0、1.2、1.5、1.8、2.2、2.7、3.3、3.9、4.7、5.6、6.8、8.2

±20% 1.0、1.5、2.2、3.3、4.7、6.8

电阻器类型 额定功率(W)

线绕电阻器 0.05、0.125、0.25、0.5、1、2、4、8、10、16、25、40、50 、75、100、150、250、

500

非线绕电阻器 0.05、0.125、0.25、0.5、1、2、5、10、25、50、100

四、集成三端稳压器

型 号 功 能 型 号 功 能

7805 +5V 1A 稳压器 7905 ­5V 1A 稳压器

7806 +6V 1A 稳压器 7906 ­6V 1A 稳压器

7808 +8V 1A 稳压器 7908 ­8V 1A 稳压器

7809 +9V 1A 稳压器 7909 ­9V 1A 稳压器

7812 +12V 1A 稳压器 7912 ­12V 1A 稳压器

7815 +15V 1A 稳压器 7915 ­15V 1A 稳压器

7818 +18V 1A 稳压器 7918 ­18V 1A 稳压器

7824 +24V 1A 稳压器 7924 ­24V 1A 稳压器

Page 329: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 329 .

附录 2 计算机总线知识

下面对微机各类总线中目前比较流行的总线技术分别加以介绍。

(一)、内部总线

1、I2C总线 ­­­­I2C(Inter­IC)总线 10 多年前由 Philips公司推出,是近年来在微电子

通信控制领域广泛采用的一种新型总线标准。它是同步通信的一种特殊形式,具有接口线

少,控制方式简化,器件封装形式小,通信速率较高等优点。在主从通信中,可以有多个

I2C总线器件同时接到 I2C总线上,通过地址来识别通信对象。

2、SPI总线 ­­­­串行外围设备接口 SPI(serial peripheral interface)总线技术是Motorola公司推出的一种同步串行接口。Motorola公司生产的绝大多数 MCU(微控制器)都配有 SPI硬件接口,如 68 系列MCU。SPI总线是一种三线同步总线,因其硬件功能很强,所以,与

SPI有关的软件就相当简单,使 CPU 有更多的时间处理其他事务。

3、SCI总线 ­­­­串行通信接口 SCI(serial communication interface)也是由 Motorola 公司推出的。它是一种通用异步通信接口UART,与 MCS­51 的异步通信功能基本相同。

(二)、系统总线

1、ISA总线 ­­­­ISA(industrial standard architecture)总线标准是 IBM公司 1984年为推

出 PC/AT机而建立的系统总线标准,所以也叫 AT总线。它是对 XT总线的扩展,以适应

8/16位数据总线要求。它在 80286至 80486 时代应用非常广泛,以至于现在奔腾机中还保

留有 ISA总线插槽。ISA总线有 98只引脚。

2、EISA总线 ­­­­EISA总线是 1988 年由 Compaq 等 9家公司联合推出的总线标准。它

是在 ISA总线的基础上使用双层插座,在原来 ISA总线的 98条信号线上又增加了 98 条信

号线,也就是在两条 ISA信号线之间添加一条 EISA信号线。在实用中,EISA总线完全兼

容 ISA总线信号。

3、VESA总线 ­­­­VESA(video electronics standard association)总线是 1992 年由 60家附件卡制造商联合推出的一种局部总线,简称为VL(VESAlocal bus)总线。它的推出为微机

系统总线体系结构的革新奠定了基础。该总线系统考虑到 CPU 与主存和 Cache 的直接相连,

通常把这部分总线称为 CPU 总线或主总线,其他设备通过VL总线与 CPU 总线相连,所以

VL总线被称为局部总线。它定义了 32 位数据线,且可通过扩展槽扩展到 64 位,使用 33MHz时钟频率,最大传输率达 132MB/s,可与 CPU 同步工作。是一种高速、高效的局部总线,

可支持 386SX、386DX、486SX、486DX 及奔腾微处理器。

4、PCI 总线 ­­­­PCI(peripheral component interconnect)总线是当前最流行的总线之一,

它是由 Intel公司推出的一种局部总线。它定义了 32位数据总线,且可扩展为 64 位。PCI总线主板插槽的体积比原 ISA总线插槽还小,其功能比 VESA、ISA有极大的改善,支持突

发读写操作,最大传输速率可达 132MB/s,可同时支持多组外围设备。 PCI 局部总线不能

兼容现有的 ISA、EISA、MCA(micro channel architecture)总线,但它不受制于处理器,

是基于奔腾等新一代微处理器而发展的总线。

5、Compact PCI ­­­­以上所列举的几种系统总线一般都用于商用 PC机中,在计算机系

统总线中,还有另一大类为适应工业现场环境而设计的系统总线,比如 STD总线、 VME

Page 330: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 330 .

总线、PC/104总线等。这里仅介绍当前工业计算机的热门总线之一——Compact PCI。 ­­­­Compact PCI的意思是“坚实的 PCI”,是当今第一个采用无源总线底板结构的 PCI系统,是

PCI总线的电气和软件标准加欧式卡的工业组装标准,是当今最新的一种工业计算机标准。

Compact PCI是在原来 PCI 总线基础上改造而来,它利用 PCI的优点,提供满足工业环境应

用要求的高性能核心系统,同时还考虑充分利用传统的总线产品,如 ISA、STD、VME 或

PC/104来扩充系统的 I/O和其他功能。

(三)、外部总线

1、RS­232­C总线 ­­­­RS­232­C是美国电子工业协会EIA(Electronic IndustryAssociation)制定的一种串行物理接口标准。RS 是英文“推荐标准”的缩写,232 为标识号,C表示修改

次数。RS­232­C总线标准设有 25条信号线,包括一个主通道和一个辅助通道,在多数情况

下主要使用主通道,对于一般双工通信,仅需几条信号线就可实现,如一条发送线、一条

接收线及一条地线。RS­232­C标准规定的数据传输速率为每秒 50、75、 100、150、300、600、1200、2400、4800、9600、19200 波特。RS­232­C标准规定,驱动器允许有 2500pF的电容负载,通信距离将受此电容限制,例如,采用 150pF/m的通信电缆时,最大通信距

离为 15m;若每米电缆的电容量减小,通信距离可以增加。传输距离短的另一原因是 RS­232属单端信号传送,存在共地噪声和不能抑制共模干扰等问题,因此一般用于 20m以内的通

信。

2、RS­485总线 ­­­­在要求通信距离为几十米到上千米时,广泛采用 RS­485串行总线

标准。RS­485采用平衡发送和差分接收,因此具有抑制共模干扰的能力。加上总线收发器

具有高灵敏度,能检测低至 200mV 的电压,故传输信号能在千米以外得到恢复。 RS­485采用半双工工作方式,任何时候只能有一点处于发送状态,因此,发送电路须由使能信号

加以控制。RS­485用于多点互连时非常方便,可以省掉许多信号线。应用 RS­485可以联网

构成分布式系统,其允许最多并联 32 台驱动器和 32台接收器。

3、IEEE­488总线 ­­­­上述两种外部总线是串行总线,而 IEEE­488总线是并行总线接口

标准。IEEE­488总线用来连接系统,如微计算机、数字电压表、数码显示器等设备及其他

仪器仪表均可用 IEEE­488总线装配起来。它按照位并行、字节串行双向异步方式传输信号,

连接方式为总线方式,仪器设备直接并联于总线上而不需中介单元,但总线上最多可连接

15台设备。最大传输距离为 20米,信号传输速度一般为 500KB/s,最大传输速度为 1MB/s。

4、USB总线 ­­­通用串行总线USB(universal serial bus)是由 Intel、 Compaq、Digital、IBM、Microsoft、NEC、Northern Telecom等 7家世界著名的计算机和通信公司共同推出的

一种新型接口标准。它基于通用连接技术,实现外设的简单快速连接,达到方便用户、降

低成本、扩展 PC连接外设范围的目的。它可以为外设提供电源,而不像普通的使用串、并

口的设备需要单独的供电系统。另外,快速是USB技术的突出特点之一,USB的最高传输

率可达 12Mbps比串口快 100倍,比并口快近 10倍,而且 USB还能支持多媒体。

Page 331: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 331 .

附录 3 印制电路板设计原则和抗干扰措施

印制电路板(PCB)是电子产品中电路元件和器件的支撑件,它提供电路元件和器件之间

的电气连接。随着电于技术的飞速发展,PGB的密度越来越高。PCB设计的好坏对抗干扰能

力影响很大。因此,在进行PCB设计时.必须遵守PCB设计的一般原则,并应符合抗干扰设

计的要求。

PCB设计的一般原则

要使电子电路获得最佳性能,元器件的布且及导线的布设是很重要的。为了设计质量

好、造价低的PCB.应遵循以下一般原则:

1.布局

首先,要考虑PCB尺寸大小。PCB尺寸过大时,印制线条长,阻抗增加,抗噪声能力下

降,成本也增加;过小,则散热不好,且邻近线条易受干扰。在确定PCB尺寸后,再确定特

殊元件的位置。最后,根据电路的功能单元,对电路的全部元器件进行布局。在确定特殊

元件的位置时要遵守以下原则:

(1)尽可能缩短高频元器件之间的连线,设法减少它们的分布参数和相互间的电磁干扰。

易受干扰的元器件不能相互挨得太近,输入和输出元件应尽量远离。

(2)某些元器件或导线之间可能有较高的电位差,应加大它们之间的距离,以免放电引

出意外短路。带高电压的元器件应尽量布置在调试时手不易触及的地方。

(3)重量超过15g 的元器件、应当用支架加以固定,然后焊接。那些又大又重、发热量

多的元器件,不宜装在印制板上,而应装在整机的机箱底板上,且应考虑散热问题。热敏

元件应远离发热元件。

(4)对于电位器、可调电感线圈、可变电容器、微动开关等可调元件的布局应考虑整机

的结构要求。若是机内调节,应放在印制板上方便于调节的地方;若是机外调节,其位置

要与调节旋钮在机箱面板上的位置相适应。

(5)应留出印制扳定位孔及固定支架所占用的位置。

根据电路的功能单元.对电路的全部元器件进行布局时,要符合以下原则:

(1)按照电路的流程安排各个功能电路单元的位置,使布局便于信号流通,并使信号尽

可能保持一致的方向。

(2)以每个功能电路的核心元件为中心,围绕它来进行布局。元器件应均匀、整齐、紧

凑地排列在PCB上.尽量减少和缩短各元器件之间的引线和连接。

(3)在高频下工作的电路,要考虑元器件之间的分布参数。一般电路应尽可能使元器件

平行排列。这样,不但美观,而且装焊容易,易于批量生产。

(4)位于电路板边缘的元器件,离电路板边缘一般不小于2mm。电路板的最佳形状为矩

形。长宽比为3:2 成4:3。电路板面尺寸大于200x150mm 时,应考虑电路板所受的机械

强度。

2.布线

Page 332: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 332 .

(1) 输入输出端用的导线应尽量避免相邻平行。最好加线间地线,以免发生反馈藕合。

(2) 印制摄导线的最小宽度主要由导线与绝缘基扳间的粘附强度和流过它们的电流值

决定。 当铜箔厚度为0.05mm、宽度为1~15mm 时.通过2A的电流,温度不会高于3,因

此导线宽度为1.5mm 可满足要求。对于集成电路,尤其是数字电路,通常选0.02~0.3mm导线宽度。当然,只要允许,还是尽可能用宽线.尤其是电源线和地线。导线的最小间距主

要由最坏情况下的线间绝缘电阻和击穿电压决定。对于集成电路,尤其是数字电路,只要

工艺允许,可使间距小至5~8mm。(3) 印制导线拐弯处一般取圆弧形,而直角或夹角在高频电路中会影响电气性能。此外,

尽量避免使用大面积铜箔,否则.长时间受热时,易发生铜箔膨胀和脱落现象。必须用大

面积铜箔时,最好用栅格状.这样有利于排除铜箔与基板间粘合剂受热产生的挥发性气体。

3.焊盘

焊盘中心孔要比器件引线直径稍大一些。焊盘太大易形成虚焊。焊盘外径D 一般不小

于(d+1.2)mm,其中d 为引线孔径。对高密度的数字电路,焊盘最小直径可取(d+1.0)mm。PCB及电路抗干扰措施

印制电路板的抗干扰设计与具体电路有着密切的关系,这里仅就PCB抗干扰设计的几项

常用措施做一些说明。

1.电源线设计

根据印制线路板电流的大小,尽量加租电源线宽度,减少环路电阻。同时、使电源线、

地线的走向和数据传递的方向一致,这样有助于增强抗噪声能力。

2.地段设计

地线设计的原则是:

(1)数字地与模拟地分开。若线路板上既有逻辑电路又有线性电路,应使它们尽量分开。

低频电路的地应尽量采用单点并联接地,实际布线有困难时可部分串联后再并联接地。高

频电路宜采用多点串联接地,地线应短而租,高频元件周围尽量用栅格状大面积地箔。

(2)接地线应尽量加粗。若接地线用很纫的线条,则接地电位随电流的变化而变化,使

抗噪性能降低。因此应将接地线加粗,使它能通过三倍于印制板上的允许电流。如有可能,

接地线应在2~3mm 以上。

(3)接地线构成闭环路。只由数字电路组成的印制板,其接地电路布成团环路大多能提

高抗噪声能力。

3.退藕电容配置

PCB设计的常规做法之一是在印制板的各个关键部位配置适当的退藕电容。退藕电容的

一般配置原则是:

(1)电源输入端跨接10~100uf 的电解电容器。如有可能,接100uF 以上的更好。

(2)原则上每个集成电路芯片都应布置一个0.01pF 的瓷片电容,如遇印制板空隙不够,

可每4~8 个芯片布置一个1~10pF 的但电容。

(3)对于抗噪能力弱、关断时电源变化大的器件,如RAM、ROM 存储器件,应在芯片

的电源线和地线之间直接接入退藕电容。

(4)电容引线不能太长,尤其是高频旁路电容不能有引线。

此外,还应注意以下两点:

(1) 在印制板中有接触器、继电器、按钮等元件时.操作它们时均会产生较大火花放电,

Page 333: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 333 .

必须采用附图所示的RC 电路来吸收放电电流。一般R 取1~2K,C 取.2~47UF。(2) CMOS 的输入阻抗很高,且易受感应,因此在使用时对不用端要接地或接正电源。

附录 4 快速识别色环电阻

目前,国产或进口电视机、收录机广泛采用色环电阻,其优点是在装配、调试和修理

过程中,不用拨动元件,即可在任意角度看清色环,读出阻值,使用很方便。以往杂志上

都介绍过色环电阻识读法,按其方法读数时,要进行换算,较麻烦,这里介绍一种快速识

别阻值的方法。

带有四个色环的其中第一、二环分别代表阻值的前两位数;第三环代表倍率;第四环

代表误差。快速识别的关键在于根据第三环的颜色把阻值确定在某一数量级范围内,例如

是几点几K、还是几十几 K的,再将前两环读出的数"代"进去,这样就可很快读出数来。

下面介绍掌握此方法的几个要点:

1、熟记第一、二环每种颜色所代表的数。

可这样记忆:棕1,红2,橙 3,黄4,绿 5,蓝6,紫7,灰 8,白9,黑 0。这样连起

来读,多复诵几遍便可记住。

记准记牢第三环颜色所代表的 阻值范围,这一点是快识的关键。具体是:

金色:几点几 Ω

黑色:几十几 Ω

棕色:几百几十 Ω

红色:几点几 kΩ

橙色:几十几 kΩ

黄色:几百几十 kΩ

绿色:几点几 MΩ

蓝色:几十几 MΩ

从数量级来看,大体上可把它们划分为三个大的等级,即:

金、黑、棕色是欧姆级的;

红橙、黄色是千欧级的;

绿、蓝色则是兆欧级的。

这样划分一下是为了便于记忆。

2、当第二环是黑色时,第三环颜色所代表的则是整数,即几,几十,几百 kΩ等,这

是读数时的特殊情况,要注意。例如第三环是红色,则其阻值即是整几kΩ的。

3、记住第四环颜色所代表的误差,即:金色为5%;银色为10%;无色为 20%。

下面举例说明:

例1:当四个色环依次是黄、橙、红、金色时,因第三环为红色、阻值范围是几点几k

Ω的,按照黄、橙两色分别代表的数"4"和"3"代入,,则其读数为43 kΩ。第环是金色表示

误差为5%。

例2:当四个色环依次是棕、黑、橙、金色时,因第三环为橙色,第二环又是黑色,阻

值应是整几十kΩ的,按棕色代表的数"1"代入,读数为10 kΩ。第四环是金色,其误差为

Page 334: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 334 .

5%。

附录 5 ASCII 码表与常用库函数

ASCII 码表

常用库函数

高 4位

低 4位

0000 0001 0010 0011 0100 0101 0110 0111

0000 NUL DEL SPACE 0 @ P ¹ p

0001 SOH DC1 ! 1 A Q a q

0010 STX DC2 ″ 2 B R b r

0011 ETX DC3 # 3 C S c s

0100 EOT DC4 ﹩ 4 D T d t

0101 ENQ NAK % 5 E U e u

0110 ACK SYN & 6 F V f v

0111 BEL ETB , 7 G W g w

1000 BS CAN ( 8 H X h x

1001 HT EM ) 9 I Y i y

1010 LF SUB * : J Z j z

1011 VT ESC + ; K [ k

1100 FF FS , < L \ l |

1101 CR GS ­ = M ] m

1110 SO RS . > N ^ n ~

1111 SI US / ? O ­ o del

数学头文件 math.h

函数名 函数与形参类型 功能 函数名 函数与形参类型 功能

acos double acos(double x) 求 x的反余弦 log10 double log10(double x) 求 log10Xasin double asin(double x) 求 x的反正弦 pow double pow( x, y) 求 x的 y次幂

atan double atan(double x) 求 x的反正切 sin double sin(double x) 求 x的正弦值

cos double cos(double x) 求 x的余弦值 sinh double sinh(double x) 求双曲正弦值

exp double exp(double x) 求 e的 x次幂 sqrt double sqrt(double x) 求 x的开方值

fabs double fabs(double x) 求 x的绝对值 tan double tan (double x) 求 x的正切值

log double log (double x) 求 lnx的值 tanh double tanh(double x) 求双曲正切值

标准库函数 stdlib.h

Page 335: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 335 .

附录 6 单片机课题选编

1、函数发生器实验

内容:将正弦、三角、矩形波的波形数据存于 EEPROM中,CPU读取这些数据,并经

D/A输出。输出端接示波器观测波形。

2、74LS165扩展并行输入口实验

目的:掌握 74LS165的并串转换原理及其编程方法。

内容:8 位并行数据(由开关电平产生)进入 74LS165,经并串转换后,通过 I/O口进

入 CPU。3、74LS164扩展键盘实验

目的:掌握 74LS164扩展键盘的方法。

内容:键盘列输出由 CPU的串口经 74LS164产生,行输入接至 P1口,通过扫描读取

键值。

4、74LS164扩展 LCD 实验

目的:了解点阵式液晶设备的使用方法及其 74LS164扩展 LCD 的方法。

内容:点阵式液晶通过 74LS164与单片机 I/O 口相连,编程显示汉字“北京理工达盛

科技有限公司”。

5、8251扩展 RS485串口实验

目的:了解 8251和 RS485 总线的工作原理,掌握其编程方法。

内容:CPU经 8251扩展的串口接至 485收发器 1,另两个 CPU 经 8251 扩展的串口分

别接至 485 收发器 2、3,进行一发多收实验。

6、可变增益D/A 输出实验

目的:了解增益可调的原理,掌握D/A 与 CPU 的接口方式及编程方法。

内容:单片机从并口输出 8位数字量,经 D/A变换、增益调节后输出。通过设置断点,

用万用表测量输出电压值。(需要 8 入 8出增益可调模块)

7、8253测量电机转速实验

目的:了解电机转速的测量方法。

内容:用 8253可编程定时/计数器来测量电机转速。

8、PC机温度控制实验

目的:掌握 PC机与单片机的通讯方式以及温度控制的方法,要求是自适应算法,可根

据不同的控制对象的参数的不同进行算法自动调整,达到温度恒定。

内容:PC机通过 232 口向单片机传送控制指令,单片机根据指令进行温度采集、温度

控制。

9、门禁系统设计

random int random(int n)

产生 0到 n之间

的随机数 randomize void randomize()

初始化随机函

数,头文件为

time.h

Page 336: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 336 .

目的:了解 IC卡的使用原理,掌握实用门禁管理系统的基本设计方法。

内容:CPU 通过读卡器读取 IC卡的信息,经 RS232 口发送至 PC机。PC机经过信息

处理后发出控制指令。

10、单片机实现 PC机键盘控制器实验

目的:掌握单片机扫描键盘原理,以及单片机与 PC机的通讯方法。

内容:单片机通过扫描键盘确定键值(有键按下时),然后通过232串口将键值发送给

PC机。

11、基于以太网的远程数据采集系统

目的:学习应用系统的综合设计。

内容:单片机将需要传送的数据经以太网模块并入以太网,PC即可通过以太网发送控

制指令或接收数据。

12、基于 485 总线的分布式模拟 IC 卡电子门锁系统

目的:了解 485 总线和 IC卡的应用。

内容:各节点根据 IC卡及输入密码控制电子门锁的开启(可用 LED模拟),密码连

续输错三次,则本地报警并通过 485总线将信息传至主机。

13、远程抄表系统

目的:掌握数字脉冲信号的容错、纠错方法及单片机的中断、定时器的使用方法。

内容:首先由数据采集单元实时采集数据,通过专用的数据线传送到数据硬处理单元,

对数据确认无误后,发送给中央处理单元,中央处理单元对采集到的数据进行分析、分类,

通过总线(485、CAN)或市话网、移动网、互联网、无线网等方式传送给 PC机的数据汇

总分析单元,PC机根据统计的结果,向控制单元发出控制信息,同时将相关的信息或结果

发送给当事人。

14、PLC可编程控制器设计实验

目的:掌握 PLC工作原理及其内部构造,设计一款 PLC主机模型.要求:PLC主机性能,I/O:16 点(8 入 8 出)以上,输入电平 5V,输出电平 24V,I/O

接口要求带光电隔离保护,可驱动 1W 以上的执行机构。

15、GPS全球卫星车载信息模拟系统

目的:掌握GPS工作原理及其应用设计方法。

要求:能够实时跟踪定位模拟车辆的经度和纬度,能够在电子地图上绘制出(在小区

或某一街道等)的具体位置信息。

16、电子密码锁设计实验

要求设计的电子密码锁密码为 4位,由三大部分组成:

1、矩阵键盘接口电路的设计

2、密码锁的控制电路设计

3、输出七段显示电路的设计

密码锁功能说明:

1、 数据输入:每按一个数字键,就输入一个数值,并在显示器上的最右方显示出

该数值,并将先前已经输入的数据依序左移一个数字位置。

2、 数码清除:按下此键可清除前面所有的输入值,清除成为“0000”。3、 密码更改:按下此键时将目前的数字设定成新的密码。

Page 337: 单片机培训教材 - read.pudn.comread.pudn.com/downloads121/ebook/515687/STC89C51.pdf · 9.2.4 中断处理过程 169 9.3 中断的 c51编程 171 9.4 外部中断的扩充 176

西安康耘电子有限责任公司

. 337 .

4、 激活电锁:按下此键可将密码锁上锁。

5、 解除电锁:按下此键会检查输入的密码是否正确,密码正确即开锁。

17、恒压供水控制系统设计

设计一个能够驱动 3台 37KW交流调速电机进行供水的系统,要求系统实时检测水管

压力,通过调节电机的转速控制水管压力保持恒定。