1. 项目概述与核心思路最近在做一个需要两块单片机协同工作的小项目手头正好有两块 Arduino Nano Every就琢磨着怎么让它们俩“说上话”。最直接、最经典的方式莫过于使用 UART通用异步收发器通信了。这个项目听起来简单——让一块板子控制另一块板子的 LED 灯亮灭但麻雀虽小五脏俱全它几乎涵盖了嵌入式设备间有线串行通信的所有基础要点硬件连接、协议理解、数据收发逻辑以及常见的调试技巧。UART 可以说是嵌入式开发中最“古老”也最可靠的通信协议之一几乎所有的微控制器都配备了至少一个 UART 接口。它的魅力在于其简单性只需要两根数据线TX 和 RX和一个共地GND就能实现全双工的数据交换。对于 Arduino Nano Every 这类板子它甚至贴心地为我们预留了专门的硬件串口Serial1用于与其他设备通信而把与电脑通信的 USB 串口Serial独立开来这样我们在调试时就不会互相干扰了。这个教程的目标很明确通过 UART 协议实现从一块 Arduino Nano Every发送端向另一块 Arduino Nano Every接收端发送指令从而远程控制接收端板载 LED 的状态。在这个过程中我们会深入理解 UART 通信的配置要点、数据格式的处理以及如何构建一个稳定可靠的双机通信系统。无论你是刚接触 Arduino 的新手还是想巩固一下通信基础的老玩家跟着走一遍这个流程都能有不少收获。2. 硬件准备与电路连接解析工欲善其事必先利其器。在开始写代码之前确保手头的硬件齐全且连接正确是成功的第一步。这个项目对硬件要求非常友好都是些基础器件。2.1 所需材料清单你需要准备以下物品Arduino Nano Every x2: 本项目的主角。当然正如原理所述只要工作电压相同通常是5V你也可以用 Nano、Uno 或其他 5V Arduino 板子进行组合。但为了保持一致性我们使用两块相同的 Nano Every。迷你面包板 x2: 用于固定和连接两块开发板避免连接线松动。如果只有一块大面包板也可以。跳线杜邦线 x3: 至少需要三根公对公的跳线用于连接两块板子的 TX、RX 和 GND 引脚。USB 数据线 x2: 用于给两块板子供电和上传程序。在后续测试阶段发送端板子需要一直通过 USB 连接电脑以打开串口监视器而接收端板子在上传程序后可以仅通过 USB 供电或改用其他5V电源无需连接电脑的串口。注意电压一致性是铁律这是硬件连接中最重要的一条安全原则。UART 通信本质上是电平信号的传输。如果两块板子的工作电压不同例如一块是5V逻辑电平的 Nano Every另一块是3.3V逻辑电平的某些板卡那么高电压板子的 TX 引脚输出的信号可能会损坏低电压板子的 RX 引脚。务必确认你使用的所有 Arduino 板子的工作电压VCC相同。对于 Nano Every其 I/O 引脚为 5V 电平。2.2 电路连接原理与实操连接电路的核心规则只有一条交叉连接 TX 和 RX并共地。听起来简单但接反是新手最常犯的错误。连接步骤放置板子将两块 Arduino Nano Every 分别插入两块迷你面包板或一块大面包板的两侧确保它们之间有足够的空间进行连线。连接地线GND用一根跳线将板子A 的 GND 引脚连接到板子B 的 GND 引脚。这一步至关重要它为两个系统建立了共同的电压参考点“零电位”没有它信号高低电平的判断会错乱通信必然失败。通常你可以选择任意一个 GND 引脚进行连接。交叉连接数据线用第二根跳线将板子A 的 TX发送引脚连接到板子B 的 RX接收引脚。用第三根跳线将板子A 的 RX接收引脚连接到板子B 的 TX发送引脚。为什么是交叉连接这需要从通信双方的角色来理解。板子A要“说话”发送数据就需要使用它的“嘴巴”TX引脚。板子B要“听”接收数据就需要用它的“耳朵”RX引脚。因此A的“嘴巴”TX必须接到B的“耳朵”RX上。反之亦然当B要回复时它的“嘴巴”TX也要接到A的“耳朵”RX上。这就是经典的“交叉互联”法。Nano Every 的引脚注意Arduino Nano Every 有两个独立的硬件串口Serial这个串口通过 USB 转串口芯片连接到了 USB 接口专门用于与电脑的 Arduino IDE串口监视器通信。它的信号引脚在芯片内部连接我们通常不直接使用。Serial1这是我们用于与其他设备进行 UART 通信的串口。它的TX 引脚是 D0RX 引脚是 D1。在板子上它们通常也被标记为 “TX1” 和 “RX1”。因此在我们的连接中所说的“TX”和“RX”指的就是D0 (TX1)和D1 (RX1)这两个引脚。最终连接示意图文字描述板子A D0 (TX1) —— 板子B D1 (RX1)板子A D1 (RX1) —— 板子B D0 (TX1)板子A GND —— 板子B GND连接完成后硬件部分就准备好了。接下来我们分别对两块板子进行编程。3. 软件编程接收端配置详解我们将首先配置作为“接收端”的板子。它的任务是持续监听 UART 端口一旦收到特定指令字符 ‘1’ 或 ‘0’就控制自身的板载 LED 亮或灭。3.1 建立新项目与基础设置打开 Arduino IDE 或 Arduino Cloud Editor。将作为接收端的那块 Nano Every 通过 USB 线连接到电脑。在 IDE 的“工具”菜单中正确选择板卡类型Arduino Nano Every和对应的端口。创建一个新的项目可以命名为Nano_UART_Receiver。代码将从初始化设置开始。3.2 接收端代码逐行解析// Nano_UART_Receiver.ino void setup() { // 1. 初始化板载LED引脚为输出模式 pinMode(LED_BUILTIN, OUTPUT); // 初始状态设为熄灭对于Nano Every低电平点亮LED高电平熄灭这里需要实测确认通常先设为熄灭状态 digitalWrite(LED_BUILTIN, HIGH); // 假设高电平熄灭根据板子调整 // 2. 初始化与另一块Arduino板通信的UARTSerial1 // 波特率设置为9600这是最常用且稳定的速率发送和接收端必须严格一致 Serial1.begin(9600); // 3. 可选初始化与电脑的串口用于调试输出方便观察接收端状态 // Serial.begin(9600); // 如果需要调试可以取消这行注释 // Serial.println(Receiver Ready!); } void loop() { // 检查Serial1UART的接收缓冲区是否有数据到达 while (Serial1.available() 0) { // 读取一个字节的数据 char receivedChar Serial1.read(); // 可选将收到的字符回显到调试串口如果启用了Serial // Serial.print(Received: ); // Serial.println(receivedChar); // 根据收到的字符控制LED if (receivedChar 1) { digitalWrite(LED_BUILTIN, LOW); // 点亮LED // Serial.println(LED ON); // 调试信息 } else if (receivedChar 0) { digitalWrite(LED_BUILTIN, HIGH); // 熄灭LED // Serial.println(LED OFF); // 调试信息 } // 如果收到其他字符可以选择忽略或做其他处理 } // 循环结束继续监听 }关键点解析与实操心得Serial1.begin(9600)这行代码启动了硬件 UART 通信。9600是波特率代表每秒传输 9600 个比特。发送端和接收端的波特率必须完全相同否则接收到的将是乱码。9600 是入门首选你也可以尝试 115200 等更高速度但在长导线或干扰环境下低波特率更稳定。Serial1.available()这个函数返回接收缓冲区中可读的字节数。在while循环中使用它可以确保只要还有数据待读就会持续读取避免数据堆积在缓冲区。这是一种非常经典和可靠的数据读取模式。Serial1.read()从接收缓冲区读取一个字节一个字符。它返回的是整型ASCII码我们通常将其存储在char类型变量中以便进行字符比较。字符比较receivedChar ‘1’这是 UART 通信编程中一个至关重要的细节。通过串口发送的数字 “1”在传输时实际是其 ASCII 码值十进制 49。因此在接收端我们不能用数字1去比较而必须用字符’1’即 ASCII 码 49。混淆数字和字符是比较常见的错误来源。LED 极性不同型号的 Arduino 板载 LED 的连接方式可能不同。大部分板子如 Uno是 LED 阳极接引脚阴极接地所以HIGH点亮LOW熄灭。但有些板子如某些 Nano 变体可能相反。Nano Every 的板载 LED 连接在引脚D13上通过一个电阻到 LED 再到 VCC因此是低电平LOW点亮高电平HIGH熄灭。如果你上传代码后 LED 反应相反只需将代码中的LOW和HIGH对调即可。3.3 上传代码至接收端代码编写完成后点击“上传”按钮将程序烧录到作为接收端的那块 Nano Every 上。上传成功后这块板子就已经准备就绪开始监听来自D0TX1和D1RX1引脚上的 UART 数据了。重要提示接收端程序上传后这块板子可以并且建议断开与电脑 USB 的数据连接但必须保持供电。你可以将它连接到电脑的另一个 USB 口仅供电。使用一个手机充电器或移动电源通过 USB 线给它供电。通过面包板从发送端板子的5V和GND引脚取电如果发送端由 USB 供电且电流足够。这能让你摆脱对两台电脑的依赖使系统更简洁。4. 软件编程发送端配置详解接下来配置“发送端”板子。它的角色是“指挥官”负责从电脑的串口监视器读取我们输入的命令1 或 0然后将这个命令通过 UART 转发给接收端同时控制自身的 LED 作为状态指示。4.1 发送端代码逻辑构建将发送端 Nano Every 连接到电脑可能需要更换 IDE 中的端口。新建一个项目命名为Nano_UART_Transmitter。发送端的逻辑比接收端稍复杂一点因为它需要同时处理两个串口Serial与电脑对话和Serial1与接收端板子对话。// Nano_UART_Transmitter.ino void setup() { // 1. 初始化板载LED pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); // 初始状态熄灭 // 2. 初始化与电脑通信的串口用于接收我们的指令 Serial.begin(9600); // 波特率通常也设为9600与串口监视器设置一致即可 while (!Serial) { ; // 等待串口连接。对于某些板子如Leonardo是必要的 } Serial.println(Transmitter Ready! Enter 1 to turn ON LED, 0 to turn OFF.); // 3. 初始化与接收端板子通信的UARTSerial1 Serial1.begin(9600); // 波特率必须与接收端设置的完全相同 } void loop() { // 检查电脑的串口监视器是否有数据输入 if (Serial.available() 0) { // 读取从串口监视器输入的一个字符 char commandFromPC Serial.read(); // 处理输入的命令 if (commandFromPC 1) { // 发送字符1给接收端板子 Serial1.println(1); // 使用println会自动在末尾发送换行符接收端需注意处理 // 也可以使用 Serial1.write(1) 或 Serial1.print(1)它们只发送字符本身 // 控制本机LED亮起作为视觉反馈 digitalWrite(LED_BUILTIN, LOW); // 点亮 Serial.println(Command Sent: ON (1)); } else if (commandFromPC 0) { // 发送字符0给接收端板子 Serial1.println(0); // 控制本机LED熄灭 digitalWrite(LED_BUILTIN, HIGH); // 熄灭 Serial.println(Command Sent: OFF (0)); } else { // 如果输入的不是1或0提示用户 Serial.println(Invalid command. Please enter 1 or 0.); } } // 短暂延迟避免过于频繁地检查串口非必需 delay(10); }关键点解析与实操心得双串口初始化发送端同时使用了Serial.begin(9600)和Serial1.begin(9600)。Serial用于人机交互我们通过它发命令Serial1用于机机交互向接收端发命令。两个串口的波特率可以独立设置但Serial1的波特率必须与接收端的Serial1波特率严格一致。Serial.println()vsSerial1.write()在向接收端发送数据时我使用了Serial1.println(‘1’)。println()函数会在发送的字符后面自动加上“换行符”\r\n。这在某些情况下是方便的但接收端如果使用Serial1.read()逐字节读取就会先读到’1’然后读到\r和\n。如果接收端代码只判断’1’和’0’那么额外的换行符会被while (Serial1.available())循环读取并忽略不影响结果。更精确的做法是使用Serial1.write(‘1’)它只发送字符本身没有任何附加内容。两种方式都可以但你需要知道它们的区别。本地反馈发送端在转发命令的同时也控制着自己的板载 LED。这提供了一个非常直观的本地确认让你知道命令确实被发送端板子接收并处理了。同时通过Serial.println()在电脑串口监视器上打印状态信息是调试过程中不可或缺的手段。错误处理代码中加入了else分支来处理非 ‘1’ 非 ‘0’ 的输入给用户一个友好的提示。在实际项目中这种鲁棒性设计很重要。4.2 上传代码至发送端完成代码后将其上传到作为发送端的那块 Nano Every。上传成功后这块板子需要保持通过 USB 与电脑连接因为我们需要通过 Arduino IDE 的串口监视器与它交互。5. 系统联调与功能测试所有硬件连接妥当两块板子的程序也各就各位现在到了最激动人心的测试环节。5.1 测试步骤确保供电接收端板子必须通电已连接电源。发送端板子通过 USB 连接电脑既供电也通信。打开串口监视器在 Arduino IDE 中确保端口选择的是发送端板子所在的端口然后点击右上角的“串口监视器”图标。配置串口监视器在右下角设置波特率为9600与代码中Serial.begin(9600)一致。确保行尾结束符设置为“没有结束符”因为我们的代码是逐字符读取Serial.read()。如果设置为“新行”那么当你按下回车时发送的实际上是’1’\r\n同样会被我们的if语句正确识别出’1’但后续的\r和\n会被当作无效命令处理。发送指令在串口监视器顶部的输入框中输入数字1然后点击“发送”或按回车键。观察现象你应该会立即看到发送端板子的 LED 亮起同时串口监视器里会打印出“Command Sent: ON (1)”。紧接着接收端板子的 LED 也应该亮起。在输入框中输入数字0并发送。发送端和接收端的 LED 应同时熄灭串口监视器打印“Command Sent: OFF (0)”。如果一切顺利恭喜你你已经成功建立了一个完整的双机 UART 通信系统。5.2 深入测试与思考完成基本功能后可以尝试一些变化来加深理解改变波特率尝试将发送端和接收端代码中的Serial1.begin()波特率都改为115200重新上传并测试。通信仍然正常吗应该正常。如果只改其中一端的波特率会怎样通信失败收到乱码。发送更多指令修改代码让发送端可以接收 ‘a’, ‘b’, ‘c’ 等字符并让接收端执行不同的动作例如用 ‘a’ 控制另一个引脚上的 LED用 ‘b’ 让蜂鸣器响一声。这可以扩展为简单的远程控制协议。加入反馈回路让接收端在收到指令并执行后通过它的Serial1发送一个确认字符如 ‘K’回给发送端。发送端在loop中增加监听Serial1的代码收到 ‘K’ 后在电脑串口打印“接收端已确认”。这就实现了简单的双向通信全双工。6. 故障排查与常见问题在实际操作中难免会遇到一些问题。下面是一个常见问题排查清单你可以像查字典一样对照检查。现象可能原因排查步骤与解决方案两个LED均无反应1. 供电问题。2. 发送端未正确连接到电脑串口。3. 代码未上传成功。1. 检查所有板子的电源指示灯是否亮起。2. 在IDE中确认选择的端口是发送端板子重新打开串口监视器。3. 重新编译并上传代码观察上传过程中的提示信息。发送端LED有反应但接收端LED无反应1. UART连线错误TX/RX未交叉。2. 接收端未供电。3. 接收端代码波特率与发送端不一致。4. GND未共地。1.这是最常见的原因仔细检查连线A.TX - B.RX, A.RX - B.TX。2. 确保接收端板子的电源指示灯亮着。3. 检查两端代码中Serial1.begin()的波特率数值是否完全相同。4. 用万用表蜂鸣档或电阻档检查两块板子的GND引脚是否确实导通。接收端LED状态与指令相反LED引脚电平逻辑弄反。修改接收端代码中digitalWrite(LED_BUILTIN, LOW/HIGH)的顺序。如果LOW是亮就换成HIGH亮。串口监视器显示乱码或无法打开1. 串口监视器波特率设置错误。2. 端口被其他程序占用。3. USB线或驱动问题。1. 确保串口监视器右下角波特率设置为9600与发送端Serial.begin(9600)一致。2. 关闭其他可能占用串口的软件如串口助手、蓝牙工具等。3. 尝试拔插USB线或更换一个USB口重启Arduino IDE。发送指令后接收端LED闪烁一下或不稳定1. 接触不良。2. 电源干扰或功率不足。3. 代码中读取逻辑有误如误读了换行符。1. 按压所有跳线接头和板子引脚确保接触牢固。使用质量好的面包板和跳线。2. 尝试用独立的电源如充电宝给接收端供电避免从发送端板子取电导致压降。3. 在接收端代码中将收到的每一个字符都打印到Serial监视器如果它连着电脑看看究竟收到了什么。确保比较的是字符’1’而不是数字1。上传代码时出错1. 板卡类型或端口选错。2. 正在使用Serial1的引脚D0 D1。1. 在“工具”菜单中仔细核对板卡和端口。2.上传代码时最好暂时断开与另一块板子连接的 TX/RX 线尤其是RX线因为编程时芯片的这两个引脚会被IDE占用外部连接可能会干扰编程信号。上传成功后再接回去。我的个人调试心得分而治之永远不要试图一次性调试整个系统。先单独测试发送端上传代码后打开串口监视器输入指令只看发送端自己的LED和打印信息是否正常。再单独测试接收端可以写一个简单的测试程序让接收端通过Serial1.println(“Hello”)循环发送数据然后用一个 USB 转 TTL 串口模块如 CH340、FT232连接电脑在串口助手软件里看能否收到数据。确保每一部分独立工作时都是好的。利用打印信息在关键位置如收到数据时、执行动作前添加Serial.println()语句输出状态是嵌入式调试的“终极武器”。对于接收端如果它不方便连接电脑Serial你可以暂时把调试信息也通过Serial1发回给发送端再由发送端的Serial打印到电脑上形成一个调试回路。检查电压手边备一个万用表。当通信异常时测量一下 TX 引脚在发送数据时的电压变化相对于共地的 GND应该能看到高低电平的跳变。如果没有说明发送端可能没工作如果有但接收端没反应可能是连线问题或接收端配置问题。这个通过 UART 连接两块 Arduino Nano Every 的项目虽然功能简单但它像一把钥匙为你打开了嵌入式设备间通信的大门。理解了硬件上的交叉连接与共地原则掌握了软件上波特率匹配、字符数据读写和双串口管理你就已经掌握了 UART 通信最核心的骨架。在此基础上你可以轻松地扩展数据传输的内容从控制 LED 到发送传感器数据、文本命令增加更多的节点一主多从甚至实现更复杂的通信协议。下次当你需要让两个单片机“对话”时不妨就从这两根线TX RX和一个共地开始吧。