You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Arduino双串行端口(USB主机盾/蓝牙)配置及代码合并故障排查

我来帮你梳理下两段代码合并后失效的核心问题,然后一步步给出修复方案:

问题根源分析

你遇到的问题主要来自三个方面:

  1. SoftwareSerial与USB主机库的中断冲突:AVR系列Arduino(比如Uno/Nano)的SoftwareSerial会占用定时器1,而USB主机库(usbhub/cdcacm)也依赖中断资源,两者冲突会导致USB无法正常轮询,蓝牙串口也会失效。
  2. 阻塞式delay()的滥用:两段代码里的delay(50)/delay(10)会让程序完全暂停,导致USB的Usb.Task()无法被及时调用(USB库需要高频轮询才能维持连接),同时蓝牙串口的缓冲区会因为延迟出现异常触发。
  3. 变量初始化错误与命名冲突:蓝牙代码里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

火山引擎 最新活动