/**
************************************************************************
* @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 <reg52.h>
#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] = '\0';
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 <reg52.h>
//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 != '\0')
{
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); //开显示但不显示光标
}
/**
************************************************************************
* @file : Ds1302.c
* @author : xr
* @date : 2014年4月21日 22:23:12
* @version : V1.2.3
* @brief : DS1302底层驱动 单片机STC89C52RC MCU 晶振 11.0592MHZ
************************************************************************
*/
#include <reg52.h>
//DS1302
sbit DS1302_SIO = P3^4;//数据IO
sbit DS1302_SCK = P3^5;//时钟线
sbit DS1302_CE = P1^7;//片选使能
/*使用外部数据类型时必须在这个文件中再一次声明*/
struct Time {
unsigned char year;//年在DS1302中只存放的是低两位,这里用unsigned char型
unsigned char month;
unsigned char day;
unsigned char hour;
unsigned char min;
unsigned char sec;
unsigned char week;
};
/*向SIO发送一个字节数据*/
void DS1302WriteByte(unsigned char byte)
{
unsigned char mask = 0x01;//低位在前,逐位发送
/*初始化DS1302时,将SIO和SCK引脚拉低,空闲*/
for (mask = 0x01; mask != 0; mask <<= 1)
{
if ((byte & mask) != 0) //上升沿DS1302进行数据的采样锁存
{
DS1302_SIO = 1;
}
else
{
DS1302_SIO = 0;
}
DS1302_SCK = 1;
DS1302_SCK = 0;//下降沿主机进行数据输出
}
//主机发送数据完成后要进行释放SIO数据线
DS1302_SIO = 1;
}
/*读取SIO数据线上的数据*/
unsigned char DS1302ReadByte()
{
unsigned char mask = 0x01;//低位在先,逐位接收
unsigned char byte = 0;
for (mask = 0x01; mask != 0; mask <<= 1)
{
if (DS1302_SIO != 0) //上升沿主机进行数据采样锁存
{
byte |= mask; //相应位置1
}
else
{
byte &= (~mask);//相应位清零
}
DS1302_SCK = 1;
DS1302_SCK = 0;//DS1302在下降沿进行数据的输出
}
return (byte);
}
/*向DS1302的reg地址寄存器中写入单个字节数据*/
void DS1302WriteSingleByte(unsigned char reg, unsigned char dat)
{
/*首先打开DS1302片选端,然后写寄存器地址reg,然后再写入数据dat*/
DS1302_CE = 1;
DS1302WriteByte((reg << 1) | 0x80);//写入寄存器地址,reg<<1留出读写方向位,reg的高位是1,次高位为0
DS1302WriteByte(dat); //写入数据
DS1302_CE = 0;
DS1302_SIO = 0;//因为板子上没有SIO口没有加上拉电阻,开漏输出不定态,将SIO=0,单片机IO口会输出稳定的0态
}
/*从DS1302的reg地址寄存器中读取一个字节的数据*/
unsigned char DS1302ReadSingleByte(unsigned char reg)
{
unsigned char byte = 0;
DS1302_CE = 1;
DS1302WriteByte((reg << 1) | 0x81); //写入寄存器地址reg并在读写方向上选择读
byte = DS1302ReadByte();
DS1302_CE = 0;
DS1302_SIO = 0; //SIO输出稳定的0状态
return (byte);
}
/*Burst模式写八个字节到DS1302八个寄存器中*/
void DS1302BurstWrite(unsigned char * date)
{
unsigned char i = 0;
DS1302_CE = 1;
DS1302WriteByte(0xBE); //写入突发模式指令(Burst模式)
for (i = 0; i < 8; i++)
{
DS1302WriteByte(date[i]);
}
DS1302_CE = 0;
DS1302_SIO = 0;
}
/*Burst模式从DS1302寄存器中连续读取八个字节的数据*/
void DS1302BurstRead(unsigned char * date)
{
unsigned char i = 0;
DS1302_CE = 1; //片选使能
DS1302WriteByte(0xBF); //突发读指令
for (i = 0; i < 8; i++)
{
date[i] = DS1302ReadByte();
}
DS1302_CE = 0;
DS1302_SIO = 0;
}
/*从DS1302中获取当前时间*/
void GetTimeFromDS1302(struct Time * time)
{
unsigned char buff[8];//保存八个寄存器中的数据
DS1302BurstRead(buff); //将DS1302寄存器中的数据读入buff数组中
time->year = buff[6];//将年寄存器中的bcd码取出来存入结构体time->year中
time->month = buff[4];
time->week = buff[5];
time->day = buff[3];
time->hour = buff[2];
time->min = buff[1];
time->sec = buff[0];
}
/*设置时间,将当前修改后的时间值设置到DS1302中*/
void SetTimeToDS1302(struct Time * time)
{
unsigned char buff[8]; //保存当前修改后的time结构体成员的值
buff[0] = time->sec; //将秒寄存器中的值存入buff[0
buff[1] = time->min;
buff[2] = time->hour;
buff[3] = time->day;
buff[4] = time->month;
buff[5] = time->week;
buff[6] = time->year;
buff[7] = 0x00; //WP写保护寄存器
DS1302BurstWrite(buff); //将buff中的值以Burst模式写入DS1302中
}
/*初始化DS1302*/
void InitDS1302()
{
struct Time code InitTime[] = {0x14, 0x04, 0x22, 0x23, 0x59, 0x59, 0x02}; //2014年4月22日23:59:59
unsigned char psec; //检测DS1302的停止位CH
/*初始化DS1302的通信引脚*/
DS1302_SCK = 0;
DS1302_CE = 0;
psec = DS1302ReadSingleByte(0x00); //0x00<<1 | 0x81=0x81, 读取秒寄存器
if ((psec & 0x80) != 0) //最高位CH=1,时钟停止
{
DS1302WriteSingleByte(7, 0x00); //去除写保护
SetTimeToDS1302(InitTime); //将初始时间设置到DS1302中
}
}
/**
**************************************************************************
* @file : Infrared.c
* @author : xr
* @date : 2014年4月23日22:01:12
* @version : V1.2.3
* @brief : 红外通信解码-NEC协议解码
**************************************************************************
*/
#include <reg52.h>
//红外通信引脚
sbit IRD = P3^3;//红外通信引脚,外部中断1引脚
bit flagIrd = 0; //红外数据码值接收完毕标志位
unsigned char irdCode[4]; //保存NEC协议解码的四个字节的数据码(用户码+用户反码,键码+键码反码)
/*红外通信-定时器T1计数进行红外接收数据判定的时间获取*/
void ConfigIrdByTimer1()
{
TMOD &= 0x0F;//清零T1控制位
TMOD |= 0x10;//使用T1方式1,16位可设定模式
TH1 = 0;
TL1 = 0;//开始时没有红外信号,清零T1计数
TR1 = 0;//开始时关闭T1计数
ET1 = 0;//关闭T1中断,只用T1计数功能
/*外部中断1配置*/
IE1 = 0;//外部中断1标志位清零
IT1 = 1;//设置外部中断1为下降沿触发
EX1 = 1;//开启外部中断
EA = 1;//开总中断
}
/*获取IRD引脚即红外引脚高电平T1计数值*/
unsigned int GetHighTimers()
{
IRD = 1;//在检测红外引脚之前,要先将IRD引脚拉高释放
TH1 = 0;
TL1 = 0;//先清零上一次的T1计数值
TR1 = 1;//开启T1计数
while (IRD)
{ //高电平T1进行计数
if (TH1 > 0x40) //0x40 * 256 * (12/11059200) * 1000 = 17.7ms
{
break; //当时间>17.7ms时我们认为是误码,强制退出
}
}
TR1 = 0; //关闭T1计数
return (TH1 * 256 + TL1); //返回IRD引脚高电平时T1的计数值
}
/*获得低电平载波时间*/
unsigned int GetLowTimers()
{
IRD = 1;//在检测红外引脚之前,要先将IRD引脚释放
TH1 = 0;
TL1 = 0; //清零T1计数值
TR1 = 1; //开启T1计数
while (!IRD)
{
//超时判断
if (TH1 > 0x40) //超出17.7ms就退出
{
break;
}
}
TR1 = 0;//关闭T1计数
return (TH1 * 256 + TL1);//返回T1计数值
}
/*外部中断1服务,判定红外并接收红外NEC解码*/
void EXINT1_ISP() interrupt 2 //EXINT1中断标号为2
{
unsigned char byte = 0;//接收解码数据
unsigned int time = 0;//接收引导码和空闲与载波的时间
unsigned char i, j;
time = GetLowTimers();//载波 引导码(9ms的载波+4.5ms的空闲):范围(8.5ms-9.5ms)
if ((time < 7833) ||(time > 8755)) //计数周期=12/11059200 计数时间=(TH1*256+TL1)*12/11059200
{
//误码,清零中断标志并退出中断
IE1 = 0;
return;
}
time = GetHighTimers();//空闲 4.5ms的空闲:范围:4ms-5ms
if ((time < 3686) || (time > 4608)) //判断时必须加上()
{
//不符合
IE1 = 0;//外部中断标志位
return; //退出中断函数
}
//引导码正确,开始循环接收用户码和键码
for (i = 0; i < 4; i++) //接收4个字节数据
{
for (j = 0; j < 8; j++) //接收8位
{
time = GetLowTimers();//载波:560us载波+560us/1.68ms空闲:340us 760us
if ((time < 313) || (time > 700))
{
//误码
IE1 = 0;//清零外部中断标志
return;
}
time = GetHighTimers();//空闲
if ((time > 313) && (time < 700)) //560us的空闲,比特值'0'time大于340us并且time<780us
{
byte >>= 1;//低位在先,逐位接收,将数据位右移到低位
}
else if ((time > 1271) && (time < 1658)) //1.68ms空闲:范围:1.38ms-1.8ms,比特值是'1'
{
byte >>= 1;//先右移一位,将这一位移到byte的最高位
byte |= 0x80;//再将这一位置1
}
else
{
//接收的是误码
IE1 = 0;//清零外部中断1标志位
return;//退出中断函数
}
}
irdCode[i] = byte;//接收码值
}
flagIrd = 1;//接收完成标志置1
IE1 = 0;//清零外部中断标志
}
/**
************************************************************************
* @file : Keyboard.c
* @author : xr
* @date : 2014年4月21日 22:23:12
* @version : V1.2.3
* @brief : 按键底层驱动 单片机STC89C52RC MCU 晶振 11.0592MHZ
************************************************************************
*/
#include <reg52.h>
//KEY
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;
static unsigned char volatile keySta[4][4] = {{1, 1, 1, 1}, {1, 1, 1, 1},
{1, 1, 1, 1}, {1, 1, 1, 1}}; //16个按键当前状态
unsigned char pdata keyCodeMap[4][4] = { //按键编码根据ASCII码进行编码
{'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键,回车键,向右键
};
/*需要使用main.c文件中的struct Time类型的结构体变量timeBuf,需要在重新进行声明*/
struct Time {
unsigned char year;
unsigned char month;
unsigned char day;
unsigned char hour;
unsigned char min;
unsigned char sec;
unsigned char week;
};
extern struct Time timeBuf;
extern unsigned char setTimeIndex;//外部变量声明
void LcdCoursorRight();
void LcdCoursorLeft();
void SetTimeBcdByte(unsigned char keyNum);
void KeyAction(unsigned char keycode);
extern void LcdRefreshSetCoursor();
extern void LcdCloseCoursor();
extern void IncTimeBysetTimeIndex();
extern void DecTimeBySetTimeIndex();
extern GetTimeFromDS1302(struct Time * time);
extern void SetTimeToDS1302(struct Time * time);
extern void RefreshLcdShowTime();
/*按键驱动,进行按键状态的判断,传递相应按键的键码到按键动作函数中*/
void KeyDriver()
{
static unsigned char pdata keyBackup[4][4] = {{1, 1, 1, 1}, {1, 1, 1, 1},
{1, 1, 1, 1}, {1, 1, 1, 1}}; //按键的备份即上一次按键的状态值
unsigned char i, j;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
if (keySta[i][j] != keyBackup[i][j])
{
//按键有动作
if (keyBackup[i][j] != 0) //根据上一次的备份值来确定本次的按键值,即按键的动作
{
//按键按下,执行动作
KeyAction(keyCodeMap[i][j]); //传递相应的键码
}
//备份本次按键的值
keyBackup[i][j] = keySta[i][j];
}
}
}
}
/*按键的动态扫描,进行按键去抖*/
void KeyScan()
{
static unsigned char keyout = 0;//按键行索引,每次都要用到上一次变化的keyout值所以定义为静态变量
static unsigned char keybuff[4][4] = {{0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF}}; //按键扫描缓冲区
unsigned char i = 0;
/*扫描按键的值,并将按键的状态值存入keybuff缓冲区中*/
keybuff[keyout][0] = (keybuff[keyout][0] << 1) | KEY_IN_1; //将第keyout行的第一个按键的扫描值存入keybuff[keyout][0]缓冲区中
keybuff[keyout][1] = (keybuff[keyout][1] << 1) | KEY_IN_2;
keybuff[keyout][2] = (keybuff[keyout][2] << 1) | KEY_IN_3;
keybuff[keyout][3] = (keybuff[keyout][3] << 1) | KEY_IN_4;
/*更新扫描消抖后按键的状态*/
for (i = 0; i < 4; i++) //每行有四个按键需要进行判断
{
if ((keybuff[keyout][i] & 0x1F) == 0x00) //5次按键扫描值都是0,表示按键是稳定按下
{
keySta[keyout][i] = 0;
}
else if ((keybuff[keyout][i] & 0x1F) == 0x1F) //5次按键的扫描值都是1,表示按键是稳定弹起
{
keySta[keyout][i] = 1;
}
}
//行++
keyout++;
keyout &= 0x03;//实现到4归零
/*根据keyout的值来确定选择哪一行按键进行扫描*/
switch (keyout)
{
case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;//扫描第1行
case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;//扫描第2行
case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;//扫描第3行
case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;//扫描第4行
default: break;
}
}
/*按键动作,根据按键的键码来执行相应的操作*/
void KeyAction(unsigned char keycode)
{
if (keycode >= '0' && keycode <= '9')
{
if (setTimeIndex != 0) //处于设置状态才相应
SetTimeBcdByte(keycode - '0'); //数字键设置时间
}
else if (keycode == 0x26) //向上键进行光标闪烁位置上的数值的++
{
IncTimeBysetTimeIndex();//递增BCD码的高位或低位数字
}
else if (keycode == 0x27) //向右键进行进行光标位置的右移
{
if (setTimeIndex != 0) //处于设置状态才相应
LcdCoursorRight();//递减BCD码的高位或低位数字
}
else if (keycode == 0x25) //向左键进行光标闪烁位置的左移
{
if (setTimeIndex != 0)
LcdCoursorLeft();
}
else if (keycode == 0x28) //向下键光标闪烁位置上的数值的递减
{
DecTimeBySetTimeIndex();
}
else if (keycode == 0x1B) //ESC键,取消当前的设置
{
setTimeIndex = 0;//时钟正常运行索引=0
LcdCloseCoursor();//关闭光标闪烁
}
else if (keycode == 0x0D) //回车键,进行设置时间和恢复时钟运行的状态的切换
{
if (setTimeIndex == 0) //正常运行状态,开始切换到设置状态
{
setTimeIndex = 1;
LcdRefreshSetCoursor();//刷新光标闪烁位置并让光标闪烁
}
else
{
//写入设置的时间值,并切换到正常运行状态
SetTimeToDS1302(&timeBuf);
GetTimeFromDS1302(&timeBuf);
setTimeIndex = 0;
LcdCloseCoursor();//关闭光标闪烁
}
}
}
/*setTimeIndex++,实现设置光标闪烁的右移*/
void LcdCoursorRight()
{
if (setTimeIndex < 12)
setTimeIndex++;//光标设置索引++
else
setTimeIndex = 1;
LcdRefreshSetCoursor();
}
/*setTimeIndex--,实现设置光标闪烁的左移*/
void LcdCoursorLeft()
{
if (setTimeIndex > 1)
setTimeIndex--;//光标设置索引--
else
setTimeIndex = 12;
LcdRefreshSetCoursor();
}
/*设置BCD码的高位数字*/
unsigned char SetBCDHighByte(unsigned char bcd, unsigned char keyNum)
{
bcd &= 0x0F;//清零高位
bcd |= (keyNum << 4); //keyNum移至高位
return (bcd);
}
/*设置BCD码的低位数字*/
unsigned char SetBCDLowByte(unsigned char bcd, unsigned char keyNum)
{
bcd &= 0xF0;//低位清零
bcd |= keyNum; //将keyNum数字放到低位
return (bcd); //返回改变后的bcd字节数据
}
/*通过按键值设置时间值(设置时间值的高位和低位数字)*/
void SetTimeBcdByte(unsigned char keyNum) //传递按键的数值
{
switch (setTimeIndex)
{
case 1: timeBuf.year = SetBCDHighByte(timeBuf.year, keyNum); break;//设置年的高位BCD字节
case 2: timeBuf.year = SetBCDLowByte(timeBuf.year, keyNum); break; //设置年的低位BCD字节
case 3: timeBuf.month = SetBCDHighByte(timeBuf.month, keyNum); break;
case 4: timeBuf.month = SetBCDLowByte(timeBuf.month, keyNum); break;
case 5: timeBuf.day = SetBCDHighByte(timeBuf.day, keyNum); break;
case 6: timeBuf.day = SetBCDLowByte(timeBuf.day, keyNum); break;
case 7: timeBuf.hour = SetBCDHighByte(timeBuf.hour, keyNum); break;
case 8: timeBuf.hour = SetBCDLowByte(timeBuf.hour, keyNum); break;
case 9: timeBuf.min = SetBCDHighByte(timeBuf.min, keyNum); break;
case 10: timeBuf.min = SetBCDLowByte(timeBuf.min, keyNum); break;
case 11: timeBuf.sec = SetBCDHighByte(timeBuf.sec, keyNum); break;
case 12: timeBuf.sec = SetBCDLowByte(timeBuf.sec, keyNum); break;
}
RefreshLcdShowTime(); //刷新时间值显示
LcdCoursorRight(); //光标闪烁右移
LcdRefreshSetCoursor(); //刷新光标闪烁
}