我有一个微控制器项目,向我的PC发送长度为8字节的串行数据帧,使用PC串行监视工具监视来自微控制器的数据帧,并且所有数据看起来都很好,每个8字节帧似乎始终以8字节的长度发送此模式的数据。
我遇到的问题如下:
每次读取串行数据时,结构化的8字节数据帧中的第1个字节都不存在。
示例:
以下各循环如下:
Mircocontroller发送8字节帧: 01 FF 8E 01 00 00 00 0A
第一次循环迭代。
.NET Serial PORT接收数据框: 01 00 00 00 00 00 00 00
阅读(buff,0,8);读取1字节读取
第二次循环迭代。
.NET Serial PORT接收数据帧: FF 8E 01 00 00 00 0A 00 (7字节读取)
阅读(buff,0,8);读取7字节读取
我希望每次循环迭代时sr.Read(buff,0,8)总是读取8字节的长度。
以下是用于从串口PORT读取的代码
static void Main(string[] args)
{
using (SerialPort sr = new SerialPort("COM5"))
{
sr.BaudRate = 9600;
sr.DataBits = 8;
sr.Parity = Parity.None;
sr.StopBits = StopBits.One;
sr.Open();
StringBuilder sb = new StringBuilder();
while (true)
{
byte[] buff = new byte[8];
int r = sr.Read(buff, 0, 8);
Console.WriteLine("Number of bytes read : " + r);
for (int i = 0; i < buff.Length; i++)
{
var hex = string.Format("{0:x2}", buff[i]);
sb.Append(hex.ToUpper());
sb.Append(" ");
}
Console.WriteLine(sb);
sb.Clear();
}
}
提前谢谢。
感谢您的反馈,每个人都非常有用。
我重建了8字节帧的缓冲逻辑,如下所示:
一切似乎都与我从端口监视器上看到的数据模式同步,最重要的是,微控制器会发生什么。
重要方面仍然缺失:
....... ect
这是更新的代码
static void Main(string[] args)
{
using (SerialPort sr = new SerialPort("COM5"))
{
sr.BaudRate = 9600;
sr.DataBits = 8;
sr.Parity = Parity.None;
sr.StopBits = StopBits.One;
sr.Handshake = Handshake.None;
sr.DtrEnable = false;
sr.Open();
sr.ReceivedBytesThreshold = 1;
StringBuilder sb = new StringBuilder();
byte[] buff = new byte[8];
byte[] temp_buffer = new byte[8];
while (true)
{
int r = sr.Read(temp_buffer, 0, 8);
Console.WriteLine("Number of bytes read : " + r);
// 1 - byte from the serial frame ?
if (r == 1)
{
buff[0] = temp_buffer[0];
}
// 7 - bytes from the serial frame ?
if (r == 7)
{
// get the remaining 7 - bytes
for (int i = 0; i <= temp_buffer.Length - 1; i++)
{
if (i != 0)
buff[i] = temp_buffer[i]; // construct a complete frame
}
// okay, we ready to display the 8-byte serial frame.
for (int i = 0; i < buff.Length; i++)
{
var hex = string.Format("{0:x2}", buff[i]);
sb.Append(hex.ToUpper());
sb.Append(" ");
}
Console.WriteLine(sb);
sb.Clear();
}
}
}
}
这里还有一个修改版本,读取一个16字节的串行帧。
using (SerialPort sr = new SerialPort("COM5"))
{
sr.BaudRate = 9600;
sr.DataBits = 8;
sr.Parity = Parity.None;
sr.StopBits = StopBits.One;
sr.Open();
sr.ReceivedBytesThreshold = 1;
StringBuilder sb = new StringBuilder();
byte[] io_buffer = new byte[16];
byte[] temp_buffer = new byte[16];
do
{
int data_length = sr.Read( temp_buffer, 0, 16 );
// 1 - Byte from the serial frame ?
if ( data_length == 1 && temp_buffer[ 0 ] == 0x28 )
{
io_buffer[ 0 ] = temp_buffer[ 0 ];
}
// 15 - Bytes from the serial frame ?
if (data_length == 15 && temp_buffer[14] == 0x29)
{
// Here we construct the 16- byte frame.(start from 1 as we already have our start frame stored) "x028" => ")"
for ( int i = 1; i < temp_buffer.Length; i++ )
{
io_buffer[ i ] = temp_buffer[ i - 1 ];
}
// okay, we ready to display the 16-byte serial frame.
for ( int i = 0; i < io_buffer.Length; i++ )
{
var hex = string.Format( "{0:x2}", io_buffer[ i ] );
sb.Append( hex.ToUpper() );
sb.Append( " " );
}
Console.WriteLine(sb);
sb.Clear();
}
} while (sr.IsOpen);
这是16字节帧输出,第4字节表示(I / O 4通道模拟到数字转换器)的通道0到3
28 FF FF 00 01 DB 00 00 00 00 00 00 0E 00 00 29
28 FF FF 01 01 02 00 00 00 00 00 00 0E 00 00 29
28 FF FF 02 01 02 00 00 00 00 00 00 0E 00 00 29
28 FF FF 03 01 A8 00 00 00 00 00 00 0E 00 00 29
答案 0 :(得分:2)
我期待sr.Read(buff,0,8)总是读取8个字节的长度 每次循环迭代。
这是毫无根据的期望。 Read
方法(以及底层的Win32例程)读取端口输入缓冲区中可用的数据。如果有1个字节可用,则可以读取1个字节或更少。
数据的可用性取决于所有因素,Read
方法不会对它们产生影响。您应该自己在接收方建立数据包。
P上。 S.是的,从读取通信线路(套接字,串口等)要比写入那条线要困难得多。
答案 1 :(得分:0)
您无法保证在最初拨打sr.Read()
时,您将与设备发送字节同步。
我原本以为你想要将sr.Read
重复调用到缓冲区中。然后在缓冲区上滑动一个8字节宽的窗口,直到找到与您期望的协议匹配的8个字节。现在丢弃窗口前的所有内容,并从缓冲区的(现在)开始读取8个字节的块。然后,您将与协议“同步”。在此基础上实现您的逻辑(可能为每个接收的有效8字节组触发一个事件并将其处理一层)。
答案 2 :(得分:0)
当设备“发送帧”时,它只是意味着它开始发送字节。您将收到发送顺序中的字节,但不保证时间 - 操作系统将收集字节一段时间,然后(以减少处理中的延迟)将它们发送给您。它无法知道帧何时“完成” - 因此可以给出一个部分数据包,同时还有更多数据排队等待设备发送。您的代码需要解释数据流以将其分解为帧。
在您的示例中,您将收到1个字节,后跟7个字节。您可以缓冲这些字节,直到您有足够的时间来构建一个8字节的完整帧然后可以解码。
如果你遇到传输错误,你可能还需要处理不完整的帧(你可能只得到帧的最后5个字节 - 你需要丢弃它们并开始读取下一个有效帧)或损坏的数据(帧似乎完整到达,但其中的一些字节具有不正确的值 - 在这种情况下,如果帧包括CRC或其他一致性检查,您可以验证它并丢弃无效帧,但如果没有信息可以帮助您验证帧你只需要希望传输是清楚的。)
基本上,串行端口只提供以偶发突发形式到达的字节流 - 由您的代码来实现应用于该数据流的任何数据编码和协议。
答案 3 :(得分:0)
sr.Read(temp_buffer,0,8)将从缓冲区中读取最多8个字节,它是一个最大数字,因此您不会覆盖temp_buffer。如果要读取8个字节,则必须等待至少8个字节。这可以通过BytesToRead方法和/或ReceivedBytesThreshold来完成。
如果将ReceivedBytesThreshold设置为8,则当事件触发时,您知道至少要读取8个字节。如果要等待8个字节,可以轮询BytesToRead属性,当它达到8时,执行读取。