0%

ESP32使用串口发送和接收数据

说明

承接上文,准备玩单片机肯定是以IO和其他的外设来玩的,这次准备试试串口。资料来自Arduino的官网。有一说一,ESP32是真的好用!
按照惯例,先介绍一下环境吧:
硬件:TTGO T-Display ESP32带1.14LCD的小开发板
软件:VSCode + PlatformIO IDE(其实就是Arduino环境)

几个常用的函数

Serial.print()

将数据作为人类可读的 ASCII 文本打印到串行端口。此命令可以有多种形式。每个数字都使用 ASCII 字符打印数字。浮数类似地打印为 ASCII 数字,默认为小数点两位。字节作为单个字符发送。字符和字符串按现在样发送。例如,

  • Serial.print(78); 输出: "78"
  • Serial.print(1.23456); 输出: "1.23"
  • Serial.print('N'); 输出: "N"
  • Serial.print("Hello world."); 输出: "Hello world."

可选的第二个参数指定要使用的基础(格式);允许的值为、、、对于浮点数字,此参数指定要使用的小数位数。例如,BIN(binary, or base 2)OCT(octal, or base 8)DEC(decimal, or base 10)HEX(hexadecimal, or base 16)

  • Serial.print(78, BIN); 输出: "1001110"
  • Serial.print(78, OCT); 输出: "116"
  • Serial.print(78, DEC); 输出: "78"
  • Serial.print(78, HEX); 输出: "4e"
  • Serial.print(1.23456, 0); 输出: "1"
  • Serial.print(1.23456, 2); 输出: "1.23"
  • Serial.print(1.23456, 4); 输出: "1.2345"

您可以通过用F()包装它们来传递基于闪存的字符串。例如:Serial.print()

Serial.print(F("Hello World"));
若要在不转换为字符表示形式的情况下发送数据,请使用Serial.write()。

语法

  • Serial.print(val);
  • Serial.print(val, format);

参数
Serial:串行端口对象。请参阅串行主页上每个主板的可用串行端口列表。要打印的值。允许的数据类型:任何数据类型。

返回值
print()返回写入的字节数,但读取该数字是可选的。数据类型: .size_t

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/*
Uses a for loop to print numbers in various formats.
*/
void setup() {
Serial.begin(9600); // open the serial port at 9600 bps:
}

void loop() {
// print labels
Serial.print("NO FORMAT"); // prints a label
Serial.print("\t"); // prints a tab

Serial.print("DEC");
Serial.print("\t");

Serial.print("HEX");
Serial.print("\t");

Serial.print("OCT");
Serial.print("\t");

Serial.print("BIN");
Serial.println(); // carriage return after the last label

for (int x = 0; x < 64; x++) { // only part of the ASCII chart, change to suit
// print it out in many formats:
Serial.print(x); // print as an ASCII-encoded decimal - same as "DEC"
Serial.print("\t\t"); // prints two tabs to accomodate the label lenght

Serial.print(x, DEC); // print as an ASCII-encoded decimal
Serial.print("\t"); // prints a tab

Serial.print(x, HEX); // print as an ASCII-encoded hexadecimal
Serial.print("\t"); // prints a tab

Serial.print(x, OCT); // print as an ASCII-encoded octal
Serial.print("\t"); // prints a tab

Serial.println(x, BIN); // print as an ASCII-encoded binary
// then adds the carriage return with "println"
delay(200); // delay 200 milliseconds
}
Serial.println(); // prints another carriage return
}

Serial.println()

描述
将数据打印为可人工可读的 ASCII 文本,后跟回车字符(ASCII 13 或”\r”)和一个新行字符(ASCII 10 或’\n’)。此命令采用与串行打印() 相同的形式。

语法
Serial.println(val);
Serial.println(val, format);

参数
Serial:串行端口对象。请参阅参考主页上每个主板的可用串行端口列表。
:要打印的值。允许的数据类型:任何数据类型。
:指定数字基数(用于积分数据类型)或小数位数(对于浮点类型)。valformat

返回值
println(); 返回写入的字节数,但读取该数字是可选的。数据类型: .size_t

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
Analog input reads an analog input on analog in 0, prints the value out.
created 24 March 2006
by Tom Igoe
*/

int analogValue = 0; // variable to hold the analog value

void setup() {
// open the serial port at 9600 bps:
Serial.begin(9600);
}

void loop() {
// read the analog input on pin 0:
analogValue = analogRead(0);

// print it out in many formats:
Serial.println(analogValue); // print as an ASCII-encoded decimal
Serial.println(analogValue, DEC); // print as an ASCII-encoded decimal
Serial.println(analogValue, HEX); // print as an ASCII-encoded hexadecimal
Serial.println(analogValue, OCT); // print as an ASCII-encoded octal
Serial.println(analogValue, BIN); // print as an ASCII-encoded binary

// delay 10 milliseconds before the next reading:
delay(10);
}

Serial.begin()

描述
为串行数据传输设置以比特/秒(波特)为单位的数据速率。要与串行监视器通信,请确保使用屏幕右下角菜单中列出的波特率之一。但是,您可以指定其他速率 - 例如,通过引脚 0 和 1 与需要特定波特速率的组件进行通信。

可选的第二个参数配置数据、奇偶校验和停止位。默认值为 8 个数据位,无奇偶校验,一个停止位。

语法
Serial.begin(speed);
Serial.begin(speed, config);

参数
Serial:串行端口对象。请参阅串行主页上每个主板的可用串行端口列表。
:以比特/秒(波特为单位)。允许的数据类型: 。
:设置数据、奇偶校验和停止位。有效值为:
(默认值): 偶数奇偶
校验:奇偶校验
speedlongconfigSERIAL_5N1SERIAL_6N1SERIAL_7N1SERIAL_8N1SERIAL_5N2SERIAL_6N2SERIAL_7N2SERIAL_8N2SERIAL_5E1SERIAL_6E1SERIAL_7E1SERIAL_8E1SERIAL_5E2SERIAL_6E2SERIAL_7E2SERIAL_8E2SERIAL_5O1
SERIAL_6O1
SERIAL_7O1
SERIAL_8O1
SERIAL_5O2
SERIAL_6O2
SERIAL_7O2
SERIAL_8O2

返回值

示例代码

1
2
3
4
5
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}

void loop() {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Arduino Mega using all four of its Serial ports
// (Serial, Serial1, Serial2, Serial3),
// with different baud rates:

void setup() {
Serial.begin(9600);
Serial1.begin(38400);
Serial2.begin(19200);
Serial3.begin(4800);

Serial.println("Hello Computer");
Serial1.println("Hello Serial 1");
Serial2.println("Hello Serial 2");
Serial3.println("Hello Serial 3");
}
void loop() {}

Serial.End()

描述
禁用串行通信,允许将 RX 和 TX 引脚用于常规输入和输出。要重新启用串行通信,请调用Serial.begin()

语法
Serial.end();

参数
Serial:串行端口对象。请参阅串行主页上每个主板的可用串行端口列表。

返回值

Serial.available()

描述
获取可用于从串行端口读取的字节数(字符)。这是已到达并存储在串行接收缓冲区(包含 64 个字节)中的数据。
Serial.available()从 Stream 实用程序类继承。

语法
Serial.available();

参数
Serial:串行端口对象。请参阅参考主页上每个主板的可用串行端口列表。

返回值
可供读取的字节数。

示例代码
以下代码返回通过串行端口接收的字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int incomingByte = 0; // for incoming serial data

void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}

void loop() {
// reply only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();

// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
}

示例2:此代码将板子的一个串行端口中接收的数据发送到另一个。例如,这可用于通过 Arduino 板将串行设备连接到计算机。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void setup() {
Serial.begin(9600);
Serial1.begin(9600);
}

void loop() {
// read from port 0, send to port 1:
if (Serial.available()) {
int inByte = Serial.read();
Serial1.print(inByte, DEC);
}
// read from port 1, send to port 0:
if (Serial1.available()) {
int inByte = Serial1.read();
Serial.print(inByte, DEC);
}
}

Serial.read()

描述
读取传入串行数据。
Serial.read()从 Stream 实用程序类继承。

语法
Serial.read();

参数
Serial:串行端口对象。请参阅串行主页上每个主板的可用串行端口列表。

返回值
传入串行数据的第一个字节可用(如果没有数据可用,或 -1)。数据类型: .int

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int incomingByte = 0; // for incoming serial data

void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}

void loop() {
// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();

// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
}

Serial.peek()

描述
返回传入串行数据的下一个字节(字符),而不将其从内部串行缓冲区中删除。

Serial.peek()从 Stream 实用程序类继承。

语法
Serial.peek();

参数
Serial:串行端口对象。请参阅串行主页上每个主板的可用串行端口列表。

返回值
传入串行数据的第一个字节可用(如果没有数据可用,或 -1)。数据类型:.int

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
char incomingByte; // for incoming serial data

void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}

void loop() {
// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.peek();

// say what you got:
# Serial.print("I received: ");
Serial.printf("%c", incomingByte);
}
}

一些注意点

最大的一个貌似是serialEvent()这个串口的时间,他本来是串口在接收数据之后会自动调用的,但是我尝试了好几次,但是都没有成功,因此在我的库函数里面我是会将其屏蔽的,即默认是不能用的,因为这个Arduino的库放到ESP32中也不一定的完全移植好了。下面给了一个线程来接收数据的方法,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include "uart.h"

String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete

// 监听串口收到数据的事件
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inputString += inChar;
if (inChar == '\n') { //注意是以换行符为标志位的
stringComplete = true;
}
}
}

// 准备串口接收数据的线程函数
// 示例:
// xTaskCreate(
// serialGetdata, /* Task function. */
// "Task_SerialGetdata", /* String with name of task. */
// 10000, /* Stack size in bytes. */
// NULL, /* Parameter passed as input of the task */
// 1, /* Priority of the task. */
// NULL); /* Task handle. */
void serialGetdata(void *uartPtr) {
while(1){
serialEvent();
if (stringComplete) {
Serial.println(inputString);
// clear the string:
inputString = "";
stringComplete = false;
}
// vTaskDelay(500 / portTICK_PERIOD_MS);
}
}

总结

我们都只是库函数的使用者,但是库函数的完善一直是任重而道远,因此发现问题也是一个很有意义的一件事儿。能够分享也是非常开心的。

-------------本文结束感谢您的阅读-------------