不同型号的单片机延时程序计算怎么写? { ms = ms * 30; while(ms--); }

当前位置: >>
单片机C51实验指导书
HC6800-ES V1.0 单片机开 发板学习指南普中科技 错误!未指定书签。目录第一讲 开发板资源介绍................................................................. 1 第二讲 软件安装 ........................................................................... 3 第三讲 程序下载 ......................................................................... 16 第四讲 KEIL 软件使用及入门 LED 灯 ......................................... 21 第五讲 蜂鸣器 ............................................................................. 36 第六讲 独立按键 ......................................................................... 38 第七讲 静态数码管 ...................................................................... 45 第八讲 矩阵键盘 ......................................................................... 51 第九讲 动态数码管 ...................................................................... 56 第十讲 电机 ................................................................................ 61 第十一讲 中断 ............................................................................. 70 第十二讲 1602 液晶显示 ............................................................. 77 第十三讲 定时器 ......................................................................... 86 普中科技 第十四讲 时钟芯片 DS1302 ........................................................ 93 第十五讲 串口通信 .................................................................... 108 第十六讲 温度传感器 18B20 ..................................................... 117 第十七讲 EEPROM 操作 24C02 .............................................. 132 第十八讲 红外遥控显示............................................................. 147 第十九讲 AD/DA 模数/数模转换 ............................................... 155 第二十讲 液晶屏显示 ................................................................ 175 附录 A 单片机 C 语言介绍 ........................................................ 194 附录 B 电路板绘制软件 PROTEL 介绍 ..................................... 243
错误!未指定书签。第一讲 开发板资源介绍本开发板相对以往开发板的特点是综合性比较高、把短路冒去掉了 省去接 线的麻烦更加方便了初学者、是一款性价比极高的产品,提供USB2.0和串口两种 通信方式,USB实现供电、编程、仿真、通信多种功能,另外还提供了Atmel单片 机的ISP接口。此板兼容STC、SST、Atmel、Philips等51家族的所有单片机。如 果使用ISP编程建议使用开发板自带的单片机,因为每个厂烧录程序的方式不一 样。HC6800S开发板有着丰富的外部资源, 通过对该开发实验仪的学习,学员不仅可以轻松快速地掌握单片机软件系统 的开发(C语言、汇编语言),而且还能快速掌握硬件电路的设计及嵌入系统开 发流程。 本套件配有丰富的实例源码、原理图等,特别适合单片机初学者,大中专院 校师生,单片机开发工程师选用,也是毕业设计和电子竞赛不可多得的参考板 ? 单片机采用 STC90c516 1280 SRAM ? 2.1 寸彩色液晶屏 ? SD 卡接口 ? 1602 液晶屏接口 ? 12864 液晶屏接口 ? 温度传感器 DS18B20 ? EEPROM 24C02 存储器 ? 8 位动态数码管 ? AD/DA 转换 PCF8951 ? DS1302 实时时钟 ? 4*4 矩阵键盘 ? 4 个独立键盘 ? 2*8 路 led 灯 ? 可更换晶振座 ? USB 接口,实现下载,供电,串行通信。通过 USB 转串口芯片 CH340T 转换 ? IR 红外接收头,红外数据传输 ? 板载继电器164K Flash 普中科技 ? 蜂鸣器 ? 步进电机 ULN2003 达林顿管驱动2 错误!未指定书签。第二讲 软件安装1、USB 转串口驱动的安装 双击 USB 驱动 SERIAL 程序的 Setup安装成功后,会出现一下提示画面点“确定”即可以结束安装过程。 安装完驱动程序成功后,把开发板与电脑连接可以看到设备管理器中的 COM 口。 在图标“我的电脑”右键,→属性,→硬件,→设备管理器(这是 XP 系统,WIN3 普中科技 系统与其相似找到设备管理器)显示成功的 COM 口安装不成功解决办法 如果发现是下图感叹号, 则没有安装成功或者没有安装、右键点击更新驱动程4 错误!未指定书签。 序选择“从列表或指定位置安装(推荐)”5 普中科技出现此对话框,选择不要搜索,自己选择安装 点“下一步”点击&下一步&6 错误!未指定书签。出现此对话框、点击“从磁盘安装”点击“浏览” 到 USB 驱动文件夹找到安装文件打开7 普中科技点击“确定”和“下一步”点击&完成&安装结束8 错误!未指定书签。KEIL 软件安装我们用现在新版本的 uVision 4 KEIL 开发环境,使用的版本是破解版,仅供大家学习使用。如做商业开发,请购买正版,可以获得更多 keil 软件原厂技 术支持。 即将安装软件如下,一个 keil 安装程序,一个注册机(右侧)。打开 C51V901.EXE 安装程序点击 Next &&9 普中科技I agree all the tems of ??选中 点击 Next &&设置安装目录,根据自己的情况选中安装目录,重新设置点击 Browse,这里默 认 C 盘,设置好安装目录后 点击 Next&&10 错误!未指定书签。输入相关信息(随便输入),输入完毕后点击 Next&&开始安装,安装过程中??..等待安装完成11 普中科技安装完成,点击 Finish 即可。 接下来破解软件。 打开注册机软件Target:选择 C5112 错误!未指定书签。 打开刚刚安装好的 keil 软件点击 File 菜单13 普中科技 选择 License Management。 现在没有破解 复制 CID 码到注册机中复制 CID 码,之后点击 Generate 生成注册码从 KEIL 复制的 CID 码粘贴到这里复制生成的注册码到 keil14 错误!未指定书签。复制到这里复制完注册码后,点击右侧的,即可完成破解。提示如下:以上软件安装完毕。接下来看程序了。15 普中科技第三讲 程序下载我们可以通过几种方式,对我们开发板的单片机下载程序。 下载接口可以分为 USB 口 和 DB9 串口。DB9 串口USB 接口下载软件又可分为 STC 官方软件 和普中科技自己开发的软件官方软件普中软件16 错误!未指定书签。 利用官方软件下载程序需要手动重启单片机,需要给单片机重新上电启动。 普中开发的下载软件及所设计的开发板实现了全自动下载功能,省去了手动操 作,极大方便了学习及开发人员。接下来就来讲一下下载程序的方式。1. USB 口,普中软件 我们插上 USB 口,打开开发板电源开关。然后打开普中 ISP 下载软件,如下图 :串口号 10在 usb 转串口驱动安装成功后,打开软件应该有串口号,如图指示。2. 芯片类型选择 STC90C5XX如下图。17 普中科技加载我们要下载到单片机里的程序,单击 的文件,,选择下载的后缀为.Hex打开后,会在文件名处有提示:文件路径,还有代码大小。代码大小4 最后一步:点击下载程序 此时继电器工作会有小的啪啪的响声。,下载成功如图显示。下载成功 !18 错误!未指定书签。接下来我们用官方 STC 下载软件下载13 241. 选择开发板上单片机型号,我们选 STC90C516 2. 选择串口,可通过设备管理器查看 3. 打开需要下载到单片机的程序 4. 点击下载 点击下载按钮后会有这样的信息:这时需要手动按开关键,关闭开关,打开开关这样一个过程,主要是给单片机冷19 普中科技 启动。 重新上电后,会出现开始下载程序,下载完成。上图表示下载成功。 现在用官方软件下载几乎都要手动重新给单片机上电过程。 市面的开发板主要也 都是采用这种下载方式。 建议使用普中软件自动下载。20 错误!未指定书签。第四讲 KEIL 软件使用及入门 led 灯单片机开发中除必要的硬件外,同样离不开软件,我们写的汇编语言源程序 要变为 CPU 可以执行的机器码有两种方法,一种是手工汇编,另一种是机器汇 编,目前已极少使用手工汇编的方法了。机器汇编是通过汇编软件将源程序变为 机器码,用于 MCS-51 单片机的汇编软件有早期的 A51,随着单片机开发技术的 不断发展,从普遍使用汇编语言到逐渐使用高级语言开发,单片机的开发软件也 在不断发展,Keil 软件是目前最流行开发 MCS-51 系列单片机的软件,这从近 年来各仿真机厂商纷纷宣布全面支持 Keil 即可看出。 Keil 提供了包括 C 编译 器、宏汇编、连接器、库管理和一个功能强大的仿真调试器等在内的完整开发方 案,通过一个集成开发环境(uVision)将这些部份组合在一起。运行 Keil 软 件需要 Pentium 或以上的 CPU,16MB 或更多 RAM、20M 以上空闲的硬盘空间、 WIN98、NT、WIN2000、WINXP 等操作系统。掌握这一软件的使用对于使用 51 系 列单片机的爱好者来说是十分必要的,如果你使用 C 语言编程,那么 Keil 几乎 就是你的不二之选(目前在国内你只能买到该软件、而你买的仿真机也很可能只 支持该软件) ,即使不使用 C 语言而仅用汇编语言编程,其方便易用的集成环境、 强大的软件仿真调试工具也会令你事半功倍。 我们将通过一些实例来学习 Keil 软件的使用, 在这一部份我们将学习如何 输入源程序,建立工程、对工程进行详细的设置,以及如何将源程序变为目标代 码。 在这里利用第二个例程,led 闪烁实验来建立 keil 工程。 打开 keil 软件,版本μ Vision2 ,μ vision3,μ vision4 都一样,在这 里用μ vision4 版本演示,打开之后如下图,有的时候会默认打开上次使用的工 程,单击 Project 菜单,选择 Close 下图界面: Project 关闭了默认打开的工程,显示21 普中科技我们要建立新的工程,选择 Project→ newμ vision projiect?选择工程要保存的路径,输入工程文件名,如图点击保存后会弹出一个对话框,要求用户选择单片机型号,可以根据用 户使用的单片机来选择, 我们使用的 STC90 单片机是兼容 51 内核的, Keil C5122 错误!未指定书签。 几乎支持所有的 51 内核单片机, 内核具有通用型, 51 如果程序用的资源不是 太复杂,我们可以选择任意一款 51 单片机内核就行。在稍后我们会继续讲解 怎么将 STC 单片机官方的头文件添加进来,在这暂不多介绍。Keil 软件的关 键是程序代码的编写,而非是用户选择什么硬件。例程以添加 Atmel 的 AT89C51 来说明。如图,然后,单击 确定(OK) 。功能描述如果出现下面的界面:单击是就可以,意思是将单片机的启动代码添加 到工程,我们不用修改。到目前我们还没建立一个完整的工程,只是有工程的名字,框架,工程 中还没有任何文件代码, (除了启动代码,有的 keil 版本不显示启动代码) , 接下来我们添加文件及代码。 单击菜单 File→New 选项,或者单击界面上的快捷图标 串口如下。 ,新建文件23 普中科技界面显示的 Text1 就是我们刚刚加入的文件,但是这个文件与我们的工 程还没有直接联系起来,单击图标 ,保存我们当前的 text1 文件,输入要保存的文件名,同时要输入文件扩展名,扩展名很关键,用 C 语言编写的 程序,则必须为**.c,汇编语言必须为**.asm(**为文件名) ,文件名是用户 自己取的。填好文件名后单击保存。24 错误!未指定书签。 回到编辑界面,单击 Target1 前面的“+”号,然后在 Source Group 1 上 右键单击,选择 Add Files to Group Source Group1,选中我们刚才建立的.c文件,这里是 led test.c,单击 add,只需单击一次。之后单击 Close 就可以。 回到主界面后,单击 Source Group 1 前的“+”号,刚刚添加的文件显示在里面。通过以上步骤我们就建立好了一个工程。接下来就可以写代码了。 在写代码之前介绍介个常用的按钮: 按钮:用来编译我们正在操作的文件。 按钮:用来编译修改过的文件,并生成应用程序共单片机下载。 按钮: 用来重新编译当前工程中所有的文件, 并生成应用程序共单片机下载。 因为很多工程不止有一个文件,当有多个文件时,用它进行编译。25 普中科技 按钮:用来打开“Option for Target”对话框,对当前工程进行设置。 工程的各个参数都可以在这里设置,具体的设置方法用到的时候,再和大家 详细讲述。 以上几个按钮是常用的,其他按钮在使用的时候再介绍。1. 编写程序 我们以一个程序为例来练习编写:led 闪烁。 这个程序大家一定要弄懂,也算是一个基础,懂了这个程序,也算是进入单 片机的领域了接下来我会详细讲解这个例程。 另外需要注意的是单片机写程序的 时候,一定是英文状态下的字符,尤其注意“;”,往往就因为这个分号,程序出 现问题,所以输入时一定要注意是在英文状态下的。 我们在编辑框中输入以下程序: /******************************************************************** * LED 闪烁的简单试验 * ********************************************************************/#include &reg51.h& //此文件中定义了 51 的一些特殊功能寄存器void delay(unsigned int i); //声明延时函数 void main() { while(1) { P2 = 0x00; //置 P0 口为低电平 delay(600); //调用延时程序 P2 = 0 //置 P0 口为高电平 delay(600); // 调用延时程序 } } /*******延时函数*************/ void delay(unsigned int i) { for(i; i & 0; i--) //循环 600*255 次 for(j = 255; j & 0; j--); }26 错误!未指定书签。我们先编译一下:第一次编译我们点击,看一下结果结果的意思: 编译 led2.c? 链接? data=9.0--占用内部 RAM9 个字节,xdata=0:外部 RAM 0 字节, Code=56 代码长度为 56 字节 生成单片机可下载的 HEX(十六进制)文件。 没有错误,没有警告。在这里说明一点:生成 HEX 文件是我们自己设置的,默认的情况下不会生成 HEX 文件。单击 确定。 进行设置,如下图,选择 Output,勾选 Creat hex,后单击27 普中科技 2. 接下来我们分析上面的程序:/* */作用, 它是用来注释一段内容的, 编译器不对其进行编译, 只要在/**/直接的内容都被注释掉。 // 是用来注释其后面的内容,只能注释一行。 #include &reg51.h& //这句告诉我们包涵 51 的头文件,那这个头文件里面 放的是什么东西呢?放的是 51 单片机对应的操作的寄存器地址,如我们直接用 来操作的 P1 口就是代表 0x90 地址,我们可以将光标低位在&reg51.h&上然后右 键打开头文件, #ifndef __REG51_H__ #define __REG51_H__ /* BYTE Register */ sfr P0 = 0x80; sfr P1 = 0x90; sfr P2 = 0xA0; sfr P3 = 0xB0; sfr PSW = 0xD0; sfr ACC = 0xE0; ??????????? /* SCON */ sbit SM0 = 0x9F; sbit SM1 = 0x9E; sbit SM2 = 0x9D; sbit REN = 0x9C; sbit TB8 = 0x9B; sbit RB8 = 0x9A; sbit TI = 0x99; sbit RI = 0x98; #endif头文件中定义了 51 系列单片机的所有功能寄存器, 我们直接使用其代号就可 以,P0,P1 等。 如:sfr P0=0x80,意思是把单片机内部地址 0x80 处的寄存器重新起名字叫 P0,以后我们在程序中直接操作 P0,就相当于对单片机内部 0x80 地址处的寄存28 错误!未指定书签。 器进行操作。通俗的讲,也就是说,通过 sfr 这个关键字,让 Keil 编译器在单 片机与人之间搭建一个桥梁,我们操作 P0 口,单片机对应就去操作内部 0x80 的 地址。所以我们写程序要在开始处直接包涵单片机的头文件。 sbit SM0 = 0x9F;是定义位操作地址 0x9F 的,这个地址只代表一个位。我们操作 SM0,对应单片机就是操作位地址 0x9F。 总结起来 sfr 与 sbit 区别 sfr 是定义字节的 sbit 是定义位的 我们在返回主程序:void delay(unsigned int i) { for(i; i & 0; i--) for(j = 100; j & 0; j--); }8位 1位定义一个函数 delay();与 c 语言一样,要用一个函数,先定义,我们可以叫 它子函数,可以调用的。void main() // 结构同 c 语言一样,main()函数开始 { // 大括号。 P2 = 0x00; //置 P0 口为低电平从这句开始,你现在控制单片机了,告诉单片机把你的 P2 口都输出 0,如果 你不操作 P2 口了,那么 P2 口一直保持这个状态,直到你去改变它。delay(1000); //调用延时程序调用子函数 delay(),告诉单片机去执行 delay 那个函数,那么 P2 口一直保 持 0 这状态。P2 = 0 //置 P0 口为高电平这时告诉 P2 口全部输出 1,(0xff=).状态从 0 变成 1 了,对应的 灯的输出也由低电平变成高电平了delay(1000); // 调用延时程序,再进行延时, }这个程序里执行完了一次又干什么呢, Keil 编译器会编译成一直重复执行 main()函数里面的代码,整个代码的效果就 是:29 普中科技 ① P2 输出低电平 ② 延时一段时间,目的是 P2 输出的低电平保持一段时间 ③ P2 输出高电平 ④ 延时一段时间,目的是 P2 输出的高电平保持一段时间 ⑤ 重复①到④的过程 实际效果就是 led 一闪一闪通过更改 delay(延时时间 i) ,参数 i,可以改变闪烁频率。 实际操作:1. 打开软件(提前装好驱动),点击打开文件,打开我们刚刚编译的 led.hex 文件,下载程序,就会看到 led 闪烁。我们在看一个例程,就是单片机最经典的流水灯例程: 以下是我们的硬件电路,led 一段接单片机 P2 口的 8 个引脚,另外一边接排阻 RP12 和 RP13,然后接到电源30 错误!未指定书签。基础知识介绍: 排阻: 一般在排阻上都标有阻值号, 其公共端附近也有明显标记。 如下下图表示为 472,表示 47*错误!未找到引用源。=4700 欧姆,还有的标号 如 3R0,表示阻值为 3 欧姆, 4K7 表示阻值为 4.7kΩ , R002 表示阻值为 0.002 欧 姆。 (2)发光二极管。它具有单向导电性,通过 5mA 左右电流即可发光,电流越 大,其亮度越强,但若电流过大,会烧毁二极管,一般我们控制在 3 mA-20mA 之 间。在这里,给发光二极管串联一个电阻的目的就是为了限制通过发光二极管的 电流不要太大,因此这个电阻又称为“限流电阻” 。当发光二极管发光时,测量 它两端电压约为 1.7V,这个电压又叫做发光二极管的“导通压降” 。图 2.2.9 和 图 2.2.10 分别为直插式发光二极管和贴片式发光二极管实物图。发光二极管正 极又称阳极,负极又称阴极,电流只能从阳极流向阴极。直插式发光二极管长脚 为阳极,短脚为阴极。仔细观察贴片式发光二极管正面的一端有彩色标记,通常 有标记的一端为阴极。图 2.2.9图 2.2.10关于排阻大小的选择:欧姆定律想必大家都清楚,U=IR,当发光二极管正常导 通时,其两端电压约为 1.7V,发光管的阴极为低电平,即 0V,阳极串接一电阻, 电阻的另一端为 Vcc , 为 5V,因此加在电阻两端的电压为 5V-1.7V=3.3V,计算 穿过电阻的电流,3.3 V/ 1000Ω =3.3mA。即穿过发光管的电流也为 3.3mA,若 想让发光管再亮一些,我们可以适当减小该电阻。31 普中科技 看我们的原理图,可以知道:LED 的正极接在 VCC 上,只要给了低电平,那 么 LED 就会亮,低电平对应到单片机的逻辑就是 0,只要单片机的某一个管脚输 出 0,那么对应的发光二极管就会亮。我们看一下源码:/****************************************************************************** * 实验名 : 左右流水灯实验 * 使用的 IO : P2 * 实验效果 : 点亮的 LED 从右边往左边移动,到达左边再往右边移动,依此循环。 * 注意 : ******************************************************************************/ #include&reg51.h& #include&intrins.h& //因为要用到左右移函数,所以加入这个头文件 #define GPIO_LED P2 //将 P2 口另外取名为 GPIO_LEDvoid Delay10ms(unsigned int); //误差 0us /****************************************************************************** * 函数名 : main * 函数功能 : 主函数 * 输入 : 无 * 输出 : 无 ******************************************************************************/ void main(void) { GPIO_LED=0 while(1) { for(n=0;n&7;n++) //左移 7 次,这样子就会到达最左边 { GPIO_LED=_crol_(GPIO_LED,1); //将 GPIO_LED 左移一位 Delay10ms(50); //延时 } for(n=0;n&7;n++) //右移 7 次,这样子就会到达最右边 { GPIO_LED=_cror_(GPIO_LED,1); //将 GPIO_LED 右移一位 Delay10ms(50); //延时 } } } /****************************************************************************** * 函数名 : Delay10ms * 函数功能 : 延时函数,延时 10ms * 输入 : 无32 错误!未指定书签。* 输出 : 无 ******************************************************************************/ void Delay10ms(unsigned int c) //误差 0us { unsigned char a,b; for(;c&0;c--) for(b=38;b&0;b--) for(a=130;a&0;a--); }以上程序实现点亮一盏 LED 之后左右移动。 讲解:移位操作‘&&’ ‘&&’ 1. 左移。C51 中操作符为“&&” ,每执行一次左移指令,被操作的数将最高位移 入单片机 PSW 寄存器的 CY 位,CY 位中原来的数丢弃,最低位补 0,其他位依 次向左移动一位,如下图所示:2. 右移。C51 中操作符为“&&’,每执行一次右移指令,被操作的数将最低位 ’ 移入单片机 PSW 寄存器的 CY 位,CY 位中原来的数丢弃,最高位补 0,其他 位依次向右移动一位,如下图所示。3. 循环左移。最高位移入最低位,其他位依次向左移一位。C 语言中没有专门 的指令,通过移位指令与简单逻辑运算可以实现循环左移,或直接利用 C51 库中自带的函数_crol_实现,如下图所示。_crol_函数所在的头文件是 &intrins.h&33 普中科技4. 循环右移。最低位移入最高位,其他位依次向右移一位。C 语言中没有专门 的指令,通过移位指令与简单逻辑运算可以实现循环右移,或直接利用 C51 库中自带的函数-cror 实现,如下图所示。_cror_函数所在的头文件是 &intrins.h&5. 讲解:PSW 寄存器 PSW (Program Status Word)全称为程序状态字标志寄存器,是一个 8 位寄 存器,位于单片机片内的特殊功能寄存器区,字节地址 DOH,用来存放运算结果 的一些特征,如有无进位、借位等,使用汇编编程时 PSW 寄存器很有用,但在利 用 C 语言编程时,编译器会自动控制该寄存器,很少人为操作它,大家只需做简 单了解即可.其每位的具体含义如下图所示.1) CY 一进位标志位,它表示运算是否有进位(或借位).如果操作结果在最 高位有进位(加法)或者借位(减法),则该位为 1,否则为 0. 2) AC 一辅助进位标志,又称半进位标志,它指两个 8 位数运算低四位是否 有半进位,即低四位相加(或相减)是否进位(或借位),如有 AC 为 1,否 则为 0e 3) FO 一由用户使用的一个状态标志位,可用软件来使它置 1 或清 0,也可 由软件来测试它,以控制程序的流向.34 错误!未指定书签。 4) RS1, RSO-4 组工作寄存器区选择控制位,在汇编语言中这两位用来选择 4 组工作寄存器区中的哪一组为当前工作寄存区. 5) OV 一溢出标志位,反映带符号数的运算结果是否有溢出.有溢出时,此 位为 1,否则为 0. 6) P 一奇偶标志位,反映累加器 ACC 内容的奇偶性,如果 ACC 中的运算结果有偶数个I(如 B,其中有 4 个 1),则 P 为 0,否则 P 为 1.6. _cror_() ;函数 循环右移函数,包含在 intrins.h 的库函数里面。 7. _crol_() ;函数 循环左移移函数,包含在 intrins.h 的库函数里面。35 普中科技第五讲 蜂鸣器蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计 算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等 电子产品中作发声器件。 我们开发板上常用的蜂鸣器就是常常说的交流蜂鸣器或 直流蜂鸣器(自激式蜂鸣器) 直流蜂鸣器是给一定的驱动直流电压就会响。而 。 交流蜂鸣器是需要给蜂鸣器一个脉冲才会响。常见的有 PWM 波控制蜂鸣器的频 率。脉冲就是高低电平的切换,如下图:一个方波脉冲我们用单片机的 IO 口实现一种这样高低电平的方波,驱动蜂鸣器发音。我们板 子上配的就是交流蜂鸣器。 接下来我们看一下蜂鸣器的硬件电路:蜂鸣器通过 ULN2003 驱动。 这里 ULN2003 暂不多做介绍,详细介绍可参考步进电机篇。 我们看一下程序源码:/****************************************************************************** * * 实验名 : 蜂鸣器实验 * 使用的 IO : P1^5 * 实验效果 : 蜂鸣器响。 * 注意 : *******************************************************************************36 错误!未指定书签。/ #include &reg51.h& sbit Beep = P1^5 ; void Delay(unsigned int i) ; /****************************************************************************** * 函数名 : main * 函数功能 : 主函数 * 输入 : 无 * 输出 : 无 ******************************************************************************/ void main() { Beep= 1; Delay(5); Beep= 0; Delay(5); } /****************************************************************************** * 函数名 : Delay() * 函数功能 : 延时函数 * 输入 : 无 * 输出 : 无 ******************************************************************************/ void Delay(unsigned int i) { for(i; i & 0; i--) for(j = 200; j & 0; j--); }下载生成的 hex 文件,就可听到蜂鸣器发音。37 普中科技第六讲 独立按键按键是什么东西,我想这个就不必由我向各位阐述了。嗯,如你所见,按键 种类繁多,功能有简有繁,极大的充斥着我们的生活。但是无论如何,所有的按 键其实都有一个原型,来源于同一种原理,所有的按键无论多复杂,多华丽,都 是从这样一个原型发展而成的。好比你就算长的再帅,你也是只猩猩变来的,呵 呵。我们平日所见到的绝大部分的按键,其实都可以归类为一种,叫“接触式按 键” 。下图为一个典型的接触式按键(又称轻触开关) 。需要特别说明的是,这里说的“接触”,是指机械层面上的接触,而不是感 光或者某些特殊涂层(比如触摸屏)一类的接触。所以,按键的工作特性其实是 一种机械特性,下文会详细说明。,如上图,请对照图一想象,1、2、3、4 分别对应按键的四个引脚,其中蓝 色的线表示按键未被按下之时的状态,我成为初始状态,它是不导通的;而绿色 的线是却永久导通的。各位明白了么,其实是两个相同的结构连在一起了。我们38 错误!未指定书签。 只要将需要按键开关作用的线路分别接在1、3 和2、4 的任意取一组合,概括起 来就是(1,2)、(1,4)、(3,2)、(3,4)四种组合,都可以起到我们预 期的开关作用。 相信以上说明使大家对按键的工作原理有了个比较清晰的认识了, 现在来说 说一个小知识。先看下图(图4):首先说明的是,上图的连法是不允许的,因为当按键按下之后,电源和地短 接,会将导线直接烧毁。但是此处用作特例,假设导线不会烧毁。现在来提出一 个问题,当按键按下以后,请问如果这时用万用表测量导线上任何一处的电压, 得到的结果是VCC 还是GND 的电压? 答案是:GND,即表示测出的电压为0V。为什么呢,因为导线上,对于两端 的电平是一种类似于程序语言逻辑运算里面的“与”,即对于导线两端:有零即 为零,只有全为一是才为一。理解了这点,按键的工作前提就有了。 键盘分为编码键盘和非编码键盘。 键盘上闭合键的识别由专用的硬件编码器 实现,并产生键编码号或键值的称为编码键盘,如计算机键盘。而靠软件编程来 识别的键盘称为非编码键盘,在单片机组成的各种系统中,用的较多的是非编码 键盘。非编码键盘又分为独立键盘和行列式键盘(常说的矩阵键盘) 。在这一讲 中我们介绍一下单片机中键盘使用。 单片机的 IO 口既可作为输出也可作为输入使用,当检测按键时用的是它的 输入功能,我们把按键的一端接地,另一端与单片机的某个 I/O 口相连,开始时 先给该 IO 口赋一高电平,然后让单片机不断地检测该 I/O 口是杏变为低电平, 当按键闭合时,即相当于该 I/O 口通过按键与地相连,变成低电平,程序一旦检 测到 I/O 口变为低电平则说明按键被按下,然后执行相应的指令。 我们先来说一下,按键常常遇到的问题―抖动问题。39 普中科技还以图四为例,按键未按下之前,图四按键左端的导线因为连在VCC 上而显 示高电平,右端显示低电平,按键按下后,按键闭合,整个导线都显示低电平, 然后按键松开,又回到按键按下之前的点评状态。如果只考察按键左端的电平变 化,应该是上图中所显示的一个负脉冲波形。但是,实际上,正确的波形应该是 下图。相比于上图,大家都看到了在高低电平直接有一段锯齿一样的波形,这就 是所谓的按键抖动。 为什么会有按键抖动呢,原因很简单,接触式按键是靠机械的接触来实现开 关作用的。这种接触方式就注定了它要经历一个“接触不稳定――正在稳定中― ―彻底稳定”的一种过程。就好比你用手抓紧一颗石头,即使你一开始就很用力 的握紧, 也不可能马上就达到最紧的状态, 也要经历一个从握住到最紧握的过程。 那么在这个过程里,接触式按键就处于一种徘徊在“闭合”与“断开”两者之间 的状态。 体现在电路中, 就是在一小段时间内有非常多的 “按下――抬起” 动作。 而这段抖动的时间,大概是10~20 毫秒,依不同的环境条件而定。 解决这个问题常见的方法有软件去抖动和硬件去抖动。 我们解释一下抖动:关于按键去抖动的解释,我们在手动按键的时候,由于 机械抖动或是其它一些非人为的因素很有可能会造成误识别, 一般手动按下一次 键然后接着释放,按键两片金属膜接触的时间大约为 50ms 左右,在按下瞬间到 稳定的时间为 5-10ms,在松开的瞬间到稳定的时间也为 5-10ms, 如果我们再首次 检测到键被按下后延时 10ms 左右再去检测,这时如果是干扰信号将不会被检测 到,如果确实是有键被按下,则可确认,以上为按键识别去抖动的原理。40 错误!未指定书签。独立按键:我们先将一下独立按键的使用方法,开发板独立按键电路图如下:独立按键一共 5 个,分别连接在单片机的 P3.0 到 P3.4 口。去抖动的方式,我们 采用软件延时的方法。过程如下: 1. 先设置 IO 口为高电平(一般上电默认就为高) 2. 读取 IO 口电平确认是否有按键按下 3. 如有 IO 电平为低电平后,延时几个 ms 4. 再读取该 IO 电平,如果任然为低电平,说明对应按键按下 5. 执行相应按键的程序/************************************************************************** * 实验名 : 独立按键实验 * 使用的IO : LED使用P2,键盘使用P3.0、P3.1、P3.2、P3.3 * 实验效果 : 按下K1键,灭掉LED,按下K2键,打开所有的LED,按下K3键,LED 左移一位,按下K4键,LED右移一位。 * 注意 :由于P3.2口跟红外线共用,所以做按键实验时为了 不让红外线影响实验 *效果,最好把红外线先取下来。 **************************************************************************/ #include&reg51.h& #include&intrins.h& #define GPIO_LED P2 sbit K1=P3^0; sbit K2=P3^1;41 普中科技sbit K3=P3^2; sbit K4=P3^3; void Delay10ms( );//延时10ms/************************************************************************** * 函数名 : main * 函数功能 : 主函数 * 输入 : 无 * 输出 : 无 **************************************************************************/ void main(void) { unsigned int i,j; while(1) { if(K1==0) //检测按键K1是否按下 { Delay10ms(); //消除抖动 if(K1==0) //再次检测按键是否按下 j=0; while((i&50)&&(K1==0)) //检测按键松手检测,如果不松手超过延时 也会自动结束等待 { Delay10ms(); i++; } i=0; } if(K2==0) //检测按键K2是否按下 { Delay10ms(); if(K2==0) j=0 while((i&50)&&(K2==0)) { Delay10ms(); i++; } i=0; } if(K3==0) //检测按键K3是否按下 { Delay10ms(); if(K3==0)42 错误!未指定书签。{ if((j==0)||(j==0xff)) 的,就点亮他的第一盏灯 { j=0 } else j=_crol_(j,1); } while((i&50)&&(K3==0)) { Delay10ms(); i++; } i=0; } //如果当前状态是全亮的或者全灭//左移一位if(K4==0) //检测按键K4是否按下 { Delay10ms(); if(K4==0) { if((j==0)||(j==0xff)) { j=0 } else j=_cror_(j,1); //右移一位 } while((i&50)&&(K4==0)) { Delay10ms(); i++; } i=0; } GPIO_LED=j; } } /************************************************************************* * 函数名 : Delay10ms * 函数功能 : 延时函数,延时10ms * 输入 : 无 * 输出 : 无43 普中科技**************************************************************************/ void Delay10ms(void) //误差 0us { unsigned char a,b,c; for(c=1;c&0;c--) for(b=38;b&0;b--) for(a=130;a&0;a--); }下载独立按键控制 led 灯.hex,观察实验结果。 实验效果是:按下 K1 键,灭掉 LED,按下 K2 键,打开所有的 LED,按下 K3 键, LED 左移一位,按下 K4 键,LED 右移一位。44 错误!未指定书签。第七讲 静态数码管我们先看看什么是数码管,上图就是各种长相各种样子的数码管了,肯定很眼熟了吧。 不管将几位数码管连在一起,数码管的显示原理都是一样的,都是靠点亮内 部的发光二极管来发光,下面就来我们讲解一个数码管是如何亮起来的。数码管 内部电路如下图所示,从右图可看出,一位数码管的引脚是 10 个,显示一个 8 字需要 7 个小段, 另外还有一个小数点, 所以其内部一共有 8 个小的发光二极管, 最后还有一个公共端,生产商为了封装统一,单位数码管都封装 10 个引脚,其 中第 3 和第 8 引脚是连接在一起的。而它们的公共端又可分为共阳极和共阴极, 中间图为共阴极内部原理图,右图为共阳极内部原理图。45 普中科技 上图展出了常用的两种数码管的引脚排列和内部结构。总所周知,点亮发光 二极管就是要给予它足够大的正向压降。 所以点亮数码管其实也就是给它内部相 应的发光二极管正向压降。如上图左(一共a、b、c、d、e、f、g、DP 八段), 如果要显示“1”则要点亮b、c 两段LED;显示“A”则点亮a、b、c、e、f、g 这 六段LED;我们还知道,既然LED 加载的是正向压降,它的两端电压必然会有高 低之分:如果八段LED 电压高的一端为公共端,我们称之为共阳极数码管(如上 图中) 如果八段LED 电压低的一段为公共端, ; 则称之为共阴极数码管 (上图右) 。 所以,要点亮共阳极数码管,则要在公共端给予高于非公共端的电平;反之点亮 共阴极数码管,则要在非公共端给予较高电平。 对共阴极数码来说, 其8个发光二极管的阴极在数码管内部全部连接在一起, 所以称“共阴”,而它们的阳极是独立的,通常在设计电路时一般把阴极接地。 当我们给数码管的任意一个阳极加一个高电平时, 对应的这个发光二极管就点亮 了。如果想要显示出一个8字,并且把右下角的小数点也点亮的话,可以给8个阳 极全部送高电平,如果想让它显示出一个0字,那么我们可以除了给第“g, dp” 这两位送低电平外,其余引脚全部都送高电平,这样它就显示出0字了。想让它 显示几,就给相对应的发光二极管送高电平,因此我们在显示数字的时候首先做 的就是给0-9十个数字编码,在要它亮什么数字的时候直接把这个编码送到它的 阳极就行了。 共阳极数码管其内部8个发光二极管的所有阳极全部连接在一起,电路连接 时, 公共端接高电平, 因此我们要点亮的那个发光管二极管就需要给阴极送低电 平,此时显示数字的编码与共阳极编码是相反的关系,数码管内部发光二极管点 亮时,也需要5mA以上的电流,而且电流不可过大,否则会烧毁发光二极管。由 于单片机的I/O口送不出如此大的电流,所以数码管与单片机连接时需要加驱动 电路,可以用上拉电阻的方法或使用专门的数码管驱动芯片,本实验板上使用的 是74HC573锁存器,其输出电流较大,电路接口简单,可借鉴使用。 一般共阳极数码管更为常用, 为什么呢?这是因为数码管的非公共端往往接 在IC 芯片的IO上, 而IC 芯片的驱动能力往往是比较小的, 如果采用共阴极数码 管, 它的驱动端在非公共端, 就有可能受限于IC芯片输出电流不够而显示昏暗 (比 如51单片机),要外加上拉电阻或者是三极管加大驱动能力。所以使用共阳数码46 错误!未指定书签。 管的好处是:将驱动数码管的工作交到公共端(一般接驱动电源),加大驱动电 源的功率自然要比加大IC芯片IO口的驱动电流简单许多。另一方面,这样也能减 轻MCU的负担。 当多位一体时,它们内部的公共端是独立的,而负责显示什么数字的段线全 部是连接在一起的,独立的公共端可以控制多位一体中的哪一位数码管点亮,而 连接在一起的段线可以控制这个能点亮数码管亮什么数字, 通常我们把公共端叫 做“位选线”,连接在一起的段线叫做“段选线”,有了这两个线后,通过单片 机及外部驱动电路就可以控制任意的数码管显示任意的数字了。 一般单位数码管有10个引脚,二位数码管也是10个引脚,四位数码管是12 个引脚,关于具体的引脚及段、位标号大家可以查询相关资料,最简单的办法就 是用数字万用表测量,若没有数字万用表也可用5V直流电源串接1k电阻后测量, 将测量结果记录,通过统计便可绘制出引脚标号。 知识点:如何用万用表检浏数码管的引脚排列 对数字万用表来说,红色表笔连接表内部电池正极,黑色表笔连接表内部电 池负极,当把数字万用表置于二极管档时,其两表笔间开路电压约为1.5V,把两 表笔正确加在发光二极管两端时,可以点亮发光二极管。 如下图所示,将数字万用表置于二极管挡,红表笔接在①脚,然后用黑表笔 去接触其他各引脚,假设只有当接触到⑨脚时,数码管的a段发光,而接触其余 引脚时则不发光。由此可知,被测数码管为共阴极结构类型,⑨脚是公共阴极, ①脚则是数码管的a段.接下来再检测各段引脚,仍使用数字万用表二极管档,将 黑表笔固定接在⑨脚,用红表笔依次接触②、③、④、⑤、⑥、⑦、⑧、⑩引脚 时,数码管的其他段先后分别发光,据此便可绘出该数码管的内部结构和引脚排 列图。47 普中科技检测中,若被测数码管为共阳极类型,则需将红、黑表笔对调才能测出上述 结果, 在判别结构类型时, 操作时要灵活掌握, 反复试验, 直到找出公共端为止, 大家只要懂得了原理,检测出各个引脚便不在是问题了。 数码管静态显示 当多位数码管应用于某一系统时,它们的“位选”是可独立控制的,而“段 选”是连接在一起的,我们可以通过位选信号控制哪几个数码管亮,而在同一时 刻,位选选通的所有数码管上显示的数字始终都是一样的,因为它们的段选是连 接在一起的,所以送入所有数码管的段选信号都是相同的,那么它们显示的数字 必定一样,数码管的这种显示方法叫做静态显示。从电路图可以看出,本开发板使用的是共阴极数码管,在每段数码管端加上 一个限流电阻。 下面我们来看一下程序48 错误!未指定书签。/************************************************************************** * 实验名 : 静态数码管实验 * 使用的IO : 数码管使用P0,键盘使用P3.0、P3.1、P3.2、P3.3 * 实验效果 : 按下K1键,显示1,按下K2键,显示2,按下K3键,显示3,按下K4 键,显示4。 * 注意 :由于P3.2口跟红外线共用,所以做按键实验时为了 不让红外线影响实验 *效果,最好把红外线先取下来。 **************************************************************************/ #include&reg51.h& #include&intrins.h& #define GPIO_DIG P0 sbit LSA=P2^2; sbit LSB=P2^3; sbit LSC=P2^4; sbit sbit sbit sbit K1=P3^0; K2=P3^1; K3=P3^2; K4=P3^3;DIG_CODE[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};// 显 示 0~9 的值 void Delay10ms(); //延时10ms/************************************************************************** * 函数名 : main * 函数功能 : 主函数 * 输入 : 无 * 输出 : 无 **************************************************************************/ void main(void) { unsigned int i,j; LSA=0; LSB=0; LSC=0; while(1) { if(K1==0) //检测按键K1是否按下 {49 普中科技Delay10ms(); //消除抖动 if(K1==0) { j=1; } while((i&50)&&(K1==0)) //检测按键是否松开 { Delay10ms(); i++; } i=0; } if(K2==0) //检测按键K2是否按下 { Delay10ms(); if(K2==0) { j=2; } while((i&50)&&(K2==0)) { Delay10ms(); i++; } i=0; } if(K3==0) //检测按键K3是否按下 { Delay10ms(); if(K3==0) { j=3; } while((i&50)&&(K3==0)) { Delay10ms(); i++; } i=0; } if(K4==0) { Delay10ms(); //检测按键K4是否按下50 错误!未指定书签。if(K4==0) { j=4; } while((i&50)&&(K4==0)) { Delay10ms(); i++; } i=0; } GPIO_DIG=DIG_CODE[j]; } } /************************************************************************* * 函数名 : Delay10ms * 函数功能 : 延时函数,延时10ms * 输入 : 无 * 输出 : 无 **************************************************************************/ void Delay10ms(void) //误差 0us { unsigned char a,b,c; for(c=1;c&0;c--) for(b=38;b&0;b--) for(a=130;a&0;a--); }下载HEX文件,观察实验效果,实验的效果是:按下K1键,显示1,按下K2 键,显示2,按下K3键,显示3,按下K4键,显示4。第八讲 矩阵键盘独立键盘与单片机连接时, 每一个按键都需要单片机的一个 I/O 口若某单片 机系统需较多按键,如果用独立按键便会占用过多的 I/O 口资源。单片机系统中 I/O 口资源往往比较宝贵,当用到多个按键时为了节省 I/O 口口线,我们引入矩 阵键盘。 我们以 4X4 矩阵键盘为例讲解其工作原理和检测方法。将 16 个按键排成 451 普中科技 行 4 列,第一行将每个按键的一端连接在一起构成行线,第一列将每个按键的另 一端连接在一起构成列线,这样便一共有 4 行 4 列共 8 根线,我们将这 8 根线连 接到单片机的 8 个 I/O 口上,通过程序扫描键盘就可检测 16 个键。用这种方法 我们也可实现 3 行 3 列 9 个键、5 行 5 列 25 个键、6 行 6 列 36 个键等。 无论是独立键盘还是矩阵键盘,单片机检测其是否被按下的依据都是一样 的,也就是检测与该键对应的 I/O 口是否为低电平。独立键盘有一端固定为低电 平,单片机写程序检测时比较方便。而矩阵键盘两端都与单片机 I/O 口相连,因 此在检测时需人为通过单片机 I/O 口送出低电平。检测时,先送一列为低电平, 其余几列全为高电平(此时我们确定了列数), 然后立即轮流检测一次各行是否有 低电平,若检测到某一行为低电平(这时我们又确定了行数),则我们便可确认当 前被按下的键是哪一行哪一列的,用同样方法轮流送各列一次低电平,再轮流检 测一次各行是否变为低电平,这样即可检测完所有的按键,当有键被按下时便可 判断出按下的键是哪一个键。当然我们也可以将行线置低电平,扫描列是否有低 电平。这就是矩阵键盘检测的原理和方法。 首先看一下电路图87654 3 2 1上图是一个4X4 的矩阵键盘,一共是16 个按键。我们照习惯称横为“行”, “竖”为列。那么5、6、7、8 我们称之为“行线”,则1、2、3、4 称为“列线”。 要正确记住各个行列线各自对应的IO。注意看,每一个按键的两端,都分别接在 某一个列线和行线上,即: “行线和列线是通过某个按键的按下和抬起实现联通52 错误!未指定书签。 和断开的”,和“导线两端上的信号是经过“与”的关系再体现到导线上的。” 这两句话便构成了矩阵键盘扫描的全部。要理解好,理解不了就背下来。 现在详细讲述一下矩阵键盘扫描的原理和步骤: 扫描矩阵键盘,即是把某一条(只有一条)行线置为低电平,而列线全部置 为输入方向,然后检测列线,如果检测到某一条列线是低电平,那么就表示位于 这条列线与输出低电平的行线的交点处的按键被按下了。要扫描16个按键,就依 次以这样的方法扫描16次,之后就可以确定哪一个按键被按下了。当然这里也少 不了延时消除按键抖动的环节。 下面看一下程序/****************************************************************************** * 实验名 : 矩阵键盘实验 * 使用的IO : 数码管使用P0,键盘使用P3.0、P3.1、P3.2、P3.3 * 实验效果 : 按矩阵键盘分别显示在数码管上面显示十六进制的0到F。 * 注意 : ******************************************************************************/ #include&reg51.h& #define GPIO_DIG P0 #define GPIO_KEY P1 sbit LSA=P2^2; sbit LSB=P2^3; sbit LSC=P2^4; unsigned char code DIG_CODE[17]={ 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //0、1、2、3、4、5、6、7、8、9、A、b、C、d、E、F的显示码 unsigned char KeyV //用来存放读取到的键值 void Delay10ms(); //延时10ms void KeyDown(); //检测按键函数 /***************************************************************************** * 函数名 : main * 函数功能 : 主函数 * 输入 : 无 * 输出 : 无 ******************************************************************************/ void main(void)53 普中科技{ LSA=0; //给一个数码管提供位选 LSB=0; LSC=0; while(1) { KeyDown(); GPIO_DIG=DIG_CODE[KeyValue]; } } /****************************************************************************** * 函数名 : KeyDown * 函数功能 : 检测有按键按下并读取键值 * 输入 : 无 * 输出 : 无 ******************************************************************************/ void KeyDown(void) { GPIO_KEY=0x0f; if(GPIO_KEY!=0x0f) { Delay10ms(); if(GPIO_KEY!=0x0f) { //测试列 GPIO_KEY=0X0F; switch(GPIO_KEY) { case(0X07): KeyValue=0; case(0X0b): KeyValue=1; case(0X0d): KeyValue=2; case(0X0e): KeyValue=3; default: KeyValue=17; //检测出错回复17意思是把数码管全灭 } //测试行 GPIO_KEY=0XF0; switch(GPIO_KEY) { case(0X70): KeyValue=KeyV case(0Xb0): KeyValue=KeyValue+4; case(0Xd0): KeyValue=KeyValue+8;// 掉。54 错误!未指定书签。case(0Xe0): KeyValue=KeyValue+12; default: KeyValue=17; } while((a&50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测 { Delay10ms(); a++; } a=0; } } } /****************************************************************************** * 函数名 : Delay10ms * 函数功能 : 延时函数,延时10ms * 输入 : 无 * 输出 : 无 ******************************************************************************/ void Delay10ms(void) //误差 0us { unsigned char a,b,c; for(c=1;c&0;c--) for(b=38;b&0;b--) for(a=130;a&0;a--); }//下载HEX文件,观察实验效果,实验的效果是:按矩阵键盘分别显示在数码 管上面显示十六进制的0到F。55 普中科技第九讲 动态数码管1. 动态扫描的原理 在实际的单片机系统中,往往需要多位显示。动态显示是一种最常见的 多位显示方法,应用非常广泛。所有数码管段选都连接在一起的时候,怎么 让数码管显示不一样的数字呢?动态显示是多个数码管,交替显示,利用人 的视觉暂停作用使人看到多个数码管同时显示的效果。 首先我们来看一下开发板上的电路原理图:56 错误!未指定书签。 本开发板上使用的是,通过P22、P23、P24控制3-8译码器来对数码管进行位 选,通过P0口经过573的驱动控制数码管的段选,通过P13控制573的使能端,为 低电平时573才会有输出。 下面看一下程序:/************************************************************************** * 实验名 : 动态显示数码管实验 * 使用的IO : 数码管使用P0,P2.2,P2.3,P2.4键盘使用P1 * 实验效果 : 按矩阵键盘分别显示在数码管上面显示十六进制的0到F。 * 注意 : **************************************************************************/ #include&reg51.h& #define GPIO_DIG P0 #define GPIO_KEY P1 sbit LSA=P2^2; sbit LSB=P2^3; sbit LSC=P2^4; unsigned char code DIG_CODE[17]={ 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //0、1、2、3、4、5、6、7、8、9、A、b、C、d、E、F的显示码 unsigned char KeyV //用来存放读取到的键值 unsigned char KeyS //记录按键的状态,0没有,1有 unsigned char DisplayData[8]; //用来存放要显示的8位数的值 void Delay10ms(); //延时10ms void KeyDown(); //检测按键函数 void DigDisplay(); //动态显示函数 /************************************************************************** * 函数名 : main * 函数功能 : 主函数 * 输入 : 无 * 输出 : 无 **************************************************************************/ void main(void) { KeyState=0; while(1) {57 普中科技KeyDown(); if(KeyState==1) { DisplayData[7]=DisplayData[6]; DisplayData[6]=DisplayData[5]; DisplayData[5]=DisplayData[4]; DisplayData[4]=DisplayData[3]; DisplayData[3]=DisplayData[2]; DisplayData[2]=DisplayData[1]; DisplayData[1]=DisplayData[0]; DisplayData[0]=DIG_CODE[KeyValue]; KeyState=0; } DigDisplay();//显示 } } /************************************************************************** * 函数名 : DigDisplay * 函数功能 : 使用数码管显示 * 输入 : 无 * 输出 : 无 **************************************************************************/ void DigDisplay() { unsigned char i,j; for(i=0;i&8;i++) { switch(i) //位选,选择点亮的数码管, { case(0): LSA=0;LSB=0;LSC=0; case(1): LSA=1;LSB=0;LSC=0; case(2): LSA=0;LSB=1;LSC=0; case(3): LSA=1;LSB=1;LSC=0; case(4): LSA=0;LSB=0;LSC=1; case(5): LSA=1;LSB=0;LSC=1; case(6): LSA=0;LSB=1;LSC=1;58 错误!未指定书签。case(7): LSA=1;LSB=1;LSC=1; } GPIO_DIG=DisplayData[i]; j=10; while(j--); GPIO_DIG=0x00;//消隐 } } /************************************************************************** * 函数名 : KeyDown * 函数功能 : 检测有按键按下并读取键值 * 输入 : 无 * 输出 : 无 **************************************************************************/ void KeyDown(void) { unsigned int a=0; GPIO_KEY=0x0f; if(GPIO_KEY!=0x0f) { Delay10ms(); a++; a=0; if(GPIO_KEY!=0x0f) { KeyState=1;//有按键按下 //测试列 GPIO_KEY=0X0F; // Delay10ms(); switch(GPIO_KEY) { case(0X07): KeyValue=0; case(0X0b): KeyValue=1; case(0X0d): KeyValue=2; case(0X0e): KeyValue=3; // default: KeyValue=17; //检测出错回复17意思是把数码管 全灭掉。 } //测试行 GPIO_KEY=0XF0; Delay10ms(); switch(GPIO_KEY) {//扫描间隔时间设定59 普中科技case(0X70): case(0Xb0): case(0Xd0): case(0Xe0): default: KeyValue=KeyV KeyValue=KeyValue+4; KeyValue=KeyValue+8; KeyValue=KeyValue+12; KeyValue=17; //按键松手检测//} while((a&500)&&(GPIO_KEY!=0xf0)) { Delay10ms(); a++; } a=0; } }} /************************************************************************** * 函数名 : Delay10ms * 函数功能 : 延时函数,延时10ms * 输入 : 无 * 输出 : 无 **************************************************************************/ void Delay10ms(void) //误差 0us { unsigned char a,b,c; for(c=1;c&0;c--) for(b=38;b&0;b--) for(a=130;a&0;a--); }在用C语言编程时,编码定义方法如下: unsigned char code DIG_CODE[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d, 0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//0、1、2、3、4、5、 6、7、8、9、A、b、C、d、E、F的显示码 编码定义方法与C语言中的数组定义方法非常相似,不同的地方就是在数组 类型后面多了一个code关键字,code即表示编码的意思。需要注意的是,单片机 C语言中定义数组时是占用内存空间的, 而定义编码时是直接分配到程序空间中, 编译后编码占用的是程序存储空间,而非内存空间。 下载数码管动态显示.hex,观察实验结果,实验效果是:按矩阵键盘分别显 示在数码管上面显示十六进制的0到F。60 错误!未指定书签。第十讲 电机1.步进电机图片普通步进电机减速步进电机直线步进电机微型步进电机61 普中科技步进电机剖视图 2.步进电机介绍 步进电机是将电脉冲信号转变为角位移或线位移的开环控制元件。 在非超载 情况下,电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数,而不受负 载变化的影响,即给电机加一个脉冲信号,电机则转过一个步距角。这一线性关 系的存在,加上步进电机只有周期性的误差而无累积误差等特点,使得步进电机 在速度、位置等控制领域的控制操作非常简单。虽然步进电机应用广泛,但它并 不像普通的直流和交流电机那样在常规状态下使用,它必须由双环形脉冲信号、 功率驱动电路等组成控制系统方可使用.因此.用好步进电机也非易事, 它涉及机 械、电机、电子及计算机等许多专业知识。 3.步进电机分类 (1)永磁式(PM)。 一般为二相, 转矩和体积较小, 步距角一般为 7.5°或 15°。 (2)反应式(VR)。一般为三相,可实现大转矩输出.步距角一般为 1.5°,但 噪声和振动都很大。在欧美等发达国家 20 世纪 80 年代已经淘汰。 (3)混合式(HB)。指混合了永磁式和反应式的优点。它又分为二相和五相, 二相步距角一般为 180°,而五相步距角一般为 0.72°。这种步进电机的应用最 为广泛。 4.技术指标 (1)步进电机的静态指标62 错误!未指定书签。 ①相数―电机内部的线圈组数。目前常用的有二相、三相、四相、五相步进 电机。 电机相数不同, 其步距角也不同。 一般二相电机的步距角为 0.9°/1.8°, 三相为 0.75°/1.5°、五相为 0.36°/0.72°。在没有细分驱动器时,用户主要 靠选择不同相数的步进电机来满足自己步距角的要求.如果使用细分驱动器,则 “相数” 将变得没有意义, 用户只需在驱动器上改变细分数, 就可以改变步距角。 ②步距角―表示控制系统每发一个步进脉冲信号,电机所转动的角度。电机 出厂时给出了一个步距角的值,如 86BYG250A 型电机的值为 0.9°/1.8°(表示 半步工作时为 0.9°、整步工作时为 1.8°),这个步距角可称为“电机固有步距 角” ,它不一定是电机实际工作时的真正步距角,真正的步距角和驱动器有关。 ③拍数―完成一个磁场周期性变化所需脉冲数或导电状态.或指电机转过一 个步距角所需脉冲数。 以四相电机为例, 有四相四拍运行方式, AB-BC-CD-DA-AB: 即 四相八拍运行方式,即 A-AB-B-BC-C-CD-D-DA-A ④定位转矩―电机在不通电状态下,转子自身的锁定力矩(由磁场齿形的谐 波以及机械误差造成). ⑥保持转矩―步进电机通电但没有转动时,定子锁住转子的力矩。它是步进 电机最重要的参数之一,通常步进电机在低速时的力矩接近保持转矩。由于步进 电机的输出力矩随速度的增大而不断衰减,输出功率也随速度的增大而变化,所 以保持转矩就成了衡量步进电机最重要参数之一。比如,当人们说 2N?m 的步进 电机时,在没有特殊说明的情况下,是指保持转矩为 2N?m 的步进电机。 (2)步进电机的动态指标 ①步距角精度―步进电机每转过一个步距角的实际值与理论值的误差, 用百 分比表示:误差/步距角 X100%。 不同运行拍数其值不同, 四拍运行时应在 5%之内, 八拍运行时应在 15%以内。 ②失步―电机运转时运转的步数不等于理论上的步数,称为失步。 ③失调角―转子齿轴线偏移定子齿轴线的角度.电机运转必存在失调角,由 失调角产生的误差,采用细分驱动是不能解决的。最大空载起动频率―电机在某 种驱动形式、电压及额定电流下,在不加负载的情况下,能够直接起动的最大频 率。最大空载运行频率―电机在某种驱动形式、电压及额定电流下,电机不带负 载的最高转速频率。运行矩频特性―电机在某种测试条件下,测得运行中输出力63 普中科技 矩与频率关系的曲线称为运行矩频特性。它是电机诸多动态曲线中最重要的,也 是电机选择的根本依据,如图所示。电机一但选定,电机的静力矩确定,而动态 力矩却不然.电机的动态力矩取决于电机运行时的平均电流(而非静态电流),平 均电流越大,电机输出力矩越大.即电机的频率特性越硬。力矩与频率关系曲线 其中.曲线 3 电流最大或电压最高;曲线 I 电流最小或电压最低, 曲线与负载的交 点为负载的最大速度点。要使平均电流大,尽可能提高驱动电压,或采用小电感 大电流的电机。 ④电机的共振点―步进电机均有固定的共振区域,其共振区一般在 50r/min-80r/min 或在 180r/min 左右。电机驱动电压越高,电机电流越大,负 载越轻,电机体积越小。则共振区向上偏移,反之亦然。为使电机输出电矩大、 不失步且整个系统的噪声降低,一般工作点均应偏移共振区较多。因此,在使用 步进电机时应避开此共振区。 5.步进电机工作原理 步进电机是一种将电脉冲转换成相应角位移或线位移的电磁机械装置。 它具 有快速启、停能力,在电机的负荷不超过它能提供的动态转矩时,可以通过输入 脉冲来控制它在一瞬间的启动或停止。 步进电机的步距角和转速只和输入的脉冲 频率有关,和环境温度、气压、振动无关,也不受电网电压的波动和负载变化的 影响。因此,步进电机多应用在需要精确定位的场合。 (1)工作原理 步进电机有三线式、五线式和六线式,但其控制方式均相同,都要以脉冲信 号电流来驱动.假设每旋转一圈需要 200 个脉冲信号来励磁,可以计算出每个励 磁信号能使步进电机前进 1.8°。其旋转角度与脉冲的个数成正比。步进电动机64 错误!未指定书签。 的正、反转由励磁脉冲产生的顺序来控制。六线式四相步进电机是比较常见的, 它的控制等效电路如下图所示。它有 4 条励磁信号引线 A,/A,B,/B 通过控制这 4 条引线上励磁脉冲产生的时刻,即可控制步进电机的转动.每出现一个脉冲信 号,步进电机只走一步。因此,只要依序不断送出脉冲信号,步进电机就能实现 连续转动。(2)励磁方式 步进电机的励磁方式分为全步励磁和半步励磁两种。 其中全步励磁又有一相 励磁和二相励磁之分;半步励磁又称一二相励磁。假设每旋转一圈需要 200 个脉 冲信号来励磁,可以计算出每个励磁信号能使步进电动机前进 1.8°。简要介绍 如下。 ①一相励磁―在每一瞬间, 步进电机只有一个线圈导通.每送一个励磁信号, 步进电机旋转 1.8,这是三种励磁方式中最简单的一种。 其特点是:精确度好、消耗电力小,但输出转矩最小,振动较大。如果以该 方式控制步进电机正转,对应的励磁顺序如下表所示。若励磁信号反向传送,则 步进电机反转。表中的 1 和 0 表示送给电机的高电平和低电平。②二相励磁―在每一瞬间,步进电动机有两个线圈同时导通。每送一个励磁 信号,步进电机旋转 1.8。 其特点是:输出转矩大,振动小,因而成为目前使用最多的励磁方式。如果 以该方式控制步进电机正转,对应的励磁顺序见下表。若励磁信号反向传送,则65 普中科技 步进电机反转。③一二相励磁―为一相励磁与二相励磁交替导通的方式。 每送一个励磁信 号,步进电机旋转 0.9。 其特点是:分辨率高,运转平滑,故应用也很广泛。如果以该方式控制步进 电机正转,对应的励磁顺序见下表。若励磁信号反向传送,则步进电机反转。6.步进电机的驱动 步进电机的驱动可以选用专用的电机驱动模块,如 L298, FF5754 等,这类 驱动模块接口简单,操作方便,它们既可驱动步进电机,也可驱动直流电机。除 此之外,还可利用三极管自己搭建驭动电路。不过这样会非常麻烦,可靠性也会 降低。另外,还有一种方法就是使用达林顿驱动器 ULN2003,该芯片单片最多可 一次驱动八线步进电机,当然如果只有四线或六线制的也是没有问题的。 首先我们看一下电路原理图66 错误!未指定书签。 本实验采用五线制四相步进电机。该电机与 ULN2003 连接如上图所示,P3 为电机五线接口。 下面我们来看程序:/****************************************************************************** * 实验名 : 步进电机实验 * 使用的 IO : 电机用 P1 口,键盘使用 P3.0、P3.1、P3.2、P3.3 * 实验效果 : 按下 K1 键,顺时针转,按下 K2 键,逆时针转,按下 K3 键,低速, *按下 K4 键,高速。 * 注意 : 由于 P3.2 口跟红外线共用, 所以做按键实验时为了不让 红外线影响实验 *效果,最好把红外线先取下来。 ******************************************************************************/ #include &reg52.h& //电机 IO #define GPIO_MOTOR P1 //sbit F1 = P1^0; //sbit F2 = P1^1; //sbit F3 = P1^2; //sbit F4 = P1^3; //按键 IO sbit K1=P3^0; sbit K2=P3^1; sbit K3=P3^2; sbit K4=P3^3; unsigned char code FFW[8]={0xf1,0xf3,0xf2,0xf6,0xf4,0xfc,0xf8,0xf9}; //反转顺序 unsigned char code FFZ[8]={0xf9,0xf8,0xfc,0xf4,0xf6,0xf2,0xf3,0xf1}; //正转顺序 unsigned char Direction,S void Delay(unsigned int t); void Motor(); /****************************************************************************** * 函数名 : main * 函数功能 : 主函数 * 输入 : 无 * 输出 : 无 ******************************************************************************/ void main(void) {67 普中科技Speed=30; while(1) { if(K1==0) //检测按键 K1 是否按下 { Delay(1); //消除抖动 if(K1==0) { Direction=1; } while((i&200)&&(K1==0)) //检测按键是否松开 { Delay(1); i++; } i=0; } if(K2==0) //检测按键 K1 是否按下 { Delay(1); //消除抖动 if(K2==0) { Direction=2; } while((i&200)&&(K2==0)) //检测按键是否松开 { Delay(1); i++; } i=0; } if(K3==0) //检测按键 K1 是否按下 { Delay(1); //消除抖动 if(K3==0) { Speed=13; } while((i&200)&&(K3==0)) //检测按键是否松开 { Delay(1); i++; } i=0;68 错误!未指定书签。} if(K4==0) //检测按键 K1 是否按下 { Delay(1); //消除抖动 if(K4==0) { Speed=40; } while((i&200)&&(K4==0)) //检测按键是否松开 { Delay(1); i++; } i=0; } Motor(); } } /****************************************************************************** * 函数名 : Motor * 函数功能 : 电机旋转函数 * 输入 : 无 * 输出 : 无 ******************************************************************************/ void Motor() { for(i=0;i&8;i++) { if(Direction==1) GPIO_MOTOR = FFW[i]&0x1f; //取数据 if(Direction==2) GPIO_MOTOR = FFZ[i]&0x1f; Delay(Speed); //调节转速 } } /****************************************************************************** * 函数名 : Delay * 函数功能 : 延时 * 输入 : t * 输出 : 无 ******************************************************************************/ void Delay(unsigned int t)69 普中科技{ while(t--) { for(k=0; k&80; k++) { } } }然后下载 HEX 文件,观察实验结果。实验效果是:按下 K1 键,顺时针转, 按下 K2 键,逆时针转,按下 K3 键,低速,按下 K4 键,高速。第十一讲 中断中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的, 中 断功能的存在,很大程度上提高了单片机处理外部或内部事件的能力。它也是单 片机最重要的功能之一,是我们学习单片机必须要掌握的。很多初学者被困在中 断中, 学了很久仍然不知道中断究竟是个什么东西, 大家千万不要认为它有多难,70 错误!未指定书签。 其实只要掌握正确的学习方法,没有哪个知识点是学不会的。 为了能让大家更容易理解中断概念,我们先来举一个生活事例:你打开火, 烧上一壶水。然后去洗衣服,在洗衣服的过程中,突然听到水壶发出水开的报警 声,这时,你停止洗衣服动作,立即去关掉火,然后将开水灌入暖水瓶中,灌完 开水后,你又回去继续洗衣服。这个过程中实际上就发生了一次中断。 对于单片机来讲,中断是指 CPU 在处理某一事件 A 时,发生了另一事件 B, 请求 CPU 迅速去处理(中断发生);CPU 暂时停止当前的工作(中断响应),转去处 理事件 B(中断服务);待 CPU 将事件 I.,处理完毕后,再回到原来事件 A 被中断 的地方继续处理事件 A(中断返回),这一过程称为中断。生活中的实例单片机处理中断的过程再回来看前面讲的生活事例,与单片机中断结合分析,你的主任务是洗衣 服,水开报警这是一个中断请求,这一时刻相当于断点处,你响应中断去关火, 然后将开水灌入暖水瓶中,这一动作实际上就是处理中断程序,灌完开水后再回 去继续洗衣服,相当于处理完中断程序后再返回主程序继续执行主程序。这里需 要注意的是,水开是随时都有可能的,但是无论什么时候开,只要一开你将立即 去处理它,处理完后再回来继续接着洗刚才那件衣服。单片机在执行程序时,中 断也随时有可能发生,但无论何时发生,只要一旦发生,单片机将立即暂停当前 程序,赶去处理中断程序,处理完中断程序后再返回刚才暂停处接着执行原来的 程序。单片机在执行程序时其程序流程图。71 普中科技引起 CPU 中断的根源,称为中断源,中断源向 CPU 提出中断请求,CPU 暂时 中断原来的事务 A,转去处理事件 B,对事件 B 处理完毕后,再回到原来被中断 的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断 机构)。 中断的开启与关闭、 设置启用哪一个中断等都是由单片机内部的一些特殊功 能寄存器来决定的,在以前的学习中我们仅对单片机内部的特殊功能寄存器 IO 口寄存器设置过,从下节起我们将会设置单片机内部更多的特殊功能寄存器。 当中央处理机 CPU 正在处理某件事的时候外界发生了紧急事件请求,要求 CPU 暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中 断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中 断系统,请示 CPU 中断的请求源称为中断源。微型机的中断系统一般允许多个中 断源,当儿个中断源同时向 CPU 请求中断,要求为它服务的时候,这就存在 CPU 优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理 最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU 总是先响 应优先级别最高的中断请求。 当 CPU 正在处理一个中断源请求的时候(执行相应的中断服务程序), 发生了 另外一个优先级比它还高的中断源请求。 如果 CPU 能够暂停对原来中断源的服务 程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服 务程序,这样的过程称为中断嵌套。这样的中断系统称为多级中断系统,没有中 断嵌套功能的中断系统称为单级中断系统。 STC90C51RC/RD+系列单片机提供了 8 个中断请求源,它们分别是:外部中断 O(INTO),定时器。中断、外部中断 1(INT1)、定时器 1 中断、定时器 2 中断、串72 错误!未指定书签。 口(UART)中断、外部中断 2(INT2)、外部中断 3(INT3)。所有的中断都具有 4 个 中断优先级。用户可以用关总中断允许位(EA/IE.7)或相应中断的允许位来屏蔽 所有的中断请求,也可以用打开相应的中断允许位来使 CPU 响应相应的中断申 请;、 每二个中断源可以用软件独立地控制为开中断或关中断状态;每一个中断的 优先级别均可用软件设置。 高优先级的中断请求可以打断低优先级的中断, 反之, 低优先级的中断请求不可以打断高优先级及同优先级的中断。 当两个相同优先级 的中断同时产生时,将由查询次序来决定系统先响应哪个中断。STC90C51RC/RD+ 系列单片机的各个中断查询次序表如下图所示。通过设置新增加的特殊功能寄存器 IPH 中的相应位, 可将中断优先级设为四 级,如果只设置 IP 或 XICON,那么中断优先级就只有两级,与传统 8051 单片机 两级中断优先级完全兼容。 如果使用 C 语言编程, ,中断查询次序号就是中断号,例如: void void void void void void void void Into_Routine(void) TimerO_Rountine(void) Intl_Routine(void) Timer1_Rountine(void) UART_Routine(void) Timer2_Routine(void) Int2_Routine(void) Int3_Routine(void) interrupt 0; interrupt 1; interrupt 2; interrupt 3; interrupt 4; interrupt 5; interrupt 6; interrupt 7;中断触发表如下图所示73 普中科技下面看一个程序:/****************************************************************************** *实验名 :外部中断实验 *注意 : 由于P3.2口跟红外线共用, 所以做按键实验时为了不让红外线影响实验 效果,最好把红外线先 *取下来。 *实验效果 :K3按下LED左循环,K4按下LED变右循环 ******************************************************************************/ #include&reg51.h& #include&intrins.h& #define GPIO_LED //外部中断的IO sbit K3=P3^2; sbit K4=P3^3; P2void IntConfiguration(); void Delay(unsigned int n); unsigned char KeyValue=0; /****************************************************************************** * 函数名 : main * 函数功能 : 主函数 * 输入 : 无 * 输出 : 无 ******************************************************************************/ void main(void) { GPIO_LED=0X01; IntConfiguration();74 错误!未指定书签。while(1) { if(KeyValue) GPIO_LED=_crol_(GPIO_LED,1); else GPIO_LED=_cror_(GPIO_LED,1); Delay(2000); } } /****************************************************************************** * 函数名 : IntConfiguration() * 函数功能 : 设置外部中断 * 输入 : 无 * 输出 : 无 ******************************************************************************/ void IntConfiguration() { //设置INT0 IT0=1;//跳变沿出发方式(下降沿) EX0=1;//打开INT0的中断允许。 //设置INT1 IT1=1; EX1=1; EA=1;//打开总中断 } /****************************************************************************** * 函数名 : Delay(unsigned int n) * 函数功能 : 延时 * 输入 : n * 输出 : 无 ******************************************************************************/ void Delay(unsigned int n) //延时50us误差 0us { unsigned char a,b; for(;n&0;n--) { for(b=1;b&0;b--) for(a=22;a&0;a--); } } /****************************************************************************** * 函数名 : Int0() interrupt 075 普中科技* 函数功能 : 外部中断0的中断函数 * 输入 : 无 * 输出 : 无 ******************************************************************************/ void Int0() interrupt 0 //外部中断0的中断函数 { Delay(1); if(K3==0) KeyValue=1; } /****************************************************************************** * 函数名 : Int1() interrupt 2 * 函数功能 : 外部中断1的中断函数 * 输入 : 无 * 输出 : 无 ******************************************************************************/ void Int1() interrupt 2 { Delay(1); if(K4==0) KeyValue=0; } //外部中断1的中断函数下载HEX文件,观察实验效果。实验效果是,K3按下LED左循环,K4按下LED 变右循环76 错误!未指定书签。第十二讲 1602 液晶显示液晶(Liquid Crystal)是一种高分子材料,因为其特殊的物理、化学、光学 特性,20 世纪中叶开始广泛应用在轻薄型显示器上。 液晶显示器(Liquid Crystal Display, LCD)的主要原理是以电流刺激液晶 分子产生点、线、面并配合背部灯管构成画面。为叙述简便,通常把各种液晶显 示器都直接叫做液晶。 各种型号的液晶通常是按照显示字符的行数或液晶点阵的行、列数来命名 的。比如:1602 的意思是每行显示 16 个字符,一共可以显示两行;类似的命名还 有 , 1601 等,这类液晶通常都是字符型液晶,即只能显示 ASCII 码 字符,如数字、大小写字母、各种符号等。12232 液晶属于图形型液晶,她的意 思是液晶由 122 列、32 行组成,即共有 122 X 32 个点来显示各种图形,我们可 以通过程序控制这 122 X 32 个点中的任一个点显示或不显示。类似的命名还有 1, 0240 等,根据客户需要,厂家可以设计出任意数组 合的点阵液晶。 液晶显示的使用有多广泛我就不多说了,LCD1602 好像10 元左右就可以拿 到了的,不算贵。我们来看看现在市面都有哪些样子的1602,下面从网上搜罗了 几个:蓝白屏黄绿屏其实显而易,见也就是背光和字体的颜色不一样罢,不过老实说,蓝色背光 的1602 看上去显得确实比较亮,也许是人眼视觉的关系。 接下来进入LCD1602 使用的重点:操作时序。操作时序永远使用是任何一片 IC 芯片的最主要的内容。一个芯片的所有使用细节都会在它的官方器件手册上 包含。所以使用一个器件事情,要充分做好的第一件事就是要把它的器件手册上77 普中科技 有用的内容提取,掌握。介于中国目前的芯片设计能力有限,所以大部分的器件 都是外国几个IC 巨头比如TI、AT、MAXIM 这些公司生产的,器件资料自然也是 英文的多,所以,英文的基础要在阅读这些数据手册时得到提高哦。即便有中文 翻译版本,还是建议看英文原版,看不懂时不妨再参考中文版,这样比较利于提 高。 我们首先来看1602 的引脚定义,1602 的引脚是很整齐的SIP 单列直插封 装,所以器件手册只给出了引脚的功能数据表:我们只需要关注以下几个管脚: 3 脚:VL,液晶显示偏压信号,用于调整LCD1602 的显示对比度,一般会外 接电位器用以调整偏压信号,注意此脚电压为0 时可以得到最强的对比度。 4 脚:RS,数据/命令选择端,当此脚为高电平时,可以对1602 进行数据字 节的传输操作,而为电平时,则是进行命令字节的传输操作。命令字节,即是用 来对LCD1602 的一些工作方式作设置的字节;数据字节,即使用以在1602 上显 示的字节。值得一提的是,LCD1602的数据是8 位的。 脚:R/W,读写选择端。当此脚为高电平可对LCD1602 进行读数据操作,反 之进行写数据操作。笔者认为,此脚其实用处不大,直接接地永久置为低电平也 不会影响其正常工作。但是尚未经过复杂系统验证,保留此意见。 6 脚:E,使能信号,其实是LCD1602 的数据控制时钟信号,利用该信号的 上升沿实现对LCD1602 的数据传输。 7~14 脚:8 位并行数据口,使得对LCD1602 的数据读写大为方便。78 错误!未指定书签。现在来看LCD1602 的操作时序:在此,我们不需要读出它的数据的状态或者数据本身。所以只需要看两个写 时序: ① 当我们要写指令字,设置LCD1602 的工作方式时:需要把RS置为低电平, RW置为低电平, 然后将数据送到数据口D0~D7, 最后E引脚一个高脉冲将数据写入。 ② 当我们要写入数据字,在1602 上实现显示时:需要把RS置为高电平,RW 置为低电平,然后将数据送到数据口D0~D7,最后E 引脚一个高脉冲将数据写入。 发现了么, 写指令和写数据, 差别仅仅在于RS的电平不一样而已。 一下是LCD1602 的时序图:大家要慢慢学会看时序图,要知道操作一个器件的精华便蕴藏在其中,看懂 看准了时序,你操控这个芯片就是非常容易的事了。1602 的时序是我见过的一 个最简单的时序:79 普中科技 1. 注意时间轴,如果没有标明(其实大部分也都是不标明的),那么从左往右 的方向为时间正向轴,即时间在增长。 2. 上图框出并注明了看懂此图的一些常识: 1) 时序图最左边一般是某一根引脚的标识,表示此行图线体现该引脚的变 化,上图分别标明了RS、R/W、E、DB0~DB7 四类引脚的时序变化。 2) 有线交叉状的部分,表示电平在变化,如上所标注。 3) 应该比较容易理解,如上图右上角所示,两条平行线分别对应高低电平, 也正好吻合(2)中电平变化的说法。 4) 上图下,密封的菱形部分,注意要密封,表示数据有效,Valid Data 这 个词也显示了这点。 3. 需要十分严重注意的是,时序图里各个引脚的电平变化,基于的时间轴是一 致的。一定要严格按照时间轴的增长方向来精确地观察时序图。要让器件严 格的遵守时序图的变化。在类似于18B20 这样的单总线器件对此要求尤为严 格。 4. 以上几点,并不是LCD1602 的时序图所特有的,绝大部分的时序图都遵循着 这样的一般规则,所以大家要慢慢的习惯于这样的规则。 也许你还注意到了上面有许多关于时间的标注,这也是个十分重要的信息, 这些时间的标注表明了某些状态所要维持的最短或最长时间。 因为器件的工作速 度也是有限的, 一般都跟不上主控芯片的速度, 所以它们直接之间要有时序配合。 话说现在各种处理器的主频也是疯狂增长, 日后搞不好出现个双核单片机也不一 定就是梦话。下面是时序参数表:大家要懂得估计主控芯片的指令时间,可以在官方数据手册上查到MCU的一 些级别参数。比如我们现在主控芯片,外部12MHz 晶振,指令周期就是一个时钟80 错误!未指定书签。 周期为(1/12MHz)us,所以至少确定了它执行一条指令的时间是us级别的。我 们看到,以上给的时间参数全部是ns级别的,所以即便我们在程序里不加延时程 序,也应该可以很好的配合LCD1602的时序要求了。怎么看这个表呢?很简单, 我们在时序图里可以找到TR1,对应时序参数表,可以查到这个是E上升沿/下降 沿时间,最大值为25ns,表示E引脚上的电平变化,必须在最大为25ns之内的时 间完成。大家看是不是这个意思? 现在我来解读我对这个时序图的理解: 当要写命令字节的时候,时间由左往右,RS 变为低电平,R/W 变为低电平, 注意看是RS 的状态先变化完成。然后这时,DB0~DB7 上数据进入有效阶段,接 着E引脚有一个整脉冲的跳变, 接着要维持时间最小值为tpw=400ns的E脉冲宽度。 然后E引脚负跳变,RS电平变化,R/W 电平变化。这样便是一个完整的LCD1602 写命令的时序。 我们看一下开发板的电路图下面看一下程序:/************************************************************************** * 实验名 : 矩阵键盘实验 * 使用的IO : 数码管使用P0,键盘使用P3.0、P3.1、P3.2、P3.381 普中科技* 实验效果 : 按矩阵键盘分别显示在数码管上面显示十六进制的0到F。 * 注意 : **************************************************************************/ #include&reg51.h& #define GPIO_LCD P0 #define GPIO_KEY P1 sbit LCDE=P2^7; sbit RW=P2^5; sbit RS=P2^6; unsigned char KeyV //用来存放读取到的键值 unsigned char KeyS //用来存放按键状态 unsigned char PuZh[]=& Pechin Science &; void Delay10ms(); //延时50us void KeyDown(); //检测按键函数 void LcdWriteCom(unsigned char); void LcdWriteData(unsigned char); void LcdInit(); void Delay1ms(unsigned int); /************************************************************************** * 函数名 : main * 函数功能 : 主函数 * 输入 : 无 * 输出 : 无 **************************************************************************/ void main(void) { LcdInit(); KeyState=0; for(i=0;i&16;i++) { // LcdWriteCom(0x80); LcdWriteData(PuZh[i]); } while(1) { KeyDown(); if(KeyState)82 错误!未指定书签。{ KeyState=0; LcdWriteCom(0x80+0x40); LcdWriteData('0'+KeyValue); } } } /************************************************************************** * 函数名 : KeyDown * 函数功能 : 检测有按键按下并读取键值 * 输入 : 无 * 输出 : 无 **************************************************************************/ void KeyDown(void) { GPIO_KEY=0x0f; if(GPIO_KEY!=0x0f) { Delay10ms(); if(GPIO_KEY!=0x0f) { KeyState=1; //测试列 GPIO_KEY=0X0F; // Delay10ms(); switch(GPIO_KEY) { case(0X07): KeyValue=0; case(0X0b): KeyValue=1; case(0X0d): KeyValue=2; case(0X0e): KeyValue=3; // default: KeyValue=17; //检测出错回复17意思是把数码管 全灭掉。 } //测试行 GPIO_KEY=0XF0; Delay10ms(); switch(GPIO_KEY) { case(0X70): KeyValue=KeyV case(0Xb0): KeyValue=KeyValue+4; case(0Xd0): KeyValue=KeyValue+8; case(0Xe0): KeyValue=KeyValue+12;83 普中科技// default: KeyValue=17;} while((a&50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测 { Delay10ms(); a++; } a=0; } } } /************************************************************************* * 函数名 : Delay10ms * 函数功能 : 延时函数,延时10ms * 输入 : 无 * 输出 : 无 **************************************************************************/ void Delay10ms(void) //误差 0us { unsigned char a,b,c; for(c=1;c&0;c--) for(b=38;b&0;b--) for(a=130;a&0;a--); } /************************************************************************** * 函数名 : Delay1ms * 函数功能 : 延时函数,延时1us * 输入 : a * 输出 : 无 **************************************************************************/ void Delay1ms(unsigned int a) { unsigned int b,c; for(c=a;c&0;c--) for(b=110;b&0;b--); } /************************************************************************** * 函数名 : LcdWriteCom * 函数功能 : 向LCD写入一个字节的命令 * 输入 : com * 输出 : 无 **************************************************************************/84 错误!未指定书签。void LcdWriteCom(unsigned char com) //写入命令 { RS=0; RW=0; GPIO_LCD= Delay1ms(10); LCDE=1; Delay1ms(10); LCDE=0; } /************************************************************************** * 函数名 : LcdWriteData * 函数功能 : 向LCD写入一个字节的数据 * 输入 : dat * 输出 : 无 **************************************************************************/ void LcdWriteData(unsigned char dat) //写入数据 { RS=1; RW=0; GPIO_LCD= Delay1ms(10); LCDE=1; Delay1ms(10); LCDE=0; } /************************************************************************** * 函数名 : LcdInit() * 函数功能 : 初始化LCD屏 * 输入 : 无 * 输出 : 无 **************************************************************************/ vo

我要回帖

更多关于 单片机c语言延时程序 的文章

 

随机推荐