学习情境2基于DS1302和LCD1602的可调数字钟_第1页
学习情境2基于DS1302和LCD1602的可调数字钟_第2页
学习情境2基于DS1302和LCD1602的可调数字钟_第3页
学习情境2基于DS1302和LCD1602的可调数字钟_第4页
学习情境2基于DS1302和LCD1602的可调数字钟_第5页
已阅读5页,还剩20页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

学习情境2-可调式数字钟基于DS1302和LCD1602的可调数字钟前面的任务中我们学会了使用DS1302,知道了如何在单片机系统中的连接,也详细的学习了如何得到DS1302的时钟,并且我们使用了16个数码管把年月日和时分秒实时地显示出来。但数码管的显示毕竟有其自身的缺陷,现在在工业控制的各个环节,都使用液晶进行人机联系。在日常生活中,我们对液晶显示器并不陌生。液晶显示模块已作为很多电子产品的通用器件,如在计算器、万用表、电子表及很多家用电子产品中都可以看到,显示的主要是数字、专用符号和图形。在单片机的人机交流界面中,一般的输出方式有以下几种:发光管、LED数码管、液晶显示器。发光管和LED数码管比拟常用,软硬件都比拟简单。本任务中,我们使用LCD1602液晶显示器芯片作为时钟显示的硬件,这将大大简化电路结构,当然,在软件设计方面就比数码管复杂得多了。通过本任务的学习。希望能掌握LCD1602的软硬件设计方法,为以后学习功能更强大的液晶芯片作好知识储藏。3.1任务设计要求和设计原理一、设计要求1、利用专用时钟芯片DS1302获得秒、分、时、日、月、年等时间单位。2、用液晶显示芯片LCD1602实时显示时间3、能通过外部按键调节时间4、利用Proteus软件设计电路原理图、PCB图、并结合软件进行仿真测试5、利用Keil软件设计C语言源程序,通过编译、链接后生成HEX文件二、设计原理本任务主要由单片机AT89C52、时钟芯片DS1302、液晶显示芯片LCD1602等元器件组成。单片机是整个系统的主控芯片,主要负责对时钟芯片内部数据进行读写、并把读取的数据处理后实时送给数码管显示;DS1302时钟芯片那么产生本任务所需的时间:秒、分、时、日、月、年这6个时间单元,DS1302通过串行方式与单片机相连,在该芯片的时钟及复位引脚信号的控制下实现与单片机的数据通信,同时,利用4输入与门和4个独立按键实行对时间的调节,可调节年、月、日、时、分等5个时间单元,星期时间的调节会自动进行。在时间的显示方面,使用了LCD1602,这大大简化了电路结构,LCD1602通过P0口与单片机相连,显示的时间数据均由单片机的P0口输出,P0口外接上拉电阻。当系统上电后,LCD1602显示时间〔在仿真条件下,可设置为显示电脑时间〕,反复按下K1按键,可依次调节年、月、日、时、分,按下K2按键,可对相应时间进行加调节,按下K3按键,可对相应时间进行的减调节,按下确定按键K4,表示时间调节完毕。3.2硬件设计一、硬件电路设计本硬件系统电路包括:单片机最小系统〔包含时钟电路、复位电路和电源电路〕、LCD显示电路、按键调节电路和DS1302时钟电路。系统原理框图如以下图所示:8051单8051单片机时钟电路、复位电路、电源电路LCD显示电路LCD显示电路按键调节电路DS1302时钟电路按键调节电路DS1302时钟电路图3-1系统框图系统各局部电路结构简单,在此不一一单独介绍,只简单介绍LCD显示电路,其电路设计原理图如图3-2所示。图3-2LCD1602液晶显示电路从图可知,液晶芯片LCD1602的D0-D7与单片机的P0口相连接,且也外接上拉电阻。引脚RS、RW和E分别与P2口的P2.O、P2.1和P2.2连接,当然你愿意的话也可以和其他未使用的端口相连。其余电路不作介绍,本任务整体系统电路原理图如图3-3所示。图3-3整体系统电路原理图二、LCD1602技术资料在单片机系统中应用液晶显示器作为输出器件有以下几个优点:〔1〕显示质量高,由于液晶显示器每一个点在收到信号后就一直保持那种色彩和亮度,恒定发光,而不像阴极射线管显示器〔CRT〕那样需要不断刷新新亮点。因此,液晶显示器画质高且不会闪烁。〔2〕数字式接口,液晶显示器都是数字式的,和单片机系统的接口更加简单可靠,操作更加方便。〔3〕体积小、重量轻,液晶显示器通过显示屏上的电极控制液晶分子状态来到达显示的目的,在重量上比相同显示面积的传统显示器要轻得多。〔4〕功耗低,相对而言,液晶显示器的功耗主要消耗在其内部的电极和驱动IC上,因而耗电量比其它显示器要少得多。1、液晶显示原理:液晶显示的原理是利用液晶的物理特性,通过电压对其显示区域进行控制,有电就有显示,这样即可以显示出图形。液晶显示器具有厚度薄、适用于大规模集成电路直接驱动、易于实现全彩色显示的特点,目前已经被广泛应用在便携式电脑、数字摄像机、PDA移动通信工具等众多领域。2、液晶显示器的分类:液晶显示的分类方法有很多种,通常可按其显示方式分为段式、字符式、点阵式等。除了黑白显示外,液晶显示器还有多灰度有彩色显示等。如果根据驱动方式来分,可以分为静态驱动〔Static〕、单纯矩阵驱动〔SimpleMatrix〕和主动矩阵驱动〔ActiveMatrix〕三种。3、液晶显示器各种图形的显示原理:线段的显示:点阵图形式液晶由M×N个显示单元组成,假设LCD显示屏有64行,每行有128列,每8列对应1字节的8位,即每行由16字节,共16×8=128个点组成,屏上64×16个显示单元与显示RAM区1024字节相对应,每一字节的内容和显示屏上相应位置的亮暗对应。例如屏的第一行的亮暗由RAM区的000H——00FH的16字节的内容决定,当〔000H〕=FFH时,那么屏幕的左上角显示一条短亮线,长度为8个点;当〔3FFH〕=FFH时,那么屏幕的右下角显示一条短亮线;当〔000H〕=FFH,〔001H〕=00H,〔002H〕=00H,……〔00EH〕=00H,〔00FH〕=00H时,那么在屏幕的顶部显示一条由8段亮线和8条暗线组成的虚线。这就是LCD显示的根本原理。字符的显示:用LCD显示一个字符时比拟复杂,因为一个字符由6×8或8×8点阵组成,既要找到和显示屏幕上某几个位置对应的显示RAM区的8字节,还要使每字节的不同位为“1”,其它的为“0”,为“1”的点亮,为“0”的不亮。这样一来就组成某个字符。但由于内带字符发生器的控制器来说,显示字符就比拟简单了,可以让控制器工作在文本方式,根据在LCD上开始显示的行列号及每行的列数找出显示RAM对应的地址,设立光标,在此送上该字符对应的代码即可。汉字的显示:汉字的显示一般采用图形的方式,事先从微机中提取要显示的汉字的点阵码〔一般用字模提取软件〕,每个汉字占32B,分左右两半,各占16B,左边为1、3、5……右边为2、4、6……根据在LCD上开始显示的行列号及每行的列数可找出显示RAM对应的地址,设立光标,送上要显示的汉字的第一字节,光标位置加1,送第二个字节,换行按列对齐,送第三个字节……直到32B显示完就可以LCD上得到一个完整汉字。1602字符型LCD简介字符型液晶显示模块是一种专门用于显示字母、数字、符号等点阵式LCD,目前常用16*1,16*2,20*2和40*2行等的模块。一般1602字符型液晶显示器实物如图3-4:图3-41602字符型液晶显示器实物图1602LCD分为带背光和不带背光两种,其控制器大局部为HD44780,带背光的比不带背光的厚,是否带背光在应用中并无差异。1602LCD主要技术参数:显示容量:16×2个字符;芯片工作电压:4.5—5.5V;工作电流:2.0mA(5.0V);模块最正确工作电压:5.0V;字符尺寸:2.95×4.35(W×H)mm

引脚功能说明:1602LCD采用标准的14脚〔无背光〕或16脚〔带背光〕接口,各引脚接口说明如表1所示:第1脚:VSS为地电源。第2脚:VDD接5V正电源。第3脚:VL为液晶显示器比照度调整端,接正电源时比照度最弱,接地时比照度最高,比照度过高时会产生“鬼影〞,使用时可以通过一个10K的电位器调整比照度。第4脚:RS为存放器选择,高电平时选择数据存放器、低电平时选择指令存放器。第5脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。当RS和R/W共同为低电平时可以写入指令或者显示地址,当RS为低电平R/W为高电平时可以读忙信号,当RS为高电平R/W为低电平时可以写入数据。第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。第7~14脚:D0~D7为8位双向数据线。第15脚:背光源正极。第16脚:背光源负极。表11602LCD引脚接口说明编号符号引脚说明编号符号引脚说明1VSS电源地9D2数据2VDD电源正极10D3数据3VL液晶显示偏压11D4数据4RS数据/命令选择12D5数据5R/

W读/写选择13D6数据6E使能信号14D7数据7D0数据15BL

A背光源正极8D1数据16BL

K背光源负极1602LCD的指令说明及时序1602液晶模块内部的控制器共有11条控制指令,如表2所示:表2:控制命令表序号指令RSR/WD7D6D5D4D3D2D1D0功能1复位显示器0000000001清屏,光标归位2光标返回000000001*设置地址计数器清零,DDRAM数据不变,光标移到左上角3字符进入模式00000001I/DS设置字符进入时的屏幕移位方式4显示开/关控制0000001DCB设置显示开关,光标开关,闪烁开关5光标或字符移位000001S/CR/L**设置字符与光标移动6功能设置00001DLNF**设置DL,显示行数,字体7设置字符发生存贮器地址0001字符发生存贮器地址设置6位的CGRAM地址以读/写数据8设置数据存贮器地址001显示数据存贮器地址设置7位的DDRAM地址以读/写数据9读忙标志或地址01BF计数器地址读忙标志及地址计数器10写数据到CGRAM或DDRAM〕10写入一字节数据,需要先设置RAM地址向CGRAM/DDRAM写入一字节的数据11从CGRAM或DDRAM读数据11读取入一字节数据,需要先设置RAM地址从CGRAM/DDRAM读取一字节的数据I/D=1递增,I/D=0递减。S=0时显示屏不移动,S=1时,如果I/D=1且有字符写入时显示屏左移,否那么右移。D=1显示屏开,D=0显示屏关。C=1时光标出现在地址计数器所指的位置,C=0时光标不出现。B=1时光标出现闪烁,B=0时光标不闪烁。S/C=0时,RL=0那么光标左移,否那么右移。S/C=1时,RL=0那么字符和光标左移,否那么右移。DL=1时数据长度为8位,DL=0时为使用D7-D4共4位,分两次送一字节。N=0为单行显示,N=1时为双行显示。F=1时为510点阵字体,F=0时为57点阵字体。BF=1时LCD忙,BF=0时LCD就绪。1602液晶模块的读写操作、屏幕和光标的操作都是通过指令编程来实现的。〔说明:1为高电平、0为低电平〕指令1:清显示,指令码01H,光标复位到地址00H位置。指令2:光标复位,光标返回到地址00H。指令3:光标和显示模式设置I/D:光标移动方向,高电平右移,低电平左移S:屏幕上所有文字是否左移或者右移。高电平表示有效,低电平那么无效。指令4:显示开关控制。D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示C:控制光标的开与关,高电平表示有光标,低电平表示无光标B:控制光标是否闪烁,高电平闪烁,低电平不闪烁。指令5:光标或显示移位S/C:高电平时移动显示的文字,低电平时移动光标。指令6:功能设置命令DL:高电平时为4位总线,低电平时为8位总线N:低电平时为单行显示,高电平时双行显示F:低电平时显示5x7的点阵字符,高电平时显示5x10的点阵字符。指令7:字符发生器RAM地址设置。指令8:DDRAM地址设置。指令9:读忙信号和光标地址BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。指令10:写数据。指令11:读数据。

与HD44780相兼容的芯片时序表如下:表3:根本操作时序表读状态输入RS=L,R/W=H,E=H输出D0—D7=状态字写指令输入RS=L,R/W=L,D0—D7=指令码,E=高脉冲输出无读数据输入RS=H,R/W=H,E=H输出D0—D7=数据写数据输入RS=H,R/W=L,D0—D7=数据,E=高脉冲输出无读写操作时序如图3-5和3-6所示:图3-5读操作时序图3-6写操作时序1602LCD的RAM地址映射及标准字库表液晶显示模块是一个慢显示器件,所以在执行每条指令之前一定要确认模块的忙标志位,如果该位为低电平,表示不忙,否那么此指令失效。要显示字符时要先输入显示字符地址,也就是告诉模块在哪里显示字符,图3-7是1602的内部显示地址。图3-71602LCD内部显示地址例如第二行第一个字符的地址是40H,那么是否直接写入40H就可以将光标定位在第二行第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位D7恒定为高电平1所以实际写入的数据应该是01000000B〔40H〕+10000000B(80H)=11000000B(C0H)。在对液晶模块的初始化中要先设置其显示模式,在液晶模块显示字符时光标是自动右移的,无需人工干预。每次输入指令前都要判断液晶模块是否处于忙的状态。1602液晶模块内部的字符发生存储器〔CGROM〕已经存储了160个不同的点阵字符图形,如图3-8所示,这些字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比方大写的英文字母“A〞的代码是01000001B〔41H〕,显示时模块把地址41H中的点阵字符图形显示出来,我们就能看到字母“A〞图3-8字符代码与图形对应图3.3软件设计软件设计中主要是针对DS1302芯片和LCD1602进行程序的设计,而DS1302的驱动程序在上个工程中我们已经作了详细介绍,在此不再赘述。我们主要编制LCD的驱动程序。根据对LCD资料的分析,程序编制主要有:读取LCD的状态,忙检查,向LCD写入命令,向LCD写入数据,LCD初始化,设置LCD液晶的显示位置,显示字符等,具体如下:1、读取LCD的状态//----------读取LCD的状态-------*/ucharRead_LCD_State(){ucharstate;RS=0;RW=1;EN=1;DelayMS(2);state=P0;EN=0;DelayMS(2);returnstate;}2、忙检查//-------------忙检查--------------*/voidLCD_Busy_Wait(){while((Read_LCD_State()&0x80)==0x80); DelayMS(5);}3、向LCD写入命令//-----------写LCD命令-----------------*/voidWrite_LCD_Command(ucharcmd){LCD_Busy_Wait();RS=0;RW=0;EN=0;P0=cmd;EN=1;DelayMS(2);EN=0;}4、向LCD写入数据//-----------向LCD写入数据-----------------*/voidWrite_LCD_Data(uchardat){LCD_Busy_Wait();RS=1;RW=0;EN=0;P0=dat;EN=1;DelayMS(2);EN=0;}5、LCD初始化//-----------LCD初始化-----------------*/voidInitialize_LCD1602()//液晶初始化函数{Write_LCD_Command(0x38);DelayMS(2);//功能设置,数据长度为8位,双行显示5×7点//阵字体Write_LCD_Command(0x01);DelayMS(2); //清屏Write_LCD_Command(0x06);DelayMS(2);//字符进入模式:屏幕不动,字符后移Write_LCD_Command(0x0c);DelayMS(2); //显示开,关光标}6、设置LCD液晶的显示位置//-----------设置液晶显示位置-----------------*/voidSet_LCD_Position(ucharpos){Write_LCD_Command(pos|0x80); //设置7位的DDRAM地址值}7、显示字符//-----------在LCD上显示字符串-----------------*/voidLCD_Display(ucharp,uchar*str){uchari;Set_LCD_Position(p);for(i=0;i<16;i++){Write_LCD_Data(str[i]);DelayMS(2);}}除此之外,还有年、月、日、时、分等的设置函数,显示函数,键盘处理函数等,在此不在详细介绍,同学们可参考完整程序中的代码。数组数组数组是在程序设计中,为了处理方便,把具有相同类型的假设干变量按有序的形式组织起来的一种形式。这些按序排列的同类数据元素的集合称为数组。在C语言中,数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是根本数据类型或是构造类型。因此按数组元素的类型不同,数组又可分为数值数组、字符数组、指针数组、结构数组等各种类别。一、一维数组1、一组的定义定义:数组是有序数据(必须是相同的数据类型结构)的集合。格式:类型说明符数组名[常量表达式]例如:inttemp[5];表示数组名为a,有10个元素,并且每个元素的类型都是int型的。floatb[10],c[5];说明实型数组b有10个元素,实型数组c有5个元素;注意:〔1〕数组名的命名规那么和标示符的命名规那么相同。〔2〕常量表达式要有方括号括起来,不能用圆括号,inta(10);这是非法的!〔3〕常量表达式表示数组元素的个数,即数组长度。并且数组的第一个元素是从下标0开始的!〔4〕常量表达式可以是常量也可以是符号常量,不能包含变量。C语言绝对不允许对数组的大小作动态定义;例如:下面是非法的intn;scanf(“%d〞,&n);inta[n];〔5〕数组的类型实际上就是指数组元素的取值类型,对于同一数组,它所有元素的数据类型都是相同的;〔6〕数组名不能与其他变量名相同;例如:main(){inta;floata[10];………。。}是非法的!〔7〕允许在同一个类型说明中,说明多个数组和多个变量;例如:inta,b,c,d[10],e[5];2、一维数组元素的初始化有以下方法初始化:1〕在定义数组时,对数组元素赋初值;例如:inta[10]={0,1,2,3,4,5,6,7,8,9};上面的语句等价于a[0]=0,a[1]=1,2〕可以只给一局部元素赋初值,例如:inta[10]={0,1,2,3,4};表示只给数组的前5个元素赋初值,后5个元素的值,系统自动默认为03〕在对全部数组元素赋初值时,可以不指定数组长度;例如:inta[5]={0,1,2,3,4};可以改写为:inta[]={0,1,2,3,4};但是,inta[10]={0,1,2,3,4};不能改写为:inta[]={0,1,2,3,4};3、一维数组的引用数组必须先定义,后使用。C语言规定:只能逐个引用数组元素,而不能一次引用整个数组!数组的引用形式为:数组名[下标]其中,下标可以是整型常量也可以是整型表达式。例如:a[0]=a[5]+a[7]+a[2*3]4、一维数组的程序举例〔1〕读10个数存入数组中,输出数组中的所有数据。main(){inti,a[10];for(i=0;i<=9;i++)a[i]=i;/*顺序给数组元素赋初*/for(i=0;i<=9;i++)printf(“%d〞,a[i]);/*顺序输出数组元素*/}〔2〕读10个整数存入数组中,输出平均值。#include<stdio。h>#definesize10main(){intx[size],i;floats=0,ave;for(i=0;i<size;i++)scanf(“%d〞,&x[i]);for(i=0;i<size;i++)s+=x[i];ave=s/size;printf(“%d\n〞,ave);}二、二维数组1、二维数组的定义定义格式:类型说明符数组名[常量表达式1][常量表达式2]其中表达式1表示第一维下标的长度;表达式2表示第二维下标的长度;例如:inta[2][3];说明了一个2行3列的数组,数组名为a,数组元素的类型为整型,该数组共有2*3个元素;即:a[0][0],a[0][1],a[0][2],a[1][0],a[1][1],a[1][2]注意的是:二维数组在概念上是二维的,就是说其下标在两个方向上变化,数组元素在数组中的位置也处于一个平面中,并不向一维数组,所有的元素是线性的,但是,二维数组中的元素和一维数组中的元素一样,也是按线性存储的;如何存储呢?首先,它和一维数组一样,也是按线性存储的。在C语言中,二维数组是按行排列的,即存完一行后,顺序存入第二行;还是上面那个例子:inta[2][3];由于数组a说明为int型,所以数组的每个元素在内存中占两个字节的存储空间,2、二维数组的引用引用形式:数组名[下标][下标]其中下标应该为整型常量或整型表达式;例如:inta[3][4];a[2][3]表示数组a的第三行第四列的元素。注意:下标变量和数组说明在形式上有些相似,但这两者具有完全不同的含义;数组说明的方括号中给出的是某一维的长度;而数组元素中的下标是该元素在数组中的位置标识;数组说明中的方括号内只能是常量。而数组元素中方括号中的下标可以是常量,变量或表达式。3、二维数组的初始化二维数组初始化也是在类型说明时给各下标变量赋以初值。二维数组可以按行分段赋值,也可以按行连续赋值1〕按行分段赋值inta[2][3]={{1,2,3},{4,5,6}};2〕按行连续赋值inta[2][3]={1,2,3,4,5,6};这两种赋值的结果是完全相同的;说明:1〕可以只对局部元素赋值,未赋值的元素自动取0;例如:inta[3][3]={{1},{2},{3}};是对每行的第一列元素赋值,未赋值的元素自动取02〕如果对所有的元素赋初值,那么第一维的长度可以不给出;例如:inta[3][3]={1,2,3,4,5,6,7,8,9};可以改为:inta[][3]={1,2,3,4,5,6,7,8,9};3〕数组是一种构造类型的数据,二维数组可以看作是一维数组的嵌套,设一维数组的每个元素又都是一个数组,就组成了一个二维数组,当然,前提是每个元素的类型必须一致;同理,一个二维数组也可以分解为多个一维数组,例如:a[3][4],可分解为3个一维数组,其数组名分别是:a[0],a[1],a[2];对这三个一维数组不需另作说明即可使用,这三个一维数组都有4个元素,例如:一维数组a[0]的元素是a[0][0],a[0][1],a[0][2],a[0][3];必须强调的是,a[0],a[1],a[2]不能当作下标变量使用,因为它们是数组名,不是一个单纯的下标变量;对于一个数组,例如:a[3][4]可以通过循环语句来赋值;for(i=0;i<3;i++)for(j=0;j<4;j++)scanf(“%d〞,&a[i][j]);4、二维数组的使用举例1〕将一个二维数组的行和列元素互换,存到另一个二维数组中。#include<stdio。h>main(){inta[2][3]={{1,2,3},{4,5,6}};intb[3][2],i,j;for(i=0;i<=1;i++){for(j=0;j<=2;j++){printf(“%d〞,a[i][j]);b[j][i]=a[i][j];}printf(“\n〞);}for(i=0;i<=2;i++){for(j=0;j<=1;j++)printf(“%d〞,b[i][j]);printf(“\n〞);}}三、字符型数组1、字符型数组的定义定义:每个数组元素的数据类型是字符型的一维数组。字符数组的引用,存储,初始化的方法和一维数组相同例如:字符型数组的定义charc[10];定义了字符型数组c,它的数组元素有10个,由于字符型和整型是相通的,因此,上面的定义也可以改为:intc[10];例如:字符型数组的初始化charc[3]={‘a’,’b’,’c’};注意:如果花括号提供的数组元素个数大于数组长度,那么作语法错误处理;如果初值个数小于数组长度,那么只将这些字符赋给前面的元素,其余的元素自动定为空字符(‘\0’);比方:charc[5]={‘a’,’b’,’c’}abc\0\0如果提供的初值个数和预定的数组长度相同,在定义时可以省掉数组长度,系统自动根据初值个数确定数组长度。例如:chara[]={‘a’,’b’,’c’,’d’,’e’};也可以用字符串常量赋值;如:charch[6]={“hello〞};charch[6]=〞hello〞;charch[]=〞hello〞;hello\02、字符型数组的引用可以引用字符数组中的一个元素即得到一个字符!例如:输出一个字符串Main(){chara[5]={‘a’,’b’,’c’,’d’,’e’};inti;for(i=0;i<5;i++)printf(“%c〞,a[i];)printf(“\n〞);}四、字符串1、字符串定义定义:C语言中用双引号括起来的任意字符序列就是字符串。C语言中没有字符串变量,对程序中的字符串,系统用字符数组方式保存,连续,顺序的存放每一个字符,最后加上一个空字符〞\0”2、字符串的输入输出1〕通过逐个字符输入和输出:使用%c;2〕通过整个字符串输入和输出:使用%s;3、字符串数组字符串数组就是数组中的每个元素又都是存放字符串的数组。例如:可以将一个二维字符数组看成是一个字符串数组;charline[10][80];数组line共有10个元素,每个元素可以存放80个字符(79个普通字符,一个结束字符),第一个下标决定字符串的个数,第二个下标决定字符串的最大长度,line是有十个字符串的数组,这些字符串的最大长度是79。字符串的初始化方法:例如:charstr[3][5]={“a〞,〞ab〞,〞abc〞};/*根据定义的大小初始化*/charstr[][5]={“a〞,〞ab〞,〞abc〞};/*根据右边字符串的个数,定义数组大小*/该数组的存储示意图为:a\0ab\0abc\03.4软件硬件的仿真调试1、首先利用ISIS-Professional软件设计好硬件电路图。2、其次,利用KEIL软件建立工程工程,把用C语言编好的程序通过该软件平台进行编译,直至把C程序修改无错误为止,并生成HEX文件。3、在ISIS-Professional软件平台下导入由KEIL软件生成的HEX文件,进行仿真,仔细观察产生的现象和效果,根据仿真的效果,边修改边调试,直到仿真效果与我们对工程的要求一致为止。本任务我们用单片机和时钟芯片DS1302和液晶LCD1602设计了一个电子时钟,通过仿真,本系统能够准确的显示秒、分、时、星期、年、月、日等时间,由于使用了专门的时钟芯片和液晶显示芯片,比前一个任务无论视觉效果更佳。3.5知识拓展-I/O接口扩展〔二〕一、I/O扩展常用芯片〔1〕TTL/CMOS锁存器/缓冲器芯片:如74LS377、74LS374、74LS373、74LS273、74LS244、74LS245等;〔2〕通用可编程I/O接口芯片:如8255、8155、8729等;〔3〕可编程阵列:如GAL16V8、GAL20V8等。I/O扩展中应注意的几个问题〔1〕访问扩展I/O的方法与访问外部数据存储器完全相同,使用相同的指令。〔2〕扩展多片I/O芯片或多个I/O设备时,注意总线的驱动能力问题;〔3〕扩展I/O口的目的是为了单片机与外部设备进行信息交换而设置的一个输入输出通道,I/O口最终与外设相连。〔4〕在软件设计时,I/O口对应初始状态设置、工作方式选择要与外接设备相匹配。二、扩展简单并行接口1.扩展并行输出口〔1〕用74LS377扩展并行输出口74LS377是带有输出允许端的8D锁存器,有8个输入端口、8个输出端口、1个时钟输入端CLK〔上升沿有效〕和1个允许控制端OE。如图3-9所示,OE与P2.7相连,74LS377的地址为7FFFH;假设与P2.0相连,那么地址相应为0EFFH。图3-9MCS-51扩展输出口74LS377【例3.1】假设以图3-8为接口电路,将片内RAM地址为50H单元的数据通过该电路输出。程序清单如下:MOVDPTR,#7FFFH;数据指针指向74LS377MOVA,50H;输出的50H单元数据送累加器AMOVX@DPTR,A;P0口将数据通过74LS377输出〔2〕用74LS374扩展并行输出口74LS374是具有三态输出的8D边沿触发器,其功能与74LS377相似,74LS374与单片机接口电路如图3-10所示,74LS374的地址为7FFFH。74LS374具有较强的驱动能力,输出低电平电流IOL最大可达24mA,是74LS377的3倍。在有较强驱动能力要求场合,可选用74LS374作为并行口扩展器件。图3-1074LS374与单片机接口电路2.扩展并行输入口并行输入扩展口比拟简单,只需采用8位缓冲器即可。常用的缓冲器有74LS244,74LS244为单向总线缓冲器,只能一个方向传输数据。并行输入接口与单片机连接如图3-11所示。图3-11扩展74LS244并行输入口【例3.2】如图3-10所示,扩展并行输入口,将输入口中的8位数据送片内51H单元。程序清单如下:MOVDPTR,#7FFFH;数据指针指向74LS244MOVXA,@DPTR;外部数据经过74LS244送入累加器AMOV51H,A;数据送51H单元保存将上述输入输出电路合并即如图3-12所示。在图3-12的输入输出接口电路中,输入采用三态门74HC244,输出采用8D触发器〔锁存器〕74HC374。P0口为双向数据线,既能从74HC244输入数据,又能将数据通过74HC374输出。输出控制信号由P2.0和WR合成,当两者同时为低电平时,或门输出0,将P0口数据锁存到74HC374,其输出控制着发光二极管LED。当某线输出为0时,该线上的LED发光。输入控制信号由P2.0和RD合成,当二者同时为低电平时,或门输出为0,选通74HC244,将外部信息输入到总线。与74HC244相连的按键开关没有按下时,输入全为1;假设按下某键那么所在的线输入为0。可见,输入输出都是在P2.0为0时有效,因此它们的口地址为0FEFFH,即占用相同的地址空间。但是由于分别用RD和WR信号控制,因此不会发生冲突。图3-12合并输入输出口正如前面所提到的,扩展I/O口和扩展外部RAM一样,因此访问外部I/O口就像访问外部RAM一样,用的是MOVX类指令。对于图3-11,如果需要实现的功能是按下任意一个按键,对应的LED发光,那么程序如下:LOOP:MOVDPTR,#0FEFFH;数据指针指向扩展I/O地址MOVXA,@DPTR;从74HC244读入数据,检测按键MOVX@DPTR,A;向74HC374输出数据,驱动LEDSJMPLOOP;循环三、可编程并行I/O口的扩展1、RAM/IO扩展芯片8155Intel8155是一个具有RAM、I/O和计数器的通用可编程接口多功能芯片。其具有的资源为:256B的静态RAM;两个可编程的8位并行I/O口PA和PB;一个可编程的6位并行I/O口PC;一个可编程14位减计数器TC;8位地址锁存器。〔1〕8155的结构及引脚功能8155的引脚如图3-13〔a〕所示,逻辑框图如图3-13〔b〕所示。8155引脚符号的含义和功能如下:AD0~AD7:地址数据线;IO/M:IO/RAM选择信号端,输入高电平选择I/O操作,低电平选择访问片内RAM;CE:片选信号输入端,低电平有效;RD:读选通输入端,低电平有效;WR:写选通输入端,低电平有效;TI:计数器计数脉冲输入端;TO:计数器的输出信号端,输出波形由内部定时工作方式决定;PA0~PA7:8位并行I/O口;PB0~PB7:8位并行I/O口;PC0~PC5:6位并行I/O口;ALE:地址锁存信号输入端,其下降沿时,锁存AD0~AD7上的地址。RESET:复位输入引脚,高电平复位。Vcc:电源+5V;Vss:接地。图3-138155引脚及逻辑框图〔2〕8155的RAM和I/O地址编码8155的I/O端口及RAM地址在单片机应用系统中与外部数据存储器是统一编址的,其控制操作见表3-4,对应I/O口存放器的地址编码见表3-5。8155当IO/M为0时,单片机对8155的RAM进行操作,共256B,低八位的地址为:00H—0FFH。8155当IO/M为1时,单片机对8155的I/O口进行操作。表3-48155控制操作表低八位地址I/O口xxxxx000B命令/状态口xxxxx001BPA口xxxxx010BPB口xxxxx011BPC口xxxxx100B定时器/计数器低字节存放器xxxxx101B定时器/计数器高字节存放器表3-5

8155的端口地址编码AD7AD6AD5AD4AD3AD2AD1AD0对应端口×××××000命令/状态存放器×××××001A口×××××010B口×××××011C口×××××100定时器/计数器低8位×××××101定时器/计数器高8位〔3〕命令/状态存放器8155的命令/状态存放器物理上只有一个端口地址〔见表3-5〕。对该口写操作,命令字被写入命令存放器;对该口读操作,那么从状态存放器读出状态字。8155所提供的每个I/O口和定时器都是可编程的。I/O的工作方式选择,定时器/计数器的工作控制都是通过对8155内部命令存放器设定命令控制字的方式来实现的,通过对状态字的读取来判别它们的工作状态。命令/状态存放器共用一个口地址,通过读/写信号加以区分。命令字存放器只能写不能读,状态存放器只能读不能写。8155命令字格式见表3-6;8155状态字格式见表3-7。表3-68155命令字格式表TM1TM1TM2IEBIEAPC2PC1PAPAPBA口方式B口方式B口A口计数器方式00:空操作01:停止计数10:计满后停止11:开始计数0:输入1:输出0:禁止中断1:允许中断00110110方式1:A、B口根本I/O;C口输入方式2:A、B口根本I/O;C口输出方式3:A口选通I/O;B口根本I/O方式4:A、B口选通I/O信号说明:INTRA、INTRB:中断请求输出线,高电平有效。STBA、STBB:设备选通信号输入线,低电平有效。BFA、BFB:口缓冲器满信号,高电平有效。表3-78155状态字格式表〔4〕接口与编程①硬件连接。8155可以直接与MCS-51单片机连接,不需要任何外加逻辑。扩展一片8155系统将增加256B片外RAM,22位I/O口线及1个14位减法计数器。MCS-51与8155的连接方法如图3-14所示。P0口不需要加锁存器,可以直接与8155的AD0~AD7相连,它既是低8位地址线也是8位数据线。8155的锁存信号ALE直接引自单片机ALE输出,用以在内部锁存地址。图3-14MCS-51与8155的一种接口逻辑CE及IO/M与MCS-51的连接方式决定了8155的地址范围:RAM字节地址范围:7E00H~7EFFH命令/状态存放器:7F00HPA口:7F01HPB口:7F02HPC口:7F03H定时器低8位存放器:7F04H定时器高8位存放器:7F05H②程序设计。图3-13接口电路中,将单片机片内RAM50H~5FH单元的内容送8155片内的50H~5FH单元,设定8155的工作方式为:A口根本输入方式,B口根本输出方式,C口输入方式,定时器作方波发生器,对输入脉冲50分频。程序清单如下:D8155:MOVR0,50H;将源数据单元首地址送存放器R0中MOVDPTR,#7E50H;数据指针指向8155内部RAM单元LP:MOVA,@R0;数据送累加器AMOVX@DPTR,A;数据从累加器A送8155内部RAM单元INCDPTR;指向下一个8155内部RAM单元INCR0;指向下一个CPU内部RAM单元CJNER0,#60H,LP;数据未传送完继续MOVDPTR,#7F04H;指向定时器低8位MOVA,#32H;分频系数32H〔即50〕MOVX@DPTR,A;低8位初值装入INCDPTR;指向定时器高8位MOVA,#40H;设定时器方式为连续方波(40H=01000000B)MOVX@DPTR,A;定时器/计数器方式及高6位初值装入MOVDPTR,#7F00H;数据指针指向控制字存放器MOVA,#0C2H;设定A,B口方式,并启动定时器〔0C2H=11000010B〕MOVX@DPTR,ARET;程序返回0C2H,其意义是:最高2位11的作用是启动计数器,最低位D0=0设PA为输入,D1=1设PB为输出。D3D2=00设PA、PB为根本I/O,PC为输入。完整程序代码1、LCD1602.c源程序//---------------------LCD1602.c--------------------------//液晶控制与显示驱动程序//--------------------------------------------------------#include<reg51.h>#include<intrins.h>#defineucharunsignedchar#defineuintunsignedintsbitRS=P2^0; //LCD存放器选择sbitRW=P2^1; //LCD读写控制sbitEN=P2^2; // LCD启动//*--------延时子程序--------*//voidDelayMS(uintK){uchari;while(K--){ for(i=0;i<120;i++); }}//----------读取LCD的状态-------*/ucharRead_LCD_State(){ucharstate;RS=0;RW=1;EN=1;DelayMS(2);state=P0;EN=0;DelayMS(2);returnstate;}//-------------忙检查--------------*/voidLCD_Busy_Wait(){while((Read_LCD_State()&0x80)==0x80); DelayMS(5);}//-----------写LCD命令-----------------*/voidWrite_LCD_Command(ucharcmd){LCD_Busy_Wait();RS=0;RW=0;EN=0;P0=cmd;EN=1;DelayMS(2);EN=0;}//-----------向LCD写入数据-----------------*/voidWrite_LCD_Data(uchardat){LCD_Busy_Wait();RS=1;RW=0;EN=0;P0=dat;EN=1;DelayMS(2);EN=0;}//-----------LCD初始化-----------------*/voidInitialize_LCD1602()//液晶初始化函数{Write_LCD_Command(0x38);DelayMS(2); //功能设置,数据长度为8位,双行显示,5×7点阵字体Write_LCD_Command(0x01);DelayMS(2); //清屏Write_LCD_Command(0x06);DelayMS(2);//字符进入模式:屏幕不动,字符后移Write_LCD_Command(0x0c);DelayMS(2); //显示开,关光标}//-----------设置液晶显示位置-----------------*/voidSet_LCD_Position(ucharpos){Write_LCD_Command(pos|0x80); //设置7位的DDRAM地址值}//-----------在LCD上显示字符串-----------------*/voidLCD_Display(ucharp,uchar*str){uchari;Set_LCD_Position(p);for(i=0;i<16;i++){ Write_LCD_Data(str[i]); DelayMS(2); }}2、main.c源程序//------------------------------------------------------------------------//名称:用DS1302和1602液晶显示的实时时钟//-------------------------------------------------------------------------//说明:本程序运行时会以PC时间为默认时间开始运行,运行过程中可以通过K1按键选择调节对象,用K2、K3//按键进行加减,用K4按键保存。//---------------------------------------------------------------------------------------#include<reg51.h>#include<string.h>#include<intrins.h>#defineucharunsignedchar#defineuintunsignedintvoidInitialize_LCD1602();//液晶初始化函数voidLCD_Display(ucharp,uchar*str);sbitSDA=P1^0; //DS1302数据线sbitCLK=P1^1; //DS1302时钟线sbitRST=P1^2; //DS1302 复位线sbitk1=P3^3; //选择按键sbitk2=P3^4; //加sbitk3=P3^5; // 减sbitk4=P3^6; // 确定uchartcount=0; ucharMonthsDays[]={0,31,0,31,30,31,30,31,31,30,31,30,31};//一年中每个月的天数,二月的天数由年份决定uchar*Week[]={"SUN","MON","TUS","WEN","THU","FRI","SAT"};//周日,周一到周六//LCD显示缓冲ucharLCD_DSY_BUFFER1[]={"DATE00-00-00"};ucharLCD_DSY_BUFFER2[]={"TIME00:00:00"};ucharDateTime[7];//所读取的日期时间charAdjust_Index=-1;//当前调节的时间对象:秒,分,时,日,月,年〔0,1,2,3,4,6〕ucharChange_Flag[]="-MHDM-Y";//(分,时,日,月,年)不调节秒和周//函数声明voidWrite_Byte_TO_DS1302(ucharX);//向DS1302写入一个字节ucharRead_Byte_FROM_DS1302(); //从DS1302中读取一个字节ucharRead_Data_FROM_DS1302(ucharaddr);//从DS1302指定位置读取数据 ,读数据voidWrite_Data_TO_DS1302(ucharaddr,uchardat);//向DS1302指定位置写入数据,写数据voidSET_DS1302(); //设置时间voidGetTime(); //读取当前时间voidInitialization();//初始化函数//------------------------------------------//主程序//----------------------------------------voidmain(){Initialization();while(1){ if(Adjust_Index==-1) GetTime(); }}//*-----------------初始化函数--------------*//voidInitialization(){Initialize_LCD1602();//调用液晶初始化函数IE=0x83; //允许中断外部0中断和T0中断IP=0x01; // 设置中断优先级IT0=0x01; // 设置外部中断的脉冲触发方式TMOD=0x01;//设置定时器的工作方式,为方式1TH0=-50000/256;//写入初值TL0=-50000%256;//写入初值TR0=1;// 启动定时器}//*--------写字节函数,向DS1302写入一个字节--------*//voidWrite_Byte_TO_DS1302(ucharX)//向DS1302写入一个字节{uchari; for(i=0;i<8;i++) { SDA=X&1; CLK=1; CLK=0; X>>=1; }}//*--------读字节函数,从DS1302读取一个字节--------*//ucharRead_Byte_FROM_DS1302() //从DS1302中读取一个字节{uchari,byte,t;for(i=0;i<8;i++){ byte>>=1; t=SDA; byte|=t<<7;CLK=1; CLK=0; } //BCD码转换 returnbyte/16*10+byte%16;}//-------------------------------------------------------------//从DS1302指定位置读取数据 ,读数据//---------------------------------------------------------------ucharRead_Data_FROM_DS1302(ucharaddr)//从DS1302指定位置读取数据 ,读数据{uchardat; RST=0; CLK=0; RST=1; Write_Byte_TO_DS1302(addr);//向DS1302写入一个地址 dat=Read_Byte_FROM_DS1302();//在上面写入的地址中读取数据 CLK=1; RST=0; returndat;}//-------------------------------------------------------------//向DS1302指定位置写入数据,写数据//---------------------------------------------------------------voidWrite_Data_TO_DS1302(ucharaddr,uchardat)//向DS1302指定位置写入数据,写数据{CLK=0; RST=1; Write_Byte_TO_DS1302(addr); Write_Byte_TO_DS1302(dat); CLK=1; RST=0;}//-----------------------------------------------------------//设置时间//---------------------------------------------------------voidSET_DS1302() //设置时间{uchari;Write_Data_TO_DS1302(0x8E,0x00);//写控制字,取消写保护//分,时,日,月,年依次写入for(i=1;i<7;i++){ //分的起始地址是10000010(0x82),后面依次是时,日,月,周,年,写入地址每次递增2 Write_Data_TO_DS1302(0x80+2*i,(DateTime[i]/10<<4)|(DateTime[i]%10)); }Write_Data_TO_DS1302(0x8E,0x80);//写控制字,加写保护}//--------------------------------------------------------//读取当前时间//-------------------------------------------------------voidGetTime() //读取当前时间{uchari;for(i=0;i<7;i++){ DateTime[i]=Read_Data_FROM_DS1302(0x81+2*i); }}//------------------------------------------------------------//日期与时间值转换为数字字符//-------------------------------------------------------------voidFormat_DateTime(uchard,uchar*a){ a[0]=d/10+'0'; a[1]=d%10+'0';}//-------------------------------------------------------------------//判断是否为闰年//-------------------------------------------------------------------ucharIs_Leapyear(uintyear){return(year%4==0&&year%100!=0)||(year%400==0);}//------------------------------------------------------------------// 求自2000.1.1开始的任何一天是星期几?//----------------------------------------------------------------------voidRefresh_Week_Day(){uinti,d,w=5;//1999年12.31是星期五for(i=2000;i<2000+DateTime[6];i++){ d=Is_Leapyear(i)?366:365; w=(w+d)%7; }d=0;for(i=1;i<DateTime[4];i++){d+=MonthsDays[i];}d+=DateTime[3];//保存星期,0-6表示星期日,星期一至星期六,为了与DS1302的星期格式匹配,返回值需要加1DateTime[5]=(w+d)%7+1;}//-------------------------------------------------//年,月,日和时,分++/--//----------------------------------------------------voidDatetime_Adjust(charX){switch(Adjust_Index){ case6: //年调整,00-99 if(X==1&&DateTime[6]<99) { DateTime[6]++; } if(X==-1&&DateTime[6]>0) { DateTime[6]--; } //获取2月天数 MonthsDays[2]=Is_Leapyear(2000+DateTime[6])?29:28; //如果年份变化后当前月份的天数大于上限那么设为上限 if(DateTime[3]>MonthsDays[DateTime[4]]) { DateTime[3]=MonthsDays[DateTime[4]]; } Refresh_Week_Day();//刷新星期 break; case4://月调整01-12 if(X==1&&DateTime[4]<12) { DateTime[4]++; } if(X==-1&&DateTime[4]>1) { DateTime[4]--; } //获取2月天数 MonthsDays[2]=Is_Leapyear(2000+DateTim

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论