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); // 断点设置处 }
问题原因分析
- 串口复位与初始化噪声:Arduino Mega ADK的DTR引脚默认连接复位电路,C#打开串口时,DTR电平变化会触发Arduino复位。高波特率下,串口模块复位后的初始化阶段,引脚电平波动更容易被误解析为有效字节(全1字节对应串口空闲高电平,可能被误判为完整字节帧);9600波特率较慢,噪声信号不足以被识别为有效字节,因此无此问题。
- 缓冲区残留数据: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
相关产品推荐
相关产品推荐

