【问题标题】:Wait for response from the Serial Port and then send next data等待串口的响应,然后发送下一个数据
【发布时间】:2019-03-06 14:57:30
【问题描述】:

我正在从.bin 文件中以字节为单位读取数据,并将整个字节数据拆分为16-16 字节帧,因此我想一个接一个地分割 16 个字节并等待第一帧完成其周期。

SerialPort 类的回调方法:

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{

    // Read data from serial port:
    byte[] buffer = new byte[serialPort.BytesToRead];
    serialPort.Read(buffer, 0, buffer.Length);
    StringBuilder sb = new StringBuilder();
    List<string> response = new List<string>();
    for (int i = 0; i < buffer.Length; i++)
    {
        string currentByte = string.Format("{0:X2}", buffer[i]);
        response.Add(currentByte);

        sb.AppendFormat("{0:X2}", buffer[i]);
    }

    string responesCode = response[1].ToString();
    if (responesCode == "44")
    {
        // Wait until the first response is not received
        foreach (var packet in packetList.Skip(1))
        {
            // This method which sending the the data
            this.ReadByteDataFromFile(packet);
        }
    }
}

FdBrowseFile_Click 按钮点击:

private void FdBrowseFile_Click(object sender, RoutedEventArgs e)
{
    Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
    Nullable<bool> result = dlg.ShowDialog();
    if (result == true)
    {
        byte[] fileBytes = File.ReadAllBytes(filename);

        foreach (byte[] copySlice in fileBytes.Slices(16))
        {
            var splitedByteArray = copySlice;
            if (splitedByteArray.Length != 16)
            {
                byte[] padd = new byte[16];
                var startAt = 0;
                Array.Copy(splitedByteArray, 0, padd, startAt, splitedByteArray.Length);
                packetList.Add(padd);
            }
            else
            {
                packetList.Add(splitedByteArray);
            }
        }
        ReadByteDataFromFile(packetList[0]);
    }
}

ReadByteDataFromFile 方法:

public void ReadByteDataFromFile(byte[] packet) {
 try {
  byte[] mBuffer = new byte[24];
  byte[] payload = new byte[16];
  int i = 0;
  foreach(var bytes in packet) {
   payload[i++] = bytes;
  }
  CheckSumHelper checkSumHelper = new CheckSumHelper();
  var ckSum = checkSumHelper.GetCheckSum(payload);
  mBuffer[0] = 0x02;
  mBuffer[1] = 0x10;
  mBuffer[2] = CheckSumHelper.GetBytesFromDecimal(packet[0]);
  mBuffer[3] = CheckSumHelper.GetBytesFromDecimal(packet[1]);
  mBuffer[4] = CheckSumHelper.GetBytesFromDecimal(packet[2]);
  mBuffer[5] = CheckSumHelper.GetBytesFromDecimal(packet[3]);
  mBuffer[6] = CheckSumHelper.GetBytesFromDecimal(packet[4]);
  mBuffer[7] = CheckSumHelper.GetBytesFromDecimal(packet[5]);
  mBuffer[8] = CheckSumHelper.GetBytesFromDecimal(packet[6]);
  mBuffer[9] = CheckSumHelper.GetBytesFromDecimal(packet[7]);
  mBuffer[10] = CheckSumHelper.GetBytesFromDecimal(packet[8]);
  mBuffer[11] = CheckSumHelper.GetBytesFromDecimal(packet[9]);
  mBuffer[12] = CheckSumHelper.GetBytesFromDecimal(packet[10]);
  mBuffer[13] = CheckSumHelper.GetBytesFromDecimal(packet[11]);
  mBuffer[14] = CheckSumHelper.GetBytesFromDecimal(packet[12]);
  mBuffer[15] = CheckSumHelper.GetBytesFromDecimal(packet[13]);
  mBuffer[16] = CheckSumHelper.GetBytesFromDecimal(packet[14]);
  mBuffer[17] = CheckSumHelper.GetBytesFromDecimal(packet[15]);
  mBuffer[18] = 0x17;
  mBuffer[19] = 0x00;
  mBuffer[20] = 0x00;
  mBuffer[21] = 0x00;
  mBuffer[22] = Convert.ToByte(int.Parse(ckSum, System.Globalization.NumberStyles.HexNumber));
  mBuffer[23] = 0x03;
  serialPort.Write(mBuffer, 0, mBuffer.Length);
 } catch (Exception ex) {
  ExceptionHandler exceptionHandler = new ExceptionHandler();
  exceptionHandler.HandleException(ex);
 }
}

如何为ReadByteDataFromFile 方法添加延迟?

【问题讨论】:

  • 请提供minimal reproducible example。从您的代码中,尚不清楚您的问题是什么。什么不起作用?在哪一行?你想达到什么目标?此外,您的代码不完整:例如缺少ReadByteDataFromFilepacketList
  • @dymanoid 我想将数据发送到串口,即这个方法:ReadByteDataFromFile
  • @dymanoid 感谢您的回复!我添加了代码
  • 我的建议是首先编写可读代码并将其拆分。 F.e. payload 只是用来计算校验和,为什么还要复制数据呢?还有更好的方法将数据从一个数组复制到另一个数组。另外,我很确定GetBytesFromDecimal 与校验和无关,是吗?另外,为什么需要解析校验和,如果它是一个数字,它可能应该返回一个int 而不是string。此外,名为ReadByteDataFromFile 的方法不会从文件中读取任何内容...
  • @huysentruitw 完全同意你的看法!

标签: c# serial-port


【解决方案1】:

您需要一种方法来阻止某些代码的执行,直到发生其他事情,或者 - 如何让事情在两个线程上同步运行。 .NET 在System.Threading 命名空间中有很多用于同步的类。我们将在这里使用AutoResetEvent

AutoResetEvent 视为旋转门。

如果对面的人停下来,你就无法前进。 当你向前移动时,你打电话给Wait - 它会阻止你移动, 直到有人打电话给Set

现在,如果我们将其应用于我们的问题: 我们需要停止发送数据,直到我们得到可接受的响应。 所以在发送数据的时候调用Wait,让响应处理代码调用Set让它继续前进。

这是一个模拟调制解调器的示例。 您发送一些 AT 命令,它会响应,但响应始终以 \r\n 结尾。

var port = new SerialPort("COM2");
port.Open();

var mre = new AutoResetEvent(false);
var buffer = new StringBuilder();

port.DataReceived += (s, e) =>
{
    buffer.Append(port.ReadExisting());
    if (buffer.ToString().IndexOf("\r\n") >= 0)
    {
        Console.WriteLine("Got response: {0}", buffer);

        mre.Set(); //allow loop to continue
        buffer.Clear();
    }
};


var commandsToSend = new string[] { "AT", "AT", "AT+CSQ" };
var responseTimeout = TimeSpan.FromSeconds(10);

foreach (var command in commandsToSend)
{
    try
    {
        Console.WriteLine("Write '{0}' to {1}", command, port.PortName);
        port.WriteLine(command);

        Console.WriteLine("Waiting for response...");

        //this is where we block
        if (!mre.WaitOne(responseTimeout))
        {
            Console.WriteLine("Did not receive response");
            //do something
        }
    }
    catch (TimeoutException)
    {
        Console.WriteLine("Write took longer than expected");
    }
    catch
    {
        Console.WriteLine("Failed to write to port");
    }
}

Console.ReadLine();

通过虚拟串口测试时的示例输出: (我只是回复OK&lt;CR&gt;&lt;LF&gt;

Write 'AT' to COM2
Waiting for response...
Got response: OK

Write 'AT' to COM2
Waiting for response...
Got response: OK

Write 'AT+CSQ' to COM2
Waiting for response...
Did not receive response

【讨论】:

    【解决方案2】:

    写完第一帧后循环等待完整响应。

    // Set read timeout to value recommended in the communication protocol specification 
    // so serial port operations don't stuck.
    _port.WriteTimeout = 200;
    _port.ReadTimeout = 200;
    
    public void OnClick()
    {
        // Write first frame.
        _port.Write(...);
        // Now wait for the full response.
    
        // Expected response length. Look for the constant value from the device communication 
        // protocol specification or extract from the response header (first response bytes) if  
        // there is any specified in the protocol.
        int count = ...; 
        var buffer = new byte[count];
        var offset = 0;
        while (count > 0)
        {
            var readCount = _port.Read(buffer, offset, count);                 
            offset += readCount;
            count -= readCount;
        }
        // Now buffer contains full response or TimeoutException instance is thrown by SerialPort.
        // Check response status code and write other frames.
    }
    

    为了不阻塞 UI 线程,您很可能仍需要使用同步 API 和 Task.Run()。请参阅 StackOverflow 上的 C# await event and timeout in serial port communication 讨论。

    有关更多信息,请查看 Kim Hamilton 的 Top 5 SerialPort Tips 文章。

    【讨论】:

    • 所以实际上我需要在while之后使用那个for循环?还是在里面?
    • 什么for 循环?我的代码 sn-p 中的while 循环需要从设备获得完整响应(某种形式的帧确认)而不是它的一部分,因为SerialPort.Read() 在收到任意数量的字节后立即完成执行。
    • 这意味着我需要用while替换那个forloop
    猜你喜欢
    • 2021-10-13
    • 1970-01-01
    • 2012-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-04
    • 1970-01-01
    相关资源
    最近更新 更多