7元成本打造旋转LED显示:ATtiny13视觉暂留项目全解析

7元成本打造旋转LED显示:ATtiny13视觉暂留项目全解析 1. 项目缘起从“7块钱能干啥”到桌面上的流光溢彩前几天在工作室整理零件盒翻出来几颗尘封已久的ATtiny13单片机。这玩意儿现在便宜得离谱一颗也就一两块钱。当时就在想现在7块钱连杯像样的奶茶都买不到但用这点成本能不能做出点让人眼前一亮的东西这个念头一冒出来就收不住了。正好手边还有一堆用剩的LED和一块废旧的电机一个点子瞬间成型做一个基于视觉暂留原理的旋转式显示器也就是常说的PoV显示器。PoV全称Persistence of Vision视觉暂留。这原理其实我们从小就知道快速挥动一根发光的荧光棒你看到的就是一条光带而不是一个光点。利用这个原理让一排LED高速旋转起来通过精确控制每个LED在特定位置的点亮与熄灭就能在空中“画”出字符、图案甚至动画。这听起来很酷但网上很多教程要么用的单片机比较贵要么电路复杂。我的目标很明确极致成本控制就用最便宜、资源最少的ATtiny13加上最基础的元件把这事儿给办了。最终做出来的效果远超预期。当电机转起来蓝色的LED光迹在空中稳定地显示出“HELLO”和一颗爱心时那种成就感绝不是花7块钱买杯饮料能比的。这不仅仅是一个电子制作更是一次对“极简设计”和“资源压榨”的实践。下面我就把这套从硬件选型、电路设计、软件编程到调试校准的完整过程毫无保留地分享出来。你会发现单片机编程和硬件DIY的门槛远没有想象中那么高。2. 核心器件选型与成本拆解如何把每一分钱花在刀刃上做低成本项目选型是第一步也是最关键的一步。每一毛钱都要精打细算同时还要保证功能实现。这是我的最终物料清单和选型思考主控芯片ATtiny13A成本约1.5元/片。为什么是它这是整个项目的灵魂。ATTiny13是Atmel现Microchip8位AVR单片机家族中资源最少、体积最小、价格最低的成员之一。它只有1KB的Flash用于存程序、64字节的SRAM、5个I/O口。用这样一个“玩具级”的MCU来做动态显示本身就是最大的挑战和乐趣。市面上更常见的ATtiny85约5元或Arduino Nano约15元当然更容易但就失去了“7块钱”的极致意义。选择ATtiny13就是选择在极限约束下跳舞。显示单元5mm 高亮蓝色LED成本约0.1元/个8个共0.8元。为什么是蓝色为什么是5mm蓝色LED的波长较短在视觉暂留显示中相比红色或绿色其光迹在暗环境下显得更清晰、更“有科技感”。5mm直径是最通用的规格亮度足够也方便焊接。数量定为8个是权衡的结果太少则显示字符高度不足太多则ATtiny13的I/O口和驱动能力都不够我们还需要一个I/O口用于同步信号。驱动与旋转部分N20微型减速电机 L9110S电机驱动模块成本电机约2.5元驱动模块约0.8元。电机的选择N20电机体积小、扭矩合适、转速稳定我选用的是3-6V供电每分钟约200-300转的型号。转速是关键太慢则画面闪烁太快则对程序时序要求极高且可能甩散架。200-300转是一个经过验证的、能产生稳定视觉暂留效果的区间。为什么需要驱动模块ATtiny13的I/O口输出电流很小通常20mA左右无法直接驱动电机。L9110S是一个双路H桥驱动芯片可以用单片机的小信号控制电机的启停和方向这里我们只用到它的一路。它价格低廉接线简单是性价比之选。同步与供电槽型光电开关 杂散元件成本光电开关约0.5元电阻、排针、洞洞板、电池盒等约1元。同步的重要性PoV显示要稳定必须知道每一圈旋转的起始点在哪里。槽型光电开关也叫光遮断器配合固定在旋转轴上的一个小挡片可以在每圈转到特定位置时产生一个脉冲信号。这个信号就是整个显示系统的“心跳”所有LED的点亮时序都以此为零点进行推算。供电采用4节5号电池盒约6V为整个系统供电同时通过一个AMS1117-5.0稳压芯片约0.3元为ATtiny13提供稳定的5V工作电压。电机则直接使用电池的6V驱动动力更足。总成本核算 ATtiny13(1.5) LEDx8(0.8) 电机(2.5) 驱动(0.8) 光电开关(0.5) 稳压/电阻/板子等(1.0) 7.1元。 看完美贴合“7块钱”的主题即使你手头什么都没有全部网购算上运费可能也就10元左右。这个清单体现了电子DIY的一个核心思路利用最通用、最廉价的元件通过设计和编程实现有趣的功能。3. 电路设计与焊接要点在洞洞板上搭建旋转的艺术电路原理并不复杂但因为是旋转装置可靠性是第一位的。所有连接必须牢固重量分布要尽量均衡。3.1 原理图设计思路整个系统可以分为三个部分主控与显示板旋转部分、固定底座供电与同步部分。旋转部分核心ATtiny13VCC接5VGND接地。我们使用其PB0-PB4五个I/O口来驱动5个LED实际焊接了8个LED但采用共阳或共阴接法后还是只需要5个控制口。具体驱动方式后面详述。LED阵列为了用最少的I/O控制最多的LED我采用了“共阳”接法。将所有8个LED的阳极长脚连接在一起接到VCC5V上。每个LED的阴极短脚通过一个限流电阻后分别接到ATtiny13的5个I/O口上。注意这里不是1对1控制。通过软件扫描和LED在物理位置上的排列5个I/O口利用人眼视觉暂留可以控制8个LED形成一列像素。重量平衡将ATtiny13、电阻等较轻的元件布置在洞洞板中心附近而8个LED则沿着板子的一条长边均匀排列。焊接时尽量保证对称避免旋转时因重心不稳导致剧烈震动。固定底座部分电机与驱动电池正负极接入L9110S模块的VCC和GND。ATtiny13的一个I/O口如PB5连接到L9110S的输入控制端IA或IB通过输出高/低电平来控制电机的转/停。电机的两根线接在L9110S的输出端。同步传感器槽型光电开关的VCC和GND接入5V和地。其输出信号线OUT通常是一个集电极开路输出需要接一个上拉电阻如10KΩ到5V然后连接到ATtiny13的另一个I/O口如PB2。当挡片穿过槽时输出会从高电平变为低电平产生一个下降沿脉冲。供电电池的6V先经过AMS1117-5.0稳压芯片降到5V再给ATtiny13和光电开关供电。电机驱动模块直接接6V电池以确保转速。3.2 焊接与组装实战经验注意旋转部分的所有焊点都必须饱满、牢固。可以用手轻轻拨动元件确认没有虚焊。松动的元件在高速旋转下很可能被甩飞。先静后动先在洞洞板上焊接好旋转部分的所有电路包括单片机、LED、电阻。先不要接电机。用USBasp等编程器给ATtiny13烧录一个简单的测试程序比如让LED轮流点亮用外部5V电源供电测试所有LED是否受控。这一步确认了核心功能正常。处理引线电机需要两根线供电旋转板上的VCC和GND也需要从底座引上来。这里有个关键技巧不要使用普通的杜邦线它们太硬且接头不牢。应该使用细软的硅胶线并且焊接在洞洞板和电机接线端子上。对于旋转部分与固定部分之间的电源连接最理想的方法是使用滑环但为了成本我们采用更直接但也更考验手艺的方法将电机固定在底座上电机的转轴直接与旋转洞洞板机械固定可以用胶粘或螺丝夹紧这样电机的两根线自然就成了旋转部分的供电通道。而同步信号线则不需要旋转连接因为光电开关是固定在底座上的。同步挡片的安装在电机的转轴或与转轴固定在一起的旋转板背面用热熔胶或胶水粘上一小片不透明的材料如剪裁的塑料片、黑色电工胶带。调整其位置确保它旋转时能准确地穿过固定在底座的槽型光电开关的凹槽。挡片不需要很大宽度能遮住光槽即可长度1-2厘米足够。配平调试这是保证显示不抖动的关键。将组装好的旋转部分板子LED轻轻放在指尖上找到它自然平衡的点。在较轻的一侧点一些热熔胶或粘上一小段焊锡丝作为配重直到它能大致保持水平。这一步做得越仔细最终旋转起来就越平稳显示效果越清晰。4. 软件驱动解析在1KB内存中编排光影之舞这是整个项目最精华的部分。ATtiny13只有1KB的Flash和64字节的RAM这意味着代码必须极度精简没有操作系统没有库函数一切都要直接操作寄存器。4.1 开发环境与熔丝位配置我使用的是PlatformIO IDE AVR平台也可以使用Arduino IDE配合arduino-tiny核心或者最原始的Atmel Studio。对于ATtiny13熔丝位配置至关重要配置错误会导致芯片无法编程或工作异常。时钟源为了省掉外部晶振的成本和空间我们使用芯片内部的9.6MHz RC振荡器。在熔丝位中需要将CKDIV8编程即设置为0这样系统时钟就是9.6MHz而不是默认的1.2MHz。更高的主频意味着我们能实现更精确的时序控制。其他熔丝SUT_CKSEL选择为0x69代表使用9.6MHz内部RC启动时间64ms。BODLEVEL掉电检测可以根据需要禁用以省电但建议在调试阶段开启一个级别如2.7V防止电压不稳导致程序跑飞。编程接口使用USBasp编程器连接ATtiny13的RESET、MOSI、MISO、SCK、VCC、GND进行烧录。4.2 核心算法与代码实现程序的核心逻辑是一个状态机它不断等待同步信号然后根据当前旋转角度查表输出对应的LED列数据。第一步定义显示数据我们要显示“HELLO”和一个爱心图案。假设我们的LED阵列在物理上是一列8个LED垂直方向。当它旋转时我们在不同角度点亮不同的LED组合就能“画”出水平方向的一排像素点。 我们可以把整个360度圆周划分为若干等份比如120份每份3度。那么每个字符或图案就由一系列比如8行 x 10列的二维点阵数据构成。但由于内存极其有限我们不能存储完整的位图。一个巧妙的办法是只存储每一列对应旋转的某个角度位置需要点亮的LED的二进制模式。例如对于字符“H”的第一列可能只有顶部和底部的LED亮中间不亮。那么这一列的数据可以用一个8位的字节来表示比如0b10000001假设最高位对应最上面的LED。我们把要显示的所有字符的每一列数据按顺序放在一个大的常量数组里。// 示例定义“HELLO”和爱心的列数据每个字符假设宽5列列之间空1列 const uint8_t pov_pattern[] PROGMEM { // H (5列) 0b10000001, 0b10000001, 0b11111111, 0b10000001, 0b10000001, 0b00000000, // 空列 // E (5列) 0b11111111, 0b10001001, 0b10001001, 0b10001001, 0b10000001, 0b00000000, // ... 后续L, L, O, 爱心图案的数据 };这里使用了PROGMEM关键字将数据存放在Flash中而不是宝贵的SRAM里。第二步同步信号中断处理将连接光电开关的I/O口如PB2配置为外部中断输入并设置为下降沿触发。当挡片穿过产生下降沿脉冲时触发中断服务程序。ISR(INT0_vect) { // ATtiny13的PB2对应INT0中断 sync_detected 1; // 设置一个同步标志位 column_index 0; // 将列索引重置为0表示新的一圈开始 }这个中断程序要尽可能短只做标记不做复杂操作。第三步主循环与显示扫描主程序的核心是一个精确的延时循环它决定了每一列数据显示的时间宽度这个宽度必须与电机的转速严格匹配。void loop() { if (sync_detected) { sync_detected 0; // 同步后开始显示一整圈的数据 for (uint8_t i 0; i TOTAL_COLUMNS; i) { // 从Flash中读取当前列的数据 uint8_t led_data pgm_read_byte(pov_pattern[column_index]); // 将数据输出到控制LED的I/O口可能需要一个简单的解码驱动因为5个I/O控制8个LED output_leds(led_data); // 精确延时等待旋转到下一个“列”的位置 delay_us(COLUMN_DELAY_US); // 这个延时值需要根据实际转速校准 column_index; if (column_index TOTAL_COLUMNS) { column_index 0; // 一圈显示完毕但通常等下一个同步信号重置更可靠 } } // 一圈显示完后关闭所有LED等待下一个同步信号 output_leds(0x00); } // 其他时间可以进入低功耗休眠模式以省电 }关键难点与技巧时序校准COLUMN_DELAY_US是核心参数。它 (每圈时间) / (总列数)。例如电机300转/分即每秒5圈每圈时间200ms。如果总列数是120那么每列显示时间就是200ms / 120 ≈ 1.666ms。你需要用示波器或者通过反复试验调整这个延时直到字符看起来稳定、不拉伸也不压缩。更高级的做法是用定时器中断来产生这个精确的延时而不是用_delay_us()函数。I/O扩展驱动前文提到用5个I/O控制8个LED。这可以通过一个74HC595移位寄存器来实现但为了成本我们也可以用软件扫描。例如将8个LED分为两组每组4个共用控制线利用人眼视觉暂留在极短时间内轮流点亮两组。但这要求MCU速度足够快。对于ATtiny13 9.6MHz在1.6ms内完成两次扫描输出是可行的。代码会复杂一些但能省下一个芯片的钱。抗干扰处理电机是巨大的噪声源。必须在ATtiny13的VCC和GND之间紧贴芯片放置一个0.1uF的瓷片电容进行去耦。光电开关的信号线也可以加入一个小的滤波电容如100pF到地并在软件中对中断信号做简单的防抖处理如连续检测到几次低电平才认为有效。5. 系统调试与效果优化让显示清晰稳定的秘诀硬件焊接好程序烧录进去只是完成了第一步。要让PoV显示器清晰稳定地工作调试环节必不可少。5.1 上电调试流程静态测试不接电机只给旋转板和底座供电。用手遮挡光电开关观察是否有LED闪烁响应可以在同步中断服务程序里加一个LED翻转的测试代码。同时测试电机驱动控制I/O口高低电平变化听电机是否有启停的声音。低速旋转测试将电机供电电压降低比如用3V让电机慢速旋转。此时观察LED是否常亮或规律闪烁。如果所有LED常亮可能是电源线在旋转中短路如果毫无规律可能是同步信号没抓到或程序时序完全不对。同步信号捕捉这是调试的关键。在同步中断服务程序里让一个特定的LED比如最边上的一个亮一下。当电机旋转时你应该看到一个固定的位置有一个光点。如果这个光点位置飘忽不定说明同步信号不稳定需要检查挡片是否安装牢固光电开关距离是否合适通常1-3mm为宜以及软件防抖是否有效。显示初现当同步稳定后加载最简单的显示数据比如只显示一条竖线或一个点调整COLUMN_DELAY_US参数。你会看到光点变成了一条短线段。继续调整直到线段长度合适并且位置固定。全速运行与微调将电压升到额定6V让电机全速运行。此时观察完整的字符显示。常见问题及解决字符断开、不连贯延时太短每列显示时间不足导致视觉暂留衔接不上。增大COLUMN_DELAY_US。字符重叠、模糊延时太长上一列的光迹还没消失下一列就显示了。减小COLUMN_DELAY_US。字符抖动、上下跳动旋转部分动平衡没做好。重新进行配平。也可能是电机轴有轻微偏心尝试调整电机固定位置或更换电机。特定位置字符扭曲同步信号的位置与LED物理位置的角度偏移没算对。在程序中加入一个角度偏移量参数在同步发生后先延时一小段时间再开始显示第一列数据通过调整这个偏移量来左右移动整个画面。5.2 效果优化进阶亮度与均匀性由于LED是扫描点亮占空比可能不足50%导致亮度下降。可以尝试在允许的电流范围内减小LED的限流电阻值如从220Ω降到100Ω但要注意不要超过ATtiny13 I/O口和LED的最大电流。也可以采用PWM调制在点亮的那段时间里让LED以更高频的PWM全亮提升视觉亮度。显示内容更新ATtiny13没有串口如何更新显示内容一个实用的方法是利用其片内EEPROM64字节。我们可以预先编写几个不同的显示图案通过一个额外的按钮来切换。按钮按下一次就改变一个存储在EEPROM中的“当前图案索引”主程序根据这个索引从不同的数据数组中读取内容。这样就能实现多组显示内容的切换。功耗控制整个系统耗电大户是电机和LED。可以在程序中加入逻辑当检测到一段时间没有同步信号电机停转时让单片机进入深度休眠模式关闭LED输出极大降低静态功耗。用电池供电时这个技巧能显著延长续航。做完所有这些步骤当你接通电源电机平稳旋转清晰稳定的光绘图案悬浮在空中时你会觉得所有折腾都是值得的。这个成本不到10元的小项目融合了模拟电路、数字电路、单片机编程、机械装配和调试技巧是一个非常好的综合性入门实践。它带给你的不仅仅是桌面上一件酷炫的装饰品更是一种“用最少资源创造价值”的工程师思维和动手解决问题的快感。