按键可调时电子钟程序

发布者:WhisperingGlow最新更新时间:2021-05-25 来源: eefocus关键字:电子钟  STC89C52 手机看文章 扫描二维码
随时随地手机看文章

简介:单片机开源项目之按键可调时电子钟(矩阵按键+红外遥控按键进行调时)
此程序是基于51hei单片机开发板上面写的,如需要移植到自己的电路上,修改相应的端口即可。


/**

************************************************************************

* @file : main.c

* @author : xr

* @date : 2014年4月21日 22:23:12 - 2014年4月26日21:22:29

* @version : V1.2.3

* @brief : 按键可调时电子钟(矩阵按键+红外遥控按键进行调时) 单片机STC89C52RC MCU 晶振 11.0592MHZ

************************************************************************

*/


#include

#include "main.h"


/*定义结构体来封装从DS1302中读取的时间和日期和设置到DS1302中的时间和日期*/

struct Time {

unsigned char year; //DS1302中只存放的是年的低两位字节

unsigned char month;

unsigned char day;

unsigned char hour;

unsigned char min;

unsigned char sec;

unsigned char week;

};


/*定义结构体时间变量来保存时间和日期*/

struct Time timeBuf; //此处必须用结构体变量,不能用结构体指针否则写入失败!


unsigned char setTimeIndex = 0; //设置时间状态及设置光标位置及设置时间位置的索引值(0时正常运行,1-12为设置时间状态,1-12是设置的位置)


bit flag200ms = 0;

unsigned char thr0, tlr0;


//红外通信解码的键码和标准PC机编码映射表

unsigned char code IrdCodeMap[] = {0x45, 0x46, 0x47, //开关,Mode, 静音

0x44, 0x40, 0x43, //播放/暂停 快退, 快进

0x07, 0x15, 0x09, //EQ, 减, 加

0x16, 0x19, 0x0D, //0, 返回, U/SD

0x0C, 0x18, 0x5E, //1, 2, 3

0x08, 0x1C, 0x5A, //4, 5, 6

0x42, 0x52, 0x4A};//7, 8, 9


//外部变量声明

extern bit flagIrd; //红外数据码值接收完毕标志位

extern unsigned char irdCode[4]; //保存NEC协议解码的四个字节的数据码(用户码+用户反码,键码+键码反码)


extern void InitLCD1602();

extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str);

extern void LcdSetCoursor(unsigned char x, unsigned char y);

extern void LcdOpenCoursor();

extern void InitDS1302();

extern void GetTimeFromDS1302(struct Time * time);

extern void SetTimeToDS1302(struct Time * time);

extern void KeyDriver();

extern void KeyScan();


extern void LcdCoursorRight();

extern void LcdCoursorLeft();


void ConfgiTimer0(unsigned int xms);

void RefreshLcdShowTime();

extern void ConfigIrdByTimer1();

void IrdDrive();

void IrdKeyAction();

extern void SetTimeBcdByte(unsigned char keyNum);


/*主程序main()*/

void main()

{

unsigned char psec = 0xFF;//用于检测sec秒是否变化,若变化则刷新时间显示


ConfgiTimer0(1); //定时1ms

ConfigIrdByTimer1();//配置红外通信

InitLCD1602();

InitDS1302();


/*液晶初始化显示*/

LcdShowStr(1, 0, "*");

LcdShowStr(2, 0, "20 - - ");

LcdShowStr(12, 0, "*");

LcdShowStr(14, 0, "--");

LcdShowStr(0, 1, "time: --:--:--");


while (1)

{


KeyDriver();//检测按键动作


if (flagIrd)

{

flagIrd = 0;

IrdKeyAction();//检测红外按键动作

}


if (flag200ms == 1 && (setTimeIndex == 0)) //每200ms且setTimeIndex==0处于非设定时间状态时刷新一次时间显示

{

flag200ms = 0;

GetTimeFromDS1302(&timeBuf); //从DS1302中获取时间到timeBuf结构体指针变量的成员中

if (timeBuf.sec != psec) //当前秒值和上一次的秒值不相等

{

RefreshLcdShowTime();//刷新时间显示

psec = timeBuf.sec;//备份当前的秒值(秒寄存器值)

}

}

}


}


/*定时器T0配置*/

void ConfgiTimer0(unsigned int xms)

{

unsigned long tmp;

tmp = 11059200/12;//周期频率

tmp = (tmp * xms) / 1000;//定时xms需要的计数值

tmp = 65536-tmp;//定时装入的初值

thr0 = (unsigned char)(tmp >> 8);

tlr0 = (unsigned char)tmp;

TMOD &= 0xF0;//清零T0控制位

TMOD |= 0x01;//T0方式1,16位可设定时模式

TH0 = thr0;

TL0 = tlr0;

TR0 = 1;

ET0 = 1;

EA = 1;

}


/*将一个BCD码字节数据分解显示到LCD1602的(x, y)坐标上*/

void LcdShowBCDByte(unsigned char x, unsigned char y, unsigned char bcdbyte)

{

unsigned char str[4];


str[0] = (bcdbyte >> 4) + '0';//取BCD码的高四位字节

str[1] = (bcdbyte & 0x0F) + '0';//取BCD码的第四位字节

str[2] = '';


LcdShowStr(x, y, str);

}


/*刷新时间显示到LCD1602液晶上*/

void RefreshLcdShowTime()

{

LcdShowBCDByte(4, 0, timeBuf.year); //显示年

LcdShowBCDByte(7, 0, timeBuf.month);

LcdShowBCDByte(10, 0, timeBuf.day);

LcdShowBCDByte(6, 1, timeBuf.hour);

LcdShowBCDByte(9, 1, timeBuf.min);

LcdShowBCDByte(12, 1, timeBuf.sec);

LcdShowBCDByte(14, 0, timeBuf.week); //显示星期

}


/************以下函数功能是BCD码字节的高位和低位数字+1和-1*******************/

/*递增BCD码的高位数字*/

unsigned char IncrementBCDByteHigh(unsigned char bcdbyte)

{

if ((bcdbyte & 0xF0) < 0x90) //取bcdbyte的高四位字节是否小于9

{

bcdbyte += 0x10;//高四位字节+1

}

else

{

//高四位字节数值到9归零

bcdbyte &= 0x0F;//0000 1111

}


return (bcdbyte); //返回修改后的BCD码值

}


/*递增BCD码的低位字节数字*/

unsigned char IncrementBCDByteLow(unsigned char bcdbyte)

{

if ((bcdbyte & 0x0F) < 0x09) //取bcdbyte的低四位字节数字

{

bcdbyte += 0x01; //低位字节+1

}

else

{

//到9归零

bcdbyte &= 0xF0;//低四位清零

}


return (bcdbyte);

}


/*递减BCD码数据字节的高字节数字*/

unsigned char DescBCDByteHigh(unsigned char bcdbyte)

{

if ((bcdbyte & 0xF0) > 0) //取BCD码字节的高四位字节

{

bcdbyte -= 0x10; //高四位字节数字-1

}

else

{

//到0归9

bcdbyte |= 0x90; //或bcdbyte &= 0x9F

}


return (bcdbyte);

}


/*递减BCD码数据字节的低字节数字*/

unsigned char DescBCDByteLow(unsigned char bcdbyte)

{

if ((bcdbyte & 0x0F) > 0)

{

bcdbyte -= 0x01;//低位数字-1

}

else

{

//到0归9

bcdbyte |= 0x09;

}


return (bcdbyte);

}



/*根据setTimeIndex的值来设置光标闪烁的位置*/

void LcdRefreshSetCoursor()

{

switch (setTimeIndex)

{

case 1: LcdSetCoursor(4, 0); break;//设置年的十位数字

case 2: LcdSetCoursor(5, 0); break;

case 3: LcdSetCoursor(7, 0); break;

case 4: LcdSetCoursor(8, 0); break;

case 5: LcdSetCoursor(10, 0); break;

case 6: LcdSetCoursor(11, 0); break;

case 7: LcdSetCoursor(6, 1); break;

case 8: LcdSetCoursor(7, 1); break;

case 9: LcdSetCoursor(9, 1); break;

case 10: LcdSetCoursor(10, 1); break;

case 11: LcdSetCoursor(12, 1); break;

case 12: LcdSetCoursor(13, 1); break;

default: break;

}

LcdOpenCoursor();

}


/*递增光标闪烁位置的时间值*/

void IncTimeBysetTimeIndex()

{

switch (setTimeIndex)

{

case 1: timeBuf.year = IncrementBCDByteHigh(timeBuf.year); break;//year10++年的十位数字++

case 2: timeBuf.year = IncrementBCDByteLow(timeBuf.year); break;//年的个位数字++

case 3: timeBuf.month = IncrementBCDByteHigh(timeBuf.month); break;//月十位++

case 4: timeBuf.month = IncrementBCDByteLow(timeBuf.month); break;//月个位++

case 5: timeBuf.day = IncrementBCDByteHigh(timeBuf.day); break;

case 6: timeBuf.day = IncrementBCDByteLow(timeBuf.day); break;

case 7: timeBuf.hour = IncrementBCDByteHigh(timeBuf.hour); break;

case 8: timeBuf.hour = IncrementBCDByteLow(timeBuf.hour); break;

case 9: timeBuf.min = IncrementBCDByteHigh(timeBuf.min); break;

case 10: timeBuf.min = IncrementBCDByteLow(timeBuf.min); break;

case 11: timeBuf.sec = IncrementBCDByteHigh(timeBuf.sec); break;

case 12: timeBuf.sec = IncrementBCDByteLow(timeBuf.sec); break;

default: break;

}

RefreshLcdShowTime();//刷新时间显示

LcdRefreshSetCoursor();//刷新光标闪烁显示

}


/*递减光标位置的时间值*/

void DecTimeBySetTimeIndex()

{

switch (setTimeIndex)

{

case 1: timeBuf.year = DescBCDByteHigh(timeBuf.year); break;//年十位数字递减

case 2: timeBuf.year = DescBCDByteLow(timeBuf.year); break;//年个位数字递减

case 3: timeBuf.month = DescBCDByteHigh(timeBuf.month); break;

case 4: timeBuf.month = DescBCDByteLow(timeBuf.month); break;

case 5: timeBuf.day = DescBCDByteHigh(timeBuf.day); break;

case 6: timeBuf.day = DescBCDByteLow(timeBuf.day); break;

case 7: timeBuf.hour = DescBCDByteHigh(timeBuf.hour); break;

case 8: timeBuf.hour = DescBCDByteLow(timeBuf.hour); break;

case 9: timeBuf.min = DescBCDByteHigh(timeBuf.min); break;

case 10: timeBuf.min = DescBCDByteLow(timeBuf.min); break;

case 11: timeBuf.sec = DescBCDByteHigh(timeBuf.sec); break;

case 12: timeBuf.sec = DescBCDByteLow(timeBuf.sec); break;

default: break;

}

/*先刷新时间显示,再刷新光标闪烁显示*/

RefreshLcdShowTime();

LcdRefreshSetCoursor();

}


/*红外按键动作函数*/

void IrdKeyAction()

{

if (irdCode[2] == 0x44) // >>||

{

if (setTimeIndex == 0) //正常运行状态

{

setTimeIndex = 1;

LcdRefreshSetCoursor();//刷新光标设置显示

}

else

{

SetTimeToDS1302(&timeBuf); //将时间写入到DS1302中

setTimeIndex = 0;

RefreshLcdShowTime();

}

}

else if (irdCode[2] == 0x40) //|<<

{

if (setTimeIndex != 0)

{

LcdCoursorLeft();

}

}

else if (irdCode[2] == 0x43) // >>|

{

if (setTimeIndex != 0)

{

LcdCoursorRight();

}

}

else if (irdCode[2] == 0x15) //-

{

DecTimeBySetTimeIndex();

}

else if (irdCode[2] == 0x09) //+

{

IncTimeBysetTimeIndex();

}

else if (irdCode[2] == 0x19) //EQ

{

setTimeIndex = 0;

RefreshLcdShowTime();

}

else if (irdCode[2] == 0x16) //数字键0

{

SetTimeBcdByte(0);

}

else if (irdCode[2] == 0x0C) //数字键1

{

SetTimeBcdByte(1);

}

else if (irdCode[2] == 0x18) //数字键2

{

SetTimeBcdByte(2);

}

else if (irdCode[2] == 0x5E)

{

SetTimeBcdByte(3);

}

else if (irdCode[2] == 0x08)

{

SetTimeBcdByte(4);

}

else if (irdCode[2] == 0x1C)

{

SetTimeBcdByte(5);

}

else if (irdCode[2] == 0x5A)

{

SetTimeBcdByte(6);

}

else if (irdCode[2] == 0x42)

{

SetTimeBcdByte(7);

}

else if (irdCode[2] == 0x52)

{

SetTimeBcdByte(8);

}

else if (irdCode[2] == 0x4A)

{

SetTimeBcdByte(9);

}

else

{


}

}


/*T0中断服务*/

void Timer0_ISP() interrupt 1

{

static unsigned char counter = 0;


TH0 = thr0;

TL0 = tlr0; //1ms

counter++;

KeyScan();//按键扫描


if (counter >= 200)

{

counter = 0;

flag200ms = 1;//200ms

}

}


#ifndef _MAIN_H_

#define _MAIN_H_


sbit ADDR0 = P1^0;

sbit ADDR1 = P1^1;

sbit ADDR2 = P1^2;

sbit ADDR3 = P1^3;

sbit ENLED = P1^4;


#endif //_MAIN_H_


/**

************************************************************************

* @file : Lcd1602.c

* @author : xr

* @date : 2014年4月21日 22:23:12

* @version : V1.2.3

* @brief : LCD1602底层驱动 单片机STC89C52RC MCU 晶振 11.0592MHZ

************************************************************************

*/


#include


//LCD1602

sbit LCD1602_RS = P1^0;

sbit LCD1602_RW = P1^1;

sbit LCD1602_EN = P1^5;


#define LCD1602_DB P0


/*LCD1602忙碌等待*/

void LCD1602Wait()

{

unsigned char sta;//读取LCD1602状态字


/*读取液晶状态字之前必须将P0口全部拉高*/

LCD1602_DB = 0xFF;


LCD1602_RS = 0;

LCD1602_RW = 1;

LCD1602_EN = 0;


do

{

LCD1602_EN = 1;

sta = LCD1602_DB;//读状态字

LCD1602_EN = 0;

} while (sta & 0x80); //检测最高位是否为1,1忙碌,0空闲

}


/*LCD1602写命令*/

void LCD1602WriteCmd(unsigned char cmd)

{

//读写前要进行液晶的忙碌等待

LCD1602Wait();


LCD1602_RS = 0;

LCD1602_RW = 0;

LCD1602_EN = 0;

LCD1602_DB = cmd;

LCD1602_EN = 1;//高脉冲

LCD1602_EN = 0;//关闭液晶输出

}


/*LCD1602写数据*/

void LCD1602WriteData(unsigned char dat)

{

LCD1602Wait();


LCD1602_RS = 1;

LCD1602_RW = 0;

LCD1602_EN = 0;

LCD1602_DB = dat;//送入数据

LCD1602_EN = 1;//高脉冲

LCD1602_EN = 0;//关闭液晶输出

}


/*液晶初始化*/

void InitLCD1602()

{

LCD1602WriteCmd(0x38); //写指令38H

LCD1602WriteCmd(0x0C); //开显示不显示光标

LCD1602WriteCmd(0x06); //写入字符时字符指针++且光标++

LCD1602WriteCmd(0x01); //显示清屏

}


/*在LCD1602的坐标(x, y)位置显示str*/

void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str)

{

unsigned char addr;


if (y == 0)

{

addr = 0x00 + x; //第一行的x位置显示

}

else

{

addr = 0x40 + x; //第二行x的位置显示

}


LCD1602WriteCmd(addr + 0x80);


while (*str != '')

{

LCD1602WriteData(*str++);

}

}


/*设置光标的位置为(x, y)*/

void LcdSetCoursor(unsigned char x, unsigned char y)

{

unsigned char addr;


if (y == 0)

{

addr = 0x00 + x;

}

else

{

addr = 0x40 + x;

}

LCD1602WriteCmd(addr | 0x80); //写入光标闪烁地址

}


/*打开光标显示*/

void LcdOpenCoursor()

{

LCD1602WriteCmd(0x0F); //显示光标病使光标闪烁

}


/*关闭光标显示*/

void LcdCloseCoursor()

{

LCD1602WriteCmd(0x0C); //开显示但不显示光标

}


/**

************************************************************************

[1] [2] [3]
关键字:电子钟  STC89C52 引用地址:按键可调时电子钟程序

上一篇:采用单片机SPWM的控制应急电源逆变电路设计
下一篇:51单片机的P0口电路结构

小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved