赞
踩
之前在网上看到辉光管的视频,感觉对前苏联工艺很感兴趣,很喜欢那种复古风,查了很多资料,发现那玩意儿好像咱也玩不起,一个大管子好几百,还要高压电路,忒麻烦了。后来又了解到前苏联的荧光数码管,虽没有那么强的复古风,但是看到那透明的基板,比头发丝还细的栅极网,还是不得不感叹那个时候居然有这么好的工艺。
当然对于我来说还是亲民的价格,一根管子不到100,自带8段数字显示,一根管子就能直接当时钟,驱动电压更低,集成DC-DC即可满足驱动需求,大大减小电路体积。
当然网上也有大神做出来的成品,但是几百块的价格实在让人难以接受,于是在查阅许多资料后,我决心用低成本方案自制一个出来,因此整篇文章都透露出贫穷的气息 。
这里放几张从网上找来的图片:(绝不是我懒得拍)
主控部分采用STM32C8T6,很常用的单片机,价格便宜,外设丰富,引脚多。主控部分就是最最最简单的单片机最小系统,由于太简单,就不贴图了。
系统由MicroUSB输入5V供电,供电电路由2个部分组成,一是5V转3.3V为单片机供电,芯片采用ASM1117这种常见的LDO,图就不贴了。
另一部分是荧光管供电,电压需控制在40-60V左右,芯片采用XL6007E1,是一款国产DCDC芯片,最高输出电压可达60V,不需要外扩MOS管,SO-8封装满足使用需求。升压电路如下图所示:
标准的boost结构,输出电压只需通过调节R7和R8的比值即可,EN引脚接入单片机可以方便控制电压是否输出。5V输入端加入TVS管,防止接错电压烧坏板子,单向TVS管还自带防反接功能,可谓一举两得。
外设部分由时钟、蜂鸣器、温度、红外、光敏、按键,六个部分组成。
时钟芯片采用DS3231MZ,内置温度补偿,晶振误差为5ppm,较为优秀,同样SO-8的封装,只需要很少的外围电路即可驱动,电路如下:
芯片采用I2C通信,同时具备32.768KHZ输出和秒脉冲输出,将时钟输出接到单片机的晶振输入上,秒脉冲输出接到GPIO上,方便单片机控制。
其余部分简单介绍一下:
闹钟使用无源蜂鸣器,准备采用单片机PWM控制音调。
温度检测使用18B20作为备选,时钟芯片内部有温度模块,但是分辨率只有0.25℃,理论上18B20要准一点。
环境光检测采用贴片PTSM D021,0805封装,非常小,准度也高,可以尝试通过检测环境光来调节荧光管亮度。
TSOP32240为红外接收管,作为备用,兴许可以用遥控器控制时钟,但似乎没什么必要,总之还是留了接口。
核心部分来了
荧光管驱动采用HV5812,专用于驱动荧光管,最高驱动电压为80V,总共20路输出,时序为SPI时序。电路如下:
内部框图如下:
DATA OUT引脚用于级联,这里只有一个管子,所以不用接,BLANK引脚可以控制是否输出高压,可以通过PWM控制这个引脚,实现荧光管的亮度控制。
HV5812的手册对芯片的操作写得很粗糙,所以查阅手册时建议查MAX6921,这个手册写的很详细,两款芯片引脚完全兼容,但是HV5812更便宜,所以这里采用HV5812。
IV18与HV5812的连接如下:
注意这不是标准连接方法,只是为了方便布线
1脚和13脚为管子的灯丝引脚,网上都说用5V供电,但我看用5V供电时灯丝都发红了,但其实采用3.3V供电甚至2.5V供电都可以正常显示,个人认为电压低一点好,可以延长寿命。灯丝同样采用三极管驱动,LTGHT端接入单片机定时器输出引脚,方便以后用PWM调节灯丝电压。
根据网上查阅的资料,荧光管总计22个引脚,呈环形分布,直径大约10mm,如何将这22个焊盘均匀分布成一个圆呢?这里有个AD使用小技巧。
①先画一个合适大小的焊盘,标号设为1,复制这个焊盘。
②选择:编辑→特殊粘贴→粘贴阵列
③阵列类型选择圆形,条款计数是焊盘数量的意思,这里有22个引脚,勾选旋转项目到适合,因为我这里是椭圆焊盘,这个选项可以让焊盘指向圆心,间距是其实是角度,每个焊盘中心与圆心的夹角,360°÷22个焊盘=16.363°,这个要根据实际情况计算。
设置完成后点确定,鼠标变成十字,点第一下是确定圆心位置,点第二下是确定1脚位置,一个环形分布的焊盘就画好了。
首先确定外形,外观方面我参考网上的大神,大致为方形,留出三个角用于上铜柱,整体结构使用PCB支撑,不需要制作外壳(省成本)。
①设计外观:
②摆放元器件
③加一点点调整
再放上自己喜欢的图案就OK啦,一块荧光管的驱动板就画好了。
这里给嘉立创打一个免费广告,双层板10*10mm以内,5张只要5块钱,还能选颜色,元器件同样是在立创商城购买,种类又多又齐,价格也比较公道。唯一的缺点是PCB板和器件不能一起发货,要给2次运费,加上荧光管的运费等,这个钟的成本里运费占了很大一部分。
将原件摆正,用电烙铁焊上即可,焊完后要用洗板水清洗,不然焊点全是助焊剂,很影响,美观。
由于没有外壳,我采用铝柱将两块板子固定起来,整个时钟由电路板自身支撑,整个时钟6个螺丝即可搞定。
盖板图案
·
初始化步骤大致分为三步:
GPIO_Configuration(); //GPIO初始化
I2C_Configuration(); //时钟芯片初始化
TIM2_Configuration(); //定时器初始化
宏定义:
#define BLANK PAout(3) //显示使能,1不显示,0显示
#define CLK PAout(4) //时钟输入
#define LOAD PAout(5) //数据加载
#define DIN PAout(6) //数据输入,20位
#define LIGHT PAout(7) //灯丝
#define BEEP PAout(8) //蜂鸣器
#define SQW PBin(0) //秒脉冲
宏定义的方法可以参考原子哥的位带操作库,优点是代码简洁,操作速度快。
·
SPI和I²C均采用IO模拟方式,I²C的操作方式比较复杂,网上也有很多例程,这里不再赘述,我也基本是照着例程来的。
SPI的操作略有不同,由于HV5812的数据是20位的,而STM32F1的硬件SPI并没有20位数据选项,所以需要对网上的例程稍微改一改。
改动就是把i<8改为了i<20,再把TxData & 0x80改成TxData & 0x80000。
// 模拟SPI-写8位数据 // SPI Mode 0: CPOL=0, CPHA=0, MSB // 时钟为低,数据上升沿发送 // 高位在前 /// void SPI_WriteByte(uint32_t TxData) { uint8_t i; LOAD=0; for(i=0;i<20;i++) { if(TxData & 0x80000) {DIN=1;} else {DIN=0;} delay_nop(3); CLK=1; TxData <<= 1; delay_nop(3); CLK=0; } LOAD=1; delay_nop(3); // LOAD=0; delay_nop(3); }
·
根据前面的电路图可得知,段码对应的数据位置如下:
因此要显示数字1,则需要将B和C所在的数据位置置1,因此数字1的16进制为:0x24。
SPI的数据是20位的,根据电路图可知,低八位存放的是段码数据,剩下的高位存放的是位码数据,高位先全部置1,写入0xFFF24进行测试:
SPI_WriteByte(0xFFF24);
就可以看到所有数码管都被点亮了,字符显示已经成功一半了。
要想实现显示不同数字,还需要使用动态刷新才行。将位码表和段码表分别保存在2个数组内。将要显示的数字用另一个数组存放,每一位存放一个数字,当要改变显示的数字时,直接改变存放数字的数组即可。由于段码和位码是在一个数据里同时输出的,因此需要在输出前进行组合。
动态刷新时,每个字符显示的时间不能太长,否则会出现频闪,也不能太短,否则会使亮度降低,且消耗大量的CPU资源。因此需要合理设置delay_nop的时间。
动态显示程序如下:原理很简单,就是高速依次点亮每一个数字,由于视觉的暂留效应,因此看到的是一串数字而不是单个的数字。
u8 DIS_TMP[10]={19,19,0,25,25,14,31,19}; //要显示的数据:hello /* *产品可能存在差异,每个显示管的亮度不一定一样 *可以分别设置不同的显示延时 *从而确保亮度一致 */ void Display(u8* disp) //要显示的数,数组形式 { SPI_WriteByte(DIG_CODE[0]|SEG_CODE[disp[0]]); //DIG_CODE为位码表 delay_nop(dsp_nop); SPI_WriteByte(DIG_CODE[1]|SEG_CODE[disp[1]]); //SEG_CODE为段码表 delay_nop(dsp_nop); SPI_WriteByte(DIG_CODE[2]|SEG_CODE[disp[2]]); delay_nop(dsp_nop); SPI_WriteByte(DIG_CODE[3]|SEG_CODE[disp[3]]); delay_nop(dsp_nop); SPI_WriteByte(DIG_CODE[4]|SEG_CODE[disp[4]]); delay_nop(dsp_nop); SPI_WriteByte(DIG_CODE[5]|SEG_CODE[disp[5]]); delay_nop(dsp_nop); SPI_WriteByte(DIG_CODE[6]|SEG_CODE[disp[6]]); delay_nop(dsp_nop); SPI_WriteByte(DIG_CODE[7]|SEG_CODE[disp[7]]); delay_nop(dsp_nop); SPI_WriteByte(DIG_CODE[8]|SEG_CODE[disp[8]]); delay_nop(dsp_nop); }
不断在主循环中调用 Display(DIS_TMP);这个函数,就能显示出不同的字符了,效果如下:
一个简单的HELLO WORLD就写好了。
DS3231MZ的读写是基于I²C的,查阅手册可知,芯片的地址为:0XD0。将芯片地址加入宏定义:
#define SlaveAddress 0xD0 //从机地址(写)
继续查阅手册可以得知,时间的寄存器位置,将寄存器位置保存在数组中,方便调用:
u8 RTC_Addr[8]={0x06,0x05,0x04,0x02,0x01,0x00,0x11,0x12}; //寄存器地址
// 年, 月, 日, 时, 分, 秒, 温度H,温度L
将读取的时间存入显示变量,需要注意的是,时间保存的方式为BCD码,需要转换为HEX才能正常使用。
for(i=0;i<6;i++) //读时间寄存器
{
RTC_TIME[i]=Single_ReadI2C(RTC_Addr[i]);
}
将转换好的数据按位转换,存入DIS_TMP 。
DIS_TMP[0]=RTC_TIME[5]%10;
DIS_TMP[1]=(RTC_TIME[5]/10)%10;
DIS_TMP[2]=16; //横杠
DIS_TMP[3]=RTC_TIME[4]%10;
DIS_TMP[4]=(RTC_TIME[4]/10)%10;
DIS_TMP[5]=16; //横杠
DIS_TMP[6]=RTC_TIME[3]%10;
DIS_TMP[7]=(RTC_TIME[3]/10)%10;
DIS_TMP[8]=19;
之前的程序中,主循环一直在调用Display(DIS_TMP);,在主循环中加入刚才的时间读取和数据转换函数,即可显示时间。效果如下:
·
·
·
·
·
·
·
·
·
溜了,剩下的后面再写
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。