51单片机基础教程

liqinwyyx

贡献于2014-01-19

字数:0 关键词: 嵌入式开发

第一讲 单片机基础知识 引言 商用 微机 工控 计算机 智能仪器仪表 单片机 集散控制 家用电器 C 语言 高级语言 PASCAL FORTRAN 计算机语言 汇编语言 (不同的 CPU,汇编语言不同) 1 位(几乎没有具体使用) 4 位(早期的产品) 单片机 8 位(当前应用最多,51 系列) 16 位(部分使用,与 8 位相比较少,80196) 32 位(未来趋势) 所有计算机的三总线结构相同; 程序流程图相同。 学习计算机的基础知识是数字电子技术:触发器、计数器、移位寄存器、 译码器、编码器 1.1 MCS-51 单片机的特点 单片机(MICROCONTROLLER,又称微控制器)是在一块硅片上集成了各种 部件的微型机算计,这些部件包括中央处理器 CPU、数据存贮器 RAM、程序存贮 器 ROM、定时器/计数器和多种 I/O 接口电路。 MCS-51单片机的基本结构如图 1-1 所示。 1 51 系列单片机结构特点: 8 位 CPU; 片内振荡器及时钟电路; 32 根 I/O 线; 外部存贮器寻址范围 ROM、RAM 各 64K; 3 个 l6 位的定时器/计数器; 5 个中断源,2 个中断优先级; 全双工串行口; 布尔处理器。 1.2 MCS-51 单片机的内部结构 图 1-2 是 MCS-5l 单片机片内部结构的总框图,它可以划分为 CPU、存贮器、 并行口、串行口、定时器/计数器、中断逻辑几部分。 2 图 1-2 MCS-51 的内部结构框图 1.2.1 中央处理器 MCS-51 的中央处理器 CPU 由运算器和控制逻辑构成,其中包括若干特殊功能 寄存器(SFR)。 ① CPU:8 位;ALU:算术、逻辑运算单元 中处理器 ② 程序状态字 PSW:8 位宽度、F0、RS1 和 RS0 ③ 振荡周期、机器周期 一、以 ALU 为中心的运算器 算术逻辑单元 ALU 能对数据进行加、减、乘、除等算术运算;“与”、“或”、 “异或”等逻辑运算以及位操作运算。 PSW 的格式如图 1-3 所示,其各位的含义是: D7 D6 D5 D4 D3 D2 D1 D0 CY AC F0 RS1 RS0 OV P 图 1-3 PSW 的格式 3 CY:进位标志。有进位/借位时 CY=1,否则 CY=0; AC:半进位标志。当 D3 位向 D4 位产生进位/借位时 AC=1,常用于十进制 调整运算中; F0:用户可设定的标志位,可置位/复位,也可供测试。 RS1、RS0:四个通用寄存器组的选择位,该两位的四种组合状态用来选择 0~3 寄存器组。见表 1-2。 表 l-2 RS1、RS0 与工作寄存器组的关系 RS1 RS0 工作寄存器组 0 0 0 组(00-07) 0 1 1 组(08-0F) 1 0 2 组(10-17) 1 1 3 组(18-1F) OV:溢出标志。当带符号数运算结果超出-128~+127 范围时 OV=1,否则 OV=0。 当无符号数乘法结果超过 255 时,或当无符号数除法的除数为 0 时,OV=1,否则 OV=0。 P:奇偶校验标志。每条指令执行完,若 A 中 l 的个数为奇数时 P=1,否则 P=0, 即奇偶校验方式。 二、控制器、时钟电路和基本时序周期 控制逻辑主要包括定时和控制逻辑、指令寄存器、译码器以及地址指针 DPTR 和程序计数器 PC 等。 1.MCS-51 的时钟 时钟是时序的基础,MCS-51 片内由一个反相放大器构成振荡器,可以由它产 生时钟。 XTAL1 XTAL2 外部时钟 XTAL2 XTAL1 Vss (a) (b) 图 l-4 时钟产生电路 (1) 内部方式:图 1-4(a) (2) 外部方式:图 1-4(b) 2.MCS-51 的基本时序周期 一条指令译码产生的一系列微操作信号在时间上有严格的先后次序,这种次序 就是计算机的时序。MCS-51 的主要时序将在存贮器扩展时讨论,这里先介绍它的 4 基本时序周期。 振荡周期:指振荡源的周 期,若为内部产生方式时, 为石英晶体的振荡周期。 机器周期:一个机器周期 含 6个时钟周期(S 周期), 图 1-5 基本时序周期 12 个震荡周期。 指令周期:完成一条指令占用的全部时间。MCS-51 的指令周期含 l-4 个 机器周期,其中多数为单周期指令,还有 2 周期和 4 周期指令。 1.2.2 存贮器结构 计算机的存贮器的管理模式,大致可分为两类。第一类是将程序存贮器和数据 存贮器分开,并有各自的寻址机构和寻址方式,这种结构形式称为哈佛型结构。另 一类是存贮器逻辑空间统一管理,可随意安排 ROM 或 RAM,访问时用同一种指 令,这种结构形式称为普林斯顿型。MCS-51 单片机的存贮器结构属于前者,一般 微机属于后者。 程序:0000-0FFFFH 内部 00-7F:工作寄存区、通用数据区 数据: 存贮器结构 80-FF:特殊功能寄存器、通用数据区 程序:1000-FFFFH(或 0000-FFFF) 外部 数据:0000-FFFFH 存贮器组织结构: 图 1-5 MCS-51 存贮器组织结构 数据存贮器 RAM 也有 64KB 寻址区,在地址上是和 ROM 重叠的。MCS-51 通过不同的信号来选通 ROM 或 RAM:当从外部 ROM 取指令时用选通信号 PSEN , 而从外部 RAM 读写数据时采用读写信号 RD 或 WR 来选通。因此不会因地址重叠而 出现混乱。 5 第二讲 MCS-51 单片机的内部结构 1.2.3 片内并行接口 P0:常用功能(数据/低 8 位地址) 单片机 P1:常用并行端口 (8051) P2:常用于地址高 8 位(A8-A15) P3:常用第二功能(RXD、TXD、INT0、INT1、T0、T1、WR、RD) 1.2.4 MCS-51 的内部资源 串行口 内部资源 定时器/计数器 中断系统:5 个中断源(INT0、T0、INT1、T1 和串口) 1.2.5 MCS-51 的芯片引脚 ① XTAL1、XTAL1:晶体、电容; ② ALE(地锁存信号):锁存P0 口的地址低 8 位,频率=fSOC/6; ③ PSEN(读指令信号):接程序存贮器的允许输端子; ④ WR、RD:分别与外部数据存贮器的读、端子相连接 ⑤ EA:接高电平(或接低电平)。 图 1-8 MCS-51 引脚图 6 1.2.6 单片机的工作方式 :复位方式、程序执行方式、单步执行方式、低功耗操 作方 ;(2)执行外部程序 第三讲 定时器/计数器 MCS-51 子系列单片机 /计数器 0 和 1,52 子 系列 式)中,有一个控制位(C/T),分别用于控制定时 器/计 器周期; 。 列兼容; 作某件事; 。 3.2 定时器/计数器 2(自己阅读) 动重装载或捕获能力的定时器/计数器。 专用 1.3.3 定时器/计数器的控制和状态寄存器 确定各定时器/计数器的功能和操 作模式。这些寄存器的内容靠软件设置。系统复位时,寄存器的所有位都被清零。 位分别控制定时器/计数器 1 和 0,参阅图 1-11、 单片机的工作方式包括 式以及 EPROM 编程和校验方式。 1. 复位方式:经典的上电复位电路 2. 程序执行方式:(1)执行内部程序 3. 单步执行方式:用于调试程序和系统 4. 低功耗操作方式 5. 编程和校验 有 2 个定时器/计数器,即定时器 单片机(8032/8052)除了有上述 2 个定时器/计数器外,还有一个定时器/计数 器 2,后者的功能比前两者强。 1.3.1 定时器/计数器 0 和 1 在专用寄存器 TMOD(定时器方 数器 0 和 1 是工作在定时器方式还是计数器方式。 1. 输入信号基本要求:24 个振荡周期,即两个机 2. 作为定时器时,计数速率是 振荡频率/12; 3. 由定时器/计数器模式控制寄存器设置工作方式 模式 0:13 位宽度,主要保持与 48 系 工作 模式 1:16 位宽度,最大计数 65535; 模式 模式 2: 8 位自动重装载,用于周期性的 模式 3:定时器/计数器 0 和 1 不同,适合于额外定时器 1. 定时器/计数器 2 是一个具有 16 位自 寄存器 T2CON 是它的控制寄存器。 可用作波特率发生器 定时/计数方式 专用寄存器 TMOD、TCON 和 T2CON 用于控制和 1. 模式控制寄存器 TMOD 8 位宽度,高四位和低四 图 1-12、图 1-13。 7 定时器 1 定时器 0 (MSB) (LSB) D7 D6 D5 D4 D3 D2 D1 D0 GATE C/ T M1 M0 GATE C/ T M1 M0 图 1-16 定时器/计数器控制寄存器 TMOD 例 MOV TOMD, 重装载 开始工作 表 1-5 操作模式控制位 控制寄存器 TCON (LSB) #0010 0001B ;定时器 1,8 位自动 MOV TH1,#56 ;时间常数 MOV TL1,#56 ;时间常数 SETB TR1 ;启动定时器 2. (MSB) D7 D6 D5 D4 D3 D2 D1 D0 TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0 图 1-17 定时器/计数器控制寄存器 TC TF0、TR0: //外部中断 0 下降沿触发 M1 MO 操 作 模 式 ON 0 0 模式 0。 TLx 中低 5 位与 THx 中 8 位构成 13 位计数器, TLx 相当一 个 5 位定标器(见图 1-11)。 0 1 模式 10 TLx 与了 Hx 构成全 16 位计数器,操作模式同上,但无定标 器。 1 0 。 8 位自动重装载的定时器/计数器,每当计数露 TLx 溢出时,模式 2 THx 中的内容重新装载到 TLx(见图 1-12)。 1 1 见图 1-13)。对于定时模式 3。对于定时器 0,分成 2 个 8 位计数器( 器 1,停止计数。 定时器/计数器 0 TF1、TR1:定时器/计数器 1 IE0、IT0:外部中断 0 IE1、IT1:外部中断 1 例如可用以下语句: SETB IT0 CLR IT1 //外部中断 1 低电平触发 8 3. 时 器 2C (MSB) (LSB) 定 器/计数 2 控制寄存器 T ON D7 D6 D5 D4 D3 D2 D1 D0 TF2 EXF2 RCLK TCLK EXEN2 TR2 C/ 2T CP/ 2RL 图 1-18 T2CON 定时器/计数器 2 控制寄存器 作为波特率发生器 参考图 1-15) 第四讲 串行口 MCS-51 中的串行接口使它增色不少 双工通信接口,即 能同 器 SBUF 器,可同时发送、接收。实际上,这两个寄存器共 1.4.2 串行口控 工作状态。它的各位定义,见图 1-19,并说明 如下 MSB) (LSB) : RCLK=TCLK=1( C/T2=0 TR2=1 。此串行接口是一个全 时进行发送和接收。它可以作 UART(通用异步接收和发送器)用,也可以作同 步移位寄存器用。 1.4.1 数据缓冲寄存 SBUF:输入、输出寄存 用了物理地址。 制寄存器 SCON SCON 用于控制和监视串行口的 : ( D7 D6 D5 D4 D3 D2 D1 D0 SM0 SM1 SM2 REN TB8 RB8 TI RI 图 1-19 串行控制寄存器 SM0、SM1:工作模式, 8 位; ; SCON 共 4 种; SM2:模式 2 和模式 3 方式时使用; REN:允许接收; TB8:发送数据的第 RB8:接收数据的第 8 位; TI: 发送完成(软件清除); RI: 接收数据就绪(软件清除) 9 表 1-6 串行口 式选择 1.4.3 模式 0 0 下,串行口作同步移位寄存器用,其波特率是固定的,为 fosc/12, 其中 ORG 8000H 010000B ;查询方式 C 语言程序: #include //包含文件 SCON=0X10; //初始化串行控制寄存器 1.4.4 模式 1 于模式 1 时,传输的是 10 位:1 位起始位(0),8 位宽度的数据(低 位在 ;通讯模式(11.0592MHz) SM1 SM0 模式 特 率 操作模 功 能 波 0 0 0 1 1 0 1 l 0 1 2 3 同步移位寄存器 fosc/ c/32 8 位 UART 9 位 UART 9 位 UART fosc/l2 可 变 64 或 fos 可 变 在操作模式 fosc 是振荡器频率。这时数据由 RXD(P3.0)端出入,同步移位时钟由 TXD(P3.1) 端输出。发送或接收的是 8 位数据,低位在先(参见图 1-20)。 汇编语言程序: MOV SCON,#00 MOV SBUF,#88H JNB TI,$ CLR TI END #include //包含文件 main() { SBUF=0X88; //输出数据 while(!TI); //查询方式 TI=0; } 串行口工作 先),1 位停止位(1)。由 TXD 发送,由 RXD 接收。波特率是可变的,取决于定 时器 1 或 2 的溢出速率(参见图 1-22)。 MOV SCON,#01010000B MOV TMOD,#00100000B ;定时模式(注意定时器 0) MOV TL1,#0E8H ;时间常数(RS232:1200) MOV TH1,#0E8H ;时间常数 SETB TR1 ;启动定时器 10 (TH1) = 波特率××− 1232 fosc256 ,fosc=11.0592MHz,SMOD=0 定时器 1 作为波特率发生器时,常用时间常数及误差对照表 波特率(Hz) TH1 误差 150 40H 0% 300 A0H 0% 600 D0H 0% 1200 E8H 0% 2400 F4H 0% 4800 FAH 0% 9600 FDH 0% 1.4.5 模式 2 和 3 操作模式 2 和 3 中,发送(通过 TXD)和接收(通过 RXD)的都是 11 位:1 位起始 位(0),8 位数据(低位在先),1 位可编程位(第 9 数据位)和 1 位停止位(1)。发送 时,可编程位(TB8 可赋予 0 或 1。接收时可编程位进入 SCON 中的 RB8。 模式 2 和模式 3 的工作原理类同,唯一的差别是:模式 2 的波特率为 fosc/32 或 fosc/64,而模式 3 的波特率是可变的,利用定时器 1 或定时器 2 作波特率发生 器(参见图 1-22 和图 1-23)。 定时器 1 作为波特率发生器: 定时器 1 的计数速率=fosc/12 SMOD=0 时,n=32 波特率=定时器 1 的计数速率/n PCON 寄存器 SMOD=1 时,n=16 波特率= 溢出速率)计数器定时器 1/(32 2SMOD × = )]1TH(256[12 f 32 2 osc SMOD −×× 定时器 2 作为波特率发生器: 波特率= )]L2RCAP,H2RCAP(65536[162 fosc −×× 11 第五讲 中断系统 MCS-51 系列中,有 5 个中断源(或 6 个中断源),可分为 2 个优先级,其中每 一个中断源的优先级都可以由程序排定(图 1-26)。 1.5.1 允许中断寄存器 IE(物理地址:A8,可按位寻址) 图 1-27 示出允许中断寄存器各位的定义。现说明如下: (MSB) (LSB) D7 D6 D5 D4 D3 D2 D1 D0 EA × ET2 ES ET1 EX1 ET0 EX0 图 1-27 IE 允许中断寄存器 1.5.2 中断优先级寄存器 IP(物理地址:D8,可按位寻址) MCS-51 的中断分为 2 个优先级。每个中断源的优先级都可以通过中断优先级 寄存器 IP 中的相应位来设定。图 1-28 示出 IP 的,各位定义,其中: (MSB) (LSB) D7 D6 D5 D4 D3 D2 D1 D0 × × PT2 PS PT1 PX1 PT0 PX0 图 1-28 IP 允许中断优先级寄存器 例如 ⋯⋯ SETB PX0 ;外部中断 0 为高优先级 SETB ES ;串行中断 SETB EA ;开总中断 ⋯⋯ 1.5.3 优先级结构 靠 IP 寄存器把各中断源的优先级分为高低 2 级。它们遵循这样 2 条基本规则: 1. 低优先级中断可被高优先级中断所中断,反之不能; 2. 一种中断(不管是什么优先级)—旦得到响应,与它同级的中断不能再中断 它。 中断源 同级内的优先权 外部中断 0 最高 定时器/计数器 0 溢出 外部中断 1 定时器/计数器 1 溢出 串行口 定时器/计数器 2 溢出 最低 12 1.5.4 中断响应协议 当某中断源提出中断请求后,作为应答,CPU 首先使相应的“优先级激活”触 发器置位,以阻断同级和低级的中断。 硬件中断服务子程序调用时,把当时程序计数器 PC 的内容压入堆栈(在 MCS-51 中,PC 是 16 位的,占用了 2 个字节,没有自动保存程序状态字 PSW 的内容),同 时还根据中断的来源,把相应的矢量单元地址装入 PC 中。这些矢量地址是: 中断源 矢量单元 外部中断 0 0003H 最高 定时器 0 溢出 000BH 外部中断 1 0013H 定时器 1 溢出 001BH 串行口 0023H 定时器 2 溢出或 T2EX 端出现负跳变 002BH 最低 注意:仿真器常常将中断入口地址映射到别的地址空间。 1.5.5 外部中断 外部中断的激活方式分为两种:—种是电平激活,另一种是边沿激活。这两种 方式可以靠 TCON 寄存器中的中断方式位 ITl 或 IT0 来控制。 (MSB) (LSB) D7 D6 D5 D4 D3 D2 D1 D0 TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0 1.5.6 中断请求的撤除 CPU 响应某中断请求后,在中断返回(RETI)前,该中断请求应该撤除,否则会 引起另一次中断。 定时器/计数器 0 和 1:进入中断服务后自动清除中断申请标志位 TF0 或 TFl; 边沿激活:自动清除 IE0、IE1; 外部中断 0 和 1: 电平激活:采取措施,例如对信号进行整形等。 1.5.7 中断响应时间 从外部中断请求有效到开始执行服务程序的第一条指令,中间要隔 3 个机器周 期,这是最短的响应时间。当对 IE,IP 寄存器进行操作或执行 RETI 指令时,不会 立即相应中断源的申请。 13 第六讲 MCS-51 单片机系统扩展 在很多应用场合,MCS-51 自身的存贮器和 I/O 资源不能满足要求,这时就要 进行系统扩展。 1.6.1 外部总线的扩展 以 8051 单片机最小系统为例,介绍数据总线、地址总线和控制总线。 基本要求:熟练掌握最小系统的结构及三组总线。 图 1-29 MCS-51 外部三总线示意图 1.6.2 外部程序存贮器的扩展 介绍简明取指令时序; 在最小系统的基础上,以扩展 2764 为程序存贮器为例,介绍扩展方法。 基本要求:熟练掌握程序存贮器的扩展 方法及取指时序。 图 1-32 外部程序存贮器的简明操作时序 图 l-33 外部程序存贮器的连接 14 1.6.3 外部数据存贮器的扩展 介绍简明数据存贮时序; 在最小系统的基础上,以扩 展 6264 为程序存贮器为例, 介绍扩展方法。 基本要求:熟练掌握数据存贮器 的扩展方法及读写时序。 图 1-36 外部数据存贮器操作的简明时序图 图 1-38 扩展 6264 静态 RAM 第七讲 MCS-51 单片机的指令系统 1.7.1 寻址方式 寻址方式就是根据指令中给出的地址寻找真实操作数地址的方式。MCS-51 单 片机的寻址方式有种:寄存器寻址、直接寻址、立即寻址、变址寻址、相对寻址、 间接寻址和位寻址。 寄存器寻址: MOV A,R0 ;A←(R0) 直接寻址: MOV A,4FH ;A4←(4FH) 立即寻址: MOV A,# 6FH ;A←6FH 间接寻址: MOV A,@R1 ;A←((R1)) 相对寻址: SJMP rel ;PC←(PC)+ 2+ rel 变址寻址: MOVC A,@A 十 DPTR ;A4←((A)十(DPTR)) 位寻址: SETB EA ;EA=1 1.7.2 指令说明 MCS-51 指令系统按其功能可分为:数据传送指令、转移指令、算术运算指令、 逻辑运算指令和十进制指令。 15 1.7.3 伪指令 汇编语言必须通过汇编器的处理,才能转换为计算机能识别和执行的机器语 言。伪指令是汇编器用的指令。MCS-51 汇编器常用的伪指令有以下几种: 一、 ORG 伪指令(Origin) ORG 0000H 二、 DB 伪指令(Define Byte) DB 40H,56H,’A’ 三、 DW 伪指令(Define Word) DW 1234H,5678,0AF0H 四、 EQU 或=伪指令(Equal) COUNT=100 SPACE EQU 50H 五、 DATA 伪指令(Data) ERROR DATA 80H 六、 XDATA 伪指令(External Data) ADC XDATA 4000H 七、 BIT 伪指令 LED BIT 30H 八、 END 伪指令 1.7.4 汇编语言编程 ?166400 =÷ ORG 0000H ;定义程序首地址 SJMP MAIN ;跳转到主程序 ORG 0040H ;主程序地址 MAIN: MOV DPTR,#6400 ;省去人工转换 MOV R2,#00H ;被除数 MOV R3,#00H ; MOV R4,DPH ; MOV R5,DPL ; MOV DPTR,#16 ;除数 MOV R6,DPH ; MOV R7,DPL ; LCALL DIVD ;调用出除法子程序 MOV DPH,R3 ;商的高8位? MOV DPL,R4 ;商的低8位? 16 NOP SJMP $ ;------------------------------------------------------------- ;标号: DIVD ;功能: 双字节二进制无符号数除法 ;入口条件:被除数在 R4 、 R5 中,除数在 R6 、 R7 中。 ;出口信息: OV=0 时,双字节商在 R2 、 R3 中, OV=1 时溢出。 ;影响资源: PSW 、 A 、 B 、 R1 ~ R7 ;堆栈需求: 2字节 ;------------------------------------------------------------- DIVD: CLR C ;比较被除数和除数 MOV A, R3 SUBB A, R7 MOV A, R2 SUBB A, R6 JC DVD1 SETB OV ;溢出 RET DVD1: MOV B, #10H ;计算双字节商 DVD2: CLR C ;部分商和余数同时左移一位 MOV A, R5 RLC A MOV R5, A MOV A, R4 RLC A MOV R4, A MOV A, R3 RLC A MOV R3, A XCH A, R2 RLC A XCH A, R2 MOV F0, C ;保存溢出位 CLR C SUBB A, R7 ;计算( R2R3 - R6R7 ) MOV R1, A MOV A, R2 SUBB A, R6 17 ANL C, /F0 ;结果判断 JC DVD3 MOV R2, A ;够减,存放新的余数 MOV A, R1 MOV R3, A INC R5 ;商的低位置一 DVD3: DJNZ B, DVD2 ;计算完十六位商( R4R5 ) MOV A, R4 ;将商移到 R2R3 中 MOV R2, A MOV A, R5 MOV R3, A CLR OV ;设立成功标志 RET END 1.7.4 C 语言编程 #include //包含文件 #include //包含文件 void main(void) //主函数 { unsigned long int x; //长整型变量 unsigned int y,z; //整型变量 x=6400; //赋初值(被除数) y=16; //除数 z=x/y; //计算 while(1){;} } 第八讲 keil 51 应用入门 KEIL51 软件是众多单片机应用软件开发的优秀软件之一,它集编辑、编译、 仿真于一体,支持汇编、PLM 语言和 C 语言的程序设计;界面友好,易学易用。 2.1 KEIL51 的集成环境 2.2 简单的程调试 2.3 建立一个项目 2.4 项目中含有多个文件 18 2.5 汇编语言 2.6 机器代码的效率比较 (在实验室进行) 第九讲 keil 51 软件调试技巧 这一章我们将简单地向读者介绍调试用户应用程序的调试技巧,并给出相应的 程序清单,必要时还给出与之相应的操作方法及执行程序时的主要屏幕画面,以方 便读者的学习。 (在实验室进行) 3.1 P1 口作为输入端口 3.2 P1 口作为输出端口 3.3 外部中断 3.4 定时/计数器 0 作为定时器 3.5 定时/计数器 0 作为计数器 3.6 调试函数 第十讲 MCS-51 单片机系统扩展 这一章将向读者介绍一些 MCS-51 单片机系统扩展的常用电路设计,其中有并 行口、串并转换、静态数码管显示、动态数码管显示、A/D 转换、D/A 转换、时钟 日历、IC 卡等,举例中,均以 C 语言编程(为了兼顾初学者,部分地给出对应的 汇编程序),不熟悉的读者,可参阅本书的第五、六、七、八、九章。 4.1 扩展并行口 (1)用闲置不用的口线作为选通信号 此种方式连线简单,编程方便灵活。如 果使用 74LS573(74LS373)且不对 P1 口进行驱动处理,则最多可扩展四个同样类 型的并行输出端口,当然还需要与之对应 4 个选通信号。 ORG 8000H CLR P3.3 ;选通信号无效 MOV P1,#00H ;P1 口设定为输出口 MOV P1,#80H ;欲输出的数据为 80 SETB P3.3 ;选通信号有效 CLR P3.3 ;锁定数据,选通信号无效 END 下边是用 C 语言编写的同样功能的程序: #include //包含文件 #include //包含文件 19 sbit CLK =P3^3; main() { CLK=0; //选通信号无效 P1=0X00; //P1口设定为输出口 P1=0X80; //欲输出的数据为 80 CLK=1; //选通信号有效 CLK=0; //锁定数据,选通信号无效 } (2)部分地址译码法扩展并行输出端口 利用地址译码法扩展并行输出端口需要占用单片机的数据存贮器空间,其优点 是可扩展的端口数几乎不受限制,编程并不比前一种方法复杂,缺点是需要一些组 合逻辑电路与之配合。图 4-2 就是基于这一种方法而实现的硬件电路。从图中可以 看到所扩展的并行输出端口地址是 0000H~1FFFH,下边分别给出操作该端口的汇 编语言和 C 语言源程序: ORG 0000H MOV DPTR,#0000H MOV A,#80H MOVX @DPTR,A END C语言源程序如下: #include #include #include #define our_port XBYTE[0x0000] main() { our_port=0x80; } 图中 74HC138 使用了地址总线的高三位,即A15A14A13,把 64K地址空间分为 8 等份,其中新扩展的 8 位并行端口占用了 0000H~1FFFH地址空间,可见地址空 间的浪费还是比较严重的,但在大多数情况下还是可以接受的。如果需要充分利用 地址空间,可以使用全地址译码法。 20 (3)全地址译码法扩展并行输出端口 这一种方法需要借助于数字电子学中的一些门电路来实现,此处我们不再详 述,而是采用大家已经比较熟悉的可编程逻辑器件来实现这一设计要求。首先设计 一个全译码器,如下是实现这一要求的 PLD 设计文件(图 4-3 原理图): subdesign encoder ( A15,A14,A13,A12,A11,A10,A9,A8 :INPUT; A7,A6,A5,A4,A3,A2,A1,A0,WR :INPUT; ENABLE :OUTPUT; ) BEGIN ENABLE=A15 & A14 & A13 & A12 & A11 & A10 & A9 & A8 & A7 & A6 & A5 & A4 & A3 & A2 & A1 & A0 & !WR; END; 4.2 串并转换 在 8051 系列单片机系统中,如果串行口闲置不用,则利用它来扩展并行输出 或输入端口。这种扩展方法不占用外部数据存贮器的地址空间,而且也节省单片机 的硬件资源,但操作速度较慢,级连的越多,速度越慢。 (1)74HC164 扩展并行输出端口(图 4-4) 扩展方法如图 4-4 所示,下边分别给出汇编语言和 C 语言程序。 汇编语言程序: ORG 0000H MOV SCON,#00000000B MOV SBUF,#88H END C 语言程序: #include //包含文件 #include //包含文件 main() { SCON=0X00; //初始化串行控制寄存器 SBUF=0X88; //输出数据 } (2)用 74HC165 扩展并行输入端口 扩展方法如图 4-5 所示,下边分别给出汇编语言和 C 语言程序。 汇编语言程序: 21 ORG 0000H SETB P1.0 ;数据锁入无效 CLR P1.0 ;锁入数据 SETB P1.0 ;数据锁入无效 MOV SCON,#00010000B ;模式 0 工作方式,启动一次接收过程 JNB RI,$ ;等待接收结束 MOV A,SBUF ;读取已接收的数据 CLR RI ;清除接收就绪标志 END C 语言程序: #include //包含文件 #include //包含文件 sbit load = P1^0; main() { unsigned char x; load=1; //并行数据锁入无效 load=0; //并行数据锁入使能 load=1; //并行数据锁入无效 SCON=0X10; //模式 0 工作方式,启动一次接收过程 while(!RI); //等待数据移入 x=SBUF; //读取已接收的数据 RI=0; //清除接收就绪标志 } 第十一讲 数码管显示 4.3 静态数码管显示 数码管显示器,是各种仪器设备所不可缺少的重要组成部分,是仪器设备与人 对话的一种重要形式,它告诉人们机器的运行状态、数据的处理结果、提示操作人 员下一步要进行的操作等。按照显示方法的不同,可分为静态显示和动态显示两种, 这一节讨论静态数码管显示的设计方法。 汇编语言源程序: ORG 0000H MOV SCON,#00H ;初始化串行控制寄存器 CLR TI ;清除数据发送结束标志 MOV SBUF,#01H ;输出数据“8” JNB TI,$ ;等待数据发送结束 22 CLR TI ;清除数据发送结束标志 MOV SBUF,#9FH ;输出数据“1” JNB TI,$ ;等待数据发送结束 CLR TI ;清除数据发送结束标志 END C 语言源程序: #include //包含文件 #include //包含文件 main() { SCON=0X00; //初始化串行控制寄存器 TI=0; //清除数据发送结束标志 SBUF=0X01; //输出数据“8” while(!TI); //等待数据发送结束 TI=0; //清除数据发送结束标志 SBUF=0X9F; //输出数据“1” while(!TI); //等待数据发送结束 TI=0; //清除数据发送结束标志 } 对于图 4-6 来讲,编写程序相对比较简单,一旦将欲显示的数据发送出去,只 要当前显示的数据没有变化,就无须理睬数码管显示器,这就是静态数码管显示的 好处。但是,如果显示的位数比较多,硬件的开销、电源的功耗等问题,将变得更 加突出。 4.4 动态数码管显示 动态数码管显示,可以大幅度地降低硬件成本和电源的功耗,因为某一时刻只 有一个数码管工作,也就是所谓的分时显示,故显示所需要的硬件电路可分时复用。 图 4-7 基于这种思想的 8 位动态显示电路。图中使用了两片 74LS373 作为 7 段码和 位码驱动锁存器,8 个数码管是共阴极数码管,一片 ULN2803 作为位增强驱动器。 ULN2803 是 8 反相驱动器,其最大驱动电流为 500mA,假如数码管的 8 个二 极管都点亮,则共有 80mA 电流从阴极流出,ULN2803 完全有能力 80mA 的灌入 电流。 若 S0 和 S1 选通信号的地址分别为 7F80H、7F90H,定时器 1 作为定时刷新定 时器,定时时间常数为 2.5mS,C 语言源程序如下: #include #include #include 23 #define SEGMENT XBYTE[0x8000] //段码寄存器地址 #define BIT_LED XBYTE[0x8001] //位码寄存器地 #define fosc 11.0592 //晶振频率 #define time0 2500 //定时 2500uS unsigned char data display_bit,display_buffer[8]; unsigned char data time0_h,time0_l; unsigned int idata time0_times; unsigned char get_code(unsigned char i); void display(void); main() { BIT_LED=0; TMOD=0x21; //定时器/计数器 0 定时方式 1 time0_times=65536-time0*fosc/12; time0_h =(time0_times/256 ); time0_l =(time0_times%256); TH0=time0_h;TL0=time0_l; //高 8 位和低 8 位时间常数 TR0=1; //启动定时器 0 EA=ET0=1; //允许中断 display_bit=0x01; //从第一个数码管开始显示 display_buffer[7]=8; display_buffer[6]=7; display_buffer[5]=6; display_buffer[4]=5; display_buffer[3]=4; display_buffer[2]=3; display_buffer[1]=2; display_buffer[0]=1; while(1); } void time0_int(void) interrupt 1 //中断服务子程序 { TH0=time0_h;TL0=time0_l; 24 display(); //共需 40m 秒 } unsigned char get_code(unsigned char i) { unsigned char p; switch (i){ case 0: p=0x3F; break; /*0*/ case 1: p=0x06; break; /*1*/ case 2: p=0x5B; break; /*2*/ case 3: p=0x4F; break; /*3*/ case 4: p=0x66; break; /*4*/ case 5: p=0x6D; break; /*5*/ case 6: p=0x7D; break; /*6*/ case 7: p=0x07; break; /*7*/ case 8: p=0x7F; break; /*8*/ case 9: p=0x67; break; /*9*/ case 10: p=0x77; break; /*A*/ case 11: p=0x7C; break; /*B*/ case 12: p=0x39; break; /*C*/ case 13: p=0x5E; break; /*D*/ case 14: p=0x79; break; /*E*/ case 15: p=0x71; break; /*F*/ default: break;} return (p); } void display(void) { unsigned char i; switch (display_bit) { case 1: i=0;break; case 2: i=1;break; case 4: i=2;break; case 8: i=3;break; case 16: i=4;break; case 32: i=5;break; 25 case 64: i=6;break; case 128: i=7;break; default : break; } { BIT_LED=0; //关闭显示 SEGMENT=get_code(display_buffer[i]); //送段码 BIT_LED=display_bit; //送位码 if (display_bit<0x64) {display_bit=display_bit*2;} else display_bit=0x01; } } 第十二讲 A/D 转换(ADC0809) ADC0809 是 8 通道 8 位逐次逼近型 A/D 转换器,典型时钟频率为 640KHz,每一通 道转换时间约为 100μS,即 64 个脉冲转换一次。 一、等待连接方式 电路如图 4-10 所示。图中,将 ALE 信号二分频作为时钟信号。如果单片机晶 振 6MHz,则 ALE 信号的频率为 1MHz,故分频后送给 ADC0809 的时钟信号为 500KHz。 IN0-IN7 接模拟信号,8 通道轮流采集一次数据,并存贮与内部 RAM 中。A/D 转唤 起的地址为 7FFFH。 此种方式编程简单,但单片机的有效利用率不高,常常在只有单一任务时使用 这一种方法。 #include #include #define IN0 XBYTE[0X7FFF] //通道 0 地址 typedef unsigned char uchar; void adc0809 (uchar idata *x); //ADC0809 采样函数 void delay (void); //延时函数 void main (void) { static uchar idata ad[8]; //定义数据存储区 adc0809(ad); } 26 void adc0809 (uchar idata *x) { uchar i; uchar xdata *ad_adr; ad_adr=&IN0; for(i=0;i<8;i++) //8 个通道 { *ad_adr=i; //启动 AD 转换 delay(); //延时等待 x[i]=*ad_adr; //取转换值 } } void delay(void) {uchar j; for(j=0;j<20;j++) {;} } 二、中断连接方式 将图 4-11 ADC0809 的 EOC 信号经过一反相器反相后接到 MCS-51 的外部中断输 入端则形成另一种接口电路——中断方式接口,即利用 EOC 信号产生中断,通知 MCS-51 转换结束。 用中断方式处理 A/D 转换问题,可以大幅度地提高单片机的事务处理能力,使 其可以更出色地执行多个任务。 #include #include #define IN0 XBYTE[0x7FFF] //通道 0 地址 typedef unsigned char uchar; uchar i=0; uchar xdata *ad_adr; static uchar idata x[8]; void main (void) { IT1=0; EX1=1; EA=1; i=0; ad_adr=&IN0; 27 *ad_adr=i; //启动转换 for(;;) {;} } void int_serv(void) interrupt 2 //中断服务程序,读取转换值 { x[i]=*ad_adr; //读取转换结果 i++; if (i<8) //8路转换完毕否? {*ad_adr=i;} //未完,继续 } 三、查询连接方式 在 8051 单片机的应用系统中,如果外部中断多于两个,而且将其分配给更重 要的事务时,常常使用查询方式处理 A/D 转换的问题。 将图 4-12 中 ADC08098 的 EOC 信号接到 MCS-51 的 I/O 线上(本程序为 EOC 与 P1.0 相连)则形成又一种接口电路——查询方式,8051 即通过循环查询 EOC 信号, 判断转换是否结束。 #include #include #define IN0 XBYTE[0X7FFF] //通道 0 地址 typedef unsigned char uchar; sbit ad_busy=P1^0; void adc0809 (uchar idata *x); //ADC0809 采样函数 void main (void) {static uchar idata ad[8]; //定义数据存储区 adc0809(ad); } void adc0809 (uchar idata *x) { uchar i; uchar xdata *ad_adr; ad_adr=&IN0; for(i=0;i<8;i++) //8 个通道 {*ad_adr=i; //启动 AD 转换 while (ad_busy==0){;} x[i]=*ad_adr; //取转换值 } } 28 第十三讲 时钟日历(DS1302) 在研制智能设备仪器仪表时,常常用到时钟和日历,能够实现这种功能的集成 电路有好几种,都有各自的优点,DS1302 就是其中的一种。DS1302 以其体积小, 功耗低,自带 31 字节 RAM,遇闰年自动修正,使用简单,且不存在“千年虫”问 题,赢得了人们的青睐。 一、DS1302 的时序 DS1302 的时序图如图 4-19 所示,寄存器地址分配如图 4-20 所示。 二、DS1302 的典型应用 图 4-21 是 DS1302 在单片机应用 系统中,最简单的应用方法之一。图 中仅使用了单片机的三条 I/O 线,便 可实现对 DS1302 所有功能的操作。 三、编程分析 图 4-21 DS1302 的简单应用 #include #include sbit SCL_DS1302 =P1^0; //时钟 sbit IO_DS1302 =P1^1; //输入输出口 引脚定义 sbit RST_DS1302 =P1^2; //复位 unsigned char data display_buffer[8]; unsigned char bdata data_ds1302; sbit bit_data0=data_ds1302^0; sbit bit_data7=data_ds1302^7; 定义全局变量 unsigned char bdata x; sbit x0 =x^0; sbit x7 =x^7; void open_write_bit(); void initial_ds1302(); unsigned char read_ds1302(char command); void open_write_bit(); 外部函数 void close_write_bit(); void read_time(); void set_time(); 29 main() { initial_ds1302(); //上电启用,否则不走时 read_time(); //读取当前时分秒,放在数组中 display_buffer[0]=0X00; display_buffer[1]=0X08; display_buffer[4]=0X01; display_buffer[5]=0X00; display_buffer[6]=0X05; display_buffer[7]=0X08; //将 08 时 10 分 58 秒设置为当前时间 set_time(); //将数组中的时间置入 DS1302 while(1); } void close_write_bit() { char i; SCL_DS1302=0; _nop_(); RST_DS1302=1; _nop_();_nop_(); data_ds1302=0x8e; //write control redister for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0; _nop_();SCL_DS1302=1; data_ds1302=data_ds1302>>1; } data_ds1302=0x80; //close write protect bit IO_DS1302=0; for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0; _nop_();SCL_DS1302=1; data_ds1302=data_ds1302>>1; } } void open_write_bit() 30 { char i; SCL_DS1302=0; _nop_(); RST_DS1302=1; _nop_();_nop_(); data_ds1302=0x8e; //write control redister for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0; _nop_();SCL_DS1302=1; data_ds1302=data_ds1302>>1; } data_ds1302=0x00; //open write protect bit IO_DS1302=0; for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0; _nop_();SCL_DS1302=1; data_ds1302=data_ds1302>>1; } } void initial_ds1302() { unsigned char i; SCL_DS1302=0; _nop_(); RST_DS1302=1; _nop_();_nop_(); data_ds1302=0x8e; //write control redister for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0; _nop_();SCL_DS1302=1; data_ds1302=data_ds1302>>1; } data_ds1302=0x80; //close write protect bit IO_DS1302=0; for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0;_nop_(); 31 SCL_DS1302=1;data_ds1302=data_ds1302>>1; } RST_DS1302=0; _nop_(); SCL_DS1302=0; SCL_DS1302=0; _nop_(); RST_DS1302=1; _nop_();_nop_(); data_ds1302=0x90; //recharge register for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0;_nop_(); SCL_DS1302=1;data_ds1302=data_ds1302>>1; } data_ds1302=0xa4; //no rechaarge for battery for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0; _nop_();SCL_DS1302=1; data_ds1302=data_ds1302>>1; } RST_DS1302=0; _nop_(); SCL_DS1302=0; SCL_DS1302=0; _nop_(); RST_DS1302=1; _nop_();_nop_(); data_ds1302=0x80; for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0; _nop_();SCL_DS1302=1; data_ds1302=data_ds1302>>1; } data_ds1302=0x80; for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0; _nop_();SCL_DS1302=1; data_ds1302=data_ds1302>>1; 32 } RST_DS1302=0; _nop_(); SCL_DS1302=0; } unsigned char read_ds1302(char command) { char i; data_ds1302=(command<<1)|0x81; SCL_DS1302=0; _nop_(); RST_DS1302=1; for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0; _nop_();SCL_DS1302=1; data_ds1302=data_ds1302>>1; } SCL_DS1302=1; for (i=1;i<=8;i++) { data_ds1302=data_ds1302>>1; SCL_DS1302=0;_nop_(); bit_data7=IO_DS1302;SCL_DS1302=1; } RST_DS1302=0; _nop_(); SCL_DS1302=0; return(data_ds1302); } void write_ds1302(unsigned char address,unsigned char numb) //写入时分秒 { char i; RST_DS1302=0; SCL_DS1302=0; RST_DS1302=0; RST_DS1302=1; data_ds1302=0x80|(address<<1); 33 for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0; _nop_();SCL_DS1302=1; data_ds1302=data_ds1302>>1; } data_ds1302=numb; for (i=1;i<=8;i++) { SCL_DS1302=0;IO_DS1302=bit_data0; _nop_();SCL_DS1302=1; data_ds1302=data_ds1302>>1; } RST_DS1302=0; SCL_DS1302=1; } void read_time() //读时分秒 { unsigned char second,minute,hour,d; second=0; //read second address d=read_ds1302(second); display_buffer[7]=d&0x0f; display_buffer[6]=d>>4; minute=1; //read minute address d=read_ds1302(minute); display_buffer[5]=d&0x0f; display_buffer[4]=(d>>4); hour=2; //read hour address d=read_ds1302(hour); display_buffer[1]=d&0x0f; display_buffer[0]=(d>>4); } void set_time() { unsigned char data temp; unsigned char data hour_address,minute_address,second_address; open_write_bit(); temp=(display_buffer[0]<<4)|display_buffer[1]; write_ds1302(hour_address,temp); 34 temp=(display_buffer[4]<<4)|display_buffer[5]; write_ds1302(minute_address,temp); temp=(display_buffer[6]<<4)|display_buffer[7]; write_ds1302(second_address,temp); close_write_bit(); } 第十四讲 IC 卡(24C01) 在日常生活中,IC 卡的使用越来越广泛,而且还有进一步扩大的趋势。因此 有必要掌握这方面的知识,下面以 24C01 为例,简单地介绍一般使用方法。 #include #include sbit SCL_IC_CARD=P1^3; sbit SDA_IC_CARD=P1^4; sbit WP_IC_CARD =P1^7; bdata char com_data; sbit mos_bit=com_data^7; sbit low_bit=com_data^0; unsigned char data display_buffer[8]; 图 4-24 简单应用 void delay(int n); unsigned char rd_24c01(char a); void wr_24c01(char a,char b); main() { unsigned char i; WP_IC_CARD=1; for (i=0;i<=7;i++) {display_buffer[i]=rd_24c01(i);delay(250);} for (i=0;i<=7;i++) {wr_24c01(i,display_buffer[i]);delay(250);} while(1); } void start() //启动读写时序 { //图 4-22 (c)开始、结束脉冲时序 35 SDA_IC_CARD=1; SCL_IC_CARD=1; SDA_IC_CARD=0; //启动 start SCL_IC_CARD=0; } void stop() //停止操作 { //图 4-22 (c)开始、结束脉冲时序 SDA_IC_CARD=0; SCL_IC_CARD=1; SDA_IC_CARD=1; } void ack() //应答函数 { SCL_IC_CARD=1; SCL_IC_CARD=0; } void shift8(char a) //8 位移位输出 { data unsigned char i; com_data=a; for(i=0;i<8;i++) { SDA_IC_CARD=mos_bit; SCL_IC_CARD=1; SCL_IC_CARD=0; com_data=com_data*2; } } unsigned char rd_24c01(char a) //读 IC 卡函数 { data unsigned char i,command; SDA_IC_CARD=1; SCL_IC_CARD=0; start(); //启动 command=0XA0; //160; shift8(command); //送出器件地址 第一步 ack(); //应答 shift8(a); //送出存储器地址 36 ack(); //应答 start(); //启动 command=0XA1 //161; shift8(command); //送出器件地址 第二步 ack(); //应答 SDA_IC_CARD=1; // for(i=0;i<8;i++) //循环 8 次读取一个字节 { com_data=com_data*2; SCL_IC_CARD=1; low_bit=SDA_IC_CARD; 读取数据 SCL_IC_CARD=0; } stop(); //停止操作 return(com_data); } void wr_24c01(char a,char b) //写 IC 卡函数 { data unsigned char command; WP_IC_CARD=0; _nop_(); SDA_IC_CARD=1; SCL_IC_CARD=0; start(); //启动 command=0XA0; //160; shift8(command); //送出器件地址 写 IC 卡函数 ack(); //应答 shift8(a); //送出存储器地址 ack(); //应答 shift8(b); //送出欲写入的数据 ack(); //应答 stop(); //停止操作 _nop_(); WP_IC_CARD=1; } void delay(int n) //延时函数 37 { int i; for (i=1;i<=n;i++){;} } 第十五讲 温度转换(DS18B20) DS18B20 是单总线温度传感器。 1·功能特点 采用单总线技术,与单片机通讯只要一根 IO 线; 通过比较系列号可以在一根线上挂接多个 DS18B20; 低压供电,电源范围从 3V~5V,也可以直接从数据线上窃取电源; 测温范围-550~1250摄氏度,在-100~850摄 氏度范围内误差为±0.5 度; 数据位可编程 9~12 位,转换 12 位温度时 间为 750ms(最大); 用户可自设定预警上下限温度; 报警搜索命令可识别和寻址那个器件的 温度至超出预定值。 2·与单片机接口如图 4-25 所示。 图 4-25 与 8051 系列单片机连接 3·程序分析 #include typedef unsigned char uchar; sbit TMDAT = P3^6; /********** FUNCTION **********/ void dmsec (unsigned int count) { // mSec Delay 11.0592 Mhz unsigned int i; // 1MS 延时 while (count--) { for (i=0;i<125;i++){} } } void tmreset (void) { // Reset TX unsigned int i; TMDAT = 0; i = 103; while (i>0) i--; // Approx 900 uS TMDAT = 1; i = 4; while (i>0) i--; 38 } void tmpre (void) { // Wait for Presence RX unsigned int i; while (TMDAT); while (~TMDAT); i = 4; while (i>0) i--; } bit tmrbit (void) { // read one bit unsigned int i; bit dat; TMDAT = 0; i++; TMDAT = 1; i++; i++; dat = TMDAT; i = 8; while (i>0) i--; return (dat); } unsigned char tmrbyte (void) { // read one byte unsigned char i,j,dat; dat = 0; for (i=1;i<=8;i++) { j = tmrbit (); dat = (j << 7) | (dat >> 1); } return (dat); } void tmwbyte (unsigned char dat) { // write one byte unsigned int i; unsigned char j; bit testb; for (j=1;j<=8;j++) { testb = dat & 0x01; dat = dat >> 1; if (testb) { TMDAT = 0; // Write 1 39 i++; i++; TMDAT = 1; i = 8; while (i>0) i--; } else { TMDAT = 0; // Write 0 i = 8; while (i>0) i--; TMDAT = 1; i++; i++; } } } void tmstart (void) { // ds1820 start convert tmreset (); tmpre (); dmsec (1); tmwbyte (0xcc); // skip rom tmwbyte (0x44); // convert } unsigned char tmrtemp (void) { // read temp unsigned char a,b,y1,y2,y3; tmreset (); tmpre (); dmsec (1); tmwbyte (0xcc); // skip rom tmwbyte (0xbe); // convert a = tmrbyte (); // LSB b = tmrbyte (); // MSB y1=a>>4; y2=b<<4; y3=y1 | y2; return(y3); } /********** MAIN **********/ void main (void) 40 { unsigned int i; unsigned char last; uchar lsb,msb; dmsec(1); tmstart (); // ds1820 start convert dmsec(1000); last=tmrtemp (); // read temperature msb=last/0x0a+0x30; lsb=last%0x0a+0x30; while(1){} } 第十六讲 键盘控制器7289A 7289A是具有SPI 串行接口功能的可同时驱动8 位共阴式数码管或64 只独立 LED 的智能显示驱动芯片,该芯片同时还可连接多达64键的键盘矩阵,单片即可 完成LED显示﹑键盘接口的全部功能。7289A内部含有译码器,可直接接受BCD码 或16进制码,并同时具有两种译码方式。此外还具有多种控制指令如消隐﹑闪烁﹑ 左移﹑右移﹑段寻址等。7289A具有片选信号,可方便地实现多于8位的显示或多 于64键的键盘接口。 一、键盘控制器7289A特点 1)串行接口无需外围元件可直接驱动LED; 2)各位独立控制译码/不译码及消隐和闪烁属性; 3)循环左移/循环右移指令; 4)具有段寻址指令方便控制独立LED; 5)64 键键盘控制器内含去抖动电路; 二、7289A 典型应用电路 如图 4-36 所示。 三、程序分析 #include #define uchar unsigned char sbit CS=P1^0; sbit CLK=P1^1; sbit DIO=P1^2; sbit KEY=P1^3; uchar rebuf,sebuf; 41 bdata uchar com_data; sbit mos_bit=com_data^7; sbit low_bit=com_data^0; void delay_50us() //延时50us { uchar i; for(i=0;i<6;i++){;} } void delay_8us() //延时8us { uchar i; for(i=0;i<1;i++){;} } void delay_50ms() //延时50ms { uchar i,j; for(j=0;j<50;j++) for(i=0;i<125;i++){;} } void send(uchar sebuf) //发送 { uchar i; com_data=sebuf; CLK=0; CS=0; delay_50us(); for(i=0;i<8;i++) { delay_8us(); DIO=mos_bit; CLK=1; delay_8us(); com_data=com_data<<1; CLK=0; 42 } DIO=0; } void receive() //接收 { uchar i; CLK=1; delay_50us(); for(i=0;i<8;i++) { com_data=com_data<<1; low_bit=DIO; CLK=1; delay_8us(); CLK=0; delay_8us(); } rebuf=com_data; DIO=1; CS=1; } void reset() //复位清零 { KEY=1; DIO=1; delay_50ms(); send(0xa4); CS=1; } main() { reset(); sebuf=0x84; //发送缓冲赋值0x84; send(0x83); 43 delay_50us(); send(sebuf); CS=1; while(1) { while(KEY); send(0x15); //读取键值 delay_50us(); receive(); delay_50us(); switch(rebuf){ case 0x00: {send(0xA3);CS=1;} break; // 循环左移 case 0x01: {send(0xA2);CS=1;} break; //循环右移 case 0x0b: { send(0x88); delay_50us(); send(0x00);CS=1; } break; //闪烁 case 0x03: { send(0x88); delay_50us(); send(0xff); CS=1; } break; //取消闪烁 default: break; } while(!KEY); KEY=1; } } 44 45

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享文档获得金币 ] 0 人已下载

下载文档

相关文档