本帖最后由 straka 于 2018-7-30 11:45 编辑
弄了个四位带冒号和小数点的数码管,想着快到1000天纪念日了,于是准备弄个计日的小东西,由于自己DIY的比较丑,就网上淘了一个,但是网上的不符合要求呢,没事,反正网上八成用的是51单片机,基本都可以在线编程了,所以买个回来复原下电路,然后自己在写程序呗。
找了个全白一体的模块,电路印刷也比较整齐的,显示效果如图:
图1 数码管显示效果图
图2 模块电路板
由于电路是双面印刷,为保险起见,还是解焊了数码管,用万用表把电路测试一遍,确保恢复的是正确的电路。主控芯片是STC15LE,sop8封装,数码管驱动芯片是TM1650,sop16封装,时钟是DS3231模块,带有16个八位寄存器,也是sop16封装,数码管是八段4位的,第二位小数点被替换成了冒号。
图3 数码管背面
手动画出的电路复原图,还比较简单,毕竟就是个时钟和显示功能,并不复杂。然后开始写程序。
图4 模块电路板背面
图5 手画电路图
图6 灵魂电路图草稿
先到官网上找到TM1650的芯片资料:
TM1650是一种带键盘扫描接口的LED(发光二极管显示器)驱动控制专用IC
特性说明:
两种显示模式( 8段×4 位 和 7 段×4 位)
支持单个按键7x4bit(28个按键)和组合按键( 4个)
8级亮度可调
段驱动电流大于25mA,位驱动电流大于150mA
高速2线串行接口( CLK,DAT)
振荡方式:内置RC振荡
内置上电复位电路
内置数据锁存电路
支持3-5.5V电源电压
抗干扰能力强
重点是引脚定义:
图7 TM1650引脚定义
图8 引脚定义
官网也有例程,其实用起来很简单,用TM1650可以很方便的驱动四位数码管,例:
先定义不同的字节对应的显示状态:
uchar CODE[] = {0xFC,0x84,0xBA,0xAE,0xC6,0x6E,0x7E,0xA4,0xFE,0xEE,0x00};//0-9,全灭状态
显示:
TM1650_Set(0x68, CODE[0]); //第一位显示0,第2、3、4位依次为0x6A、0x6C、0x6E
TM1650_Set(0x48,0x21); //设置亮度,亮度从低到高依次为
TM1650的显示内容、亮度状态设置和获取都是通过访问设置寄存器实现的,具体就是通过I2C总线发送地址、数据,所以看看文档里关于寄存器的定义就可以实现,而TM1650的扫描按键功能通过I2C总线发送0x49命令获取,根据I2C总线发回的值判断按下的键,同时要注意,当按揭释放后,第6bit会变为0,据此判断按键释放。
key = Scan_Key();
if(key==0x47) //key set
{
……//do something here
while(Scan_Key()==0x47);
}
DS3231也是I2C总线的,网上资料非常多,这里不复述,引用封装好的函数,可以很方便的完成时间获取、设置和寄存器的访问、设置。本程序中除了用DS3231获取设置时间,还利用DS3231产生的1HZ方波对51引发外部中断,完成调整时的冒号闪烁功能以及显示内容切换(主要在月-日显示与年-周显示之间切换)。外部中断函数如下:
void int2() interrupt 10 //DS3231输出1HZ方波,下降沿更新
{
//display time now, flash colon
if(dspmode == 0){
sec=!sec; //sec标识冒号的显示与隐藏,实现闪烁
}
//when adjust date
else if(dspmode == 1){
secMode++;
secMode%=8; //secMode标示显示子模式
}
//display date now
else if(dspmode == 2){
}
AutoLight(); //根据当前小时信息进行时钟亮度调整
}
由于模块自带只有两个按键,所以设计功能逻辑的时候还毕竟麻烦,主函数里一堆switch—case,先根据dspmode变量判断显示模式,然后根据setmode判断调整模式,当setmode为0时为该显示模式下的正常显示状态,即不进行调整,当该模式下检测到set按键则跳转到对应的设置状态,当调整模式为0时检测到按键为add,则跳转到不同的显示模式,而除此以外的调整模式下按键add为调整状态。主要涉及时间、日期、年、星期、积日的显示和设定,因而逻辑毕竟繁杂,但是不难。伪代码如下:
switch(dspmode){
case 0://时:分模式
switch(setmode){
case 0:
//显示模式
case 1:
//调整小时
case 2:
//调整分
case 3:
//调整秒
}
break;
case 1: //月:日----年-星期模式
switch(setmode){
case 0:
//显示模式
case 1:
//调整年份
case 2:
//调整月份
case 3:
//调整日期
case 2:
//调整星期
}
break;
case 2://日期计数模式
//显示当前日期累计值
break;
}
51单片机内置时钟中断,当调整模式时闪烁调整位,代码如下:
void timer0() interrupt 1
{
uchar flag;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
flag++;
if(flag==5)
{
flag=0;
flash=!flash;
}
}
通过flag标示闪烁与否,闪烁速度比DS3231输出中断快以示区别。
代码总览(代码参考:http://www.pudn.com/Download/item/id/2881058.html),因为STC15LE程序存储空间只有4kb,而程序一度达到4080字节,因而主函数用了整个大的switch-case语句,毕竟重用部分少没有另设函数,所以代码看起来没那么清晰。
最终效果见图1和下图:
PS:
中间遇到显示乱码和显示值跳动问题,一直没解决,后来发现是中断函数中调用了某些函数导致函数重入破坏堆栈了,后来就把中断函数中的函数全部移到主函数中就OK了。
后面又改了下程序,弄成起始日期也可以设置的,就是在第三个模式下增加了日期调整功能,又加了200行代码,差点单片机空间不够烧不进去了。主要是只有两个按键造成程序的复杂和庞大,需要代码的留言或邮箱联系,因为增加的功能有点故弄玄虚,没有贴出来的必要。。。
附录:
详细信息可以见原文:【http://www.straka.cn/blog/nixie_tube_clock/】
原文访问不畅可以在此处下载:
代码见【http://download.csdn.net/download/atp1992/10208348】
参考资料【 http://download.csdn.net/download/atp1992/10208353 】
|