0

我有一个微控制器项目向我的 PC 发送长度为 8 字节的串行数据帧,我使用 PC 串行监视器工具监视来自微控制器的数据帧,所有数据看起来都不错,每个 8 字节帧似乎一直在发送此模式中的数据长度为 8 字节。

我遇到的问题如下:

每次我读取串行数据时,结构的 8 字节数据帧中的第一个字节都不存在。

例子:

每个循环如下:

微控制器发送 8 字节帧:01 FF 8E 01 00 00 00 0A

  1. 第一次循环迭代。

    .NET 串行端口接收数据帧:01 00 00 00 00 00 00 00

    读取(buff, 0, 8); 读取 1 字节读取

  2. 第二次循环迭代。

.NET 串行端口接收数据帧:FF 8E 01 00 00 00 0A 00 (7 字节读取)

读取(buff, 0, 8); 读取 7 字节读取

我期望 sr.Read(buff, 0, 8) 在每次循环迭代时总是读取 8 字节的长度。

这是我用来从串行端口读取的代码

    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 字节帧重构了缓冲逻辑,如下所示:

一切似乎都与我从端口监视器上看到的数据模式同步,最重要的是,来自微控制器的数据模式。

仍然缺少重要方面:

  1. 帧 CRC。
  2. 验证每个字节的顺序。
  3. .......等等

    这是更新的代码

    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

4

4 回答 4

2

我期望 sr.Read(buff, 0, 8) 在每次循环迭代时总是读取 8 字节的长度。

这是一个没有根据的期望。Read方法(和底层的 Win32 例程)读取端口输入缓冲区中可用的数据。如果有 1 个字节可用,则可以读取 1 个字节或更少。

数据的可用性取决于多种因素,Read方法不能影响它们。您应该自己在接收方构建数据包。

PS 是的,从通信线路(套接字、串口等)读取写入该线路要困难得多。

于 2012-09-26T07:52:52.333 回答
0

当设备“发送帧”时,它仅仅意味着它开始发送字节。您将按发送顺序接收字节,但不能保证时间 - 操作系统将收集字节一段时间,然后(以减少处理延迟)将它们发送给您。它无法知道帧何时“完成” - 因此您可以获得部分数据包,而仍有更多数据排队等待设备发送。您的代码需要解释数据流以将其分解为帧。

在您的示例中,您收到 1 个字节,后跟 7 个字节。由您决定缓冲这些字节,直到您有足够的能力构建一个完整的 8 字节帧,然后您可以对其进行解码。

如果您遇到传输错误,您可能还必须处理不完整的帧(您可能只获得帧的最后 5 个字节 - 您需要丢弃它们并开始读取下一个有效帧)或损坏的数据(帧似乎到达完好无损,但其中的某些字节具有不正确的值 - 在这种情况下,如果帧包含 CRC 或其他一致性检查,您可以对其进行验证并丢弃无效帧,但如果没有信息可以帮助您验证帧,您只需必须希望传输是明确的)。

基本上,串行端口仅提供以零星突发形式到达的字节流 - 由您的代码来实现应用于该数据流的任何数据编码和协议。

于 2012-09-26T08:21:51.020 回答
0

您无法保证在您sr.Read()最初调用时会与发送字节的设备同步。

我原以为您会想要sr.Read反复调用缓冲区。然后在缓冲区上滑动一个 8 字节宽的窗口,直到找到与您期望的协议匹配的 8 个字节。现在丢弃窗口之前的所有内容,并从缓冲区的(现在)开始处读取 8 个字节的块。然后,您将与您的协议“同步”。在此基础上实现您的逻辑(可能为收到的每个有效 8 字节组触发一个事件并向上处理一层)。

于 2012-09-26T08:02:46.943 回答
0

sr.Read(temp_buffer, 0, 8) 将从缓冲区读取最多 8 个字节,这是一个最大数字,因此您不会覆盖您的 temp_buffer。如果要读取 8 个字节,则必须至少等待 8 个字节。这可以通过 BytesToRead 方法和/或 ReceivedBytesThreshold 来完成。

如果将 ReceivedBytesThreshold 设置为 8,则当事件触发时,您知道您将至少有 8 个字节要读取。如果要等待 8 字节,可以轮询 BytesToRead 属性,当它达到 8 时,执行读取。

于 2013-01-02T01:19:33.000 回答