STM32和C#应用程序中的UART通信错误



请耐心回答这个令人困惑的问题。我发现这很难描述,因为它既复杂又令人厌倦。读一读,你就会知道为什么。

我已经在这个问题上纠缠了一个多月,但没有取得多大进展。我使用STM32(安装在BluePill板上的STM32F103C8(通过FT232r串行USB转换器与C#应用程序通信。完整的通信协议有点复杂。我在这里写了一个简单的代码版本,它非常准确地解释了我的问题。

STM32执行以下操作。

在初始设置中,

  1. Serial.begin为2000000(是的,它非常高,但我用示波器进行了分析,信号非常健康;阻抗匹配和时钟抖动非常准确(。

  2. 等待来自C#端的命令进入循环

在循环中,它执行以下操作。

  1. 在串行端口上发送长度为N的字节缓冲区。数据包结构为0xAA,N字节,1字节校验和
  2. 重复循环

在C#端(伪代码(,

  1. 新线程(((=>{while(true(IOTick((;Thread.Sleep(30(;}(.Start((
IOTick() is defined as:
{
while(SerialPortObject.BytesToRead > 1)
{
header = read();
if (header != 0xAA) continue;
byte [] buffer = new byte[N + 1];
receivedBytes = readBytes(buffer, N + 1, Timeout = 500ms); // receivedBytes is never less than N + 1 for timeout greater than 120)
use the N=16 bytes. Check Nth byte to compare checksum. Doen't take too much CPU time. 
Send a packet received software event.
}
}

readBytes定义为

int readBytes(byte[] buffer, int count, int timeout)
{
var st = DateTime.Now;
for (int i = 0; i < count; i++)
{
var b_ = read(timeout);
if (b_ == -1)
return i;
buffer[i] = (byte)b_;
timeout -= (int)(DateTime.Now - st).TotalMilliseconds;
}
return count;
}

int buffer2ReadIndex = 0;
byte[] buffer2= new byte[0];
int read(int timeout)
{
DateTime start = DateTime.Now;
if (buffer2.Length == 0)
{
while (SerialPortObject.BytesToRead <= 0)
{
if ((DateTime.Now - start).TotalMilliseconds > timeout)
return -1;
System.Threading.Thread.Sleep(30);
}
buffer2 = new byte[SerialPortObject.BytesToRead];
sp.Read(buffer2, 0, buffer2.Length);
}
if (buffer2.Length > 0)
{
var b = buffer2[buffer2ReadIndex];
buffer2ReadIndex++;
if (buffer2ReadIndex >= buffer2.Length)
{
buffer2ReadIndex = 0;
buffer2 = new byte[0];
}
return b;
}
return -1;
}

现在,一切都按预期进行。接收到数据包的软件事件不迟于每30ms(窗口滴答时间(触发一次。如果我必须在STM侧的每个数据包TX之间等待,那么问题就开始了。首先,我怀疑我在每个数据包TX之间用于某些任务的I2C导致了一些硬件或软件与串行数据的冲突,这些数据会被破坏。但后来我注意到,只有当我使用Arduino delay((在每个数据包TX之间引入1毫秒的延迟时,才会发生同样的事情。现在,每秒应该接收大约1K个数据包。在成功的标头异常之后,几乎十分之一的数据包要么没有完全传递,要么传递的校验和已损坏,导致C#应用程序丢失数据包标头。新的头跟踪显然需要刷新一些字节,在通信中丢失一些数据包。即使对于一个能够承受5%数据包丢失的应用程序来说,这听起来也不算太糟,但奇怪的是,当这种异常发生时,数据包接收软件中断会在每几百个连续事件后等待1秒以上。

我在这里完全失明了。即使尝试了115200波特率,也能以略低的损耗率实现相同的损耗。需要注意的是,在9600波特时,问题不会发生。这是我现在得到的唯一提示。

看起来我找到了答案。

在深入研究了SerialPort和SerialPort.base流类之后,在进行了一些文档阅读和基准测试之后,我观察到了以下内容:SerialPort.BytesToRead更新不统一。DataReceived事件似乎紧随其后。当字节以约200kHz(波特率=2Mbps(的频率到来时,它几乎会立即更新(最坏情况下,在30ms内(。当它们的频率为20kHz或更低(使用微控制器按时间均匀间隔(时,SerialPort.BytesToRead可能需要长达400ms的更新时间。这种情况只有在十几次30ms更新后才会发生。

因此,观察到这一点,我可以说SerialPort.BytesToRead是在两个条件下更新的。数据到达后已经过了一段时间(此时间不限于30ms(,或者数据来得太快。

这是一种奇怪的行为。发生此异常时不会丢失任何数据。毫不奇怪,在全带宽(波特率为2Mbps时为200KBps(下工作时,0.06%的字节丢失。

最新更新