1. 项目概述一个为游泳训练量身定制的智能计数器如果你和我一样是个游泳爱好者或者正在指导游泳训练肯定遇到过这样的烦恼游了多少圈今天的目标完成了吗不同泳姿的练习量怎么分开统计靠脑子记游着游着就乱了用传统的手动计数器又得在池边频繁操作湿漉漉的手也不方便。几年前我在指导一个业余游泳队训练时就深受其扰。于是我决定动手做一个能自动识别泳者、精准计数并且操作直观的智能设备——这就是Pisciduino项目的由来。简单来说Pisciduino是一个基于Arduino和RFID技术的智能泳池计数器。它的核心功能是为每位泳者分配一张唯一的RFID卡当泳者触壁转身时用卡片靠近读卡器系统就会自动记录一圈通常是25米或50米。设备上配有独立的按钮可以方便地清零单个泳者的计数或者查看总里程。这听起来可能像是一个简单的“打卡”装置但在实际设计和实现过程中从硬件选型、电路连接、RFID数据处理到软件逻辑和防误触设计每一个环节都藏着不少门道。这个项目完美地诠释了嵌入式系统开发中“传感器数据采集与处理”这一核心环节通过微控制器将物理世界的动作刷卡转化为可管理的数据圈数、里程是物联网和智能硬件在运动科学领域一个非常接地气的应用。无论你是想复现一个实用的训练工具还是希望学习如何将Arduino、RFID模块、按钮和显示屏整合到一个完整的嵌入式系统中这个项目都能提供从原理到实操的完整路径。接下来我将从设计思路开始一步步拆解如何实现这个智能泳池计数器。2. 核心设计思路与硬件选型解析在动手焊接第一根线之前理清设计思路至关重要。这个项目的核心需求很明确自动识别不同泳者并对其游泳圈数进行可靠、便捷的计数与管理。围绕这个需求我拆解出了几个关键的设计决策点。2.1 为什么选择RFID而非其他传感器识别泳者的方式有很多比如红外对管、压力传感器、摄像头图像识别等。我最终选择RFID射频识别主要基于以下几个考量可靠性高泳池环境潮湿可能存在水雾、飞溅。RFID采用非接触式读取读卡器和卡片都可以做防水封装受环境影响小。红外对管则容易因水渍或雾气导致误触发或失效。识别唯一性每张RFID卡都有全球唯一的ID号完美对应一位泳者不存在混淆的可能。成本与复杂度平衡低频RFID模块如RC522价格低廉技术成熟Arduino社区支持完善开发难度远低于摄像头方案。用户体验好泳者只需在触壁时用佩戴的卡片轻松一晃无需精确对准操作自然快捷。这里我选用的是最常见的MFRC522 RFID读卡模块它支持ISO/IEC 14443 A类标准有效读取距离在几厘米非常适合这种需要主动、近距离触发的场景。2.2 主控与外围设备的搭配逻辑主控芯片选择了Arduino Uno R3。对于这个项目它的性能绰绰有余需要处理RFID读卡中断、多个按钮的扫描、四则运算圈数转里程以及驱动显示模块。其丰富的数字和模拟IO口为连接各类外设提供了便利。外围设备除了RFID读卡器还包括显示模块为了同时显示多位泳者的数据我选择了TM1638模块。这是一个“神器”级别的模块它集成了8位7段数码管、8个LED指示灯和8个独立按键的扫描电路通过简单的3线串行接口与Arduino通信。这意味着我用一个模块就解决了显示和大部分输入问题极大简化了电路和编程。控制按钮虽然TM1638自带8个按键但为了实现一些特殊功能如总里程显示、全部清零并考虑到按键的可靠性和手感我额外增加了几个外置的轻触开关。TM1638的按键用于泳者选择、单个清零等高频操作外置按钮用于全局性、需谨慎操作的功能。电源采用9V直流电源适配器供电经过Arduino板载稳压芯片输出稳定的5V和3.3V为所有模块供电。务必确保电源功率足够特别是在所有数码管都点亮时。2.3 系统工作流程设计整个系统的工作流程可以概括为一个循环状态监控Arduino不断扫描TM1638模块的按键和外置按钮检测是否有操作指令。事件触发当RFID读卡器检测到卡片进入磁场并成功读取卡号后会触发一个“读卡事件”。身份匹配程序将读取到的卡号与预先存储在代码中的授权卡号列表进行比对。计数逻辑如果卡号匹配成功则找到对应的泳者将其圈数加1。同时根据预设的泳池长度如25米自动计算并更新该泳者的总游进距离。显示更新TM1638模块的数码管会实时更新当前选中泳者的圈数和里程LED指示灯可以用于显示状态如哪位泳者被选中。指令处理如果用户按下按钮则执行对应的清零、切换显示或全部重置功能。这个流程看似简单但实现时需要考虑防抖按键和RFID读取、防重复计数一次刷卡只计一圈等细节我们会在软件部分详细探讨。3. 硬件连接与电路搭建详解有了清晰的设计图接下来就是“搬砖”环节——硬件连接。正确的电路连接是项目成功的物理基础这里我会提供详细的接线表和注意事项。3.1 主要元件清单在开始焊接或使用面包板之前请准备好以下核心元件Arduino Uno R3 开发板 x1MFRC522 RFID读卡器模块 x1TM1638 按键显示模块 x1轻触开关6x6mm x4 用于外置功能按钮10kΩ 电阻 x4 用于按钮上拉RFID卡片或钥匙扣 xN 根据泳者数量建议使用防水卡片9V DC电源适配器 x1面包板、杜邦线公对公、公对母若干3.2 接线图与引脚定义为了避免接线错误强烈建议先在面包板上搭建测试电路。以下是各模块与Arduino Uno的引脚连接表MFRC522 RFID模块连接MFRC522引脚Arduino Uno引脚功能说明SDA (SS)Digital 10片选/从机选择可与其它SPI设备区分SCKDigital 13SPI时钟线MOSIDigital 11主机输出从机输入MISODigital 12主机输入从机输出IRQ不连接中断引脚本项目未使用GNDGND电源地RSTDigital 9复位引脚3.3V3.3V务必接3.3V接5V会烧毁模块注意MFRC522的工作电压是3.3V其IO引脚也兼容5V但电源引脚必须接3.3V。Arduino Uno的3.3V输出引脚可以提供约150mA电流足够驱动该模块。TM1638模块连接TM1638采用类似SPI但更简单的3线串行协议连接非常简洁。TM1638引脚Arduino Uno引脚功能说明VCC5V电源正极该模块需5V供电GNDGND电源地STB (CS)Digital 7片选/使能引脚低电平有效CLKDigital 8时钟引脚DIODigital 9数据输入/输出引脚外置功能按钮连接四个轻触开关一端分别接Arduino的数字引脚2, 3, 4, 5另一端通过一个10kΩ的上拉电阻接至5V。同时开关的这一端也直接连接到对应的数字引脚。这样当按钮未按下时引脚过上拉电阻保持高电平按下时引脚接地变为低电平。按钮1 - Digital 2 清零泳者1按钮2 - Digital 3 清零泳者2按钮3 - Digital 4 显示总里程按钮组合78- Digital 5 全部清零实际用一个按钮实操心得在焊接最终版电路时建议使用排针和排母来连接Arduino和各个模块方便日后调试和更换。对于外置按钮可以使用带导线的微动开关并将其用热缩管或防水盒封装以适应泳池边潮湿的环境。3.3 电源与布线注意事项共地所有模块的GND引脚必须连接到Arduino的GND形成共同的参考地这是电路正常工作的前提。电源去耦在Arduino的5V和GND之间靠近板子电源输入的地方可以并联一个100μF的电解电容和一个0.1μF的陶瓷电容用于滤除电源噪声提高系统稳定性尤其是在数码管动态扫描时。线缆整理使用不同颜色的杜邦线区分电源红色-5V/3.3V黑色-GND、数据线等可以极大减少接线错误。对于最终成品可以考虑制作一个简单的PCB或使用穿孔板来固定所有元件使设备更牢固可靠。4. 软件实现从RFID识别到计数逻辑硬件是骨架软件才是灵魂。这一部分我们将深入代码看看如何让这些电子元件“活”起来协同工作。4.1 开发环境与库文件准备首先确保你安装了Arduino IDE。然后需要导入两个关键的库MFRC522库用于驱动RFID读卡器。可以在Arduino IDE的“库管理器”中搜索“MFRC522”并安装。TM1638库用于驱动显示模块。同样在库管理器中搜索“TM1638”通常名为“TM1638”或“Grove 4-Digit Display”的库可能包含所需功能但更推荐使用专为TM1638编写的库如TM1638plus。如果库管理器没有可以手动下载ZIP包通过“项目” - “加载库” - “添加.ZIP库”导入。4.2 核心代码结构解析完整的代码较长我将拆解其核心逻辑部分进行讲解。你可以在文章末尾找到完整代码的获取方式。第一步初始化与卡号绑定#include SPI.h #include MFRC522.h #include TM1638.h // 根据实际库名调整 // 定义引脚 #define RST_PIN 9 #define SS_PIN 10 #define STB_PIN 7 #define CLK_PIN 8 #define DIO_PIN 9 MFRC522 mfrc522(SS_PIN, RST_PIN); // 创建RFID对象 TM1638 module(CLK_PIN, DIO_PIN, STB_PIN); // 创建显示模块对象 // 预定义授权卡号需要替换成你自己的卡号 byte authorizedCard1[4] {0xEA, 0x51, 0x56, 0xD3}; // 示例卡号1 byte authorizedCard2[4] {0x82, 0xA5, 0x56, 0xD3}; // 示例卡号2 // 泳者数据 int swimmer1Laps 0; int swimmer2Laps 0; float poolLength 25.0; // 泳池长度单位米程序开头我们引入了必要的库定义了硬件连接的引脚并初始化了RFID和TM1638对象。最关键的是authorizedCard1和authorizedCard2这两个数组它们存储了合法RFID卡的UID唯一标识符。如何获取你自己卡片的UID这就需要用到我们提供的第二个小程序lector_tarjeta_RFID.ino。将它烧录到Arduino打开串口监视器波特率9600用卡片靠近读卡器串口就会打印出卡片的UID将其替换到上述数组中即可。第二步RFID读取与匹配逻辑在loop()函数中我们需要周期性地检查是否有新卡片。void loop() { // 检查是否有新卡片 if (mfrc522.PICC_IsNewCardPresent() mfrc522.PICC_ReadCardSerial()) { // 读取卡片的UID byte* uid mfrc522.uid.uidByte; byte uidSize mfrc522.uid.size; // 与授权卡号比对 if (compareUID(uid, authorizedCard1, uidSize)) { // 匹配卡1 swimmer1Laps; updateDisplay(1); // 更新显示为泳者1的数据 Serial.println(Swimmer 1 lap counted!); } else if (compareUID(uid, authorizedCard2, uidSize)) { // 匹配卡2 swimmer2Laps; updateDisplay(2); Serial.println(Swimmer 2 lap counted!); } else { // 未授权卡片 Serial.println(Unauthorized card!); module.setLEDs(0b11111111); // 让所有LED闪烁以示警告 delay(500); module.setLEDs(0); } // 停止本次读卡 mfrc522.PICC_HaltA(); } // ... 其他逻辑按钮扫描等 }compareUID是一个自定义函数用于逐字节比较两个UID数组是否完全相同。这里有一个重要的防误触设计PICC_IsNewCardPresent()和PICC_ReadCardSerial()的组合确保了只有一张新卡片被完整读取时才会触发计数避免了卡片在感应区晃动导致的重复计数。第三步按钮扫描与功能实现我们需要同时扫描TM1638的8个内置键和4个外置功能键。// 扫描TM1638按键 byte keys module.getButtons(); for (byte i 0; i 8; i) { if (keys (1 i)) { switch(i) { case 0: // 假设按键0对应选择泳者1 currentSwimmer 1; updateDisplay(1); break; case 1: // 按键1对应选择泳者2 currentSwimmer 2; updateDisplay(2); break; case 2: // 按键2清零当前显示的泳者 if (currentSwimmer 1) swimmer1Laps 0; else if (currentSwimmer 2) swimmer2Laps 0; updateDisplay(currentSwimmer); break; // ... 其他按键功能 } delay(200); // 简单按键防抖 } } // 扫描外置功能按钮使用digitalRead if (digitalRead(BUTTON_TOTAL_PIN) LOW) { delay(50); // 防抖延时 if (digitalRead(BUTTON_TOTAL_PIN) LOW) { showTotalMeters(); // 显示总里程函数 while(digitalRead(BUTTON_TOTAL_PIN) LOW); // 等待按键释放 } } // ... 其他外置按钮扫描TM1638的getButtons()函数可以一次性读取8个按键的状态非常高效。外置按钮则使用digitalRead配合软件防抖延时再检测来确保每次按下只触发一次动作。showTotalMeters()函数会计算(swimmer1Laps swimmer2Laps) * poolLength并显示在数码管上。第四步显示更新函数updateDisplay()函数负责将选定泳者的圈数和换算成的里程格式化后显示在TM1638的数码管上。void updateDisplay(int swimmer) { module.clearDisplay(); int laps (swimmer 1) ? swimmer1Laps : swimmer2Laps; float meters laps * poolLength; // 显示圈数例如 LAP: 25 module.setDisplayToString(LAP String(laps)); // 短暂延时后显示里程例如 M: 625.0 delay(1500); module.clearDisplay(); module.setDisplayToString(M: String(meters, 1)); // 保留一位小数 }TM1638库通常提供setDisplayToString或类似函数可以直接显示字符串。注意它可能不支持所有字符需要参考具体库的文档。5. 系统调试、优化与功能扩展代码烧录进去硬件连接完毕并不意味着项目结束。真正的挑战往往从第一次通电开始。这一章我们来解决那些实际运行中可能出现的问题并探讨如何让这个计数器变得更聪明、更好用。5.1 常见问题排查实录在调试过程中我遇到了以下几个典型问题希望你的搭建过程能因此更顺利RFID模块完全无反应现象卡片靠近无任何反应串口无输出。排查电压首先用万用表确认MFRC522的VCC引脚电压是否为3.3V。这是最常犯的错误。接线逐根检查SPI总线MISO, MOSI, SCK以及SS和RST引脚是否与代码定义、实际连接一致。特别是SCK13引脚和MISO12引脚容易接反。库文件确认安装了正确的MFRC522库并且代码中#include MFRC522.h和#include SPI.h都已包含。串口监视器打开Arduino IDE的串口监视器波特率设置为9600查看是否有初始化成功的提示信息。TM1638显示乱码或不亮现象数码管显示奇怪的符号或者完全不亮。排查电源TM1638需要5V供电检查VCC引脚电压。接线STB, CLK, DIO三根线是否接错。可以尝试交换CLK和DIO试试不同库或模块定义可能略有不同。库函数你使用的TM1638库的API可能与示例代码不同。仔细阅读库的说明确认初始化对象和显示函数的正确用法。例如有些库需要调用module.setupDisplay(true, 7)来设置亮度。刷卡计数延迟或重复计数现象刷一次卡计数增加多次或者刷卡后要等一会儿才计数。优化防重复触发确保在成功处理一次刷卡事件后调用mfrc522.PICC_HaltA()让卡片进入休眠状态。同时在主循环loop()中一次刷卡处理完成前不要再次进入读卡判断。引入“冷却时间”可以为每个泳者设置一个简单的防重入时间戳。记录上次刷卡成功的时间在接下来500毫秒内忽略同一张卡的刷卡事件。unsigned long lastSwipeTime[2] {0, 0}; // 泳者1和2的最后刷卡时间 const unsigned long cooldown 500; // 冷却时间500ms if (compareUID(uid, authorizedCard1, uidSize)) { if (millis() - lastSwipeTime[0] cooldown) { swimmer1Laps; lastSwipeTime[0] millis(); updateDisplay(1); } }按钮响应不灵或连击现象按一下按钮执行了多次操作。解决这就是按键抖动。除了代码中简单的delay(50)再检测更可靠的方法是使用状态机或millis()进行非阻塞式的防抖处理这能保证系统响应更及时。// 更健壮的按钮检测示例非阻塞 int buttonState HIGH; int lastButtonState HIGH; unsigned long lastDebounceTime 0; const unsigned long debounceDelay 50; int reading digitalRead(buttonPin); if (reading ! lastButtonState) { lastDebounceTime millis(); // 重置防抖计时器 } if ((millis() - lastDebounceTime) debounceDelay) { if (reading ! buttonState) { buttonState reading; if (buttonState LOW) { // 执行按钮按下动作 } } } lastButtonState reading;5.2 功能优化与扩展思路基础版本实现后你可以根据实际需求进行优化和扩展数据持久化目前数据断电即丢失。可以增加一个AT24Cxx系列的EEPROM模块在每次计数变化时将数据写入上电时读取。这样训练中途断电也不怕数据丢失。无线数据传输增加一个ESP-01s WiFi模块或HC-05蓝牙模块将游泳数据实时同步到手机APP或云端服务器便于长期统计和分析。多泳道/多泳者支持当前设计支持2名泳者。你可以通过扩展RFID读卡器数量使用不同的SS引脚或使用支持多标签识别的RFID读卡器来支持更多泳者。TM1638的显示可以通过模式切换来轮播不同泳者的数据。训练模式在代码中预设几种训练计划如“10组100米间歇游”设备可以通过LED或蜂鸣器提示休息间隔和组数完成情况。防水封装这是投入实际使用的关键。可以使用防水接线盒封装整个电子部分RFID读卡器天线部分用环氧树脂胶做防水处理外置按钮选用防水型开关。5.3 从原型到产品的思考如果你希望这个设备更加稳固耐用可以考虑以下步骤PCB设计使用Eagle或KiCad等软件将面包板电路转化为一块定制PCB能大大提高稳定性和美观度。电源管理改用更大容量的锂电池配合充电管理电路实现无线便携。结构设计使用3D打印或亚克力切割为设备制作一个专属外壳将屏幕、读卡区、按钮合理布局。这个项目从想法到实现贯穿了需求分析、硬件选型、电路搭建、软件编程、调试优化整个嵌入式开发流程。它不仅仅是一个计数器更是一个如何将具体问题转化为技术方案并一步步实现的完整案例。希望这份详细的拆解能帮助你成功复现它甚至激发灵感创造出更适合自己需求的智能训练装备。
基于Arduino与RFID的智能泳池计数器:嵌入式系统在运动训练中的应用实践
发布时间:2026/6/4 23:46:50
1. 项目概述一个为游泳训练量身定制的智能计数器如果你和我一样是个游泳爱好者或者正在指导游泳训练肯定遇到过这样的烦恼游了多少圈今天的目标完成了吗不同泳姿的练习量怎么分开统计靠脑子记游着游着就乱了用传统的手动计数器又得在池边频繁操作湿漉漉的手也不方便。几年前我在指导一个业余游泳队训练时就深受其扰。于是我决定动手做一个能自动识别泳者、精准计数并且操作直观的智能设备——这就是Pisciduino项目的由来。简单来说Pisciduino是一个基于Arduino和RFID技术的智能泳池计数器。它的核心功能是为每位泳者分配一张唯一的RFID卡当泳者触壁转身时用卡片靠近读卡器系统就会自动记录一圈通常是25米或50米。设备上配有独立的按钮可以方便地清零单个泳者的计数或者查看总里程。这听起来可能像是一个简单的“打卡”装置但在实际设计和实现过程中从硬件选型、电路连接、RFID数据处理到软件逻辑和防误触设计每一个环节都藏着不少门道。这个项目完美地诠释了嵌入式系统开发中“传感器数据采集与处理”这一核心环节通过微控制器将物理世界的动作刷卡转化为可管理的数据圈数、里程是物联网和智能硬件在运动科学领域一个非常接地气的应用。无论你是想复现一个实用的训练工具还是希望学习如何将Arduino、RFID模块、按钮和显示屏整合到一个完整的嵌入式系统中这个项目都能提供从原理到实操的完整路径。接下来我将从设计思路开始一步步拆解如何实现这个智能泳池计数器。2. 核心设计思路与硬件选型解析在动手焊接第一根线之前理清设计思路至关重要。这个项目的核心需求很明确自动识别不同泳者并对其游泳圈数进行可靠、便捷的计数与管理。围绕这个需求我拆解出了几个关键的设计决策点。2.1 为什么选择RFID而非其他传感器识别泳者的方式有很多比如红外对管、压力传感器、摄像头图像识别等。我最终选择RFID射频识别主要基于以下几个考量可靠性高泳池环境潮湿可能存在水雾、飞溅。RFID采用非接触式读取读卡器和卡片都可以做防水封装受环境影响小。红外对管则容易因水渍或雾气导致误触发或失效。识别唯一性每张RFID卡都有全球唯一的ID号完美对应一位泳者不存在混淆的可能。成本与复杂度平衡低频RFID模块如RC522价格低廉技术成熟Arduino社区支持完善开发难度远低于摄像头方案。用户体验好泳者只需在触壁时用佩戴的卡片轻松一晃无需精确对准操作自然快捷。这里我选用的是最常见的MFRC522 RFID读卡模块它支持ISO/IEC 14443 A类标准有效读取距离在几厘米非常适合这种需要主动、近距离触发的场景。2.2 主控与外围设备的搭配逻辑主控芯片选择了Arduino Uno R3。对于这个项目它的性能绰绰有余需要处理RFID读卡中断、多个按钮的扫描、四则运算圈数转里程以及驱动显示模块。其丰富的数字和模拟IO口为连接各类外设提供了便利。外围设备除了RFID读卡器还包括显示模块为了同时显示多位泳者的数据我选择了TM1638模块。这是一个“神器”级别的模块它集成了8位7段数码管、8个LED指示灯和8个独立按键的扫描电路通过简单的3线串行接口与Arduino通信。这意味着我用一个模块就解决了显示和大部分输入问题极大简化了电路和编程。控制按钮虽然TM1638自带8个按键但为了实现一些特殊功能如总里程显示、全部清零并考虑到按键的可靠性和手感我额外增加了几个外置的轻触开关。TM1638的按键用于泳者选择、单个清零等高频操作外置按钮用于全局性、需谨慎操作的功能。电源采用9V直流电源适配器供电经过Arduino板载稳压芯片输出稳定的5V和3.3V为所有模块供电。务必确保电源功率足够特别是在所有数码管都点亮时。2.3 系统工作流程设计整个系统的工作流程可以概括为一个循环状态监控Arduino不断扫描TM1638模块的按键和外置按钮检测是否有操作指令。事件触发当RFID读卡器检测到卡片进入磁场并成功读取卡号后会触发一个“读卡事件”。身份匹配程序将读取到的卡号与预先存储在代码中的授权卡号列表进行比对。计数逻辑如果卡号匹配成功则找到对应的泳者将其圈数加1。同时根据预设的泳池长度如25米自动计算并更新该泳者的总游进距离。显示更新TM1638模块的数码管会实时更新当前选中泳者的圈数和里程LED指示灯可以用于显示状态如哪位泳者被选中。指令处理如果用户按下按钮则执行对应的清零、切换显示或全部重置功能。这个流程看似简单但实现时需要考虑防抖按键和RFID读取、防重复计数一次刷卡只计一圈等细节我们会在软件部分详细探讨。3. 硬件连接与电路搭建详解有了清晰的设计图接下来就是“搬砖”环节——硬件连接。正确的电路连接是项目成功的物理基础这里我会提供详细的接线表和注意事项。3.1 主要元件清单在开始焊接或使用面包板之前请准备好以下核心元件Arduino Uno R3 开发板 x1MFRC522 RFID读卡器模块 x1TM1638 按键显示模块 x1轻触开关6x6mm x4 用于外置功能按钮10kΩ 电阻 x4 用于按钮上拉RFID卡片或钥匙扣 xN 根据泳者数量建议使用防水卡片9V DC电源适配器 x1面包板、杜邦线公对公、公对母若干3.2 接线图与引脚定义为了避免接线错误强烈建议先在面包板上搭建测试电路。以下是各模块与Arduino Uno的引脚连接表MFRC522 RFID模块连接MFRC522引脚Arduino Uno引脚功能说明SDA (SS)Digital 10片选/从机选择可与其它SPI设备区分SCKDigital 13SPI时钟线MOSIDigital 11主机输出从机输入MISODigital 12主机输入从机输出IRQ不连接中断引脚本项目未使用GNDGND电源地RSTDigital 9复位引脚3.3V3.3V务必接3.3V接5V会烧毁模块注意MFRC522的工作电压是3.3V其IO引脚也兼容5V但电源引脚必须接3.3V。Arduino Uno的3.3V输出引脚可以提供约150mA电流足够驱动该模块。TM1638模块连接TM1638采用类似SPI但更简单的3线串行协议连接非常简洁。TM1638引脚Arduino Uno引脚功能说明VCC5V电源正极该模块需5V供电GNDGND电源地STB (CS)Digital 7片选/使能引脚低电平有效CLKDigital 8时钟引脚DIODigital 9数据输入/输出引脚外置功能按钮连接四个轻触开关一端分别接Arduino的数字引脚2, 3, 4, 5另一端通过一个10kΩ的上拉电阻接至5V。同时开关的这一端也直接连接到对应的数字引脚。这样当按钮未按下时引脚过上拉电阻保持高电平按下时引脚接地变为低电平。按钮1 - Digital 2 清零泳者1按钮2 - Digital 3 清零泳者2按钮3 - Digital 4 显示总里程按钮组合78- Digital 5 全部清零实际用一个按钮实操心得在焊接最终版电路时建议使用排针和排母来连接Arduino和各个模块方便日后调试和更换。对于外置按钮可以使用带导线的微动开关并将其用热缩管或防水盒封装以适应泳池边潮湿的环境。3.3 电源与布线注意事项共地所有模块的GND引脚必须连接到Arduino的GND形成共同的参考地这是电路正常工作的前提。电源去耦在Arduino的5V和GND之间靠近板子电源输入的地方可以并联一个100μF的电解电容和一个0.1μF的陶瓷电容用于滤除电源噪声提高系统稳定性尤其是在数码管动态扫描时。线缆整理使用不同颜色的杜邦线区分电源红色-5V/3.3V黑色-GND、数据线等可以极大减少接线错误。对于最终成品可以考虑制作一个简单的PCB或使用穿孔板来固定所有元件使设备更牢固可靠。4. 软件实现从RFID识别到计数逻辑硬件是骨架软件才是灵魂。这一部分我们将深入代码看看如何让这些电子元件“活”起来协同工作。4.1 开发环境与库文件准备首先确保你安装了Arduino IDE。然后需要导入两个关键的库MFRC522库用于驱动RFID读卡器。可以在Arduino IDE的“库管理器”中搜索“MFRC522”并安装。TM1638库用于驱动显示模块。同样在库管理器中搜索“TM1638”通常名为“TM1638”或“Grove 4-Digit Display”的库可能包含所需功能但更推荐使用专为TM1638编写的库如TM1638plus。如果库管理器没有可以手动下载ZIP包通过“项目” - “加载库” - “添加.ZIP库”导入。4.2 核心代码结构解析完整的代码较长我将拆解其核心逻辑部分进行讲解。你可以在文章末尾找到完整代码的获取方式。第一步初始化与卡号绑定#include SPI.h #include MFRC522.h #include TM1638.h // 根据实际库名调整 // 定义引脚 #define RST_PIN 9 #define SS_PIN 10 #define STB_PIN 7 #define CLK_PIN 8 #define DIO_PIN 9 MFRC522 mfrc522(SS_PIN, RST_PIN); // 创建RFID对象 TM1638 module(CLK_PIN, DIO_PIN, STB_PIN); // 创建显示模块对象 // 预定义授权卡号需要替换成你自己的卡号 byte authorizedCard1[4] {0xEA, 0x51, 0x56, 0xD3}; // 示例卡号1 byte authorizedCard2[4] {0x82, 0xA5, 0x56, 0xD3}; // 示例卡号2 // 泳者数据 int swimmer1Laps 0; int swimmer2Laps 0; float poolLength 25.0; // 泳池长度单位米程序开头我们引入了必要的库定义了硬件连接的引脚并初始化了RFID和TM1638对象。最关键的是authorizedCard1和authorizedCard2这两个数组它们存储了合法RFID卡的UID唯一标识符。如何获取你自己卡片的UID这就需要用到我们提供的第二个小程序lector_tarjeta_RFID.ino。将它烧录到Arduino打开串口监视器波特率9600用卡片靠近读卡器串口就会打印出卡片的UID将其替换到上述数组中即可。第二步RFID读取与匹配逻辑在loop()函数中我们需要周期性地检查是否有新卡片。void loop() { // 检查是否有新卡片 if (mfrc522.PICC_IsNewCardPresent() mfrc522.PICC_ReadCardSerial()) { // 读取卡片的UID byte* uid mfrc522.uid.uidByte; byte uidSize mfrc522.uid.size; // 与授权卡号比对 if (compareUID(uid, authorizedCard1, uidSize)) { // 匹配卡1 swimmer1Laps; updateDisplay(1); // 更新显示为泳者1的数据 Serial.println(Swimmer 1 lap counted!); } else if (compareUID(uid, authorizedCard2, uidSize)) { // 匹配卡2 swimmer2Laps; updateDisplay(2); Serial.println(Swimmer 2 lap counted!); } else { // 未授权卡片 Serial.println(Unauthorized card!); module.setLEDs(0b11111111); // 让所有LED闪烁以示警告 delay(500); module.setLEDs(0); } // 停止本次读卡 mfrc522.PICC_HaltA(); } // ... 其他逻辑按钮扫描等 }compareUID是一个自定义函数用于逐字节比较两个UID数组是否完全相同。这里有一个重要的防误触设计PICC_IsNewCardPresent()和PICC_ReadCardSerial()的组合确保了只有一张新卡片被完整读取时才会触发计数避免了卡片在感应区晃动导致的重复计数。第三步按钮扫描与功能实现我们需要同时扫描TM1638的8个内置键和4个外置功能键。// 扫描TM1638按键 byte keys module.getButtons(); for (byte i 0; i 8; i) { if (keys (1 i)) { switch(i) { case 0: // 假设按键0对应选择泳者1 currentSwimmer 1; updateDisplay(1); break; case 1: // 按键1对应选择泳者2 currentSwimmer 2; updateDisplay(2); break; case 2: // 按键2清零当前显示的泳者 if (currentSwimmer 1) swimmer1Laps 0; else if (currentSwimmer 2) swimmer2Laps 0; updateDisplay(currentSwimmer); break; // ... 其他按键功能 } delay(200); // 简单按键防抖 } } // 扫描外置功能按钮使用digitalRead if (digitalRead(BUTTON_TOTAL_PIN) LOW) { delay(50); // 防抖延时 if (digitalRead(BUTTON_TOTAL_PIN) LOW) { showTotalMeters(); // 显示总里程函数 while(digitalRead(BUTTON_TOTAL_PIN) LOW); // 等待按键释放 } } // ... 其他外置按钮扫描TM1638的getButtons()函数可以一次性读取8个按键的状态非常高效。外置按钮则使用digitalRead配合软件防抖延时再检测来确保每次按下只触发一次动作。showTotalMeters()函数会计算(swimmer1Laps swimmer2Laps) * poolLength并显示在数码管上。第四步显示更新函数updateDisplay()函数负责将选定泳者的圈数和换算成的里程格式化后显示在TM1638的数码管上。void updateDisplay(int swimmer) { module.clearDisplay(); int laps (swimmer 1) ? swimmer1Laps : swimmer2Laps; float meters laps * poolLength; // 显示圈数例如 LAP: 25 module.setDisplayToString(LAP String(laps)); // 短暂延时后显示里程例如 M: 625.0 delay(1500); module.clearDisplay(); module.setDisplayToString(M: String(meters, 1)); // 保留一位小数 }TM1638库通常提供setDisplayToString或类似函数可以直接显示字符串。注意它可能不支持所有字符需要参考具体库的文档。5. 系统调试、优化与功能扩展代码烧录进去硬件连接完毕并不意味着项目结束。真正的挑战往往从第一次通电开始。这一章我们来解决那些实际运行中可能出现的问题并探讨如何让这个计数器变得更聪明、更好用。5.1 常见问题排查实录在调试过程中我遇到了以下几个典型问题希望你的搭建过程能因此更顺利RFID模块完全无反应现象卡片靠近无任何反应串口无输出。排查电压首先用万用表确认MFRC522的VCC引脚电压是否为3.3V。这是最常犯的错误。接线逐根检查SPI总线MISO, MOSI, SCK以及SS和RST引脚是否与代码定义、实际连接一致。特别是SCK13引脚和MISO12引脚容易接反。库文件确认安装了正确的MFRC522库并且代码中#include MFRC522.h和#include SPI.h都已包含。串口监视器打开Arduino IDE的串口监视器波特率设置为9600查看是否有初始化成功的提示信息。TM1638显示乱码或不亮现象数码管显示奇怪的符号或者完全不亮。排查电源TM1638需要5V供电检查VCC引脚电压。接线STB, CLK, DIO三根线是否接错。可以尝试交换CLK和DIO试试不同库或模块定义可能略有不同。库函数你使用的TM1638库的API可能与示例代码不同。仔细阅读库的说明确认初始化对象和显示函数的正确用法。例如有些库需要调用module.setupDisplay(true, 7)来设置亮度。刷卡计数延迟或重复计数现象刷一次卡计数增加多次或者刷卡后要等一会儿才计数。优化防重复触发确保在成功处理一次刷卡事件后调用mfrc522.PICC_HaltA()让卡片进入休眠状态。同时在主循环loop()中一次刷卡处理完成前不要再次进入读卡判断。引入“冷却时间”可以为每个泳者设置一个简单的防重入时间戳。记录上次刷卡成功的时间在接下来500毫秒内忽略同一张卡的刷卡事件。unsigned long lastSwipeTime[2] {0, 0}; // 泳者1和2的最后刷卡时间 const unsigned long cooldown 500; // 冷却时间500ms if (compareUID(uid, authorizedCard1, uidSize)) { if (millis() - lastSwipeTime[0] cooldown) { swimmer1Laps; lastSwipeTime[0] millis(); updateDisplay(1); } }按钮响应不灵或连击现象按一下按钮执行了多次操作。解决这就是按键抖动。除了代码中简单的delay(50)再检测更可靠的方法是使用状态机或millis()进行非阻塞式的防抖处理这能保证系统响应更及时。// 更健壮的按钮检测示例非阻塞 int buttonState HIGH; int lastButtonState HIGH; unsigned long lastDebounceTime 0; const unsigned long debounceDelay 50; int reading digitalRead(buttonPin); if (reading ! lastButtonState) { lastDebounceTime millis(); // 重置防抖计时器 } if ((millis() - lastDebounceTime) debounceDelay) { if (reading ! buttonState) { buttonState reading; if (buttonState LOW) { // 执行按钮按下动作 } } } lastButtonState reading;5.2 功能优化与扩展思路基础版本实现后你可以根据实际需求进行优化和扩展数据持久化目前数据断电即丢失。可以增加一个AT24Cxx系列的EEPROM模块在每次计数变化时将数据写入上电时读取。这样训练中途断电也不怕数据丢失。无线数据传输增加一个ESP-01s WiFi模块或HC-05蓝牙模块将游泳数据实时同步到手机APP或云端服务器便于长期统计和分析。多泳道/多泳者支持当前设计支持2名泳者。你可以通过扩展RFID读卡器数量使用不同的SS引脚或使用支持多标签识别的RFID读卡器来支持更多泳者。TM1638的显示可以通过模式切换来轮播不同泳者的数据。训练模式在代码中预设几种训练计划如“10组100米间歇游”设备可以通过LED或蜂鸣器提示休息间隔和组数完成情况。防水封装这是投入实际使用的关键。可以使用防水接线盒封装整个电子部分RFID读卡器天线部分用环氧树脂胶做防水处理外置按钮选用防水型开关。5.3 从原型到产品的思考如果你希望这个设备更加稳固耐用可以考虑以下步骤PCB设计使用Eagle或KiCad等软件将面包板电路转化为一块定制PCB能大大提高稳定性和美观度。电源管理改用更大容量的锂电池配合充电管理电路实现无线便携。结构设计使用3D打印或亚克力切割为设备制作一个专属外壳将屏幕、读卡区、按钮合理布局。这个项目从想法到实现贯穿了需求分析、硬件选型、电路搭建、软件编程、调试优化整个嵌入式开发流程。它不仅仅是一个计数器更是一个如何将具体问题转化为技术方案并一步步实现的完整案例。希望这份详细的拆解能帮助你成功复现它甚至激发灵感创造出更适合自己需求的智能训练装备。