摘要随着人们生活水平的提高和生活节奏的加快,对时间的要求越来越高,精准数字计时的消费需求也是越来越多。
我国生产的电子万年历有很多种,总体上来说以研究多功能电子万年历为主,使万年历除了原有的显示时间,日期等基本功能外,还具有闹铃,报警等功能。商家生产的电子万年历更从质量,价格,实用上考虑,不断的改进电子万年历的设计,使其更加的具有市场。
本设计为软件,硬件相结合的一组设计。在软件设计过程中,应对硬件部分有相关了解,这样有助于对设计题目的更深了解,有助于软件设计。基本的要了解一些主要器件的基本功能和作用。
此次课程设计使用专门的时钟芯片DS1302在液晶显示器LCD12864上显示时钟,并能通过按键对其进行调试和校准以及实现年月日。DS1302是一种高性能、低功耗、带RAM的实时时钟芯片,它能够对时分秒进行精确计时。将RST、SCLK、I/O引脚分别与STC12C5A60S2单片机P2^0、P2^1、P2^2相连,使单片机对其进行读写操作,把读出的数据送到LCD12864上显示,通过4个按键实现对时间的调整和闹铃的设定和调整,具有显示直观、电路简洁、使用方便简单等优点。
第1章 绪论1.万年历简介电子万年历是一种非常广泛的日常计时工具,它可以对年、月、日、周、时、分、秒进行精确计时在日常生活中极为实用,DS1302是常用的时钟芯片,价格低廉,精度高且对与数字电子万年历采用直观的数字显示,还具有时间校准等功能。
1.2设计主要内容1.2.1设计方案- 显示部分:通过12864液晶显示模块显示年、月、日、时、分、秒,并可以切换显示时间设置、闹钟设置和波形图。
- 按键部分:本次设计将采用
矩阵键盘作为功能按键,需要的功能按键有:日期/时间切换键,控制键,加键,减键,闹钟关闭键,贪睡键,方波输出键,正弦波输出键。
(3)时钟模块:采用时钟芯片DS1302。美国DALLAS公司推出的具有涓细电流充电能的低功耗实时时钟电路DS1302。它可以对年、月、日、周日、时、分、秒进行计时,还具有闰年补偿等多种功能,而且DS1302的使用寿命长,误差小。对于数字电子万年历采用直观的数字显示,可以同时显示年、月、日、周日、时、分、秒,还具有时间校准等功能。利用单片机对其进行读写操作,然后将读出的时间数据送到LCD12864上显示。
- 查阅相关资料,了解老师所给的资料里面开发板的线路连接和各个模块资料;
- 用protel实现硬件的仿真,画出原理图以及PCB图;
- 参照老师所给示例程序和网上资料用keil 软件进行单片机的编程设计,运行调试;
- 将keil生成的hex文件烧录进入开发板,进行测试。
1.2.3主要功能(1)使用扫描式键盘,通过按键实现设定、修改日期和时间,省电(关闭显示)等功能,键盘功能自行定义;
(2)实现倒计时功能,倒计时时间由键盘输入设定,定时时间到LED闪烁;
(3)实现4路可调闹钟功能,当4路闹铃中的任一路时间到时,通过不同的铃声显示闹铃时间到,且铃声持续时间不超过1分钟。1分钟以内若按下停止键,铃声停止;若按下贪睡键,铃声停止,3分钟以后再次响铃;1分钟以内若不按任何键,铃声自动停止,3分钟后再次响铃。
(4)通过2个按键,分别实现输出10Hz的正弦波及方波。
第2章 系统硬件设计
2.1硬件组成硬件以STC12C5A60S2单片机为核心,外部扩展LCD12864液晶显示模块、按键控制键盘模块、DS1302时钟芯片,蜂鸣器模块实现动态显示时间日期,闹铃设置,倒计时,波形图显示等功能。
基本硬件结构图如图2.1所示。
图2.1 万年历基本硬件结构图
2.2显示模块 图2.2 数码管
显示模块用的是两个4位7段数码管,切换显示比分和时间,例如比分显示为111--111,时间显示为01-12-00:1为第一节,12-00为倒计时。
2.3时钟电路、复位电路模块
图2.3 时钟电路、复位电路
时钟电路在单片机系统中有着非常重要的作用,是保证系统正常工作的基础。在一个单片机应用系统中,时钟是保证系统正常工作的基准震荡定时信号,主要由晶振和外围电路组成,晶振频率的大小决定了系统工作的快慢。图中采用12MHZ的晶振,另外有两个30P的电容,两晶振引脚分别连到XTAL1和XTAL2振荡脉冲输入引脚。复位电路是单片机的初始化操作,图中RST引脚是复位信号的输入端。具体连接如图2.3所示。
2.4按键模块S2控制甲队加分,S5控制甲队减分;S3控制乙队加分,S6控制乙队减分;S8控制比分互换;S13控制比分切换到时间,S11控制时间切换到比分;S9为比分清零;S12为增加节数,S21为减少节数。具体连接分布如图2.4所示。
图2.4 矩阵键盘电路
第3章 系统软件设计3.1 主程序框图当程序启动时,先进入各个模块初始化,初始化完成后,进入闹钟判断,若是闹钟设置的时间到了,则进入闹钟中断子程序中,处理闹钟中断;若是没有,则是在数码管上显示初始化程序里的时间。接着是调用键盘扫描程序,判断控制键是否按下,若按下,则i的值加1,同时进入相应的功能程序。其中主要的功能程序有:4个闹钟时间的设置,波形输出,时间的设置,以及倒计时功能。当它们执行完相应的子程序后,则返回时间显示界面。
图3.1 主程序框图
3.2控制键程序 控制键按下时,程序的变量i会相应地加1,而且同时进入相应的功能程序,例如i=1、2、3时,会进入相应地闹钟时间设置,分别设置闹钟1的时/分/秒。同理,i=4~12分别设置闹钟2、3、4的时间。然后i=13、14、15、16、17、18接下来是正常的时间设置,分别设置年/月/日/小时/分钟/秒钟。I=19时,进入波形输出,i=20时,进入倒计时功能。最后程序返回时间显示,同时i的值清零。
图3.2.控制键程序流程图
东北大学课程设计报告 参考文献
3.3日期/时间设置流程图控制键按下时,i=13、14、15,分别进入年/月/日的设置,然后通过加/减键来使当前键值加1或者减1;当i=1、4、7、10、16时,进入小时设置;当i=2、5、8、11、17进入分钟设置;i=3、6、9、12、18,进入秒钟设置。
图3.3时间/日期设置程序流程图
3.4 DS1302程序 图3.4DS1302时控流程图
3.4.1 DS1302读写程序void SendByte(uchar Command,SendDat)
{ //字节发送子程序。入口参数:命令字,数据
uchar i;
RST=0;CLK=0;RST=1;
for(i=0;i<8;i++) //发送命令字
{
DP=Command&0x01; //取一位送数据口
CLK=0;CLK=1; //产生一个上升沿
Command>>=1;
}
for(i=0;i<8;i++) //发送数值
{
DP=SendDat&0x01;
CLK=0;CLK=1;
SendDat>>=1;
}
RST=0;
}
uchar ReadByte(uchar Command) //读字节子程序。入口参数:命令字
{ //出口参数:数值
uchar i,RecDat=0;
RST=0;CLK=0;RST=1;
for(i=0;i<8;i++) //发送命令字
{
DP=Command&0x01;
CLK=0;CLK=1;
Command>>=1;
}
for(i=0;i<8;i++) //读入数值
{
CLK=1;CLK=0; //产生一个下降沿
if(DP) RecDat|=0x01<<i; 读入数据[="" align]" }[="" rst="0;" return(recdat);="" 返回数值[="" null,="" left][size="12pt]当闹钟设置的时间到了时,这时程序就会进入闹钟中断程序。例如闹钟1设置的时间到了,就会使蜂鸣器响起闹铃,键盘扫描停止键是否按下,若在一分钟内按下,则闹钟停止,同时键盘扫描判断贪睡键是否按下,若按下,则三分钟后再次响铃,若一分钟内没有键按下,自动停止,然后3分钟后响铃;
图3.5闹钟中断程序流程图
第4章 实验与分析为了实现篮球计分器的功能,我们必须通过不断地测试和分析,实现软件与硬件融为一体。从内容上,我们将它分为比分的测试和时间的测试。
4.1 篮球计分器的比分测试4.1.1相关功能键的测试- 按下S2键控制A队加1分(2)按下S5键控制A队减1分(3)按下S3键控制B队加1分(4)按下S6键控制B队减1分(5)按下S8键控制比分互换(6)按下S9键为比分清零
如图4.1所示,当前比分为24比17 图4.1篮球计分器比分测试图
4.1.2出现的问题与分析分析:出现这种情况可能的原因:一、硬件方面,可能是矩阵键盘的S2和S3连线问题,使按下它们两个键时,实现功能相同,二、软件方面,可能是定义该功能时,重复使用了该键。沿着这两个思路,我们首先将老师给的例程烧录进去,该例程实现的功能就是按下不同的键,数码管将会显示不同的数字,当我们分别按下S2和S3时,出现的数字不同,那么证明硬件方面没问题。然后查了一遍程序,果然是编写程序时,重复使用了该键。
分析:可能原因一、S8键可能出现故障了,二,编写程序时,比分交换的那段代码逻辑结构出错。因为已经检测过矩阵键盘的所有按键,都是好的,那么只能时软件方面出错。最后发现果然是t=a1,a1=a2,a2==t这个逻辑关系顺序写错了。
4.2篮球计分器时间测试4.2.1相关功能键的测试- 按下S13键控制比分切换到时间(2)按下S11键控制时间切换到比分(3)按下S12键为增加节数(4)按下S21键为减少节数。
如图4.2所示,当前时间显示为第一节还剩9分42秒。
图4.2篮球计分器时间测试图
4.2.2出现的问题与分析- 按下S13键比分切换到时间时,再次按时,却无法将时间切换比分
分析:首先这种情况不可能是硬件问题,因为 S13键按下后能有反应,那么只能是程序问题。后面经过多次分析程序,发现还是自己程序结构问题,因为定义了两种情况,f==0和f==1,从而实现比分和时间切换,用两个键简单快捷。当按下某一键只能实现f==0或f==1,所以需要两个键。
分析:因为按照算数逻辑,节数一直减总会减到小于零,而数码管无法显示小于零的数,所以它只能是逻辑上存在而无法显示,当我们按下S12键使节数加时,一直加到大于零时,它又将重新显示出来。
参考文献- 张建勋编著. C语言程序设计教程. 北京:清华大学出版社,2008.
- 曾屹、刘辉编著. 单片机原理及应用. 中南大学出版社,2009
心得体会刚选这个题目,我以为很简单到网上一搜就有,谁知道自己真正做起来才知道这个课题要实现的功能很多,在网上找了好多类似的但都用不了,不是编译错误就是功能太复杂不会改。这个星期由于是考试周的原因,对于这个课题我一心就想早点把它搞好,以后的时间好复习。但是和伙伴早晚一直待在实验室连续做了4天还是没有很大的进展,刚开始的时候用的是数码管切换显示时间和日期,但是考虑到还要加4个闹钟和倒计时、显示波形等功能,我们改用了LCD12864来显示这些东西,本来想把两个结合起来,但一直搞不好,第三天的时候将时间和日期显示到了液晶屏上,接下来我们想办法设计程序来调节从DS1302读出来的时间,参考网上的程序尝试了好几次,却一直没有想要的结果,就这样一直到了第四天验收,虽然通过了,但离题目要求还差很远,自己还有好多需要学习的东西,还有好多没有理解的东西,真正在做的时候才发现自己差的并不只是一点。
虽然这次课设过程很艰难,成果也很有限,但是还是学到了许多东西,以后在接触到类似的东西就不会还是一头雾水,遇到问题要一步一步来,不断取得一些进展,才能激发自己前进的动力。
程序清单:
- #include
- #include
- #define uchar unsigned char
- #define uint unsigned int
- sbit DP=P1^6;
- sbit CLK=P1^5;
- sbit RST =P1^7;
- sbit DAT_595_PIN = P1^0; //串行数据输入
- sbit SCK_595_PIN = P1^2; //移位时钟脉冲
- sbit RCK_595_PIN = P1^1; //输出锁存器控制脉冲
- uint a1=0;
- uint a2=0;
- uint t=0;
- uint ft=0;
- uint jie=1;
- uchar Year,Month,Day,Week,Hour,Minute,Sec;
- uchar code num_buf[22]= {0xf5,0x05,0xb3,0x97,0x47,0xd6,0xf6,0x85,0xf7,0xd7,
- //0 1 2 3 4 5 6 7 8 9
- 0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xef,0xef,0x02,0x00};
- //0. 1. 2. 3. 4. 5. 6. 7. 8. 9.
- Uchar code bit_buf[8] = {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//0xf7,0xef,0xdf,0xbf,0x7f};
- uchar data led_buf[8] = {0,0,0,20,20,0,0,0};
- unsigned int num;
- // Function Declare ********************************************************************
- void Delay(void);
- void Write_595(void);
- uchar Scan_Key(void);
- void delay(int ms)
- {
- while(ms--)
- {
- uchar i;
- for(i=0;i<250;i++)
- {
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- }
- }
- }
- void mDelay(uchar DelayTime) //延时子程序,入口参数每加1约加2us
- { while(--DelayTime);
- }
- void SendByte(uchar Command,SendDat)
- { //字节发送子程序。入口参数:命令字,数据
- uchar i;
- RST=0;CLK=0;RST=1;
- for(i=0;i<8;i++) //发送命令字
- {
- DP=Command&0x01; //取一位送数据口
- CLK=0;CLK=1; //产生一个上升沿
- Command>>=1;
- }
- for(i=0;i<8;i++) //发送数值
- {
- DP=SendDat&0x01;
- CLK=0;CLK=1;
- SendDat>>=1;
- }
- RST=0;
- }
-
- uchar ReadByte(uchar Command) //读字节子程序。入口参数:命令字
- { //出口参数:数值
- uchar i,RecDat=0;
- RST=0;CLK=0;RST=1;
- for(i=0;i<8;i++) //发送命令字
- {
- DP=Command&0x01;
- CLK=0;CLK=1;
- Command>>=1;
- }
- for(i=0;i<8;i++) //读入数值
- {
- CLK=1;CLK=0; //产生一个下降沿
- if(DP) RecDat|=0x01<<i; 读入数据
- }
- RST=0;
- return(RecDat); //返回数值
- }
-
- void OscCtrl(bit CtrlDat) //振荡起动和停止控制,入口参数1(起动)或0(停止)
- {
- if (CtrlDat) SendByte(0x80,0x00);
- else SendByte(0x80,0x80);
- }
- // Main Function
- void main(void) //主程序
- {
- uchar key_value,cnt;
- uchar i;
- OscCtrl(1);
- SendByte(0x80,0x00); //送字节
- SendByte(0x82,0x20);
- SendByte(0x84,0x13);
- SendByte(0x86,2);
- SendByte(0x88,1);
- SendByte(0x8C,11);
- while(1){
- while(ft==0)
- {
- led_buf[0] = a1/100; //左侧比分的百位
- led_buf[1] = (a1%100)/10; //左侧比分的十位
- led_buf[2] = a1%10; //左侧比分的个位
- led_buf[3] = 20; //短横线
- led_buf[4] = 20; //短横线
- led_buf[5] = a2/100; //右侧比分百位
- led_buf[6] = (a2%100)/10; //右侧比分十位
- led_buf[7] = a2%10; //右侧比分个位
- Write_595();
- key_value = Scan_Key();
- //keydown();
- //Delay();
- switch(key_value) //键盘扫描函数
- {
- case 0:
- break;
- case 0x88: //按第一列第一行键a1+1
- a1++;
- break;
- case 0x84: //按第一列第二行键a1-1
- a1--;
- break;
- case 0x82: //交换比分
- t=a1;
- a1=a2;
- a2=t ;
- break;
- case 0x81: // ft=1,切换比分/时间
- ft=1;
- break;
- case 0x48: //按第二列第一行键a2+1
- a2++;
- break;
- case 0x44: //按第二列第一行键a2-1
- a2--;;
- break;
- case 0x42: //clear,比分置零
- a1 = 0;
- a2 = 0;
- break;
- case 0x41: //ft=0,按第二列第四行键切换比分
- ft=0;
- break;
- default:
- break;
- }
- }
- while(ft==1) //当ft=1时,即定义切换成时间显示函数
- {
- Year =ReadByte(0x8D); //读小时
- Month =ReadByte(0x89); //读分钟
- Day =ReadByte(0x87); //读秒
- Hour =ReadByte(0x85); //读小时
- Minute=ReadByte(0x83); //读分钟
- Sec =ReadByte(0x81); //读秒
- if((Minute/16*10+Minute%16)<=21) {
- led_buf[0] = 0; //0
- led_buf[1] = jie; //节
- led_buf[2] = 20; //短横线
- led_buf[3] = 1;
- led_buf[4] = 1-Minute%16; //11分
- led_buf[5] = 20; //短横线
- led_buf[6] = 5-Sec/16;
- led_buf[7] = 9-Sec%16; //59秒
- }
- else{
- led_buf[0] = 0; //0
- led_buf[1] = jie; //jie
- led_buf[2] = 20; //-
- led_buf[3] = 0; //0
- if(11-Minute%16<10){
- led_buf[4] = 11-Minute%16;
- }
- else{
- led_buf[4] = 1-Minute%16; //1
- }
- led_buf[5] = 20; //-
- led_buf[6] = 5-Sec/16; //5
- led_buf[7] = 9-Sec%16; //9
- }
- Write_595();
- key_value = Scan_Key();
- //keydown();
- //Delay();
- switch(key_value) //键盘扫描函数
- {
- case 0:
- break;
- case 0x81: // 按第一列第四行键 ft=1; 比分和时间转换
- ft=1;
- break;
- case 0x41: // 按第二列第四行ft=0;时间/比分转换
- ft=0;
- break;
- case 0x21: //按第三列第四行键,jie+1
- jie++;
- break;
- case 0x11:
- jie--; //按第四列第四行jie-1
- break;
- default:
- break;
- }
- }
-
-
- }
- }
- // Some Functions
- void Delay(void) //延时子程序
- {
- unsigned char m,n;
- for(m=200;m>0;m--)
- for(n=200;n>0;n--);
- }
-
- //***************************************************************************************
- //* FunctionName : Write_595() **
- //* Descriptions : write 595 **
- //* Input : led_buf[]&bit_buf[] **
- //* Output : None **
- //***************************************************************************************
- void Write_595(void) //定义595写入函数
- {
- uchar i,j;
- uint dat;
- for(i=0;i<8;i++) //发送命令字
- {
- RCK_595_PIN=0;
- dat =bit_buf[i];
- dat =dat<<8;
- dat |= num_buf[led_buf[i]];
- for(j=0;j<16;j++)
- {
- SCK_595_PIN = 0;
- if(dat & 0x8000)
- {
- DAT_595_PIN = 1;
- }else{
- DAT_595_PIN = 0;
- }
- dat=dat<<1;
- SCK_595_PIN = 1;
- }
- RCK_595_PIN = 1;
- }
- }
- //***************************************************************************************
- //* FunctionName : Num_change() **
- //* Descriptions : Num_change **
- //* Input : **
- //* Output : None **
- //***************************************************************************************
- uchar Scan_Key(void)
- {
- uchar sm; //存放行扫描代码,该函数的返回值的低四位
- uchar fh; //该函数的返回值的高四位
- P2 = 0xf0;
- if((P2&0xf0)!=0xf0) //判断是否有按键按下
- {
- Delay(); //去抖动延时(1~10ms)
- if((P2&0xf0)!=0xf0)
- {
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码完整论文下载(word格式可编辑):
篮球计分器.doc
(920.5 KB, 下载次数: 152)