在arduino IDE的开发环境中,如果使用的开发板不是arduino的开发平台,而是ESP32模组的开发板,那么在实际开发中由于ESP32的支持库与arduino不同,会使得我们在使用一些类,对象,函数等系统资源时会与arduino 标准的用法有很多区别。对于没有arduino硬件平台使用经验的人则不用于关注这个区别,直接按ESP32的规则去使用就行了。对于原来有arduino硬件平台上开发经历的人而言,则要注意这此区别。下面就ESP32平台中Serial这个串行通信对象的进行说明:
概述
ESP32的基于硬件的串行通信接口总共有三个。分别定义在了如下的GPIO口:
名称 | IO口的对应关系 | 系统默认的对象名 |
---|---|---|
UART0 | GPIO1 - TX0 GPIO3 - RX0 | Serial |
UART1 | GPIO10 - TX1 GPIO9 - RX1 | Serial1 |
UART2 | GPIO17 - TX2 GPIO16 - RX2 | Serial2 |
针对三个硬件串行通信接口,ESP32已预先对应的建立了三个对象Serial、Serial1、Serial2。因此我们才可以在开发时直接上来就写Serial.begin(9600) ; Serial.print(“Hello World!”);这样使用串口。
HardwareSerial库
三个串行通讯对象实际,都在HardwareSerial.h库中进行了定义。
该库具体路径:
C:\Users\loneve\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.5\cores\esp32\HardwareSerial.cpp
(每个人的安装路径在C:\Users\loneve部分可能不一样,请注意区分)。
【Heardware.h中的定义】
#ifndef HardwareSerial_h
#define HardwareSerial_h
#include <inttypes.h>
#include "Stream.h"
#include "esp32-hal.h"
class HardwareSerial: public Stream
{
public:
HardwareSerial(int uart_nr);
void begin (
unsigned long baud,
uint32_t config=SERIAL_8N1,
int8_t rxPin=-1,
int8_t txPin=-1,
bool invert=false,
unsigned long timeout_ms = 20000UL
);
...................................
protected:
int _uart_nr;
uart_t* _uart;
uint8_t _tx_pin;
uint8_t _rx_pin;
};
///
【Heardware.h中的定义】
...............
#include "pins_arduino.h"
#include "HardwareSerial.h"
#define RX1 9
#define TX1 10
#define RX2 16
#define TX2 17
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
HardwareSerial Serial(0);
HardwareSerial Serial1(1);
HardwareSerial Serial2(2);
#endif
HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL) {}
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms)
{
if(0 > _uart_nr || _uart_nr > 2) {
log_e("Serial number is invalid, please use 0, 1 or 2");
return;
}
if(_uart) {
end();
}
if(_uart_nr == 0 && rxPin < 0 && txPin < 0) {
rxPin = 3;
txPin = 1;
}
if(_uart_nr == 1 && rxPin < 0 && txPin < 0) {
rxPin = RX1;
txPin = TX1;
}
if(_uart_nr == 2 && rxPin < 0 && txPin < 0) {
rxPin = RX2;
txPin = TX2;
}
.....................
}
}
这本来是一件方便开发者使用的设置,但是我们在实际开发时会发现,Serial对象一般已被开发板定义用来进行USB通讯,以及用来给开发板烧录编译好的程序。而Serial1由于占用了GPIO9和GPIO10,而这两个IO口已被开发板用在了SPI Flash上。因此实际Serial1我们也是用不上的。只有Serial2可以用。
幸运的是,ESP32有个可以把硬件串口映射到其它任意端口的能力。
映射到其它端口
因此我们可以这样用:
HardwareSerial MySerial(1);
void setup() {
MySerial.begin(9600, SERIAL_8N1, 16, 17);
}
还是根据库文件源代码
HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL) {}
void begin (
unsigned long baud,
uint32_t config=SERIAL_8N1,
int8_t rxPin=-1,
int8_t txPin=-1,
bool invert=false,
unsigned long timeout_ms = 20000UL );
我们可以知道这两个函数所带参数的定义:
构造函数HardwareSerial(int uart_nr)
只有一个参数。设定是串行通行口几,因此上例中HardwareSerial MySerial(1);
是建立了一个指向了硬件UART1的串行通讯对象MySerial。由于自已为这个对象起了名字。因此在启动该端口时必须至少指定(波特率、串行帧格式、rx对应的GPIO序号、TX对应的GPIO序号)。
另两个invert 和timeout_ms两个参数取默认值就好,不用设定。那接下来,就可以愉快的使用自已定义的串行通讯对象了。
下面把两种不同的使用方式放在一起对比,便于理解:
使用系统默认的串口:
Serial.begin(9600);
Serial.print(“Hello World!”);
使用自定义串口:
HardwareSerial MySerial(1);
void setup() {
MySerial.begin(9600, SERIAL_8N1, 16, 17);
}
void loop{
uint8_t inputByte =MySerial.read();
}
这里有两点需要注意:
1、GPIO6,7,8,9,10,11已被用于SPI Flash,所以在定义串口TX和RX时请避开这些引脚。
2、由于ESP32开发板的GPIO36、39、34、35是纯输入口,因此最好不要把串口通讯IO口的TX定义在上面这四个口。RX则可以随便定义。
关于Serial接口,先定这些,之后如有必要再进一步深入探讨。