1. 项目概述与核心思路几年前我在捣鼓一个需要精简显示数字的项目时第一次接触到BCD编码。当时觉得用四位二进制数直接“翻译”一个十进制位这种思路既优雅又高效。后来市面上各种智能手表层出不穷功能花哨但我总想做一个能体现“极客”精神、有点不一样的东西。于是把BCD编码和手表结合起来的念头就冒出来了——为什么不做一个用LED灯阵来显示二进制时间的手表呢它不直接告诉你“12:34”而是让你自己“解码”灯光的明灭这本身就是一种乐趣和挑战。这个项目我们称之为“二进制手表”更准确地说是基于BCD编码原理的二进制手表。它的核心目标是用最基础的电子元件微控制器、LED、电阻通过硬件和软件的配合实现时间的获取、编码和可视化显示。整个项目麻雀虽小五脏俱全涉及了嵌入式开发的全流程从核心的Attiny85微控制器编程到利用74HC595扩展IO口驱动LED阵列再到自主设计圆形PCB并集成充电管理电路。完成它你不仅能收获一块独一无二的、能彰显技术品味的配饰更能深入理解数字系统、电源管理和紧凑型硬件设计的实战要点。2. 核心硬件解析与选型考量2.1 大脑为什么是Attiny85在微控制器选型上我们面临几个核心约束低功耗毕竟是手表、引脚数量要驱动多个LED、尺寸要能放进手表壳以及成本。Arduino Uno虽然易用但体积和功耗完全不适合可穿戴设备。Attiny85几乎是为这类场景量身定做的极致紧凑只有8个引脚采用SMD封装面积微小。足够性能基于AVR架构运行我们这种简单的计时、按钮检测和LED控制逻辑绰绰有余。超低功耗在1MHz时钟下配合睡眠模式功耗可以控制在微安级别极大延长电池续航。成本低廉单价仅几元人民币非常适合DIY项目。注意Attiny85内部没有硬件RTC实时时钟这意味着断电后时间会丢失。我们的方案是利用其内部定时器进行软件计时每次上电从0开始。这是为了极致简化设计做的取舍。如果你希望断电记忆需要额外增加一颗DS3231之类的RTC芯片但这会显著增加复杂度和体积。2.2 经脉74HC595移位寄存器的必要性Attiny85只有6个可用IO口除去复位引脚。我们需要驱动至少12个LED小时和分钟各需6个LED因为最大数字59需要6位二进制表示。直接驱动是绝对不够的。这时74HC595这类串行输入、并行输出的移位寄存器就成了救星。工作原理微控制器仅需使用3个引脚数据、时钟、锁存就可以通过串行方式将数据一位一位地“推”到74HC595内部的8位寄存器中然后一个锁存信号同时更新所有8个输出引脚的状态。级联多片74HC595可以驱动几乎任意数量的LED而仅占用MCU的3个引脚。选型对比为什么不用更简单的串行转并行芯片比如74HC164因为74HC595带有输出锁存功能。在数据传输过程中输出引脚状态不会变化只有在我们发出锁存信号时才一次性更新。这避免了LED在数据传输时产生“鬼影”或闪烁视觉效果更稳定。2.3 能量中枢电源与充电管理方案可穿戴设备的电源系统设计是成败关键之一直接关系到安全性和用户体验。电池选择选择了500mAh的锂聚合物电池。容量适中能在小巧的体积和可接受的续航预计数天间取得平衡。关键是它的放电曲线相对平坦电压在大部分容量区间保持在3.7V左右。电压调节Attiny85和74HC595的工作电压范围较宽2.7V-5.5V但为了稳定和低功耗我们将其工作电压设定在3.3V。电池满电电压约4.2V需要降压。这里使用了MIC5205-3.3V这款LDO低压差线性稳压器。相比开关稳压器LDO电路简单噪声小虽然效率稍低但对于这种小电流设备完全可接受。充电管理TP4056是单节锂电池充电IC的“明星产品”成本极低外围电路简单。它负责以恒定电流/恒定电压方式安全地为电池充电并集成充电状态指示红灯充电绿灯充满。原项目作者“魔改”了现成的TP4056模块将芯片拆下焊到自己的PCB上这需要一定的焊接技巧但能最大化利用空间是硬件黑客精神的体现。2.4 交互与显示按钮与LED阵列按钮选用微型贴片轻触开关。所有功能查看时间、进入设置、调整数值都通过这一颗按钮以不同的按压时长短按、长按来区分这是嵌入式系统中常见的“单按键多功能”设计能极大节省IO口和物理空间。LED选用0603封装的SMD LED红蓝两色。红色代表“小时”位蓝色代表“分钟”位。0603尺寸极小适合高密度布局。每个LED串联一个1KΩ的限流电阻根据3.3V供电电压计算电流大约在(3.3V - LED压降约2V)/1000Ω ≈ 1.3mA既能保证亮度又兼顾了低功耗。3. 电路设计与PCB布局实战3.1 原理图设计要点使用KiCad这类开源EDA工具进行设计是首选。原理图需要清晰表达以下几个部分MCU最小系统Attiny85及其必要的去耦电容通常为0.1uF紧贴电源引脚放置。LED驱动网络74HC595的VCC和GND、每个输出引脚通过限流电阻连接到对应的LEDLED另一端统一接GND共阴极接法。电源树Type-C输入口 - TP4056充电电路 - 电池 - MIC5205 LDO - 产生3.3V系统电源VCC。务必在LDO的输入和输出端都放置足够容量的滤波电容如10uF。用户接口按钮一端接MCU的某个IO口配置为内部上拉输入另一端接地。3.2 PCB布局与走线的心得圆形PCB是为了贴合手表形态。布局是挑战也是艺术。模块化分区将PCB想象成一个圆盘。中心区域放置耗电较大的LDO和充电IC。MCU作为“大脑”放在相对中心且靠近按钮的位置。74HC595放在靠近其需要驱动的LED区域以缩短走线。电源优先首先布置电源路径VCC和GND。确保电源线宽足够例如0.5mm以上减少压降。采用“星型”或“单点”接地策略避免数字噪声通过地线干扰模拟部分虽然本项目模拟部分简单。信号线处理74HC595与MCU连接的数据、时钟、锁存线尽量等长、平行走线并远离可能产生噪声的电源线。对于LED控制线因为频率很低要求可以放宽。关于作者遇到的“坑”原设计文件中74HC595的封装画错了导致SMD芯片无法焊接。这是一个非常典型的教训。在投板前必须、反复、交叉检查每一个元器件的封装是否与实物完全匹配。可以利用KiCad的3D视图功能在线查看元件模型是否贴合。我们的补救措施是飞线焊接一个直插封装的74HC595但这破坏了美观并增加了体积。对于V2版本修正封装是首要任务。3.3 PCB打样与焊接打样将KiCad生成的Gerber文件打包提交给PCBWay等制造商。对于这种异形圆形且带有槽孔表带孔的板子需要特别在订单备注中说明“按外形轮廓铣边”。收到板子后第一件事是目视检查和用万用表通断档测量电源与地之间是否短路这是焊接前的“保命”步骤。焊接顺序遵循“先矮后高先内后外”的原则。对于这个双面贴片板先焊接电阻、电容等小而无源器件。然后焊接ICAttiny85, 74HC595, MIC5205, TP4056。使用烙铁和尖头配合焊锡丝和助焊剂采用“拖焊”技巧处理密脚芯片。技巧先给焊盘上一个脚的焊锡用镊子将芯片对准放好固定这个脚再焊接对角的一个脚以完全定位最后再处理其余引脚。焊接LED。注意极性0603 LED通常有一个绿色标记或缺口的一端是阴极负极。不确定时用万用表二极管档测试。最后焊接Type-C插座和按钮这些稍大的元件。焊接后的检查再次用万用表检查关键点电压电池接口电压、LDO输入输出电压、MCU的VCC引脚电压是否为3.3V。确保没有虚焊、短路。4. 软件设计与代码实现详解4.1 开发环境搭建与芯片配置Attiny85不是Arduino IDE默认支持的芯片需要手动添加支持。添加板卡支持打开Arduino IDE依次点击 文件 - 首选项在“附加开发板管理器网址”中输入https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json。然后在 工具 - 开发板 - 开发板管理器 中搜索并安装 “attiny” 相关的包。配置编程器我们需要用另一块Arduino板如Uno作为编程器ISP。在示例中找到ArduinoISP程序将其烧录到作为编程器的Arduino板上。连接硬件按照引脚对应关系用杜邦线将编程器Arduino的MOSI, MISO, SCK, RESET, VCC, GND分别连接到目标板我们的手表PCB上Attiny85的对应引脚。务必确保VCC电压一致都是5V或都是3.3V否则可能损坏芯片。我们的手表板在烧录时需要外部提供5V电源可从编程器取电。设置熔丝位熔丝位决定了芯片的时钟源、启动延时等底层配置。为了低功耗我们选择使用内部1MHz RC振荡器。在Arduino IDE中选择开发板为 “ATtiny25/45/85”处理器为 “ATtiny85”时钟为 “1 MHz (internal)”编程器选择 “Arduino as ISP”。然后点击工具 - 烧录引导程序。这个操作不仅烧录了引导程序虽然Attiny85一般不用更重要的是按照IDE的预设配置了正确的熔丝位。4.2 核心代码逻辑剖析代码的核心是状态机管理、时间维护和BCD编码显示。// 伪代码与关键逻辑说明 #include // 用于驱动74HC595 // 引脚定义 const int dataPin 0; // Attiny85 PB0 const int clockPin 1; // PB1 const int latchPin 2; // PB2 const int buttonPin 3; // PB3 // 全局变量 unsigned long lastMillis 0; int seconds 0, minutes 0, hours 0; enum Mode {NORMAL, SET_HOUR, SET_MINUTE} currentMode NORMAL; unsigned long buttonPressTime 0; bool buttonActive false; // BCD编码函数将十进制数转换为4位二进制表示的BCD码仅适用于0-59 byte decToBcd(byte val) { return ( (val/10)4 ) | (val%10); // 十位放在高4位个位放在低4位 } void updateDisplay() { byte dataToSend 0; if (currentMode NORMAL || currentMode SET_MINUTE) { // 显示分钟蓝色LED dataToSend decToBcd(minutes); // 同时点亮“分钟”指示LED假设连接在74HC595的某一位 dataToSend | (1 7); } else if (currentMode SET_HOUR) { // 显示小时红色LED dataToSend decToBcd(hours); // 同时点亮“小时”指示LED dataToSend | (1 6); } // 通过74HC595输出 digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, dataToSend); digitalWrite(latchPin, HIGH); } void handleButton() { int buttonState digitalRead(buttonPin); if (buttonState LOW !buttonActive) { buttonPressTime millis(); buttonActive true; } if (buttonState HIGH buttonActive) { buttonActive false; unsigned long duration millis() - buttonPressTime; if (duration 1000) { // 短按在NORMAL模式下切换显示小时/分钟在设置模式下增加数值 shortPressAction(); } else { // 长按切换模式 NORMAL - SET_HOUR - SET_MINUTE - NORMAL longPressAction(); } } } void loop() { // 1. 时间累积简易软件计时存在累积误差 unsigned long currentMillis millis(); if (currentMillis - lastMillis 1000) { lastMillis currentMillis; seconds; if (seconds 60) { seconds 0; minutes; } if (minutes 60) { minutes 0; hours; } if (hours 24) { hours 0; } } // 2. 处理按钮事件 handleButton(); // 3. 更新显示 updateDisplay(); // 4. 根据模式可能需要进行显示延时如交替显示时 delay(50); // 短暂延时用于去抖和降低功耗 }关键点解析软件计时millis()函数返回自开机以来的毫秒数。通过比较时间差来实现秒累加。这是最简单的方案但精度受晶振误差和中断响应影响。每天可能有几秒到几十秒的误差。对于手表这是一个明显短板但作为学习项目可以接受。BCD编码实现decToBcd函数是核心。例如分钟数35传入35/103十位35%105个位。34即左移4位变成0b0011 0000与50b0101进行或运算得到0b0011 0101这就是35的BCD码。这个8位数据正好可以通过74HC595输出驱动两组4位的LED分别代表十位和个位。单按键状态机通过记录按键按下和释放的时间戳来区分短按1000ms和长按1000ms。currentMode变量在不同模式间循环切换驱动不同的显示和按键响应逻辑。4.3 代码烧录与调试连接编程器确保硬件连接正确给手表板供电。编译与上传在Arduino IDE中选择正确的板卡、端口和编程器Arduino as ISP点击项目 - 使用编程器上传。这会将编译好的机器码直接烧录到Attiny85的Flash中。首次上电现象烧录完成后断开编程器连接单独给手表板上电。你可能会看到LED乱闪这是正常的因为初始变量是随机的。此时务必按一下按钮触发时间显示流程系统就会进入正常的工作状态。调试技巧由于没有串口调试Attiny85比较困难。可以“滥用”LED用不同闪烁模式来表示程序运行到了哪个阶段或某个变量的值。例如进入设置模式时让某个LED快速闪烁3下。这是一种最原始但有效的“printf”调试法。5. 组装、测试与优化心得5.1 机械组装与结构固定PCB上的两个槽孔是用于安装表带的。可以使用弹性尼龙表带或真皮表带通过生耳弹簧杆进行连接。作者的方案是从旧手表上拆下表带并用强力胶将表带头部固定在PCB槽孔内。这不是最优雅的方式存在开胶风险。更优建议可以设计一个简单的3D打印外壳。外壳分为前盖和后盖将PCB卡在中间表带则连接在外壳的耳片上。这样既能保护PCB和元件也使得表带的更换和佩戴更加可靠舒适。在PCB设计时就需要预留外壳的固定孔位。5.2 系统测试流程电源测试连接电池测量LDO输出是否为稳定的3.3V。插入Type-C充电线观察充电指示灯红色是否亮起。充电一段时间后指示灯变绿假设TP4056模块带此功能。功能测试短按测试短按按钮观察是否交替点亮红色小时和蓝色分钟指示灯并且对应的LED阵列显示的数字需要你进行二进制解码是否合理递增。长按测试长按进入设置模式观察小时指示灯是否闪烁。短按增加小时再次长按切换到分钟设置短按增加分钟最后长按退出。检查设置的时间是否正确保存并开始运行。功耗粗略评估断开充电让手表正常运行。使用万用表电流档串联在电池和电路板之间测量工作电流。理想情况下大部分时间MCU应处于空闲或睡眠模式仅定时唤醒更新时间和检测按键平均电流应在1mA以下。如果电流过大如5mA需要检查代码中是否关闭了未用的外设如ADC以及LED的限流电阻是否过小。5.3 常见问题与排查实录即使完全按照步骤你也可能会遇到以下问题。这里是我的“踩坑”记录问题现象可能原因排查步骤与解决方案上电后无任何反应LED不亮1. 电源问题2. MCU未工作1. 检查电池电压是否正常3.5V。测量LDO输出是否为3.3V。2. 检查Attiny85的VCC和GND是否连接正确。用示波器或逻辑分析仪检查复位引脚是否一直为低被拉死。3.重烧熔丝位误操作可能导致熔丝位被配错如禁用复位引脚或选择了错误时钟源。使用高压编程器恢复或换一片新芯片。LED显示乱码或部分不亮1. 74HC595连接错误2. LED或电阻虚焊/损坏3. 代码中引脚定义错误1. 用万用表蜂鸣档逐一检查从74HC595输出引脚到LED再到电阻的物理连接是否导通。2. 编写一个简单的测试程序让74HC595顺序点亮每一个LED观察是哪个环节出了问题。3. 确认代码中dataPin,clockPin,latchPin的定义与PCB实际布线完全一致。按钮无反应或反应异常1. 按钮硬件连接错误2. 软件去抖逻辑问题3. IO口模式配置错误1. 检查按钮是否一端接IO口另一端接地。按下时用万用表测量IO口对地电阻应为0。2. 在handleButton函数中增加更稳健的消抖处理例如检测到低电平后延时20ms再确认。3. 确认代码中已将按钮引脚设置为INPUT_PULLUP启用内部上拉电阻。时间走得飞快或特别慢熔丝位中时钟源配置错误这是最常见的问题之一。我们选择的是内部1MHz RC振荡器。如果熔丝位被错误地配成了外部时钟或8MHz内部RC就会导致millis()的计时基准错误。务必通过“烧录引导程序”的方式让Arduino IDE帮你配置好熔丝位。电池耗电极快1. LED限流电阻过小2. MCU未进入睡眠3. 电源路径存在漏电1. 计算并适当增大LED限流电阻值在可见亮度下尽量减小电流。2. 在代码循环中当无事可做时使用#include库让MCU进入空闲Idle或掉电Power-down睡眠模式通过定时器中断唤醒。这是降低功耗最有效的手段。3. 检查PCB上是否有焊锡桥接导致短路或者元件如LDO本身静态电流过大。5.4 项目优化与扩展思路这个项目作为一个起点有巨大的优化和扩展空间精度优化软件计时误差大。可以引入外部32.768kHz晶振并利用Attiny85的异步定时器/计数器功能实现更精确的秒信号产生。或者直接集成一颗DS3231高精度RTC模块。功耗优化代码层面尽可能让MCU睡眠。使用中断唤醒定时器中断、引脚变化中断。硬件层面在74HC595的电源路径上增加一个MOSFET开关当不需要显示时彻底切断LED驱动电路的电源。功能扩展增加光感添加一个光敏电阻或环境光传感器自动调节LED亮度在暗处降低亮度以节省电量。增加振动马达实现闹钟或通知震动功能。蓝牙连接使用超低功耗蓝牙BLE模块如HM-10通过手机APP同步时间、设置闹钟甚至接收手机通知通过特定LED闪烁模式表示。结构优化如前所述设计3D打印外壳提升成品质感。甚至可以设计一个亚克力表壳让内部的PCB和元件成为“赛博朋克”风格展示的一部分。制作这样一块手表最大的成就感不在于它比商业产品更精准或功能更多而在于从无到有将代码、电流与物理结构融合成一个可佩戴、可交互的作品。每一个闪烁的LED都是你对底层逻辑理解的直接映射。过程中遇到的每一个问题从封装画反到时间飘移都是嵌入式开发中最真实的教科书。当你最终抬起手腕成功解读出那一排排LED所代表的二进制时间时那种感觉是购买任何成品都无法替代的。
从BCD编码到可穿戴设备:自制二进制LED手表全流程解析
发布时间:2026/5/31 12:27:48
1. 项目概述与核心思路几年前我在捣鼓一个需要精简显示数字的项目时第一次接触到BCD编码。当时觉得用四位二进制数直接“翻译”一个十进制位这种思路既优雅又高效。后来市面上各种智能手表层出不穷功能花哨但我总想做一个能体现“极客”精神、有点不一样的东西。于是把BCD编码和手表结合起来的念头就冒出来了——为什么不做一个用LED灯阵来显示二进制时间的手表呢它不直接告诉你“12:34”而是让你自己“解码”灯光的明灭这本身就是一种乐趣和挑战。这个项目我们称之为“二进制手表”更准确地说是基于BCD编码原理的二进制手表。它的核心目标是用最基础的电子元件微控制器、LED、电阻通过硬件和软件的配合实现时间的获取、编码和可视化显示。整个项目麻雀虽小五脏俱全涉及了嵌入式开发的全流程从核心的Attiny85微控制器编程到利用74HC595扩展IO口驱动LED阵列再到自主设计圆形PCB并集成充电管理电路。完成它你不仅能收获一块独一无二的、能彰显技术品味的配饰更能深入理解数字系统、电源管理和紧凑型硬件设计的实战要点。2. 核心硬件解析与选型考量2.1 大脑为什么是Attiny85在微控制器选型上我们面临几个核心约束低功耗毕竟是手表、引脚数量要驱动多个LED、尺寸要能放进手表壳以及成本。Arduino Uno虽然易用但体积和功耗完全不适合可穿戴设备。Attiny85几乎是为这类场景量身定做的极致紧凑只有8个引脚采用SMD封装面积微小。足够性能基于AVR架构运行我们这种简单的计时、按钮检测和LED控制逻辑绰绰有余。超低功耗在1MHz时钟下配合睡眠模式功耗可以控制在微安级别极大延长电池续航。成本低廉单价仅几元人民币非常适合DIY项目。注意Attiny85内部没有硬件RTC实时时钟这意味着断电后时间会丢失。我们的方案是利用其内部定时器进行软件计时每次上电从0开始。这是为了极致简化设计做的取舍。如果你希望断电记忆需要额外增加一颗DS3231之类的RTC芯片但这会显著增加复杂度和体积。2.2 经脉74HC595移位寄存器的必要性Attiny85只有6个可用IO口除去复位引脚。我们需要驱动至少12个LED小时和分钟各需6个LED因为最大数字59需要6位二进制表示。直接驱动是绝对不够的。这时74HC595这类串行输入、并行输出的移位寄存器就成了救星。工作原理微控制器仅需使用3个引脚数据、时钟、锁存就可以通过串行方式将数据一位一位地“推”到74HC595内部的8位寄存器中然后一个锁存信号同时更新所有8个输出引脚的状态。级联多片74HC595可以驱动几乎任意数量的LED而仅占用MCU的3个引脚。选型对比为什么不用更简单的串行转并行芯片比如74HC164因为74HC595带有输出锁存功能。在数据传输过程中输出引脚状态不会变化只有在我们发出锁存信号时才一次性更新。这避免了LED在数据传输时产生“鬼影”或闪烁视觉效果更稳定。2.3 能量中枢电源与充电管理方案可穿戴设备的电源系统设计是成败关键之一直接关系到安全性和用户体验。电池选择选择了500mAh的锂聚合物电池。容量适中能在小巧的体积和可接受的续航预计数天间取得平衡。关键是它的放电曲线相对平坦电压在大部分容量区间保持在3.7V左右。电压调节Attiny85和74HC595的工作电压范围较宽2.7V-5.5V但为了稳定和低功耗我们将其工作电压设定在3.3V。电池满电电压约4.2V需要降压。这里使用了MIC5205-3.3V这款LDO低压差线性稳压器。相比开关稳压器LDO电路简单噪声小虽然效率稍低但对于这种小电流设备完全可接受。充电管理TP4056是单节锂电池充电IC的“明星产品”成本极低外围电路简单。它负责以恒定电流/恒定电压方式安全地为电池充电并集成充电状态指示红灯充电绿灯充满。原项目作者“魔改”了现成的TP4056模块将芯片拆下焊到自己的PCB上这需要一定的焊接技巧但能最大化利用空间是硬件黑客精神的体现。2.4 交互与显示按钮与LED阵列按钮选用微型贴片轻触开关。所有功能查看时间、进入设置、调整数值都通过这一颗按钮以不同的按压时长短按、长按来区分这是嵌入式系统中常见的“单按键多功能”设计能极大节省IO口和物理空间。LED选用0603封装的SMD LED红蓝两色。红色代表“小时”位蓝色代表“分钟”位。0603尺寸极小适合高密度布局。每个LED串联一个1KΩ的限流电阻根据3.3V供电电压计算电流大约在(3.3V - LED压降约2V)/1000Ω ≈ 1.3mA既能保证亮度又兼顾了低功耗。3. 电路设计与PCB布局实战3.1 原理图设计要点使用KiCad这类开源EDA工具进行设计是首选。原理图需要清晰表达以下几个部分MCU最小系统Attiny85及其必要的去耦电容通常为0.1uF紧贴电源引脚放置。LED驱动网络74HC595的VCC和GND、每个输出引脚通过限流电阻连接到对应的LEDLED另一端统一接GND共阴极接法。电源树Type-C输入口 - TP4056充电电路 - 电池 - MIC5205 LDO - 产生3.3V系统电源VCC。务必在LDO的输入和输出端都放置足够容量的滤波电容如10uF。用户接口按钮一端接MCU的某个IO口配置为内部上拉输入另一端接地。3.2 PCB布局与走线的心得圆形PCB是为了贴合手表形态。布局是挑战也是艺术。模块化分区将PCB想象成一个圆盘。中心区域放置耗电较大的LDO和充电IC。MCU作为“大脑”放在相对中心且靠近按钮的位置。74HC595放在靠近其需要驱动的LED区域以缩短走线。电源优先首先布置电源路径VCC和GND。确保电源线宽足够例如0.5mm以上减少压降。采用“星型”或“单点”接地策略避免数字噪声通过地线干扰模拟部分虽然本项目模拟部分简单。信号线处理74HC595与MCU连接的数据、时钟、锁存线尽量等长、平行走线并远离可能产生噪声的电源线。对于LED控制线因为频率很低要求可以放宽。关于作者遇到的“坑”原设计文件中74HC595的封装画错了导致SMD芯片无法焊接。这是一个非常典型的教训。在投板前必须、反复、交叉检查每一个元器件的封装是否与实物完全匹配。可以利用KiCad的3D视图功能在线查看元件模型是否贴合。我们的补救措施是飞线焊接一个直插封装的74HC595但这破坏了美观并增加了体积。对于V2版本修正封装是首要任务。3.3 PCB打样与焊接打样将KiCad生成的Gerber文件打包提交给PCBWay等制造商。对于这种异形圆形且带有槽孔表带孔的板子需要特别在订单备注中说明“按外形轮廓铣边”。收到板子后第一件事是目视检查和用万用表通断档测量电源与地之间是否短路这是焊接前的“保命”步骤。焊接顺序遵循“先矮后高先内后外”的原则。对于这个双面贴片板先焊接电阻、电容等小而无源器件。然后焊接ICAttiny85, 74HC595, MIC5205, TP4056。使用烙铁和尖头配合焊锡丝和助焊剂采用“拖焊”技巧处理密脚芯片。技巧先给焊盘上一个脚的焊锡用镊子将芯片对准放好固定这个脚再焊接对角的一个脚以完全定位最后再处理其余引脚。焊接LED。注意极性0603 LED通常有一个绿色标记或缺口的一端是阴极负极。不确定时用万用表二极管档测试。最后焊接Type-C插座和按钮这些稍大的元件。焊接后的检查再次用万用表检查关键点电压电池接口电压、LDO输入输出电压、MCU的VCC引脚电压是否为3.3V。确保没有虚焊、短路。4. 软件设计与代码实现详解4.1 开发环境搭建与芯片配置Attiny85不是Arduino IDE默认支持的芯片需要手动添加支持。添加板卡支持打开Arduino IDE依次点击 文件 - 首选项在“附加开发板管理器网址”中输入https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json。然后在 工具 - 开发板 - 开发板管理器 中搜索并安装 “attiny” 相关的包。配置编程器我们需要用另一块Arduino板如Uno作为编程器ISP。在示例中找到ArduinoISP程序将其烧录到作为编程器的Arduino板上。连接硬件按照引脚对应关系用杜邦线将编程器Arduino的MOSI, MISO, SCK, RESET, VCC, GND分别连接到目标板我们的手表PCB上Attiny85的对应引脚。务必确保VCC电压一致都是5V或都是3.3V否则可能损坏芯片。我们的手表板在烧录时需要外部提供5V电源可从编程器取电。设置熔丝位熔丝位决定了芯片的时钟源、启动延时等底层配置。为了低功耗我们选择使用内部1MHz RC振荡器。在Arduino IDE中选择开发板为 “ATtiny25/45/85”处理器为 “ATtiny85”时钟为 “1 MHz (internal)”编程器选择 “Arduino as ISP”。然后点击工具 - 烧录引导程序。这个操作不仅烧录了引导程序虽然Attiny85一般不用更重要的是按照IDE的预设配置了正确的熔丝位。4.2 核心代码逻辑剖析代码的核心是状态机管理、时间维护和BCD编码显示。// 伪代码与关键逻辑说明 #include // 用于驱动74HC595 // 引脚定义 const int dataPin 0; // Attiny85 PB0 const int clockPin 1; // PB1 const int latchPin 2; // PB2 const int buttonPin 3; // PB3 // 全局变量 unsigned long lastMillis 0; int seconds 0, minutes 0, hours 0; enum Mode {NORMAL, SET_HOUR, SET_MINUTE} currentMode NORMAL; unsigned long buttonPressTime 0; bool buttonActive false; // BCD编码函数将十进制数转换为4位二进制表示的BCD码仅适用于0-59 byte decToBcd(byte val) { return ( (val/10)4 ) | (val%10); // 十位放在高4位个位放在低4位 } void updateDisplay() { byte dataToSend 0; if (currentMode NORMAL || currentMode SET_MINUTE) { // 显示分钟蓝色LED dataToSend decToBcd(minutes); // 同时点亮“分钟”指示LED假设连接在74HC595的某一位 dataToSend | (1 7); } else if (currentMode SET_HOUR) { // 显示小时红色LED dataToSend decToBcd(hours); // 同时点亮“小时”指示LED dataToSend | (1 6); } // 通过74HC595输出 digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, dataToSend); digitalWrite(latchPin, HIGH); } void handleButton() { int buttonState digitalRead(buttonPin); if (buttonState LOW !buttonActive) { buttonPressTime millis(); buttonActive true; } if (buttonState HIGH buttonActive) { buttonActive false; unsigned long duration millis() - buttonPressTime; if (duration 1000) { // 短按在NORMAL模式下切换显示小时/分钟在设置模式下增加数值 shortPressAction(); } else { // 长按切换模式 NORMAL - SET_HOUR - SET_MINUTE - NORMAL longPressAction(); } } } void loop() { // 1. 时间累积简易软件计时存在累积误差 unsigned long currentMillis millis(); if (currentMillis - lastMillis 1000) { lastMillis currentMillis; seconds; if (seconds 60) { seconds 0; minutes; } if (minutes 60) { minutes 0; hours; } if (hours 24) { hours 0; } } // 2. 处理按钮事件 handleButton(); // 3. 更新显示 updateDisplay(); // 4. 根据模式可能需要进行显示延时如交替显示时 delay(50); // 短暂延时用于去抖和降低功耗 }关键点解析软件计时millis()函数返回自开机以来的毫秒数。通过比较时间差来实现秒累加。这是最简单的方案但精度受晶振误差和中断响应影响。每天可能有几秒到几十秒的误差。对于手表这是一个明显短板但作为学习项目可以接受。BCD编码实现decToBcd函数是核心。例如分钟数35传入35/103十位35%105个位。34即左移4位变成0b0011 0000与50b0101进行或运算得到0b0011 0101这就是35的BCD码。这个8位数据正好可以通过74HC595输出驱动两组4位的LED分别代表十位和个位。单按键状态机通过记录按键按下和释放的时间戳来区分短按1000ms和长按1000ms。currentMode变量在不同模式间循环切换驱动不同的显示和按键响应逻辑。4.3 代码烧录与调试连接编程器确保硬件连接正确给手表板供电。编译与上传在Arduino IDE中选择正确的板卡、端口和编程器Arduino as ISP点击项目 - 使用编程器上传。这会将编译好的机器码直接烧录到Attiny85的Flash中。首次上电现象烧录完成后断开编程器连接单独给手表板上电。你可能会看到LED乱闪这是正常的因为初始变量是随机的。此时务必按一下按钮触发时间显示流程系统就会进入正常的工作状态。调试技巧由于没有串口调试Attiny85比较困难。可以“滥用”LED用不同闪烁模式来表示程序运行到了哪个阶段或某个变量的值。例如进入设置模式时让某个LED快速闪烁3下。这是一种最原始但有效的“printf”调试法。5. 组装、测试与优化心得5.1 机械组装与结构固定PCB上的两个槽孔是用于安装表带的。可以使用弹性尼龙表带或真皮表带通过生耳弹簧杆进行连接。作者的方案是从旧手表上拆下表带并用强力胶将表带头部固定在PCB槽孔内。这不是最优雅的方式存在开胶风险。更优建议可以设计一个简单的3D打印外壳。外壳分为前盖和后盖将PCB卡在中间表带则连接在外壳的耳片上。这样既能保护PCB和元件也使得表带的更换和佩戴更加可靠舒适。在PCB设计时就需要预留外壳的固定孔位。5.2 系统测试流程电源测试连接电池测量LDO输出是否为稳定的3.3V。插入Type-C充电线观察充电指示灯红色是否亮起。充电一段时间后指示灯变绿假设TP4056模块带此功能。功能测试短按测试短按按钮观察是否交替点亮红色小时和蓝色分钟指示灯并且对应的LED阵列显示的数字需要你进行二进制解码是否合理递增。长按测试长按进入设置模式观察小时指示灯是否闪烁。短按增加小时再次长按切换到分钟设置短按增加分钟最后长按退出。检查设置的时间是否正确保存并开始运行。功耗粗略评估断开充电让手表正常运行。使用万用表电流档串联在电池和电路板之间测量工作电流。理想情况下大部分时间MCU应处于空闲或睡眠模式仅定时唤醒更新时间和检测按键平均电流应在1mA以下。如果电流过大如5mA需要检查代码中是否关闭了未用的外设如ADC以及LED的限流电阻是否过小。5.3 常见问题与排查实录即使完全按照步骤你也可能会遇到以下问题。这里是我的“踩坑”记录问题现象可能原因排查步骤与解决方案上电后无任何反应LED不亮1. 电源问题2. MCU未工作1. 检查电池电压是否正常3.5V。测量LDO输出是否为3.3V。2. 检查Attiny85的VCC和GND是否连接正确。用示波器或逻辑分析仪检查复位引脚是否一直为低被拉死。3.重烧熔丝位误操作可能导致熔丝位被配错如禁用复位引脚或选择了错误时钟源。使用高压编程器恢复或换一片新芯片。LED显示乱码或部分不亮1. 74HC595连接错误2. LED或电阻虚焊/损坏3. 代码中引脚定义错误1. 用万用表蜂鸣档逐一检查从74HC595输出引脚到LED再到电阻的物理连接是否导通。2. 编写一个简单的测试程序让74HC595顺序点亮每一个LED观察是哪个环节出了问题。3. 确认代码中dataPin,clockPin,latchPin的定义与PCB实际布线完全一致。按钮无反应或反应异常1. 按钮硬件连接错误2. 软件去抖逻辑问题3. IO口模式配置错误1. 检查按钮是否一端接IO口另一端接地。按下时用万用表测量IO口对地电阻应为0。2. 在handleButton函数中增加更稳健的消抖处理例如检测到低电平后延时20ms再确认。3. 确认代码中已将按钮引脚设置为INPUT_PULLUP启用内部上拉电阻。时间走得飞快或特别慢熔丝位中时钟源配置错误这是最常见的问题之一。我们选择的是内部1MHz RC振荡器。如果熔丝位被错误地配成了外部时钟或8MHz内部RC就会导致millis()的计时基准错误。务必通过“烧录引导程序”的方式让Arduino IDE帮你配置好熔丝位。电池耗电极快1. LED限流电阻过小2. MCU未进入睡眠3. 电源路径存在漏电1. 计算并适当增大LED限流电阻值在可见亮度下尽量减小电流。2. 在代码循环中当无事可做时使用#include库让MCU进入空闲Idle或掉电Power-down睡眠模式通过定时器中断唤醒。这是降低功耗最有效的手段。3. 检查PCB上是否有焊锡桥接导致短路或者元件如LDO本身静态电流过大。5.4 项目优化与扩展思路这个项目作为一个起点有巨大的优化和扩展空间精度优化软件计时误差大。可以引入外部32.768kHz晶振并利用Attiny85的异步定时器/计数器功能实现更精确的秒信号产生。或者直接集成一颗DS3231高精度RTC模块。功耗优化代码层面尽可能让MCU睡眠。使用中断唤醒定时器中断、引脚变化中断。硬件层面在74HC595的电源路径上增加一个MOSFET开关当不需要显示时彻底切断LED驱动电路的电源。功能扩展增加光感添加一个光敏电阻或环境光传感器自动调节LED亮度在暗处降低亮度以节省电量。增加振动马达实现闹钟或通知震动功能。蓝牙连接使用超低功耗蓝牙BLE模块如HM-10通过手机APP同步时间、设置闹钟甚至接收手机通知通过特定LED闪烁模式表示。结构优化如前所述设计3D打印外壳提升成品质感。甚至可以设计一个亚克力表壳让内部的PCB和元件成为“赛博朋克”风格展示的一部分。制作这样一块手表最大的成就感不在于它比商业产品更精准或功能更多而在于从无到有将代码、电流与物理结构融合成一个可佩戴、可交互的作品。每一个闪烁的LED都是你对底层逻辑理解的直接映射。过程中遇到的每一个问题从封装画反到时间飘移都是嵌入式开发中最真实的教科书。当你最终抬起手腕成功解读出那一排排LED所代表的二进制时间时那种感觉是购买任何成品都无法替代的。