Forum: PC-Programmierung C# ComPort Problem


von Jan (Gast)


Lesenswert?

Hallo

Ich bin nun schon seit fast einer Woche dabei den folgenden Fehler zu 
Suchen.

Rahmenbedingungen:

Auf dem uRechner läuft ein Programm, welches Messwerte erfassen soll. 
Diese werden in Arrays gespeichert. Ein kleine C# Sharp Programm soll 
die Messerte auslesen und in einer Datei Speichern, welche mittels Ajax 
auf eine Webseite geladen wird.

Auf dem uRechner sind Baudraten und Parity so wie Stop bits richtig 
gesetzt. Es wird ein 20MHz eingesetzt. Die Taktrate habe ich mit 
_delay_ms(1000) und einer blinkenden LED schon getestet.

das Problem:

Sende ich ein bestimmtes Zeichen an den Controller beginnt dieser damit 
254 Byte an den PC zu schicken. Mittels hTerm klappt dies Wunderbar. Bei 
C# kommen aber immer unterschiedlich viele Zeichen an. meistens so an 
die 120 bis 180 und dann hört es einfach auf.

Mitlerweile habe ich das Programm schon so angeändert, dass immer nur 25 
byte übertragen werden und der uRechner dann erst auf eine Checksumme 
des PCs Wartet. Aber selbst hier treten sehr oft Checksummen Fehler auf 
oder es kommen nicht alle 25 Byte am PC an. Hier nochmal. Mit hTerm kein 
Problem.

In Visual C# nutze ich die fertige SerialPort Klasse. Es sind 8 bit 
Daten, 2 Stop bits und keine Parität eingetragen. Der Puffer ist auf den 
Standartmäßigen 4kb. Es ist keine Timeout überwachung.

Die Daten lese ich mit dem Interrupt aus, welches ausgelöst wird, wenn 
sich ein neues byte im Puffer befindet. Es wird dann immer nur ein byte 
mit .Readbyte() aus dem ComPort gelesen. Trotzdem gehen jede Menge Daten 
verloren oder kommen fehlerhaft an.

Die einstellungen bei Visual C# sind genau die gleichen wie bei hterm.
Was vieleicht noch eine Interessante Information ist, ist dass es auf 
meinem Desktop PC auch mit C# wunderbar klappt. Auf allen anderen 
Laptops allerdings nicht. Die USB-Serial Adapter sind die gleichen mit 
gleichem Treiber und gleichem OS.

Am ATmega644 ist ein MAX232 vor den UART geschaltet.

Ich hoffe mir kann jemand helfen. Bin echt am verzweifeln. Ich würde 
auch euf eine andere Programmiersprache umsteigen, wenn mir jemand die 
Einbindung der Bibliotheken erklährt. Vieleicht gibt es auch ne 
Alternative zur .net Klasse von C#.

Ein Umsteigen auf Visual C++ oder VB wird wahrscheinlich keinen sinn 
machen, da diese ja die gleiche Klasse verwenden.

Danke für eure Bemühungen.

von Peter (Gast)


Lesenswert?

der fehler liegt sehr wahrscheinlich im Quellcode. Ohne den können wir 
nicht sinnvoll zu hilfe beitragen.

von Jan (Gast)


Lesenswert?

Hmm dass das als Antwort kommt habe ich mir fast gedacht. Prolem ist, 
dass der Qullcode ziemloch groß ist. Ich veruche mal die Relevanten 
Teile darzustellen:

c#
1
//Comport Initialisieren
2
ComPort = new System.IO.Ports.SerialPort(this.components);
3
ComPort.Parity = System.IO.Ports.Parity.None;
4
ComPort.BaudRate = 9600;
5
ComPort.PortName = "COM10";
6
ComPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(ComPort_DataReceived);
7
ComPort.DataBits = 8;
8
ComPort.StopBits = 2;
9
10
//mit einem Klich auf eine Schaltfläche wird der Comport geöfnet
11
12
private void onButton(object sender, EventArgs e)
13
{
14
    try
15
   {
16
          ComPort.Open();
17
          ComPort.Write(new byte[] { (byte)(0x02) }, 0, 1);
18
   }
19
   catch (Exception ex)
20
   {
21
       MessageBox.Show(ex.Message);
22
   }
23
}
24
25
private int counter; //Steht hier natürlich nicht in echt ;)
26
27
private void ComPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
28
{
29
  byte puffer = Convert.ToByte(ComPort.ReadByte());
30
  Console.WriteLine("Befehlteil: " + counter + "//" + puffer);
31
  counter++;
32
}

Und der Atmel
1
#define BAUD 9600UL      //halbe Baudrate
2
 
3
// Berechnungen
4
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
5
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate
6
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler.
7
 
8
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
9
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! 
10
#endif  
11
12
void USART1_init()
13
{
14
  //Baudrate einstellen
15
    UBRR1H = UBRR_VAL >> 8;
16
    UBRR1L = UBRR_VAL & 0xFF;
17
18
  //Empfangen und Senden
19
  //8 Bit
20
  //2 Stop Bit
21
  //None Parity
22
23
  UCSR1B = 0b10011000;
24
  UCSR1C = 0b10001110;
25
}
26
27
void USART1_send_Byte(uint8_t byte)
28
{
29
  while(!( UCSR1A & (1<<UDRE1))); //warten auf Datenregister empty;
30
  //_delay_ms(40); //Steht dieser wert auf 100 oder mehr läuft es einigermaßen gut. Ist aber ja kein Zustand
31
  UDR1= byte;
32
}
33
34
ISR (USART1_RX_vect) 
35
{
36
  cli();
37
  uint8_t puffer = UDR1;
38
         if(puffer == 0x02)
39
         {
40
               for(uint8_t i = 0; i<254;i++)
41
               {
42
                 USART1_send_Byte(Datenarray[i]); // das Array ist natürlich lang genug und woanders definiert
43
                }
44
         }
45
}

Das ist ja eigendlich alles was man wissen muss denke ich. Ich habe 
vorsichtshalber noch einmal ein sehr abgespecktes Programm geschrieben, 
was nur mit diesem Quellcode aufgebaut ist. Auch da kommen nach manchmal 
gerade 20 byte keine neuen Daten mehr an. Mit hterm werden ohne Probleme 
254 Byte mit dem Richtigen Inhalt empfangen.

von Peter (Gast)


Lesenswert?

Jan schrieb:
> private void ComPort_DataReceived(object sender, 
System.IO.Ports.SerialDataReceivedEventArgs e)
> {
>   byte puffer = Convert.ToByte(ComPort.ReadByte());
>   Console.WriteLine("Befehlteil: " + counter + "//" + puffer);
>   counter++;
> }
woher weist du das nur 1byte ankommt?

while ( ComPort.BytesToRead > 0 ) {
   byte puffer = Convert.ToByte(ComPort.ReadByte());
   Console.WriteLine("Befehlteil: " + counter + "//" + puffer);
   counter++;
}

sinnvoller ist es aber vermutlich gleich alle verfügbaren bytes mit Read 
in ein array einzulesen.

von Jan (Gast)


Lesenswert?

Könnte ich mal ausprobieren. Aber ich muss dann wissen wie viele es 
sind.

Seltsam ist dass es auf dem Desktop PC geht. Aber aber der hat auch 
wesendlich mehr Rechnleistung als der Laptop. Vieleicht ist es ja 
tatsäch so hinzubekommen. Werde morgen mal 2 Testprogramme schreiben und 
hochladen und es da dann direkt ausprobieren.

von Arc N. (arc)


Lesenswert?

Jan schrieb:

> Die Daten lese ich mit dem Interrupt aus, welches ausgelöst wird, wenn
> sich ein neues byte im Puffer befindet. Es wird dann immer nur ein byte
> mit .Readbyte() aus dem ComPort gelesen. Trotzdem gehen jede Menge Daten
> verloren oder kommen fehlerhaft an.

1.: Passendes Encoding eingestellt? Sollte 
System.Text.Encoding.GetEncoding(28591) (ISO-8859-1) sein, da die 
anderen u.U. die übertragenen Werte verändern (ASCII "kürzt" alle Bytes 
> 127, UTF-8 wartet u.U. auf Multibyte-Zeichen etc.)
2.: Wie ist der ReceivedBytesThreshold eingestellt? U.U. diesen höher 
stellen
3.: Im DataReceived-Event sollte mit BytesToRead abgefragt werden wie 
viele Bytes im Buffer sind z.B.
1
private static void SP_OnDataReceived(object sender, SerialDataReceivedEventArgs e) {
2
    SerialPort sp = (SerialPort)sender;
3
    byte[] buffer = new byte[sp.BytesToRead];
4
    sp.Read(buffer, 0, buffer.Length);
5
    // do something
6
}
Falls Strings übertragen werden kann man ReadExisting() verwenden.

von Peter (Gast)


Lesenswert?

Jan schrieb:
> Seltsam ist dass es auf dem Desktop PC geht. Aber aber der hat auch
> wesendlich mehr Rechnleistung als der Laptop.
das ist doch klar, der PC schafft es bei jeden byte die Methode 
aufzurufen, der Laptop schafft es scheinbar nicht. Es aber aber drozdem 
ein Programmierfehler.

von Jan (Gast)


Lesenswert?

Ich habe gerade nochmal ein Testprogramm geschrieben.
Die Usart schnittstelle läuft tatsächlich sehr viel besser, wenn alle 
verfügbaren bytes ausgelesen werden. Es treten zwar immernoch oft Fehler 
auf aber das ist nicht schlimm.

Normalerweise hat aber der Comport schon einen Buffer der 4kb groß ist. 
Selbst wenn mein Laptop also nicht in der lage ist immer gleich die 
bytes einzulesen wenn ich sie einzeln einlese müssten sie aber im 
Speicher puffer bleiben. Der uRechner sendet nach 25 byte erst weiter 
wenn ein CRC empfange wurde. Der 4kB speicher kann also nie überlaufen.

Naja Hauptsache es geht so. Danke für die Hilfe.

von Peter (Gast)


Lesenswert?

Jan schrieb:
> Normalerweise hat aber der Comport schon einen Buffer der 4kb groß ist.
> Selbst wenn mein Laptop also nicht in der lage ist immer gleich die
> bytes einzulesen wenn ich sie einzeln einlese müssten sie aber im
> Speicher puffer bleiben. Der uRechner sendet nach 25 byte erst weiter
> wenn ein CRC empfange wurde. Der 4kB speicher kann also nie überlaufen.

das Probelm ist/war das die funktion SerialDataReceivedEventArgs auch 
wenn 25byte im Puffer sind, nur einmal aufgerufen wird. Wenn du jetzt 
nur 1byte abrufst, dann bleiben noch 24byte im Puffer und es erfolgt 
kein ReceivedEvent mehr, weil ja nichts neues Empfangen wurde.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.