跳到主要内容

串行通信 (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 数据包


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

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

UART 连接


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 引脚图

ESP32-S3-Zero-Pinout

接线图

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 代码解析

  1. input(): 这是 Python 的内置函数。在 MicroPython 的 REPL 环境中,它会从标准输入(通常是连接电脑的 USB 串口)读取数据,直到检测到换行符。程序执行到这里会“阻塞”(暂停),直到用户发送了指令。

  2. 逻辑判断: 根据 command 的内容,控制 GPIO 输出高电平或低电平,并通过 print() 函数将反馈信息发送回电脑显示。

3.4 运行结果

  1. 运行代码。
  2. 在 Thonny 的 Shell(REPL)窗口中,输入 on 并按回车,LED 灯亮起,Shell 显示 "LED is ON"。
  3. 输入 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 引脚图

ESP32-S3-Zero-Pinout

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

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_statelast_button_state 来检测按钮动作,仅在状态改变时发送数据,避免重复发送。

4.3.3 接收端 (Board B)

  • uart.any(): 检查接收缓冲区中是否有等待读取的数据。如果有数据,返回大于 0 的整数。这是一个非阻塞的检查方法。
  • uart.read(n): 从缓冲区读取 n 个字节。如果不指定 n,则读取所有可用数据。 注意:MicroPython 的 read() 方法返回 bytes(字节串) 对象,因此在比较时需要使用 b'1' 而不是字符串 '1'

4.4 运行结果

  1. 将两份代码分别运行在两块 ESP32 开发板上。

    提示:要同时运行两块开发板,有两种方法:
    • 方法一(推荐):将代码分别保存为 main.py 上传到各自的开发板中。这样开发板上电后会自动运行程序。

      注意:如果 Thonny 处于连接状态,它可能会发送中断信号停止 main.py 程序。若遇到此情况,请在 Shell 中按 Ctrl+D 软复位,或关闭 Thonny 后重新上电。

    • 方法二:打开两个 Thonny 窗口,分别连接不同的 COM 口,然后分别点击运行按钮。

      注意:默认情况下 Thonny 只允许打开一个实例。如需打开多个窗口,请在菜单栏选择 工具 -> 选项 -> 常规,取消勾选 "只允许一个 Thonny 实例",然后重启 Thonny 即可。

  2. 按下发送端的按钮,接收端的 LED 应亮起,且发送端 Shell 显示 "Sent: 1",接收端 Shell 显示 "Received: 1"。

  3. 松开按钮,接收端的 LED 应熄灭。

5. 相关链接