第 7章 单片机的 C语言应用程序设计第 7章 单片机的 C语言应用程序设计
7.1 C语言与 MCS-51
7.2 C51数据类型及在 MCS-51中的存储方式
7.3 C51数据的存储类型与 MCS-51存储结构
7.4 MCS-51特殊功能寄存器 (SFR)的 C51定义
7.5 MCS-51并行接口的 C51定义
7.6 位变量的 C51定义
7.7 C51构造数据类型
7.8 模块化程序开发过程
7.9 MCS-51内部资源使用的 C语言编程
7.10 MCS-51片外扩展的 C语言编程
7.11 频率量测量的 C语言编程
7.12 MCS-51机间通信的 C语言编程
7.13 键盘和数码显示人机交互的 C语言编程第 7章 单片机的 C语言应用程序设计
7.1 C语言与 MCS–51
用汇编程序设计 MCS–51系列单片机应用程序时,必须要考虑其存储器结构,尤其必须考虑其片内数据存储器与特殊功能寄存器正确、合理的使用以及按实际地址处理端口数据。用 C语言编写
MCS–51单片机的应用程序,虽然不像用汇编语言那样具体地组织、
分配存储器资源和处理端口数据,但在 C语言编程中,对数据类型与变量的定义,必须要与单片机的存储结构相关联,否则编译器不能正确地映射定位。用 C语言编写单片机应用程序与编写标准的
C语言程序的不同之处就在于根据单片机存储结构及内部资源定义相应的 C语言中的数据类型和变量,其它的语法规定、程序结构及程序设计方法都与标准的 C语言程序设计相同。
第 7章 单片机的 C语言应用程序设计用 C语言编写的应用程序必须经单片机的 C语言编译器 (简称 C51),转换生成单片机可执行的代码程序。支持 MCS–51系列单片机的 C语言编译器有很多种。如 American Automation、
Auocet,BSO/TASKING,DUNFIELD SHAREWARE、
KEIL/Franklin等。其中 KEIL/Franklin以它的代码紧凑和使用方便等特点优于其它编译器。本章是针对这种编译器介绍 MCS–
51单片机 C语言程序设计。
第 7章 单片机的 C语言应用程序设计
7.2 C51数据类型及在 MCS-51中的存储方式
7.2.1 C51的数据类型
Franklin C51编译器具体支持的数据类型有:位型 (bit)、无符号字符 (unsigned char)、有符号字符 (singed char)、无符号整型
(unsigned int )、有符号整型 (signed int )、无符号长整型 (unsigned
long )、有符号长整型 (signed long )、浮点型 (float)和指针类型等。
第 7章 单片机的 C语言应用程序设计表 7.1 Franklin C51的数据类型数据类型 长度 (bit) 长度 (byte) 值域
bit 1 1 0,1
unsigned char 8 1 0~255
signed char 8 1 –128~127
unsigned int 16 2 0~65535
signed int 16 2 –32768~32767
unsigned long 32 4 0~4294967295
signed long 32 4 –2147483648~2147483647
float 32 4 ± 1.176E–38~± 3.40E+38(6位数字 )
double 64 8 ± 1.176E–38~± 3.40E+38(10位数字 )
一般指针 24 3 存储空间 0~65535
第 7章 单片机的 C语言应用程序设计
7.2.2 C51数据在 MCS-51中的存储方式位变量 (bit):与 MCS-51硬件特性操作有关的可以定义成位变量。位变量必须定位在 MCS-51单片机片内 RAM的位寻址空间中。
字符变量 (char):字符变量的长度为 1 byte即 8位。这很合适
MCS-51单片机,因为 MCS-51单片机每次可处理 8位数据。对于无符号变量 (unsigned char)的值域范围是 0~255。对于有符号字符变量
(signed char),最具有重要意义的位是最高位上的符号标志位 (msb)。
此位为 1代表 "负 ",为 0代表 "正 "。有符号字符变量和无符号字符变量在表示 0~127的数值时,其含义是一样的,都是 0~0x7F。负数一般用补码表示,即用 11111111表示 -1,用 11111110表示 -2…… 。当进行乘除法运算时,符号问题就变得十分复杂,而 C51编译器会自动地将相应的库函数调入程序中来解决这个问题。
第 7章 单片机的 C语言应用程序设计整型变量 (int),整型变量的长度为 16位。与 8080和 8086
CPU系列不同,MCS-51系列单片机将 int型变量的高位字节数存放在低地址字节中,低位字节数存放在高地址字节中。有符号整型变量 (signed int)也使用 msb位作符号标志位,并使用二进制补码表示数值。可直接使用几种专用的机器指令来完成多字节的加、减、乘、除运算。整型变量值 0x1234以图 7.1所示的方式存放在内存中。
第 7章 单片机的 C语言应用程序设计图 7.1 整型数的存储结构

0x12
0x34

+0
+1
地址 0x12
0x34
0x56
0x78
….
+0
+1
+2
+3
地址图 7.2 长整型变量的存储结构第 7章 单片机的 C语言应用程序设计浮点型变量 (float),浮点型变量为 32位,占 4个字节,许多复杂的数学表达式都采用浮点变量数据类型。应用符号位表示数的符号,用阶码和尾数表示数的大小。
用它们进行任何数学运算都需要使用由编译器决定的各种不同效率等级的库函数。 Franklin C51的浮点变量数据类型的使用格式与 IEEE-754标准有关,具有 24位精度,尾数的高位始终为 "1",因而不保存,位的分布如下:
● 1位符号位。
● 8位指数位。
● 23位尾数。
第 7章 单片机的 C语言应用程序设计符号位是最高位,尾数为低 23位,内存中按字节存储顺序如下:
地址 +0 +1 +2 +3
内容 MMMMMMMM MMMMMMMM EMMMMMMM SEEEEEEE
其中,S为符号位,1表示负,0表示正; E为阶码; M为 23
位尾数,最高位为 "1"。
浮点变量值 -12.5的十进制为,0xC1480000,它按图 7.3所示方式存于内存中。
第 7章 单片机的 C语言应用程序设计
0x00
0x00
0x48
0xC1
….
+0
+1
+2
+3
地址图 7.3 浮点数的存储结构第 7章 单片机的 C语言应用程序设计在编程时,如果只强调运算速度而不进行负数运算时,
最好采用无符号 (unsigned)格式。
无符号字符类型的使用:无论何时,应尽可能使用无符号字符变量,因为它能直接被 MCS-51所接受。基于同样的原因,
也应尽量使用位变量。有符号字符变量虽然也只占用一个字节,但需要进行额外的操作来进行测试代码的符号位。这无疑会降低代码效率。
第 7章 单片机的 C语言应用程序设计使用简化形式定义数据类型。其方法是在源程序开头使用
#define语句自定义简化的类型标识符。例如:
#define uchar unsigned char
#define uint unsigned int
这样,在编程中,就可以用 uchar代替 unsigned char,用 uint
代替 unsigned int来定义变量。
第 7章 单片机的 C语言应用程序设计
7.3 C51数据的存储类型与 MCS-51存储结构表 7.2 C51存储类型与 MCS-51存储空间的对应关系存储类型 与存储空间的对应关系
data 直接寻址片内数据存储区,访问速度快 (128字节 )
bdata 可位寻址片内数据存储区,允许位与字节混合访问 (16字节 )
idata 间接寻址片内数据存储区,可访问片内全部 RAM地址空间 (256字节 )
pdata 分页寻址片外数据存储区 (256字节 )由 MOV @Ri访问 (i=0,1)
xdata 片外数据存储区 (64 KB)由 MOVX @DPTR访问
code 程序存储器 64 KB空间,由 MOVC @DPTR访问第 7章 单片机的 C语言应用程序设计表 7.3 C51存储类型及其数据长度和值域存储类型 长度 (bit) 长度 (byte) 值域范围
data 8 1 0~255
idata 8 1 0~255
pdata 8 1 0~255
xdata 16 2 0~65 535
code 16 2 0~65 535
第 7章 单片机的 C语言应用程序设计带存储类型的变量的定义的一般格式为数据类型 存储类型 变量名带存储类型的变量定义举例:
char data var1;
bit bdata flags;
float idata x,y,z;
unsigned int pdata var2;
unsigned char vector[3][4];
第 7章 单片机的 C语言应用程序设计表 7.4 存储模式说明存储模式 说 明
SMALL
默认的存储类型是 data,参数及局部变量放入可直接寻址片内
RAM的用户区中 (最大 128字节 )。 另外所有对象 (包括堆栈 ),
都必须嵌入片内 RAM。 栈长很关键,因为实际栈长依赖于函数嵌套调用层数
COMPA
CT
默认的存储类型是 pdata,参数及局部变量放入分页的外部数据存储区,通过 @R0或 @R1间接访问,栈空间位于片内数据存储区中
LARGE
默认的存储类型是 xdata,参数及局部变量直接放入片外数据存储区,使用数据指针 DPTR来进行寻址 。 用此数据指针进行访问效率较低,尤其对两个或多个字节的变量,这种数据类型的访问机制直接影响代码的长度第 7章 单片机的 C语言应用程序设计
7.4 MCS-51特殊功能寄存器 (SFR)的 C51定义
MCS-51单片机中,除了程序计数器 PC和 4组工作寄存器组外,其它所有的寄存器均为特殊功能寄存器 (SFR),分散在片内
RAM区的高 128字节中,地址范围为 80H~0FFH。 SFR中有 11个寄存器具有位寻址能力,它们的字节地址都能被 8整除,即字节地址是以 8或 0为尾数的。
为了能直接访问这些 SFR,Franklin C51提供了一种自主形式的定义方法,这种定义方法与标准 C语言不兼容,只适用于对
MCS-51系列单片机进行 C语言编程。特殊功能寄存器 C51定义的一般语法格式如下:
sfr sfr-name = int constant;
第 7章 单片机的 C语言应用程序设计
"sfr"是定义语句的关键字,其后必须跟一个 MSC-51单片机真实存在的特殊功能寄存器名,"="后面必须是一个整型常数,
不允许带有运算符的表达式,是特殊功能寄存器 "sfr-name"的字节地址,这个常数值的范围必须在 SFR地址范围内,位于
0x80~0xFF。
例如:
sfr SCON=0x98; /* 串口控制寄存器地址 98H */
sfr TMOD=0x89; /* 定时器 /计数器方式控制寄存器地址 89H */
第 7章 单片机的 C语言应用程序设计
MCS-51系列单片机的特殊功能寄存器的数量与类型不尽相同,
因此建议将所有特殊的 "sfr"定义放入一个头文件中,该文件应包括
MCS-51单片机系列机型中的 SFR定义。 C51编译器的 "reg51.h"头文件就是这样一个文件。
在新的 MCS-51系列产品中,SFR在功能上经常组合为 16位值,
当 SFR的高字节地址直接位于低字节之后时,对 16位 SFR的值可以直接进行访问。例如 52子系列的定时器 /计数器 2就是这种情况。为了有效地访问这类 SFR,可使用关键字 "sfr16"来定义,其定义语句的语法格式与 8位 SFR相同,只是 "="后面的地址必须用 16位 SFR的低字节地址,即低字节地址作为 "sfr16"的定义地址。
第 7章 单片机的 C语言应用程序设计例如:
sfr16 T2 = 0xCC /*定时器 /计数器 2,T2低 8位地址为
0CCH,T2高 8位地址为 0CDH*/
这种定义适用于所有新的 16位 SFR,但不能用于定时器 /计数器 0和 1。
对于位寻址的 SFR中的位,C51的扩充功能支持特殊位的定义,像 SFR一样不与标准 C兼容,使用 "sbit"来定义位寻址单元。
第 7章 单片机的 C语言应用程序设计第一种格式,sbit bit-name = sfr-name^int constant;
"sbit"是定义语句的关键字,后跟一个寻址位符号名 (该位符号名必须是 MCS-51单片机中规定的位名称 ),"="后的 "sfr-name"必须是已定义过的 SFR的名字,"^"后的整常数是寻址位在特殊功能寄存器 "sfr-name"中的位号,必须是 0~7范围中的数。例如:
sfr PSW=0xD0 ; /* 定义 PSW寄存器地址为 D0H */
sbit OV=PSW^2 ; /* 定义 OV位为 PSW.2,地址为 D2H */
sbit CY=PSW^7 ; /* 定义 CY位为 PSW.7,地址为 D7H */
第 7章 单片机的 C语言应用程序设计第二种格式,sbit bit-name = int constant^int constant;
"="后的 int constant为寻址地址位所在的特殊功能寄存器的字节地址,"^"符号后的 int constant为寻址位在特殊功能寄存器中的位号。例如:
sbit OV=0XD0^2 ; /* 定义 OV位地址是 D0H字节中的第 2位 */
sbit CY=0XD0^7 ; /* 定义 CY位地址是 D0H字节中的第 7位 */
第 7章 单片机的 C语言应用程序设计第三种格式,sbit bit-name = int constant;
"="后的 int constant为寻址位的绝对位地址。例如:
sbit OV=0XD2 ; /* 定义 OV位地址为 D2H */
sbit CY=0XD7 ; /* 定义 CY位地址为 D7H */
特殊功能位代表了一个独立的定义类,不能与其它位定义和位域互换。
第 7章 单片机的 C语言应用程序设计
7.5 MCS-51并行接口的 C51定义
MCS-51系列单片机并行 I/O接口除了芯片上的 4个 I/O口 (P0 ~
P3)外,还可以在片外扩展 I/O口。 MCS-51单片机 I/O口与数据存储器统一编址,即把一个 I/O口当作数据存储器中的一个单元来看待。
使用 C51进行编程时,MCS-51片内的 I/O口与片外扩展的 I/O
可以统一在一个头文件中定义,也可以在程序中 (一般在开始的位置 )进行定义,其定义方法如下:
对于 MCS-51片内 I/O口按特殊功能寄存器方法定义。例如:
sfr P0=0x80 ; /* 定义 P0口,地址为 80H */
sfr P1=0x90 ; /* 定义 P1口,地址为 90H */
第 7章 单片机的 C语言应用程序设计对于片外扩展 I/O口,则根据硬件译码地址,将其视作为片外数据存储器的一个单元,使用 #define语句进行定义。例如
#include <absacc.h>
#define PORTA XBYTE [0xFFC0]
absacc.h是 C51中绝对地址访问函数的头文件,将 PORTA定义为外部 I/O口,地址为 FFC0H,长度为 8位。
一旦在头文件或程序中对这些片外 I/O口进行定义后,在程序中就可以自由使用变量名与其实际地址的联系,以便使程序员能用软件模拟 MCS-51的硬件操作。
第 7章 单片机的 C语言应用程序设计
(1) 位变量 C51定义。使用 C51编程时,定义了位变量后,就可以用定义了的变量来表示 MCS-51的位寻址单元。
位变量的 C51定义的一般语法格式如下:
位类型标识符 (bit) 位变量名;
例如:
bit direction_bit ; /* 把 direction_bit定义为位变量 */
bit look_pointer ; /* 把 look_pointer定义为位变量 */
7.6 位变量的 C51定义第 7章 单片机的 C语言应用程序设计
(2) 函数可包含类型为 "bit"的参数,也可以将其作为返回值。
例如:
bit func(bit b0,bit b1) /* 变量 b0,b1作为函数的参数 */
{
return (b1); /* 变量 b1作为函数的返回值 */
}
注意,使用 (#pragma disable)或包含明确的寄存器组切换
(using n)的函数不能返回位值,否则编辑器将会给出一个错误信息。

第 7章 单片机的 C语言应用程序设计
(3) 对位变量定义的限制。位变量不能定义成一个指针,如不能定义,bit * bit_pointer。不存在位数组,如不能定义,bit
b_array[ ]。
在位定义中,允许定义存储类型,位变量都被放入一个位段,此段总位于 MCS-51片内的 RAM区中。因此,存储类型限制为 data和 idata,如果将位变量的存储类型定义成其它存储类型都将编译出错。
第 7章 单片机的 C语言应用程序设计例 1 先定义变量的数据类型和存储类型:
bdata int ibase; /* 定义 ibase为 bdata整型变量 */
bdata char bary[4]; /* bary[4]定义为 bdata字符型数组 */
然后可使用 "sbit"定义可独立寻址访问的对象位:
sbit mybit0 = ibase^0 ; /* mybit0定义为 ibase的第 0位 */
sbit mybit15 = ibase^15; /* mybit0定义为 ibase的第 15位 */
sbit Ary07 = bary[0]^7 ; /* Ary07定义为 abry[0]的第 7位 */
sbit Ary37 = bary[3]^7 ; /* Ary37定义为 abry[3]的第 7位 */
第 7章 单片机的 C语言应用程序设计对象 ibase和 bary也可以字节寻址,
ary37=0; /* bary[3]的第 7位赋值为 0 */
bary[3]='a'; /* 字节寻址,bary[3] 赋值为 'a' */
sbit定义要位寻址对象所在字节基址对象的存储类型为
"bdata",否则只有绝对的特殊位定义 (sbit)是合法的。 "^"操作符后的最大值依赖于指定的基类型,对于 char/uchar而言是 0~7,
对于 int/uint而言是 0~15,对于 long/ulong而言是 0~31。
第 7章 单片机的 C语言应用程序设计
7.7 C51构造数据类型
1.基于存储器的指针基于存储器的指针以存储器类型为参量,它在编译时才被确定。
因此,为指针选择存储器的方法可以省掉,以便这些指针的长度为一个字节 (idata *,data *,pdata *)或 2个字节 (code *,xdata *)。
编译时,这类操作一般被 "行内 "(inline)编码,而无需进行库调用。
基于存储器的指针定义举例:
char xdata *px;
第 7章 单片机的 C语言应用程序设计在 xdata存储器中定义了一个指向字符型 (char)的指针变量 px。
指针自身在默认存储区 (决定于编译模式 ),长度为 2个字节 (值为
0~0xFFFF)。
char xdata *data pdx;
除了明确定义指针位于 MCS-51内部存储区 (data)外,其它与上例相同,它与编译模式无关。
data char xdata *pdx;
第 7章 单片机的 C语言应用程序设计
struct time
{ char hour ;
char min;
char sec;
struct time xdata *pxtime;
}
在结构 struct time中,除了其它结构成员外,还包含有一个具有和 struct time相同的指针 pxtime,time位于外部数据存储器
(xdata),指针 pxtime具有两个字节长度。
第 7章 单片机的 C语言应用程序设计
struct time idata *ptime ;
这个声明定义了一个位于默认存储器中的指针,它指向结构
time,time位于 idata存储器中,结构成员可以通过 MCS-51的 @R0
或 @R1 进行间接访问,指针 ptime为 1个字节长。
ptime→pxtime→hour = 12 ;
使用上面的关于 struct time和 struct idata *ptime的定义,指针
"pxtime"被从结构中间接调用,它指向位于 xdata存储器中的 time
结构。结构成员 hour被赋值为 12。
第 7章 单片机的 C语言应用程序设计
2.一般指针一般指针包括 3个字节,1个字节存储类型和 2个字节偏移地址,即地址 +0 +1 +2
内容 存储器类型 偏移地址高位字节 偏移地址低位字节其中,第一字节代表了指针的存储器类型,存储器类型编码如下:
存储器类型 idata xdata pdata data code
值 1 2 3 4 5
第 7章 单片机的 C语言应用程序设计例如,以 xdata类型的 0x1234地址为指针可以表示如下:
地址 +0 +1 +2
内容 0x02 0x12 0x34
当用常数作指针时,必须注意正确定义存储器类型和偏移量。
例如,将常数值 0x41写入地址为 0x8000的外部数据存储器。
#define XBYTE ( (char *) 0x20000L)
XBYTE[0x8000] = 0x41 ;
其中,XBYTE被定义为 (char *)0x20000L,0x20000L为一般指针,其存储类型为 2,偏移量为 0000H,这样 XBYTE成为指向
xdata零地址的指针。而 XBYTE[8000]则是外部数据存储器的
0x8000绝对地址。
第 7章 单片机的 C语言应用程序设计7.8 模块化程序开发过程图 7.4 程序开发过程第 7章 单片机的 C语言应用程序设计
7.8.1 混合编程
1.命名规则表 7.5 函数名的转换说 明 符号名 转 换 规 则
void func(void) FUNC
无参数传递或不含寄存器参数的函数名不作改变转入目标文件中,名字只是简单地转换为大写形式
void func(void) _FUNC
带寄存器参数的函数名加入,_”字符前缀,表明这类函数包含寄存器的参数传递
void func(void)
reentrant _?FUNC
对于重入函数加上,_?”字符串前缀,表明这类函数包含栈内的参数传递第 7章 单片机的 C语言应用程序设计例 2 用汇编语言编写函数 "toupper",参数传递发生在寄存器 R7中。
UPPER SEGMENT CODE ;程序段
PUBLIC _TOUPPER ;入口地址
PSEG UPPER ; 程序段
_TOUPPER,MOV A,R7 ;从 R7中取参数
CJNE A,# 'a',$+3
JC UPPERET
CJNE A,# 'z'+1,$+3
JNC UPPERET
CLR ACC,5
UPPERET,MOV R7,A ;返回值放在 R7中
RET ;返回到 C
第 7章 单片机的 C语言应用程序设计
2.参数传递规则表 7.6 参数传递的寄存器选择参数类型 char int long,float 一般指针第 1个参数 R7 R6,R7 R4~R7 R1,R2,R3
第 2个参数 R5 R4,R5 R4~R7 R1,R2,R3
第 3个参数 R3 R2,R3 无 R1,R2,R3
第 7章 单片机的 C语言应用程序设计
func1(int a) "a"是第一个参数,在 R6,R7中传递。
func2 (int b,int c,int *d ) "b"是第一个参数,在 R6,R7中传递;
"c"是第二个参数,在 R4,R5中传递; "d"是第三个参数,在 R1,
R2,R3中传递。
func3(long e,long f ) "e"是第一个参数,在 R4~R7中传递; "f"
是第二个参数,不能在寄存器中传递,只能在参数传递段中传递。
func4(float g,char h ) "g"是第一个参数,在 R4~R7中传递;
"h"是第二个参数,必须在参数传递段中传递。
第 7章 单片机的 C语言应用程序设计表 7.7 函数返回值的寄存器返 回 值 寄 存 器 说 明
bit C 进位标位
(unsigned) char R7
(unsigned) int R6,R7 高位字节在 R6,低位字节在 R7
(unsigned) long R4~R7 高位字节在 R4,低位字节在 R7
float R4~R7 32位 IEEE格式,指数和符号位在 R7
指针 R1,R2,R3 R3放存储器类型,高位在 R2,低位在 R1
第 7章 单片机的 C语言应用程序设计在汇编子程序中,当前选择的寄存器组及寄存器 ACC,B、
DPTR和 PSW都可能改变。当被 C调用时,必须无条件地假设这些寄存器的内容已被破坏。如果已在连接 /定位程序时选择了覆盖,
那么每个汇编子程序包含一个单独的程序段是必要的,因为在覆盖过程中,函数间参量通过子程序各自的段参量计算。汇编子程序的数据区甚至可包含在覆盖部分中,但应注意下面两点:
(1) 所有段名必须以 C51类似的方法建立。
(2) 每个有局部变量的汇编程序必须指定自己的数据段,这个数据段只能为其它函数访问作参数传递用。所有参数一个接一个被传递,由其它函数计算的结果保存入栈。
第 7章 单片机的 C语言应用程序设计
7.8.2 覆盖和共享
1.覆盖单片机片内存储空间有限,连接器 /定位器通常重新启用程序不再用的位置。这就是说,若一个程序不再调用,也不由其它程序调用 (甚至间接调用 ),那么在其它程序执行完之前,这个程序不再运行。这个程序的变量可以放在与其它程序完全相同的
RAM空间,很像可重用的寄存器。这种技术就是覆盖。在汇编中直接通过手工完成的这些空间分配,C语言中可以由连接器自动管理。若有几个不相关联的程序时,它可以使 RAM单元比手工考虑要用的少。
第 7章 单片机的 C语言应用程序设计
2.共享
1) 共享变量类 型 汇 编 语 言 C 语 言动态变量 y( ){int x; }
静态变量 static int x;
公用变量 PUBLIC XX,ds 2 Int x;
外部变量 EXTERN DATA(X)MOV DPTR,# X extern int x;
静态子程序 /函数 Y,… static y( ){… };
公共子程序 /函数 PUBLIC YY:
y( ) {

};
外部子程序 /函数 EXTERN CODE(Y)LCALL Y y( )
第 7章 单片机的 C语言应用程序设计
2) 共享函数 /子程序
C中函数若是全局的 (公用的 ),可以放在调用的函数之后。若函数是模块专用的,它可以定义为静态函数,这样它不能被其它模块调用。 C语言的 ANSI标准建议所有函数在主函数前要有原型
(进行说明 ),然后实际函数可在主函数之后或其它模块中。这符合自顶向下编程的概念。
汇编语言中,子程序使用标号可在给定模块的任何位置。汇编器首先扫描得到所有的符号名,然后值就可填入 LCALL或
LJMP。一个模块或另一模块共享子程序,一个使用 PUBLIC而另一个使用 EXTERN。当指定为 EXTERN,符号类型 (CODE,DATA,
XDATA,IDATA,BIT或 NUMBER)必须特别加以指定,以便连接器可以确定放在一起的正确类型。
第 7章 单片机的 C语言应用程序设计
7.8.3 库和连接器 /定位器
1,库表 7.9 Franklin C51的编译库库 说 明
C51S.LIB SMALL模式,无浮点运算
C51FPS.LIB 浮点数学运算库 (SMALL模式 )
C51C.LIB COMPACT模式,无浮点运算
C51FPC.LIB 浮点运算库 (COMPACT模式 )
C51L.LIB LARGE模式,无浮点运算
C51FPL.LIB 浮点运算库 (LARGE模式 )
第 7章 单片机的 C语言应用程序设计
2.连接器 /定位器
1) 组合程序模块将几个不同程序模块组合为一个模块,并自动从库中挑选模块嵌入目标文件。输入文件按命令行中出现的顺序处理。通常的程序模块是由 C51编译器或 A51宏汇编生成的可重入的目标文件。
第 7章 单片机的 C语言应用程序设计
2) 组合段将具有相同段名的可重定位段组合成单一的段。在一个程序模块中定义的一个段成为部分段。一个部分段在源文件中以下列形式指定:
(1) 名字 每个重定位段有一个名字,它可与来自其它模块的同名的可重定位段组合。绝对段没有名字。
(2) 类型 类型表明段所属的地址空间 CODE,XDATA,
DATA或 BIT。
第 7章 单片机的 C语言应用程序设计
(3) 定位方式 可重定位段的定位方式有 PAGE,INPAGE,
INBLOCK,BITADD RESSABLE或 UNIT。 INPAGE表明段必须放入一页 (高 8位地址相同 )中以使用短转移和调用指令。 INBLOCK段应使用 ACALL,必须放在 2048字节块中。因为没有连接器可以灵活地判知调用和转移是否在块内。可重定位的其它限制是:
PAGE--不能超过 256字节; BITADDRESSABLE--必须放在可位寻址的内部 RAM空间; UNIT--允许段从任意字节开始 (对位变量是位 )。
(4) 长度 一个段的长度。
第 7章 单片机的 C语言应用程序设计
(5) 基址 段的首址。对于绝对段,地址由汇编器赋予,对于可重定位段,地址由 L51决定。在处理程序模块时,L51自动产生段表 (MAP),该表包含了每个段的类型、基址、长度、可重定位性和名字。 L51自动将所具有相同名字的所有部分段组合到单一可重定位段中。例如,三个程序模块包含字段 VAR,在组合时,
三个段的长度相加,从而组合段的长度也增加了。对组合段有下列规则:
① 所有具有相同名的部分段必须有相同类型 (CODE,DATA,
IDATA,XDATA或 BIT)。
② 组合段的长度不能超过存储区的物理长度。
③ 每个组合的部分段的定位方法也必须相同。
④ 绝对段相互不组合,它们被直接拷贝到输出文件。
第 7章 单片机的 C语言应用程序设计
3) 存储器分配物理存储区 最大长度 地址区 段类型程序 64 KB 0~0FFFFH CODE
外部数据 64 KB 0~0FFFFH XDATA
直接寻址片内数据 128字节 0~7FH DATA
间接寻址片内数据 256字节 0~0FFH IDATA
片内数据的位空间 128位 0~7FH BIT
表 7.10 MCS-51系列的物理存储区第 7章 单片机的 C语言应用程序设计
4) 采用覆盖技术使用数据存储器通过采用一定的覆盖技术,MCS-51系列少量的片内数据存储器可由 L51有效地使用。由 C51编译器或是 A51汇编器生成的参数和局部变量 (若使用它们的函数不相互调用 )可在存储器中覆盖。这样,所用的存储器得到相当程度地减少。
为完成数据覆盖,L51分析所有不同函数间的调用,使用该信息可以确定哪个数据和位段可被覆盖。使用控制参数
OVERLAY和 NOOVERLAY可允许或禁止覆盖。 OVERLAY是默认值,用它可产生非常紧凑的数据区。
第 7章 单片机的 C语言应用程序设计
5) 决定外部参考地址具有相同名的外部符号 (EXTERN)和公用符号 (PUBLIC)被确定后,外部符号指向其它模块中的地址。一个已声明的外部符号用具有相同名字的功用符号确定,外部参考地址由其公共参考地址确定。这还与类型 (DATA,IDATA,XDATA,CODE,
BIT或 NUMBER)有关,如果类型不符或未发现外部符号参考地址的公用符号,则会产生错误。公用符号的绝对地址在段定位后决定。
第 7章 单片机的 C语言应用程序设计
6) 绝对地址计算定义绝对地址并计算可重定位段的地址。在段分配和外部公用参考地址处理完后,程序模块中所有可重定位地址和外部地址要进行计算,此时生成的目标文件中的符号信息 (DEBUG)
被改变以反映新的值。
第 7章 单片机的 C语言应用程序设计
7) 产生绝对目标文件可执行程序以绝对目标格式产生。该绝对目标文件可包含附加的符号信息 (DEBUG),从而使符号调试成为可能。符号信息可用参数 NODEBUGSYMBOLS,NODEBUGPUBLICS和
NODEBUGLINES禁止。输出文件是可执行的,并可由仿真器装入调试或被 OHS51翻译为 Intel HEX格式文件以供 EPROM固化。
第 7章 单片机的 C语言应用程序设计
8) 产生映像文件产生一个反映每个处理步骤的映像文件,它显示有关连接 /
定位过程的信息和程序符号,并包含一个公用和外部符号的交叉参考报告。映像文件包含下列信息:
① 文件名和命令行参数。
② 模块的文件名和模块名。
③ 一个包含段地址、类型、定位方法和名字的存储器分配表。该表可在命令行中用 NOMAP参考禁止。
第 7章 单片机的 C语言应用程序设计
④ 段和符号的所有错误列表。列表文件末尾显示出所有出错的原因。
⑤ 一个包含输入文件中符号信息的符号表。该信息由
MODULES,SYMBOLS,PUBLICS和 LINES名组成,LINES是 C
编译器产生的行号。符号信息可用参数 NOSYMBOLS,
NOPUBLICS和 NOLINES完全或部分禁止。
⑥ 一个按字母顺序排列的有关所有 PUBLIC和 EXTERN符号的交叉参考报告,其中显示出符号类型和模块名。第一个显示的模块名是定义了 PUBLIC符号的模块,后面的模块名是定义了
EXTERN符号的模块。在命令行输入参数 IXREF可产生此报告。
⑦ 在连接器 /定位器运行期间检测到的错误同时显示在屏幕和文件尾部。
第 7章 单片机的 C语言应用程序设计
7.8.4 程序优化以下选择对提高程序效率有很大影响:
(1) 尽量选择小存储模式以避免使用 MOVX指令。
(2) 使用大模式 (COMPACT/LARGE)应仔细考虑要放在内部数据存储器的变量要求是经常用的或是用于中间结果的。访问内部数据存储器要比访问外部数据存储器快得多。内部 RAM由寄存器组、位数据区和其它用户用,data”类型定义的变量共享。由于内部 RAM容量的限制 (128~256字节,由使用的单片机决定 ),必须权衡利弊以解决访问效率和这些对象的数量之间的矛盾。
第 7章 单片机的 C语言应用程序设计
(3) 要考虑操作顺序,完成一件事后再做一件事。
(4) 注意程序编写细则。例如,若使用 for(;; )循环,DJNZ指令比 CJNE指令更有效,可减少重复循环次数。
(5) 若编译器不能使用左移和右移完成乘除法,应立即修改,
例如,左移为乘 2。
(6) 用逻辑 AND/&取模比用 MOD / %操作更有效。
(7) 因计算机基于二进制,仔细选择数据存储器和数组大小可节省操作。
第 7章 单片机的 C语言应用程序设计
(8) 尽可能使用最小的数据类型,MCS-51系列是 8位机,显然对具有 "char"类型的对象的操作比 "int"或 "long"类型的对象的操作要方便得多。
(9) 尽可能使用 "unsigned"数据类型。 MCS-51系列 CPU并不直接支持有符号数的运算。因而 C51编译器必须产生与之相关的更多的程序代码以解决这个问题。
(10) 尽可能使用局部函数变量。编译器总是尝试在寄存器里保持局部变量。这样,将循环变量 (如 for和 while循环中的计数变量 )说明为局部变量是最好的。使用 "unsigned char/int"的对象通常能获得最好的结果。
第 7章 单片机的 C语言应用程序设计
7.9 MCS-51内部资源使用的 C语言编程
7.9.1 中断应用的 C语言编程
C51编译器支持在 C源程序中直接开发中断程序。中断服务程序是通过按规定语法格式定义的一个函数。
中断服务程序的函数定义的语法格式如下:
返回值 函数名 ([参数 ]) interrupt m[using n]
{
}

第 7章 单片机的 C语言应用程序设计表 7.11 MCS-51中断源编号编 号 中 断 源 入 口 地 址
0 外部中断 0 0003H
1 定时器 /计数器 0 000BH
2 外部中断 1 0013H
3 定时器 /计数器 1 001BH
4 串行口中断 0023H
第 7章 单片机的 C语言应用程序设计
using n 选项用于实现工作寄存器组的切换,n是中断服务子程序中选用的工作寄存器组号 (0 ~ 3)。在许多情况下,响应中断时需保护有关现场信息,以便中断返回后,能使中断前的源程序从断点处继续正确地执行下去。这在 MCS-51单片机中,能很方便地利用工作寄存器组的切换来实现。即在进入中断服务程序前的程序中使用一组工作寄存器,进入中断服务程序后,由 "using n"
切换到另一组寄存器,中断返回后又恢复到原寄存器组。这样互相切换的两组寄存器中的内容彼此都没有被破坏。
第 7章 单片机的 C语言应用程序设计图 7.5 扩展多个中断源例 3 图 7.5所示是利用优先权解码芯片,在单片机 8031的一个外部中断 INT1上扩展多个中断源的原理电路图。图中是以开关闭合来模拟中断请求信号。当有任一中断源产生中断请求,能给 8031的 INT1引脚送一个有效中断信号,由 P1的低 3位可得对应中断源的中断号。
第 7章 单片机的 C语言应用程序设计在中断服务程序中仅设置标志,并保存 I/O口输入状态。
Franklin C51编译器提供定义特定 MCS-51系列成员的寄存器头文件。 MCS-51头文件为 reg51.h。 C51程序如下:
# include <reg51.h>
unsigned char status;
bit flag;
void service_int1( ) interrupt 2 using 2 /* INT1中断服务程序,使用第 2组工作寄存器 */
{ flag=1; /* 设置标志 */
status=p1; /* 存输入口状态 */
}
void main(void)
{ IP=0x04 ; /* 置 INT1为高优先级中断 */
IE=-0x84 ; /* INT1开中断,CPU开中断 */
第 7章 单片机的 C语言应用程序设计
for(; ; )
{ if(flag) /* 有中断 */
{ switch(status) /* 根据中断源分支 */
{ case 0,break ; /* 处理 IN0 */
case 1,break ; /* 处理 IN1 */
case 2,break; /* 处理 IN2 */
case 3,break; /* 处理 IN3 */
default,;
}
flag=0 ; /* 处理完成清标志 */
}
}
}
第 7章 单片机的 C语言应用程序设计
7.9.2 定时器 /计数器 (T/C)应用的 C语言编程例 4 设单片机的 fosc=12 MHz晶振,要求在 P1.0脚上输出周期为 2 ms的方波。
周期为 2 ms的方波要求定时时间隔 1 ms,每次时间到 P1.0取反。
机器周期 =12/fosc=1 μs
需计数次数 =1000/(12/fosc)=1000/1=1000
由于计数器是加 1计数,为得到 1000个计数之后的定时器溢出,必须给定时器置初值为 -1000(即 1000的补数 )。
第 7章 单片机的 C语言应用程序设计
(1) 用定时器 0的方式 1编程,采用查询方式,程序如下:
# include <reg51.h>
sbit P1_0=P1^0 ;
void main(void)
{ TMOD=0x01 ; /* 设置定时器 1为非门控制方式 1*/
TR0=1 ; /* 启动 T/C0 */
for( ; ; )
{ TH0= -(1000/256) ; /* 装载计数器初值 */
TL0= -(1000%256) ;
do { } while (!TF0) ; /* 查询等待 TF0置位 */
P1_0=!P1_0; /* 定时时间到 P1.0反相 */
TF0=0; /* 软件清 TF0 */
}
}
第 7章 单片机的 C语言应用程序设计
(2) 用定时器 0的方式 1编程,采用中断方式。程序如下:
# include <reg51.h>
sbit P1_0=P1^0 ;
void time (void) interrupt 1 using 1 /* T/C0中断服务程序入口 */
{ P1_0=!P1_0 ; /* P1.0取反 */
TH0= -(1000/256); / * 重新装载计数初值 */
}
void main( void )
{ TMOD=0x01 ; /* T/C0工作在定时器非门控制方式 1 */
P1_0=0;
TH0= -(1000/256 ); /* 预置计数初值 */
TL0= -(1000%256) ;
EA=1 ; /* CPU中断开放 */
ET0= 1 ; /* T/C0中断开放 */
TR0=1 ; /* 启动 T/C0开始定时 */
do { } while(1) ; /* 等待中断 */
}
第 7章 单片机的 C语言应用程序设计例 5 采用 10 MHz晶振,在 P1.0脚上输出周期为 2.5 s,占空比 20%的脉冲信号。
10 MHz晶振,使用定时器最大定时几十毫秒。取 10 ms定时,
周期 2.5 s需 250次中断,占空比 20%,高电平应为 50次中断。
10 ms定时,晶振 fosc=10 MHz。
需定时器计数次数 =10× 103× 10/12=8333
# include <reg51.h>
# define uchar unsigned char
uchar period=250;
uchar high=50;
第 7章 单片机的 C语言应用程序设计
timer0( )interrupt 1 using 1 /* T/C0中断服务程序 */
{TH0= - 8333/256 ; /* 重置计数值 */
TL0= - 8333%256 ;
if(++time==high)P1=0; /* 高电平时间到变低 */
else if (time==period) /* 周期时间到变高 */
{time=0 ;
P1=1 ;
}
}
第 7章 单片机的 C语言应用程序设计
main( )
{ TMOD=0x01 ; /* 定时器 0方式 1 */
TH0= - 8333/256 ; /* 预置计数初值 */
TL0= - 8333%256 ;
EA=1; /* 开 CPU中断 */
ET0=1 ; /* 开 T/C0中断 */
TR0=1 ; /* 启动 T/C0 */
do { }while(1) ;
}
第 7章 单片机的 C语言应用程序设计图 7.6 中断服务程序流程图第 7章 单片机的 C语言应用程序设计产生一个占空比变化脉冲信号的程序,它产生的脉宽调制信号用于电机变速控制。
# include <reg51.h>
# define uchar unsigned char
# define uint unsigned int
unchar time,status,percent,period;
bit one_round;
uint oldcount,target=500;
void pulse (void) interrupt 1 using 1 /* T/C0中断服务程序 */
{ TH0= - 833/256 ; /* 1ms - -10 MHz */
第 7章 单片机的 C语言应用程序设计
TL0= - 833%256 ;
ET0=1 ;
if (++time=percent) P1=0 ;
else if (time= =100 )
{ time=0 ; P1=1; }
}
void tachmeter ( void ) interrupt 2 using 2 /* 外中断 1服务程序 */
{ union
{ unit word ;
struct { uchar hi ; uchar lo ; }
byte ; }
第 7章 单片机的 C语言应用程序设计
newcount ;
newcount,byte,hi=TH1;
newcount,byte,lo=TL1 ;
period=newcount,word - oldcount ; /* 测得周期 */
oldcount=newcount,word ;
one_round=1; /* 每转一圈,引起中断,设置标志 */
}
void main ( void )
{ IP=0x04 ; /* 置 INT1为高位优先级 */
TMOD= 0x11 ; /* T0,T1 16位方式 */
TCON=0x54 ; /* T0,T1运行,IT1边沿触发 */
第 7章 单片机的 C语言应用程序设计
TH1=0; TL1=0; /* 设置初始计数值 */
IE=0x86 ; /* 允许中断 EX1,ET0 */
for (; ; )
{ if (one_round ) /* 每转一圈,调整 */
{ if ( period <target )
{if (percent <100 ) ++percent ; /* 占空比增 */
}
else if ( percent >0 ) - -percent ; / * 占空比减 */
one_round=0 ;
}
}
}
第 7章 单片机的 C语言应用程序设计
7.9.3 串行口使用的 C语言编程例 6 单片机 fosc=11.0592 MHz,波特率为 9600,各设置 32字节的队列缓冲区用于发送接收。设计单片机和终端或另一计算机通信的程序。
单片机串行口初始化成 9600波特,中断程序双向处理字符,程序双向缓冲字符。背景程序可以 "放入 "和 "提取 "在缓冲区的字符串,
而实际传入和传出 SBUF的动作由中断完成。
Loadmsg 函数加载缓冲数组,标志发送开始。缓冲区分发 (t)
和收 (r)缓冲,缓冲区通过两种指示 (进 in和出 out)和一些标志 (满 full,
空 empty,完成 done)管理。队列缓冲区 32字节接收缓冲 (r_buf)区满,
不能再有字符插入。当 t_in=t_out,发送缓冲区 (t_buf)空,发送中断清除,停止 UART请求。具体程序如下:
第 7章 单片机的 C语言应用程序设计
# include <reg51.h>
# define uchar unsigned char
uchar xdata r_buf[32] ; /* item1 */
uchar xdata t_buf[32] ;
uchar r_in,r_out,t_in,t_done ; /* 队列指针 */
bit r_full,t_empty,t_done ; /* item2 */
code uchar m[ ]={ " this is a test program \r\n "} ;
serial ( ) interrupt 4 using 1 /* item3 */
{if( RI && ~ r_full )
{r_buf[r_in]=SBUF ;
RI=0 ;
第 7章 单片机的 C语言应用程序设计
r_in= ++r_in & ox1f ;
if ( r_in= =r_out ) r_full=1;
}
else if (TI && ~t_empty )
{SBUF=t_buf [t_out] ;
TI=0;
t_out = ++ t_out & 0x1f ;
i f ( t_out= =t_in ) t_empty=1 ;
}
else if (TI )
{ TI=0;
t_done=1 ;
}
}
第 7章 单片机的 C语言应用程序设计
void loadmsg (uchar code * msg ) /* item4 */
{while ((*msg !=0 )&& (((( t_in+1)^t_out ) & 0x1f ) !=0 )) / *测试缓冲区满 */
{ t_ buf [t_in]= * msg ;
msg++ ;
t_in = ++ t_in & 0x1f ;
if ( t_done )
{TI = 1 ;
t_empty =t_done =0 ; /* 完成重新开始 */
}
}
}
第 7章 单片机的 C语言应用程序设计
void process (uchar ch ) { return ; } / * item5 */
/* 用户定义 */
void processmsg ( void ) /* item6 */
{while ((( r_out+1 ) ^ r_in) !=0 )
/* 接收非缓冲区 */
{process ( r_buf [r_out ] ) ;
r_out= ++r_out & 0x1f ;
}
}
第 7章 单片机的 C语言应用程序设计
main ( ) /* item7 */
{TMOD=0x20 ; /* 定时器 1方式 2 */
TH1=0xfd ; /* 9600波特 11.0592 MHz */
TCON=0x40 ; /* 启动定时器 1 */
SCON=0x50 ; /* 允许接收 */
IE=0x90 ; /* 允许串行口中断 */
t_empty=t_done=1 ;
r_full =0 ;
r_out=t_in =0 ;
r_ in=1 ; /* 接收缓冲和发送缓冲置空 */
for ( ; ; )
{loadmsg ( & m ) ;
processmsg ( );
}
}
第 7章 单片机的 C语言应用程序设计
item1,背景程序 "放入 "和 "提取 "字符队列缓冲区。
item2,缓冲区状态标志。
item3,串行口中断服务程序,从 RI,TI判别接收或发送中断,由软件清除。判别缓冲区状态 (满 full,空 empty)和全部发送完成 (done)。
item4,此函数把字符串放入发送缓冲区,准备发送。
item5,接受字符的处理程序,实际应用自定义。
item6,此函数逐一处理接收缓冲区的字符。
item7,主程序即背景程序,进行串行口的初始化,载入字符串,处理接收的字符串。
第 7章 单片机的 C语言应用程序设计
7.10 MCS-51片外扩展的 C语言编程
7.10.1 8255与 8031接口 C应用程序举例例 7 8255控制打印机。
图 7.7是 8031扩展 8255与打印机接口的电路。 8255的片选线为
P0.7,打印机与 8031采用查询方式交换数据。打印机的状态信号输入给 PC7,打印机忙时 BUSY=1,微型打印机的数据输入采用选通控制,当 ---STB上负跳变时数据被输入。 8255采用方式 0由 PC0
模拟产生 ---STB信号。
按照接口电路,口 A地址为 7CH,口 C地址为 7EH,命令口地址为 7FH,PC7~PC4输入,PC3~PC0输出。方式选择命令字为 8EH。
第 7章 单片机的 C语言应用程序设计图 7.7 8031扩展 8255与打印机接口的电路第 7章 单片机的 C语言应用程序设计向打印机输出字符串 "WELCOME"的程序如下:
# include <absacc.h>
# include <reg51.h>
# define uchar unsigned char
# define COM8255 XBYTE[0x007f ] /* 命令口地址 */
# define PA8255 XBYTE[ 0x007c] /* 口 A地址 */
# define PC8255 XBYTE[ 0x007e] /* 口 C 地址 */
void toprn ( uchar *p ) / * 打印字符串函数 */
{ while ( * p!= '\0')
{while (( 0x80 & PC8255 )! =0 ) ; /* 查询等待打印机的 BUSY状态 */
第 7章 单片机的 C语言应用程序设计
PA8255 = * p ; /* 输出字符 */
COM8255 = 0x00 ; /* 模拟 STB脉冲 */
COM8255=0x01 ;
p++ ;
}
}
void main ( void )
{ uchar idata prn [ ]= "WELCOME"; /* 设测试用字符串 */
COM8255=0x8e ; /* 输出方式选择命令 */
toprn ( prn ) ; /* 打印字符串 */
}
第 7章 单片机的 C语言应用程序设计例 8 EPROM编程器。
由 8031扩展 1片 EPROM2716,2片 SRAM6116及 1片 8255构成
EPROM编程器,编程对象是 EPROM2732。扩展编程系统中 2716
用来存放固化用监控程序,用户的待固化程序放在 2片 6116中。
8255的口 A作编程器数据口,口 B输出 2732的低 8位地址,
PC3~PC0输出 2732高 4位地址,PC7作 2732启动保持控制器与 PGM
连接。
译码地址为,6116(1),0800H; 6116(2),1000H~17FFH;
8255的口 A,07FCH;口 B,07FDH;口 C,07FEH;命令口:
07FFH。
第 7章 单片机的 C语言应用程序设计
8255的口 A、口 B、口 C均工作在方式 0输出,方式选择命令字为 80H; 2732的启动编程和停止编程,由 PC7的复位 /置位控制,
当 PC7=0时启动编程,PC7=1时,编程无效。
EPROM编程如下所示,参数为 RAM起始地址,EPROM起始地址和编程字节数。
# include < absacc.h >
# include <reg51.h >
# define COM8255 XBYTE [0x07ff ]
# define PA8255 XBYTE [0x07fc]
# define PB8255 XBYTE [0x07fd ]
# define PC8255 XBYTE [0x07fe ]
# define uchar unsigned char
# define uint unsigned int
第 7章 单片机的 C语言应用程序设计
void d1_ms ( unit x ) ;
void program (ram,eprom,con )
uchar xdata * ram ; /* RAM起始地址 */
uint eprom,con ; /* EPROM起始固化地址,固化长度 */
{ int i ;
COM8255=0x08 ; /* 送方式选择命令字 */
COM8255=0x0f ; /* PC7=1 */
for (i=0 ; i<con ; i++ )
{ PA8255= * ram ; /* 固化内容口 A锁存 */
PB8255=eprom % 256 ; /* 2732地址低 8位 */
PC8255=eprom /256 ; /* 2732地址高 4位 */
第 7章 单片机的 C语言应用程序设计
eprom ++ ;
ram ++ ;
COM8255=0x0e ; /* PC7=0 */
d1_ms (50 ) ;
COM8255=0x0f ; /* PC7=1 */
}
}
main ( )
{ program ( 0x1000,0x0000,0x0100 ) ;
}
第 7章 单片机的 C语言应用程序设计7.10.2 MCS-51数据采集的 C语言编程例 9 ADC0809与 8031接口的数据采集程序举例。

7.
8A
DC
08
09

80
31
的接口电路第 7章 单片机的 C语言应用程序设计程序如下:
# include <absacc.h >
# include <reg51.h >
# define uchar unsigned char
# define IN0 XBYTE [ 0x7ff8 ] /* 设置 AD0809的通道 0地址 */
sbit ad_busy =P3^3 ; /* 即 EOC状态 */
void ad0809 ( uchar idata *x ) /* 采样结果放指针中的 A/D采集函数 */
{ uchar i ;
uchar xdata *ad_adr ;
ad_adr= & IN0 ;
for ( i=0 ; i<8 ; i++ ) /* 处理 8通道 */
{ *ad_adr=0 ; /* 启动转换 */
第 7章 单片机的 C语言应用程序设计
i=i ; /* 延时等待 EOC变低 */
i=i ;
while (ad_busy = =0 ) ; /* 查询等待转换结束 */
x[i ]= * ad_adr ; /* 存转换结果 */
ad_adr ++ ; /* 下一通道 */
}
}
void main ( void )
{ static uchar idata ad [ 10 ] ;
ad0809 ( ad ) ; /* 采样 AD0809通道的值 */
}
第 7章 单片机的 C语言应用程序设计例 10 AD574与 8031接口的数据采集程序举例。

7.
9AD
57
4

80
31
的接口电路第 7章 单片机的 C语言应用程序设计源程序如下:
# include < absacc.h >
# inlucde < reg51.h >
# define uint unsigned int
# define ADCOM XBYTE[ 0xff7c ] /* 使 A0=0,R/C=0,CS=0 */
# define ADLO XBYTE [ 0xff7f ] /* 使 R/C =1,A0=1,CS=0 */
# define ADHI XBYTE [ 0xff7d ] /* 使 R/C=1,A0=0,CS =0 */
sbit r = P3 ^ 7 ;
sbit w = P3 ^ 6 ;
sbit adbusy = P1 ^ 0 ;
第 7章 单片机的 C语言应用程序设计
uint ad574 ( void ) /* AD574转换器 */
{ r = 0 ; /* 产生 CE=1 */
w = 0 ;
ADCOM = 0 ; /* 启动转换 */
while ( adbusy = =1 ) ; /* 等待转换 */
return ( ( uint )(ADHI<<4 )+( ADLO &0x0f ) ); /* 返回 12位采样值 */
}
main ( )
{ uint idata result ;
result =ad574 ( ) ; /* 启动 AD574进行一次转换,得转换结果 */
}
第 7章 单片机的 C语言应用程序设计7.10.3 MCS-51输出控制的 C语言编程例 11 8031与 DAC0832双缓冲接口的数据转换程序举例 。

7.
10DA
C
08
32
的双缓冲接口电路第 7章 单片机的 C语言应用程序设计将 data1和 data2数据同时转换为模拟量的 C51程序如下:
# include < absacc.h >
# include < reg51.h >
# define INPUTR1 XBYTE[ 0x8fff ]
# define INPUTR2 XBYTE[ 0xa7ff]
# define DACR XBYTE [0x2fff ]
# define uchar unsigned char
void dac2b (data1,data2 )
uchar data1,data2 ;
{
INPUTR1 = data1 ; /* 送数据到一片 0832 */
INPUTR2 = data2 ; /* 送数据到另一片 0832 */
DACR= 0 ; /* 启动两路 D/A 同时转换 * /
}
第 7章 单片机的 C语言应用程序设计例 12 8031与 DAC0832单缓冲区接口的数据转换举例。
图 7.11 DAC0832与 8031 的单缓冲接口第 7章 单片机的 C语言应用程序设计按片选线确定 FFFEH为 DAC0832的端口地址。使运行输出端输出一个锯齿波电压信号的 C51程序如下:
# include < absacc.h >
# include < reg51.h >
# define DA0832 XBYTE [0xfffe ]
# define uchar unsigned char
# define uint unsigned int
void stair (void )
{ uchar i ;
while ( 1 )
{for ( i=0 ; i<=255 ; i=I++ ) /* 形成锯齿波输出值,最大值为 255 */
{ DA0832 = i ; /* D/A转换输出 */
}
}
}
第 7章 单片机的 C语言应用程序设计例 13 8031与 AD7521接口的数据转换程序举例。
图 7.12 AD7521与 8031的接口第 7章 单片机的 C语言应用程序设计使 AD7521输出梯形波的 C51程序如下:
# include < absacc.h >
# include < reg51.h >
# define DA7521L XBYTE[0x7fff ]
# define DA7521H XBYTE[0xbfff ]
# define UP 0x010
# define T 1000
# define uint unsigned int
第 7章 单片机的 C语言应用程序设计
void dlms ( uint a );
void stair(void)
{ uint i ;
for ( i=0 ; i<=4095 ; i=i+UP ) /* 以阶高增量增值,形成梯形波输出值,
最大 4095*/
{ DA7521L= i % 256 ; /* 送低 8位数据到第一级缓冲器 */
DA7521H= i /256 ; /* 送高 4位数据到高 4位缓冲器,同时送低 8位到第二级
/* 缓冲转换 */
dlms ( T ) ; /* 延时 */
}
}
第 7章 单片机的 C语言应用程序设计
7.11 频率量测量的 C语言编程
7.11.1 测量频率法测量频率法的最简单的接口电路,可将频率脉冲直接连接到
MCS-51的 T1端,将 8031的 T/C0用作定时器,T/C1用作计数器。
在 T/C0定时时间里,对频率脉冲进行计数。 T/C1的计数值便是单位定时时间里的脉冲个数。
第 7章 单片机的 C语言应用程序设计定时输入脉冲
< T
图 7.13测量频率中的脉冲丢失第 7章 单片机的 C语言应用程序设计例 14 带同步控制的频率测量。
图 7.14 带同步控制的频率测量法接口第 7章 单片机的 C语言应用程序设计控制时,首先 P1.0发一个清零负脉冲,使 U1,U2两个 D触发器复位,其输出封锁与门 G1和 G2。接着由 P1.1发一个启动正脉冲,其有效上升沿使 U1=1,门 G1被开放。之后,被测脉冲上升沿通过 G2送 T1计数;同时 U2输出的高电平使 INT0 =1,定时器 0
的门控 GATE有效,启动 T/C0开始定时。直到定时结束时,从
P1.0发一负脉冲,清零 U2,封锁 G2,停止 T/C1计数,完成一次频率采样过程。
第 7章 单片机的 C语言应用程序设计测量 T/C定时时间为 500 ms,这样长的时间定时,先由 T/C0
定时 100 ms,之后软件 5次中断后的时间即为 5× 100 ms=500 ms。
中断次数的计数值在 msn中。
T/C0定时 100 ms的计数初值,03B0H。计数器 1采用 16位计数。设 T/C0为高优先级,允许计数中断过程定时中断,即定时时间到就中止计数。 tf为 500 ms定时时间到标志。程序如下:
第 7章 单片机的 C语言应用程序设计
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
#define A 5 /*500 ms 的中断次数 */
sbit P1_0=P1^0;
sbit P1_1=P1^1;
uchar msn=A;
bit idata tf=0; /*500 ms时间到标志 */
第 7章 单片机的 C语言应用程序设计
uint count(void)
{
P1_0=0 ; P1_0=1; /*产生清零用负脉冲 */
TMOD=0x59 ;
TH1=0x00; TL1=0x00; /* T/C1计数器 */
TH0=0x3c ; TL0=0xb0; /* T/C0定时器 100 ms */
TR0=1; TR1=1; PT0=1; ET0=1; ET1=1; EA=1; /*启动 T/C,开中断 */
P1_1=0 ; P1_1=1 ; /* 产生启动正脉冲 */
while (tf!=1 ) ; /* 等待 500 ms定时到 */
P1_0=0 ; P1_0=1 ; /* 产生负脉冲,封锁 G2 */
TR0=0 ; TR1=0 ; /* 关 T/C */
return (TH1*256+TL1) ; / 返回计数值 */
}
第 7章 单片机的 C语言应用程序设计
void timer0( void ) interrupt 1 using 1 /* 100 ms定时中断服务 */
{ TH0=0x3c ; /* 重置初值 */
TL0=-0xb0 ;
msn - - ;
if ( msn = = 0) { msn=A ; tf=1 ; } /* 500 ms定时时间到设标志 */
}
void timer 1 (void ) interrupt 3 { }
void main ( void )
{
float rate ;
rate=( 10/A )* count( ) ; /* 得每秒的计数率 */
}
第 7章 单片机的 C语言应用程序设计
7.11.2 频率脉冲的测量周期法图 7.15 周期测量接口第 7章 单片机的 C语言应用程序设计图 7.16 频率与周期波第 7章 单片机的 C语言应用程序设计例 15 测量周期的程序举例。
设 fosc = 6 MHz,机器周期为 2 μs,测周期的测量值为计数值乘以 2。用 C语言编写的程序如下:
#include<reg51.h>
#define uint unsigned int
sbit P1_0=P1^0;
uint count,period;
bit rflag=0; / * 周期标志 */
void control (void)
{
第 7章 单片机的 C语言应用程序设计
TMOD=0x09; /*定时器 /计数器 0为方式 1*/
IT0=1; TR0=1;
TH0=0; TL0=0;
P1_0=0; P1_0=1; /*触发器清零 */
TR0=1; ET0=1; EA=1; /*启动 T/C0开中断 */
}
void int_0(void)interrupt 0 using 1 /* INT0 中断服务 */
{
EA=0; TR0=0;
count=TL0+TH0*256; /* 取计数值 */
rflag=1; /* 设标志 */
EA=1;
}
第 7章 单片机的 C语言应用程序设计
void main(void)
{
contro1( );
while(rflag==0); /* 等待一周期 */
period=count*2; /* fosc=6 MHz,2 μs计数增 1,周期值单位 μs */
}
第 7章 单片机的 C语言应用程序设计
7.12 MCS-51机间通信的 C语言编程
7.12.1 点对点的串行异步通信
1.通信双方的硬件连接图 7.17 8031间 RS—232C电平信号的传第 7章 单片机的 C语言应用程序设计2.通信双方的约定图
7.
18
点对点通信的程序框图第 7章 单片机的 C语言应用程序设计
3,点对点通信编程点对点通信双方基本等同,只是人为规定一个为发送,一个为接收。要求两机串行口的波特率相同,因而发送和接收方串行口的初始化相同。可编制含有初始化函数、发送函数接收函数的程序,在主函数中根据程序的发送、接收设置 TR,采用条件判别决定使用发送函数还是接收函数。这样点对点通信的双方都可运行此程序,只需在程序运行之前人为设置选择 TR,
一个令 TR=0,一个令 TR=1,然后分别编译,在两机上分别装入,同时运行。
第 7章 单片机的 C语言应用程序设计例 16 点对点通信。
点对点通信的程序如下:
#include<reg51.h>
#define uchar unsigned char
#define TR 1 /*发送接收差别值 TR=0发送 */
uchar idata buf[10];
uchar pf;
void init(void) /*串行口初始化 */
{ TMOD=0x20; /*设 T/C1为定时方式 2*/
TH1=0xe8; /*设定波特率 */
第 7章 单片机的 C语言应用程序设计
TL1=0xe8;
PCON=0x00;
TR1=1; /*启动 T/C1*/
SCON=0x50; /*串行口工作在方式 1*/
}
void send(uchar idata *d)
{ uchar i;
do {
SBUF=0xaa; /*发送联络信号 */
while(TI= =0); /*等待发送出去 */
TI=0;
第 7章 单片机的 C语言应用程序设计
while(RI= =0); /*等待 B机回答 */
RI=0;
}while((SBUF^0xbb)!=0); /*B机未准备好,继续联络 */
do {
pf=0; /*清校验和 */
for ( i=0; i<16; i++)
{ SBUF=d[i]; /*发送一个数据 */
pf+ =d[i]; /*求校验和 */
while(TI= =0); TI=0;
}
SBUF=pf; /*发送校验和 */
第 7章 单片机的 C语言应用程序设计
while(TI= =0); TI=0;
while(RI= =0); RI=0; /*等待 B机回答 */
}while(SBUF!=0); /*回答出错,则重发 */
}
void receive (uchar idata *d)
{ uchar i;
do {
while (RI= =0); RI=0;
}
while ((SBUF^0xaa)! =0); /*判 A机请求否 */
SBUF=0xbb; /*发应答信号 */
第 7章 单片机的 C语言应用程序设计
while (TI= =0); TI=0;
while (1)
{pf=0; /*清校验和 */
for ( i=0; i<16; i++)
{ while (RI= =0); RI=0;
d[ i ]=SBUF; /*接收一个数据 */
pf+ =d[i]; /*求校验和 */
}
while (RI= =0); RI=0; /*接收 A机校验和 */
if ((SBUF^ pf) = =0) /*比较校验和 */
{ SBUF=0x00; break; } /*校验和相同发 "00"*/
else
{SBUF=0xff; /*出错发 "FF",重新接收 */
第 7章 单片机的 C语言应用程序设计
while(TI= =0); TI=0;
}
}
}
void main (void)
{ init ( );
if(TR= =0)
{ send(buf);
}
else
{ receive(buf);
}
}
第 7章 单片机的 C语言应用程序设计
7.12.2 多机通信
1.通信接口图 7.19 总线式主从式多机系统第 7章 单片机的 C语言应用程序设计
2.通信协议根据 MCS-51串行口的多机通信能力,多机通信可以按照以下协议进行:
(1) 首先使所有从机的 SM2位置 1处于只接收地址帧的状态。
(2) 主机先发送一帧地址信息,其中 8位地址,第 9位为地址 /
数据信息的标志位,该位置 1表示该帧为地址信息。
(3) 从机接收到地址帧后,各自将接收的地址与本机的地址比较。对于地址相符的那个从机,使 SM2位清零,以接收主机随后发来的所有信息;对于地址不符的从机,仍保持 SM2=1,对主机随后发来的数据不予理睬,直至发送新的地址帧。
第 7章 单片机的 C语言应用程序设计
(4) 当从机发送数据结束后,发送一帧校验和,并置第 9位
(TB8)为 1,作为从机数据传送结束标志。
(5) 主机接收数据时先判断数据结束标志 (RB8),若 RB8=1,
表示数据传送结束,并比较此帧校验和,若正确,则会送正确信号 00H,此信号令该从机复位 (即重新等待地址帧 );若校验和出错,则发送 0FFH,令该从机重发数据。若接收帧的 RB8=0,
则原数据到缓冲区,并准备接收下帧信息。
(6) 若主机向从机发送数据,从机在第 (3)步中比较地址相符后,从机令 SM2=0,同时把本站地址发回主机。作为应答之后才能收到主机发送来的数据。其它从机 (SM2=1),无法收到数据。
第 7章 单片机的 C语言应用程序设计
(7) 主机收到从机的应答地址后,确认地址是否相符。如果地址不符,发复位信号 (数据帧中 TB8=1);如果地址相符,则清
TB8,开始发送数据。
(8) 从机接收到复位命令后回到监听地址状态 (SM2=1)。否则开始接收数据和命令。
第 7章 单片机的 C语言应用程序设计
3,通信程序设主机发送的地址联络信号 00H,01H,02H为从机设备地址,地址 FFH是命令各从机恢复 SM2为 1的状态,即复位。主机的命令编码为:
01H 请求从机接收主机的数据命令;
02H 请求从机向主机发送数据命令。
其它都按从机向主机发送数据命令 02H对待。
第 7章 单片机的 C语言应用程序设计从机的状态字节格式为:
ERR 0 0 0 0 0 TRDY RRDY
D7 D6 D5 D4 D3 D2 D1 D0
RRDY=1:从机准备好接收主机的数据。
TRDY=1:从机准备好向主机发送数据。
ERR=1:从机接收到的命令是非法的。
通常从机以中断方式控制和主机的通信。程序可分成主机程序和从机程序,约定一次传送的数据为 16个字节,以 02H地址的从机为例。
第 7章 单片机的 C语言应用程序设计1) 主机程序图
7.
20
多机通信主机程序流程图第 7章 单片机的 C语言应用程序设计主机程序如下:
#include <reg51.h>
#define uchar unsigned char
#define SLAVE 0x02 /*从机地址 */
#define BN 16
uchar idata rbuf [16];
uchar idata tbuf [16]={"master transmit"};
void err (void)
{
SBUF=0xff;
while(TI!=1); TI=0;
}
第 7章 单片机的 C语言应用程序设计
uchar master (char addr,uchar command)
{uchar aa,i,p;
while(1)
{SBUF=SLAVE; /* 发呼叫地址 */
while (TI!=1); TI=0;
while (RI!=1); RI=0; /* 等待从机回答 */
if(SBUF!=addr) err( ); /* 若地址错,发复位信号 */
else { /* 地址相符 */
TB8=0; /* 清地址标志 */
SBUF=command; /* 发命令 */
while (TI!=1); TI=0;
while (RI!=1); RI=0;
第 7章 单片机的 C语言应用程序设计
aa=SBUF; /* 接收状态 */
if((aa&0x08)= =0x08) {TB8=1; err( ); } /* 若命令未被接收,发复位信号 */
else {
if ( command= =0x01) /* 是发送命令 */
{if ((aa&0x01)= =0x01) /* 从机准备好接收 */
{do {
p=0; /* 清校验和 */
for(i=0; i<BN; i++)
{ SBUF=tbuf[i]; /* 发送一数据 */
p+=tbuf[i];
第 7章 单片机的 C语言应用程序设计
while(TI!=1); TI=0;
}
SBUF=p; /* 发送校验和 */
while (TI= =0); TI=0;
while (RI= =0); RI=0;
}while (SBUF! =0); /* 接收不正确,重新发送 */
TB8=1; /* 置地址标志 */
return(0)
}
}
else {
if((aa&0x02)= =0x02) /* 是接收命令,从机准备好发送 */
第 7章 单片机的 C语言应用程序设计
{while(1)
{ p=0; /* 清校验和 */
for(i=0; i<BN; i++)
{ while (RI! =1); RI=0;
rbuf[i]=SBUF; /* 接收一数据 */
P+=rubf[i];
}
while(RI= =0); RI=0;
if(SBUF= =p)
{ SBUF=0X00; /* 校验和相同发 "00" */
while(TI= =0); TI=0;
break;
}
第 7章 单片机的 C语言应用程序设计
else
{ SBUF=0xff; /* 校验和不同发 "0FF",重新接收 */
while(TI= =0); TI=0;
}
}
TB8=1; /* 置地址标志 */
Retuen(0);
}
}
}
}
}
}
第 7章 单片机的 C语言应用程序设计
void main (viod)
{
TMOD=0x20; /* T/C1定义为方式 2 */
TL1=0xfd; TH1=0xfd; /* 置初值 */
PCON=0x00;
TR1=1;
SCON=0xf0; /* 串行口为方式 3 */
master(SLAVE,0x01);
master( SLAVE,0x02 );
}
第 7章 单片机的 C语言应用程序设计2) 从机程序图
7.
21
多机通信的从机中断程序流第 7章 单片机的 C语言应用程序设计从机程序如下:
#include <reg51.h>
#define uchar unsigned char
#define SLAVE 0x02
#define BN 16
uchar idata trbuf[16];
uchar idata rebuf[16];
bit tready;
bit rready;
void main (void)
{
第 7章 单片机的 C语言应用程序设计
TMOD=0x20; /*T/C1定义为方式 2*/
TL1=0xfd; /*置初值 */
TH1=0xfd;
PCON=0x00;
TR1=1;
SCON=0xf0; /*串行口为方式 3*/
ES=1; EA=1; /*开串行口中断 */
while(1) {tready=1; rready=1; } /*假定准备好发送和接收 */
}
第 7章 单片机的 C语言应用程序设计
void ssio (void ) interrupt 4 using 1
{ void str(void);
void sre(void);
uchar a,i;
RI=0;
ES=0; /*关串行口中断 */
if(SBUF! =SLAVE) {ES=1; goto reti; } /*非本机地址,继续监听 */
SM2=0 ; /* 取消监听状态 */
SBUF=SLAVE ; /* 从本地址发回 */
第 7章 单片机的 C语言应用程序设计
while ( TI ! =1 ) ; TI =0 ;
while ( RI !=1 ) ; RI =0 ;
if ( RB8 == 1 ) { SM2=1 ; ES=1 ; goto reti ; } /* 是复位信号,恢复监听 */
a=SBUF ; /* 接收命令 */
if (a =0x01 ) /* 从主机接收的数据 */
{if ( rready = =1 ) SBUF =0x01 ; / * 接收准备好发状态 */
else SBUF=0x00 ;
while ( TI ! =1 ) ; TI=0 ;
while ( RI ! =1 ) ; RI =0 ;
if ( RB8= =1 ) { SM2 =1 ; ES =1 ; goto reti ; }
sre( ) ; /* 接收数据 */
}
第 7章 单片机的 C语言应用程序设计
else {
if( a= 0x02 ) /* 从机向主机发送数据 */
{if ( tready = =1 ) SBUF =0x02 ; /* 发送准备好发状态 */
else SBUF=0x00 ;
while ( TI ! = 1 ) ; TI =0 ;
while ( RI ! =1 ) ; RI =0 ;
if ( RB8 = =1 ) { SM2 =1 ; ES =1 ; goto reti ; }
str ( ) ; / * 发送数据 */
}
第 7章 单片机的 C语言应用程序设计
else
{SBUF = 0x80 ; /* 命令非法,发状态 */
while ( TI ! =1 ) ; TI =0 ;
SM2 =1 ; ES =1 ; /* 恢复监听 */
}
}
reti:;
}
void str ( void ) /* 发数据块 */
{ uchar p,i ;
tready =0 ;
do { p=0 ; /* 清校验和 */
第 7章 单片机的 C语言应用程序设计
for ( i= 0; i<BN ; i++ )
{ SBUF= trbuf[ i ] ; /* 发送一数据 */
p+=trbuf[i ] ;
while ( TI !=1 ) ;
TI =0 ;
}
SUBF= p ; /* 发送校验和 */
while ( TI = =0 ) ; TI =0;
while ( RI= =0 ); RI =0 ;
} while ( SBUF !=0 ) ; /* 主机接收不正确,重新发送 */
SM2=1 ;
ES = 1 ;
}
第 7章 单片机的 C语言应用程序设计
void sre ( void ) / * 接收数据块 */
{ uchar p,i ;
rready = 0 ;
while ( 1 )
{ p= 0 ; /* 清校验和 */
for ( i =0 ; i< BN ; i++)
{ while ( RI !=1 ); RI =0 ;
rebuf [ i ] =SBUF ; /* 接收数据 * /
p+=rebuf [ i ] ;
}
第 7章 单片机的 C语言应用程序设计
while ( RI !=1 ) ; RI =0 ;
if ( SBUF = = p ) { SBUF= 0x00 ; break ; } /* 校验和相同发 "00"*/
else {
SBUF=0xff ; /* 校验和不同发 "0FF",重新接收 */
while ( TI = = 0 ) ; TI=0;
}
}
SM2 = 1 ;
ES = 1 ;
}
第 7章 单片机的 C语言应用程序设计
7.13 键盘和数码显示人机交互的 C语言编程
7.13.1 行列式键盘与 8031的接口键盘输入信息的主要过程是:
(1) 单片机判断是否有键按下。
(2) 确定按下的是哪一个键。
(3) 把此步骤代表的信息翻译成计算机所能识别的代码,如
ASCII或其它特征码。
第 7章 单片机的 C语言应用程序设计图 7.22 8031与行列式键盘的接口第 7章 单片机的 C语言应用程序设计例 17 4× 4键盘的扫描程序。
扫描程序查询的内容为:
(1) 查询是否有键按下。首先单片机向行扫描 P1.0 ~ P1.3输出全为 "0"扫描码 F0H,然后从列检查口 P1.4~ P1.7输入列扫描信号,
只要有一列信号不为 "1",即 P1口不为 F0H,则表示有键按下。
接着要查出按下键所在的行、列位置。
第 7章 单片机的 C语言应用程序设计
(2) 查询按下键所在的行列位置。单片机将得到的信号取反,
P1.4~P1.7中的为 1的位便是键所在的列。接下来要确定键所在的行,需要进行逐行扫描。单片机首先使 P1.0为 "0",P1.1~P1.7
为 "1",即向 P1口发送扫描码 FEH,接着输入列检查信号,若全为 "1",表示不在第一行。接着使 P1.1接地,其余为 "1",再读入列信号 …… 这样逐行发 "0"扫描码,直到找到按下键所在的行,
将该行扫描码取反保留。当各行都扫描以后仍没有找到,则放弃扫描,认为是键的误动作。
第 7章 单片机的 C语言应用程序设计
(3) 对得到的行号和列号译码,得到键值。
(4) 键的抖动处理。当用手按下一个键时,往往会出现所按键在闭合位置和断开位置之间跳几下才稳定到闭合状态的情况。
在释放一个键时,也会出现类似的情况,这就是键抖动,抖动的持续时间不一,通常不会大于 10 ms,若抖动问题不解决,就会引起对闭合键的多次读入,对于键抖动最方便的解决方法就是当发现有键按下后,不是立即进行逐行扫描,而是延时 10 ms
后再进行。由于键按下的时间持续上百毫秒,延时后再也不迟。
第 7章 单片机的 C语言应用程序设计扫描函数的返回值为键特征码,若无键按下,返回值为 0。程序如下:
# include <reg51.h>
# define uchar unsigned char
# define uint unsigned int
void dlms( void )
void kbscan( void ) ;
void main ( void )
{
uchar key ;
while( 1 )
{ key =kbscan ( ) ;
dlms( ) ;
}
}
第 7章 单片机的 C语言应用程序设计
void dlms( void )
{ uchar i ;
for ( i=200 ; i>0 ; i- -) { }
}
uchar kbscan ( void ) /* 键扫描函数 */
{ uchar scode,recode ;
P1=oxf0 ;
if ( (P1 & 0xf0 ) ! =0xf0 ) /* 若有键按下 */
{ dlms ( ) ; /* 延时去抖动 */
if (( P1 & 0xf0 )! = 0xf0 )
{ scode =0xfe ; /* 逐行扫描初值 */
while (( scode & 0x10 ) !=0 )
{ P1=scode ; /* 输出扫描码 */
第 7章 单片机的 C语言应用程序设计
if (( P1 & 0xf0 )! =0xf0 ) /* 本行有键按下 */
{ recode= ( P1 & 0xf0 ) | 0x0f ;
return (( ~scode ) + (~ recode ) ) ; /* 返回特征字节码 */
}
else
scode = ( scode < < 1) | 0x01 ; /* 行扫描左移一位 */
}
}
}
return ( 0 ) ;
}
第 7章 单片机的 C语言应用程序设计
7.13.2 七段数码显示与 8031的接口数码显示器有静态显示和动态显示两种显示方式。
数码显示器有发光管的 LED和液晶的 LCD两种。
LED显示器工作在静态方式时,其阴极 (或其阳极 )点连接在一起接地 (或 +5 V),每一个的端选线 (a,b,c,d,e,f,g,dp)分别与一个 8位口相连。 LCD数码显示只能工作在静态显示,并要求加上专门的驱动芯片 4056。
LED显示器工作在动态显示方式时,段选码端口 I/O1用来输出显示字符的段选码,I/O2输出位选码。 I/O1不断送待显示字符的段选码,I/O2不断送出不同的位扫描码,并使每位显示字符停留显示一段时间,一般为 1~5 ms,利用眼睛的视觉惯性,从显示器上便可以见到相当稳定的数字显示。
第 7章 单片机的 C语言应用程序设计例 18 8155控制的动态 LED显示。
图 7.23 经 8155扩展端口的 6位 LED动态显示第 7章 单片机的 C语言应用程序设计确定的 8155片内 4个端口地址如下:
命令 /状态口,FFF0H
口 A,FFF1H
口 B,FFF2H
口 C,FFF3H
第 7章 单片机的 C语言应用程序设计
6位待显示字符从左到右依次放在 dis_buf数组中,显示次序从右向左顺序进行。程序中的 table 为段选码表,表中段选码表存放的次序为 0~F等。以下为循环动态显示 6位字符的程序,8155命令字为 07H。
# include < absacc.h >
# include < reg51.h >
#define uchar unsigned char
# define COM8155 XBYTE[ 0xfff0 ]
# define PA8155 XBYTE[ 0xfff1 ]
# define PB8155 XBYTE[ 0xfff2 ]
# define PC8155 XBYTE[ 0xfff3 ]
第 7章 单片机的 C语言应用程序设计
uchar idata dis_buf[6] = { 2,4,6,8,10,12 } ;
uchar code table[18 ]= { 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,
0x77,0x7c,0x39,0x5e,0x79,0x71,0x40,0x00 } ;
void dl_ms ( uchar d ) ;
void display ( uchar idata * p )
{ uchar sel,i ;
COM8155 = 0x07 ; /* 送命令字 */
sel = 0x01 ; /* 选出右边的 LED */
for ( i= 0 ; i<6 ; i++ )
{PB8155=table [ * p] ; /* 送段码 */
第 7章 单片机的 C语言应用程序设计
PA8155=sel ; /* 送位选码 */
dl_ms ( 1 ) ;
p - - ; /* 缓冲区下移 1位 */
sel =sel << 1 /* 左移 1 位 */
}
}
void main ( void )
{
display ( dis_buf +5 ) ;
}
第 7章 单片机的 C语言应用程序设计例 19 串行口控制的静态 LCD显示。
图 7.24 串行口连接的静态 LCD显示电路第 7章 单片机的 C语言应用程序设计输出两位显示,即一字节的程序如下:
# include < reg51.h >
# define uchar unsigned char
uchar byte=0x59 ;
void display ( uchar x )
{
SBUF=x ; /* 由串口输出 */
while ( TI = =0 ); /* 等待 8位发送结束 * /
TI = 0 ;
}
void main ( void )
{
display( byte )
}