更多详细的内容可以访问我的个人博客时莫如初
1.系统功能简介
1.1硬件介绍
本系统由ds1302时钟芯片产生时基掉电可用后备电源不影响时间准确关于ds1302,蜂鸣器用于闹钟(也可用温度报警本系统没有),ds18b20数字温度传感器用于检测环境温度关于ds18b20,24c02芯片是IIC时序的存储器用于存储用户信息,整个系统的显示用的是LCD12864(串口的),整个系统均由stc90c516协调控制。
功能
- 可按键调整时间
- 能显示精确温度
- 可设置当前时间24小时以内的闹钟
- ds1302电路
- 24c02电路
- 完整电路图和PCB
见文末
2.程序
运行环境win10 keil5
由于代码量过大这里主要介绍主函数和按键功能实现函数其余代码见文末
- main.h,引入标准头文件和数据类型重命名
#ifndef _MAIN_H
#define _MAIN_H
#include "reg52.h"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
sbit beep = P1^5;//蜂鸣器控制引脚定义
#define ds1302init 0 //ds1302初始化时间标志
void Delay10ms(unsigned int c);//10ms延时
#endif
- main.c
#include "main.h"
#include "lcd.h" //显示头文件
#include "ds1302.h" //温度传感器
#include "24c02.h" //存储
#include "ds18b20.h" //时钟
#include "key.h" //按键
void Delay10ms(unsigned int c)
{
unsigned char a,b;
for(;c>0;c--)
for(b=38;b>0;b--)
for(a=130;a>0;a--);
}
void main(void)
{
uchar b = 50,n=10;
TMOD|=0X01; //选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0 = 0xff;
TL0 = 0x9c;
ET0 = 1;
EA = 1; //使用定时器0中断
Init_lcd();
#if ds1302init //是否初始化时间
Init_ds1302();
#endif
clear_all();
display_graphic_8x16(1,1,&num[2][0]);
display_graphic_8x16(1,8,&num[0][0]);
display_graphic_8x16(3,8*12,O);
display_graphic_8x16(3,8*13,F);
display_graphic_8x16(3,8*14,F);
while(1)
{
At24c02Write(1,0x00);
if(At24c02Read(1) == 0x00)
display_graphic_16x16(3,8*14,ri);
Key_Function(Key_Read()); //按键读取函数并实现相应功能
if(n == 10) //延时控制
{
time_display(); //时间显示函数
n=0;
}
if(b == 50) //延时控制
{
Ds18b20_display(Ds18b20ReadTemp()); //温度显示函数
b=0;
}
if(clock[2] == 0x01) //判断闹钟标志
{
if(Alarm_clock[0] == time[2]) //检查闹钟是否到时
{
if(Alarm_clock[1] == time[1])
{
TR0 = 1; //开启定时器0
clear_8x16(3,8*13);
display_graphic_8x16(3,8*13,F);
display_graphic_8x16(3,8*14,F);
clear_8x16(5,8*3);
clear_8x16(5,8*4);
clear_8x16(5,8*5);
clear_8x16(5,8*6);
clear_8x16(5,8*7);
clock[2] = 0x00;
clock[0] = 0x00;
clock[1] = 0x00;
}
}
}
Delay10ms(1);
b++;
n++;
}
}
void Timer0() interrupt 1 //中断服务函数
{
static uint i;
TH0=0xff;
TL0=0x9c;
beep = ~beep; //蜂鸣器
i++;
if(i==15000) //可调整想多长时间
{
i=0;
TR0 = 0;
}
}
- key.h
#ifndef _KEY_H
#define _KEY_H
#include "main.h"
sbit ADD = P3^2; //加
sbit RED = P3^3; //减
sbit ALARM = P3^4; //闹钟
sbit SET = P3^5; //设置
#define ADD_1 1 //加返回值
#define RED_2 2 //减返回值
#define ALARM_3 3 //闹钟返回值
#define SET_4 4 //设置返回值
extern uchar clock[3]; //clock[0]接收小时数据 clock[1]接收分钟数据 clock[2]是进入闹钟标志
extern uchar Alarm_clock[2]; //闹钟的时,分
uchar Key_Read(void); //读取按键值的函数
void Key_Function(uchar temp); //根据按键值实现功能的函数
#endif
- key.c
#include "key.h"
#include "lcd.h"
#include "ds1302.h"
#include "24c02.h"
uchar clock[3]; //clock[0]接收小时数据 clock[1]接收分钟数据 clock[2]是进入闹钟标志
uchar Alarm_clock[2]; //闹钟的时,分
uchar Key_Read()
{
if((ADD == 0) || (RED == 0) || (ALARM == 0) || (SET) == 0)
{
Delay10ms(1); //延时去抖动
if(ADD == 0)
{
while(!ADD);
return ADD_1;
}
else if(RED == 0)
{
while(!RED);
return RED_2;
}
else if(ALARM == 0)
{
while(!ALARM);
return ALARM_3;
}
else if(SET == 0)
{
while(!SET);
return SET_4;
}
}
return 0; //如果无则返回0
}
/********************************************************
数值加一函数
参数一用于按键检查 如果检查不对直接退出函数
参数二用于修改那个时间比如 i=1 则修改年,i=2 则修改月 以此类推
time全局外部数组 在ds1303.c中定义,顺序是 秒,分,时,日,月,星期,年
无返回值
*************************************************************/
void Add_one(uchar temp,uchar i)
{
if(temp == ADD_1)//加一
{
Write_ds1302(0x8e,0x00); //关闭写保护
if(i == 1)
{
if((time[6] & 0x0f) == 0x09) //判断是否需要进位 比如19到20 以下均是
{
time[6] >>= 4;
time[6] ++;
time[6] <<= 4;
}
else
time[6]++;
Write_ds1302(0x8c,time[6]);
}
if(i == 2)
{
if((time[4] & 0x0f) == 0x09)
{
time[4] >>= 4;
time[4] ++;
time[4] <<= 4;
}
else
time[4]++;
if(time[4] == 0x13)
time[4] = 0x01;
Write_ds1302(0x88,time[4]);
}
if(i == 3)
{
if((time[3] & 0x0f) == 0x09)
{
time[3] >>= 4;
time[3] ++;
time[3] <<= 4;
}
else
time[3]++;
if(time[3] == 0x32)
time[3] = 0x01;
Write_ds1302(0x86,time[3]);
}
if(i == 4)
{
time[5]++;
if(time[5] == 0x08)
time[5] = 0x01;
Write_ds1302(0x8a,time[5]);
}
if(i == 5)
{
if((time[2] & 0x0f) == 0x09)
{
time[2] >>= 4;
time[2] ++;
time[2] <<= 4;
}
else
time[2]++;
if(time[2] == 0x24)
time[2] = 0x00;
Write_ds1302(0x84,time[2]);
}
if(i == 6)
{
if((time[1] & 0x0f) == 0x09)
{
time[1] >>= 4;
time[1] ++;
time[1] <<= 4;
}
else
time[1]++;
if(time[1] == 0x60)
time[1] = 0x00;
Write_ds1302(0x82,time[1]);
}
if(i == 7)
{
if((time[0] & 0x0f) == 0x09)
{
time[0] >>= 4;
time[0] ++;
time[0] <<= 4;
}
else
time[0]++;
if(time[0] == 0x60)
time[0] = 0x00;
Write_ds1302(0x80,time[0]);
}
Write_ds1302(0x8e,0x80); //打开写保护
}
}
/********************************************************
数值减一函数
参数一用于按键检查 如果检查不对直接退出函数
参数二用于修改那个时间比如 i=1 则修改年,i=2 则修改月 以此类推
time全局外部数组 在ds1303.c中定义,顺序是 秒,分,时,日,月,星期,年
无返回值
*************************************************************/
void Red_one(uchar temp,uchar i)
{
if(temp == RED_2)
{
Write_ds1302(0x8e,0x00);//关闭写保护
if(i == 1)
{
if((time[6] & 0x0f) == 0x00)
{
time[6] >>= 4;
time[6] --;
time[6] <<= 4;
time[6] |= 0x09;
}
else
time[6]--;
Write_ds1302(0x8c,time[6]);
}
if(i == 2)
{
if((time[4] & 0x0f) == 0x00)
{
time[4] >>= 4;
time[4] --;
time[4] <<= 4;
time[4] |= 0x09;
}
else
time[4]--;
if(time[4] == 0x00)
time[4] = 0x12;
Write_ds1302(0x88,time[4]);
}
if(i == 3)
{
if((time[3] & 0x0f) == 0x00)
{
time[3] >>= 4;
time[3] --;
time[3] <<= 4;
time[3] |= 0x09;
}
else
time[3]--;
if(time[3] == 0x00)
time[3] = 0x31;
Write_ds1302(0x86,time[3]);
}
if(i == 4)
{
time[5]--;
if(time[5] == 0x00)
time[5] = 0x07;
Write_ds1302(0x8a,time[5]);
}
if(i == 5)
{
if((time[2] & 0x0f) == 0x00)
{
time[2] >>= 4;
time[2] --;
time[2] <<= 4;
time[2] |= 0x09;
}
else
time[2]--;
Write_ds1302(0x84,time[2]);
}
if(i == 6)
{
if((time[1] & 0x0f) == 0x00)
{
time[1] >>= 4;
time[1] --;
time[1] <<= 4;
time[1] |= 0x09;
}
else
time[1]--;
Write_ds1302(0x82,time[1]);
}
if(i == 7)
{
if((time[0] & 0x0f) == 0x00)
{
time[0] >>= 4;
time[0] --;
time[0] <<= 4;
time[0] |= 0x09;
}
else
time[0]--;
Write_ds1302(0x80,time[0]);
}
Write_ds1302(0x8e,0x80); //打开写保护
}
}
/********************************************************
按键功能实现函数(调整时间,闹钟)
参数一用于检查是哪个功能
无返回值
*************************************************************/
void Key_Function(uchar temp)
{
static uchar i = 0,k = 0,filg = 1,filg1 = 1,j = 0,s = 0;
if(((k == 1) || (temp == SET_4)) && (filg == 1))//进入设置时间模式
{
filg1 = 0;
if(temp == SET_4) //实现调整时间闪烁提示
i++;
k = 1;
switch(i)
{
case 1:clear_8x16(1,8*2); clear_8x16(1,8*3); break;
case 2:clear_8x16(1,8*6); clear_8x16(1,8*7); break;
case 3:clear_8x16(1,8*10); clear_8x16(1,8*11); break;
case 4:clear_8x16(1,8*15); break;
case 5:clear_8x16(3,8*3); clear_8x16(3,8*4); break;
case 6:clear_8x16(3,8*6); clear_8x16(3,8*7); break;
case 7:clear_8x16(3,8*9); clear_8x16(3,8*10); break;
case 8:i = 0; k = 0; filg1 = 1;
}
Add_one(temp,i);
Red_one(temp,i);
}
if(((s == 1) || (temp == ALARM_3)) && (filg1 == 1))//设置闹钟时间
{
s = 1;
clock[2] = 0x01; //进入闹钟标志置一
if(k == 0)
{
clock[0] = time[2];
clock[1] = time[1];
clear_8x16(3,8*13); //清除OFF 的前两个字母
clear_8x16(3,8*14);
}
filg = 0;
k = 1;
display_graphic_8x16(3,8*13,N); //显示ON
display_graphic_8x16(5,8*3,&num[(clock[0] / 16)][0]);
display_graphic_8x16(5,8*4,&num[(clock[0] & 0x0f)][0]);
display_graphic_8x16(5,8*5,mao_hao);
display_graphic_8x16(5,8*6,&num[(clock[1] / 16)][0]);
display_graphic_8x16(5,8*7,&num[(clock[1] & 0x0f)][0]);
if(temp == SET_4) //计数按了几次
j++;
switch(j)
{
case 1:clear_8x16(5,8*3);clear_8x16(5,8*4);break;
case 2:clear_8x16(5,8*6);clear_8x16(5,8*7);break;
case 3:filg = 1; s = 0;
}
if(j == 1)
{
if(temp == ADD_1)
{
if((clock[0] & 0x0f) == 0x09) //判断是否需要进位 比如19到20
{
clock[0] >>= 4;
clock[0] ++;
clock[0] <<= 4;
}
else
clock[0]++;
}
if(temp == RED_2)
{
if((clock[0] & 0x0f) == 0x00)
{
clock[0] >>= 4;
clock[0] --;
clock[0] <<= 4;
clock[0] |= 0x09;
}
else
clock[0]--;
}
}
if(j == 2)
{
if(temp == ADD_1)
{
if((clock[1] & 0x0f) == 0x09)
{
clock[1] >>= 4;
clock[1] ++;
clock[1] <<= 4;
}
else
clock[1]++;
}
if(temp == RED_2)
{
if((clock[1] & 0x0f) == 0x00)
{
clock[1] >>= 4;
clock[1] --;
clock[1] <<= 4;
clock[1] |= 0x09;
}
else
clock[1]--;
}
}
if(j == 3) //表示设置闹钟完成 设置值存入全局数组
{
Alarm_clock[1] = clock[1];
Alarm_clock[0] = clock[0];
j = 0;
}
}
}
下面我对以上代码思路做简单介绍。如要所有代码见文末
程序采用C语言的模块化思想把功能封装成函数,主函数主要实现所用硬件的初始化定时器中断和所有需要显示到LCD上的内容。ds1302初始时间只需要设置一次因为掉电程序会重新运行但是时间掉电也会运行所以时间不需要初始化我们采用条件编译来实现。ds1302读回来的时间放在一个可供所有文件使用的全局数组time
里。
key.c主要介绍按键功能实现的函数这个函数实现两个功能调整时间和设置闹钟时间(最好把这两个功能写成两个独立的函数,也不知道当时我是咋想的☹☹)进入设置后利用加一减一函数实现加减在通过time数组写给ds1302就能够设置时间,关于怎么知道你当前设置的是那一个时间我用了局部清屏之后在写然后在局部清屏实现局部屏幕闪烁提示用户当前正在修改那个时间。
(最会说一下该程序的bug第一次开机必须先按下设置时间键才能按下闹钟键否则按下闹钟键没反应如有大神谢谢指点)
请点击源码+原理图+pcb提取码w1kb