目 录
第1章 项目背景
1.1 背景及意义
在当今数字化时代,电子产品已经渗透到我们日常生活的方方面面。数字电子钟作为时间管理和展示的重要工具,具有广泛的应用前景。本课设选用了STC89C52RC单片机作为主控芯片,同时结合DS1302时钟芯片等外围电路,旨在设计一款功能强大、稳定可靠的数字电子钟。除了基础的时间显示功能外,还实现了遥控接收电路,可以方便地通过遥控器对钟表进行时间调整和操作。这一设计不仅是数字电子技术的应用,更是对硬件设计、嵌入式系统开发和电子通信等多个领域知识的综合运用。
这个数字电子钟课设对我来说具有重要的意义。首先,通过这个项目,我将深入了解STC89C52RC单片机的工作原理及其在数字电子钟中的应用。其次,与DS1302时钟芯片等外围电路的结合将使我掌握多种芯片之间的通信接口和协作方式,提升了我的硬件设计水平。尤其是设计遥控接收电路,这将使我深入了解无线通信和遥控技术的应用,对我的电子通信技能和知识有着积极的促进作用。通过这个项目,我将全面提升自己在硬件设计、嵌入式系统开发和电子通信领域的能力,为未来的学术和职业发展打下坚实基础。
1.2 任务要求
为更好地完成该项目,需要做到一下的4个任务要求:
(1)确定STC89C52RC单片机作为主控芯片,理解其引脚排序和基本功能,包括时钟、定时器、串口等核心模块的应用。
(2)利用集成DS1302时钟芯片,实现精准的时间信息获取与管理。设置时钟芯片的时钟校准功能,确保长时间运行后的时间精准性。
(3)使用LCD1602液晶模块,实现时间的直观显示。并考虑设计合适的显示格式,包括年、月、日、时、分、秒、星期。
(4)利用HS0038红外接收模块,接收来自遥控器的信号。并解码遥控信号,实现对数字电子钟的时间调整和操作。
第2章 设计方案
2.1 系统方案
无线遥控设定时间的电子钟设计难度中等,设计思路也相当的明确。如图2-1所示,首先是遥控器给HS0038红外模块发送响应的指令,红外模块接收到数据后经过软件程序进行解码得到正确的指令,再通过DS1302时钟芯片对数据进行读写,最后将需要的数据显示在LCD1602液晶模块上。
图2-1 系统设计框图
2.2 硬件设计
在硬件的设计过程中,首先是对硬件的选取,本项目中考虑到身边的现有资源因素,选择了我比较熟悉的STC89C52RC单片机作为主控芯片,红外接收模块为HS0038,时钟芯片采用比较广泛的DS1302,以及LCD1602液晶显示模块。设计时用到的硬件外设的电路连接如图2-2所示。
图2-2 硬件设计电路图
2.3 软件设计
在该项目中工作量占比最大的部分就是软件的编写,主要有四个部分,分别是LCD1602液晶模块的显示、DS1302时钟芯片数据的读写、HS0038红外接收模块数据的解码以及整个系统运行的执行逻辑。如图2-3所示,当给开发板上电后,DS1302会读取寄存器中时间的初始数据,并显示在LCD1602液晶模块上,知道HS0038红外接收模块收到来自遥控器的指令后会进入中断,此时可以利用遥控器进行调时,调时结束进行数据保存,DS1302时钟芯片会重新读取数据,并将新设定的时间显示在LCD上。
图2-3 软件设计流程图
2.3.1 液晶模块数据的显示
查看LCD1602的数据手册可知,在对液晶模块进行操作时,需要执行三个步骤, (1)液晶模块写命令。(2)液晶模块写数据。(3)液晶模块的初始化。
如图2-3-1.1所示,当我们进行写命令时,首先将LCD1602的RS引脚、R/W引脚、E引脚设置为低电平;然后将数据命令传送给数据端口,再延时大约1毫秒;接着将E引脚设置为高电平,再延时大约1毫秒;最后将E引脚设置成低电平。从而完成LCD1602的写命令操作。
从时序图可以看出,当进行写数据时,只需要在写命令的基础上,把RS引脚设置为高电平,其他保持不变即可。
最后是液晶模块的初始化,初始化的过程为显示模式设置;显示开及光标设置;显示光标移动设置;显示清屏。相应的执行命令是0x38;0x0C;0x06;0x01。具体主要的代码可见附录。
图2-3-1.1 液晶模块写操作时序图
图2-3-1.2 液晶模块子程序流程图
2.3.2 时钟芯片数据的读写
要完成时钟芯片数据的读写,需要依据DS1302时钟芯片数据手册里的时序图,如图2-3-2.1所示,DS1302时钟芯片在读写数据时,每次都会读写两个字节的数据,第一个字节是写命令,第二个字节才是要读写的数据。
当进行单字节读操作时,首先要将CE使能端设置为高电平,然后在时钟的上升沿进行写命令,经过八个时钟上升沿后,再经过时钟的下降沿进行读数据操作;单字节写操作也是同理,唯一不同的点是,在单字节写时,所有数据的传输都是在时钟的上升沿完成。另外,值得注意得是,DS1302在读写数据时是从数据的低位开始的。
图2-3-2.1 时钟模块单字节读写操作时序图
图2-3-2.2 时钟模块子程序流程图
2.3.3 红外模块数据的解码
在该项目中所使用红外遥控器采用的是NEC协议,所以解码过程需要清晰的知道NEC协议编码的工作原理。
如图2-3-3.1所示,在传输数据的过程中首先有一个起始信号,起始信号是由9毫秒的低电平和4.5毫秒的高电平组成。发送一组数据的总时长约为110毫秒。一组数据格式由地址码、地址反码、命令以及命令反码四部分组成,每一部分都是8个bit,所以一个完整的数据型号一共32个bit。NEC协议中,逻辑0是由560微秒低电平和560微秒的高电平组成。逻辑1是由560微秒的低电平和1690微秒的高电平组成。所以在软件解码的过程中,可以通过高电平持续的时间来判断逻辑0和逻辑1。
图2-3-3.1 NEC协议编码说明
图2-3-3.2 红外解码子程序流程图
另外,当传输完一个完整的数据后,如果按着遥控器按钮不放,就会触发连发信号。连发信号是由9毫秒的低电平和2.25毫秒的高电平组成。同样,在数据传输的过程中,依然是先传输数据的最低位。
第3章 实物测试
3.1 测试方法
在完成了硬件部分和软件部分后,需要进一步进行实物测试,具体的测试方法如下,如图3-1所示。
图3-1红外遥控器和对应的键值
(1)按下遥控器上“Mode”键(对应键值为46)开始调时,此时对应年的位置会开始闪烁。
(2)按下“右移”键(对应键值为43)会在月、日、时、分、秒、星期之间跳转,并依次顺序循环,同样地,按下“左移”键(对应键值为40)也会有同样地效果。
(3)当对应的位置开始闪烁时,按下“VOL+”键(对应键值为09)使该位置数字加1;同样地,按下“VOL-”键(对应键值为15)使该位置数字减1。
(4)调整好正确的时间后,再按下“Mode”键保存数据。这时会从当前设置的时间开始走时。
另外,为了增加项目的趣味性,在遥控按键按下的同时会由“嘀”的提示音。
3.2 测试结果
图3-2实物测试图
经过上述的方法进行测试,顺利的达到了想要的预期效果。从测试结果来看,采用DS1302时钟芯片后时间走时准确,几乎没有误差;HS0038红外接收模块接收数据反应很快,说明软件部分的解码很有效。
同时,由于加了按键提示音,在进行调时时有了更多的趣味性。由此可以得出结果,本课程设计在满足基本任务的基础之上,又适当增加了“闪烁”、“提示音”等内容,完成效果很好。
第4章 总结与展望
4.1 总结
对于有一定单片机基础的我来说,这个题目难度不是很大。经过这次课设,我深入了解了单片机的应用,掌握了STC89C52RC芯片的使用方法,并成功地完成了数字电子钟电路的设计。该设计以STC89C52RC为主控芯片,扩展了DS1302等外围电路,为数字钟提供了稳定的时钟信息源。同时,设计的遥控接收电路能够接收信息,用于数字钟的时间调整,增强了其实用性。
在课设过程中,我遇到了许多挑战,如如何确保时钟的精确性、如何优化遥控接收电路的性能等。但通过查阅资料、不断尝试,最终克服了这些困难。这次经历不仅提升了我的专业技能,更让我对未来职业生涯规划有了一定的思考。在未来的学习和工作中,我将继续保持这种积极的态度和进取的精神。
4.2 自我评价
对于这次单片机课设,我给予了极大的热情和专注。在设计过程中,我始终坚持追求卓越,力求每一个细节都做到完美。面对挑战,我积极寻找解决方案,不畏难、不退缩。这种执着和毅力,我认为是我最大的收获。
同时,我也意识到自己在某些方面仍有不足。例如,在时间管理上,我有时会过于追求完美,导致进度稍有滞后。但这也是一种成长的机会,提醒我在今后的学习和工作中,要更加注重效率与质量的平衡。
总的来说,这次课设让我对自己有了更深入的了解,也为我未来的学习和实践提供了宝贵的经验。我坚信,在不断的自我反思和努力下,我能成为一名更加优秀的电子工程师。
参考文献
[1]段刚.基于51单片机的无线遥控技术[J].科技促进发展,2011,(S1):24.
[2]施新华.利用单片机实现的红外遥控技术[J].上海电机学院学报,2006,(03):69-71.
[3]赵春红,杨勇.基于单片机和无线电遥控技术的密码锁设计[J].测控技术,2005,(09):9-11+31.DOI:10.19708/j.ckjs.2005.09.004
[4]罗志刚.基于单片机的可调时钟设计[J].电子制作,2018,(19):71-74.DOI:10.16589/j.cnki.cn11-3571/tn.2018.19.026
[5]代少君,刘健犇,张业茂等.一种基于单片机的时钟可调脉冲电源[J].湖北电力,2017,41(03):39-42+50.DOI:10.19308/j.hep.2017.03.009
[6]谭建斌,郭金佳,林涛.基于STC15单片机的太阳能自动跟踪系统的研究[J].科技创新与应用,2018,(08):34-35.
[7]王晨旭.基于51单片机电子时钟的设计与实现[J].科技风,2019,(07):79.DOI:10.19392/j.cnki.1671-7341.201907071
[8]刘翠玲,吕娣,丛俊玲.基于AT89S51单片机数字电子钟设计与实现[J].信息系统工程,2011,(12):16-17.
[9]蒋汝根,钱丹浩.基于AT89C51单片机的交通灯模拟控制系统[J].无锡商业职业技术学院学报,2006,(06):8-9+12.
下面是main.c
//#include <REGX52.H>
#include "public.h"
#include "LCD1602.h"
#include "DS1302.h"
#include "Timer0.h"
#include "ired.h"
#include "beep.h"
unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;
void TimeShow(void)//时间显示功能
{
DS1302_ReadTime();//读取时间
LCD_ShowNum(2,9,DS1302_Time[0],2);//显示年
LCD_ShowNum(2,12,DS1302_Time[1],2);//显示月
LCD_ShowNum(2,15,DS1302_Time[2],2);//显示日
LCD_ShowNum(1,1,DS1302_Time[3],2);//显示时
LCD_ShowNum(1,4,DS1302_Time[4],2);//显示分
LCD_ShowNum(1,7,DS1302_Time[5],2);//显示秒
LCD_ShowNum(1,16,DS1302_Time[6],1);//显示星期
}
void TimeSet(void)//时间设置功能
{
if(gired_data[2]==0x43)//右移键按下
{
beep_alarm(500,5);//短暂提示音
gired_data[2]=0;
TimeSetSelect++;//设置选择位加1
TimeSetSelect%=7;//越界清零
}
if(gired_data[2]==0x40)//左移键按下
{
beep_alarm(500,5);//短暂提示音
gired_data[2]=0;
TimeSetSelect--;//设置选择位减1
TimeSetSelect%=7;//越界清零
}
if(gired_data[2]==0x09)//加1键按下
{
beep_alarm(500,5);//短暂提示音
gired_data[2]=0;
DS1302_Time[TimeSetSelect]++;//时间设置位数值加1
if(DS1302_Time[0]>99){DS1302_Time[0]=0;}//年越界判断
if(DS1302_Time[1]>12){DS1302_Time[1]=1;}//月越界判断
if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
{
if(DS1302_Time[2]>31){DS1302_Time[2]=1;}//大月
}
else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
{
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}//小月
}
else if(DS1302_Time[1]==2)
{
if(DS1302_Time[0]%4==0)
{
if(DS1302_Time[2]>29){DS1302_Time[2]=1;}//闰年2月
}
else
{
if(DS1302_Time[2]>28){DS1302_Time[2]=1;}//平年2月
}
}
if(DS1302_Time[3]>23){DS1302_Time[3]=0;}//时越界判断
if(DS1302_Time[4]>59){DS1302_Time[4]=0;}//分越界判断
if(DS1302_Time[5]>59){DS1302_Time[5]=0;}//秒越界判断
if(DS1302_Time[6]>7){DS1302_Time[6]=1;}//星期越界判断
}
if(gired_data[2]==0x15)//减1键按下
{
beep_alarm(500,5);//短暂提示音
gired_data[2]=0;
DS1302_Time[TimeSetSelect]--;//时间设置位数值减1
if(DS1302_Time[0]<0){DS1302_Time[0]=99;}//年越界判断
if(DS1302_Time[1]<1){DS1302_Time[1]=12;}//月越界判断
if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
{
if(DS1302_Time[2]<1){DS1302_Time[2]=31;}//大月
if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
}
else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
{
if(DS1302_Time[2]<1){DS1302_Time[2]=30;}//小月
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
}
else if(DS1302_Time[1]==2)
{
if(DS1302_Time[0]%4==0)
{
if(DS1302_Time[2]<1){DS1302_Time[2]=29;}//闰年2月
if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
}
else
{
if(DS1302_Time[2]<1){DS1302_Time[2]=28;}//平年2月
if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
}
}
if(DS1302_Time[3]<0){DS1302_Time[3]=23;}//时越界判断
if(DS1302_Time[4]<0){DS1302_Time[4]=59;}//分越界判断
if(DS1302_Time[5]<0){DS1302_Time[5]=59;}//秒越界判断
if(DS1302_Time[6]<1){DS1302_Time[6]=7;}//星期越界判断
}
//更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能
if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(2,9," ");}
else {LCD_ShowNum(2,9,DS1302_Time[0],2);}
if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(2,12," ");}
else {LCD_ShowNum(2,12,DS1302_Time[1],2);}
if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(2,15," ");}
else {LCD_ShowNum(2,15,DS1302_Time[2],2);}
if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(1,1," ");}
else {LCD_ShowNum(1,1,DS1302_Time[3],2);}
if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(1,4," ");}
else {LCD_ShowNum(1,4,DS1302_Time[4],2);}
if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(1,7," ");}
else {LCD_ShowNum(1,7,DS1302_Time[5],2);}
if(TimeSetSelect==6 && TimeSetFlashFlag==1){LCD_ShowString(1,16," ");}
else {LCD_ShowNum(1,16,DS1302_Time[6],1);}
}
void main()
{
LCD_Init();
ired_init();
DS1302_Init();
Timer0Init();
LCD_ShowString(1,1," : : ");//静态字符初始化显示
LCD_ShowString(2,9," - - ");
LCD_ShowNum(2,7,20,2);
DS1302_SetTime();//设置时间
while(1)
{
if(gired_data[2]==0x46)//mode键按下
{
beep_alarm(500,5);//短暂提示音
gired_data[2]=0;
if(MODE==0){MODE=1;TimeSetSelect=0;}//功能切换
else if(MODE==1){MODE=0;DS1302_SetTime();}
}
switch(MODE)//根据不同的功能执行不同的函数
{
case 0:TimeShow();break;
case 1:TimeSet();break;
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=500)//每500ms进入一次
{
T0Count=0;
TimeSetFlashFlag=!TimeSetFlashFlag;//闪烁标志位取反
}
}
下面是LCD1602.c
#include <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在LCD1602指定位置开始显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length 要显示数字的长度,范围 :1~5
* @retval 无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围:1~4
* @retval 无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
}
}
下面是LCD1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__
//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
#endif
下面是DS1302.c
#include <REGX52.H>
//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
//寄存器写入地址/指令定义
#define DS1302_SECOND 0x80
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84
#define DS1302_DATE 0x86
#define DS1302_MONTH 0x88
#define DS1302_DAY 0x8A
#define DS1302_YEAR 0x8C
#define DS1302_WP 0x8E
//时间数组,索引0~6分别为年、月、日、时、分、秒、星期,设置为有符号的便于<0的判断
char DS1302_Time[]={23,01,01,12,59,55,7};
/**
* @brief DS1302初始化
* @param 无
* @retval 无
*/
void DS1302_Init(void)
{
DS1302_CE=0;
DS1302_SCLK=0;
}
/**
* @brief DS1302写一个字节
* @param Command 命令字/地址
* @param Data 要写入的数据
* @retval 无
*/
void DS1302_WriteByte(unsigned char Command,Data)
{
unsigned char i;
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
for(i=0;i<8;i++)
{
DS1302_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
DS1302_CE=0;
}
unsigned char DS1302_ReadByte(unsigned char Command)
{
unsigned char i,Data=0x00;
Command|=0x01; //将指令转换为读指令
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0;
DS1302_SCLK=1;
}
for(i=0;i<8;i++)
{
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO){Data|=(0x01<<i);}
}
DS1302_CE=0;
DS1302_IO=0; //读取后将IO设置为0,否则读出的数据会出错
return Data;
}
/**
* @brief DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
* @param 无
* @retval 无
*/
void DS1302_SetTime(void)
{
DS1302_WriteByte(DS1302_WP,0x00);
DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
// DS1302_WriteByte(DS1302_WP,0x80);//写保护
}
/**
* @brief DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
* @param 无
* @retval 无
*/
void DS1302_ReadTime(void)
{
unsigned char Temp;
Temp=DS1302_ReadByte(DS1302_YEAR);
DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制后读取
Temp=DS1302_ReadByte(DS1302_MONTH);
DS1302_Time[1]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DATE);
DS1302_Time[2]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_HOUR);
DS1302_Time[3]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MINUTE);
DS1302_Time[4]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_SECOND);
DS1302_Time[5]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DAY);
DS1302_Time[6]=Temp/16*10+Temp%16;
}
下面是DS1302.h
#ifndef __DS1302_H__
#define __DS1302_H__
//外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期,设置为有符号的便于<0的判断
extern char DS1302_Time[];
void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);
#endif
下面是Delay.c
#include "Delay.h"
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
下面是Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay(unsigned int xms);
#endif
下面是红外解码ired.c
#include "ired.h"
u8 gired_data[4];//存储4个字节接收码(地址码+地址反码+控制码+控制反码)
void ired_init(void)
{
IT0=1; //下降沿触发
EX0=1; //打开中断0允许
EA=1; //打开总中断
IRED=1; //初始化端口
}
void ired() interrupt 0 //外部中断0服务函数
{
u8 ired_high_time=0;
u16 time_cnt=0;
u8 i=0,j=0;
if(IRED==0)
{
time_cnt=1000;
while((!IRED)&&(time_cnt))//等待引导信号9ms低电平结束,若超过10ms强制退出
{
delay_10us(1);//延时约10us
time_cnt--;
if(time_cnt==0)return;
}
if(IRED)//引导信号9ms低电平已过,进入4.5ms高电平
{
time_cnt=500;
while(IRED&&time_cnt)//等待引导信号4.5ms高电平结束,若超过5ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
for(i=0;i<4;i++)//循环4次,读取4个字节数据
{
for(j=0;j<8;j++)//循环8次读取每位数据即一个字节
{
time_cnt=600;
while((IRED==0)&&time_cnt)//等待数据1或0前面的0.56ms结束,若超过6ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
time_cnt=20;
while(IRED)//等待数据1或0后面的高电平结束,若超过2ms强制退出
{
delay_10us(10);//约0.1ms
ired_high_time++;
if(ired_high_time>20)return;
}
gired_data[i]>>=1;//先读取的为低位,然后是高位
if(ired_high_time>=8)//如果高电平时间大于0.8ms,数据则为1,否则为0
gired_data[i]|=0x80;
ired_high_time=0;//重新清零,等待下一次计算时间
}
}
}
if(gired_data[2]!=~gired_data[3])//校验控制码与反码,错误则返回
{
for(i=0;i<4;i++)
gired_data[i]=0;
return;
}
}
}
下面是红外解码ired.h
#ifndef _ired_H
#define _ired_H
#include "public.h"
//管脚定义
sbit IRED=P3^2;
//声明变量
extern u8 gired_data[4];
//函数声明
void ired_init(void);
#endif
下面是public.c
#include "public.h"
/*******************************************************************************
* 函 数 名 : delay_10us
* 函数功能 : 延时函数,ten_us=1时,大约延时10us
* 输 入 : ten_us
* 输 出 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
/*******************************************************************************
* 函 数 名 : delay_ms
* 函数功能 : ms延时函数,ms=1时,大约延时1ms
* 输 入 : ms:ms延时时间
* 输 出 : 无
*******************************************************************************/
void delay_ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
下面是public.h
#ifndef _public_H
#define _public_H
#include "reg52.h"
typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;
void delay_10us(u16 ten_us);
void delay_ms(u16 ms);
#endif
下面是定时器timer0.c
#include <REGX52.H>
/**
* @brief 定时器0初始化,1毫秒@12.000MHz
* @param 无
* @retval 无
*/
void Timer0Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
/*定时器中断函数模板
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
}
}
*/
下面是定时器timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0Init(void);
#endif
下面是蜂鸣器beep.c
#include "beep.h"
/*******************************************************************************
* 函 数 名 : beep_alarm
* 函数功能 : 蜂鸣器报警函数
* 输 入 : time:报警持续时间
fre:报警频率
* 输 出 : 无
*******************************************************************************/
void beep_alarm(u16 time,u16 fre)
{
while(time--)
{
BEEP=!BEEP;
delay_10us(fre);
}
}
下面是蜂鸣器beep.h
#ifndef _beep_H
#define _beep_H
#include "public.h"
//管脚定义
sbit BEEP=P2^5;
//函数声明
void beep_alarm(u16 time,u16 fre);
#endif