串口通信
单片机中最常用的通讯协议有 UART、I2C、SPI。我们已经学习了 I2C 和 SPI。这节课,我们来学习 UART,也就是串口通讯。
串口基本上是所有单片机中都具备的资源外设,使用它可实现程序下载,串口通信等。由于串口通信的简单方便,现如今越来越多的设备和模块支持串口通信功能,让开发工作变得越来越简单且高效。这节课我们来学习如何使用 MicroPython 控制 ESP32 的串口实现数据收发。
实验原理
要了解串口通信就要先了解串行通信和并行通信:
并行通信
就是说我们的数据字节用多条数据线同时开始发送,这种传输方式只适合短距离传输,这种传输方式使用较少,而且长距离传输成本高,所以只需要简单了解即可;串行通信
是将数据字节一位一位的形式在一条传输线上逐个的传输,只需要一条数据线就可以了。发送时,要把并行数据变成串行数据发送到线路上,接收时,再把串行数据变为并行数据。
而关于串行数据传输也分为了两种方式,异步串行通信和同步串行通信,一般同步串行方式使用较少,一般不会使用,不了解也没关系,而一定要了解的是异步串行通信方式。
异步通信
是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程,为使双方收发协调,要求发送和接收的设备的时钟尽可能一致。
异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时间间隔)是任意的,当每个字符的各位是以固定的时间传送的,即字符之间不一定有 位间隔
的整数倍关系,但同一字符内的各位之间的距离均为 位间隔
的整数倍。异步通信的一帧字符信息由 4 部分组成,如下图所示:
起始位,数据位,校验位还有就是停止位,由上图所示,一般我们也不需要使用校验位。但是串行通信偶尔也会使用校验位,校验位由名字就可以知道,就是说看你这帧数据有没有错误,在我们的串行通信中一般使用奇偶校验,数据位尾随的 1 位为奇偶校验位。奇校验时,数据中 1 的个数与校验位的和是奇数就为奇校验,反之就是偶校验,接收字符时,我们通过对 1 的个数的校验,若发现 1 的个数不一致,那么就说明数据传输过程中出现了错误。
UART 全称为通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),其工作原理是约定好通讯的波特率,然后将数据一位位地进行传输。
ESP32 有三个硬件 UART:UART0、UART1 和 UART2。它们每个都分配有默认的 GPIO,如下表:
UART0 | UART1 | UART2 | |
---|---|---|---|
TX | 1 | 10 | 17 |
RX | 3 | 9 | 16 |
UART0 用于下载和 REPL(交互式解释器) 调试,UART1 用于模块内部连接 FLASH,通常也不使用,因此可以使用 UART2 与外部串口设备进行通信。
硬件电路设计
物料清单(BOM 表):
材料名称 | 数量 |
---|---|
串口模块 | 1 |
杜邦线(跳线) | 若干 |
ESP32 的 RX2 引脚连串口模块的 TX,TX2 连 RX,让 ESP32 和 串口模块都连接电脑。
将材料按照下图相连:
软件程序设计
我们在之前一直都在使用 Serial
对象,在 Arduino IDE 的串口监视器中显示信息,其实这个 Serial
,就是在 HardwareSerial.h
头文件中定义好的,它提供了与硬件串口通信相关的类和函数。该头文件定义了 HardwareSerial
类,允许你使用硬件串口进行通信。
HardwareSerial
类是用于访问和控制硬件串口的主要类。你可以使用预定义好的 HardwareSerial
对象(Serial
、Serial1
、Serial2
分别对应了 UART0、UART1 和 UART2。)与特定的硬件串口进行通信。
下面是 HardwareSerial
类的常用函数:
begin()
:初始化硬件串口的通信。你需要在使用硬件串口之前调用该函数,并指定所需的波特率。available()
:检查是否有可用的串口数据可供读取。如果串口接收缓冲区中有数据,该函数将返回一个大于0的值。read()
:从串口接收缓冲区中读取一个字节的数据,并将其作为无符号字节返回。write()
:向串口发送数据。你可以使用该函数发送单个字节、字符数组或字符串。print()
和println()
:这些函数可用于将数据以文本形式发送到串口。你可以使用这些函数来发送数字、字符串、字符和其他数据类型的内容。
所以,我们就可以使用 Serial
与 Serial2
实现两个串口之间的信息交互,代码如下:
void setup() {
// 初始化串口通信波特率
Serial.begin(9600);
Serial2.begin(9600);
}
void loop() {
// 从串口监视器读取输入数据
if (Serial.available()) {
char data = Serial.read();
// 将数据发送到 UART2
Serial2.write(data);
}
// 从UART2读取输入数据
if (Serial2.available()) {
char data = Serial2.read();
// 将数据发送到 UART0
Serial.write(data);
}
}