2016-02-25 75 views
0

我正在尝试使用C#在MSVS上创建串行通信工具。它与Photon MCU和蓝牙适配器进行通信。MSVS C#SerialPort接收到数据丢失

当按下“开始”按钮时,UI向Photon发送一个“1”,它首先发送当前时间戳并开始从函数发生器发送流数据。当按下“停止”按钮时,它首先发送光子收到时10“2”(由于光子端的定时器问题),它停止发送函数发生器的数据。然后它休眠一秒钟并发送一个“3”,它发送另一个当前时间戳。然后用户界面放弃InBuffer中的数据并停止读取数据。

connectBT与启动按钮连接,并且disconnectBT与停止按钮连接。

这是代码,我现在所拥有的:

SerialPort serial = new SerialPort(); 
string recieved_data; 
int startBuffer = 0; 

private void connectBT(object sender, RoutedEventArgs e) 
{ 
    startBuffer++; // keep track of BT open counter 
    if (serial.IsOpen) Debug.WriteLine("BT Open"); 

    // first time BT is open and BT is not open 
    if (!serial.IsOpen) 
    { 
     if (startBuffer == 1) 
     { 
      // COM port properties 
      serial.PortName = "COM7"; 
      serial.BaudRate = 38400; 
      serial.Handshake = Handshake.None; 
      serial.Parity = Parity.None; 
      serial.DataBits = 8; 
      serial.StopBits = StopBits.One; 
      serial.ReadTimeout = 200; 
      serial.WriteTimeout = 50; 
      serial.Open(); 
     } 

     startButton.Content = "Recording"; 
     Send_Data("1"); // tell Photon to start sending data 
     serial.DiscardInBuffer(); // discard whatever is in inbuffer 
     serial.DataReceived += new SerialDataReceivedEventHandler(Recieve); // start receiving data 
    } 

    // after BT has been opened and start button has been pressed again 
    else if (serial.IsOpen && startBuffer > 1) 
    { 
     startButton.Content = "Recording"; 
     Send_Data("1"); 
     serial.DiscardInBuffer(); 
     serial.DataReceived += new SerialDataReceivedEventHandler(Recieve); 
    } 
} 

// stop button is pressed 
private void disconnectBT(object sender, RoutedEventArgs e) 
{ 

    // send "2" ten times to tell photon to stop transmitting function generator data 
    int i = 0; 
    while (i < 10) 
    { 
     Send_Data("2"); 
     Thread.Sleep(1); 
     i++; 
    } 
    Thread.Sleep(1000); 
    Send_Data("3"); // send a 3 to tell photon to send the last time stamp 
    Thread.Sleep(1000); 

    serial.DiscardInBuffer(); // discard in buffer 
    serial.DataReceived -= Recieve; // stop receiving data 
    //serial.Close(); // close BT 
    startButton.Content = "Start"; 

} 

private void Recieve(object sender, SerialDataReceivedEventArgs e) 
{ 

    recieved_data = serial.ReadLine(); 
    Debug.WriteLine(recieved_data); 

} 

我遇到了一个问题,当我按下“停止”按钮,这是从蓝牙发送的数据的最后一块是丢失。当按下停止按钮时,我从来没有收到我应该收到的最后一次印章。根据我们的数学,我们应该得到每秒500点(500Hz),但我只收到约100个。

我的理论是,用户界面以较慢(或延迟)的速率接收数据,即使在数据可以打印到调试输出之前,它也会丢弃接收到的数据。我知道,由于与数据包关联的计数器值,我收到的第一个和最后一个数据之间的所有数据全部存在。基本上如果我得到1〜500个数据点,我只能得到1〜100个数据点。我也尝试过,只要发送1,2和3的白蚁,因为UI应该是这样的,我可以根据需要获取所有数据。我没有故意关闭BT。

我该怎么做才能防止这种数据丢失?我在代码中做错了什么,我不应该为正确的蓝牙协议做或做什么?这是我第一次写蓝牙代码,所以我很不熟悉它。

+0

如果不是全部调用睡眠模式,而是调用睡眠模式(50次),而不是循环调用20次,会有帮助吗?我想可能接收事件想要触发,但不能,因为你已经冻结了主线程。 –

+0

您接收数据的方式可能非常错误。首先 - 你假设只有一行要阅读。不要尝试使用ReadLine并使用ReadExisting。其次 - 你只是把你读到的任何东西放到一个字符串中,每当一个新的事件被触发时就会被覆盖。其中一些字符串可能(几乎可以肯定)不能长时间处理它们。 –

回答

0

不知道这是你的问题的原因,但你的接收有一个非常大的陷阱。

每个接收事件只能读取一行,并且在一个事件中可以有多行读取,然后在最后累积并丢弃它们。

ReadLine旨在以类似于流的同步方式使用,您只需读取一行,然后对其进行处理,然后再编写,而不用于DataReceived事件。

你有两个选择:用serial.ReadLine()连续循环读取一个新线程(它会阻塞,直到有新的一行可用)或更好的方法,读取每个接收事件的串行缓冲区。

要那样做,你可以做smething这样的:

List<byte> tmpBuffer = new List<byte>(); 
    static byte newLineB = Encoding.ASCII.GetBytes("\n")[0]; 

    void Receive(object sender, SerialDataReceivedEventArgs e) 
    { 

     lock (tmpBuffer) 
     { 
      while (serial.BytesToRead > 0) 
      { 
       byte[] segment = new byte[serial.BytesToRead]; 
       serial.Read(segment, 0, segment.Length); 
       tmpBuffer.AddRange(segment); 
       ProcessBuffer(); 

      } 

     } 

    } 

    private void ProcessBuffer() 
    { 
     int index = 0; 

     while ((index = tmpBuffer.IndexOf(newLineB)) > -1) 
     { 
      string line = Encoding.ASCII.GetString(tmpBuffer.Take(index + 1).ToArray()); 

      //Do whatever you need to do with the line data 
      Debug.WriteLine(line); 

      tmpBuffer.RemoveRange(0, index + 1); 
     } 
    } 

正如你所看到的,所接收到的数据被存储用作缓冲时间列表(是的,一个数组,并使用缓冲区函数会更快,但对于小的消息,为了简单起见,大多数情况下列表就足够了),然后将接收到的数据添加到缓冲区,并且当没有剩余字节时,列表将被处理以搜索字符串行。

还要注意读是一个循环,我在同时执行此功能并没有接收事件被解雇接收数据,其中存在这样的情况已经运行,所以这样做的更好的是创建一个循环读取,同时有仍然是数据。

0

谢谢大家的回应,他们都帮我解决了我的问题,但最终是什么修正了它延迟发送“3”和放弃我的inBuffer和关闭接收连接之间的时间。

async Task DelayBT() 
{ 
    await Task.Delay(100); 
} 

Thread.Sleep()没有因为它的性质禁用线程内的所有行动(我仍然需要)的工作,所以这种方法的工作就像一个魅力。我只是打电话await DelayBT我需要延迟。

希望这可以帮助任何人遇到与我一样的问题。