You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SerialPort.Open未调用Write前向Arduino发送全1字节的异常排查

串口通信异常:Arduino在C#调用Write前收到无效字节(高波特率下)

问题场景

开发串口通信程序时,用C#给Arduino Mega ADK发送消息,发现高波特率下(19200/57600/115200)存在异常:在C#代码的port.Write()处设置断点时,Arduino已经收到了无效字节(LED全亮对应255,偶尔是7个1),但此时还未执行Write操作。恢复代码执行后,能正常收到预期的99(二进制01100011)。波特率设为9600时无此问题,且丢弃前1-2字节后后续通信正常,可排除波特率不匹配问题。

Arduino代码(.ino)

void setup(){
    Serial.begin(19200);
    for(int i=22;i<30;i++){
        pinMode(i, OUTPUT);
    }
}

void loop(){
    if(Serial.available() > 0){
        for(int i=0;i<8;i++){
            digitalWrite(22+i, (Serial.peek() >> i) & 1);
        }
        delay(1000);
        Serial.read();
    }
}

C#代码

public static void Main(string[] args)
{
    var port = new SerialPort(SerialPort.GetPortNames()[0])
    {
        BaudRate = 19200,
        StopBits = StopBits.One,
        Parity = Parity.None,
        Handshake = Handshake.None
    };
    port.Open();
    Thread.Sleep(1000);
    port.Write(new byte[] { 99 }, 0, 1);    // 断点设置处
}

问题原因分析

  1. 串口复位与初始化噪声:Arduino Mega ADK的DTR引脚默认连接复位电路,C#打开串口时,DTR电平变化会触发Arduino复位。高波特率下,串口模块复位后的初始化阶段,引脚电平波动更容易被误解析为有效字节(全1字节对应串口空闲高电平,可能被误判为完整字节帧);9600波特率较慢,噪声信号不足以被识别为有效字节,因此无此问题。
  2. 缓冲区残留数据:C#的SerialPort打开端口时,可能未清空底层串口的残留数据,这些数据会被Arduino读取到,但多次测试均出现问题,核心原因还是复位初始化阶段的噪声干扰。

解决办法

1. 两端清空串口缓冲区

  • C#端:打开串口后立即清空输入输出缓冲区,避免残留数据干扰:
port.Open();
Thread.Sleep(1000);
port.DiscardInBuffer(); // 清空输入缓冲区
port.DiscardOutBuffer(); // 清空输出缓冲区
port.Write(new byte[] { 99 }, 0, 1);
  • Arduino端:初始化串口后,主动清空缓冲区里的垃圾字节:
void setup(){
    Serial.begin(19200);
    // 清空初始化时的无效字节
    while(Serial.available() > 0){
        Serial.read();
    }
    for(int i=22;i<30;i++){
        pinMode(i, OUTPUT);
    }
}

2. 禁用DTR自动复位

通过C#端设置关闭DTR引脚,避免打开串口时触发Arduino复位,从根源上消除复位带来的噪声:

var port = new SerialPort(SerialPort.GetPortNames()[0])
{
    BaudRate = 19200,
    StopBits = StopBits.One,
    Parity = Parity.None,
    Handshake = Handshake.None,
    DtrEnable = false // 关闭DTR触发复位
};

3. 延迟Arduino串口初始化

在Arduino的setup()中添加延迟,等C#端串口完全稳定后再初始化串口,减少时序冲突:

void setup(){
    delay(2000); // 等待串口连接稳定
    Serial.begin(19200);
    for(int i=22;i<30;i++){
        pinMode(i, OUTPUT);
    }
}

4. 通信前添加同步握手

正式发送数据前,先发送约定的同步字节(比如0x00),Arduino端收到同步字节后再处理有效数据,自动过滤前面的垃圾字节:

  • C#端:
port.Open();
Thread.Sleep(1000);
port.Write(new byte[] { 0x00 }, 0, 1); // 发送同步字节
Thread.Sleep(100);
port.Write(new byte[] { 99 }, 0, 1);
  • Arduino端:
void loop(){
    static bool syncDone = false;
    if(Serial.available() > 0){
        if(!syncDone){
            if(Serial.read() == 0x00){
                syncDone = true;
            }
            return;
        }
        // 处理有效数据
        byte data = Serial.read();
        for(int i=0;i<8;i++){
            digitalWrite(22+i, (data >> i) & 1);
        }
        delay(1000);
    }
}

内容的提问来源于stack exchange,提问作者Janilson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 11:01:58