[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升压方案是否可行,我用洞洞板搭了一个实际电路。虽然比较乱,但是可以验证升压电路效果,见下图:
三、电路原理图设计及PCB板设计
这个试验做完,就可以放心大胆的设计MCU控制部分、电源部分、译码及驱动部分电路了,这里面用到的译码电路是74HC595,但要注意的是驱动辉光管用的三极管要耐高压,所以这里选用了常用的A42。MCU采用常用的C8051F300,体积小,内置晶体,支持在线编程及调试,很方便,价格便宜,9RMB左右。还有值得一提的是,DS1302是很常用的时钟IC,但时钟走的准不准,取决于匹配的32.768kHz晶体的精度。我这里选用的是日本西铁城晶体,精度为±5ppm。
成这些电路部分几乎不用做实验,马上设计电路图 ^_^ :
四、硬件电路的焊接与调试过程
辉光数码管跟普通电子管样子相似,起初对它无任何了解,拿到这个QS30-1管子以后发
下面那块小的是给QS18的辉光管做的PCB板,采用的同样的布板风格,将所有GND网络线条禁上阻焊,然后采用沉金工艺处理。实际拿到手上看到的效果会比图片上好很多,而且金层不会被氧化腐蚀,这个效果会持久下去。
因为辉光管和管座加起来的高度比较高,而氖灯体积太小,太矮,如果直接焊在电路板上感觉很不协调,所以一直在为这个不协调而绞尽脑汁。后来淘到一批小试管(是我见过的直径最小的试管了),外径6mm,内径5mm,长度大概3cm左右,正好跟辉光管+底座以后的高度相同,所以就产生了一个想法,把氖灯放进这个试管里,用这个玻璃试管做一个长度3cm直径6mm的氖灯灯管。
氖灯有很多种,现在有些氖灯还能发出绿色红色的光。当然,最早的氖灯也叫氖泡,就是我们常用的测电笔上的那种氖泡,发出的是橘红色光,正好跟辉光管发出的光颜色一样,所以才想到用氖灯来做搭配,指示冒号等小点点。
不过在选用这个氖灯的时候也走了很多弯路。一开始淘了一些直径4mm左右的氖灯,回来测试了一下,必须在电压高于190v的时候才能被点亮,而且一致性不好,有些210v都不亮,而且外径很不均匀,有些不是很圆,放不进小试管里面去。后来好不容易淘到了一批进口的3mm直径的氖灯,效果很好,90v就能点亮,而且亮度很一致。也做了反复的实验,在180V的时候,串联240K的电阻,能够在亮度满意的情况下达到理想的亮度,并且使耗电电流降到最低(0.8mA)。
但是这个玻璃管怎么固定在电路板上呢?毕竟玻璃没法焊接,而且氖灯的焊脚很细,只有0.5mm,也无法支撑的那么好那么一致,这要焊上去一排灯管结果东倒西歪岂不是大煞风景。后来想到了一个办法,用5mm直径的LED来制作管座。因为小试管的内径是5mm,LED的外径小于等于5mm,正好能套上,并且LED的管脚硬,还带个底座,能够很牢靠的固定在焊盘上,所以找来了这些东西以后,开始动手做,先看一下制作氖灯灯管的材料:
这是把LED套进小试管的效果,比较理想。
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
#include
#include
#include
/********************************************************************************************************
** 功能描述: 往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
#include
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的标准时间,看一下测量的结果,好准啊,呵呵:
九、使用红外线遥控器实现相应功能
十、大功告成,为辉光钟上一个底板,就用镀金的PCB板做外壳。