1. 项目缘起从“五灯谜题”到动手实践最近在整理旧物时翻出了一个多年前用面包板搭的“五灯谜题”电路。这是一个非常经典的电子入门项目核心玩法是五个LED灯随机点亮玩家需要快速按下按钮让所有灯同时熄灭。听起来简单但实际玩起来对反应速度和节奏感是个不小的挑战。当年用一堆杜邦线和ATtiny13A单片机随手搭的虽然能玩但外观实在不敢恭维而且面包板上的元件摇摇晃晃体验感大打折扣。这次我决定彻底重制它目标是把一个“玩具级”的验证品升级成一个可以拿在手里、随时把玩的精致小物件。整个过程涉及了从固件编程、PCB设计到最终焊接调试的全链路。我选择了ATtiny13A这颗经典的8位AVR单片机作为核心用Arduino IDE搭配MicroCore来编写程序并最终设计了一块小巧的PCB。这不仅仅是复现一个游戏更是一次对微型单片机项目从软件到硬件完整流程的实践。无论你是刚接触Arduino和PCB设计的新手还是想重温AVR单片机编程的老玩家这个项目都能让你在动手的过程中巩固关于IO控制、中断、PCB布局和焊接的诸多细节。2. 核心器件选型与开发环境搭建为什么是ATtiny13A在如今ESP32、STM32大行其道的时代选择这颗仅有1KB Flash、64字节SRAM的“古董”芯片似乎有点反潮流。但正是它的极致精简让这个项目充满了挑战和教学意义。它没有复杂的库函数依赖每一个IO口的操作、每一个定时器的配置都需要你清晰地理解底层寄存器。对于“五灯谜题”这种逻辑简单、对资源消耗极低的应用ATtiny13A绰绰有余且成本极低、体积小巧非常适合集成到最终的小型PCB中。要让Arduino IDE支持ATtiny13A我们需要安装一个第三方硬件支持包最常用的是MicroCore。这是一个非常优秀的项目它为ATtiny系列单片机包括13A提供了近乎完美的Arduino兼容层。安装步骤如下打开Arduino IDE进入“文件”-“首选项”。在“附加开发板管理器网址”中添加MicroCore的地址https://mcudude.github.io/MicroCore/package_MicroCore_index.json。如果你之前添加过其他地址用逗号分隔即可。打开“工具”-“开发板”-“开发板管理器”在搜索框中输入“MicroCore”。找到并安装最新版本的MicroCore。安装完成后你就可以在“工具”-“开发板”列表中看到“ATtiny13/13A”的选项了。接下来是关键的配置环节在“工具”菜单下进行Clock时钟ATtiny13A内部有校准的9.6MHz RC振荡器。对于本项目选择“9.6 MHz internal oscillator”即可无需外部晶振简化电路。BOD掉电检测可以禁用BOD Disabled以降低功耗。但如果你的供电电压可能不稳比如使用旧电池建议开启一个较低的阈值如“BOD 2.7V”防止电压过低时程序跑飞。EEPROM保持默认的“EEPROM retained”即可。LTO链接时优化强烈建议开启Enabled。LTO可以显著优化生成的代码大小对于Flash空间只有1KB的ATtiny13A来说这是必须开启的选项往往能为你节省出几十甚至上百字节的宝贵空间。millis()/micros()由于资源有限MicroCore默认不提供millis()功能。本项目用不到保持禁用状态。环境搭好了还需要一个编程器。ATtiny13A不支持串口引导程序Bootloader必须使用ISP在线系统编程方式烧录。最经济实惠的选择是USBasp网上很容易买到。将USBasp的MOSI、MISO、SCK、RST、VCC、GND引脚分别连接到ATtiny13A对应的引脚上具体引脚定义见下文原理图部分。在Arduino IDE的“工具”-“编程器”中选择“USBasp”然后就可以通过“项目”-“上传”或者“项目”-“使用编程器上传”来烧写程序了。注意首次使用USBasp等第三方编程器时Arduino IDE可能会提示“找不到编程器”。此时可能需要手动安装USBasp的驱动程序通常为libusb-win32或Zadig具体步骤因操作系统而异请根据你购买的编程器型号查找相关教程。3. “五灯谜题”的电路设计与原理图绘制电路是整个项目的骨架。我们需要为ATtiny13A设计一个最小系统并连接五个LED、一个按钮以及电源。为了追求最终产品的完整性我直接跳过了在面包板上反复调试的步骤因为逻辑相对简单而是在立创EDALCEDA中从零开始绘制原理图并设计PCB。这样做的好处是一旦设计完成可以直接发送给工厂打样获得专业且稳定的电路板。核心原理分析ATtiny13A有8个引脚其中6个可以作为IO口PB0-PB5。我们的资源规划如下PB0, PB1, PB2, PB3, PB4这5个引脚用于驱动5个LED灯。采用共阳极接法即所有LED的正极阳极通过一个限流电阻共同连接到VCC电源正极。LED的负极阴极分别连接到单片机的这5个IO口。当IO口输出低电平0时LED两端形成电压差点亮输出高电平1时LED熄灭。这种接法的好处是ATtiny13A的IO口在输出低电平时吸收电流Sink Current其驱动能力通常强于输出高电平时的提供电流Source Current能力让LED更稳定地发光。PB5 (RESET/ADC0/dW)这个引脚复用了复位功能。为了保留ISP编程能力绝不能将其用作普通IO口驱动LED或读取按钮我们需要将其保持为上拉状态仅作为复位引脚使用。因此按钮和额外的LED都不能用这个引脚。按钮输入由于PB0-PB4已经全部用于LED我们没有多余的独立IO口了。这里需要一个巧妙的设计将按钮连接到VCC和GND之间并利用ADC模数转换功能来检测。具体来说我将按钮的一端接GND另一端通过一个10kΩ的上拉电阻接VCC同时这个连接点还接到PB2它同时也是ADC1通道引脚。在软件中我将PB2配置为ADC输入。当按钮未按下时PB2通过上拉电阻接到VCCADC读到高电平接近VCC电压当按钮按下时PB2直接连接到GNDADC读到低电平0V。这样我们就用ADC“模拟”了一个数字输入引脚解决了IO口不足的问题。根据以上分析绘制出的核心原理图模块包括单片机最小系统ATtiny13A、VCC与GND之间的0.1uF去耦电容尽可能靠近芯片电源引脚放置。LED驱动电路5个LED如0805封装的红色LED每个LED的阴极串联一个220Ω的限流电阻后分别接到PB0-PB4。所有LED的阳极并联后接VCC。电阻值可以根据公式R (VCC - Vf) / If计算其中Vf是LED正向压降红/绿LED约1.8-2.2VIf是期望的工作电流通常3-10mA。假设VCC5VVf2VIf10mA则R (5-2)/0.01 300Ω选用220Ω或330Ω都是常见选择。按钮检测电路一个常开按键一端接GND另一端接10kΩ上拉电阻至VCC上拉节点连接至PB2引脚。ISP编程接口一个2x3PIN的排母用于连接USBasp编程器引脚顺序需与编程器线序匹配通常为MOSI、VCC、SCK、MISO、RST、GND。电源接口一个简单的2PIN接线端子或Micro USB口用于接入5V电源。我选择了一个TP4056充电模块加上一块小锂电池的方案实现可充电功能但这会增加复杂度。对于初版直接使用一个5V USB电源供电最为简单可靠。在立创EDA中绘制这些原理图时要养成良好的习惯为每一个网络Net取一个清晰的名字如LED1、BUTTON、VCC、GND等这会在后续PCB布局和检查时带来巨大便利。4. 从原理图到PCB的布局与布线实战原理图完成后点击“设计”-“转换原理图到PCB”就进入了真正的挑战环节——PCB布局布线。一块好的PCB不仅是电气连接的实现更是稳定性、可制造性和美观度的体现。4.1 元件布局的哲学布局优先于布线。我的核心思路是功能模块化将LED和其对应的限流电阻视为一个小组尽量靠近放置。5个LED的排列要考虑到最终外壳的显示效果我将其排成一条直线或一个弧形。信号流导向遵循“输入-处理-输出”的流向。按钮电路靠近板边便于操作单片机位于中心LED驱动电路靠近单片机输出引脚。电源路径最短去耦电容0.1uF必须紧贴ATtiny13A的VCC和GND引脚这是抑制电源噪声的第一道防线。整个板子的电源走线要尽量粗壮我使用了20mil宽度形成低阻抗的供电网络。编程接口位置固定ISP接口通常放在板子边缘方便插拔且不要被其他较高的元件如电池遮挡。在立创EDA中你可以拖动元件实时看到飞线表示电气连接的细线的变化。布局的目标就是让这些飞线交叉尽可能少路径尽可能短且直接。4.2 布线规则与技巧布局满意后开始布线。我使用了立创EDA的标准版免费其功能对于此类简单双面板完全足够。线宽设置电源线VCC、GND我用了20-30mil约0.5-0.76mm信号线如PB0-PB5、ADC、ISP信号用了10-12mil约0.25-0.3mm。更宽的电源线意味着更小的电阻和压降也能承载更大电流。过孔的使用双面板布线过孔是连接顶层和底层的关键。对于信号线过孔尺寸可以小一些如外径24mil内径12mil。对于电源线尤其是GND过孔可以多打几个形成良好的接地通路。切忌滥用过孔过多的过孔会增加制板成本和潜在故障点。接地平面GND Plane这是提升电路稳定性的“神器”。我几乎将整个PCB的底层都铺设为GND铜区。在立创EDA中使用“铺铜”工具选择底层网络为GND然后框选整个板子。铺铜后所有连接到GND网络的焊盘会自动与铜区连接。一个完整的地平面提供了稳定的参考地电位并能有效屏蔽噪声。布线优先级先布电源线和关键信号线如复位线RST再布其他普通IO线。尽量走45度角或圆弧拐角避免90度直角后者在高频下可能产生辐射干扰虽然本项目频率不高但养成好习惯。DRC设计规则检查布线完成后务必运行DRC立创EDA的DRC可以检查线宽、间距、短路、开路等错误。我设置的最小线宽为8mil最小间距为6mil这是大多数PCB打样厂商的“工艺边距”确保设计能被可靠生产。如果DRC报错比如“不在DRC规则范围内”必须逐一修改直到通过。4.3 一些容易踩的坑元件封装错误原理图中的符号和PCB中的封装Footprint必须对应。比如你选了0805的电阻PCB库里就必须是0805的焊盘尺寸。从立创EDA的元件库调用标准封装是最稳妥的。丝印层混乱丝印层Top/Bottom Silkscreen用于放置元件标号如R1 C1和轮廓。确保丝印清晰、不重叠、不被焊盘覆盖。我将元件标号放在元件旁边显眼位置并在板子空白处添加了项目名称“Five LEDs Puzzle”和版本号。孔环太小过孔或插件孔的铜环钻孔周围的铜皮不能太小否则容易在钻孔时被钻掉导致断路。一般要保证环宽至少3-4mil。忘记泪滴在导线进入焊盘的点添加泪滴Teardrop可以加强连接防止应力集中导致断裂。立创EDA有自动添加泪滴的功能建议在布线完成后使用。完成所有布线和检查后就可以生成Gerber文件提交给PCB打样厂商如嘉立创了。等待几天你就能拿到属于自己的专业电路板。5. 固件编程让ATtiny13A“活”起来硬件准备就绪接下来是软件灵魂。我们需要让ATtiny13A控制LED随机点亮并检测按钮动作。由于资源极其有限代码必须精简高效。5.1 核心逻辑与状态机游戏逻辑可以抽象为一个简单的状态机待机状态所有LED熄灭等待游戏开始例如长按按钮进入游戏。游戏进行状态随机点亮一个LED并开始一个递减的计时器。如果在计时器归零前按下按钮则熄灭该LED得分并进入下一个更快的循环缩短点亮时间。如果超时未按下则游戏结束。游戏结束状态所有LED闪烁几次显示最终分数例如用短闪次数表示然后回到待机状态。为了增加趣味性我设计为同时随机点亮多个LED比如2-3个玩家需要在极短时间内按下按钮让所有点亮的LED同时熄灭这对反应速度要求更高。5.2 关键代码实现#include avr/io.h #include util/delay.h #include avr/interrupt.h // 引脚定义LED阴极连接PB0-PB4按钮ADC检测在PB2 #define LED_MASK ((1 PB0) | (1 PB1) | (1 PB2) | (1 PB3) | (1 PB4)) #define BUTTON_ADC_CHANNEL 1 // PB2对应ADC1 // 全局变量 volatile uint8_t game_level 0; volatile uint8_t active_leds 0; volatile uint8_t score 0; // 初始化函数 void init() { // 1. 设置LED引脚为输出初始高电平LED灭 DDRB | LED_MASK; PORTB | LED_MASK; // 上拉电阻使能对于输出模式写1为高电平 // 2. 初始化ADC用于按钮检测 ADMUX (1 ADLAR) | (BUTTON_ADC_CHANNEL 0x07); // 左对齐结果选择通道 ADCSRA (1 ADEN) | (1 ADPS2); // 使能ADC预分频16 (9.6MHz/16600kHz, 在50-200kHz推荐范围内) // 3. 初始化一个定时器例如Timer0用于游戏计时 // 这里使用CTC模式每1ms产生一次中断假设9.6MHz时钟预分频64 TCCR0A (1 WGM01); // CTC模式 TCCR0B (1 CS01) | (1 CS00); // 预分频64 OCR0A 149; // 9.6MHz / 64 / (1491) 1000Hz - 1ms TIMSK0 (1 OCIE0A); // 使能比较匹配A中断 sei(); // 开启全局中断 } // 读取按钮状态使用ADC uint8_t read_button() { ADCSRA | (1 ADSC); // 开始转换 while (ADCSRA (1 ADSC)); // 等待转换完成 // 由于设置了左对齐结果在ADCH寄存器。阈值需要根据实际上拉电压调整。 if (ADCH 50) { // 假设电压低于约0.25V5V参考时认为按钮按下 _delay_ms(20); // 简单消抖 if (ADCH 50) return 1; } return 0; } // 随机点亮指定数量的LED void light_random_leds(uint8_t count) { PORTB | LED_MASK; // 先全部熄灭 active_leds 0; for(uint8_t i0; icount; i) { uint8_t led; do { led (uint8_t)(rand() % 5); // 生成0-4的随机数 } while (active_leds (1 led)); // 防止重复点亮同一个LED PORTB ~(1 led); // 拉低对应引脚点亮LED active_leds | (1 led); } } // 游戏主循环简化示例在main中实现 int main(void) { init(); uint16_t timer_count 0; uint8_t game_active 0; while(1) { if (!game_active) { // 待机状态检测长按开始游戏 if(read_button()) { _delay_ms(1000); // 检测长按1秒 if(read_button()) { game_active 1; game_level 1; score 0; } } } else { // 游戏进行状态 light_random_leds(min(game_level1, 5)); // 随等级增加点亮LED数量 timer_count 1000 - game_level*100; // 随等级减少允许反应时间 while(timer_count 0) { _delay_ms(1); // 在定时器中断中递减timer_count if(read_button()) { // 检查是否所有点亮的LED都被“熄灭”即玩家反应正确 // 这里简化逻辑按下按钮即算成功 PORTB | LED_MASK; // 熄灭所有LED score; game_level; break; } } if(timer_count 0) { // 超时游戏结束 game_active 0; // 闪烁LED显示分数 for(uint8_t i0; iscore; i) { PORTB ~LED_MASK; _delay_ms(200); PORTB | LED_MASK; _delay_ms(200); } } } } } // Timer0 比较匹配A中断服务程序 ISR(TIMER0_COMPA_vect) { if (timer_count 0) timer_count--; }这段代码是一个高度简化的框架实际实现中需要更完善的随机数种子初始化可以利用ADC读取浮动值、更精确的计时、按钮消抖以及游戏难度曲线调整。它展示了如何操作IO口、使用ADC、配置定时器中断等核心操作。在ATtiny13A上编程必须时刻关注代码体积避免使用浮点数、大型库函数等。6. 焊接、组装与调试全记录收到打样回来的PCB后看着光亮的绿色阻焊层和清晰的白色丝印成就感油然而生。接下来就是焊接环节。6.1 焊接顺序与技巧我采用的焊接顺序是“先矮后高先贴片后插件”焊接贴片元件首先是电阻、电容这些0805封装的元件。使用细尖头的烙铁我用的刀头也很顺手配合焊锡丝和助焊剂。先在焊盘上点少量锡然后用镊子夹住元件放上去加热焊盘和元件引脚使锡熔化流动包裹住引脚。焊接LED时要注意极性PCB上通常有标记LED的绿色色带或切角对应阴极短脚。焊接单片机ATtiny13A是SOIC-8封装。我使用了拖焊法先在一边的四个焊盘上上少量锡将芯片对准放好注意方向芯片上的圆点或凹槽对应PCB上的白点固定一个引脚后检查对齐然后快速拖动烙铁头将四个引脚焊好。另一侧同样操作。最后检查有无桥连用吸锡带或助焊剂配合烙铁清理。焊接接口和按钮最后焊接ISP排母、电源接口和按钮这些插件元件。从背面插入正面焊接剪掉过长的引脚。注意焊接ATtiny13A这类CMOS芯片时烙铁最好接地或者至少保证烙铁头干净并温度适宜320-350°C避免静电或过热损坏芯片。6.2 上电前检查与调试焊接完成后不要急于上电务必进行以下检查目视检查用放大镜或手机微距功能仔细检查有无焊锡桥连、虚焊、元件错位或极性焊反。万用表通断测试测量VCC和GND之间的电阻确保没有短路应有一个较大的阻值而非接近0欧姆。这是最重要的一步可以防止电源短路烧毁元件。ISP连接测试将USBasp编程器连接到板子的ISP接口确保连接牢固线序正确。确认无误后连接5V电源可以用USB转5V的线。首先观察板子有无异常发热、冒烟。如果没有用万用表测量ATtiny13A的VCC引脚第8脚和GND引脚第4脚之间电压确认是否为稳定的5V左右。6.3 程序烧录与功能验证将编写好的程序通过Arduino IDE和USBasp烧录到ATtiny13A中。烧录时确保在“工具”菜单中正确选择了芯片型号ATtiny13/13A、时钟、编程器等选项。如果烧录失败常见原因有电源不稳定尝试给板子单独供电而不是仅靠USBasp供电USBasp的VCC输出电流可能不足。复位引脚被拉低检查是否有元件意外地将RESET引脚拉低。编程器连接错误仔细核对MOSI、MISO、SCK、RST的连线。烧录成功后按下复位键如果有的话或重新上电游戏应该就开始运行了。如果LED没有任何反应首先检查LED的驱动电路测量当某个IO口被程序设置为低电平时对应的LED阴极引脚电压是否接近0V。如果电压没变化可能是程序IO配置错误如果电压变化但LED不亮检查LED是否焊反、限流电阻是否虚焊。按钮检测如果失灵可以用万用表测量按钮按下和松开时PB2引脚对地的电压变化看是否在0V和5V之间跳变。如果电压变化正常但程序检测不到可能是ADC初始化代码或阈值判断有问题。7. 项目总结与进阶思考这个“Five LEDs Puzzle”项目从构思到实现走完了单片机小产品开发的全流程需求分析、器件选型、原理图设计、PCB布局布线、固件编程、焊接调试。它麻雀虽小五脏俱全涵盖了嵌入式开发中最基础也最重要的环节。几个关键的体会资源规划是微型项目的生命线在ATtiny13A这样的芯片上每一个IO口、每一个字节的RAM和Flash都要精打细算。用ADC读取按钮这个“骚操作”就是被IO口数量逼出来的创意。在项目开始前花时间画一张资源分配图能避免后期很多麻烦。PCB布局决定成败良好的布局不仅能减少布线难度更能从根本上提升电路的稳定性和抗干扰能力。地平面的使用、电源去耦电容的摆放、信号路径的规划这些细节在低频数字电路中可能影响不大但却是养成专业习惯的起点。调试需要耐心和逻辑硬件调试就像破案需要根据现象LED不亮、按钮失灵提出假设电源问题程序问题焊接问题然后用工具万用表、逻辑分析仪逐一验证。系统地、分模块地排查远比盲目更换元件有效。这个项目还可以从哪些方向扩展增加声音反馈加入一个无源蜂鸣器用PWM产生不同音调在游戏成功、失败、升级时给出提示音。设计专属外壳用3D打印或亚克力激光切割为PCB制作一个漂亮的外壳提升整体质感。改用更强大的芯片如果觉得ATtiny13A太局促可以换成ATtiny858KB Flash更多IO甚至ESP8266自带Wi-Fi实现分数上传、多人对战等联网功能。优化游戏算法引入更复杂的随机模式、连击奖励、动态难度调整等让游戏更耐玩。最终当我拿着这块自己设计、焊接、编程的小板子看着LED随着我的反应明灭时那种从无到有创造的满足感是任何现成玩具都无法比拟的。它不仅仅是一个谜题游戏更是一个凝结了思考与动手能力的实体作品。希望这个详细的记录能为你开启自己的硬件创造之旅提供一份可靠的参考。