2014-03-30 246 views
2

USB 직렬 어댑터 (FTDI FT232H)를 사용하여 높은 전송 속도 (8 MegaBaud)로 마이크로 컨트롤러에서 이진 데이터를 읽는 C# 어플리케이션을 구현 중입니다. 문제는 스트림에 0x1A가 포함되어 있으며이 바이트 다음에 큰 데이터 청크 (수천 바이트)가 손실되는 경우입니다.SerialPort는 데이터가 높은 통신 속도로 손실되면 0x1A를 포함합니다.

포럼에서 0x1A가 특수 문자 (EOFCHAR)이고 Windows에서이 작업을 특별하게 처리하고 있음을 발견했습니다. 그러나 SerialStream을 디 컴파일하고 EOFCHAR을 다른 바이트 값으로 변경하면 this solution이 도움이되지 않습니다. 전체 바이트 범위 (0..255)를 사용해야합니다.

는 I 동일한 컴퓨터 FT232R-TX 연결된 2 직렬 어댑터를 사용하여, 반복적으로 동일한 바이트를 전송하는 제 문제를 재현하기 위해 소정의 테스트 프로그램을 생성 FT232H-RX하는

using System; 
using System.IO.Ports; 
using System.Collections.Generic; 
using System.Text; 

namespace SerialPort_0x1A_Loss_Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      const byte BYTE_FILL = 0x1A; // 0x1A is EOFCHAR 
      const int BAUD_RATE = 3000000; // 3 MegaBaud 
      const int BUFF_SIZE = 1000000; 

      SerialPort sp1_FT232R = 
       new SerialPort("COM3", BAUD_RATE, Parity.None, 8, StopBits.One); 
      SerialPort sp2_FT232H = 
       new SerialPort("COM6", BAUD_RATE, Parity.None, 8, StopBits.One); 

      sp1_FT232R.Encoding = Encoding.GetEncoding(1252); 
      sp1_FT232R.WriteBufferSize  = 20000000; 
      sp1_FT232R.Open(); 

      sp2_FT232H.Encoding = Encoding.GetEncoding(1252); 
      sp2_FT232H.ReadBufferSize   = 20000000; 
      sp2_FT232H.ReceivedBytesThreshold = 20000000; 
      sp2_FT232H.Open(); 

      byte[] bufferTx = new byte[BUFF_SIZE]; 
      for (int i = 0; i < BUFF_SIZE; i++) 
      { 
       bufferTx[i] = BYTE_FILL; 
      } 

      Console.WriteLine("Sending ..."); 
      sp1_FT232R.Write(bufferTx, 0, BUFF_SIZE); 
      Console.WriteLine("Sending finished. " + 
       "Press a key to view status, ESC to exit."); 

      // Receiving maybe not yet finished, 
      // query the status with a keypress 
      while (Console.ReadKey(true).Key != ConsoleKey.Escape) 
      { 
       Console.WriteLine("TOTAL RX = " + sp2_FT232H.BytesToRead); 
      } 

      // A second test, using .Read() call 
      // This will be executed after pressing ESC for the previous test 
      int totalRX_Read_Call = 0; 
      var listBufferRx = new List<byte>(); 
      int btr; // BytesToRead 
      while ((btr = sp2_FT232H.BytesToRead) > 0) 
      { 
       var bufferRx = new byte[btr]; 
       totalRX_Read_Call += sp2_FT232H.Read(bufferRx, 0, btr); 
       listBufferRx.AddRange(bufferRx); 
       Console.WriteLine("totalRX_Read_Call = " + totalRX_Read_Call + 
        "; listBufferRx.Count = " + listBufferRx.Count); 
      } 
      Console.ReadKey(); 

      sp1_FT232R.Close(); 
      sp2_FT232H.Close(); 
     } 
    } 
} 

테스트 결과 (BUFF_SIZE 연결된 = 1000000) 모든 시험 : 2, 3, 4, 그러나 낮은 같은 시험에도

1. BYTE_FILL = 0x55; BAUD_RATE = 3000000; TOTAL RX = 1000000 (no loss) 
2. BYTE_FILL = 0x1A; BAUD_RATE = 3000000; TOTAL RX = 333529 (66% loss) 
3. BYTE_FILL = 0x1A; BAUD_RATE = 2000000; TOTAL RX = 627222 (37% loss) 
4. BYTE_FILL = 0x1A; BAUD_RATE = 1000000; TOTAL RX = 1000000 (no loss) 

의 CPU의 부하 (i7-4770k 4 GHz에서) 높은 (30 % 이상이다) (3 %) 테스트 1에 대한, 나는 다른 모든 패턴을 바이트 (0x00..0x19, 0x1B..0xFF)로 시도했지만 손실이 없다. .

해결 방법이 있습니까? 대단히 감사합니다!

+0

이 테스트는 의미가 없다 실제로는 Read()를 호출하지 마십시오. 하나의 Read() 호출로 모든 데이터를 거의 얻지는 못합니다. 수신 버퍼에있는 모든 것입니다. 0x1a 바이트가 포함되면 더 적습니다. DataReceived 이벤트를 사용하는 경우 e.EventType 속성을 무시하지 않아야합니다. –

+0

@HansPassant 답장을 보내 주셔서 감사합니다. 내 질문의 코드를 편집하고 Read()로 테스트를 추가했으며 그 결과는 같습니다. 또한, 내 응용 프로그램에서는 DataReceived 이벤트를 사용하고'e.EventType == SerialData.Eof'를 반환하려고했지만 0x1A 데이터 손실 문제는 해결되지 않았습니다. 8 MBaud에서 0x1A 만 보낼 때 전송하는 것의 약 5 % 만 수신합니다. – nnn

+0

나는 어떤 것들이 문제가 있는지보기 위해 모든 8 비트 패턴으로 이것을 시도 할 것이다. * 단지 * 1A라면 소프트웨어 문제 일 수 있습니다. 그렇지 않으면 아마 하드웨어가 그것을 처리 할 수 ​​없을 것입니다. – Gabe

답변

3

첫째, 내가 설명 할 수 없다. 문제가 EOF 문자를 설정한다고 생각할 수도 있지만, 그건 의미가 없습니다.

documentation for the DCB (device control block) structureEofChar이 "데이터의 끝을 알리는 데 사용되는 문자의 값"임을 나타내지 만 그 의미는 설명하지 않습니다. 신비한 EofChar에 대한 다른 언급은 찾을 수 없습니다. 또한 동일한 페이지에 fBinary 회원에 대해 다음과 같이 명시되어 있습니다. "이 멤버가 TRUE이면 이진 모드가 활성화되고 Windows는 이진 모드 전송을 지원하지 않으므로이 멤버는 TRUE 여야합니다."

연결은 무엇입니까? 즉

 fBinary - If fBinary is set to zero, reception of the EofChar 
     character indicates the end of the input stream. ReadComm() 
     will not return any characters past EofChar. If any characters 
     are received after EofChar, it will be treated as overflowing 
     the receive queue (CE_RXOVER). The reception of EofChar is 
     indicated in the COMSTAT status flag CSTF_EOF. If fBinary is 
     set to one, the EofChar character has no special meaning. 

EofCharfBinary은 제로입니다, 아직 윈도우가 더 이상 모드, 따라서 EofChar는 것 같다 것을 지원하는 경우 사용됩니다 : 음, kb101419 상태는 16 비트 윈도우에서 작동하는 데 사용하는 방법 무시.

그래서 0x1A은 (는) 특수 문자로 취급 되나요? DCB에는 EvtChar이라는 또 다른 멤버가 있으며 The value of the character used to signal an event으로 정의됩니다. 이 문자가 포트에서 수신되면 포트의 이벤트가 신호되고 해당 포트에 EV_RXFLAG 비트가 설정됩니다. SerialData 열거 형은 Eof = NativeMethods.EV_RXFLAG을 정의합니다. 따라서 EOF가 어떤 의미인지 혼란 스럽습니다.

그래, 그렇다고해서 그 문자가 데이터 손실을 일으키는 이유는 설명하지 않습니다.그것에 대해 어떤 설명서도 찾을 수 없지만 포트에서 EvtChar을 수신하면 이벤트가 신호되고 해당 이벤트가 지워질 때까지 더 이상 데이터가 버퍼되지 않습니다. 낮은 데이터 속도에서 이벤트는 다른 바이트가 수신되기 전에 지워 지므로 아무도 이전에 문제를 발견하지 못했습니다. 높은 데이터 전송률에서는이 기간 동안 수천 바이트가 수신 될 수 있습니다. 그리고 이것이 실제로 직렬 포트 드라이버의 측면이라면 다른 시스템에서 동작을 재현하기 어려울 수 있습니다.

이제 문제는이 동작을 비활성화하는 방법입니다. SerialStream 클래스는 항상 EvtChar0x1A으로 설정합니다. 문제를 해결하기보다는 다른 바이트로 변경하면 문제가 이동하기 때문에 문제가되지 않습니다. 실제 문제는 EV_RXFLAG 비트 (0x0002)가 설정된 SetCommMask을 호출하여 발생한다고 생각합니다. 불행하게도이 플래그는 이벤트를 청취하고 있는지 여부에 관계없이 항상 설정됩니다. 즉, 드라이버가 항상 신호를 보내야합니다.

해당 비트가 지워진 SetCommMask으로 전화하여 문제를 해결할 수있을 것으로 생각됩니다. SerialStream의 기본값은 0x1fb (모든 비트는 EV_TXEMPTY)입니다. EV_RXFLAG0x002이므로 0x1F9을 전달하여 삭제할 수 있습니다.

SetCommMask의 P/호출 서명은 다음과 같습니다

using Microsoft.Win32.SafeHandles; 
using System.Runtime.InteropServices; 

    [DllImport("Kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)] 
    static extern bool SetCommMask(
     SafeFileHandle hFile, 
     int dwEvtMask 
    ); 

얻으려면 hFile 당신이 sp1_FT232R.BaseStream_handle 필드를 얻기 위해 반사를 사용해야 할 것입니다 : 당신이 경우

var _handle = (SafeFileHandle)sp1_FT232R.BaseStream.GetType() 
       .GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance) 
       .GetValue(sp1_FT232R.BaseStream); 
SetCommMask(_handle, 0x1F9); 
+0

이것은 문제를 해결했습니다! 지금 막 실제 시스템에서 100MB를 초과하는 '0x1A'만 보내고 '0x00'과'0xFF'를 스윕하여 바이트가 손실되지 않도록 테스트했습니다. 위의 호출을'serialPort1.Open(); 다음에 삽입했습니다. 고맙습니다! – nnn

0

다른 엔코더 사용을 고려해보십시오. 저는 8 비트 MCU에서 작은 (100 바이트) 데이터 스트림을 자주 처리 할 때 windows-1252 인코더를 사용하는 것을 좋아합니다. 그 문자를 수신하는 바이트가 손실 될 수 왜 모든

http://msdn.microsoft.com/en-us/library/aa332096(v=vs.71).aspx

+0

방금 ​​인코딩을 Windows-1252로 설정하여 내 게시물을 편집했습니다. 하드웨어에서 테스트 한 결과 여전히 문제가 있습니다 (높은 보드 속도로 0x1A를 보낼 때만). – nnn