串口通信
串口通信(Serial Communication)是 Arduino 与电脑、其他微控制器或外部设备交互的重要方式。本章将详细介绍串口通信的原理和应用。
什么是串口通信
串口通信是一种逐位传输数据的通信方式,数据一位一位顺序传送。Arduino 使用 UART(通用异步收发传输器)实现串口通信。
串口通信参数
配置串口通信时需要指定以下参数:
| 参数 | 说明 | 常用值 |
|---|---|---|
| 波特率 | 每秒传输的比特数 | 9600, 115200 |
| 数据位 | 每个数据包的位数 | 8 位 |
| 停止位 | 数据包结束标志 | 1 位 |
| 校验位 | 错误检测 | 无 |
Arduino 串口引脚
- D0 (RX):接收数据
- D1 (TX):发送数据
注意
使用串口通信时,D0 和 D1 不能用于其他功能。上传程序时也需要断开这两脚的外部连接。
基础串口操作
初始化串口
在 setup() 中使用 Serial.begin() 初始化串口:
void setup() {
Serial.begin(9600); // 设置波特率为 9600
}
发送数据
发送字符串
Serial.print("Hello World"); // 不换行
Serial.println("Hello World"); // 换行
发送变量值
int count = 42;
float temp = 23.5;
Serial.print("计数: ");
Serial.println(count);
Serial.print("温度: ");
Serial.print(temp);
Serial.println(" °C");
格式化输出
int value = 255;
Serial.print("十进制: ");
Serial.println(value, DEC); // 255
Serial.print("十六进制: ");
Serial.println(value, HEX); // ff
Serial.print("二进制: ");
Serial.println(value, BIN); // 11111111
Serial.print("八进制: ");
Serial.println(value, OCT); // 377
完整发送示例
void setup() {
Serial.begin(9600);
}
void loop() {
static int counter = 0;
counter++;
// 多种输出方式
Serial.print("计数: ");
Serial.print(counter);
Serial.print(" | 时间: ");
Serial.print(millis() / 1000);
Serial.println(" 秒");
delay(1000);
}
接收数据
检查可用数据
void loop() {
if (Serial.available() > 0) { // 检查是否有数据可读
// 有数据到达,进行读取
}
}
读取数据
void loop() {
if (Serial.available() > 0) {
char incomingByte = Serial.read(); // 读取一个字节
Serial.print("收到: ");
Serial.println(incomingByte);
}
}
读取字符串
String inputString = "";
bool stringComplete = false;
void setup() {
Serial.begin(9600);
inputString.reserve(200); // 预分配内存,提高性能
}
void loop() {
if (stringComplete) {
Serial.print("收到命令: ");
Serial.println(inputString);
// 处理命令
processCommand(inputString);
// 清空字符串
inputString = "";
stringComplete = false;
}
}
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
if (inChar == '\n') {
stringComplete = true;
} else {
inputString += inChar;
}
}
}
void processCommand(String cmd) {
cmd.trim(); // 去除首尾空白
cmd.toLowerCase(); // 转为小写
if (cmd == "on") {
Serial.println("开启设备");
} else if (cmd == "off") {
Serial.println("关闭设备");
} else {
Serial.println("未知命令");
}
}
串口调试技巧
调试输出宏
// 定义调试开关
#define DEBUG 1
#if DEBUG
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif
void setup() {
Serial.begin(9600);
DEBUG_PRINTLN("调试模式已启动");
}
void loop() {
int sensorValue = analogRead(A0);
DEBUG_PRINT("传感器值: ");
DEBUG_PRINTLN(sensorValue);
delay(1000);
}
数据可视化
使用串口绘图器(Serial Plotter)可视化数据:
void setup() {
Serial.begin(9600);
}
void loop() {
int sensor1 = analogRead(A0);
int sensor2 = analogRead(A1);
// 格式:值1,值2(用逗号分隔)
Serial.print(sensor1);
Serial.print(",");
Serial.println(sensor2);
delay(50);
}
在 Arduino IDE 中点击 工具 → 串口绘图器 即可看到实时波形。
综合示例:串口控制系统
通过串口命令控制 LED 和读取传感器:
const int LED_PIN = 13;
const int SENSOR_PIN = A0;
bool ledState = false;
bool autoReport = false;
unsigned long lastReportTime = 0;
void setup() {
Serial.begin(9600);
pinMode(LED_PIN, OUTPUT);
Serial.println("=== Arduino 控制系统 ===");
Serial.println("命令列表:");
Serial.println(" on - 开启 LED");
Serial.println(" off - 关闭 LED");
Serial.println(" toggle - 切换 LED 状态");
Serial.println(" read - 读取传感器");
Serial.println(" auto - 开启/关闭自动上报");
Serial.println(" help - 显示帮助");
Serial.println("========================");
}
void loop() {
// 处理串口命令
handleSerialCommands();
// 自动上报传感器数据
if (autoReport && millis() - lastReportTime > 1000) {
reportSensorData();
lastReportTime = millis();
}
}
void handleSerialCommands() {
if (Serial.available() == 0) return;
String command = Serial.readStringUntil('\n');
command.trim();
command.toLowerCase();
if (command == "on") {
ledState = true;
digitalWrite(LED_PIN, HIGH);
Serial.println("LED 已开启");
}
else if (command == "off") {
ledState = false;
digitalWrite(LED_PIN, LOW);
Serial.println("LED 已关闭");
}
else if (command == "toggle") {
ledState = !ledState;
digitalWrite(LED_PIN, ledState ? HIGH : LOW);
Serial.println(ledState ? "LED 已开启" : "LED 已关闭");
}
else if (command == "read") {
reportSensorData();
}
else if (command == "auto") {
autoReport = !autoReport;
Serial.println(autoReport ? "自动上报已开启" : "自动上报已关闭");
}
else if (command == "help") {
printHelp();
}
else if (command.length() > 0) {
Serial.println("未知命令,输入 help 查看帮助");
}
}
void reportSensorData() {
int value = analogRead(SENSOR_PIN);
float voltage = value * 5.0 / 1023.0;
Serial.print("传感器值: ");
Serial.print(value);
Serial.print(" (");
Serial.print(voltage, 2);
Serial.println(" V)");
}
void printHelp() {
Serial.println("命令列表:");
Serial.println(" on - 开启 LED");
Serial.println(" off - 关闭 LED");
Serial.println(" toggle - 切换 LED 状态");
Serial.println(" read - 读取传感器");
Serial.println(" auto - 开启/关闭自动上报");
Serial.println(" help - 显示帮助");
}
软件串口
Arduino Uno 只有一个硬件串口。如需更多串口,可使用 SoftwareSerial 库:
#include <SoftwareSerial.h>
// 创建软串口,RX=10, TX=11
SoftwareSerial mySerial(10, 11);
void setup() {
// 硬件串口用于与电脑通信
Serial.begin(9600);
// 软串口用于与其他设备通信
mySerial.begin(9600);
Serial.println("软串口测试");
}
void loop() {
// 从软串口读取,转发到硬件串口
if (mySerial.available()) {
char c = mySerial.read();
Serial.write(c);
}
// 从硬件串口读取,转发到软串口
if (Serial.available()) {
char c = Serial.read();
mySerial.write(c);
}
}
软串口限制
- 不能同时收发高速数据
- 某些引脚可能不支持(取决于板子)
- 会占用更多 CPU 资源
与其他设备通信
与 ESP8266 WiFi 模块通信
#include <SoftwareSerial.h>
SoftwareSerial esp8266(10, 11); // RX, TX
void setup() {
Serial.begin(9600);
esp8266.begin(115200);
delay(2000);
// 发送 AT 指令测试通信
sendCommand("AT");
sendCommand("AT+CWMODE=1"); // 设置为 Station 模式
sendCommand("AT+CWJAP=\"SSID\",\"PASSWORD\""); // 连接 WiFi
}
void loop() {
// 转发数据
if (esp8266.available()) {
Serial.write(esp8266.read());
}
if (Serial.available()) {
esp8266.write(Serial.read());
}
}
void sendCommand(String cmd) {
esp8266.println(cmd);
delay(1000);
while (esp8266.available()) {
Serial.write(esp8266.read());
}
}
两个 Arduino 通信
发送端:
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.print("TIME:");
Serial.println(millis());
delay(1000);
}
接收端:
#include <SoftwareSerial.h>
SoftwareSerial receiver(10, 11);
void setup() {
Serial.begin(9600);
receiver.begin(9600);
}
void loop() {
if (receiver.available()) {
String data = receiver.readStringUntil('\n');
if (data.startsWith("TIME:")) {
String timeStr = data.substring(5);
Serial.print("收到时间: ");
Serial.println(timeStr);
}
}
}
串口监视器使用
Arduino IDE 内置串口监视器,用于查看串口输出:
- 点击工具栏的放大镜图标,或按
Ctrl+Shift+M - 选择正确的波特率(必须与代码中一致)
- 选择换行符格式(通常选"换行和回车")
常用串口工具
| 工具 | 平台 | 特点 |
|---|---|---|
| Arduino IDE 串口监视器 | 全平台 | 简单,集成在 IDE 中 |
| 串口调试助手 | Windows | 功能丰富,支持多种编码 |
| CoolTerm | 全平台 | 专业,支持数据记录 |
| PuTTY | 全平台 | 功能强大,支持多种协议 |
| Serial Monitor (VS Code) | 全平台 | PlatformIO 集成 |
常见问题
1. 串口输出乱码
原因:波特率不匹配
解决:确保串口监视器波特率与代码中 Serial.begin() 一致
2. 无法上传程序
原因:D0/D1 被占用
解决:断开 D0/D1 上的外部连接,上传完成后再连接
3. 数据丢失
原因:缓冲区溢出
解决:
- 提高波特率
- 减少发送频率
- 增加接收端处理速度
4. 软串口不稳定
原因:引脚不支持或波特率过高
解决:
- 更换软串口引脚
- 降低波特率
- 使用硬件串口(如果可用)
下一步
掌握了串口通信后,我们将学习如何使用各种常用传感器,如温湿度传感器、超声波传感器等。