2016-09-02 35 views
6

Wybacz, szybkie pytanie:port komunikacji szeregowej na tym samym komputerze z systemem Windows nie działa

mam tę konfigurację sprzętową:

sama maszyna: "Com3" -> USB -> to Serial -> USB -> "Com4"

i następnie MSDN SerialPort Class i MSDN SerialPort.ReadLine() budować ten rutynowe:

SerialPort SendSerialPort = new SerialPort("Com3", 9600); 
SerialPort ReceiveSerialPort = new SerialPort("Com4", 9600); 

SendSerialPort.Open(); 
ReceiveSerialPort.Open(); 

SendSerialPort.WriteLine("Test"); 
var message = ReceiveSerialPort.ReadLine(); // control stops here 

SendSerialPort.Close(); 
ReceiveSerialPort.Close(); 

Console.WriteLine(message); 

Jednak gdy mam tendencję do ReadLine(), moja kontrola zatrzymuje się i po prostu czeka. Nie spodziewałem się tego.

Spodziewam się otrzymać ciąg Test i przypisać go do mojego var message. Czy możesz mi powiedzieć, co tu robię źle?

EDIT:

testowałem mój sprzęt używając Serial Port Utility Application i it worked just fine.

+1

Nie używasz żadnych operacji gwintowania –

+1

Operacje we/wy są blokowane. Musisz przenieść co najmniej jedną z operacji we/wy do innego wątku/zadania. – rene

Odpowiedz

4

mam zmieniony from the example you linked:

Aby rzeczywiście oba porty działa do odczytu i zapisu iz powrotem będzie faktycznie potrzeba wdrożenia wątków do czytania i pisania dla obu.

Dobrym pomysłem może być skorzystanie z timera.

public static void Main() 
{ 
    SerialPort SendSerialPort = new SerialPort("Com3", 9600); 
    SerialPort ReceiveSerialPort = new SerialPort("Com4", 9600); 

    StringComparer stringComparer = StringComparer.OrdinalIgnoreCase; 
    Thread readThread = new Thread(Read); 

    // Set the read/write timeouts 
    _serialPort.ReadTimeout = 500; 
    _serialPort.WriteTimeout = 500; 

    SendSerialPort.Open(); 
    ReceiveSerialPort.Open(); 
    bool _continue = true; 
    readThread.Start(); 

    Console.Write("Name: "); 
    name = Console.ReadLine(); 

    Console.WriteLine("Type QUIT to exit"); 

    while (_continue) 
    { 
     message = Console.ReadLine(); 

     if (stringComparer.Equals("quit", message)) 
      _continue = false; 
     else 
      SendSerialPort.WriteLine(String.Format("<{0}>: {1}", name, message)); 
    } 
    readThread.Join(); 
    SendSerialPort.Close(); 
} 

public static void Read() 
{ 
    while (_continue) 
    { 
     try 
     { 
      string message = ReceiveSerialPort.ReadLine(); 
      Console.WriteLine(message); 
     } 
     catch (TimeoutException) { } 
    } 
} 

Zazwyczaj będzie początkiem i wartość końcowa terminie pisemnych danych powiedzieć innego portu, że wiadomość jest zakończona, a także do portów w celu sprawdzenia poprawności, które są odczytu danych powinny być, zazwyczaj z poleceniami co zrobić z tymi danymi. (poza zakresem tego pytania).

Brakuje również i ważna jest intializacja portów.

Wolę użyć konstruktora domyślnego (preferencje tylko)

SerialPort Constructor() 

a następnie ustawić dowolne wartości tak:

_serialPort.BaudRate = SetPortBaudRate(_serialPort.BaudRate); 
_serialPort.Parity = SetPortParity(_serialPort.Parity); 
_serialPort.DataBits = SetPortDataBits(_serialPort.DataBits); 
_serialPort.StopBits = SetPortStopBits(_serialPort.StopBits); 
_serialPort.Handshake = SetPortHandshake(_serialPort.Handshake); 

Wszystkie konstruktorzy daje następujące wartości:

This constructor uses default property values when none are specified. Na przykład właściwość DataBits przyjmuje domyślną wartość 8, właściwość Parity ma domyślną wartość None enume wartość racji, właściwość StopBits przyjmuje wartość 1, a domyślna - COM1.

Nawet handshake ma wartość domyślną. Jeśli jesteś look at the source code.

private const Handshake defaultHandshake = Handshake.None; 
+1

Nie mogłem odtworzyć problemu. Dlatego zakładam, że był to przypadek, ponieważ parametry "DataBits", 'StopBits' i' Parzystość' [są ustawione domyślnie] (http://stackoverflow.com/questions/39297839/are-uninitialised-attributes- given-default-values-as-one-are-when-using-the-def). Chcę się upewnić, że to już nie nastąpi, dlatego użyję powyższej zalecanej metody wielu wątków do ciągłego odczytu i zapisu przez port szeregowy. –

+1

@FirstStep Od pierwszej instancji Jeśli wystąpił błąd lub inny problem z odczytem, ​​mógł on tam zostać, nie wiedziałbyś o tym bez logowania. I tak asynchroniczne zadanie lub wątkowanie (jak już wspomniałem, zadania asynchroniczne używają wątków, za kulisami) lub jakiś typ obsługi zdarzeń. Jest to interesujące i czekam na wiadomość o twoim rozwoju. –

2

Nie można zablokować, gdy dostępne są dane. To, co wysłałeś, utknęło w buforze nadawczym, zgubiło się z powodu błędu okablowania, wywołało błąd lub zostało zignorowane. Jeśli działa z innym programem, błąd okablowania nie może być problemem.

Należy pamiętać, że samo ustawienie prędkości transmisji nie wystarczy, ale należy również ustawić właściwości DataBits, parzystości i stopów, aby dopasować ustawienia urządzenia. Niezgodność może wywołać błąd, rodzaj, który można zobaczyć tylko podczas pisania programu obsługi zdarzeń dla zdarzenia ErrorReceived. Nigdy nie pomijaj tego wydarzenia, mogą pojawić się kłopotliwe problemy, jeśli nigdy nie sprawdzasz.

A przede wszystkim właściwość Handshake musi być poprawnie ustawiona. Właściwa wartość zależy od tego, jak porty są połączone, jest zbyt powszechna, aby ich nie łączyć. Zacznij od ustawienia Handshake. Brak, więc zły stan sygnałów DSR i CTS nie może zablokować odbioru i zły stan dla sygnałów DTR i RTS nie może zablokować transmisji. Uważaj, aby inne programy włączały funkcję uzgadniania sprzętowego, a niedopasowanie zagwarantuje, że komunikacja będzie utrudniona.

Jeśli korzystasz z odczytów synchronicznych zamiast zdarzenia DataReceived, powinieneś zasadniczo poradzić sobie z tym, że urządzenie nie odpowiada. Albo dlatego, że jest wyłączony, nie podłączony albo działa nieprawidłowo. Skorzystaj z właściwości ReadTimeout, aby program nie mógł się zawiesić. Celuj wysoko, 10000 milisekund to rozsądny wybór.

Uważaj na losowość tego problemu, po prostu zamarznij z innym programem, możesz łatwo skonfigurować port poprawnie, a teraz zadziała. I strzeż się, że uruchomienie nici nic nie da, to będzie ten wątek, który utknie, a wywołanie Join() stanie się impasem.

+0

Jedna z Q: Czy polecasz potrzebę wielu wątków? Działało na tym samym wątku. Zakładam, że potrzebuję tylko upewnić się, że poprawna/prawidłowa konfiguracja. –

+1

Próbowałem was ostrzec w ostatnim akapicie, że używanie nici nie jest rozwiązaniem. Więc nie. Musisz się skupić na konfiguracji, to twój problem tutaj. Zasadą twardą jest to, że nigdy nie można na nią skrócić. –

+0

Aby go uruchamiać w sposób ciągły, potrzebne będzie zadanie wątku lub asynchronizacji. Jest to w porządku do jednorazowego testu. Szczególnie jeśli chcesz przesyłać dane pomiędzy tymi dwoma portami. –

5

Problem z kodem jest w tej linii

var message = ReceiveSerialPort.ReadLine(); 

zablokować kod czekać na linii, jeśli linia nie dotrze to pozostanie tu na zawsze lub wartość ustawiona ReadTimeout

Dlaczego więc linia nigdy nie nadejdzie?

Problemem może być błąd w WriteLine("Test"); należy obsłużyć błędy, lub może być, że w blokują kod ReadLine() przed zarządzać WriteLine("Test") przyjść poprzez, można wstawić Thread.Sleep(100) pomiędzy, ale nie jest to Naprawdę ulepszam kod.

Uwaga: Twój kod będzie działał tak, jak czasami, w zależności od warunków wyścigu.

Ten zsynchronizowany/blokujący odczyt z portów szeregowych wydaje się prosty w kodzie tylko w jednej linii; ale powoduje to wiele negatywnych skutków ubocznych w twoim protokole komunikacyjnym.

Znacznie lepszym rozwiązaniem (biorąc pod uwagę, że lubisz czytać/zapisywać dane z mikrokontrolera) jest użyć wątku jako Yvette suggested lub użyj asynchronicznie czytanie Stream.BeginRead (Byte[], Int32, Int32, AsyncCallback, Object) których wolałbym.

Asynchroniczne odczytywanie spowoduje zgłoszenie zdarzenia, gdy coś znajdzie się na porcie szeregowym. Podstawową ideą tej strategii programowania jest nieskrępowanie programowania krokowego, ale oczekiwanie rezultatów, a następnie poprawne ich przetwarzanie.

W protokole komunikacyjnym z asynchronicznie przeczytaniu AutoResetEvent jest bardzo przydatny, stąd wysłać coś, a następnie uruchomieniu AutoResetEvent, czy asynchronicznie oczekiwany wynik jest przyjazd będzie ustawić tę imprezę i kod może być kontynuowane, jeśli nie dotrze AutoResetEvent upłynie limit czasu i możesz sobie z tym poradzić.

+0

Dziękujemy za odpowiedź. Mam Q, jeśli nie masz nic przeciwko temu, że jeśli "moja linia nigdy nie nadejdzie", dlaczego nigdy nie przybyła i zajęła tak długo? Mój kod powinien pozostać w 'Read()' dla "_100_" milisekund, powiedzmy, następnie i kontynuować, gdy nadejdą wiadomości. czego mi brakuje? –

+0

To mógł być błąd podczas wysyłania, mogło to być, że zablokowałeś wysyłanie (nie miał czasu na rozpoczęcie) zanim zablokowałeś go w readLine (stąd readLine() faktycznie blokuje twój kod), podczas gdy writeLine nie jest. –

+0

oh, nie miał czasu, aby przejść na pierwszym miejscu i został "_interupted_". Widzę. Dzięki za lekcję! –