对了,这个程序编译之后大于8k(大概11k多点),大家可以下载个“HEX2BIN”程序,能将hex文件转化成bin文件,我在后面附上它了。只要将生成的hex文件拉到这个程序上就可以生成bin文件了。这样可以减小编译生成的文件大小。其实我试验了一下,下面这个程序编译生成的hex在8052的单片机上还是可以装下的。。。哈哈~~
由于空间写日志复制的时候没办法完全按照keil编译器里的格式对齐,所以朋友们还是将就着看了,抱歉。。。
/*******************************************************************
*名称:基于LCD12864的多功能数字电子钟
*功能:实现了基本显示(包括年、月、日、时、分、秒、星期、当前室温)
* 和校时功能,并能够设定闹铃(按key3确定键停止响铃)。
*单位:东莞理工学院07电子信息工程
*作者:胡**
*日期:2009年8月22日 *版本:Vision 1.10
*更新:相对于v1.00版本增加了温度显示并修复了部分bug
********************************************************************/
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit dula="P2"^6; //定义数码管段选端
sbit wela="P2"^7; //定义数码管位选端
sbit lcden="P3"^4; //定义lcd使能端
sbit lcdrs="P3"^5; //定义lcd数据命令选择端
sbit lcdwr="P3"^6; //定义lcd读写选择端
sbit lcdrd="P3"^7; //定义并串选择端
sbit beep="P2"^3; //定义蜂鸣器端口
sbit key0=P3^0; //定义key0
sbit key1=P3^1; //定义key1
sbit key2=P3^2; //定义key2
sbit key3=P3^3; //定义key3
sbit DQ="P2"^2; //定义DS18B20数据线引脚端
bit flag_alarm;
uchar high,low;
uint i,j,temp;
float f_temp;
char a,d2,d1,d0,t0,k0,k1,mon=12,day=31,hour=23,min=59,sec=50,temp_day,hour_alarm=0,min_alarm=0;
int year="2009";
uchar code line1[]="20 年 月 日";
uchar code line2[]=" 时 分 秒";
uchar code line3[]="星期 ";
uchar code line4[]="当前室温:00.0度";
uchar code alarm2[]="闹铃时间:";
uchar code alarm3[]=" 时 分";
uchar code week0[]="日";
uchar code week1[]="一";
uchar code week2[]="二";
uchar code week3[]="三";
uchar code week4[]="四";
uchar code week5[]="五";
uchar code week6[]="六";
//-------------------毫秒级延时函数-------------------------
void delay_ms(uint z) //延时z毫秒
{
uint x,y;
for(x=z;x>0;x--)
for(y=115;y>0;y--);
}
//----------------微妙级延时函数---------------------
void delay_us(uint z) //延时z*10us
{
uint x,y;
for(x=z;x>0;x--)
for(y=1;y>0;y--);
}
//---------------蜂鸣器发声函数--------------------------
void beep_pro(char be) //产生几种不同频率声音
{
switch(be)
{
case 0: //用于闹铃
{
for(i=50;i>0;i--)
{
for(j=2;j>0;j--)
{
beep=~beep;
delay_us(1);
}
}
}
case 1: //用于整点前报时
{
for(i=1250;i>0;i--)
{
beep=~beep;
delay_us(20);
}
}break;
case 2: //用于整点报时
{
for(i=2500;i>0;i--)
{
beep=~beep;
delay_us(10);
}
}break;
}
}
//------------lcd写指令函数-------------
void wr_cmd(uchar cmd)
{
lcden=0;
lcdrs=0;
P0=cmd;
delay_ms(1);
lcden=1;
delay_ms(1);
lcden=0;
}
//-----------lcd写数据函数-----------
void wr_dat(uchar dat)
{
lcden=0;
lcdrs=1;
P0=dat;
delay_ms(1);
lcden=1;
delay_ms(1);
lcden=0;
}
//-------------星期计算及显示函数--------------
void week_day(int y,char m,char d) //根据已知的日期计算星期几(适用2001年~2099年)
{
//-------------------------以下部分为蔡勒公式-------------------------------------
/*c是世纪数减一,y是年份后两位,m是月份(从3月开始,1月和2月要按上一年的13月和14月
来算,这时c和y均按上一年取值),d是日数。w 等于多少就是星期几,等于0即为星期天*/
// 亦可不用此公式,当day自加时,星期数也自加
char w,c,a;
if(m==1||m==2)
{
m=m+12;
y=y-1;
}
c=20;
y=y%100;
w=(y+y/4+c/4-2*c+26*(m+1)/10+d-1)%7;
if(w<0||w>6)
w=w+7;
//-------------------------------------------------------------------------------
switch(w) //根据计算得到的值显示到lcd相应位置上
{
case 0:
{
wr_cmd(0x8d); //设置第三行第三个汉字地址为起始位
for(a=0;a<2;a++) //写入第一行数据
{
wr_dat(week0[a]);
}
break;
}
case 1:
{
wr_cmd(0x8d); //设置第三行第三个汉字地址为起始位
for(a=0;a<2;a++) //写入第一行数据
{
wr_dat(week1[a]);
}break;
}
case 2:
{
wr_cmd(0x8d); //设置第三行第三个汉字地址为起始位
for(a=0;a<2;a++) //写入第一行数据
{
wr_dat(week2[a]);
}break;
}
case 3:
{
wr_cmd(0x8d); //设置第三行第三个汉字地址为起始位
for(a=0;a<2;a++) //写入第一行数据
{
wr_dat(week3[a]);
}break;
}
case 4:
{
wr_cmd(0x8d); //设置第三行第三个汉字地址为起始位
for(a=0;a<2;a++) //写入第一行数据
{
wr_dat(week4[a]);
}break;
}
case 5:
{
wr_cmd(0x8d); //设置第三行第三个汉字地址为起始位
for(a=0;a<2;a++) //写入第一行数据
{
wr_dat(week5[a]);
}break;
}
case 6:
{
wr_cmd(0x8d); //设置第三行第三个汉字地址为起始位
for(a=0;a<2;a++) //写入第一行数据
{
wr_dat(week6[a]);
}break;
}
}
}
//-----------------------用户界面文字显示函数--------------------------
void face_dis()
{
wr_cmd(0x30); //选择功能:8位数据基本指令操作
wr_cmd(0x0c); //整体显示开,不显示光标
wr_cmd(0x01); //lcd清屏
wr_cmd(0x02); //游标归原点
wr_cmd(0x80); //设置第一行第二个汉字地址为起始位
for(a=0;a<14;a++) //写入第一行数据
{
wr_dat(line1[a]);
}
wr_cmd(0x91); //设置第二行起始位
for(a=0;a<12;a++) //写入第二行数据
{
wr_dat(line2[a]);
}
wr_cmd(0x8b); //设置第三行第三个汉字地址为起始位
for(a=0;a<6;a++) //写入第三行数据
{
wr_dat(line3[a]);
}
wr_cmd(0x98); //设置第四行起始位
for(a=0;a<16;a++) //写入第四行数据
{
wr_dat(line4[a]);
}
wr_cmd(0x81); //显示年
wr_dat(0x30+year%100/10);
wr_dat(0x30+year%100%10);
wr_cmd(0x83); //显示月
wr_dat(0x30+mon/10);
wr_dat(0x30+mon%10);
wr_cmd(0x85); //显示日
wr_dat(0x30+day/10);
wr_dat(0x30+day%10);
wr_cmd(0x91); //显示时
wr_dat(0x30+hour/10);
wr_dat(0x30+hour%10);
wr_cmd(0x93); //显示分
wr_dat(0x30+min/10);
wr_dat(0x30+min%10);
wr_cmd(0x95); //显示秒
wr_dat(0x30+sec/10);
wr_dat(0x30+sec%10);
wr_cmd(0x9d); //显示温度
wr_dat(0x30+d2);
wr_dat(0x30+d1);
wr_dat(0x2e); //写入“.”号
wr_dat(0x30+d0);
}
//------------------------用户界面数据显示函数-------------------------------
void dat_dis()
{
wr_cmd(0x81); //显示年
wr_dat(0x30+year%100/10);
wr_dat(0x30+year%100%10);
wr_cmd(0x83); //显示月
wr_dat(0x30+mon/10);
wr_dat(0x30+mon%10);
wr_cmd(0x85); //显示日
wr_dat(0x30+day/10);
wr_dat(0x30+day%10);
wr_cmd(0x91); //显示时
wr_dat(0x30+hour/10);
wr_dat(0x30+hour%10);
wr_cmd(0x93); //显示分
wr_dat(0x30+min/10);
wr_dat(0x30+min%10);
wr_cmd(0x95); //显示秒
wr_dat(0x30+sec/10);
wr_dat(0x30+sec%10);
wr_cmd(0x9d); //显示温度
wr_dat(0x30+d2);
wr_dat(0x30+d1);
wr_dat(0x2e); //写入“.”号
wr_dat(0x30+d0);
}
//-------------------------闹铃显示函数--------------------------------
void alarm_dis()
{
wr_cmd(0x01); //lcd清屏
wr_cmd(0x91); //设置第二行起始位
for(a=0;a<10;a++) //写入第二行数据
{
wr_dat(alarm2[a]);
}
wr_cmd(0x8a); //设置第三行第三个汉字地址为起始位
for(a=0;a<8;a++) //写入第三行数据
{
wr_dat(alarm3[a]);
}
wr_cmd(0x8a); //闹铃显示界面第三行时位置
wr_dat(0x30+hour_alarm/10);
wr_dat(0x30+hour_alarm%10);
wr_cmd(0x8c); //闹铃显示界面第三行分位置
wr_dat(0x30+min_alarm/10);
wr_dat(0x30+min_alarm%10);
}
//------------------------功能键盘扫描函数----------------------------------
void keyscan()
{
do
{
if(k0>6) //key0按下超过6次的情况
{
k0=k0%6;
if(k0==0)
k0=6;
}
if(k0!=0)
{
switch(k0)
{
case 1:
{
TR0=0; //关闭定时器0
wr_cmd(0x0f); //整体显示开,显示光标
wr_cmd(0x95); //光标地址指向秒位置
}break;
case 2:
{
wr_cmd(0x93); //光标地址指向分位置
}break;
case 3:
{
wr_cmd(0x91); //光标地址指向时位置
}break;
case 4:
{
wr_cmd(0x85); //光标地址指向日位置
}break;
case 5:
{
wr_cmd(0x83); //光标地址指向月位置
}break;
case 6:
{
wr_cmd(0x81); //光标地址指向年位置
}break;
}
}
if(key0==0) //key0的功能:校时模式
{
delay_ms(10);
if(key0==0) //在校时模式时,按键key0不起作用
{
if(k1!=0)
k0=0;
else
{
while(key0==0);
k0++;
}
}
}
if(key1==0) //key1的功能:闹铃模式
{
delay_ms(10);
if(key1==0) //在闹铃模式时,按键key1不起作用
{
if(k0!=0)
k1=0;
else
{
while(key1==0); //等待按键释放
k1++;
wr_cmd(0x01); //lcd清屏
alarm_dis(); //闹铃界面显示
}
}
}
if(k1>2) //key1按下超过两次的情况
{
k1=k1%2;
if(k1==0)
k1=2;
}
if(k1!=0)
{
switch(k1)
{
case 1:
{
wr_cmd(0x0f); //整体显示开,显示光标
wr_cmd(0x8c); //光标地址指向分位置
}break;
case 2:
{
wr_cmd(0x8a); //光标地址指向分位置
}break;
}
}
if(key2==0) //key2的功能:加计数
{
delay_ms(10);
if(key2==0)
{
while(key2==0);
if(TR0==0) //校时模式加计数
{
switch(k0)
{
case 1:
{
sec++;
if(sec==60)
sec=0;
}break;
case 2:
{
min++;
if(min==60)
min=0;
}break;
case 3:
{
hour++;
if(hour==24)
hour=0;
}break;
case 4:
{
day++;
if(mon==1||mon==3||mon==5||mon==7||mon==8||mon==10||mon==12)
temp_day=32;
if(mon==4||mon==6||mon==9||mon==11)
temp_day=31;
if(mon==2)
{
if((year%4==0&&year%100!=0)||(year%4==0&&year%400==0)) //判断是否为闰年(四年一闰,百年不闰,四百年再闰)
temp_day=30; //是闰年的处理
else
temp_day=29; //不是闰年的处理
}
if(day==temp_day)
day=1;
}break;
case 5:
{
mon++;
if(mon==13)
mon=1;
}break;
case 6:
{
year++;
if(year==2100)
year=2001;
}break;
}
dat_dis();
}
if(k1!=0) //闹铃模式加计数
{
switch(k1)
{
case 1:
{
min_alarm++;
if(min_alarm==60)
min_alarm=0;
}break;
case 2:
{
hour_alarm++;
if(hour_alarm==24)
hour_alarm=0;
}break;
}
alarm_dis(); //显示闹铃模式界面
}
}
}
if(key3==0) //key3的功能:确定返回
{
delay_ms(10);
if(key3==0)
{
k0=0; //将k0清零
k1=0; //将n0清零
flag_alarm=1; //闹铃标志位
wr_cmd(0x0c); //整体显示开,不显示光标
TR0=1; //开启定时器0
wr_cmd(0x01); //lcd清屏
face_dis(); //显示主界面
}
}
}while(k0!=0||k1!=0);
}
//-----------------------------初始化函数------------------------------------
void init()
{
P0=0xff; //让数码管不显示
dula=0; //关闭段选
wela=0; //关闭位选
delay_ms(20); //等待液晶输入电源稳定
lcdrd=1; //选定lcd与单片机并行连接
lcdwr=0; //因为我们不需要从lcd模块读数据,所以可直接将此端口清零,便于之后利用矩阵键盘
face_dis(); //显示用户界面
TMOD=0x11; //选择定时器0方式1
TH0=(65536-46080)/256; //赋初值,每隔50ms产生一次中断
TL0=(65536-46080)%256;
EA=1; //开启总中断
ET0=1; //开启定时器0中断
TR0=1; //开启定时器0
}
//----------------进位判断函数------------------------
void carry() //carry为“进位”的意思
{
if(hour==hour_alarm&&min==min_alarm&&flag_alarm==0&&(hour_alarm!=0||min_alarm!=0)) //到时闹铃,当设定为00:00时闹定关闭
{
flag_alarm=0; //闹铃标志位清零
wr_cmd(0x01); //lcd清屏
wr_cmd(0x0c); //整体显示开,游标关,允许反白关
while(flag_alarm==0) //等待key3按下,将flag_alarm置位
{
beep_pro(0);
wr_cmd(0x91); //设置第二行起始位
for(a=0;a<10;a++) //写入第二行数据
wr_dat(alarm2[a]);
wr_cmd(0x8a); //设置第三行第三个汉字地址为起始位
for(a=0;a<8;a++) //写入第三行数据
wr_dat(alarm3[a]);
wr_cmd(0x8a); //闹铃显示界面第三行时位置
wr_dat(0x30+hour_alarm/10);
wr_dat(0x30+hour_alarm%10);
wr_cmd(0x8c); //闹铃显示界面第三行分位置
wr_dat(0x30+min_alarm/10);
wr_dat(0x30+min_alarm%10);
if(key3==0) //key3的功能:确定返回,停止闹铃
{
delay_ms(10);
if(key3==0)
{
wr_cmd(0x01); //lcd清屏
flag_alarm=1; //闹铃标志位置位
face_dis();
}
}
}
}
if(flag_alarm==1&&(hour!=hour_alarm||min!=min_alarm))
{
flag_alarm=0;
}
if(min==59&&(sec>=57&&sec<=59)) //整点前报时,2500Hz
{
beep_pro(1);
while(t0!=0); //等待下一次秒自加
}
if(min==0&&sec==0) //整点报时,5000Hz
{
beep_pro(2);
while(t0!=0); //等待下一次秒自加
}
if(sec==60) //当sec=60为时一分
{
sec=0;
min++;
if(min==60) //当min=60为时一时
{
min=0;
hour++;
if(hour==24) //当hour=24为时一天
{
hour=0;
day++;
if(mon==1||mon==3||mon==5||mon==7||mon==8||mon==10||mon==12)
{
if(day==32)
{
day=1;
mon++;
if(mon==13)
{
mon=1;
year++;
}
}
}
if(mon==4||mon==6||mon==9||mon==11)
{
if(day==31)
{
day=1;
mon++;
if(mon==13)
{
mon=1;
year++;
}
}
}
if(mon==2)
{
if((year%4==0&&year%100!=0)||(year%4==0&&year%400==0)) //判断是否为闰年(四年一闰,百年不闰,四百年再闰)
{
if(day==30)
{
day=1;
mon++;
if(mon==13)
{
mon=1;
year++;
}
}
}
else //不是闰年的处理
{
if(day==29)
{
day=1;
mon++;
if(mon==13)
{
mon=1;
year++;
}
}
}
}
}
}
}
}
//-----------------------获温部分:初始化函数--------------------
void init_temp()
{
DQ=0; //拉低总线,产生复位信号
i=80;while(i>0)i--; //延时480~960us
DQ=1; //拉高总线
i=4;while(i>0)i--; //延时15~60us
while(DQ); //等待产生应答脉冲
i=70;while(i>0)i--; //延时至少480us
}
//------------------获温部分:写函数-------------------------------
void wr_temp(uchar dat) //写1个字节
{
bit testb;
for(j=8;j>0;j--)
{
testb=dat&0x01;
dat=dat>>1;
if(testb) //写1
{
DQ=0; //拉低总线,产生写时间隙
i++; //延时大于1us
DQ=1; //拉高总线
i=8;while(i>0)i--; //延时至少60us,供DS18B20采样
}
else //写0
{
DQ=0; //拉低总线,产生写时间隙
i=8;while(i>0)i--; //保持至少60us,供DS18B20采样
DQ=1; //拉高总线
i++;i++;
}
}
}
//------------------获温部分:读函数-------------------------------
uchar rd_temp() //读1个字节
{
bit b; //定义存放接收到的1个字节
uchar i_b;
uchar rdbyte;
for(j=8;j>0;j--)
{
DQ=0; //拉低总线,产生读时隙
i++; //延时大于1us
DQ=1; //释放总线
i++;i++; //给一定时间让总线释放
b=DQ; //读取数据
i=8;while(i>0)i--; //延时至少60us
i_b=b;
rdbyte=(i_b<<7)|(rdbyte>>1); //将读取到得一位值左移7位,存放读取的数据变量rdbyte右移1位
}
return rdbyte;
}
//----------------------温度获取函数---------------------------
void get_temp()
{
init_temp(); //初始化,复位并获取应答信号
wr_temp(0xcc); //跳过ROM
wr_temp(0x44); //开始温度转换
init_temp(); //将DS18B20复位
wr_temp(0xcc); //跳过ROM
wr_temp(0xbe); //读暂存器
low=rd_temp(); //读取低8位
high=rd_temp(); //读取高8位
temp=high;
temp<<=8;
temp=temp|low; //将读取的低8位和高8位合并
f_temp=temp*0.0625; //温度在寄存器中为12位 分辨率位0.0625°
temp=f_temp*10+0.5; //精确到十分位,四舍五入
d2=temp/100; //显示数据:十位
d1=temp%100/10; //显示数据:个位
d0=temp%10; //显示数据:十分位
}
//-------------------定时器0中断函数--------------------
void timer0() interrupt 1
{
TH0=(65536-46080)/256; //赋初值,每隔50ms产生一次中断
TL0=(65536-46080)%256;
t0++; //用于产生基本秒信号
if(t0==20)
{
t0=0;
sec++; //秒自加
}
}
//-----------------主函数------------------------
void main()
{
init(); //初始化
while(1)
{
carry(); //判断是否该年、月、日、时、分、秒是否要进位
get_temp(); //获取温度
keyscan(); //功能键盘扫描
dat_dis(); //显示年、月、日、时、分、秒
week_day(year,mon,day); //星期计算,给出年月日,计算出星期几
}
}
用户269609 2012-5-6 23:03
int year="2009"; 报错,怎么解决啊
12864.C(40): 错误 C247: non-address/-constant initializer
用户377235 2012-5-6 20:51
用户307424 2011-3-6 13:02
用户264440 2010-1-20 23:05
用户238961 2009-12-29 09:39
用户1487147 2009-8-26 18:55
bin362422 2009-8-26 16:57