跳到主要内容

串行通信 (UART)

UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是一种通用、应用广泛的串行通信协议。它通过 TX(发送)RX(接收) 两根信号线,辅以一根 GND(地线),即可实现设备间的点对点数据传输。常见应用包括:与传感器/模块通信,开发板与电脑之间的数据收发(如打印日志、调试信息)等。

UART 具有以下特点:

  • 异步通信:发送和接收设备不需要共享时钟信号,而是通过预先约定的波特率来同步数据传输。
  • 串行传输:数据按位逐个发送,而不是并行传输多个位。
  • 全双工:可以同时进行发送和接收操作。

UART 是 异步 通信,意味着它没有共享的时钟线。为了让双方能同步数据,它们约定好使用相同波特率数据帧。每个 UART 数据帧包含以下部分:

  • 起始位 (Start Bit):1 位,总是 0,表示数据传输开始
  • 数据位 (Data Bits):通常 5-9 位,常用 8 位,包含实际要传输的数据
  • 奇偶校验位 (Parity Bit):可选,用于错误检测
  • 停止位 (Stop Bits):1-2 位,总是 1,表示数据传输结束

UART 数据包

UART 通信需要两根核心信号线:

  • TX (Transmit):发送数据线
  • RX (Receive):接收数据线
  • GND (地): 通信双方的“共同参考点”,确保电压信号能被正确解读。
  • 连接方式:两个设备之间需要交叉连接,即设备 A 的 TX 连接设备 B 的 RX,设备 A 的 RX 连接设备 B 的 TX。设备需要共地。

UART 连接

1. ESP32 中的 UART

ESP32 芯片通常有 3 个 UART 通道。每个 UART 控制器可以独立配置波特率、数据位长度、位顺序、停止位位数、奇偶校验位等参数。

在 Arduino,你可以用以下类来调用串口:

  • Serial0 (UART0): 这是默认的串口,常用于上传代码和打印调试。它在大多数开发板上都连接到了板载的 USB-to-Serial 芯片(如 CP2102 或 CH340),通过 USB 线连接电脑进行编程和打开“串口监视器”时,用的就是这个通道。
  • Serial1 (UART1): 额外的硬件串口,可以分配给几乎任意未被占用的 GPIO 引脚。
  • Serial2 (UART2): 同上,也是一个可自由分配引脚的额外硬件串口。
  • Serial: Serial 是最常用的串口对象,无论板子是通过串口芯片还是原生 USB 连接电脑,都可以使用 Serial 来和电脑的串口监视器通信。它会自动指向正确的底层串口。

拥有多个硬件串口是 ESP32 的一大优势。这意味着,主 Serial 口在用于连接电脑进行日志打印或调试的同时,ESP32 还能利用 Serial1Serial2 与其他串口设备(如 GPS 模块、激光雷达、指纹传感器等)独立通信,互不干扰。

2. 示例 1:通过串口监视器控制 LED

这个示例将演示 UART 的经典应用:通过电脑发送指令,控制 ESP32 的硬件。我们将通过 Arduino IDE 的串口监视器发送 "on" 或 "off" 字符串,来点亮或熄灭连接在 ESP32 上的 LED。

2.1 搭建电路

需要使用的器件有:

  • LED * 1
  • 330Ω 电阻 * 1
  • 面包板 * 1
  • 导线
  • ESP32 开发板

按照下面接线图连接电路:

ESP32-S3-Zero 引脚图

ESP32-S3-Zero-Pinout

接线图

2.2 代码

const int ledPin = 7;  // 定义 LED 连接的引脚

void setup() {
pinMode(ledPin, OUTPUT); // 设置 LED 引脚为输出模式

Serial.begin(115200); // 初始化串口通信,波特率 115200
while (!Serial) {}; // 等待串口准备好
}

void loop() {
if (Serial.available() > 0) { // 如果串口收到数据
String msg = Serial.readStringUntil('\n'); // 读取一行输入(以换行结束)
msg.trim(); // 去除首尾空格和换行符

if (msg == "on") {
// 如果输入为"on", 点亮 LED
digitalWrite(ledPin, HIGH);
Serial.println("LED 已打开");
} else if (msg == "off") {
// 如果输入为"off", 关闭 LED
digitalWrite(ledPin, LOW);
Serial.println("LED 已关闭");
} else {
Serial.println("请输入 'on' 或 'off'"); // 提示有效输入
}
}
}

2.3 代码解析

  1. Serial.begin(115200);: 初始化 Serial,并将波特率设置为 115200。串口监视器的波特率也需要设置为此值。
  2. Serial.available(): 检查串口接收缓冲区中是否有数据。如果大于 0,说明电脑发送了新消息。
  3. Serial.readStringUntil('\n'): 读取串口缓冲区中的字符,直到遇到换行符 \n 或超时为止,并将读取到的字符组合成一个 String 对象。这种方式适合接收由串口监视器发送的、以回车结尾的指令。
  4. msg.trim();: 当我们从串口监视器发送文本并按回车时,除了文本本身,通常还会发送一个的换行符(\n)或回车符(\r)。trim() 函数会移除字符串首尾的这些空白字符,确保 msg == "on" 这样的比较能够成功。
  5. if / else if: 根据清理过的 msg 字符串内容,执行相应的 digitalWrite() 操作来控制 LED,并向串口监视器打印反馈信息。

2.4 运行结果

  1. 打开串口监视器,将设置波特率为 115200,确保与代码中设置的波特率一致。

  2. 在串口监视器中,输入"on"并按回车,LED 灯会亮起;输入"off"并按回车,LED 灯会熄灭。

3. 示例 2:ESP32 之间串口通信

这个示例将展示如何使用 ESP32 的额外硬件串口(Serial1)实现两块 ESP32 开发板之间的通信。我们将用一块开发板连接的按钮,来控制另一块板子开发板连接的 LED。

3.1 搭建电路

需要使用的器件有:

  • LED * 1
  • 330Ω 电阻 * 1
  • 面包板 * 2
  • 按钮 * 1
  • 导线
  • ESP32 开发板 * 2

按照下面接线图连接电路:

ESP32-S3-Zero 引脚图

ESP32-S3-Zero-Pinout

接线图
发送端 (开发板 A)接收端 (开发板 B)说明
GPIO 11 (RX1)GPIO 2 (TX1)数据从 B 发送到 A
GPIO 12 (TX1)GPIO 1 (RX1)数据从 A 发送到 B
GNDGND必须共地,保证信号稳定

3.2 代码

发送端代码 (ESP32 开发板 A)

将此代码上传到连接了按钮的 ESP32 开发板。

#define RXD1 11  // 定义 UART1 的接收引脚(RX)
#define TXD1 12 // 定义 UART1 的发送引脚(TX)

const int buttonPin = 7; // 按钮连接的引脚
int lastButtonState = LOW; // 初始状态为未按下

void setup() {
// 启动默认串口,用于调试输出到电脑
Serial.begin(115200);

// 启动 Serial1,并指定 RX 和 TX 引脚,用于设备间通信
Serial1.begin(9600, SERIAL_8N1, RXD1, TXD1);

pinMode(buttonPin, INPUT_PULLUP); // 配置按钮引脚为上拉输入模式
Serial.println("Sender Ready. Press the button.");
}

void loop() {
int currentButtonState = digitalRead(buttonPin);

// 如果按钮状态发生变化
if (currentButtonState != lastButtonState) {
if (currentButtonState == HIGH) {
Serial1.write('0'); // 按钮松开时发送'0'
Serial.println("Sent: 0 (Button Released)");
} else {
Serial1.write('1'); // 按钮按下时发送'1'
Serial.println("Sent: 1 (Button Pressed)");
}

lastButtonState = currentButtonState; // 更新按钮状态
delay(50); // 简单的防抖动
}
}

接收端代码 (ESP32 开发板 B)

将此代码上传到连接了 LED 的 ESP32 开发板。

#define RXD1 1  // 定义 UART1 的接收引脚(RX)
#define TXD1 2 // 定义 UART1 的发送引脚(TX)

const int ledPin = 7;

void setup() {
// 启动默认串口,用于调试输出到电脑
Serial.begin(115200);

// 启动 Serial1,并指定 RX 和 TX 引脚,用于设备间通信
Serial1.begin(9600, SERIAL_8N1, RXD1, TXD1);

pinMode(ledPin, OUTPUT); // 配置 LED 引脚为输出模式

// while(!Serial){};
Serial.println("Receiver Ready. Waiting for commands...");
}

void loop() {
// 检查是否从 UART1 串口接收到数据
if (Serial1.available()) {
char command = Serial1.read(); // 读取一个字节(字符)

// 根据接收到的命令控制 LED
if (command == '1') {
// 接收到'1'时点亮 LED
digitalWrite(ledPin, HIGH);
Serial.println("Received: 1 -> LED ON");
} else if (command == '0') {
// 接收到'0'时熄灭 LED
digitalWrite(ledPin, LOW);
Serial.println("Received: 0 -> LED OFF");
}
}
}

3.3 代码解析

两份代码的共同点

  1. #define RXD1 ... / #define TXD1 ...: 使用宏定义来指定 Serial1 的 RX 和 TX 引脚。这让代码更易读,方便修改。
  2. Serial.begin(115200);: 两块板子都启动了默认的 Serial 口,这样它们可以分别连接到两台电脑(或同一个电脑的两个串口工具)上,打印调试信息,方便我们观察通信过程。
  3. Serial1.begin(9600, SERIAL_8N1, RXD1, TXD1);:
    • 这是本示例的核心。它初始化了 Serial1 通道。两个开发板用各自的 Serial1 通道通信。
    • 9600: 这是两块 ESP32 之间通信的波特率,必须保持一致
    • SERIAL_8N1: 这是标准的串口配置(8 数据位,无校验,1 停止位)。
      • 8: 8 位数据长度(可选 5、6、7 位)
      • N: 无校验(可选偶校验 E、奇校验 O)
      • 1: 位停止位(可选 2 位)
    • RXD1, TXD1: 将 Serial1 绑定到定义的 GPIO 引脚上。

发送端 (ESP32 开发板 A)

  • if (currentButtonState != lastButtonState): 这个判断用于检测按钮状态的变化(从按下到松开,或从松开到按下),确保只在状态改变时发送一次数据,而不是持续发送。
  • Serial1.write('1');: 当按钮被按下时(状态变为LOW),通过 Serial1 发送单个字符 '1' 给接收端。
  • Serial1.write('0');: 当按钮松开时(状态变为HIGH),发送 '0'

接收端 (ESP32 开发板 B)

  • if (Serial1.available()): 在主循环中,不断检查 Serial1 的接收缓冲区是否有数据。
  • char command = Serial1.read();: 如果有数据,读取一个字节(字符)并存入 command 变量。
  • if (command == '1'): 判断接收到的字符。如果是 '1',就点亮 LED;如果是 '0',就熄灭 LED。同时,通过自己的 Serial 口打印收到的信息和执行的动作,方便调试。

3.4 运行结果

  1. 将两份代码分别上传到两块 ESP32 开发板。
  2. 你可以用两根 USB 线将两块板子都连接到电脑上,并打开两个串口监视器窗口,分别对应两块板子的 COM 端口。
  3. 按下 发送端 (ESP32 开发板 A) 的按钮,你会观察到:
    • ESP32 A 的串口监视器打印出 "Sent: 1 (Button Pressed)"。
    • 接收端 (ESP32 开发板 B) 上的 LED 亮起
    • ESP32 B 的串口监视器打印出 "Received: 1 -> LED ON"。
  4. 松开按钮,你会观察到:
    • ESP32 A 的串口监视器打印出 "Sent: 0 (Button Released)"。
    • 接收端 (ESP32 开发板 B) 上的 LED 熄灭
    • ESP32 B 的串口监视器打印出 "Received: 0 -> LED OFF"。

4. 相关链接