回顾 这是楼主在高三时候做的一个项目,我和我的组员们共同完成了这个小钟从电路设计,焊接,再到编程调试的过程。我们将之前学习到的单片机和电路知识运用起来,虽然经历了不计其数的Bug和坎坷,可是就是在这样痛苦的过程中我们不知不觉中成长了许多,收获了更多的实战经验。 提出一直以来,我多数在开发软件,偶尔碰碰Arduino实现点小功能,幸运的是我的高中有机器人实验室,于是我们就在龚鹏老师的带领下,系统的学习STC单片机开发和基础硬件知识。上个学期,课程已经基本学完,在7*5点阵的基础下,我们几个都想独立自主的完成一个有规模的项目,在大家的提议下,我们选择去做一个可以显示温度的LED时钟,而且显示要用LED才大气,于是我们开始了有序的准备工作。 实现为了实现这个小钟的预期功能,我们听取了老师的意见。使用STC15W402AS芯片(宏晶科技生产)作为主控芯片,利用LED小灯组成的4个“8”作为显示部分,同时使用DS1302(Maxim生产)作为系统时钟芯片,使用DS18B20(Maxim生产)作为温度传感器芯片。电源使用MicroUSB+5V供电。电路图为自行设计,使用Protel绘制,再有工厂加工,随后按电路图焊接原件,最后进行编程调试。 图片展示结构使用STC15W402AS芯片(宏晶科技生产)作为主控芯片,利用LED小灯组成的4个“8”作为显示部分,同时使用DS1302(Maxim生产)作为系统时钟芯片,使用DS18B20(Maxim生产)作为温度传感器芯片。电源使用MicroUSB+5V供电。电路图为自行设计。 重要逻辑显示原理 - 视觉暂留扫描由于我们使用了类似于放大版“数码管”的显示方式,限于单片机引脚数量限制,我们不能把每个8的每个部分都和单片机相连。于是我们采用了现在LED显示屏的显示原理--扫描点亮的原理。在我们的电路中,我们将4个“8”的8个组成部分(a,b,c,d,e,f,g)的正极和单片机P1端口(恰好8个端口)连接(也就是把每个“8”的a并连起来接到一个单片机引脚,把每个“8”的b并连起来接到另一个单片机引脚,以此类推),再将每个“8”的所有组成部分的所有负极并联,分别和单片机上P2端口上的4个引脚相连,组成显示控制电路。需要点亮第1个“8”时就先将这个“8”的负极引脚置低,再将需要点亮的部分引脚同时置高。例如下图所示想要显示“1”,就将b,c的引脚置高
这样问题就来了:现在我们能一个一个点亮了,如果我让这4个“8”一起亮怎么办? 这就需要视觉暂留原理了,大家都知道电影之所以能动起来就是因为我们人眼有0.1秒的视觉暂留。我们点亮小灯也如此,先点亮第一个,延迟5ms,再点亮第二个,延迟5ms,以此类推。5ms这样短的时间人根本看不出闪动感,于是完整的图像显示出来。 下面是时间送显的代码(chart数组里是预先设计好的自模): - void convertShow(char hour_s1,hour_s2,min_s1,min_s2)
- {
- E1 = 0;
- P1 = chart[hour_s1];
- delayMS(5);
- P1 = allclear;
- E1 = 1;
- E2 = 0;
- P1 = chart[hour_s2];
- delayMS(5);
- P1 = allclear;
- E2 = 1;
- E3 = 0;
- P1 = chart[min_s1];
- delayMS(5);
- P1 = allclear;
- E3 = 1;
- E4 = 0;
- P1 = chart[min_s2];
- delayMS(5);
- P1 = allclear;
- E4 = 1;
- if(sec_2%0x02==0x01)
- {
- MDLIGHT = 0;
- }
- else
- MDLIGHT = 1;
复制代码
读取时间和温度这个想起来很简单,无非是单片机一边显示,一边不断向时钟芯片或者温度芯片询问数据,至于芯片如何驱动,则是后面要讲的重头戏 时间修改/模式切换 - 按钮的使用谁家的钟不能改时间啊,所以我们要支持时间修改功能,于是我们决定用传统的按钮来修改时间。使用三个按钮(称呼他们为Btn1,Btn2,Btn3)。Btn1负责加时间,短按是加1,长按递加,直到松开。Btn2和Btn1相反,而Btn3则是模式切换(一共三种:修改小时,修改分钟,显示温度)。 可是在实际编程时我们发现,按钮只要收到轻微扰动就响应,并不是按下才响应,这是为何?
原来通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖,如下图。 看来我们需要软件软件消抖(硬件消抖效果好可是我们的电路图已经印刷了啊)。软件消抖即检测出键闭合后执行一个延时程序,5ms~10ms的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。当检测到按键释放后,也要给5ms~10ms的延时,待后沿抖动消失后才能转入该键的处理程序。程序如下: - if (KEY_1==0 || KEY_2==0 || KEY_3==0)
- {
- delayMS(20); //20毫秒软件防抖
- if (KEY_1 == 0)
- {
- keyValue = 1;
- while(KEY_1==0)
- {
- modifyTime(status+8);
- delayShow(500);
- }
- }
- if (KEY_2 == 0)
- {
- keyValue = 2;
- while(KEY_2==0)
- {
- modifyTime(status+16);
- delayShow(500);
- }
- }
- if (KEY_3 == 0)
- {
- keyValue = 3;
- }
- }
- }
复制代码
下面是按钮修改的代码:- /*************按钮扫描函数******************************/
- void keyScan()
- {
- if (KEY_1==0 || KEY_2==0 || KEY_3==0)
- {
- delayMS(20); //20毫秒软件防抖
- if (KEY_1 == 0)
- {
- keyValue = 1;
- while(KEY_1==0)
- {
- modifyTime(status+8);
- delayShow(500);
- }
- }
- if (KEY_2 == 0)
- {
- keyValue = 2;
- while(KEY_2==0)
- {
- modifyTime(status+16);
- delayShow(500);
- }
- }
- if (KEY_3 == 0)
- {
- keyValue = 3;
- }
- }
- }
- /*************按钮响应处理函数******************************/
- void keyHandle()
- {
- if(keyValue==1)
- {
- DS1302_Write(sec,min,hour,day,month,week,year);
- keyValue = 0;
- }
- else if(keyValue==2)
- {
- DS1302_Write(sec,min,hour,day,month,week,year);
- keyValue = 0;
- }
- else if(keyValue==3)
- {
- if (status==1)
- {
- status = 2;
- }
- else if(status==2)
- {
- status = 3;
- }
- else if(status==3)
- {
- status = 1;
- }
- keyValue = 0;
- }
- }
复制代码
还有修改时间的一些诡异逻辑代码,涉及到一些标志变量,请仔细研读体会:- /*************修改时间函数******************************/
- void modifyTime(int mode) //mode指明修改小时或分钟 1:小时 2:分钟
- {
- //修改小时(1)+递增(8)
- if(mode == 9)
- {
- if(hour==0x23)
- {
- hour = 0x00;
- }
- else if(hour==0x09)
- {
- hour=0x10;
- }
- else if(hour==0x19)
- {
- hour=0x20;
- }
- else
- {
- hour=hour+0x01;
- }
- timeConvert();
- convertShow(hour_1,hour_2,min_1,min_2);
- }
- //修改小时(1)+递减(16)
- if(mode == 17)
- {
- if(hour==0x00)
- {
- hour = 0x23;
- }
- else if(hour==0x10)
- {
- hour = 0x09;
- }
- else if(hour==0x20)
- {
- hour = 0x19;
- }
- else
- {
- hour = hour-0x01;
- }
- timeConvert();
- convertShow(hour_1,hour_2,min_1,min_2);
- }
- //修改分钟(2)+递增(8)
- if(mode == 10)
- {
- if(min==0x59)
- {
- min=0x00;
- }
- else if(min-(min_1<<4)==0x09)
- {
- min=(min_1+0x01)<<4;
- }
- else
- {
- min=min+0x01;
- }
- timeConvert();
- convertShow(hour_1,hour_2,min_1,min_2);
- }
- //修改分钟(2)+递减(16)
- if(mode == 18)
- {
- if(min==0x00)
- {
- min=0x59;
- }
- else if(min_2==0x00)
- {
- min=min-0x10;
- min=min+0x09;
- }
- else
- {
- min=min-0x01;
- }
- timeConvert();
- convertShow(hour_1,hour_2,min_1,min_2);
- }
- }
复制代码
整体逻辑各部分的逻辑设计都完成了,我们现在开始设计整个程序的主函数部分。单片机上电后首先初始化(包括引脚置低什么的不再赘述)检测DS1302是否在运行(内置电池可以掉电走时),如果在运行就读取时间送显,不在运行就起振(开始走时)。然后在每个显示函数过后侦测按钮操作,如果发现有效按钮操作就进行处理按钮的逻辑,该修改时间就修改时间,该显示温度就显示温度。这个流程用死循环加以嵌套,就可以无止无休的运行下去(不断电的话)。下面是主函数代码: - void main()
- {
- init();
- while(1)
- {
- if(status==3)
- {
- temp = DS18B20_ReadTemp();
- tempHandle();
- tempshow(temp_1,temp_2);
- }
- else
- {
- DS1302_readtime();
- convertShow(hour_1,hour_2,min_1,min_2);
- }
- keyScan();
- if(keyValue!=0&&KEY_1==1&&KEY_2==1&&KEY_3==1)
- {
- keyHandle();
- }
- }
- }
复制代码
下面是初始化函数:- void init()
- {
- //关闭所有小灯
- E1=1;
- E2=1;
- E3=1;
- E4=1;
- MDLIGHT = 1;
- P1 = allclear;
- T_CE = 0;
- T_SCLK = 0;
- //默认初始化时间12:00
- sec = 0x00;
- min = 0x00;
- hour = 0x16;
- year = 0x01;
- month = 0x01;
- week = 0x01;
- day = 0x01;
- status = 1;
- keyValue = 0;
- delayMS(1000);
- //DS1302初始化判断是否存在后备电源
- if(DS1302_Read(0x81)&0x80==0x80)
- {
- DS1302_Write_one(0x8e,0x00);
- DS1302_Write_one(0x80,sec); //起振
- DS1302_Write_one(0x8e,0x80);
- }
- else
- {
- }
- }
复制代码
下面附上DS1302类库,亲测好用:- /*************写入一字节****************/
- void DS1302_Input_Byte(char Input) //向时钟IC写入一字节
- {
- char i;
- T_SCLK = 0;
- delay2us();
- ACC =Input;
- for(i=8; i>0; i--)
- {
- T_DIO = ACC_0; //相当于汇编中的 RRC
- delay2us();
- T_SCLK = 1;
- delay2us();
- T_SCLK = 0;
- ACC = ACC >> 1;
- }
- }
-
- /*************读取一字节****************/
- char DS1302_Output_Byte(void) //从时钟IC读取一字节()
- {
- char i;
- for(i=8; i>0; i--)
- {
- ACC>>=1;
- T_DIO= 1;
- delay2us();
- ACC_7 = T_DIO;
- T_SCLK = 1; //相当于汇编中的 RRC
- delay2us();
- T_SCLK = 0;
- delay2us();
- }
- T_DIO = 0;
- delay2us();
- return(ACC);
- }
-
- /*************写一字节数据****************/
- void DS1302_Write_one( char addr,dat ) // 写入地址、数据子程序
- {
- T_CE=0; //T_CE引脚为低,数据传送中止
- T_SCLK=0; //清零时钟总线
- T_CE = 1; //T_CE引脚为高,逻辑控制有效
- DS1302_Input_Byte(addr); // 地址,命令
- DS1302_Input_Byte(dat); // 写1Byte数据
- T_SCLK = 1;
- T_CE = 0;
- }
-
- /*************读一字节数据****************/
- char DS1302_Read ( char addr ) //数据读取子程序
- {
- char date;
- T_CE=0;
- T_SCLK=0;
- T_CE = 1;
- DS1302_Input_Byte(addr); // 地址,命令
- date = DS1302_Output_Byte(); // 读1Byte数据
- T_SCLK = 1;
- T_CE = 0;
- return(date);
- }
-
-
- /*************写入时间数据****************/
- void DS1302_Write(char sec_w,min_w,hour_w,day_w,month_w,week_w,year_w)
- {
- DS1302_Write_one(0x8e,0x00);
- DS1302_Write_one(0x82,min_w);
- DS1302_Write_one(0x84,hour_w);
- DS1302_Write_one(0x86,day_w);
- DS1302_Write_one(0x88,month_w);
- DS1302_Write_one(0x8a,week_w);
- DS1302_Write_one(0x8c,year_w);
- DS1302_Write_one(0x80,sec_w);
- DS1302_Write_one(0x8e,0x80);
-
- }
-
- /*************时间转换为显示格式****************/
- void timeConvert()
- {
- sec_1 = sec>>4;
- sec_2 = sec&0x0f;
- min_1 = min>>4;
- min_2 = min&0x0f;
- hour_1 = hour>>4;
- hour_2 = hour&0x0f;
-
- }
-
- /*************从芯片读取时间****************/
- void DS1302_readtime()
- {
- sec=DS1302_Read(0x81); //读秒
- min=DS1302_Read(0x83); //读分
- hour=DS1302_Read(0x85); //读时
- day=DS1302_Read(0x87); //读日
- month=DS1302_Read(0x89); //读月
- year=DS1302_Read(0x8d); //读年
- week=DS1302_Read(0x8b); //读星期
- timeConvert();
-
- }
复制代码
DIY教程Windows1.克隆或下载本仓库,PCB电路图在仓库内,印刷后按照引脚焊接元件。 2.下载宏晶科技官网所提供的相关烧录软件与教程,并安装。 3.下载KeilC51开发工具,并安装。 4.下载TKStudio,并安装(可选)。 5.使用USB转TTl烧录器连接板载串口与计算机(如果需要驱动请自行安装) 6.使用STC烧录软件,选择STC15W402AS型号,使用默认配置(单片机震荡频率11.0592MHz),载入想要烧录的二进制文件,上电烧录。 7.如果需要增加修改逻辑代码,可以使用TKStudio打开main-code/Clock.xmp进行二次开发,其他IDE也可。 最后附上代码以及PCB:
全部资料51hei下载地址:
ds1302-stc15-clock-master.zip
(184.78 KB, 下载次数: 73)
|