辉光管电子时钟

本篇转载自http://yanzeyuan.blog.163.com/blog/static/466021972010102091129748/

[DIY]二十四小时的感动(古典版I) 自制QS30-1辉光管电子时钟

[DIY]QS30-1辉光 2010-11-20 21:33:56 阅读1293 评论6   字号: 订阅

        辉光管是电子管的一种,属于很古老的数码显示器件,在没有LED的年代,显示数字和符号就靠它。这种管子现在应该算古董了,这次DIY用的QS30-1辉光管大部分都是70年代和80年代生产的。在国外,有不少玩家在用它制作辉光时钟,辉光时钟也叫NIXIE tube Clock,受他们的启发,我也自己从头开始设计了一套使用国产QS30-1的辉光数码管电子时钟。

        先秀一下做好的NIXIE tube Clock(拍照片的时候用了些客厅里的东西做道具 ^_^ ),然后再付上详细的设计制作过程。

 

 

 

 

 

 

 

 

一、本次DIY的QS30-1辉光数码管电子钟功能及特点

        1、采用DC12V供电,最大耗电电流400mA。

        2、使用6只QS30-1辉光数码管,能同时显示年月日或同时显示时分秒。

        3、采用13颗直径3mm的氖灯,能指示星期和时钟冒号。

        4、采用红外遥控器进行所有功能操作(开/关时钟、显示农历、公历、室内温度、调整日期时间等等)。

        5、每颗辉光管底部有一颗贴片高亮白光LED做背景灯,在关机的时候背景灯能够每秒钟交替明暗一次(注意是明暗过渡,不是亮和灭,采用了PWM制作的LED调光,在晚上效果很炫),也可以在开机的时候通过遥控器打开或关闭背景灯功能。

        6、采用无源蜂鸣器来对每一次遥控操作进行声音提示,不同的操作功能和结果会有不同的响声。

        7、能够根据公历时间自动计算出星期几,并且能够自动计算出农历日期,在设置时只需要设置好公历日期即可。

        8、为了延长辉光管的寿命(毕竟电子管的寿命相对LED短很多),可以用遥控器关闭辉光时钟,但每到整点时刻,辉光钟能自动启动显示10秒钟时间,启动时会有整点报时。

        9、采用18B20作为温度传感器,能够显示室内温度。

        10、全部电路布局在一块双面电路板上,电路板经过精心设计,线条细腻,与辉光管相映成辉,所有GND网络线条均镀金处理,电路板厚2mm。

        11、内部采用DC-DC升压电路,提供180v的直流高压供辉光管使用,体积小,效率高。

        12、采用C8051F310作为主控MCU,所有程序采用C语言编写。

        13、采用74HC595做译码控制辉光管显示相应数字。

        14、采用DS1302时钟芯片,配合西铁城高精度晶体谐振器(32.768kHz,精度±5ppm)。

        15、MCU及时钟芯片等所使用的5v电源也采用DC-DC电路降压,效率高,发热量小。

 

二、设计之前了解到的QS30-1辉光数码管参数特性及相应的必要试验

        辉光数码管跟普通电子管样子相似,起初对它无任何了解,拿到这个QS30-1管子以后发现它内部并没有加热灯丝,后来查阅部分资料才知道它是一种冷阴极辉光放电管,玻璃管内部封装有10个阴极,分别用金属丝做成了0-9一共10个阿拉伯数字形状,然后再管芯里面用陶瓷绝缘子把它们相互分隔开,并且重叠在一起。

        先SHOW一下手头上的这些QS30-1辉光管,这些管比我的年龄都大许多:

 

 

 

QS30-1辉光管是插脚的,可以配上管座,旁边的这个白色的陶瓷座就是可以配合使用的管座。

 

下面要做的就是怎么能让这个辉光管亮起来,发出她那迷人的辉光,说实话起初对它没抱太大希望,后来点亮以后的确见识了辉光那迷人的橘红色。先看一下它的资料,资料很老了已经。

 

 

        可以看到,辉光管的阳极电压为DC170-280v,电流1-3mA,启辉电压为150v,限流电阻跟阳极电压有关,推荐的参数也写的很清楚了,所以要让它工作,就必须为它提供170v以上的高压。结合以前的经验,计划选用MC34063作为DC-DC升压IC,电路图设计如下:

        MC34063的应用方案比较简单,但是从12v升到180v左右,效率应该会比较低。但6颗辉光管一共的工作电流也不大,所以用这套电路应该没有问题。电路中使用了一个MOS管IRF840做为高压推动,并且输入端用了一颗MOS管做供电开关,可以由MCU控制给MC34063的供电的通断。

        为了验证这个DC-DC升压方案是否可行,我用洞洞板搭了一个实际电路。虽然比较乱,但是可以验证升压电路效果,见下图:

 

 
        辉光管在180v左右的时候被点亮,看一下近距离的数字0被点亮的效果:

 

 

三、电路原理图设计及PCB板设计

        这个试验做完,就可以放心大胆的设计MCU控制部分、电源部分、译码及驱动部分电路了,这里面用到的译码电路是74HC595,但要注意的是驱动辉光管用的三极管要耐高压,所以这里选用了常用的A42。MCU采用常用的C8051F300,体积小,内置晶体,支持在线编程及调试,很方便,价格便宜,9RMB左右。还有值得一提的是,DS1302是很常用的时钟IC,但时钟走的准不准,取决于匹配的32.768kHz晶体的精度。我这里选用的是日本西铁城晶体,精度为±5ppm。

        成这些电路部分几乎不用做实验,马上设计电路图 ^_^ :

 

 
        在设计PCB的时候,我花了些时间在元器件布局上, 全部采用SMT贴片器件,并且将所有元器件布局在一块257mm x 60mm的双面电路板上。在电路板上层只放置辉光管座,不放置任何元器件,这样会使电路板看起来整洁,并且围绕每颗辉光管画了很多类似迷宫的环形线条。这些线条有的是作为网络线路走线,有的是GND网络填充空白区域做得线条,每段线条都不同。
        在电路板上层用焊盘绘制了星期一到星期日的标识,电路板边缘也用了弧形线条布置。
        电路板底层也用了对称的布局方法,把驱动辉光管每个脚的对应电阻和三极管环绕辉光管座进行布置。
        SHOW一下PCB上层和底层的效果图:
 
 
 
        为了让所有GND网络的线条看起来漂亮一些,利用Altium Designer分离了所有的GND网络,然后复制到原来的网络线条上,再将其全部改为Top Solder层,然后将线宽全部增加4mil,这样,所有的GND网络线条就全部禁止了阻焊,能露出铜箔部分,然后再要求PCB加工厂将所有未上阻焊的铜箔线条做沉金工艺处理(不是镀金,是沉金,沉金的视觉效果更好,更黄亮,达到我的设计目的,但金层的致密性不如镀金的好,但满足我的要求),用3D模式看一下效果图就是如下样式:
 
 
        PCB板设计好了以后,就是漫长的等待,等待PCB板厂加工好。
        ...........................

四、硬件电路的焊接与调试过程

        辉光数码管跟普通电子管样子相似,起初对它无任何了解,拿到这个QS30-1管子以后发

        过了10天时间,PCB板到货,SHOW一下效果,跟设计的效果无异,欢喜ing...........
        SHOW一下PCB板实际效果:
 

 

        下面那块小的是给QS18的辉光管做的PCB板,采用的同样的布板风格,将所有GND网络线条禁上阻焊,然后采用沉金工艺处理。实际拿到手上看到的效果会比图片上好很多,而且金层不会被氧化腐蚀,这个效果会持久下去。

  
 
 
 
 
        下面的这就是用焊盘做得星期一到星期天的标识。
        星期一至星期五每天用一颗氖灯指示,星期六星期天是周末嘛,就用两颗并排指示!
 
 
 
 
        再跟辉光管来个合影:
 
 
 
        PCB板到了,元器件齐了,马上该操刀焊接了。
        因为除了高压DC-DC电路部分,其他电路部分均凭经验设计,并没有做实验,所以焊接元器件还是要分模块分步骤来进行,并且做好一个模块要进行相应的验证,防止费了九牛二虎之力全部焊好元器件,结果无法正常工作,再回过头来找原因会走很多弯路。
        把家伙事儿都鼓捣出来,元器件、焊台、热风枪、万用表等等,开始上刀。的确是上刀,自从用惯了刀头,对刀头情有独钟。焊0805的件儿和TQFP封装的件儿轻松完成,准备一下热热身,SHOW一下白光焊台的刀头:
 
        一边看着电路原理图,一遍焊着元器件,首先先把DC-DC升压180v的电路焊接完成。两颗SOP8封装的IC,一颗MC34063,一颗MOS管SPP9435,还有个330uH的贴片电感和MOS管IRF840,几个外围器件整整齐齐的趴在电路板上,要比当初做实验搭的洞洞板整洁多了。
        焊接完之后仔细用万用表做了一下重要的地方有无短路,电容有无焊反,二极管有无焊反等等必要的检查之后,准备通电。说到首次通电,倒是有过惊心动魄的经历。曾经一次电源一插上,芯片就像燃气灶一样突然起火,伴随着啪啪的裂开的声音,所以通电之前一定要做仔细的检查。电容不要焊反,还有电容的耐压值一定要够。
 
 
 
        测量一下DC-DC升压,183v,升压电路部分调试成功。
        MC34063升压电路方案的输出电压值是靠两颗电阻来调节的,暂且把阻值大的一颗叫R1,阻值小的一颗叫R2,输出电压与这两颗电阻之间的关系是 Vout=1.25*(R1/R2+1),在我的电路当中,R1我选用了680K,R2我选用了4.7K,理论输出电压值为182v。因为这个电压值不要求那么精确,所以没有选用1%精度的精密电阻,输出电压与计算值差了1.2v。
 
 
        接下来把其他IC所用系统电压5V的DC-DC降压模块调试一下。采用的DC-DC降压芯片是常用的MP1484,它效率比较高,而且最大驱动电流3A,所以效率高,实际工作过程中几乎不热。
        外围原器件也很少,焊好以后通电测试,SHOW一下结果:
 
 
 
        电源模块部分都已经正常工作了,接下来要把MCU和驱动电路部分焊接完毕并调试一下。
        因为采用的驱动芯片是串行通讯的74HC595,所以只需要3个IO口就可以完成对芯片的控制。MCU选用的是C8051F300,共32个脚,LQFP封装,工作电压2.7v-3.6v,16K Flash,内部带可编程振荡器24.5MHz,共29个IO端口,4个16位定时器。最好的是它支持片内调试电路,提供全速、非侵入式的在线系统调试,还不需要仿真器。价格在9RMB之间。主要是体积小,资源丰富,支持在线编程,并且价格低,以前做过一些小东西用的都是它,这次毫无例外也选用了它^_^;
        SHOW一下MCU:
 
        因为是LQFP封装,脚间距0.8mm,所以焊接难度很小。在以前,我常用尖头烙铁来焊IC,焊一颗TQFP100的IC大概得5-6分钟时间,几乎是一个脚一个脚的去焊,而且一旦焊联了还得拿铜网去刷锡。其实用刀头是最方便的,我也建议朋友们都试用一下刀头焊IC,我现在贴一颗TQFP100的IC大概用40秒钟就完成,比以前用尖头来得快,而且焊接质量高。
        先说一下用刀头焊接时的大致方法。首先找一个角上的焊盘上点锡,然后将IC四边仔细对准焊盘,将刚才上锡的那个脚给焊好,这样IC就被固定在了电路板上。这个芯片0.8的脚间距还比较好对准。接下来就用刀头把四面都上满锡,不用管它们是否连锡。最厚擦干净刀头,沾点松香,顺着管脚方向慢慢拉,这样因为刀头面积大,融化的锡有张力,那些连脚的焊锡就会自己往刀头上流,这样一来拉过的焊脚就会牢牢的焊在焊盘上。完成.......
 
 
 
        焊完MCU要清理电路板,因为有松香,所以电路板有点惨不忍睹。
        清理松香最好的办法是用洗板水擦洗,或者有条件的也可以使用超声波清洗一次完成。根据个人习惯不同,可以边焊接边清理,也可以等全部元器件焊完以后放到洗板水水槽里泡一泡,然后刷洗。我还是喜欢电路板干干净净的样子,所以焊完之后,取来棉签,沾上洗板水,将电路板上残留的松香反复抹去。用洗板水的时候要注意,尽量不要擦拭带有塑料外壳的东西,轻则使塑料件失去光泽,重则会腐蚀塑料部分。我还曾经见过一些劣质的元器件比如电容什么的,上面的字竟然用洗板水一泡就没有了,相当的郁闷。
 
 
 
        看一下擦完松香的样子,干干净净。不过棉签已经变黄了,这表明残留在电路板上的松香已经被洗板水稀释并且全部吸进棉签里了。
 
 
         MCU已经焊上了,也就是整个辉光钟有了大脑,可以让该亮的亮起来了。打算马上试验一下氖灯和辉光管在金板上亮起来的效果,呵呵,取来元件盒把氖泡的驱动电路(电阻、三极管什么的),辉光管的译码电路和驱动电路(74HC595等)统统焊上。
        SHOW一下焊好的效果:
 
 
 
 
 
 
         陶瓷管座已经稳稳的扎在了金板上,装上辉光管,比我想象中的效果要好。译码电路和驱动三极管环绕在辉光管座下面,跟我当初想象的效果一样。
        还是 反复的检查,测量,确保电源部分,尤其是高压部分没有短路和断路,马上上电看效果。
        期待已久的效果出现了,辉光管和氖灯都正常的被点亮。
        看一下氖灯和辉光管在板子上正常亮起的效果吧,照的不是很好,真的没有完全表现出辉光的迷人色彩 ^_^ :
 
 
 
 
        氖灯和辉光管都已经能够正常工作了,然后根据实际测量,调整了一下串联在氖灯和辉光管阳极的电阻阻值。
        加在辉光管上的高压是180v,电阻从18K,20K,22K,24K,30K,33K都测试了一下,后来在保证亮度OK的情况下,尽量将电阻用大一些,确保辉光管的寿命能相对长一些。经过反复测试后来将电阻阻值确定在24K。不过在18K的时候亮度是最好的,仔细看那些金属阿拉伯数字阴极的边缘,就像一层发散着光芒的迷雾 ^_^ 。
        为了美观,我在辉光管底座的中心位置用了一颗1210封装的高亮白光LED,通过仔细的观察,我发现QS30-1辉光管底部有一个玻璃尾巴,如果对着这个尾巴送上一束光,将会使辉光管从顶上看起来正好有一圈迷人的光环,所以在焊接管座之前,我也试验了一下这个LED。
        高亮LED的电流比较大,每两颗LED我用了一个贴片的三极管推动,并且用一个MCU的IO口线驱动三级管,这样可以根据需要调整LED的亮或灭,如果使用PWM调制的话还能调整LED的亮度。
        重要的测试已经完成,下面就搬弄出所有的元件盒按照电路图把所有的器件都给焊上,SHOW一下过程:
 
 
 
 
        这些是以往积攒的一些元器件。因为个人情有独钟与贴片器件,所以都用这样的小盒子一个个的装起来,贴上标签。原来在北京的时候,经常为了几种几块钱的元器件花半天时间从南五环赶到北四环的中关村。中发电子附近的几个大厦哪个柜台卖什么都记得清清楚楚,尤其是老中发西南角的那家卖贴片电阻电容的经常去,每次为了用几颗0805的电阻都要去买500个,因为少了不卖,多了又用不了。一盘5千个电阻批发的话大概30RMB左右,买500个就要5RMB。慢慢的,就攒了下面这些元器件。
 
 
        这是全部焊接好的电路板的正面效果,和没有焊元器件之前的空板来个合影吧:
 
 
 
 
        这是全部焊好元器件的板子被面,也跟没有焊元器件之前的空板来个合影吧。
        因为这颗MCU支持在线编程和调试,所以需要有一个JTAG接口与编程器相连,MCU上面的那个1.25mm间距的6PIN端子座就是了。
        除了那颗高压电容,其他所有电容都采用贴片铝电解电容和钽电容,还有一些0805封装的小瓷片电容。电阻也全部采用0805封装的小电阻。焊完这些东西已经天黑了,眼睛也快废了......
 
 
 
 
 
 
 
 
  五、制作带底座的高脚氖灯灯管(用于指示星期和时间冒号)

        因为辉光管和管座加起来的高度比较高,而氖灯体积太小,太矮,如果直接焊在电路板上感觉很不协调,所以一直在为这个不协调而绞尽脑汁。后来淘到一批小试管(是我见过的直径最小的试管了),外径6mm,内径5mm,长度大概3cm左右,正好跟辉光管+底座以后的高度相同,所以就产生了一个想法,把氖灯放进这个试管里,用这个玻璃试管做一个长度3cm直径6mm的氖灯灯管。

        氖灯有很多种,现在有些氖灯还能发出绿色红色的光。当然,最早的氖灯也叫氖泡,就是我们常用的测电笔上的那种氖泡,发出的是橘红色光,正好跟辉光管发出的光颜色一样,所以才想到用氖灯来做搭配,指示冒号等小点点。

        不过在选用这个氖灯的时候也走了很多弯路。一开始淘了一些直径4mm左右的氖灯,回来测试了一下,必须在电压高于190v的时候才能被点亮,而且一致性不好,有些210v都不亮,而且外径很不均匀,有些不是很圆,放不进小试管里面去。后来好不容易淘到了一批进口的3mm直径的氖灯,效果很好,90v就能点亮,而且亮度很一致。也做了反复的实验,在180V的时候,串联240K的电阻,能够在亮度满意的情况下达到理想的亮度,并且使耗电电流降到最低(0.8mA)。

        但是这个玻璃管怎么固定在电路板上呢?毕竟玻璃没法焊接,而且氖灯的焊脚很细,只有0.5mm,也无法支撑的那么好那么一致,这要焊上去一排灯管结果东倒西歪岂不是大煞风景。后来想到了一个办法,用5mm直径的LED来制作管座。因为小试管的内径是5mm,LED的外径小于等于5mm,正好能套上,并且LED的管脚硬,还带个底座,能够很牢靠的固定在焊盘上,所以找来了这些东西以后,开始动手做,先看一下制作氖灯灯管的材料:

 

        这是把LED套进小试管的效果,比较理想。

 
 
        然后要做的,就是要利用LED的这两个管脚,连上氖灯的管脚。
        为了不破坏LED的底座和管脚,并且又能把氖灯的两个脚连接到LED的管脚上,所以用大偏口钳把LED的上面半部分的透明塑料钳掉了,这样就露出了两个管脚的末端,当然,LED也就不能用了,在这里只是用它的底座和这两个管脚,就是下图的样子:
 
 
        然后将氖灯管脚剪去一部分,在加工过的LED焊脚顶部镀上锡,将氖灯焊接在LED管脚顶部。这个过程比较费劲,因为有时候用钳子并不能完美的在LED需要的嵌断的地方夹断它,夹多了就破坏LED底座,夹少了金属管脚露不出来又没法焊接,一共为了制作13个氖灯灯管,费了20多颗5mm直径的LED。还好,这些都是以前不知道做什么剩下来的透明外壳LED,至于通电什么颜色也都不知道了,呵呵。
        焊上氖灯以后的效果见下图:
 
 
        13个带底座的氖灯全部制作完成,剩下的就是如何将小试管固定在底座上。
        一开始的想法很有新意,我打算买一个透明的蜡烛,然后取下一块蜡,用热风台加热成液体,然后将它倒进这个试管里,趁它还没有凝固的时候,将加工好的带底座的氖灯放进试管里,这样就氖灯就可以牢牢地被玻璃试管包裹了。后来为此我还专门跑了一趟宜家,后来没有买到透明体的蜡烛(不过后来发现淘宝上有),所以这个想法也就没有实现。
 
 
 
        最终固定这个玻璃试管的办法被我想的太复杂,无意中请示了一下老婆大人,结果老婆一语道破天机,用502胶 ^_^。
        说实话我对502胶的印象并不怎么好,感觉它不是很管用,而且容易溅的到处都是,很难弄掉。不过后来用了以后发现,用它来把玻璃试管粘到LED底座上还是很好使的,呵呵,这就是完全制作好的氖灯灯管了,一共13颗:
 
 
        用于指示星期和冒号的氖灯灯管做好了,这下可以跟辉光管完美搭配了,因为有了底座,所以很容易固定在电路板上,所以马上把它们各就各位,效果出乎我的想想,实在是太般配了。
        看一下整体效果吧,高度几乎一致,而且氖灯位于顶端,发光的位置也刚好,并且粗细作为小点来说刚刚合适,不粗也不细。
        SHOW一下:
 
 
 
 
 
 
 
 
  六、编写译码电路驱动程序

        74HC595是很常用的器件,3线串口通讯,每个驱动芯片提供8个译码输出,以前常用于LED数码管的驱动,加上小数点正好8段。但这次使用的是辉光管,每显示一个数字就需要有一个驱动,所以为了6个辉光管每个数字都能亮,一共需要60个译码输出,这就要用到8片74HC595芯片。

        这颗芯片很常用,看了一下DATASHEET,很快就做好了通讯程序,下面我把74HC595的通讯代码及控制各辉光管显示数字的代码部分贴出来:

 /********************************************************************************************************
** 功能描述:   向74HC595发送数据 
** 入口参数:   count: 发送数据的位数
**      dat: 发送的数据(MAX 16Bit)
** 创 建 者:   严泽远
** 创建时间:   2010-11-21 13:09
** 版    本:   v1.0.0
********************************************************************************************************/
void send_data(uchar count,uint dat)
{
 for(;count>0;count--)
 {
  DATA = dat&0x0001;_nop_();
  SHCP  = 1;_nop_();SHCP  = 0;_nop_();
  dat>>=1;
 }
}

/********************************************************************************************************
** 功能描述:   刷新QS30-1辉光管的显示数字
** 入口参数:   N1:  第一位显示的数
**      N2:  第一位显示的数
**      N3:  第一位显示的数
**      N4:  第一位显示的数
**      N5:  第一位显示的数
**      N6:  第一位显示的数
** 创 建 者:   严泽远
** 创建时间:   2010-11-21 14:23
** 版    本:   v1.0.0
********************************************************************************************************/
void DispNum(uchar N1,uchar N2,uchar N3,uchar N4,uchar N5,uchar N6)
{
 uchar buf,buf2;
 if(N1>9) buf = 0x00;
 else  buf = 0x08>>N1;
 send_data(8,buf);  //U1

 if(N1>9) buf = 0x00;
 else  buf = 0x80>>(N1-4);
 if(N2>9) buf2= 0x00;
 else  buf2= 0x02>>N2;
 send_data(8,buf|buf2); //U2

 if(N2>9) buf = 0x00;
 else  buf = 0x80>>(N2-2);
 send_data(8,buf);  //U3

 if(N3>9) buf = 0x00;
 else  buf = 0x80>>N3;
 send_data(8,buf);  //U4

 if(N3>9) buf = 0x00;
 else  buf = 0x80>>(N3-8);
 if(N4>9) buf2= 0x00;
 else  buf2= 0x20>>N4;
 send_data(8,buf|buf2); //U5

 if(N4>9) buf = 0x00;
 else  buf = 0x80>>(N4-6);
 if(N5>9) buf2= 0x00;
 else  buf2= 0x08>>N5;
 send_data(8,buf|buf2); //U6

 if(N5>9) buf = 0x00;
 else  buf = 0x80>>(N5-4);
 if(N6>9) buf2= 0x00;
 else  buf2= 0x02>>N6;
 send_data(8,buf|buf2); //U7

 if(N6>9) buf = 0x00;
 else  buf = 0x80>>(N6-2);
 send_data(8,buf);  //U8

 STCP = 1;_nop_();STCP = 0;_nop_();  //数据锁存
}

        这段程序调试成功了以后就能够在每个辉光管上显示任意字符了,有模有样的效果出来啦,SHOW一下:

 

七、编写DS1302时钟芯片读写程序

        DS1302也是很常用的一款时钟芯片,外围元器件非常少,仅一个电池,一颗晶体,一颗电容即可,通过3线与MCU通讯, 具体介绍网上一搜一大把,时序也很简单,以前在其他作品上用过这个芯片,程序也很简单,调试几分钟就OK,下面我也把代码贴出来,可以给朋友们做个参考:

 /****************************************Copyright (c)**************************************************
**    
** 项目名称:   QS30-1辉光钟 目标板试验程序
** 文件名称:   DS1302.c
** 模块功能:   实现DS1302时钟芯片的完全控制
**
**------------------------------------------------------------------------------------------------------

**  创 建 者:   严泽远
** E-mail  :   
[email protected]
** QQ  :   6626209
** Mobile :   18602007878
** 创建时间:   2010-11-19
** 版    本:   v1.0.0
** 描    述:   基础程序
**
********************************************************************************************************/

#include     //加载C8051F310.h头文件
#include      //加载Define.h头文件
#include    //加载DS1302Function.h头文件
#include    //加载DS1302Extern.h头文件

/********************************************************************************************************
** 功能描述:   往DS1302写入1Byte数据  
** 入口参数:   ucDa   写入的数据   
** 创 建 者:   严泽远
** 创建时间:   2010-02-05 23:52
** 版    本:   v2.1.3
********************************************************************************************************/
void   v_RTInputByte(uchar   ucDa)    
{    
     xdata uchar   i;
   xdata uchar  j; 
   j   =   ucDa;  
   for(i=8;   i>0;   i--)  
   {  
    T_IO   =   (j&0x01);   /*相当于汇编中的   RRC   */
    T_CLK   =   1;  
    _nop_();_nop_();
    T_CLK   =   0;  
    j=j>>1;
   }    
}  

/********************************************************************************************************
** 功能描述:   从DS1302读取1Byte数据
** 出口参数:   ACC
** 创 建 者:   严泽远
** 创建时间:   2010-02-06 00:11
** 版    本:   v2.1.3
********************************************************************************************************/
uchar   uc_RTOutputByte(void)    
{    
   uchar   i; 
   uchar j;
   for(i=8;   i>0;   i--)  
   {  
    j=j>>1;
    if(T_IO){j|=0x80;}else{j&=0x7f;}
    T_CLK   =   1;  
    _nop_();_nop_();
    T_CLK   =   0;  
   }    
   return(j);    
}  

/********************************************************************************************************
** 功能描述:   往DS1302写入数据  
** 入口参数:   ucAddr:    DS1302地址
**      ucDa:    要写的数据  
** 创 建 者:   严泽远
** 创建时间:   2010-02-06 00:11
** 版    本:   v2.1.3
********************************************************************************************************/
void   v_W1302(uchar   ucAddr,   uchar   ucDa)  
{  
   T_RST   =   0;  
   T_CLK   =   0;  
   T_RST   =   1;  
   _nop_();_nop_();
   v_RTInputByte(ucAddr);   /*   地址,命令   */  
   v_RTInputByte(ucDa);   /*   写1Byte数据*/  
   T_CLK   =   1;  
      T_RST   =0;  
}    

/********************************************************************************************************
** 功能描述:   读取DS1302某地址的数据 
** 入口参数:   ucAddr:   DS1302地址 
** 创 建 者:   严泽远
** 创建时间:   2010-02-06 00:17
** 版    本:   v2.1.3
********************************************************************************************************/
uchar   uc_R1302(uchar   ucAddr)  
{  
   uchar   ucDa;  
   T_RST   =   0;  
   T_CLK   =   0;  
   T_RST   =   1;
   _nop_();_nop_();
   v_RTInputByte(ucAddr);   /*   地址,命令   */  
   ucDa   =   uc_RTOutputByte();   /*   读1Byte数据   */  
   T_CLK   =   1;  
   T_RST   =0;  
   return(ucDa);  

/********************************************************************************************************
** 功能描述:   设置初始时间
** 入口参数:   pSecDa:   初始时间地址。初始时间格式为:   秒   分   时   日   月   星期   年
** 7Byte   (BCD码)   1B   1B   1B   1B   1B   1B   1B      
** 创 建 者:   严泽远
** 创建时间:   2010-02-06 01:02
** 版    本:   v2.1.3
********************************************************************************************************/
void   v_Set1302(uchar   *pSecDa)    
{  
   uchar   i;  
   uchar   ucAddr   =   0x80;    
   v_W1302(0x8e,0x00);   /*   控制命令,WP=0,写操作?*/  
   for(i   =7;i>0;i--)  
   {    
    v_W1302(ucAddr,*pSecDa);   /*   秒   分   时   日   月   星期   年   */  
   
    pSecDa++;  
    ucAddr   +=2;  
   }  
   v_W1302(0x8e,0x80);   /*   控制命令,WP=1,写保护?*/  
}  

/********************************************************************************************************
** 功能描述:   读取DS1302当前时间
** 入口参数:   ucCurtime:   保存当前时间地址。当前时间格式为:   秒   分   时   日   月   星期   年    
** 7Byte   (BCD码)   1B   1B   1B   1B   1B   1B   1B      
** 创 建 者:   严泽远
** 创建时间:   2010-02-06 01:02
** 版    本:   v2.1.3
********************************************************************************************************/
void   v_Get1302(uchar   ucCurtime[])    
{  
   uchar   i;  
   uchar   ucAddr   =   0x81;  
   for   (i=0;i<7;i++)  
   {  
    ucCurtime[i]   =   uc_R1302(ucAddr);/*格式为:   秒   分   时   日   月 星期   年   */  
   ucAddr   +=   2;  
   }  
}

 

七、编写18B20温度传感器读写程序

        18B20我就更不用多介绍了,常用的不得了,他的封装就像一个直插的三极管一样,只有三个管脚,电源两个脚,通讯只需要一个管脚。这也是它的特点,单线接口。而且不需要任何外围元器件,温度测量范围是-55℃至125℃,所以我说用在测量室温里面的确有点大材小用,呵呵。

        虽然读写都用一个口线,但是通讯时序并不复杂,也很简单,很容易调试出来,当然在这里我只用了读取温度的功能,其他还有很多功能比如温度告警设置,唯一序列号读取等等都没有用,程序还是很简单的,我也贴出来,有用的朋友们可以借鉴一下。

 /****************************************Copyright (c)**************************************************
**    
** 项目名称:   QS30-1辉光钟 目标板试验程序
** 文件名称:   18B20.c
** 模块功能:   实现18B20温度传感器的温度读取
**
**------------------------------------------------------------------------------------------------------

**  创 建 者:   严泽远
** E-mail  :   
[email protected]
** QQ  :   6626209
** Mobile :   18602007878
** 创建时间:   2010-11-19
** 版    本:   v1.0.0
** 描    述:   基础程序
**
********************************************************************************************************/

#include    //加载C8051F310.h头文件
#include     //加载Define.h头文件

uint temperature;    //温度全局变量

/********************************************************************************************************
** 功能描述:   简单的延时程序  
** 创 建 者:   严泽远
** 创建时间:   2010-11-17 20:52
** 版    本:   v1.0.0
********************************************************************************************************/
void delay_18b20(uchar N)
{
 uchar i,j;
 for(j=0;j<40;j++)
 for(i=0;i {;}
}

/********************************************************************************************************
** 功能描述:   DS18B20初始化子程序
** 创 建 者:   严泽远
** 创建时间:   2010-11-17 21:03
** 版    本:   v1.0.0
********************************************************************************************************/
void Init_DS18B20(void)
{
 unsigned char result_18b20;  //用来存储DQ的脉冲
 do{
  DQ=0;       //拉低总线,发出复位脉冲
  delay_18b20(25);    //延时530us
  DQ=1;       //释放总线,等待ds18b20的应答脉冲
  delay_18b20(5);    //等待110us
  result_18b20=DQ;    //读取ds18b20的应答脉冲
  delay_18b20(15);    //等待周期结束
 }while(result_18b20==1);   //若ds18b20无应答,则继续发复位脉冲
}

/********************************************************************************************************
** 功能描述:   向DS18B20读一字节数据
** 出口参数:   读取的一个字节
** 创 建 者:   严泽远
** 创建时间:   2010-11-17 20:55
** 版    本:   v1.0.0
********************************************************************************************************/
unsigned char ReadOneChar(void)
{
 unsigned char i,j;
 unsigned char dat=0;
 for(i=8;i>0;i--)
 {
  dat>>=1;      //右移一位
  DQ=0;       //拉低总线
  for(j=2;j>0;j--);    //延时4us
   DQ=1;      //释放总线,以让DS18B20把数据传输到单总线上
  for(j=4;j>0;j--);    //延时8us
   if(DQ==1)     //读取ds18b20发来的数据,从低位读起
    dat|=0x80;    //将读到的数据保存在d中
  delay_18b20(2);    //延时等待读周期结束
  DQ=1;
 }
   return(dat);      //返回读到的数据
}

/********************************************************************************************************
** 功能描述:   向DS18B20写一字节数据
** 如口参数:   dat: 要写入的一个字节
** 创 建 者:   严泽远
** 创建时间:   2010-11-17 21:09
** 版    本:   v1.0.0
********************************************************************************************************/
void WriteOneChar(unsigned char dat)
{
   unsigned char i,j;
   for(i=8;i>0;i--)
   {
   DQ=0;      //拉低总线
   for(j=2;j>0;j--);   //延时4us
    DQ=dat&0x01;   //主机在总线上写数据传送到ds18b20
   delay_18b20(3);   //延时等待写周期结束
   DQ=1;      //重新将总线拉高
     dat>>=1;     //右移一位
   }
}

/********************************************************************************************************
** 功能描述:   向DS18B20读温度值并转换成实际温度
** 创 建 者:   严泽远
** 创建时间:   2010-11-17 21:49
** 版    本:   v1.0.0
********************************************************************************************************/
void ReadTemperature()
{
 uchar tempL,tempH;
 
 Init_DS18B20();     //初始化ds18b20
 WriteOneChar(0xcc);    //发送读指令跳过rom
 WriteOneChar(0x44);    //发送指令启动温度转换
 Init_DS18B20();     //初始化ds18b20
 WriteOneChar(0xcc);    //发送读指令匹配rom     
 WriteOneChar(0xbe);    //发送指令开始读取温度
 tempL=ReadOneChar();    //读取温度值低位
 tempH=ReadOneChar();    //读取温度值高位
 Init_DS18B20();     //再发送一次复位脉冲,终止温度的读取
 temperature=((tempH*256)+tempL);//*0.625;   //转换成实际温度
 temperature/=16;
}

八、测试红外线遥控器发射和接受,编写红外遥控器解码程序

        为了让复古的辉光管与现代科技完美结合,我花了几块钱淘了一个很可爱的红外线遥控器,打算用它来实现所有的控制功能。包括开关机、调整时间等等。红外遥控在十年前要搞解码会很麻烦,要用红外线接收管,然后要用一大堆电路去解调红外线编码。现在方便了,直接用38B的红外线接收头,三个脚,两个接电源,另外一个在接收到红外线遥控编码的时候就直接将编码输出出来,可以直接接到MCU的输入口上进行软件处理。

        我在电路板上设计了一个位置用于安装红外线接收头,看一下,就在冒号的底下:

 
 

 

 

        红外线解码程序最好是用MCU的外部中断口去连接红外接收头的数据脚,这样软件会好做一些,可惜我用的MCU不带外部中断输入,所以我在MCU上随便找了一个IO,打算用定时器加软件查询的办法去对红外线数据进行解码。

        首先先认识一下红外线编码格式,我们所使用的遥控器为NEC格式的码(最常用的编码格式),通过红外接收头出来的电平是这样的,我用示波器给掐出来分析一下:

 

    

        由上面可以看出来,这是一个标准的完整红外线遥控数据,其中由引导码+16位系统码+8位按键码+8位按键反码组成。

        其中引导码由9ms的低电平和4.5ms的高电平组成。

        数据码为一段560us低电平,后引一段高电平,高电平长度为1680us时表示该BIT为1,高电平长度为560us时表示该BIT为0。

        如果我们按着遥控器的按键不放,那么遥控器会送出一段重复码,表示该按键没有释放, 重复码由9ms的低电平和2.25ms的高电平作开头,然后跟着一个脉冲。

        要精确的解码,最重要的是要去精确测量这些电平的时间长短,所以要产生一个用于测量的标准时间出来,所以我用了MCU的一个定时器,产生一个100us的标准时间,看一下测量的结果,好准啊,呵呵:

 
 
        接下来就是把所有遥控器的按键对应的编码解出来,然后根据按下的键是哪个,执行相应的功能就好了。
        在这里我把用定时器2中断去查询解码的一段程序贴出来,有需要的朋友可以借鉴一下。
 
/********************************************************************************************************
** 功能描述:   T2定时器计时完毕后执行的红外线数据解码函数
** 创 建 者:   严泽远
** 创建时间:   2010-11-19 01:45
** 版    本:   v1.0.0
********************************************************************************************************/
void Timer2Interrupt() interrupt 5  

 bit F_Pulse = 0;        //是否收到一个数据
 TF2H=0;           //清零中断标志位
 if(IR_LastLH==1 && IR==0)      //一个脉冲完毕
 {
  switch(IR_State)
  {
   case WAIT:       //等待状态
    if((IR_LC>=85 && IR_LC<=95) && (IR_HC>=40 && IR_HC<=50))  //新引导码由9ms的低电平和4.5ms的高电平组成
    {
     IR_State = RECIVE;   //当前为开始接收数据状态
     IR_BufByte = 0;    //从第一个数据开始接收
     IR_BufBit = 0;    //从第零位数据开始接收
     IR_Rep  = 0;    //重复码数量减少
    }
    if((IR_LC>=85 && IR_LC<=95) && (IR_HC>=17 && IR_HC<=27))  //重复码由9ms的低电平,2.25ms的高电平,跟着是一个短脉冲
    {
     F_NewIRdata = 1;    //有了新红外数据 
     if(IR_Rep<255) IR_Rep++;  //重复码数量增加
    }
    break;
   case RECIVE:       //开始接受数据状态
    if((IR_LC>=3 && IR_LC<=7) && (IR_HC>=14 && IR_HC<=18))   //数据码为一段560us低电平,后引一段高电平,高电平长度为1680us时为1
    {
     IR_Buf[IR_BufByte] <<= 1;  //左移一位
     IR_Buf[IR_BufByte] |= 0x01; //或上数据
    }
    if((IR_LC>=3 && IR_LC<=7) && (IR_HC>=3 && IR_HC<=7))   //数据码为一段560us低电平,后引一段高电平,高电平长度为560us时为0
    {
     IR_Buf[IR_BufByte] <<= 1;  //左移一位
    }
    IR_BufBit++;      //数据位数增加
    if(IR_BufBit>=8)     //如果记录了8位数据
    {
     IR_BufBit = 0;    //开始计算另一字节的第零位数据
     IR_BufByte++;     //字节数增加
    }
    if(IR_BufByte>3)     //如果记录满了4个字节则开始退出分析按键码
    {
     IR_State = WAIT;   //恢复到等待接受红外信号状态
     F_NewIRdata = 1;    //有了新红外数据 
    }
  }
  IR_LC=0; IR_HC=0;      //复位电平计数值  
 }     
 if(IR==0) {IR_LC++;IR_LastLH=0;IR_HC=0;}  //低电平时长计数
 if(IR_LC>100) {IR_LC=100; IR_State=WAIT;}  //如果低电平连续超过10ms即为非正常数据
 if(IR==1) {IR_HC++;IR_LastLH=1;}    //高电平时长计数
 if(IR_HC>100) {IR_HC=100; IR_State=WAIT;}  //如果高电平连续超过10ms即为非正常数据
 
 ShowLED++;
 if(ShowLED>=40) ShowLED=0;
 if(ShowLED
 else     BLUELED = !ShowLEDBit;
}
 

九、使用红外线遥控器实现相应功能

        大功即将告成,先看一下带有遥控功能,并且能正确显示时钟的辉光钟的样子吧。
        点亮之前,先把我仅有的6颗风光牌的辉光管拿出来,在国产QS30-1辉光管里面,风光牌的应该是最好的,风光是上海产的,他跟南昌牌的最大区别就是表面的一层网是很细的,南昌的是蜂窝状的,最大的区别还是显示的效果,可能风光牌儿的真空度好一些吧,显示的效果更加明亮细腻,不拖泥带水。先把南昌的拆下来,对比一下,然后上风光咯。
 
 
 
 
 
 
        先说一下遥控实现的功能:
        1、按下“箭头上”,可以打开或关闭背景灯。
        2、按下“电源”,可以关闭辉光钟,但是背景灯会按一秒为频率进行明暗交替,注意哦,不是亮和灭,是渐亮和渐暗,幽幽的光芒晚上好是迷人。虽然关闭辉光钟,但是没到整点的时候,辉光钟会自动打开显示,并且会通过蜂鸣器报时,显示10秒钟之后再次进入关机状态。
        3、按下“箭头左”可显示阳历时间。
        4、按下“中间键”可显示农历时间(注意,这里农历时间是不需要设置的,我写了一个公历转农历的程序,农历时间会根据公历时间自动计算出来。代码我就不贴了,有需要的朋友可以咨询我)。
        5、按下“箭头右”可显示当前的室内温度。
        6、按下“右上角的键”不放,三秒钟之后进入时间设置功能(必须按住三秒钟以上,不然误按了可要改了时间了)。此时可以通过数字键重新设定年、月、日、时、分、秒。当然,星期不用设置,会按照日期自动计算出来,这个算法大学里面都学过的,我就不赘述了,简单得不得了。
        对应的图片都在下面,SHOW一下啦:
 
 
 
 
 
 
 
 
 
 
 
 
 

十、大功告成,为辉光钟上一个底板,就用镀金的PCB板做外壳。

        至此,6位辉光管的红外遥控辉光钟制作完毕,因为一块电路板就完成了所有硬件装配,所以要给他配一个底板。
        有点奢侈,我又用了一块镀金电路板,呵呵,为了防止螺丝把金层磨坏了,我用了尼龙螺丝和螺柱,把两块电路板拼装起来。
        还有,原本设计的温度传感器18B20是直接焊接在电路板上的,但是为了防止电路板上的器件发热的热量直接通过管脚传导给传感器,造成室内文温度读取不准,所以我用延长线把传感器单独拉出来了,效果图如下:
 
 
 
 
 
 
十一、总结,上图,收工。
 
        从硬件设计到软件设计,中间还包括10天的PCB板制作过程,整个辉光钟设计制作耗时20多天。整个电路硬件部分全部集中在一块PCB电路板的背面,为了美观,绘制PCB板图的时候耗费了大量的精力和时间,但最终做出来的镀金线条还是达到了设计目的,PCB板正面没有任何元器件,并且用这些镀金线条围绕衬托在托辉光管周围还是很般配的,所以不需要配置外壳。在打造这个辉光钟的过程中,从硬件到软件都体现了古典与现代结合的特色。
        这里面用到的元器件前后跨越30多年,现在把它们组合在一起做成了一个记录时间流逝的时钟,也算用它们去铭记历史,见证发展。
        不多说了,上图,收工。
 
 
 
 
 
 
 
 
 
        跟我以前做的LED点阵时钟也来一个亲密接触。
        让他们合个影:
 
 
 
 
 
 
 
 
 
DIY是一种享受,享受的是过程,更是结果......
 
========================================================
作者:严泽远
QQ:6626209
2010-11-20
 
 

你可能感兴趣的:(电子DIY)