Arduino双串行端口(USB主机盾/蓝牙)配置及代码合并故障排查
我来帮你梳理下两段代码合并后失效的核心问题,然后一步步给出修复方案:
问题根源分析
你遇到的问题主要来自三个方面:
- SoftwareSerial与USB主机库的中断冲突:AVR系列Arduino(比如Uno/Nano)的SoftwareSerial会占用定时器1,而USB主机库(
usbhub/cdcacm)也依赖中断资源,两者冲突会导致USB无法正常轮询,蓝牙串口也会失效。 - 阻塞式
delay()的滥用:两段代码里的delay(50)/delay(10)会让程序完全暂停,导致USB的Usb.Task()无法被及时调用(USB库需要高频轮询才能维持连接),同时蓝牙串口的缓冲区会因为延迟出现异常触发。 - 变量初始化错误与命名冲突:蓝牙代码里
data = "";的错误初始化(data是char类型,不能赋值为空字符串),以及两段代码都用了i作为循环变量,会导致逻辑误判和变量覆盖。
具体修复方案
1. 替换SoftwareSerial为AltSoftSerial(解决中断冲突)
AltSoftSerial是对中断更友好的软串口库,它使用定时器2而非定时器1,能避开和USB主机库的冲突。
// 替换原来的SoftwareSerial头文件和定义 #include <AltSoftSerial.h> AltSoftSerial portBluetooth(10, 11); // RX=10, TX=11
2. 移除阻塞式delay,改用非阻塞延时
所有delay()都会阻塞程序,导致USB任务无法执行。我们用millis()实现非阻塞逻辑,确保USB和蓝牙逻辑都能被及时处理:
// 全局定义非阻塞延时变量 unsigned long bluetoothReadDelay = 0; const long bluetoothDelayInterval = 50; void loop() { // 优先处理USB任务,必须放在loop最开头 Usb.Task(); // 蓝牙逻辑:非阻塞读取 if (portBluetooth.available()) { if (millis() - bluetoothReadDelay >= bluetoothDelayInterval) { bluetoothReadDelay = millis(); test = ""; char data = '\0'; // 正确初始化char类型变量 while (portBluetooth.available()) { data = portBluetooth.read(); // 原来的蓝牙指令解析逻辑... } } } // USB逻辑:移除所有delay if (Acm.isReady()) { uint8_t buf[64]; uint16_t rcvd = 64; uint8_t rcode = Acm.RcvData(&rcvd, buf); if (rcode && rcode != hrNAK) { ErrorMessage<uint8_t>(PSTR("Ret"), rcode); } if (rcvd) { // 把循环变量i改成idx,避免和蓝牙代码的i冲突 for(uint16_t idx=0; idx < rcvd; idx++ ) { Serial.print((char)buf[idx]); } } } }
3. 修复蓝牙开门逻辑的长延时
蓝牙代码里的delay(5000)会让程序暂停5秒,期间USB完全无法工作。我们改成带USB轮询的非阻塞延时:
// 替换原来的digitalWrite+delay(5000) digitalWrite(PowerPin1, LOW); digitalWrite(PowerPin2, LOW); unsigned long doorOpenStart = millis(); while (millis() - doorOpenStart < 5000) { Usb.Task(); // 延时期间也要维持USB连接 } digitalWrite(PowerPin1, HIGH); digitalWrite(PowerPin2, HIGH);
完整合并后的代码示例
#include <AltSoftSerial.h> #include <cdcacm.h> #include <usbhub.h> #include "pgmstrings.h" #ifdef dobogusinclude #include <spi4teensy3.h> #endif #include <SPI.h> // 蓝牙相关配置 AltSoftSerial portBluetooth(10, 11); char data; int i = 0; int e = 0; String test; String win = "0000"; const int PowerPin1 = 4; const int PowerPin2 = 5; #define TabSize 10 String ImpTab[TabSize]; String ExpTab[TabSize]; // USB异步操作类 class ACMAsyncOper : public CDCAsyncOper { public: uint8_t OnInit(ACM *pacm); }; uint8_t ACMAsyncOper::OnInit(ACM *pacm) { uint8_t rcode; rcode = pacm->SetControlLineState(3); if (rcode) { ErrorMessage<uint8_t>(PSTR("SetControlLineState"), rcode); return rcode; } LINE_CODING lc; lc.dwDTERate = 9600; lc.bCharFormat = 0; lc.bParityType = 0; lc.bDataBits = 8; rcode = pacm->SetLineCoding(&lc); if (rcode) ErrorMessage<uint8_t>(PSTR("SetLineCoding"), rcode); return rcode; } USB Usb; ACMAsyncOper AsyncOper; ACM Acm(&Usb, &AsyncOper); // 非阻塞延时变量 unsigned long bluetoothReadDelay = 0; const long bluetoothDelayInterval = 50; void setup() { Serial.begin(9600); #if !defined(__MIPSEL__) while (!Serial); #endif Serial.println("Start"); // 初始化蓝牙 portBluetooth.begin(9600); pinMode(PowerPin1, OUTPUT); pinMode(PowerPin2, OUTPUT); digitalWrite(PowerPin1, HIGH); digitalWrite(PowerPin2, HIGH); // 初始化USB if (Usb.Init() == -1) Serial.println("OSCOKIRQ failed to assert"); delay(200); } void loop() { // 优先处理USB任务 Usb.Task(); // 处理蓝牙逻辑 if (portBluetooth.available()) { if (millis() - bluetoothReadDelay >= bluetoothDelayInterval) { bluetoothReadDelay = millis(); test = ""; char data = '\0'; while (portBluetooth.available()) { data = portBluetooth.read(); if ((byte)data == 13) { if (test.equals(win)) { portBluetooth.println("secret code acknowledged: opening all doors, sending current loaded numbers to serial"); for (int k = 0; k < TabSize; k++) { portBluetooth.println("import: " + ImpTab[k]); portBluetooth.println("export: " + ExpTab[k]); } digitalWrite(PowerPin1, LOW); digitalWrite(PowerPin2, LOW); // 非阻塞延时,同时维持USB unsigned long doorOpenStart = millis(); while (millis() - doorOpenStart < 5000) { Usb.Task(); } digitalWrite(PowerPin1, HIGH); digitalWrite(PowerPin2, HIGH); } else if (test.equals("1111")) { portBluetooth.println("secret code acknowledged: clearing tracking number list"); for (int k = 0; k < TabSize; k++) { ImpTab[k] = ""; ExpTab[k] = ""; } } } else { test.concat(data); portBluetooth.println(test); } } } } // 处理USB逻辑 if (Acm.isReady()) { uint8_t buf[64]; uint16_t rcvd = 64; uint8_t rcode = Acm.RcvData(&rcvd, buf); if (rcode && rcode != hrNAK) { ErrorMessage<uint8_t>(PSTR("Ret"), rcode); } if (rcvd) { for(uint16_t idx=0; idx < rcvd; idx++ ) { Serial.print((char)buf[idx]); } } } }
内容的提问来源于stack exchange,提问作者Adrien MANSUY




