|
VFD显示屏玩了快10年了,数码之家也有了新站了。感慨~
自从大学第一次接触到这玩意就立刻爱上其效果,无奈兜里没钱,矿坛老陈VFD只收了10块左右(现在遇上先50块起),现已绝迹。还有一些可玩性不佳的字符型屏、二十几块数码管屏、零零散散的老旧则武25664、12864等等,质量普遍不高,都是有明显老化痕迹的旧玩意儿。VFD以难驱动著称,需要逻辑3.3-5V,灯丝交流浮地,阳极栅极正高压或负高压,所以在老一些的屏幕上(2010年以前)普遍采用单管或双管自激的变压器方案,对于自动控制出身的我而言,费劲研究变压器是十分不明智的举动,于是这类屏幕都直接购买模组。后来出现了DC-DC驱动器+倍压整流的邪道,省去了变压器,灯丝用BTL推挽或电桥的方案,可以简化供电设计,也是我在使用的方案之一。
随后就到了今天的主角:卡罗拉车机VFD。这是一款256*50的点阵,已经有不少朋友玩过,例如:
https://www.mydigit.cn/forum.php?mod=viewthread&tid=119776
https://www.mydigit.cn/forum.php?mod=viewthread&tid=81043
车机前面板
背后样式
电路板正面
这款屏幕的特点有三:
首先是全新。256*50的图形点阵,但完全没有烧蚀痕迹,模组买到手的时候还有保护膜,可见品质非常好;
其次是低价。目前咸鱼30元就能买到,货源相对充足(直接入手40块,留着慢慢玩);
第三是先进。VFD玻璃屏的集成度按从低到高分为三类:栅极阳极直接引出的全外置驱动式,集成了高压移位寄存器和锁存器的半集成式,集成了完整的扫描控制器的全集成式。这块屏幕就是全集成式,用MCU和屏玻璃直接通信就好像在和以前的一个模组通信一样,方便至极。并且!该屏幕的灯丝是直流驱动!DIY玩家要跪谢双叶了。VFD驱动难,核心就在浮地的灯丝上,这家伙直接直流驱动,一了百了(后会附上示波器图)。
模组详细数据的链接都来自这位网友的帖子(在此感谢,大大减少了研究指令的时间):https://www.mydigit.cn/forum.php?mod=viewthread&tid=119776
一般而言接下来的事情就是写个代码往屏幕上写数据就行了,但是我想玩点不一样的东西,毕竟STM32 CM3和CM4的性能都很强大,只是推个SPI就显得十分牛刀杀鸡。u8g2虽然好用但毕竟是第三方的东西,享受不到自己搭建软件框架的乐趣,于是我决定在这个屏幕上,实现还没人玩过的灰度显示。
先上效果(肉眼看效果会比图上好很多,毕竟扫描周期和手机曝光时间有些接近):
显示4级灰度条纹,从左往右对应灰度值1-4
对于LED这类器件,灰度显示的原理统统是PWM占空比控制,原因是占空比描述了一个周期方波信号的RMS值比例,通过合理控制正脉宽,就能实现调光的效果。
为了不用公式把事情搞复杂,这里引出两个概念即可:
PWM频率fPWM,和PWM分辨率N(N值代表一个PWM周期内最多可有的占空比数量)
PWM频率是PWM信号的基频,就是我们所说的帧率;而PWM分辨率则是指正脉宽宽度变化的最小单位。则我们可以看到,当给定一个PWM频率和PWM分辨率的时候,可以得到产生PWM所使用的计数器最低时钟频率 fclk=fPWM * (N-1)
反之,当fclk固定,给出fPWM,即可得到最大可能拥有的分辨率。
那这和实现灰度有什么关系呢?
前面提到过,卡罗拉车机这款屏幕是一款全集成屏,意味着我无法随意控制它的扫描方式和扫描频率,而为了肉眼看到的画面没有闪烁感,fPWM不可能太低,意味着N不可能太大。那么有没有办法知道这块屏幕的扫描频率呢?相似屏的手册中给出了答案:
这块屏有一个INT输出引脚,一旦配置打开,它将输出当前激活的扫描单元的位置信息:是第一个单元就为有效,否则为无效。可见,记录这个引脚的频率,就可以获得整块显示屏的扫描频率。上700元收购的二手老旧示波器(我想买泰克的2.5Gsps四通,然而它要4W块):
扫描频率是189Hz左右(上方的波形),这就是上文提到的fclk。
此时如果切出5级灰度,即N=5,那么fPWM就是fclk / (5 - 1) ≈ 45Hz,属于一个比较可接受的范围。灰度级再多,屏幕就会不可忽视地闪烁。
接下来就是设计软件部分了,板子采用2012年买的STM32F103VET,配一个盗版STlink,先用旧板子初步测试
底层负责将显示数据pour到VFD上,需要有强实时性,同时不能占用CPU。其逻辑如下:
1、准备显存。256*50按每列7个字节安排,一屏数据需要256*7=1792字节,非零的灰度等级有N-1=4级,因此需要1792*4=7168字节的RAM作为底层显存。这部分显存是为最底层的数据收发使用的,其layout完全是为了发送效率,不便于高层API读写操作,因此还需要一个高层所使用的通用显存,为每个点单独分配一个uint8_t,这部分需要256*50=12800字节;
2、我们在每一次第一个扫描单元被激活的时候启动整屏数据的传输,传输完毕之后等待下一个激活信号的到来。这个激活信号就是INT,使用GPIO外部中断触发;
3、每次进入外部中断以后,等待上次传输结束(其实根本不用等),然后更新下一次要发送的数据位置和数据长度,拉低片选NCS,发送写显示数据指令,等待指令解码(按GP1212手册需要等待360ns,实测不等待也可以),并启动DMA传输;
4、等待DMA传输完成中断,拉高NCS,结束一次数据发送。
*这块屏幕SPI时钟最高频率在4.2MHz左右,再高会传输失败,因此传送完整屏的数据大约需要3.2ms左右。可见如果只用CPU而不用DMA,STM32的CPU将有超过一半的时间都在负责数据外发(SPI传输使用等待方式),或者频繁响应SPI传输中断(SPI传输使用中断方式),使用效率将大打折扣。
中层负责将通用显存中的数据映射到底层的显存中,需要把每一个字节切片,然后按照一定的pattern分发到底层的显存中,这部分很难用硬件完成,需要占用CPU,好在编译器优化开到O3也是非常快的。
直接上代码片段:
- /** Split into subframes */
- ppixel = vfd_px.pix;
- for (ii = 0; ii < VFD_COL_CNT; ++ii)
- {
- for (jj = 0; jj < VFD_ROW_CNT; ++jj)
- {
- /** Edit below to change subframe layout */
- idxf = jj & 1 ? (ii & 1) ^ 0x03 : (ii & 1);
- pxbuf = *ppixel++;
-
- for (i = 0; i < VFD_GRAYSCALE_CNT; ++i)
- {
- if (pxbuf)
- {
- vfd_frame.sframe[idxf].vmem[ii*VFD_BYTES_PER_COL + jj / 8] |= 1 << (jj % 8);
- pxbuf--;
- }
- else
- vfd_frame.sframe[idxf].vmem[ii*VFD_BYTES_PER_COL + jj / 8] &= ~(1 << (jj % 8));
-
- if (++idxf == VFD_GRAYSCALE_CNT)
- idxf = 0;
- }
- }
- }
复制代码
中层同时还要负责提供一些图形和文字API给上层调用,例如显示个数字、画个圆、画个方块之类的,现在还没写,后续会慢慢实现(工作太忙。。。)。
中层的数据链路为:上层调用 -> 中层API -> 中层显存 -> 切片 -> 底层显存 -> VFD
灰度实现大概就这么多。
终极目的是用它做一个通用显示器,可以和树莓派对接显示终端,或者换STM32F4以后做一个复古游戏机什么的。别老做时钟万年历啦!现在都用手机,不如做一点与众不同的东西嘛。
后续会在GitHub上分享代码。如果希望了解更多的细节,可以邮件联系我:532493398@qq.com
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
打赏
-
查看全部打赏
|