/***********************************************************************
* 说明:这是用EDC190作为显示的电子钟和温度计的代码.它的功能是: 一 *
* 般状态下,它以24小时制显示时间(只显示时和分),并且小时和分钟之间的 *
* 冒号闪烁.板子正面的圆柱形晶振是32.768k,用于实时时钟芯片DS1302. *
* 板子背面的晶振是12M,用于单片机的起振.单片机采用的是STC89RC, *
* LQFP44封装,体积较小.但因为没有编程器,所以板子上留下了四个插针,用 *
* 于给单片机下载程序.插针左边的是电位器,用于调节液晶屏的显示亮度, *
* 如果知道HT1621B的VLCD的电压的话,可以选择一个合适的电阻, 一端 *
* 接VCC,另一端接VLCD引脚,这样的话,可以使板子做得更小一些. 左侧有 *
* 两个电源输入端,一个是DC2.1接口,另一个是MINI_USB接口, 任接一个 *
* 都可,输入电压是5V.时钟芯片和温度芯片分别是美国DALLAS公司的 *
* DS1302和NXP公司的温度范围在-55°C to 125°C的LM75A,.板子背面 *
* 还有一个芯片是台湾合泰公司的HT1621B,用于液晶的显示.板子下面的四个 *
* 按键有两个功能,一是用于调整时间,另一个用用于显示年月日等其它信息. *
* 四个按键从左到右分别是S1,S2,S3,S4. *
* 先说S4,按下S4键液晶停止显示时间,开始依次显示年;月,日;星期;温度. *
* 每个项目显示时间大约两秒.然后又回到正常显示时间状态.S1是功能键, *
* S1键可以用来调整年月日星期和时间(时,分).S2和S3分别是加减键,当调 *
* 整时间时,可用S2进行加,S3进行减.下面说明如何调整时间. *
* 第一次按下S1键,进入调整年的状态,可以用S2或S3进行加减的调整. *
* 第二次按下S1键,进入调整月的状态,调整方法同上. *
* 第三次按下S1键,进入调整日的状态,调整方法同上. *
* 第四次按下S1键,进入调整星期的状态,调整方法同上. *
* 第五次接下S1键,进入调整小时的状态,调整方法同上. *
* 第六次接下S1键,进入调整分钟的状态,调整方法同上. *
* 再次按下S1键,调整完毕,程序将调整好的现在的时间写入寄存器,时钟将 *
* 按现在调整好的时间进行走时.因为DS1302时钟芯片只支持2000~2099年, *
* 因此程序也只允许年的调整在这个范围内.DS1302有闰年和月的补偿功能 *
* ,能自动进行调整,因此,程序中也进行了大量的判断,以保证用户不能调整 *
* 出一个错误的时间,例如,4月31日,2月30日等等不可能出现的日期.但当用 *
* 户正确调整好年月日后,再调整星期时,如果用户调整的星期数不是正确的 *
* 星期数,那么程序将无法判断,比如某年某月某日是星期三,用户却把它调 *
* 整成星期四或星期一,那么程序将无法判断.因为程序也不知道那天到底是 *
* 星期几.它只是接用户的意愿将调整好的时间写入寄存器.至于DS1302声称 *
* 它有自动补偿功能,能否在一定时间后将错误修正,本人没有经过测试,尚 *
* 无从知晓. *
* PS:经过测试,时钟行走正常,电位器可调整液晶屏对比度.走时偏快一些,大约 *
* 两天能差一分钟.温度显示正常,我把它放在冰霜里测试,能正常显示零下的 *
* 温度. *
************************************************************************/
#include <reg52.h>
#include <intrins.h> /* for _nop_() */
#include <string.h> /* for strcpy etc. */
#include "Zh_Type.h" /* for uint8, uint16 etc. */
#include "def.h" /* for const, sbit definition */
/* clear display */
uint8 code Ht1621Tab[]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
/************************************************************************
* 这个表要说明一下.编码是液晶主要显示用的编码0~9.这个编码是经过调 *
* 整过的,编码0本应是0xfc,编码1本应是0x60等等.由于硬件连接和移位 *
* 显示的需要,才把原来的编码的高四位和低四位对调.因为显示有用的位是 *
* D3 (00001000b,就是为1的位).为了显示时移位的方便和移位次数的最少, *
* 编码就成了现在这个样子. *
************************************************************************/
/* 0~9 */
uint8 code tab[]={
0xcf,0x06,0xad,0x2f,0x66,
0x6b,0xeb,0x0e,0xef,0x6f};
uint8 first,second,third,fourth; /*每位液晶显示的数码0~9*/
bit egative; /*温度正负的标志位*/
uint8 colon; /*冒号*/
uint8 getTimebuf[7]; /*用于存放获取的时间数据*/
uint8 setTimebuf[7]; /*用于存放要设置的时间日期数据*/
uint8 dis[4]; /*BCD码翻译成显示数字*/
uint8 adj; /*调节时间标志*/
/****************** OTHERS ***********************/
void delay() /*延时*/
{;;}
void DelayMS(uint16 iMS) /* delay ms */
{
uint16 i,j;
for(i=0;i<iMS;i++)
for(j=0;j<65;j++)
delay();
}
/************************************************************************
* 说明:先来说一下主函数中的对DS1302的初始化.DS1302工作前需要进行 *
* 初始化,调用的是Init1302函数.但初始化只能是一次,即如果时间没有走, *
* 就初始化,假如时钟在走,那么就不进行初始化.如何判断时钟是否在走呢?第 *
* 一次进行初始化时,就在芯片的RAM单元中写入数据,在这里写了两个单元, *
* 分别写入19和65.每次启动时,都去读这两个单元,如果这两个单元是19和 *
* 65,说明己经初始化过,此时就无须再初始化,否则,对DS1302进行初始化. *
* PS:很多**中说,靠读取秒寄存器也可以知道DS1302是否经过初始化, *
*那就是判断它的D7位. *
************************************************************************/
/****************** DS1302 *************************/
void WriteByte(uint8 ucData) /*向DS1302写一个字节*/
{
uint8 i,dat;
dat=ucData;
DS1302_CLK = 0;
for(i=0;i<8;i++)
{
dat=dat>>1;
DS1302_IO = CY;
DS1302_CLK = 1;
DS1302_CLK = 0;
}
}
uint8 ReadByte(void) /* 从DS1302读一个字节 */
{
uint8 i,val;
for(i=0;i<8;i++)
{
val = val>>1;
if(DS1302_IO)
val|=0x80;
DS1302_CLK = 1;
DS1302_CLK = 0;
}
return(val);
}
void Write1302(uint8 ucAddr, uint8 ucDa)/* 向DS1302指定地址写一个字节数据 */
{
DS1302_RST = 0;
DS1302_CLK = 0; /* 只有在CLK为低电平时,才能将RST置为高电平 */
DS1302_RST = 1;
WriteByte(ucAddr);
WriteByte(ucDa);
DS1302_CLK = 1;
DS1302_RST =0;
}
uint8 Read1302(uint8 ucAddr) /* 从DS1302指定地址读取一个字节数据 */
{
uint8 val;
DS1302_RST = 0;
DS1302_CLK = 0; /* 只有在CLK为低电平时,才能将RST置为高电平 */
DS1302_RST = 1;
WriteByte(ucAddr);
val=ReadByte();
DS1302_CLK = 1;
DS1302_RST =0;
return(val);
}
/*******************************************
* 设置DS1302时间数据,传入的数组中 *
* 格式为:秒 分 时 日 月 星期 年 *
*******************************************/
void SetTime(uint8 *pTime)
{
uint8 i;
uint8 ucAddr = 0x80;
Write1302(0x8e,0x00); /* 控制命令,WP=0,允许写操作 */
for(i=0;i<7;i++)
{
Write1302(ucAddr,*pTime); /* 秒 分 时 日 月 星期 年 */
pTime++;
ucAddr +=2;
}
Write1302(0x8e,0x80); /* 控制命令,WP=1,写保护 */
}
/************************************************
* 从DS1302读取时间数据,返回到传入的数组 *
* 中格式为:秒 分 时 日 月 星期 年 *
************************************************/
void GetTime(uint8 ucCurtime[])
{
uint8 i,ucAddr;
ucAddr = 0x81;
for (i=0;i<7;i++)
{
ucCurtime[i] = Read1302(ucAddr);
ucAddr += 2;
}
}
void Init1302(void) /*初始化DS1302*/
{
uint8 flag1,flag2;
flag1=Read1302(0xc1); /*读取DS1302的RAM*/
flag2=Read1302(0xc3);
if((flag1!=0x19)&&(flag2!=0x65))
{
Write1302(0x8e,0x00); /*控制写入WP=0*/
//Write1302(0x90,0xa5); /*辅助电源充电命令*/
Write1302(0x80,0x00); /*写秒*/
Write1302(0x82,0x53); /*写分*/
Write1302(0x84,0x16); /*写时*/
Write1302(0x86,0x06); /*写日*/
Write1302(0x88,0x09); /*写月*/
Write1302(0x8a,0x05); /*写星期*/
Write1302(0x8c,0x13); /*写年*/
Write1302(0xc0,0x19); /*写入标志位*/
Write1302(0xc2,0x65); /*写入标志位*/
Write1302(0x8e,0x80); /*写保护WP=1*/
}
}
/****************** LM75A *************************/
void start() /*IIC开始*/
{
LM75A_SDA=1;
delay();
LM75A_SCL=1;
delay();
LM75A_SDA=0;
delay();
}
void stop() /*IIC停止*/
{
LM75A_SDA=0;
delay();
LM75A_SCL=1;
delay();
LM75A_SDA=1;
delay();
}
void respons() /*IIC从机应答*/
{
uint8 i;
LM75A_SCL=1;
delay();
while((LM75A_SDA==1)&&(i<250))
i++;
LM75A_SCL=0;
delay();
}
void ack() /*IIC主机应答*/
{
LM75A_SDA=0;
LM75A_SCL=1;
delay();
LM75A_SCL=0;
LM75A_SDA=1;
}
void nack() /*主机非应答*/
{
LM75A_SDA=1;
LM75A_SCL=1;
delay();
LM75A_SCL=0;
LM75A_SDA=0;
}
void write_byte(uint8 date) /*IIC写一个字节*/
{
uint8 i,temp;
temp=date;
for(i=0;i<8;i++)
{
temp=temp<<1;
LM75A_SCL=0;
delay();
LM75A_SDA=CY;
delay();
LM75A_SCL=1;
delay();
}
LM75A_SCL=0;
delay();
LM75A_SDA=1;
delay();
}
uint8 read_byte() /*IIC读一个字节*/
{
uint8 i,k;
LM75A_SCL=0;
delay();
LM75A_SDA=1;
delay();
for(i=0;i<8;i++)
{
LM75A_SCL=1;
delay();
k=(k<<1)|LM75A_SDA;
LM75A_SCL=0;
delay();
}
return k;
}
void gettemp() /*读取温度值*/
{
uint8 temph,templ;
uint16 temp,value;
float ftemp;
start();
write_byte(0x9e); /*10011110*/
respons();
write_byte(0x00);
respons();
start();
write_byte(0x9f); /*10011111*/
respons();
temph=read_byte(); /*先读到高字节*/
ack();
templ=read_byte(); /*再读到低字节*/
nack();
stop();
temp=temph;
temp<<=8;
temp=temp+templ; /*合并*/
temp=temp>>7; /*高9位有效*/
if(temp&0x100) /*零下*/
{
temp=~temp;
temp&=0xff;
temp++;
ftemp=temp/2.0;
negative=1;
}
else
{
ftemp=(temp&0xff)/2.0; /*LM75精度是0.5度*/
negative=0;
}
value=ftemp*10; /*得到每位数码*/
first=value/1000;
second=value%1000/100;
third=value%100/10;
fourth=value%10;
}
void initLM75A() /*初始化LM75A*/
{
LM75A_SDA=1;
delay();
LM75A_SCL=1;
delay();
}
/****************** EDC190 ***********************/
/************************************************
写数据函数,cnt为传送数据位数,数据传送为低位在前
************************************************/
void Ht1621Wr_Data(uint8 Data,uint8 cnt)
{
uint8 i;
for(i=0;i<cnt;i++)
{
HT1621_WR=0;
_Nop();
HT1621_DAT=Data&0x80;
_Nop();
HT1621_WR=1;
_Nop();
Data<<=1;
}
}
/************************************
函数名称:void Ht1621WrCmd(uint8 Cmd)
功能描述:HT1621命令写入函数
全局变量:无
参数说明:Cmd为写入命令数据
返回说明:无
说明 :写入命令标识位100
************************************/
void Ht1621WrCmd(uint8 Cmd)
{
HT1621_CS=0;
_Nop();
Ht1621Wr_Data(0x80,4); /* 写入命令标志100 */
Ht1621Wr_Data(Cmd,8); /* 写入命令数据 */
HT1621_CS=1;
_Nop();
}
/********************************************************
函数名称:void Ht1621WrOneData(uint8 Addr,uint8 Data)
功能描述:HT1621在指定地址写入数据函数
全局变量:无
参数说明:Addr为写入初始地址,Data为写入数据
返回说明:无
说明 :因为HT1621的数据位4位,所以实际写入数据为参数的后4位
********************************************************/
void Ht1621WrOneData(uint8 Addr,uint8 Data)
{
HT1621_CS=0;
Ht1621Wr_Data(0xa0,3); /* 写入数据标志101 */
Ht1621Wr_Data(Addr<<2,6); /* 写入地址数据 */
Ht1621Wr_Data(Data<<4,4); /* 写入数据 */
HT1621_CS=1;
_Nop();
}
/**************************************************************
函数名称:void Ht1621WrAllData(uint8 Addr,uint8 *p,uint8 cnt)
功能描述:HT1621连续写入方式函数
全局变量:无
参数说明:Addr为写入初始地址,*p为连续写入数据指针,
cnt为写入数据总数
返回说明:无
说明 :HT1621的数据位4位,此处每次数据为8位,写入数据
总数按8位计算
**************************************************************/
void Ht1621WrAllData(uint8 Addr, uint8 *p,uint8 cnt)
{
uint8 i;
HT1621_CS=0;
Ht1621Wr_Data(0xa0,3); /* 写入数据标志101 */
Ht1621Wr_Data(Addr<<2,6); /* 写入地址数据 */
for(i=0;i<cnt;i++)
{
Ht1621Wr_Data(*p,8); /* 写入数据 */
p++;
}
HT1621_CS=1;
_Nop();
}
/***************************************
函数名称:void Ht1621_Init(void)
功能描述:HT1621初始化
全局变量:无
参数说明:无
返回说明:无
说明 :无
***************************************/
void Ht1621_Init(void)
{
HT1621_CS=1;
HT1621_WR=1;
HT1621_DAT=1;
DelayMS(2000); /* 延时使LCD工作电压稳定 */
Ht1621WrCmd(BIAS);
Ht1621WrCmd(RC256); /* 使用内部振荡器 */
Ht1621WrCmd(SYSDIS);
Ht1621WrCmd(WDTDIS);
Ht1621WrCmd(SYSEN);
Ht1621WrCmd(LCDON);
Ht1621WrAllData(0,Ht1621Tab,16); /* 清除1621寄存器数据,即清屏 */
}
/***************************************
函数名称:void Ht1621write(uint8 loc,uint8 num)
功能描述:EDC190显示函数
全局变量:无
参数说明:
loc:显示位置 1~4
num:要显示的数 0~9
返回说明:无
***************************************/
void Ht1621write(uint8 loc,uint8 number)
{
uint8 shift;
shift=tab[number];
switch(loc)
{
case 1:
Ht1621WrOneData(0x00,(shift=_crol_(shift,0)));
Ht1621WrOneData(0x01,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x02,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x03,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x04,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x05,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x06,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x07,(shift=_crol_(shift,1))); //DP1
break;
case 2:
Ht1621WrOneData(0x08,(shift=_crol_(shift,0)));
Ht1621WrOneData(0x09,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x0A,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x0B,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x0C,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x0D,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x0E,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x0F,(shift=_crol_(shift,1))); //DP2
break;
case 3:
Ht1621WrOneData(0x10,(shift=_crol_(shift,0)));
Ht1621WrOneData(0x11,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x12,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x13,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x14,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x15,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x16,(shift=_crol_(shift,1)));
//Ht1621WrOneData(0x17,(shift=_crol_(shift,1))); //colon
break;
case 4:
Ht1621WrOneData(0x18,(shift=_crol_(shift,0)));
Ht1621WrOneData(0x19,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x1A,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x1B,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x1C,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x1D,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x1E,(shift=_crol_(shift,1)));
Ht1621WrOneData(0x1F,(shift=_crol_(shift,1))); //DP3
break;
}
}
void Display() /* 液晶显示 */
{
Ht1621write(1,dis[0]);
Ht1621write(2,dis[1]);
Ht1621write(3,dis[2]);
Ht1621write(4,dis[3]);
}
void ExtraDisplay() /*装入显示数据(年月日星期温度)*/
{
Ht1621WrAllData(0,Ht1621Tab,16); /* 清除1621寄存器数据,即清屏 */
dis[0]=2; /*year*/
dis[1]=0;
dis[2]=getTimebuf[6]/16;
dis[3]=getTimebuf[6]%16;
Display();
DelayMS(2000);
dis[0]=getTimebuf[4]/16; /*month,day*/
dis[1]=getTimebuf[4]%16;
dis[2]=getTimebuf[3]/16;
dis[3]=getTimebuf[3]%16;
Display();
DelayMS(2000);
dis[0]=0; /*week*/
dis[1]=0;
dis[2]=0;
dis[3]=getTimebuf[5];
Display();
DelayMS(2000);
/*temperature*/
gettemp();
if(negative) /*如果是零下,显示负号*/
{
Ht1621WrAllData(0,Ht1621Tab,16); /* 清除1621寄存器数据,即清屏 */
dis[1]=second;
dis[2]=third;
dis[3]=fourth;
Ht1621WrOneData(0x06,0x08); /* 第一位写负号 */
Ht1621write(2,dis[1]);
Ht1621write(3,dis[2]);
Ht1621write(4,dis[3]);
Ht1621WrOneData(0x1F,0x08); /* DP3 */
DelayMS(2000);
}
else /*否则,显示一位小数*/
{
dis[0]=first;
dis[1]=second;
dis[2]=third;
dis[3]=fourth;
Display();
Ht1621WrOneData(0x1F,0x08); /* DP3 */
DelayMS(2000);
}
}
void NormalDisplay() /*正常显示时间(时分)*/
{
dis[0]=(getTimebuf[2])/16;
dis[1]=(getTimebuf[2])%16;
dis[2]=(getTimebuf[1])/16;
dis[3]=(getTimebuf[1])%16;
Display();
}
uint8 BCDtoDEC(uint8 bcd) /* 压缩BCD码转换为十进制数 */
{
return (bcd/16*10+bcd%16);
}
uint8 DECtoBCD(uint8 dec) /* 十进制数转换为压缩BCD码 */
{
return (dec/10*16+dec%10);
}
|