1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
1. 前言
之前在网上看到辉光管的视频,感觉对前苏联工艺很感兴趣,很喜欢那种复古风,查了很多资料,发现那玩意儿好像咱也玩不起,一个大管子好几百,还要高压电路,忒麻烦了。后来又了解到前苏联的荧光数码管,虽没有那么强的复古风,但是看到那透明的基板,比头发丝还细的栅极网,还是不得不感叹那个时候居然有这么好的工艺。 当然对于我来说还是亲民的价格,一根管子不到100,自带8段数字显示,一根管子就能直接当时钟,驱动电压更低,集成DC-DC即可满足驱动需求,大大减小电路体积。 当然网上也有大神做出来的成品,但是几百块的价格实在让人难以接受,于是在查阅许多资料后,我决心用低成本方案自制一个出来,因此整篇文章都透露出贫穷的气息 。 这里放几张从网上找来的图片:(绝不是我懒得拍) 2. 电路设计 2.1 主控部分 主控部分采用STM32C8T6,很常用的单片机,价格便宜,外设丰富,引脚多。主控部分就是最最最简单的单片机最小系统,由于太简单,就不贴图了。 2.2 电源部分 系统由MicroUSB输入5V供电,供电电路由2个部分组成,一是5V转3.3V为单片机供电,芯片采用ASM1117这种常见的LDO,图就不贴了。 另一部分是荧光管供电,电压需控制在40-60V左右,芯片采用XL6007E1,是一款国产DCDC芯片,最高输出电压可达60V,不需要外扩MOS管,SO-8封装满足使用需求。升压电路如下图所示: 标准的boost结构,输出电压只需通过调节R7和R8的比值即可,EN引脚接入单片机可以方便控制电压是否输出。5V输入端加入TVS管,防止接错电压烧坏板子,单向TVS管还自带防反接功能,可谓一举两得。 2.3 外设部分 外设部分由时钟、蜂鸣器、温度、红外、光敏、按键,六个部分组成。 时钟芯片采用DS3231MZ,内置温度补偿,晶振误差为5ppm,较为优秀,同样SO-8的封装,只需要很少的外围电路即可驱动,电路如下: 芯片采用I2C通信,同时具备32.768KHZ输出和秒脉冲输出,将时钟输出接到单片机的晶振输入上,秒脉冲输出接到GPIO上,方便单片机控制。 其余部分简单介绍一下: 闹钟使用无源蜂鸣器,准备采用单片机PWM控制音调。 温度检测使用18B20作为备选,时钟芯片内部有温度模块,但是分辨率只有0.25℃,理论上18B20要准一点。 环境光检测采用贴片PTSM D021,0805封装,非常小,准度也高,可以尝试通过检测环境光来调节荧光管亮度。 TSOP32240为红外接收管,作为备用,兴许可以用遥控器控制时钟,但似乎没什么必要,总之还是留了接口。 2.4 显示部分 核心部分来了 荧光管驱动采用HV5812,专用于驱动荧光管,最高驱动电压为80V,总共20路输出,时序为SPI时序。电路如下: 内部框图如下: DATA OUT引脚用于级联,这里只有一个管子,所以不用接,BLANK引脚可以控制是否输出高压,可以通过PWM控制这个引脚,实现荧光管的亮度控制。 HV5812的手册对芯片的操作写得很粗糙,所以查阅手册时建议查MAX6921,这个手册写的很详细,两款芯片引脚完全兼容,但是HV5812更便宜,所以这里采用HV5812。 IV18与HV5812的连接如下: 注意这不是标准连接方法,只是为了方便布线 1脚和13脚为管子的灯丝引脚,网上都说用5V供电,但我看用5V供电时灯丝都发红了,但其实采用3.3V供电甚至2.5V供电都可以正常显示,个人认为电压低一点好,可以延长寿命。灯丝同样采用三极管驱动,LTGHT端接入单片机定时器输出引脚,方便以后用PWM调节灯丝电压。 2.5 PCB设计 2.5.1 封装制作 根据网上查阅的资料,荧光管总计22个引脚,呈环形分布,直径大约10mm,如何将这22个焊盘均匀分布成一个圆呢?这里有个AD使用小技巧。 ①先画一个合适大小的焊盘,标号设为1,复制这个焊盘。 ②选择:编辑→特殊粘贴→粘贴阵列 ③阵列类型选择圆形,条款计数是焊盘数量的意思,这里有22个引脚,勾选旋转项目到适合,因为我这里是椭圆焊盘,这个选项可以让焊盘指向圆心,间距是其实是角度,每个焊盘中心与圆心的夹角,360°÷22个焊盘=16.363°,这个要根据实际情况计算。 设置完成后点确定,鼠标变成十字,点第一下是确定圆心位置,点第二下是确定1脚位置,一个环形分布的焊盘就画好了。 2.5.2 电路绘制 首先确定外形,外观方面我参考网上的大神,大致为方形,留出三个角用于上铜柱,整体结构使用PCB支撑,不需要制作外壳(省成本)。 ①设计外观: ②摆放元器件 ③加一点点调整 再放上自己喜欢的图案就OK啦,一块荧光管的驱动板就画好了。 2.5.3 投板+采购 这里给嘉立创打一个免费广告,双层板10*10mm以内,5张只要5块钱,还能选颜色,元器件同样是在立创商城购买,种类又多又齐,价格也比较公道。唯一的缺点是PCB板和器件不能一起发货,要给2次运费,加上荧光管的运费等,这个钟的成本里运费占了很大一部分。 2.6 焊接 将原件摆正,用电烙铁焊上即可,焊完后要用洗板水清洗,不然焊点全是助焊剂,很影响,美观。 2.7 装配 由于没有外壳,我采用铝柱将两块板子固定起来,整个时钟由电路板自身支撑,整个时钟6个螺丝即可搞定。 盖板图案 3. 软件设计 · 3.1 初始化 初始化步骤大致分为三步: 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); } 3.2 字符显示 · 根据前面的电路图可得知,段码对应的数据位置如下: 因此要显示数字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就写好了。 3.3 时钟芯片读写 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=Single_ReadI2C(RTC_Addr); } 将转换好的数据按位转换,存入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);,在主循环中加入刚才的时间读取和数据转换函数,即可显示时间。效果如下: 3.4 温度 · 3.5 秒表 · 3.6 闹钟 · · · · · · · 溜了,剩下的后面再写 |
|
|
|
只有小组成员才能发言,加入小组>>
3310 浏览 9 评论
2991 浏览 16 评论
3492 浏览 1 评论
9055 浏览 16 评论
4086 浏览 18 评论
1174浏览 3评论
603浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
596浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2333浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1894浏览 2评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-21 16:41 , Processed in 1.144951 second(s), Total 78, Slave 59 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号