一、Proteus仿真演示
二、代码特点
三、开发环境介绍
本实验代码基于Keil C51,仿真软件:Protues_8.9
四、主要元件介绍
1、单片机:AT89C51或STC89C51
2、实时时钟芯片:DS1302
1)DS1302介绍
它是DALLAS(达拉斯)公司出的一款涓流充电时钟芯片,2001年DALLAS被MAXIM(美信)收购,因此我们看到的DS1302的数据手册既有DALLAS的标志,又有MAXIM的标志;
它的主要性能指标如下:
(1)DS1302是一个实时时钟芯片,可以提供秒、分、小时、日期、月、年等信息,并且还有闰年自动调整的能力,可以通过配置AM/PM来决定采用24小时格式还是12小时格式。
(2)拥有31字节数据存储RAM。
(3)串行I/O通信方式,相对并行来说比较节省IO口的使用。
(4)DS1302的工作电压比较宽,大概是2.0V~5.5V都可以正常工作。采用双电源供电,当主电源比备用电源高0.2V时,由主电源供电,否则采用备用电源,一般是一个纽扣电池。
(5)DS1302这种时钟芯片功耗一般都很低,它在工作电压2.0V的时候,工作电流小于300nA。
(6)DS1302共有8个引脚,有两种封装形式,一种是DIP-8封装,芯片宽度(不含引脚)是300mil,一种是SOP-8封装,有两种宽度,一种是150mil,一种是208mil。
------------------------------
2)引脚定义
------------------------------
3)外围电路
一般与单片机IO口相连时要加上拉电阻,提高 IO 口的驱动能力,这样信号比较稳定,计时也比较准确。
------------------------------
4)分析时序图
这是单字节写入的时序图,可见,先拉高使能端,进行使能选择,然后在时钟上升沿写入一个字节。
DS1302在进行读写操作时最少读写两个字节,第一个是控制字节,就是一个命令,说明是读还是写操作,第二个时需要读写的数据。
对于单字节写,只有在SCLK为低电平时才能将 CE 置高电平,所以刚开始将SCLK 置低,CE置高,然后把需要写入的字节送入 IO口,然后跳变SCLK,在SCLK下降沿时,写入数据。
------------------------------------------------------------
3、LCD1602
Proteus里,LCD1602就是LM016L,除了没有背光灯电源外用法一致。
1)LCD1602概述
字符型液晶显示模块是一种专门用于显示字母、数字、符号等点阵式 LCD,目前常用 16*1、16*2、20*2 与40*2 行等的模块。硬件如图:
------------------------------
2)1602LCD 主要技术参数
显示容量:16×2 个字符
芯片工作电压:4.5~5.5V
工作电流:2.0mA(5.0V)
模块最佳工作电压:5.0V
------------------------------
3)引脚定义
PIN1:VSS 为地电源。
PIN2:VDD 接 5V 正电源。
PIN3:VL 为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个 10K 的电位器调整对比度。
PIN4:RS 为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。
PIN5:R/W 为读写信号线,高电平时进行读操作,低电平时进行写操作。当 RS 和 R/W共同为低电平时可以写入指令或者显示地址,当 RS 为低电平 R/W 为高电平时可以读忙信号,当 RS 为高电平 R/W 为低电平时可以写入数据。
PIN6:E 端为使能端,当 E 端由高电平跳变成低电平时,液晶模块执行命令。
PIN7~PIN14:D0~D7 为 8 位双向数据线。
PIN15:背光源正极。
PIN16:背光源负极。
------------------------------
4)指令集
LCD1602 液晶模块的读写操作、屏幕和光标的操作都是通过指令编程来实现的。(说明: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:读数据。
------------------------------
5)如何显示
液晶显示模块是一个慢显示器件,所以在执行每条指令之前一定要确认模块的忙标志为低电平,表示不忙,否则此指令失效。要显示字符时要先输入显示字符地址,也就是告诉模块在哪里显示字符,下图是 LCD1602 的内部显示地址。
例如第二行第一个字符的地址是 40H,那么是否直接写入 40H 就可以将光标定位在第二行第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位 D7 恒定为高电平 1 所以实际写入的数据应该是 01000000B(40H)+10000000B(80H)=11000000B(C0H)。
1602LCD 的一般初始化(复位)过程:
延时 15mS
写指令 38H(不检测忙信号)
延时 5mS
写指令 38H(不检测忙信号)
延时 5mS
写指令 38H(不检测忙信号)
以后每次写指令、读/写数据操作均需要检测忙信号
写指令 38H:显示模式设置
写指令 08H:显示关闭
写指令 01H:显示清屏
写指令 06H:显示光标移动设置
写指令 0CH:显示开及光标设置
五、原代码
/********************************************************
程序名:main.C
版 本:Ver1.0
芯 片:AT89C51
晶 体:片外12MHz
编 程: liht1634@163.com
日 期:2023-7-3
描 述:本实例通过LCD1602液晶显示DS1302实时时钟
C编译器:Keil C51 V9.54
注意事项:
重要事项:
********************************************************/
#include
#include
#include
#define uint unsigned int
#define uchar unsigned char
/***************************************************************************/
//LCD
sbit LCDRS=P2^0; //LCD数据/命令选择端
sbit LCDRW=P2^1;
sbit LCDEN=P2^2; //LCD使能新号端
//DS1302
sbit IO=P1^0; //1302数据线
sbit SCLK=P1^1; //1302时钟线
sbit RST=P1^2; //复位
uint num=0;
//uchar *week[]={"SUN","***","MON","TUS","WEN","THU","FRI","SAT"};
uchar Date[]={"Date: 2000-00-00 "};
uchar Time[]={"Time: *00:00-00* "};
uchar date_time[7]; //从ds1302读取的当前日期时间
/***************************************************************************/
//延时
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
//向1302写数据
void write_ds1302(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
IO=dat & 0x01; //保持最后一位为1,读状态
SCLK=1;
delay(1);
SCLK=0;
dat >>= 1;
}
}
//*********从1302读数据*********
uchar read_ds1302()
{
uchar i,b=0x00;
for(i=0;i<8;i++)
{
b |= _crol_((uchar)IO,i);
SCLK=1;
delay(1);
SCLK=0;
}
return b/16*10+b%16; //与BCD码转换
}
//*********从指定位置读数据********
uchar read_data(uchar addr)
{
uchar dat;
RST=0;
SCLK=0;
RST=1;
write_ds1302(addr);
dat=read_ds1302();
SCLK=1;
RST=0;
return dat;
}
//*********读取当前日期时间*********
void read_date_time()
{
uchar i,addr=0x81;
for(i=0;i<7;i++)
{
date_time[i]=read_data(addr);
addr+=2;
}
}
//LCD写指令
void lcd_write_com(uchar com)
{
LCDRS=0; //RS为0时,写指令,RS为1时,写数据
P0=com;
delay(5);
LCDEN=1;
delay(5);
LCDEN=0;
}
//LCD写数据
void lcd_write_data(uchar dat)
{
LCDRS=1;
P0=dat;
delay(5);
LCDEN=1;
delay(5);
LCDEN=0;
}
//LCD初始化
void Lcd_init()
{
LCDEN=0;
lcd_write_com(0x38); //LCD显示模式设置
lcd_write_com(0x0c); //LCD显示开/关及光标设置
lcd_write_com(0x06); //当写一个字符后地址指针加1,且光标加1
lcd_write_com(0x01); //显示清屏
}
//设置液晶显示位置
void set_lcd_pos(uchar p)
{
lcd_write_com(p | 0x80);
}
//液晶显示程序
void lcd_print(uchar p,uchar *s)
{
set_lcd_pos(p);
for(num=0;num<16;num++)
{
lcd_write_data(s[num]);
delay(10);
}
}
//日期时间转换
void format_datetime(uchar d,uchar *p)
{
p[0]=d/10+'0';
p[1]=d%10+'0';
}
/***************************************************************************/
//主程序
void main()
{
LCDRW=0;
Lcd_init(); //LCD初始化
while(1)
{
read_date_time();
format_datetime(date_time[6],Date+8); //年月日
format_datetime(date_time[4],Date+11);
format_datetime(date_time[3],Date+14);
// strcpy(Date+13,week[date_time[5]]);
format_datetime(date_time[2],Time+7); //时间转换
format_datetime(date_time[1],Time+10);
format_datetime(date_time[0],Time+13);
lcd_print(0x00,Date);
lcd_print(0x40,Time);
}
}
/***************************************************************************/
六、实例文件下载
链接:https://pan.baidu.com/s/1HrEgRBdajNhLlaemArarxA
提取码:2m8s