第二单元 单片机系统的显示界面
综合教学目标
学习单片机编程常用软件的操作;掌握单片机系统中常用显示模块的程序设计。
主要内容
项目2.1流水灯、项目2.2电子秒表、项目2.3电子钟、项目2.4两级菜单的显示界面。分别介绍了发光二极管、数码管、字符型液晶显示器RTC1602、点阵型液晶显示器TG12864。
岗位技能综合职业素质要求:学会操作相关软件,处理烧录芯片时的问题。
单片机系统的显示界面主要用于显示系统的参数设置界面、输出结果界面、系统运行中的报警界面等。友好的人机界面将使产品大受欢迎。
本教材中选用“YL-236型单片机控制功能实训考核装置”作为项目实训平台。
YL-236装置将单片机系统中的常用显示模块集中在MCU04显示模块上,其照片如图2-1所示。其中包含有:8个发光二极管LED0~LED7(左下)、8个数码管显示器(右下)、1个RTC1602液晶显示器(左中)、1个TG12864液晶显示器(左上)、点阵显示器(右上)。
图2-1 YL-236装置中的MCU04显示模块
项目实训中可以根据需要选择其中某个或多个显示器来使用。
项目2.1 流水灯
2.1.1 项目描述
本项目要求采用显示模块MCU04中的发光二极管(LED),完成下述任务。
任务2-1-1:点亮一个发光二极管。
① 了解发光二极管工作原理。
② 点亮第一个发光二极管。
③ 掌握51单片机仿真器的使用,以及MedWin软件操作。
任务2-1-2:实现二极管闪烁。
① 了解ms级延时C51函数。
② 用单片机控制发光二极管闪烁。
③ 掌握双龙下载器的使用。
任务2-1-3:实现流水灯。
2.1.2 项目分析
通过项目描述,实现本项目需完成以下两方面工作。
①硬件电路的设计:以单片机为控制中心,通过其I/O口与MCU04显示模块中的发光二极管连接,构成单片机控制LED电路。
② 程序的设计:用C51语言编写单片机控制LED程序。
2.1.3 任务2-1-1 点亮一个发光二极管
一、发光二极管的工作原理
在YL-236装置的MCU04显示模块中,发光二极管部分的照片如图2-2(a)所示。
LED显示电路工作原理分析:如图2-2(b)所示,当LED0端口为低电平时,发光二极管D1正向导通,有电流通过D1,D1发光;当LED0端口为高电平时,发光二极管D1无法导通,D1熄灭。将单片机某I/O口与LED0端口相连,通过软件控制该I/O输出高或低电平就可以使D1灭或亮。图2-2(b)中,电阻R1起限流作用,调整R1阻值大小就可以调节LED的亮度。
图2-2 MCU04显示模块中的发光二极管照片与电路原理图
二、任务2-1-1的实施
1.硬件电路的设计
本任务需要使用YL-236装置中的三个模块:MCU01主机模块、MCU02电源模块、MCU04显示模块。模块接线图如图2-3所示,其中主机模块的P0.0~P0.7用排线接到显示模块的LED0~LED7;主机模块、显示模块的+5V电源端子接到电源模块的+5V端口;主机模块、显示模块的GND端子接到电源模块的GND端口。
图2-3 单片机控制LED显示的模块接线图
2.点亮LED0的程序设计
(1)单片机P0.0的输出电平与LED0状态关系表
由LED工作原理可知,单片机P0.0的输出电平与LED0状态关系见表2-1。
表2-1 P0.0的输出电平与LED0状态关系表
(2)用C51语言指令控制单片机I/O口输出
在C51语言中,使单片机P0.0输出低电平,只需要写语句“P0^0=0;”;使单片机P0.0输出高电平,只需要写语句“P0^0=1;”。
任务2-1-1的程序清单
#include<at89x52.h> //头文件包含 sbit LED0=P0^0; //定义符号LED0为单片机的P0.0引脚 void main() { LED0=0; //P0.0输出低电平,灯亮;P0.0输出高电平,灯熄灭 while(1); //死循环 }
【知识链接一】C程序的基础知识
1.C程序的基本结构
(1)C程序是由函数构成的。一个C源程序至少且仅包含一个main函数,也可以包含一个main函数和若干其他函数。函数体的内容由一对{}括起来,{}必须成对出现。
(2)main为“主函数”,一个C程序总是从main函数开始执行,而且不论main函数在整个程序中的位置如何。
(3)C程序书写格式自由,一行内可以写几条语句,一条语句可以分写在多行上。
(4)每条语句和数据声明的最后必须有一个分号,分号是C语句的必要组成部分,不可缺少。既使程序中最后一条语句也应包含分号。
2.文件包含
“文件包含”是指一个文件将另外一个文件的内容全部包含进来。其格式为:
① #include<文件名称>
② #include"文件名称"
两者区别在于""和< >。< >表示头文件在编译器(KeilC51)的安装目录下,一般都是编译器自带的头文件;""表示头文件在当前工程的目录下,一般都是自己写的头文件,编译器将首先查找当前目录,如果没找到,则在由菜单选择项所确定的目录中查找。
任务2-1-1的程序中,文件at89x52.h在路径“C:\Keil\C51\INC\Atmel”中,是编译器自带的头文件,它主要定义了ATMEL公司的52单片机内部相关资源名称,以方便使用。
3.KeilC51单片机集成开发环境
KeilC51是目前使用最广泛的基于51单片机内核的开发平台之一,其编译器性能较好。
4.使用KeilC的关键字sbit来定义位变量
第一种方法:sbit位变量名=位地址值
第二种方法:sbit位变量名=字节名称^序号
第三种方法:sbit位变量名=字节地址值^序号
例如:sbitOV=PSW^2。
三、单片机仿真器及其软件的使用
YL-236装置中配备的仿真器为南京万利公司的“insight”51单片机仿真器。YL-236装置的主机模块与仿真器模块的照片如图2-4所示。先将主机模块上的AT89S52芯片取下,将仿真头的引脚插在主机模块的卡座上并卡紧,注意仿真头上的弧形标识对准卡座的上方,方向不能插反;然后按照图2-3连接好系统接线,并接通仿真器的电源与USB线等。
图2-4 主机模块与仿真器模块照片
“insight”51单片机仿真器的配套软件为MedWin3.0,它是一个国产的中文界面单片机集成开发平台,比KeilC51更好操作,但其自身不带C51编译器,需要调用KeilC编译器。操作步骤如下。
(1)启动MedWin3.0,创建项目,命名后保存。
启动MedWin3.0后,在菜单“项目管理”中选择“新建项目”,如图2-5所示。
图2-5 新建项目
弹出对话框如图2-6所示。点击其中“设备驱动程序名”后的下拉按钮,出现若干选项:第一项“InsightME-52HUFamilyEmulator”为上述万利仿真器,使用仿真器时应选此项;第二项“80C51SimlatorDriver”为软件仿真,若不连接仿真器,只是使用MedWin编译源程序产生机器码,可以选此项。这里选中第一项,点击“下一步”按钮。
图2-6 选择驱动程序
在第2步的对话框中,选择如图2-7所示编译工具,然后点击“下一步”按钮。
图2-7 选择编译工具
然后,在图2-8所示对话框中输入项目名称led0,然后点击“下一步”按钮。其存储位置一般默认在计算机最后一个盘符根目录的“WorkDir”文件夹下。也可点击“浏览”按钮自行设定存储路径。
图2-8 输入项目名称
然后,在图2-9所示对话框中,设置有关参数。点击“片内RAM长度”选项后的下拉按钮,选择“256”,可以充分使用AT89S52的256字节RAM空间;若单片机为AT89S51,则选择“128”。
图2-9 设置有关参数
点击“操作系统内核”选项后的下拉按钮,出现3个选项:① None(无操作系统);② RTX_51Tiny(RTX_51操作系统精简版);③ PTX_51Full(RTX_51操作系统完整版)。本项目没有采用操作系统,因此选①。
在图2-9中,点击“完成”按钮后,项目led0就创建完毕,如图2-10所示。
图2-10 新建项目完成
(2)创建源程序文件并命名保存。
选中菜单“文件/新建”,创建源程序文件,如图2-11所示。
图2-11 创建源程序文件
选中菜单“文件/另存为”,保存为C语言程序文件格式,如图2-12所示。
图2-12 另存为C语言程序文件格式
(3)导入/添加C文件到项目中。
用鼠标右键单击源文件组,在弹出的菜单中选择“导入/添加文件”,如图2-13所示。
图2-13 导入C文件(一)
找到之前保存的C文件,并添加到项目工程的源文件组中去,如图2-14所示。
导入源程序文件后,源文件组将出现该文件,如图2-15所示。
(4)在刚导入的C文件中编写C51程序。
在程序编辑区输入C51源程序,如图2-16所示。
图2-14 导入C文件(二)
图2-15 文件导入完成
图2-16 输入C51程序
(5)编译C51程序。
由于MedWin3.0自身不带编译器,因此选中菜单“设置/设置编译工具”,如图2-17所示。
图2-17 设置编译工具(一)
在如图2-18所示的对话框中,设置编译工具及相关文件路径后,点击“确定”按钮。
图2-18 设置编译工具(二)
选中菜单“项目管理/重新产生代码并装入”,如图2-19所示。
用软件编译C51源程序,如果编译链接无误,将产生机器码,并装入仿真器;若编译链接中发现错误(主要为语法错误),则会在构建窗口中提示错误种类,无法产生机器码,此时应该改正程序中的错误,重新产生代码并装入,正确后,才能进行下一步。
图2-19 重新产生代码并装入
(6)调试C51程序。
如图2-20所示,点击菜单“调试”后出现的下拉菜单中,可选择以“单步”或“全速运行”等方式运行程序,并在系统中查看程序运行结果。若结果有错(主要为逻辑错误),则应改正程序错误,重新编译、调试,直至程序运行结果完全正确。
图2-20 调试C51程序
本任务中,以“全速运行”方式运行程序后,将会看到LED0亮。在“调试”状态下,选中菜单“调试/返回监控”,将退出“调试”状态。
2.1.4 任务2-1-2 实现二极管闪烁
一、任务要求
本任务要求单片机控制LED0间隔1s闪烁,就是让LED0灯亮1s,然后熄灭1s,反复循环不止。
二、任务2-1-2的实施
1.硬件电路的设计
硬件电路的设计与任务2-1-1相同。
2.程序的设计
(1)下面函数为ms级延时函数。
void delayms(uintx) //当晶振为12MHz,延时xms, { //用MedWin3.0的 “查看/工具栏/时间”功能测试结果: uchar i; //x小于500(ms)时,最大误差为17μs; while(x--) //x为1000、2000、3000(ms)时,误差分别为19、23、27μs。 for(i=0;i<123;i++); } //当晶振为11.0592MHz,延时12x/11ms。
(2)单片机控制LED0闪烁的程序流程图如图2-21所示。
图2-21 单片机控制LED0闪烁的程序流程图
任务2-1-2的程序清单
#include<at89x52.h> #define uintunsignedint //定义uint=unsignedint(无符号整型) #define ucharunsignedchar //定义uchar=unsignedchar(无符号字符型) sbit LED0=P0^0; //定义符号LED0为单片机的P0.0引脚 void delayms(uintx) //函数省略,请参考上文 void main() { while(1) //另一种解法 { LED0=0; //LED0亮1s delayms(922); //实际晶振11.0592MHz,1000*11.0592/12=922 LED0=1; //LED0灭1s delayms(922); } /* while(1) //另一种解法 { LED0=!LED0; //LED0亮1s,灭1s。效果同上 delayms(922); }*/ }
正确连接好仿真器,在MedWin3.0中建立名为led1项目,输入上面的源程序,编译、连接正确后,全速运行,可以看到发光二极管LED0亮1s、灭1s,不停闪烁。
【知识链接二】程序流程图与程序的构成等
1.程序流程图
程序流程图是用一些图框来表示各种操作,直观形象,易于理解。美国国家标准化协会ANSI(AmericanNationalStandardInstitute)规定的一些常用的流程图符号如知识链接图2-1所示。
知识链接图2-1
2.程序的构成
程序=数据结构+算法
① 对数据的描述,在程序中指定数据的类型和数据的组织形式,即数据结构。
② 对操作的描述,即操作步骤,也就是算法。
3.逻辑运算符“!”的作用
!为逻辑非运算符,其作用是将逻辑量或位变量的值取反,即原来为1的,变为0;原来为0的,变为1。
三、单片机程序的烧录
程序调试成功后,下一步要把程序代码下载(烧录)到单片机芯片AT89S52中。
YL-236装置中配备了双龙ISP下载器。按照其光盘里产品说明书上的步骤,安装好相关软件后,第一次用USB线连接双龙ISP下载器和计算机时,会在电脑屏幕上看到“已经找到新硬件”的提示,选择“自动安装软件”,计算机会自动安装好新增的设备。
单片机程序烧录的操作步骤如下。
① 下载程序前,首先关闭系统电源,将仿真头取下;然后将AT89S52的引脚插在主机模块的卡座上并卡紧,注意单片机芯片上的弧形标识对准卡座的上方,方向不能插反。
② 用排线连接双龙ISP下载器与主机模块后,再接通双龙ISP下载器的USB线。
③ 启动双龙ISP下载器软件后,出现如图2-22所示界面。
图2-22 双龙ISP下载器软件界面
④ 如图2-22所示,首先在界面上部各选项中,正确选择“下载端口”、“下载速度”、“芯片型号”等,然后点击“FLASH存贮器”选项右边的“Flash”按钮,添加要烧录的机器码文件(一般为HEX文件),如图2-23所示。
⑤ 添加HEX文件后,点击图2-22中的“编程”按钮,软件会自动烧录机器码程序到AT89S52单片机中。但烧录有时会失败,可能是由于在设计的目标系统电路中,P1.5、P1.6、P1.7的电平被钳制住(例如:作为输入端口,而从外界输入电平为0V等),ISP下载器无法通过这3个引脚下载程序,这时,可以将主机模块上P1.5、P1.6、P1.7端子上的导线拔除,再下载程序。下载失败,有时是因为目标系统自身电源未供应,这时可以通过ISP下载器给目标系统供电,然后再完成下载,相关设置操作如图2-24所示。
图2-23 添加机器码文件
图2-24 给目标系统供电
⑥ 本任务中,正确烧录“LED0闪烁”的程序代码后,先断开双龙ISP下载器的USB线,再断开双龙ISP下载器与主机模块的排线,打开系统电源后,即可看到LED0不停闪烁。
2.1.5 任务2-1-3 实现流水灯
一、任务要求
单片机控制8个发光二极管从LED0到LED7间隔1s依次点亮(亮1s后熄灭,下一个LED点亮),当LED7亮1s后,又从LED0开始点亮,如此循环不止,视觉效果上像一个亮的灯从右到左流动。
二、任务2-1-3的实施
1.硬件电路的设计
硬件电路的设计与任务2-1-1相同。
2.程序的设计
(1)流水灯形成原理。
LED形成流水灯的效果实质是按顺序依次点亮发光二极管,其发光过程见表2-2。
表2-2 流水灯发光过程表
注:表中数据0或1,表示该引脚输入低或高电平,以使相应LED亮或灭。
(2)本教材提供两种方法实现单片机控制LED流水灯的程序清单,方法二的流程图如图2-25所示。
图2-25 单片机控制LED流水灯的程序流程图
任务2-1-3的程序清单
#include<at89x52.h> #define uintunsignedint //定义uint=unsignedint(无符号整型) #define ucharunsignedchar //定义uchar=unsignedchar(无符号字符型) sbit LED0=P0^0; //定义符号LED0为单片机的P0.0引脚 sbit LED1=P0^1; //定义符号LED1为单片机的P0.1引脚 sbit LED2=P0^2; //定义符号LED2为单片机的P0.2引脚 sbit LED3=P0^3; //定义符号LED3为单片机的P0.3引脚 sbit LED4=P0^4; //定义符号LED4为单片机的P0.4引脚 sbit LED5=P0^5; //定义符号LED5为单片机的P0.5引脚 sbit LED6=P0^6; //定义符号LED6为单片机的P0.6引脚 sbit LED7=P0^7; //定义符号LED7为单片机的P0.7引脚 void delayms(uintx) //函数省略,请参考任务2-1-2 void main() //方法一 { while(1) //方法一比较容易想到 { P0=0xff; //熄灭所有的LED LED0=0; //点亮LED0 delayms(922); //实际晶振为11.0592MHz,延时1s P0=0xff; LED1=0; //点亮LED1 delayms(922); P0=0xff; LED2=0; //点亮LED2 delayms(922); P0=0xff; LED3=0; //点亮LED3 delayms(922); P0=0xff; LED4=0; //点亮LED4 delayms(922); P0=0xff; LED5=0; //点亮LED5 delayms(922); P0=0xff; LED6=0; //点亮LED6 delayms(922); P0=0xff; LED7=0; //点亮LED7 delayms(922); } } void main() //方法二 { uchar num,i; //定义2个变量 while(1) //方法二比较简洁 { num=0xfe; //准备点亮第一个灯(LED0) for(i=0;i<8;i++) //8个LED,共循环8遍 { P0=num; //点亮某个LED delayms(922); //延时1s num=num<<1|0x01; //准备下一个LED } } } //0xfe:点亮LED0,0xfd:点亮LED1,0xfb:点亮LED2,0xf7:点亮LED3, //0xef:点亮LED4,0xdf:点亮LED5,0xbf:点亮LED6,0x7f:点亮LED7
正确连接好仿真器,在MedWin3.0中建立名为led2的项目,输入上面的源程序(main函数在方法一与方法二中选一个),编译、连接正确后,全速运行,可以看到一个亮的灯从右至左流动。关闭系统电源后,正确烧录“流水灯”的程序代码到单片机中,取下双龙ISP下载器,打开系统电源后,即可看到同样的结果。
在本书后面各任务中,请参考任务2-1-1~2-1-2中介绍的方法正确使用仿真器和双龙ISP下载器调试和下载程序,只有程序下载到单片机中并运行正确后,方为任务完成。
【知识链接三】C51的数据结构
一、常量与变量
C语言的基本数据类型,按其取值是否可改变分为常量和变量两种。在程序执行过程中,其值不发生改变的量称为常量,取值可变的量称为变量。
1.数值常量
数值常量也称为常数,例如12、-5.3、′c′、′abc′等。其中12为整型常量,-5.3为浮点型常量,′c′为字符型常量,"abc"为字符串常量。
2.符号常量
在C语言中,可以用一个标识符(标识符的定义见下文)来表示一个常量,称之为符号常量。符号常量在使用之前必须先定义,其一般形式为:“#define标识符 常量”。
其中,#define也是一条预处理命令,称为宏定义命令,其功能是把该标识符定义为其后的常量值。一经定义,以后在程序中所有出现该标识符的地方均代之以该常量值。
二、变量
值可以改变的量称为变量。一个变量应该有一个名字,在内存中占据一定的存储单元,在该存储单元中存放变量的值。请注意:变量名与变量值的区别如知识链接图2-2所示。
知识链接图2-2
在C语言中,要求对所有用到的变量做强制定义,也就是“先定义,后使用”。
在C语言中用来标识变量名、符号常量名、函数名、数组名、类型名等的有效字符序列称为标识符。简单地说,标识符就是一个名字。
C语言规定标识符只能由字母、数字和下划线三种字符组成,且第一个字符必须为字母或下划线。要注意的是,C语言中大写字母与小写字母被认为是两个不同的字符,即Sum与sum是两个不同的标识符。
1.整型变量
整型变量的基本类型符为int,在int之前可以根据需要分别加上修饰符。在KeilC中规定,基本整型数据在存储器中占用2个字节(即16bit)、长整型占用4个字节的存储空间。知识链接表2-1列出了各种整型数据的有关数据。
知识链接表2-1 整数类型的有关数据
2.字符型变量
字符型变量的基本类型符为char,其表达的范围是-128~+127;字符型变量只有一个修饰符unsigned,即无符号数,而加上了unsigned后,其表达的范围变为0~255。
3.浮点型变量
在8位单片机中,尽量不要用浮点型数据,这里不做介绍。
4.KeilC51特有的变量类型
(1)位型变量
位型变量是使用一个二进制位来存放数据,其值只有“0”和“1”两种。位型变量的定义和其他数据类型一样,关键字为bit。例如:
bita=0; //定义一个位变量,并赋初值为0
(2)sfr型变量
80C51内部有一些特殊功能寄存器(sfr)。为定义、存取这些特殊功能寄存器,C51增加了sfr型数据,相应也增加了sfr、sfr16和sbit这3个关键字。例如:
sfrP0=0x80; //定义8位特殊功能寄存器P0 sfr16DPTR=0x82; //定义16位特殊功能寄存器DPTR
三、变量的存储类型
在变量前加一个修饰符可以指定变量的存储器类型。
① data:片内RAM低128B,直接寻址访问,存储类型默认为data。② bata:片内RAM中20H~2FH,可以位寻址。
③ idata:片内RAM中256B,间接寻址访问。
④ xata:片外RAM中或片外I/O口扩展。
⑤ code:ROM中一般为固定数据表格,用MOVC指令查表访问。
例如:
unsignedcharbdataflag; sbit F0=flag∧0; sbit F1=flag∧1;