C51教程_图文


第7章 单片机的C语言应用程序设计 7.1 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 7.10 7.11 7.12 7.13

第7章 单片机的C语言应用程序设计
C语言与MCS-51 C51数据类型及在MCS-51中的存储方式 C51数据的存储类型与MCS-51存储结构 MCS-51特殊功能寄存器(SFR)的C51定义 MCS-51并行接口的C51定义 位变量的C51定义 C51构造数据类型 模块化程序开发过程 MCS-51内部资源使用的C语言编程 MCS-51片外扩展的C语言编程 频率量测量的C语言编程 MCS-51机间通信的C语言编程 键盘和数码显示人机交互的C语言编程

第7章 单片机的C语言应用程序设计

7.1 C语言与MCS–51
用汇编程序设计MCS–51系列单片机应用程序时,必须要考虑 其存储器结构,尤其必须考虑其片内数据存储器与特殊功能寄存 器正确、合理的使用以及按实际地址处理端口数据。用C语言编写 分配存储器资源和处理端口数据,但在C语言编程中,对数据类型 与变量的定义,必须要与单片机的存储结构相关联,否则编译器

MCS–51单片机的应用程序,虽然不像用汇编语言那样具体地组织、

不能正确地映射定位。用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 unsigned char signed char unsigned int signed int unsigned long signed long float double 长度(bit) 1 8 8 16 16 32 32 32 64 长度(byte) 值域 0,1 0~255

1
1 1 2 2 4 4 4 8

–128~127
0~65535

–32768~32767
0~4294967295

–2147483648~2147483647
±1.176E–38~±3.40E+38(6位数字) ±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。对于有符号字符变量
此位为1代表"负",为0代表"正"。有符号字符变量和无符号字符变

(signed char),最具有重要意义的位是最高位上的符号标志位(msb)。

量在表示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语言应用程序设计

地址 … +0 +1 0x12

地址 +0 0x12 0x34

+1
+2 +3

0x56
0x78 ….

0x34


图7.1 整型数的存储结构

图7.2 长整型变量的存储结构

第7章 单片机的C语言应用程序设计 浮点型变量(float): 浮点型变量为32位,占4个字节,许多复 杂的数学表达式都采用浮点变量数据类型。应用符号位表示数

的符号,用阶码和尾数表示数的大小。
用它们进行任何数学运算都需要使用由编译器决定的各种 不同效率等级的库函数。Franklin C51的浮点变量数据类型的使 用格式与IEEE-754标准有关,具有24位精度,尾数的高位始终 为"1",因而不保存,位的分布如下:

● 1位符号位。
● 8位指数位。 ● 23位尾数。

第7章 单片机的C语言应用程序设计 符号位是最高位,尾数为低23位,内存中按字节存储顺序如下:
地址 内容 +0 MMMMMMMM +1 MMMMMMMM +2 EMMMMMMM +3 SEEEEEEE

其中,S为符号位,1表示负,0表示正;E为阶码;M为23 位尾数,最高位为"1"。 浮点变量值 -12.5的十进制为:0xC1480000,它按图7.3所 示方式存于内存中。

第7章 单片机的C语言应用程序设计

地址 +0 +1 +2

0x00 0x00 0x48 0xC1 ….

+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
idata pdata

可位寻址片内数据存储区,允许位与字节混合访问(16字节)
间接寻址片内数据存储区,可访问片内全部RAM地址空间(256字节) 分页寻址片外数据存储区(256字节)由MOV @Ri访问(i=0,1)

xdata
code

片外数据存储区(64 KB)由MOVX @DPTR访问
程序存储器64 KB空间,由MOVC @DPTR访问

第7章 单片机的C语言应用程序设计 表7.3 C51存储类型及其数据长度和值域

存储类型 data

长度(bit) 8

长度(byte) 1

值域范围 0~255

idata
pdata xdata

8
8 16

1
1 2

0~255
0~255 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。栈长很关键,因为实际栈长依赖于函 数嵌套调用层数 默认的存储类型是pdata,参数及局部变量放入分页的外部数 据存储区,通过@R0或@R1间接访问,栈空间位于片内数据 存储区中 默认的存储类型是xdata,参数及局部变量直接放入片外数据 存储区,使用数据指针DPTR来进行寻址。用此数据指针进行 访问效率较低,尤其对两个或多个字节的变量,这种数据类 型的访问机制直接影响代码的长度

COMPA CT

LARGE

第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 ; sbit OV=PSW^2 ; sbit CY=PSW^7 ; /* 定义PSW寄存器地址为D0H */ /* 定义OV位为PSW.2,地址为D2H */ /* 定义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 ; sbit CY=0XD7 ; /* 定义OV位地址为D2H */ /* 定义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语言应用程序设计

7.6 位变量的C51定义
(1) 位变量C51定义。使用C51编程时,定义了位变量后,就可 以用定义了的变量来表示MCS-51的位寻址单元。 位变量的C51定义的一般语法格式如下: 位类型标识符(bit) 位变量名; 例如: bit direction_bit ; bit look_pointer ; /* 把direction_bit定义为位变量 */ /* 把look_pointer定义为位变量 */

第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; bdata char bary[4]; /* 定义ibase为bdata整型变量 */ /* bary[4]定义为bdata字符型数组 */

然后可使用"sbit"定义可独立寻址访问的对象位: sbit mybit0 = ibase^0 ; sbit mybit15 = ibase^15; sbit Ary07 = bary[0]^7 ; sbit Ary37 = bary[3]^7 ; /* mybit0定义为ibase的第0位 */ /* mybit0定义为ibase的第15位 */ /* Ary07定义为abry[0]的第7位 */ /* Ary37定义为abry[3]的第7位 */

第7章 单片机的C语言应用程序设计 对象ibase和bary也可以字节寻址: ary37=0; bary[3]='a'; /* bary[3]的第7位赋值为0 */ /* 字节寻址,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
1

xdata
2

pdata
3

data
4

code
5

第7章 单片机的C语言应用程序设计 例如,以xdata类型的0x1234地址为指针可以表示如下:
地址 内容 +0 0x02 +1 0x12 +2 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 PUBLIC PSEG SEGMENT CODE _TOUPPER UPPER ;程序段 ;入口地址 ; 程序段 ;从R7中取参数

_TOUPPER: MOV A,R7

CJNE A,# 'a',$+3
JC UPPERET

CJNE A, # 'z'+1 ,$+3

JNC UPPERET
CLR ACC ,5 ;返回值放在R7中

UPPERET: MOV R7 ,A

RET

;返回到C

第7章 单片机的C语言应用程序设计 2.参数传递规则 表7.6 参数传递的寄存器选择
参数类型 第1个参数 第2个参数 第3个参数 一般指针 R1,R2,R3 R1,R2,R3 R1,R2,R3

char R7 R5 R3

int R6, R7 R4, R5 R2, R3

long ,float R4~R7 R4~R7 无

第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
(unsigned) char (unsigned) int (unsigned) long float 指针

C
R7 R6,R7 R4~R7 R4~R7 R1,R2,R3

进位标位

高位字节在R6,低位字节在R7 高位字节在R4,低位字节在R7 32位IEEE格式,指数和符号位在R7 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) 共享变量
类 型 动态变量 静态变量 公用变量 外部变量 静态子程序/函数 公共子程序/函数 外部子程序/函数 汇编语言 y( ){ int x;} static int x; C 语 言

PUBLIC X X:ds 2
EXTERN DATA(X) MOV DPTR,# X Y:… PUBLIC Y Y: EXTERN CODE(Y) LCALL Y

Int x; extern int static y( ){ … }; y( ) { … }; y( ) x;

第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
C51C.LIB C51FPC.LIB

浮点数学运算库(SMALL模式)
COMPACT模式,无浮点运算 浮点运算库(COMPACT模式)

C51L.LIB
C51FPL.LIB

LARGE模式,无浮点运算
浮点运算库(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) 存储器分配 表7.10 MCS-51系列的物理存储区
物理存储区 程序 外部数据 直接寻址片内数据 间接寻址片内数据 片内数据的位空间 最大长度 64 KB 64 KB 128字节 256字节 128位 地址区 0~0FFFFH 0~0FFFFH 0~7FH 0~0FFH 0~7FH 段类型 CODE XDATA DATA IDATA BIT

第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 1 2 3 4

中断源
外部中断0 定时器/计数器0 外部中断1 定时器/计数器1

入口地址
0003H 000BH 0013H 001BH 0023H

串行口中断

第7章 单片机的C语言应用程序设计 using n 选项用于实现工作寄存器组的切换,n是中断服务子 程序中选用的工作寄存器组号(0 ~ 3)。在许多情况下,响应中断

时需保护有关现场信息,以便中断返回后,能使中断前的源程序
从断点处继续正确地执行下去。这在MCS-51单片机中,能很方便 地利用工作寄存器组的切换来实现。即在进入中断服务程序前的 程序中使用一组工作寄存器,进入中断服务程序后,由"using n" 切换到另一组寄存器,中断返回后又恢复到原寄存器组。这样互 相切换的两组寄存器中的内容彼此都没有被破坏。

第7章 单片机的C语言应用程序设计 例3 图7.5所示是利用优先权解码芯片,在单片机8031的一 个外部中断INT1上扩展多个中断源的原理电路图。图中是以开 关闭合来模拟中断请求信号。当有任一中断源产生中断请求,能

给8031的INT1引脚送一个有效中断信号,由P1的低3位可得对应
中断源的中断号。

图 7.5 扩展多个中断源

第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
{ flag=1;

/* INT1中断服务程序,使用第2组工
作寄存器 */ /* 设置标志 */

status=p1;
} void main(void) { IP=0x04 ; IE=0x84 ;

/* 存输入口状态 */

/* 置INT1为高优先级中断 */ /* INT1开中断,CPU开中断 */

第7章 单片机的C语言应用程序设计
for(; ;)
{ if(flag) { switch(status) /* 有中断 */ /* 根据中断源分支 */

{ case 0 : break ;
case 1 : break ; case 2 : break;

/* 处理IN0 */
/* 处理IN1 */ /* 处理IN2 */

case 3 : break;
default : ; }

/* 处理IN3 */

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 ; TR0=1 ; /* 设置定时器1为非门控制方式1*/ /* 启动 T/C0 */ /* 装载计数器初值 */ /* 查询等待TF0置位 */ /* 定时时间到P1.0反相 */ /* 软件清 TF0 */

for( ; ;)
{ TH0= -(1000/256) ; TL0= -(1000%256) ; do { } while (!TF0) ; P1_0=!P1_0; TF0=0; } }

第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 {TH0= - 8333/256 ; TL0= - 8333%256 ; if(++time==high)P1=0; /* 高电平时间到变低 */ /* T/C0中断服务程序 */ /* 重置计数值 */

else if (time==period)
{time=0 ; P1=1 ; } }

/* 周期时间到变高 */

第7章 单片机的C语言应用程序设计
main( ) { TMOD=0x01 ; /* 定时器0方式1 */

TH0= - 8333/256 ;
TL0= - 8333%256 ; EA=1; ET0=1 ; TR0=1 ;

/* 预置计数初值 */

/* 开CPU中断 */ /* 开T/C0中断 */ /* 启动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 { TH0= - 833/256 ;

/* T/C0中断服务程序 */ /* 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
{ union { unit word ;

/* 外中断1服务程序 */

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 ; TMOD= 0x11 ; TCON=0x54 ; /* 置INT1为高位优先级 */ /* T0,T1 16位方式 */ /* T0,T1运行,IT1边沿触发 */ /* 每转一圈,引起中断,设置标志 */

第7章 单片机的C语言应用程序设计
TH1=0;TL1=0; IE=0x86 ; for (; ;) { if (one_round ) { if ( period <target ) {if (percent <100 ) ++percent ; } /* 占空比增 */ /* 每转一圈,调整 */ /* 设置初始计数值 */ /* 允许中断EX1,ET0 */

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] ; uchar xdata t_buf[32] ; /* item1 */

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 {if( RI && ~ r_full ) /* item3 */

{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 ; } /* 用户定义 */ void processmsg ( void ) {while ((( r_out+1 ) ^ r_in) !=0 ) /* item6 */ / * item5 */

/* 接收非缓冲区 */
{process ( r_buf [r_out ] ) ; r_out= ++r_out & 0x1f ; } }

第7章 单片机的C语言应用程序设计
main ( )
{TMOD=0x20 ; TH1=0xfd ;

/* item7 */
/* 定时器1方式2 */ /* 9600波特11.0592 MHz */

TCON=0x40 ;
SCON=0x50 ; IE=0x90 ; t_empty=t_done=1 ; r_full =0 ; r_out=t_in =0 ; r_ in=1 ;

/* 启动定时器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地址 */
/* 口C 地址 */ / * 打印字符串函数 */

# define PC8255 XBYTE[ 0x007e]

void toprn ( uchar *p ) { while ( * p!= '\0') {while (( 0x80 & PC8255 )! =0 ) ;

/* 查询等待打印机的BUSY状态*/

第7章 单片机的C语言应用程序设计
PA8255 = * p ; COM8255 = 0x00 ; COM8255=0x01 ; p++ ; /* 输出字符 */ /* 模拟STB脉冲 */

}
} 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 ; uint eprom , con ; /* RAM起始地址 */ /* EPROM起始固化地址,固化长度 */

{ int i ;
COM8255=0x80 ; COM8255=0x0f ; for (i=0 ; i<con ;i++ ) { PA8255= * ram ; /* 固化内容口A锁存 */ /* 送方式选择命令字 */ /* PC7=1 */

PB8255=eprom % 256 ;
PC8255=eprom /256 ;

/* 2732地址低8位 */
/* 2732地址高4位 */

第7章 单片机的C语言应用程序设计
eprom ++ ; ram ++ ;

COM8255=0x0e ;
d1_ms (50 ) ; COM8255=0x0f ; } }

/* PC7=0 */

/* PC7=1 */

main ( )
{ program ( 0x1000,0x0000,0x0100 ) ;

}

7.10.2 MCS-51数据采集的C语言编程 第7章 单片机的C语言应用程序设计

例9 ADC0809与8031接口的数据采集程序举例。
图 7 8

. ADC

08 09 与 80 31 的 接 口 电 路

第7章 单片机的C语言应用程序设计 程序如下:
# include <absacc.h > # include <reg51.h > # define uchar unsigned char # define IN0 XBYTE [ 0x7ff8 ] sbit ad_busy =P3^3 ; void ad0809 ( uchar idata *x ) { uchar i ; uchar xdata *ad_adr ; ad_adr= & IN0 ; for ( i=0 ; i<8 ;i++ ) /* 处理8通道 */ /* 设置AD0809的通道0地址 */ /* 即EOC状态 */ /* 采样结果放指针中的A/D采集函数 */

{ *ad_adr=0 ;

/* 启动转换 */

第7章 单片机的C语言应用程序设计
i=i ; i=i ; while (ad_busy = =0 ) ; /* 查询等待转换结束 */ /* 延时等待EOC变低 */

x[i ]= * ad_adr ;
ad_adr ++ ; } } void main ( void )

/* 存转换结果 */
/* 下一通道 */

{ static uchar idata ad [ 10 ] ;
ad0809 ( ad ) ; } /* 采样AD0809通道的值 */

第7章 单片机的C语言应用程序设计 例10 AD574与8031接口的数据采集程序举例。

图 7 9

. AD

57 4 与 80 31 的 接 口 电 路

第7章 单片机的C语言应用程序设计 源程序如下:
# include < absacc.h >

# inlucde < reg51.h >
# define uint unsigned int # define ADCOM XBYTE[ 0xff7c ] # define ADLO XBYTE [ 0xff7f ] # define ADHI XBYTE [ 0xff7d ] sbit r = P3 ^ 7 ; sbit w = P3 ^ 6 ; /* 使A0=0 ,R/C=0, CS=0 */ /* 使 R/C =1,A0=1, CS=0 */ /* 使R/C=1,A0=0,CS =0 */

sbit adbusy = P1 ^ 0 ;

第7章 单片机的C语言应用程序设计
uint ad574 ( void ) { r=0; /* AD574转换器 */ /* 产生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 10

. DAC

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 ;
DACR= 0 ; }

/* 送数据到另一片0832 */
/* 启动两路D/A 同时转换 * /

例12 8031与DAC0832单缓冲区接口的数据转换举例。 第7章 单片机的C语言应用程序设计

图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++ )
{ DA0832 = i ; }

/* 形成锯齿波输出值,最大值为255 */
/* 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 ; DA7521H= i /256 ; /* 送低8位数据到第一级缓冲器 */ /* 送高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; TH0=0x3c ;TL0=0xb0; P1_1=0 ; P1_1=1 ; while (tf!=1 ) ; /* T/C1计数器 */ /* T/C0定时器100 ms */ /* 产生启动正脉冲 */ /* 等待500 ms定时到 */ /*产生清零用负脉冲*/

TR0=1;TR1=1;PT0=1;ET0=1;ET1=1;EA=1; /*启动T/C,开中断*/

P1_0=0 ; P1_0=1 ;
TR0=0 ; TR1=0 ; return (TH1*256+TL1) ;

/* 产生负脉冲,封锁G2 */
/* 关T/C */ / 返回计数值*/

}

第7章 单片机的C语言应用程序设计
void timer0( void ) interrupt 1 using 1 { TH0=0x3c ; TL0=-0xb0 ; msn - - ; if ( msn = = 0) { msn=A ; tf=1 ; } } /* 500 ms定时时间到设标志 */ /* 100 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;
IT0=1;TR0=1; TH0=0;TL0=0;

/*定时器/计数器0为方式1*/

P1_0=0;P1_0=1;
TR0=1;ET0=1;EA=1; } void int_0(void)interrupt 0 using 1 { EA=0;TR0=0; count=TL0+TH0*256; rflag=1; EA=1; }

/*触发器清零*/
/*启动T/C0开中断*/

/* INT0 中断服务*/

/* 取计数值 */ /* 设标志 */

第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电平信号的传

2.通信双方的约定 第7章 单片机的C语言应用程序设计 图 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; TH1=0xe8;

/*串行口初始化*/ /*设T/C1为定时方式2*/ /*设定波特率*/

第7章 单片机的C语言应用程序设计
TL1=0xe8; PCON=0x00; TR1=1; SCON=0x50; } /*启动T/C1*/ /*串行口工作在方式1*/

void send(uchar idata *d) { uchar i; do { SBUF=0xaa; while(TI= =0); TI=0; /*发送联络信号*/ /*等待发送出去*/

第7章 单片机的C语言应用程序设计
while(RI= =0); RI=0; /*等待B机回答*/

}while((SBUF^0xbb)!=0);
do { pf=0;

/*B机未准备好,继续联络*/

/*清校验和*/

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; }while(SBUF!=0); } /*等待B机回答*/ /*回答出错,则重发*/

void receive (uchar idata *d) { uchar i;

do {
while (RI= =0); RI=0; }

while ((SBUF^0xaa)! =0);
SBUF=0xbb;

/*判A机请求否*/
/*发应答信号*/

第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;} else

/*比较校验和*/
/*校验和相同发"00"*/

{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语言应用程序设计 从机的状态字节格式为: D7
ERR

D6
0

D5
0

D4
0

D3
0

D2
0

D1
TRDY

D0
RRDY

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; while(TI= =0);TI=0; break; /* 校验和相同发"00" */ /* 接收一数据 */

}

第7章 单片机的C语言应用程序设计
else
{ SBUF=0xff; while(TI= =0);TI=0; /* 校验和不同发"0FF",重新接收 */

}
} TB8=1; /* 置地址标志 */

Retuen(0);
} }

}
} } }

第7章 单片机的C语言应用程序设计
void main (viod)

{
TMOD=0x20; TL1=0xfd;TH1=0xfd; PCON=0x00; TR1=1; /* T/C1定义为方式2 */ /* 置初值 */

SCON=0xf0;
master(SLAVE,0x01); master( SLAVE,0x02 ); }

/* 串行口为方式3 */

2)第7章 单片机的C语言应用程序设计 从机程序 图 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; TL1=0xfd; TH1=0xfd; PCON=0x00;

/*T/C1定义为方式2*/ /*置初值*/

TR1=1;
SCON=0xf0; ES=1;EA=1; while(1) {tready=1; rready=1;} } /*串行口为方式3*/ /*开串行口中断*/ /*假定准备好发送和接收*/

第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 ; while ( TI = = 0 ) ;TI=0; } } SM2 = 1 ; ES = 1 ; } /* 校验和不同发"0FF",重新接收 */

第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,利用眼睛的视觉惯性,从显示器上便 可以见到相当稳定的数字显示。

例18 8155控制的动态LED显示。 第7章 单片机的C语言应用程序设计

图 7.23 经8155扩展端口的6位LED动态显示

第7章 单片机的C语言应用程序设计 确定的8155片内4个端口地址如下: 命令/状态口 : FFF0H 口A: 口B: 口C: FFF1H FFF2H 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 ; for ( i= 0 ; i<6 ; i++ ) {PB8155=table [ * p] ; /* 送段码 */ /* 送命令字 */ /* 选出右边的LED */

第7章 单片机的C语言应用程序设计
PA8155=sel ;
dl_ms ( 1 ) ; p--; /* 缓冲区下移1位 */

/* 送位选码 */

sel =sel << 1
} } void main ( void ) { display ( dis_buf +5 ) ; }

/* 左移1 位 */

第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 ); TI = 0 ; } void main ( void ) { display( byte ) /* 由串口输出 */ /* 等待8位发送结束 * /

}


相关文档

Keil C51单片机教程
C51快速入门教程
C51单片机教程第9章
C51程序设计__课程设计报告
C51单片机教程-精品文档
C51单片机教程---输入口的应用
C51单片机教程第9章-文档资料
单片机应用技术汇编 C51项目教程
C51课程设计
电脑版