串行通信 (UART)
本节介绍通用异步收发器 (UART) 的基本概念,并演示如何使用 MicroPython 控制 ESP32 的 UART 功能实现设备间的通信。
1. 什么是 UART?
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器) 是一种硬件接口电路,用于实现异步串行通信。常见应用包括:与传感器/模块通信,开发板与电脑之间的数据收发(如打印日志、调试信息)等。
基于 UART 的串行通信具有以下特点:
- 异步通信:发送和接收设备不需要共享时钟信号,而是通过预先约定的波特率来同步数据传输。
- 串行传输:数据按位逐个发送,而不是并行传输多个位。
- 全双工:可以同时进行发送和接收操作。
UART 是 异步 通信,意味着它没有共享的时钟线。为了实现数据的正确收发,通信双方必须约定使用相同的 波特率 和 数据帧格式。
波特率 (Baud Rate) 表示每秒钟传输的数据位数(bps,bits per second)。通信双方必须使用相同的波特率才能正确传输数据。常见的波特率有 9600 和 115200。
每个 UART 数据帧 包含以下部分:
- 起始位 (Start Bit):1 位,总是 0,表示数据传输开始
- 数据位 (Data Bits):通常 5-9 位,常用 8 位,包含实际要传输的数据
- 奇偶校验位 (Parity Bit):可选,用于错误检测
- 停止位 (Stop Bits):1-2 位,总是 1,表示数据传输结束
UART 通信需要两根核心信号线:
- TX (Transmit):发送数据线
- RX (Receive):接收数据线
- GND (地): 通信双方的“共同参考点”,确保电压信号能被正确解读。
- 连接方式:两个设备之间需要交叉连接,即设备 A 的 TX 连接设备 B 的 RX,设备 A 的 RX 连接设备 B 的 TX。此外,两个设备必须共地(连接 GND),以确保信号电平有稳定的参考点。
2. ESP32 与 MicroPython 中的 UART
ESP32 芯片通常集成了多个 UART 控制器(通常为 UART0, UART1, UART2)。在 MicroPython 中,这些功能通过 machine.UART 类进行封装和调用。
- UART0:通常用于 REPL(交互式解释器)控制台,即通过 USB 连接电脑时看到的输入输出界面。
- UART1 / UART2:可用于连接外部设备。
使用 machine.UART 可以轻松配置波特率、引脚分配等参数。
3. 示例 1:通过 REPL 控制 LED
本示例演示最基础的串行通信应用:通过电脑发送指令控制 ESP32。在 MicroPython 中,直接使用 REPL(Read-Eval-Print Loop)作为“串口监视器”。程序将等待用户输入 "on" 或 "off",从而控制 LED 的亮灭。
3.1 搭建电路
需要使用的器件有:
- LED * 1
- 330Ω 电阻 * 1
- 面包板 * 1
- 导线
- ESP32 开发板
按照下面接线图连接电路:
ESP32-S3-Zero 引脚图


3.2 代码
import sys
from machine import Pin
# 定义引脚
LED_PIN = 7
# 初始化引脚
led = Pin(LED_PIN, Pin.OUT)
print("System Ready. Please enter 'on' or 'off':")
while True:
# input() 函数会暂停程序运行,等待用户输入一行文本并按回车
# 这是通过 REPL 接收电脑指令的最简单方式
command = input()
if command == "on":
# 如果输入为 "on", 点亮 LED
led.value(1)
print("LED is ON")
elif command == "off":
# 如果输入为 "off", 关闭 LED
led.value(0)
print("LED is OFF")
else:
print("Unknown command. Please enter 'on' or 'off'.")
3.3 代码解析
-
input(): 这是 Python 的内置函数。在 MicroPython 的 REPL 环境中,它会从标准输入(通常是连接电脑的 USB 串口)读取数据,直到检测到换行符。程序执行到这里会“阻塞”(暂停),直到用户发送了指令。 -
逻辑判断: 根据
command的内容,控制 GPIO 输出高电平或低电平,并通过print()函数将反馈信息发送回电脑显示。
3.4 运行结果
- 运行代码。
- 在 Thonny 的 Shell(REPL)窗口中,输入
on并按回车,LED 灯亮起,Shell 显示 "LED is ON"。 - 输入
off并按回车,LED 灯熄灭,Shell 显示 "LED is OFF"。
4. 示例 2:ESP32 之间串口通信
本示例展示如何使用 ESP32 的硬件串口(UART1)实现两块 ESP32 开发板之间的通信。使用一块开发板(发送端)连接按钮,控制另一块开发板(接收端)上的 LED。
4.1 搭建电路
需要使用的器件有:
- LED * 1
- 330Ω 电阻 * 1
- 面包板 * 2
- 按钮 * 1
- 导线
- ESP32 开发板 * 2
按照下面接线图连接电路:
ESP32-S3-Zero 引脚图


| 发送端 (开发板 A) | 接收端 (开发板 B) | 说明 |
|---|---|---|
| GPIO 11 (RX) | GPIO 2 (TX) | 数据从 B 发送到 A |
| GPIO 12 (TX) | GPIO 1 (RX) | 数据从 A 发送到 B |
| GND | GND | 必须共地,保证信号稳定 |
4.2 代码
4.2.1 发送端代码 (ESP32 开发板 A)
将此代码保存并运行在连接了按钮的开发板上。
import time
from machine import Pin, UART
# 定义引脚
BUTTON_PIN = 7
TX_PIN = 12
RX_PIN = 11
# 配置 UART1
# baudrate=9600: 波特率
# tx=12, rx=11: 指定发送和接收引脚
uart = UART(1, baudrate=9600, tx=TX_PIN, rx=RX_PIN)
# 配置按钮引脚 (上拉输入)
button = Pin(BUTTON_PIN, Pin.IN, Pin.PULL_UP)
last_button_state = 1 # 初始状态 (上拉默认为 1)
print("Sender Ready. Press the button.")
while True:
current_button_state = button.value()
# 检测状态变化
if current_button_state != last_button_state:
if current_button_state == 0:
# 按钮按下 (低电平)
# write() 方法发送字节数据
uart.write('1')
print("Sent: 1 (Button Pressed)")
else:
# 按钮松开 (高电平)
uart.write('0')
print("Sent: 0 (Button Released)")
last_button_state = current_button_state
time.sleep_ms(50) # 简单的防抖动
4.2.2 接收端代码 (ESP32 开发板 B)
将此代码保存并运行在连接了 LED 的开发板上。
import time
from machine import Pin, UART
# 定义引脚
LED_PIN = 7
RX_PIN = 1
TX_PIN = 2
# 配置 UART1
# 注意:接收端的 RX 接发送端的 TX,接收端的 TX 接发送端的 RX
# 根据接线图:RX=1, TX=2
uart = UART(1, baudrate=9600, tx=TX_PIN, rx=RX_PIN)
# 配置 LED 引脚
led = Pin(LED_PIN, Pin.OUT)
print("Receiver Ready. Waiting for commands...")
while True:
# any() 返回接收缓冲区中的字符数,如果大于 0 表示有数据
if uart.any():
# read(1) 读取 1 个字节
command = uart.read(1)
# 注意:read() 返回的是 bytes 对象 (如 b'1')
if command == b'1':
led.value(1)
print("Received: 1 -> LED ON")
elif command == b'0':
led.value(0)
print("Received: 0 -> LED OFF")
time.sleep_ms(10) # 稍作延时,避免 CPU 占用过高
4.3 代码解析
4.3.1 machine.UART 类
UART(id, baudrate, tx, rx):id: UART 通道编号,通常使用 1 或 2(0 通常保留给 REPL)。baudrate: 波特率,通信双方必须一致(本例为 9600)。tx,rx: 指定用于发送和接收的 GPIO 引脚。
4.3.2 发送端 (Board A)
uart.write(data): 用于发送数据。data可以是字符串或字节串。例如uart.write('1')发送字符 '1'。- 状态检测:
代码通过比较
current_button_state和last_button_state来检测按钮动作,仅在状态改变时发送数据,避免重复发送。
4.3.3 接收端 (Board B)
uart.any(): 检查接收缓冲区中是否有等待读取的数据。如果有数据,返回大于 0 的整数。这是一个非阻塞的检查方法。uart.read(n): 从缓冲区读取n个字节。如果不指定n,则读取所有可用数据。 注意:MicroPython 的read()方法返回 bytes(字节串) 对象,因此在比较时需要使用b'1'而不是字符串'1'。
4.4 运行结果
-
将两份代码分别运行在两块 ESP32 开发板上。
提示:要同时运行两块开发板,有两种方法:-
方法一(推荐):将代码分别保存为
main.py上传到各自的开发板中。这样开发板上电后会自动运行程序。注意:如果 Thonny 处于连接状态,它可能会发送中断信号停止
main.py程序。若遇到此情况,请在 Shell 中按Ctrl+D软复位,或关闭 Thonny 后重新上电。 -
方法二:打开两个 Thonny 窗口,分别连接不同的 COM 口,然后分别点击运行按钮。
注意:默认情况下 Thonny 只允许打开一个实例。如需打开多个窗口,请在菜单栏选择 工具 -> 选项 -> 常规,取消勾选 "只允许一个 Thonny 实例",然后重启 Thonny 即可。
-
-
按下发送端的按钮,接收端的 LED 应亮起,且发送端 Shell 显示 "Sent: 1",接收端 Shell 显示 "Received: 1"。
-
松开按钮,接收端的 LED 应熄灭。