按键可调时电子钟程序

来源:本站
导读:目前正在解读《按键可调时电子钟程序》的相关信息,《按键可调时电子钟程序》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《按键可调时电子钟程序》的详细说明。
简介:单片机开源项目之按键可调时电子钟(矩阵按键+红外遥控按键进行调时)
此程序是基于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 <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] = '';

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 != '')

{

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(); //刷新光标闪烁

}

提醒:《按键可调时电子钟程序》最后刷新时间 2024-03-14 01:09:35,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《按键可调时电子钟程序》该内容的真实性请自行鉴别。