main.c
/*
2019.9.1
完成一个按键控制日期显示
日期显示为
***2019-08-30***
****17:05:30****
每隔200ms对时钟进行一次读取,刷新液晶显示
按下回车键进入调整时间的界面
光标在年份十位上显示
按上下键进行数值加减调节,注意的是调整是由范围的
按下左右键,进行光标的移动,注意移动也是有范围
再按回车键,保存时间设置,写入芯片,然后退出光标显示
2019.9.2
新增功能,按下数字0
显示温度
25.4C
再按一次 显示时间
*/
#include <reg52.h>
struct sTime{
unsigned int year;
unsigned char mon;
unsigned char day;
unsigned char hour;
unsigned char min;
unsigned char sec;
unsigned char week;
};
struct sTime buftime;
unsigned char flag200ms=0;
unsigned char flag1000ms=0;
unsigned char adjust=0;
unsigned char key=0;
bit turn=0;
extern unsigned char TORH;
extern unsigned char TORL;
extern void GetRealTime(struct sTime *time);
extern void LcdShowStr(unsigned char x,unsigned char y,unsigned char *str);
extern void KeyScan();
extern void InitLcd1602();
extern void InitDS1302();
extern void ConfigTimer0(unsigned int ms);
extern void LcdSetCursor(unsigned char x,unsigned char y);
extern void KeyDriver();
extern void LcdCloseCursor();
extern void LcdOpenCursor();
extern void SetRealTime(struct sTime *time);
extern void LcdFullClear();
extern bit Start18B20();
extern bit Get18B20Temp(int *temp);
void RefreshTime();
void LCD1602ShowTime();
void MoveCursor(unsigned char situation);
void BcdToStr(unsigned char x,unsigned char y,unsigned char dat);
void UpNumber(unsigned char situation);
void DownNumber(unsigned char situation);
void ShowTemp(int temp);
unsigned char NumToStr(unsigned char *str,signed int dat);
void InitMain()//主程序函数初始化
{
InitLcd1602();//初始化液晶
InitDS1302();//初始化时钟
Start18B20();//初始化温度显示
ConfigTimer0(1);//初始化定时1ms
}
void main()
{
unsigned char oldsec=0xFF;
int temp;
bit old=1;
bit res;
EA=1;
InitMain();
while(1)
{
KeyDriver();// 按键驱动
if(turn==0)
{
if(old!=turn)
LcdFullClear();//清理屏幕
if(flag200ms==1&&key==0)//每隔200ms刷新时间,key 为按键指示,调整时间时,不进入刷新
{
flag200ms=0;
GetRealTime(&buftime);
if(oldsec!=buftime.sec)
RefreshTime();
oldsec=buftime.sec;
}
}
else
{
if(old!=turn)
LcdFullClear();//清理屏幕
if(flag1000ms==1)//1000ms刷新
{
flag1000ms=0;
res=Get18B20Temp(&temp);
if(res)
{
ShowTemp(temp);
}
Start18B20(); //重新转换
}
}
old=turn;
}
}
void ShowTemp(int temp) //对温度进行显示
{
int high,low;
unsigned char str[12];
unsigned char len;
high=temp>>4;//整数部分
low=temp&0xf;//小数部分
len=NumToStr(str,high);
str[len++]='.';
str[len++]=(low*10/16)+'0';
str[len++]='C';
str[len++]='\0';
LcdShowStr(0,0,str);
}
void BcdToStr(unsigned char x,unsigned char y,unsigned char dat) //将BCD码转化为字符串显示
{
unsigned char str[4];
str[0]=(dat>>4)+'0';
str[1]=(dat&0x0f)+'0';
str[2]='\0';
LcdShowStr(x,y,str);
}
void RefreshTime()//刷新时间设置,读取时间并显示
{
LcdShowStr(3,0,"20 - - "); //固定输出显示
LcdShowStr(4,1," : : ");
BcdToStr(5,0,buftime.year);
BcdToStr(8,0,buftime.mon);
BcdToStr(11,0,buftime.day);
BcdToStr(4,1,buftime.hour);
BcdToStr(7,1,buftime.min);
BcdToStr(10,1,buftime.sec);
}
void InterruptTimer0() interrupt 1
{
static unsigned char cnt1=0;
static int cnt2=0;
TH0=TORH;
TL0=TORL;
KeyScan();
cnt1++;
cnt2++;
if(cnt1>200)
{
cnt1=0;
flag200ms=1;
}
if(cnt2>1000)
{
cnt2=0;
flag1000ms=1;
}
}
void KeyAction(unsigned char keycode)//按键动作检测
{
static signed char step=0;
if(keycode==0x0D&&turn==0)//按下回车键 温度显示时候不进入
{
adjust=1;
key=1;
LcdSetCursor(5,0);//光标定位
LcdOpenCursor();//打开光标闪烁
}
else if(adjust==1&&keycode==0x26&&turn==0)//按下回车键后按下向上键
{
UpNumber(step);
}
else if(adjust==1&&keycode==0x25&&turn==0)//按下回车键后按下向左键,对指针进行移动
{
step--;
if(step<0)
step=11;
MoveCursor(step);
}
else if(adjust==1&&keycode==0x28&&turn==0)//按下回车键后按下向下键
{
DownNumber(step);
}
else if(adjust==1&&keycode==0x27&&turn==0)//按下回车键后按下向右键
{
step++;
if(step>11)
step=0;
MoveCursor(step);
}
else if(keycode==0x1B&&turn==0)//按下向esc键
{
adjust=0;
LcdCloseCursor();
step=0;
key=0;
SetRealTime(&buftime);
}
else if(keycode=='0'&&adjust==0) //没有进入回车键时候使用该按键
{
turn=~turn;
}
}
unsigned char BcdIncreaseH(unsigned char bcd)//递增一个BCD码的高位
{
if((bcd&0xf0)<0x90)
bcd+=0x10;
else
bcd&=0x0f;
return bcd;
}
unsigned char BcdIncreaseL(unsigned char bcd)//递增一个BCD码的低位
{
if((bcd&0x0f)<0x09)
bcd+=0x01;
else
bcd&=0xf0;
return bcd;
}
unsigned char BcdReduceH(unsigned char bcd)//递减一个BCD码的高位
{
if((bcd&0xf0)>0x00)
bcd-=0x10;
else
bcd|=0x90;
return bcd;
}
unsigned char BcdReduceL(unsigned char bcd)//递减一个BCD码的低位
{
if((bcd&0x0f)>0x00)
bcd-=0x01;
else
bcd|=0x09;
return bcd;
}
void MoveCursor(unsigned char situation)//光标移动显示函数
{
switch(situation)
{
case 0: LcdSetCursor(5,0);break;
case 1: LcdSetCursor(6,0);break;
case 2: LcdSetCursor(8,0);break;
case 3: LcdSetCursor(9,0);break;
case 4: LcdSetCursor(11,0);break;
case 5: LcdSetCursor(12,0);break;
case 6: LcdSetCursor(4,1);break;
case 7: LcdSetCursor(5,1);break;
case 8: LcdSetCursor(7,1);break;
case 9: LcdSetCursor(8,1);break;
case 10:LcdSetCursor(10,1);break;
case 11:LcdSetCursor(11,1);break;
default:break;
}
}
void DownNumber(unsigned char situation) //减少指数值
{
switch(situation)//这里需要注意buftime是BCD码
{
case 0: buftime.year=BcdReduceH(buftime.year);BcdToStr(5,0,buftime.year);LcdSetCursor(5,0);break;
case 1: buftime.year=BcdReduceL(buftime.year);BcdToStr(5,0,buftime.year);LcdSetCursor(6,0);break;
case 2: buftime.mon=BcdReduceH(buftime.mon);BcdToStr(8,0,buftime.mon);LcdSetCursor(8,0);break;
case 3: buftime.mon=BcdReduceL(buftime.mon);BcdToStr(8,0,buftime.mon);LcdSetCursor(9,0);break;
case 4: buftime.day=BcdReduceH(buftime.day);BcdToStr(11,0,buftime.day);LcdSetCursor(11,0);break;
case 5: buftime.day=BcdReduceL(buftime.day);BcdToStr(11,0,buftime.day);LcdSetCursor(12,0);break;
case 6: buftime.hour=BcdReduceH(buftime.hour);BcdToStr(4,1,buftime.hour);LcdSetCursor(4,1);break;
case 7: buftime.hour=BcdReduceL(buftime.hour);BcdToStr(4,1,buftime.hour);LcdSetCursor(5,1);break;
case 8: buftime.min=BcdReduceH(buftime.min);BcdToStr(7,1,buftime.min);LcdSetCursor(7,1);break;
case 9: buftime.min=BcdReduceL(buftime.min);BcdToStr(7,1,buftime.min);LcdSetCursor(8,1);break;
case 10:buftime.sec=BcdReduceH(buftime.sec);BcdToStr(10,1,buftime.sec);LcdSetCursor(10,1);break;
case 11:buftime.sec=BcdReduceL(buftime.sec);BcdToStr(10,1,buftime.sec);LcdSetCursor(11,1);break;
default:break;
}
}
void UpNumber(unsigned char situation) //增加指数值
{
switch(situation)
{
case 0: buftime.year=BcdIncreaseH(buftime.year);BcdToStr(5,0,buftime.year);LcdSetCursor(5,0);break;
case 1: buftime.year=BcdIncreaseL(buftime.year);BcdToStr(5,0,buftime.year);LcdSetCursor(6,0);break;
case 2: buftime.mon=BcdIncreaseH(buftime.mon);BcdToStr(8,0,buftime.mon);LcdSetCursor(8,0);break;
case 3: buftime.mon=BcdIncreaseL(buftime.mon);BcdToStr(8,0,buftime.mon);LcdSetCursor(9,0);break;
case 4: buftime.day=BcdIncreaseH(buftime.day);BcdToStr(11,0,buftime.day);LcdSetCursor(11,0);break;
case 5: buftime.day=BcdIncreaseL(buftime.day);BcdToStr(11,0,buftime.day);LcdSetCursor(12,0);break;
case 6: buftime.hour=BcdIncreaseH(buftime.hour);BcdToStr(4,1,buftime.hour);LcdSetCursor(4,1);break;
case 7: buftime.hour=BcdIncreaseL(buftime.hour);BcdToStr(4,1,buftime.hour);LcdSetCursor(5,1);break;
case 8: buftime.min=BcdIncreaseH(buftime.min);BcdToStr(7,1,buftime.min);LcdSetCursor(7,1);break;
case 9: buftime.min=BcdIncreaseL(buftime.min);BcdToStr(7,1,buftime.min);LcdSetCursor(8,1);break;
case 10:buftime.sec=BcdIncreaseH(buftime.sec);BcdToStr(10,1,buftime.sec);LcdSetCursor(10,1);break;
case 11:buftime.sec=BcdIncreaseL(buftime.sec);BcdToStr(10,1,buftime.sec);LcdSetCursor(11,1);break;
default:break;
}
}
ds18b20.c
#include <reg52.h>
#include <intrins.h>
sbit IO_18B20=P3^2;
void DelayX10us(unsigned char t)//延迟时间为t*10us
{
do{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}while(--t);
}
/*
拉低引脚,持续500us
延时60us
读取存在脉冲,并等待脉冲结束
*/
bit Get18B20Ack()
{
bit ack;
EA=0; //禁止总中断
IO_18B20=0;
DelayX10us(50);
IO_18B20=1;
DelayX10us(6);
ack=IO_18B20;
while(!IO_18B20);//等待存在脉冲结束
EA=1; //使能总中断
return ack;
}
void Write18B20(unsigned char dat)
{
unsigned char mask;
EA=0;
for(mask=0x01;mask!=0;mask<<=1)
{
IO_18B20=0;
_nop_();
_nop_();//延时2us
if((mask&dat)==0)
IO_18B20=0;
else
IO_18B20=1;
DelayX10us(6);
IO_18B20=1;
}
EA=1;
}
unsigned char Read18B20()
{
unsigned char mask;
unsigned char dat;
EA=0;
for(mask=0x01;mask!=0;mask<<=1)
{
IO_18B20=0;
_nop_();
_nop_();//延时2us
IO_18B20=1;
_nop_();
_nop_();//延时2us
if(!IO_18B20)
dat&=~mask;
else
dat|=mask;
DelayX10us(6);
}
EA=1;
return dat;
}
//启动温度转换操作
bit Start18B20()
{
bit ack;
ack= Get18B20Ack();
if(ack==0)
{
Write18B20(0xCC);//跳过ROM操作
Write18B20(0x44);//启动温度转换
}
return ~ack;
}
//读取温度,返回值表示是否读取成功
bit Get18B20Temp(int *temp)
{
bit ack;
unsigned char LSB,MSB;
ack= Get18B20Ack(); //获取应答
if(ack==0)
{
Write18B20(0xCC);
Write18B20(0xBE);//发送读命令
LSB=Read18B20(); //读取低位
MSB=Read18B20(); //读取高位
*temp=((int)MSB<<8)+LSB; //合成整型数
}
return ~ack;
}
keyboard.c
#include <reg52.h>
sbit KEY_IN_1 = P2^4;
sbit KEY_IN_2 = P2^5;
sbit KEY_IN_3 = P2^6;
sbit KEY_IN_4 = P2^7;
sbit KEY_OUT_1 = P2^3;
sbit KEY_OUT_2 = P2^2;
sbit KEY_OUT_3 = P2^1;
sbit KEY_OUT_4 = P2^0;
unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表
{ '1', '2', '3', 0x26 }, //数字键1、数字键2、数字键3、向上键
{ '4', '5', '6', 0x25 }, //数字键4、数字键5、数字键6、向左键
{ '7', '8', '9', 0x28 }, //数字键7、数字键8、数字键9、向下键
{ '0', 0x1B, 0x0D, 0x27 } //数字键0、ESC键、 回车键、 向右键
};
unsigned char pdata KeySta[4][4] = { //全部矩阵按键的当前状态
{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
};
extern void Reset();
extern void GetResult();
extern void NumKeyAction(unsigned char n);
extern void OprtKeyAction(unsigned char type);
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
extern void KeyAction(unsigned char keycode);
extern void LcdOpenCursor();
/* 按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 */
void KeyDriver()
{
unsigned char i, j;
static unsigned char pdata backup[4][4] = { //按键值备份,保存前一次的值
{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
};
for (i=0; i<4; i++) //循环检测4*4的矩阵按键
{
for (j=0; j<4; j++)
{
if (backup[i][j] != KeySta[i][j]) //检测按键动作
{
if (backup[i][j] != 0) //按键按下时执行动作
{
KeyAction(KeyCodeMap[i][j]); //调用按键动作函数
}
backup[i][j] = KeySta[i][j]; //刷新前一次的备份值
}
}
}
}
/* 按键扫描函数,需在定时中断中调用,推荐调用间隔1ms */
void KeyScan()
{
unsigned char i;
static unsigned char keyout = 0; //矩阵按键扫描输出索引
static unsigned char keybuf[4][4] = { //矩阵按键扫描缓冲区
{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}
};
//将一行的4个按键值移入缓冲区
keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;
//消抖后更新按键状态
for (i=0; i<4; i++) //每行4个按键,所以循环4次
{
if ((keybuf[keyout][i] & 0x0F) == 0x00)
{ //连续4次扫描值为0,即4*4ms内都是按下状态时,可认为按键已稳定的按下
KeySta[keyout][i] = 0;
}
else if ((keybuf[keyout][i] & 0x0F) == 0x0F)
{ //连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起
KeySta[keyout][i] = 1;
}
}
//执行下一次的扫描输出
keyout++; //输出索引递增
keyout &= 0x03; //索引值加到4即归零
switch (keyout) //根据索引,释放当前输出引脚,拉低下次的输出引脚
{
case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
default: break;
}
}
ds1302.c
#include <reg52.h>
sbit DS1302_CE=P1^7;//使能
sbit DS1302_CK=P3^5;//时钟信号
sbit DS1302_IO=P3^4;//信号读写
struct sTime{
unsigned int year;
unsigned char mon;
unsigned char day;
unsigned char hour;
unsigned char min;
unsigned char sec;
unsigned char week;
};
//发送单个字节到DS1302通信总线上
void DS1302ByteWrite(unsigned char dat)
{
unsigned char mask;
for(mask=0x01;mask!=0;mask<<=1)//低位先读
{
if((mask&dat)!=0)
DS1302_IO=1;
else
DS1302_IO=0;
DS1302_CK=1;
DS1302_CK=0;
}
DS1302_IO=1;
}
//从DS1302通信总线上读取一个字节
unsigned char DS1302ByteRead()
{
unsigned char mask;
unsigned char dat=0;
for(mask=0x01;mask!=0;mask<<=1)
{
if(DS1302_IO!=0)
dat|=mask;
DS1302_CK=1;
DS1302_CK=0;
}
return dat;
}
//用单次操作从某一寄存器中读取一个字节,reg为寄存器地址,dat为待写入字节
unsigned char DS1302SingleRead(unsigned char reg)
{
unsigned char dat;
DS1302_CE=1;
DS1302ByteWrite((reg<<1)|0x81);
dat=DS1302ByteRead();
DS1302_CE=0;
return dat;
}
//用单次写操作向某一寄存器写入一个字节,reg为寄存器地址,dat为待写入字节
void DS1302SingleWrite(unsigned char reg,unsigned char dat)
{
DS1302_CE=1;
DS1302ByteWrite((reg<<1)|0x80);//写入指令,寄存器地址即操作
DS1302ByteWrite(dat);
DS1302_CE=0;
}
//突发模式连续写入8个寄存器数据
void DS1302BurstWrite(unsigned char *dat)
{
unsigned char i;
DS1302_CE=1;
DS1302ByteWrite(0xBE);
for (i=0;i<8;i++) //连续写入八个数据
{
DS1302ByteWrite(dat[i]);
}
DS1302_CE=0;
}
void DS1302BurstRead(unsigned char *dat)
{
unsigned char i;
DS1302_CE=1;
DS1302ByteWrite(0xBF);
for (i=0;i<8;i++) //连续写入八个数据
{
dat[i]=DS1302ByteRead();
}
DS1302_CE=0;
}
//获取实时时间
void GetRealTime(struct sTime *time)
{
unsigned char buf[8];
DS1302BurstRead(buf);
time->year=buf[6]+0x2000;
time->mon=buf[4];
time->day=buf[3];
time->hour=buf[2];
time->min=buf[1];
time->sec=buf[0];
time->week=buf[5];
}
//设定实时时间
void SetRealTime(struct sTime *time)
{
unsigned char buf[8];
buf[7]=0;
buf[6]=time->year;
buf[5]=time->week;
buf[4]=time->mon;
buf[3]=time->day;
buf[2]=time->hour;
buf[1]=time->min;
buf[0]=time->sec;
DS1302BurstWrite(buf);
}
//初始化
void InitDS1302()
{
unsigned char dat;
struct sTime code InitTime[]={
0x2019,0x08,0x29,0x12,0x00,0x00,0x02};
DS1302_CE=0;
DS1302_CK=0;
dat=DS1302SingleRead(0);//读取秒寄存器
if((dat&0x80)!=0) //判断时钟是否已经停止
{
DS1302SingleWrite(7,0x00);//撤销写保护以允许写入数据
SetRealTime(&InitTime);//设置时钟为默认的初始时间
}
}
configtime0.c
#include <reg52.h>
unsigned char TORH=0;
unsigned char TORL=0;
void ConfigTimer0(unsigned int ms)
{
unsigned long tmp;
tmp=11059200/12*ms/1000;
tmp=65536-tmp;
TORH=(unsigned char)(tmp>>8);
TORL=(unsigned char)(tmp);
TMOD&=0XF0;
TMOD|=0X01;
TH0=TORH;
TL0=TORL;
ET0=1;
TR0=1;
}
lcd1602.c
/*
液晶显示相关代码
*/
#include <reg52.h>
#define LCD1602_DB P0
sbit LCD1602_RS=P1^0;
sbit LCD1602_RW=P1^1;
sbit LCD1602_E=P1^5;
void LcdWaitReady()//读状态
{
unsigned char sta;
LCD1602_DB=0XFF;//开始就进行读取
LCD1602_RS=0;
LCD1602_RW=1;
do{
LCD1602_E=1;
sta=LCD1602_DB;
LCD1602_E=0;
}while(sta&0x80);//判断高位为1,即为忙状态
}
void LcdWriteCmd(unsigned char cmd)//写入命令
{
LcdWaitReady();//写入之前需要判断状态
LCD1602_RS=0;
LCD1602_RW=0;
LCD1602_DB=cmd;
LCD1602_E=1;
LCD1602_E=0;
}
void LcdWriteDat(unsigned char dat)//写入数据
{
LcdWaitReady(); //进行状态的判断
LCD1602_RS=1;
LCD1602_RW=0;
LCD1602_DB=dat;
LCD1602_E=1;
LCD1602_E=0;
}
void LcdSetCursor(unsigned char x,unsigned char y)//进行光标位置的判断
{
unsigned char addr;
if(y)//y为1
addr=0x40+x;
else
addr=0x00+x;
LcdWriteCmd(addr|0x80);
}
void LcdShowStr(unsigned char x,unsigned char y,unsigned char *str)//对字符进行显示
{
LcdSetCursor( x, y);
while(*str!='\0')
{
LcdWriteDat(*str++);
}
}
//打开光标闪烁
void LcdOpenCursor()
{
LcdWriteCmd(0x0f);
}
//关闭光标显示
void LcdCloseCursor()
{
LcdWriteCmd(0x0C);
}
//进行液晶显示的初始化
void InitLcd1602()
{
LcdWriteCmd(0x38);
LcdWriteCmd(0x0c);
LcdWriteCmd(0x06);//文字不动,地址加1
LcdWriteCmd(0x01);//进行清屏显示
}
void LcdFullClear() //清屏函数
{
LcdWriteCmd(0x01);
}
numtostr.c
#include <reg52.h>
//主要是将数字转化为字符串
unsigned char NumToStr(unsigned char *str,signed int dat)
{
unsigned char len=0;
unsigned char buf[12];
signed char i=0;
if(dat<0)
{
dat=-dat;
*str++='-';
len++;
}
while(dat)
{
buf[i++]=dat%10;
dat/=10;
}
len+=i;
while(i-->0)
{
*str++=buf[i]+'0';
}
*str='\0';
return len;
}