基于树莓派Pico与MicroPython的辉光管时钟融合设计
1. 缘起从复古辉光到现代液晶的融合构想最近在整理工作室的零件箱时翻出了几枚早年收藏的QS27-1辉光管。看着那橙红色的数字一种强烈的复古情怀涌上心头。辉光管时钟作为电子爱好者和复古美学爱好者的“梦中情物”其独特的视觉魅力毋庸置疑。然而一个现实问题摆在面前辉光管本身功耗高、驱动电路复杂、需要高压而且我那几枚管子品相不一直接做一个全尺寸的时钟无论是从制作难度、安全性还是日常使用的便利性比如夜间亮度来看都不是最优解。就在我琢磨怎么处理这些管子时一个想法冒了出来为什么不做一个“融合体”呢用现代的低功耗、高清晰度的液晶屏LCD来模拟辉光管的显示效果同时保留辉光管作为“灵魂点缀”。具体来说就是制作一个以液晶屏为主显示器的时钟但专门开辟两个数字位用真实的辉光管来显示“小时”的十位和个位。这样既满足了日常清晰看时的需求又能在关键时刻比如整点、或者你特意想看的时候点亮那两枚辉光管让温暖的辉光成为整个作品的点睛之笔。这个“2位辉光管液晶时钟”的项目构想就此诞生。这个项目听起来简单但涉及了多个技术层面的融合微控制器编程、液晶屏驱动、高压产生与辉光管驱动、以及两者之间的协同逻辑。它非常适合那些已经玩过Arduino或树莓派Pico想挑战更综合项目的爱好者。你将学到如何用MicroPython这类高级语言优雅地控制底层硬件如何驱动SPI接口的彩色液晶屏以及如何安全地设计和控制一个需要近百伏电压的辉光管驱动电路。最终你会得到一个独一无二、兼具实用性与艺术感的桌面摆件。2. 核心器件选型与电路设计逻辑确定了“液晶主显辉光管点缀”的架构后接下来的核心就是为这个架构选择合适的“大脑”和“四肢”并设计让它们安全、协同工作的电路。2.1 微控制器为什么是树莓派Pico主控芯片的选择很多从经典的ATmega2560到热门的ESP32系列。我最终选择了树莓派Pico基于以下几个非常实际的考量性能与性价比的平衡Pico采用的RP2040双核ARM Cortex-M0处理器主频133MHz性能对于驱动液晶屏和运行时钟逻辑绰绰有余。其价格极具竞争力远低于同性能级别的其他开发板。对MicroPython的极致友好这是最关键的一点。树莓派Pico从设计之初就将MicroPython作为一等公民支持。其出厂固件就包含MicroPython烧录固件Download Firmware的过程简单到只需拖拽一个.uf2文件到虚拟U盘即可完全避免了ESP32等平台烧录时可能遇到的端口识别、工具链配置等麻烦。对于快速原型开发来说这种便捷性是无价的。丰富的GPIO与硬件接口Pico提供了26个多功能GPIO足够我们同时连接SPI液晶屏、辉光管驱动电路、按键、以及未来的扩展如温湿度传感器。其硬件SPI、I2C、PWM等外设性能稳定在MicroPython下有成熟的库支持。社区与生态围绕Pico和MicroPython的社区非常活跃遇到任何问题几乎都能找到相关的讨论和解决方案。针对ST7789液晶屏的驱动库也非常成熟。注意虽然ESP32功能更强大集成Wi-Fi/蓝牙但对于这个纯离线时钟项目其优势并不突出反而在MicroPython固件烧录和基础GPIO操作的简易性上略逊于Pico。因此Pico是更专注、更合适的选择。2.2 显示部分ST7789液晶屏与QS27-1辉光管液晶屏选择了1.54英寸或1.3英寸的ST7789驱动芯片的IPS液晶屏。这类屏幕分辨率常见为240x240或240x135色彩鲜艳视角广且通常采用SPI接口只需4-5根线即可驱动非常节省GPIO资源。在MicroPython中我们可以使用st7789.py驱动库例如来自Adafruit的版本或社区优化版来轻松控制。关键词adafruit st7789指的就是这个流行的驱动库。辉光管主角是QS27-1。这是国内最常见、存量最大的辉光管型号显示数字0-9。理解其引脚是关键。QS27-1是一种冷阴极辉光放电管内部有0-9十个数字形状的阴极和一个公共的网状阳极。当某个阴极与阳极之间的电压差达到起辉电压约170V时该数字周围的氖气就会电离发光呈现橙红色。它的引脚排列通常如下管脚朝下正视数字面阳极A通常是最粗的两个引脚或位于中间内部是连通的。阴极K0-K9对应数字0-9的引脚。需要查阅具体的数据手册或通过测试确定。一个常用的方法是将阳极通过一个数十K的限流电阻接至高压正极如180V然后用高压负极依次触碰其他引脚观察哪个数字亮起从而建立引脚与数字的映射关系。这就是为什么qs27-1辉光管引脚会成为搜索热词——它是实操的第一步。2.3 核心电路设计高压生成与安全驱动这是本项目与普通单片机项目最大的不同也是需要格外谨慎对待的部分。我们需要的是一套能安全、可控地产生并切换约170V直流电压的电路。高压生成方案为了安全和简便我推荐使用成熟的DC-DC升压模块。例如输入5V或12V输出可调至170V-200V的小功率模块。这种模块通常集成了振荡、升压和整流效率较高外围电路简单。你可以在电商平台搜索“高压升压模块 DC-DC”找到它们。绝对不要试图用面包板和分立元件自行搭建反激电路除非你有丰富的开关电源设计经验。辉光管驱动电路我们不能直接用单片机的GPIO3.3V去控制170V的通断。需要一套“中介”电路。最经典、可靠的方法是使用高压达林顿晶体管阵列ULN2003/ULN2803配合N沟道MOSFET。逻辑控制与隔离单片机GPIO输出信号3.3V首先送入ULN2003。ULN2003内部是达林顿晶体管开集电极输出可以看作一个受逻辑电平控制的“电子开关”但其输出端集电极可以承受最高50V的电压。这里我们并不用它直接开关高压而是用它来驱动后级的MOSFET。ULN2003起到了电流放大和初步隔离的作用。高压开关ULN2003的输出连接到一个N沟道MOSFET如IRF840耐压500V以上的栅极G。MOSFET的源极S接地漏极D连接辉光管的某个数字阴极。辉光管的阳极则连接到高压电源的正极170V。工作原理当单片机想让数字“5”亮起时对应的GPIO置高 → ULN2003对应通道输出低电平导通→ 这个低电平连接到MOSFET的栅极由于MOSFET是N沟道低电平无法使其导通MOSFET处于关闭状态。等等这里是不是反了是的这是一个需要特别注意的逻辑ULN2003是低电平有效输出导通。因此电路需要设计成负逻辑控制即单片机输出低电平时ULN2003导通将MOSFET栅极拉低相对于源极地MOSFET关闭阴极断开数字不亮。当单片机输出高电平时ULN2003截止其输出端被上拉电阻拉到高电平例如用一个大电阻连接到低压电源从而驱动MOSFET栅极为高MOSFET导通将阴极接地该阴极与阳极之间形成170V压差数字“5”辉光点亮。保护元件在辉光管阳极串联一个100K-220K/2W的限流电阻至关重要它用于限制辉光管的电流通常1-3mA防止过流损坏管子或电源。在每个MOSFET的栅极和源极之间并联一个10K电阻确保在ULN2003未连接时MOSFET处于关闭状态。这样我们通过单片机输出3.3V的逻辑信号经过两级放大和隔离最终安全地控制了170V高压的通断。一个辉光管有10个数字阴极因此需要10套这样的MOSFET驱动电路。我们可以用两片ULN2003每片7路和10个MOSFET来完成。3. 固件准备与MicroPython环境搭建硬件框架确定后就要让“大脑”运行起来。这里的关键是让树莓派Pico运行MicroPython并配置好必要的驱动库。3.1 给Pico烧录MicroPython固件这是最简单的一步。访问树莓派基金会官方提供的MicroPython固件下载页面找到针对树莓派Pico的.uf2文件并下载。按住Pico板上的BOOTSEL按钮不放将其通过USB线连接到电脑然后松开按钮。此时电脑会识别出一个名为RPI-RP2的可移动磁盘。将下载好的.uf2固件文件直接拖拽进去。Pico会自动重启之后它就变成了一个MicroPython设备。3.2 配置开发环境与上传代码你可以使用任何文本编辑器编写代码但我强烈推荐使用Thonny这款IDE。它专为MicroPython设计集成了代码编辑、串口REPL交互式命令行和文件管理功能。安装并打开Thonny。在右下角选择解释器选择“MicroPython (Raspberry Pi Pico)”。Thonny会自动连接到Pico。在Thonny的“视图”菜单中打开“文件”面板你会看到Pico的本地文件系统通常显示为/。我们需要将驱动库和主程序上传到Pico。首先需要获取st7789的驱动库。你可以从Adafruit的GitHub仓库下载或者使用一些社区维护的、针对Pico优化过的版本。将st7789.py和可能需要的fontx.py字体文件等通过Thonny的文件面板上传到Pico的根目录。3.3 基础代码结构规划在开始写具体的时钟逻辑前先规划好主程序main.py的结构。它将在Pico上电后自动运行。# main.py - 主程序框架 import machine import utime import st7789 from driver.htu21d import HTU21D # 示例可能的温湿度传感器驱动 from driver.nixie_driver import NixieDriver # 我们将编写的辉光管驱动类 # 1. 初始化硬件 # 1.1 初始化SPI和屏幕 spi machine.SPI(0, baudrate40000000, polarity1, phase0, sckmachine.Pin(2), mosimachine.Pin(3)) tft st7789.ST7789(spi, 240, 240, resetmachine.Pin(4), dcmachine.Pin(5), csmachine.Pin(1)) tft.init() # 1.2 初始化辉光管驱动 nixie NixieDriver() # 这个类需要我们自己实现 # 1.3 初始化按键用于调整时间 btn_set machine.Pin(6, machine.Pin.IN, machine.Pin.PULL_UP) btn_plus machine.Pin(7, machine.Pin.IN, machine.Pin.PULL_UP) btn_minus machine.Pin(8, machine.Pin.IN, machine.Pin.PULL_UP) # 2. 定义全局变量 current_time [12, 34, 56] # 时分秒 display_mode 0 # 0:正常时间1:设置小时2:设置分钟... nixie_enabled True # 辉光管总开关 # 3. 主循环 while True: # 3.1 获取/更新时间可以从RTC读取这里先模拟 # 3.2 处理按键事件 # 3.3 根据display_mode更新液晶屏显示 # 3.4 控制辉光管显示如果使能 if nixie_enabled: hour_tens current_time[0] // 10 hour_ones current_time[0] % 10 nixie.display_digit(0, hour_tens) # 假设0号管是十位 nixie.display_digit(1, hour_ones) # 假设1号管是个位 # 3.5 短暂延时控制刷新率 utime.sleep_ms(100)这个框架清晰地划分了初始化、变量定义和主循环。接下来我们需要填充最核心的两个部分液晶屏的图形界面绘制和辉光管的驱动类。4. 液晶屏界面绘制与MicroPython图形编程我们的液晶屏需要显示除了小时两位数字外的所有信息分钟、秒、日期、星期或许还有温湿度。我们需要用MicroPython代码来“画”出这个界面。4.1 使用ST7789驱动库进行绘图st7789库通常提供基础的画点、画线、画矩形、填充和显示文字的功能。我们的时钟界面可以这样设计布局设计将屏幕分为几个区域。顶部区域显示日期YYYY-MM-DD和星期。中部核心区域用超大字体显示分钟和秒格式为“MM : SS”。因为小时由辉光管显示这里分钟就是最核心的视觉焦点。底部区域显示温湿度信息或者一些装饰性元素、IP地址如果未来联网等。侧边或角落可以放置一些状态图标比如辉光管开关状态、闹钟标志等。字体与图形MicroPython下处理中文字体比较麻烦通常使用点阵字体文件.fon或自定义格式。对于英文数字我们可以使用内置的位图字体或者使用轻量级的fontx格式字体。为了获得最好的视觉效果尤其是巨大的分钟数字我建议预先将数字0-9以及冒号“:”制作成位图数组直接使用blit位图块传输函数来显示速度远快于绘制字符。例如你可以用PC上的工具如LCD Assistant将一张包含大号数字的图片转换成C语言数组格式然后稍加修改变成Python的bytes或bytearray。在st7789库中可能会有一个blit_buffer方法可以直接将这样的缓冲区数据推送到屏幕的指定区域。刷新策略为了降低CPU占用和避免屏幕闪烁应采用局部刷新策略。日期、星期、温湿度等变化慢的元素可以每秒或每10秒刷新一次。分钟和秒数字需要每秒刷新。但刷新时不要重绘整个背景只更新数字变化的区域。例如判断秒的个位数字是否变化如果变化只重绘秒个位所在的矩形区域。实现一个draw_digit函数接收画布位置、数字值用位图方式绘制该数字。代码示例绘制一个数字位# 假设我们有一个包含数字0-9位图数据的字典 # 每个数字的位图数据是一个字节数组对应一块固定大小如40x60像素的彩色区域 digit_bitmaps { 0: bytearray(...), # 这里替换为实际的RGB565数据 1: bytearray(...), # ... 2-9 } def draw_digit(tft, x, y, digit): 在屏幕(x,y)位置绘制单个数字 if 0 digit 9: bitmap_data digit_bitmaps[digit] # 使用blit_buffer快速绘制。注意参数数据缓冲区x, y, 宽度, 高度 tft.blit_buffer(bitmap_data, x, y, DIGIT_WIDTH, DIGIT_HEIGHT)通过这种方式我们可以高效地构建出美观且流畅的时钟界面。关键在于前期准备好高质量的字体位图资源。5. 辉光管驱动类的实现与安全控制这是项目的硬件核心我们需要编写一个NixieDriver类来抽象化底层复杂的GPIO和高压控制让主循环可以简单地调用display_digit(tube_index, number)。5.1 引脚映射与初始化首先我们需要定义每个辉光管本项目是两个的每个数字阴极对应的控制引脚。假设我们使用两片ULN2003A和B每片控制一个辉光管。# nixie_driver.py import machine import utime class NixieDriver: def __init__(self): # 定义两个辉光管每个管10个数字0-9对应的控制引脚 # 这些引脚连接ULN2003的输入ULN2003输出控制MOSFET # 注意根据电路设计这里是负逻辑。初始化时所有引脚置高辉光管熄灭。 self.tube_pins [ [ # 管0小时十位 machine.Pin(10, machine.Pin.OUT, value1), # 数字0 machine.Pin(11, machine.Pin.OUT, value1), # 数字1 # ... 数字2-8 machine.Pin(19, machine.Pin.OUT, value1), # 数字9 ], [ # 管1小时个位 machine.Pin(20, machine.Pin.OUT, value1), # 数字0 # ... 数字1-9 machine.Pin(29, machine.Pin.OUT, value1), # 数字9 ] ] # 辉光管阳极高压开关控制引脚可选 # 我们可以用一个MOSFET控制所有辉光管阳极的供电实现整体开关/调光PWM self.hv_enable_pin machine.Pin(30, machine.Pin.OUT, value0) # 默认关闭高压 # 如果使用PWM调光 self.hv_pwm machine.PWM(self.hv_enable_pin) self.hv_pwm.freq(1000) # 1kHz PWM频率 self.hv_pwm.duty_u16(0) # 初始占空比0关闭 self.current_digits [None, None] # 记录两个管当前显示的数字用于防止频繁开关 def enable_high_voltage(self, brightness100): 开启高压电源。brightness: 0-100 通过PWM控制阳极电压实现调光 if brightness 0: self.hv_pwm.duty_u16(0) elif brightness 100: self.hv_enable_pin.value(1) # 全开时直接置高避免PWM损耗 else: # 将亮度百分比转换为16位duty值 (0-65535) duty int(brightness / 100.0 * 65535) self.hv_pwm.duty_u16(duty) utime.sleep_ms(5) # 等待高压稳定 def disable_high_voltage(self): 关闭高压电源 self.hv_pwm.duty_u16(0) self.hv_enable_pin.value(0) # 熄灭所有数字 for tube in self.tube_pins: for pin in tube: pin.value(1) # 负逻辑置高熄灭 self.current_digits [None, None]5.2 数字显示与消隐控制直接切换辉光管显示的数字如果旧数字阴极断开和新数字阴极接通之间没有延时可能会导致两个数字间发生“串扰”或“竞争”甚至因同时导通两个阴极而缩短管子寿命。因此必须引入“消隐”时间。def display_digit(self, tube_index, digit): 让指定辉光管显示指定数字。 tube_index: 0 或 1 digit: 0-9 if tube_index not in [0, 1] or digit not in range(10): return # 参数错误 # 如果当前已经是这个数字则无需操作防止频繁开关 if self.current_digits[tube_index] digit: return tube self.tube_pins[tube_index] # 步骤1消隐 - 先熄灭当前显示的数字如果是负逻辑需要将当前引脚置高 if self.current_digits[tube_index] is not None: old_pin tube[self.current_digits[tube_index]] old_pin.value(1) # 负逻辑置高熄灭 utime.sleep_us(200) # 关键的消隐延时100-500微秒确保旧数字完全熄灭 # 步骤2点亮新数字 new_pin tube[digit] new_pin.value(0) # 负逻辑置低点亮 self.current_digits[tube_index] digit def set_brightness(self, brightness): 全局调光 self.enable_high_voltage(brightness)在主循环中我们可以这样调用# 每小时的第0分钟第0秒时点亮辉光管30秒 if current_time[1] 0 and current_time[2] 0: if not nixie_enabled: nixie.enable_high_voltage(80) # 80%亮度 nixie_enabled True elif current_time[2] 30: # 30秒后关闭 if nixie_enabled: nixie.disable_high_voltage() nixie_enabled False这种控制方式既保护了辉光管又节约了能源还让辉光管的点亮成为一种有仪式感的事件。6. 时间管理与功能集成一个完整的时钟需要可靠的时间源和设置功能。6.1 使用RP2040的RTC树莓派Pico的RP2040芯片内置了一个简单的实时时钟RTC但它需要在VbusUSB电源或VBAT引脚上有备用电源才能在主电源断开时保持运行。对于桌面时钟通常一直供电所以可以直接使用。# 在初始化部分 rtc machine.RTC() # 设置初始时间通常只在第一次或需要校准时 # rtc.datetime((2024, 5, 27, 1, 12, 34, 56, 0)) # (年,月,日,星期,时,分,秒,亚秒) # 星期0-6对应周一至周日 # 在主循环中获取时间 def get_rtc_time(): year, month, day, weekday, hour, minute, second, _ rtc.datetime() # RTC返回的hour是24小时制 return hour, minute, second, year, month, day, weekday6.2 按键调整与状态机我们需要至少三个按键SET模式切换、PLUS加、MINUS减。通过一个状态机display_mode变量来管理不同的设置模式。# 在主循环的按键处理部分 def handle_buttons(): global display_mode, current_time, nixie_enabled if btn_set.value() 0: # 按键按下假设低电平有效 utime.sleep_ms(50) # 消抖 if btn_set.value() 0: display_mode (display_mode 1) % 4 # 循环切换模式0正常1设时2设分3设日... while btn_set.value() 0: # 等待按键释放 utime.sleep_ms(10) utime.sleep_ms(200) # 模式切换后稍作延时 if display_mode 1: # 设置小时 if btn_plus.value() 0: current_time[0] (current_time[0] 1) % 24 utime.sleep_ms(200) # 长按加速的逻辑可以在这里添加 if btn_minus.value() 0: current_time[0] (current_time[0] - 1) % 24 utime.sleep_ms(200) # 退出设置模式后将current_time写入RTC # if display_mode changes back to 0, call rtc.datetime(...)6.3 最终整合与优化将时间获取、界面绘制、辉光管控制、按键处理整合到主循环中。注意优化延时确保界面响应流畅。可以使用utime.ticks_ms()来管理定时任务例如每秒刷新一次时间显示每100毫秒扫描一次按键而不是使用固定的sleep。一个重要的优化点是辉光管的寿命。辉光管长期显示同一数字会导致该数字阴极的涂层损耗更快最终亮度降低或失效。为了解决这个问题可以编写一个简单的“数字轮换”或“辉光管保护”程序。例如在整点之外的某些时间如下午茶时间或者随机间隔让两个辉光管轮流显示所有数字几秒钟就像一次短暂的“热身运动”这能有效平衡所有阴极的损耗。7. 组装、调试与艺术化呈现当所有代码模块测试通过后就可以进行最终的硬件组装了。焊接与布局建议使用洞洞板或定制PCB进行焊接。将高压升压模块、ULN2003、MOSFET、限流电阻等元件合理安排。务必确保高压部分100V与低压部分3.3V/5V之间有足够的物理间隔至少5mm避免爬电或意外短路。高压走线要粗而短并做好绝缘。分步上电调试首先只连接单片机、液晶屏和按键不接高压部分。测试液晶屏显示、按键功能、RTC时间读取是否正常。然后断开单片机与驱动电路的连接单独测试高压模块。用万用表测量输出电压调整到约170-180V。关键安全步骤连接驱动电路但先不接辉光管。用万用表测量每个MOSFET的漏极连接阴极的引脚对地电压。当单片机控制某个数字点亮时该引脚电压应接近0VMOSFET导通控制熄灭时该引脚应为高阻态电压不确定但不应是稳定的高压。切勿在测试时用手触碰任何高压部分最后连接辉光管。先接通高压然后通过程序控制点亮单个数字观察是否正常发光。测试所有数字位。外壳与美学时钟的灵魂一半在电路一半在外壳。你可以使用3D打印一个外壳将液晶屏嵌入前面板并为两个辉光管开出精致的圆窗。内部做好绝缘和固定。可以考虑在辉光管后面增加一块深色亚克力或玻璃作为背景板增强其深邃感和对比度。电源部分可以使用一个5V 2A的USB电源适配器为整个系统供电。这个“2位辉光管液晶时钟”项目从构思到实现贯穿了硬件设计、嵌入式编程和人机交互的思考。它不仅仅是一个计时工具更是对过去技术美学的一次致敬和现代数字工艺的一次实践。当你看到液晶屏上清晰跳动的数字与旁边偶尔温柔点亮的橙色辉光交相辉映时那种跨越时代的融合感正是DIY项目最迷人的回报。