Forum: PC-Programmierung ReadFile WriteFile an serielle Schnittstelle


von Zoran St. (Gast)


Lesenswert?

Hallo!

Nach stundenlangen Tests mal hier die Anfrage bei euch:
Ich habe auf Basis der LUFA Library und einem USB-AVR eine Firmware mit 
zwei virtuellen Schnittstellen erstellt.
eine dient der Kommunikation zw. AVR und PC.

Jetzt habe ich das Problem, dass meine Applikation nicht funktioniert 
und ich weiß gerade nicht weiter:
Ich verwende die WinAPI um über die serielle mit dem Gerät zu 
kommunizieren.
Mittlerweile bin ich der Meinung, dass das Problem beim Windows eigenen 
USB-seriell Treiber oder im FileRead bzw. FileWrite liegt.

Zum Programmablauf:
Es werden mehrere Byte (zw. 2 und 20) an das Gerät geschickt und danach 
1 Byte zurückgelesen.

Hier ein "Code-Schnippel":
// create file not overlapped
        flasher_port = CreateFile(strcat(buf, jtag_port), GENERIC_READ 
|GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

        ....
        // write some bytes --> 2 .... 16 bytes
        if (!WriteFile(flasher_port, usb_cache, 2 + usb_cache_cnt + 3 / 
4, &written, NULL)) {
            fprintf(stderr, "Sending BLOCK_DATA error!\n");
            exit(1);
        }
        // read 1 byte
        if (!ReadFile(flasher_port, recbuf, sizeof (unsigned char), 
&read, NULL)) {
            fprintf(stderr, "Can't receive BLOCK_DATA response!\n");
            exit(1);
        }

Jetzt kann es passieren, dass ReadFile keinen Fehler zurückmeldet, aber 
statt 1 Byte (sizeof (unsigned char)) leider 0 in die read Variable 
schreibt. Das ganze natürlich obwohl das Gerät geantwortet hat, d.h. 
dieses Byte übertragen hat.
Dadurch stürzt mein Programm in weiterer Folge ab.
Ich habe nun eine Zeile eingefügt in der das Programm bevor es weiter 
macht überprüft, ob eh ein Byte gelesen wurde und falls nicht dann noch 
ein Mal liest.
So funktioniert dieser Teil des Programmes dann.

Leider aber gibt es da noch einen Fehler beim Schreiben, d.h. manchmal 
ist der Returnwert von WriteFile 0 und das Programm beendet wieder.

Dieses Problem habe ich nur mit einem Rechner wirklich extrem (i5 CPU). 
Bei einem anderen, langsameren Rechner (Core2Duo) passiert das nicht. 
Beide sind XP-SP3 rechner mit gleichem Softwarestand.
Jemand eine Idee?
Kann es an irgendwelchen Buffern liegen? Oder hat jemand einen debug 
Ansatz?

Danke im Vorhinein.

von Peter II (Gast)


Lesenswert?

Ich habe zwar in der Doku auf die schnelle nichts gefunden, aber ich 
kann es sein das bei einem timeout dieses Verhalten auftritt?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Zoran St. schrieb:
> if (!WriteFile(flasher_port, usb_cache, 2 + usb_cache_cnt + 3 /
> 4, &written, NULL))

Das macht ziemlich sicher nicht das, was Du willst. Die Anzahl der zu 
schreibenden Bytes dürfte deutlich größer sein als das, was Du 
erwartest.

Sieh Dir den Ausdruck

  2 + usb_cache_cnt + 3 / 4

mal genau an.

Was wird durch 4 geteilt?

von Zoran St. (Gast)


Lesenswert?

Hallo!
Danke f.d. schnellen Antworten.
Zuerst : ja, es sollte 2 + (usb_cache_cnt + 3) / 4 heißen und
Ja, es ist die Timeout Ausgabe von Readfile.
Sorry, das hatte ich mittlerweile vergessen. Wenn ich das Timeout 
deaktiviere, dann bleibt die SW stehen. Mit dem Timeout konnte ich 
wenigstens Readfile noch ein Mal ausführen.
Es scheint so als ob windows nicht mitkriegt, dass das Byte schon da 
ist.

Den Errorcode von Writefile muss ich noch ermitteln.

Danke,
Zoran

von W.S. (Gast)


Lesenswert?

Zoran St. schrieb:
> Hier ein "Code-Schnippel":

Ja, sieht grausig aus.

Hier ein Schnippel von mir, bei dem weiß ich wenigstens, daß er 
funktioniert:

{ Write a string out the COM port, return true if all chars written }
function TComPort.Write (const sData: String): DWORD;
var
  dwCharsWritten: DWORD;
  i: longint;
begin
  dwCharsWritten := 0;
  Result := 0;
  if hPort <> INVALID_HANDLE_VALUE
  then
  begin
    WriteFile(hPort, PChar(sData)^, Length(sData), dwCharsWritten, nil);
    result:= dwCharsWritten;
  end;
end;

bemerke bitte, daß mich der Rückgabewert von WriteFile nicht 
interessiert, sondern die Sache über dwCharsWritten geregelt wird.

{ Return the number of bytes waiting in the queue }
function TComPort.GetInCount: LongInt;
var
  statPort: TCOMSTAT;
  dwErrorCode: DWord;
  i: LongInt;
begin
  i := 0;
  if hPort <> INVALID_HANDLE_VALUE
  then begin
         ClearCommError(hPort, dwErrorCode, @statPort);
         i:= statPort.cbInQue;
       end;
  if inPtr > outPtr
  then begin
         i:= i + inPtr - outPtr;
       end;
  Result:= i;
end;


function TComPort.Read: Char;
var
  Ch_Cnt,
  Ch_Read: DWord;
begin
  Result := #0;
  if inPtr > outPtr          { Puffer ist nicht leer, also daraus lesen 
}
  then begin
         Result:= RxBuff[outPtr];
   inc(outPtr);
         exit;
       end;
  { Puffer ist leer, also Windows bemühen... }
  if hPort = INVALID_HANDLE_VALUE then exit;
  Ch_Cnt:= GetInCount;
  if Ch_Cnt=0 then exit;    { wenn nix vorhanden, Ende }
  if Ch_Cnt > buffermax then Ch_Cnt:= buffermax;

  ReadFile(hPort, RxBuff, Ch_Cnt, Ch_Read, nil);
  inPtr:= Ch_read;
  outPtr:= 0;
  if inPtr > 0
  then begin
         Result:= RxBuff[0];
         outPtr:= 1;
       end;
end;

Und hier wird der Stream programmintern gepuffert und wieder über die 
Anzahl gelesener Zeichen die Sache geregelt.

W.S.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ob unkommentierter Delphi-Code jemandem hilft, der in C programmieren 
möchte?

von Purzel H. (hacky)


Lesenswert?

Das Comport ist bei Windows leider vermurkst. Die Ideen einem 
Characterdevice das Verhalten eines Block- oder Stream-devices 
ueberzustuelpen ist derart absurd von Beginn weg. Am Sinnvollsten nimmt 
man eine Library, die zur Sprache passt und das schoen versteckt.

von Programmierer (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Ob unkommentierter Delphi-Code jemandem hilft, der in C programmieren
> möchte?
Dann soll er "begin/end" durch "{}" ersetzen, den Code als symbolische 
Programmiersprache akzeptieren und das Ding zwischen den Ohren 
aktivieren.

von Peter II (Gast)


Lesenswert?

Zweieinhalb Oschi schrieb:
> Das Comport ist bei Windows leider vermurkst. Die Ideen einem
> Characterdevice das Verhalten eines Block- oder Stream-devices
> ueberzustuelpen ist derart absurd von Beginn weg. Am Sinnvollsten nimmt
> man eine Library, die zur Sprache passt und das schoen versteckt.

versteht nicht wo dein Problem ist. Wo wird dann etwas von BLock und 
Characterdevice device gemixt? Es gibt eine Read und eine Write funktion 
und die arbeiten so wie man es erwartet. Wenn es ein Blockdevice ist 
könnte man ja mit Seek arbeiten was aber nicht geht.

von Zoran S. (zoran_s)


Lesenswert?

Hallo Allerseids!

Danke für die Info, dass das delphi code ist. Damit hatte ich noch 
nichts zu tun. Leider verstehe ich deshalb auch nicht viel davon, aber 
versuchen wir es mal:
Eigentlich scheint sich dein Programm nur darin zu unterscheiden, dass
dich die Return Werte von Read- und Writefile nicht interessieren -> ich 
breche danach ab.
Wieso (bzw. wann) wird ClearCommError aufgeführt?
Mich würden auch die Einstellungen der COM Schnittstelle interessieren 
(Timeouts, DCB). Vielleicht ist dort der Hund drin.

Ich habe schon einiges versucht:
wenn BytesRead != BytesToRead dann wiederhole ich ReadFile bis zu 10 
Mal.
Meistens funktioniert es wenn ich einmalig wiederhole, dann liest das 
Programm den empfangenen Wert auch aus.
Hier die beiden Funktionen die mir die Sorgen bereiten:
1
void flush_serial_cache(void) {
2
  int i;
3
  if (serial_cache_cnt) {
4
    countit++;
5
    //Send the cache via SERIAL but update the cache header before
6
    serial_cache[0] = BLOCK_DATA | (serial_cache_cnt >> 8 & 0xf);
7
    serial_cache[1] = serial_cache_cnt;
8
    // the cached data is sent without '\n' a.k.a raw !!!
9
    DWORD readed, written;
10
    // send cache to the device
11
    if (!WriteFile(flasher_port, serial_cache, 2 + (serial_cache_cnt + 3) / 4, &written, NULL))
12
      fprintf(stderr, "Sending BLOCK_DATA error!\n"), exit(1);
13
14
    if (written != 2 + (serial_cache_cnt + 3) / 4) {
15
      fprintf(stderr, "Sending BLOCK_DATA error %d instead of %d\n", written, 2 + (serial_cache_cnt + 3) / 4); 
16
      exit(1);
17
    }
18
19
    // clear receivebuffer before reading new data to make sure to read new received data
20
    memset(recbuf, 0, sizeof (recbuf));
21
22
    // Read 1 byte of data 
23
    if (!ReadFile(flasher_port, recbuf, sizeof (unsigned char), &readed, NULL)) {
24
      fprintf(stderr, "Can't receive BLOCK_DATA response!\n");
25
      exit(1);
26
    }
27
    else {  // check readed bytes value, if not as expected retry ReadFile max. 10 times
28
      register unsigned char ntimes = 0;
29
      while (readed != sizeof (unsigned char) && ntimes++<MAX_READ_RETRIES)
30
        fprintf(stderr, "%hhd %s() read-retries: %d\n", ntimes, __FUNCTION__, readed),
31
        memset(recbuf, 0, sizeof (recbuf)),
32
        ReadFile(flasher_port, recbuf, sizeof (unsigned char), &readed, NULL);
33
    }
34
35
    if (*recbuf != BLOCK_DATA) {
36
      fprintf(stderr, "Incorrect BLOCK_DATA response at %d: %x (%x)\n", countit, *recbuf, BLOCK_DATA);
37
      exit(1);
38
    }
39
40
    serial_cache_cnt = 0;
41
  }
42
}
43
44
int single_data_with_read(register int tms_tdi) {
45
  int res;
46
  unsigned char single_with_read[] = {SINGLE_DATA_WITH_READ | (tms_tdi & 3), '\n'};
47
  
48
  if (!WriteFile(flasher_port, single_with_read, sizeof (single_with_read), (DWORD *) & res, NULL))
49
    fprintf(stderr, "Can't write!\n"), exit(1);
50
    
51
  if (res != sizeof (single_with_read)) {
52
    fprintf(stderr, "Send SINGLE_DATA_WITH_READ error %d instead of %d\n", res, sizeof (single_with_read));
53
    exit(1);
54
  }
55
  
56
  memset(recbuf, 0, sizeof (recbuf));
57
  
58
  if (!ReadFile(flasher_port, recbuf, sizeof (unsigned char), (DWORD *) & res, NULL))
59
    res = -1;
60
61
  if (res < 0) {
62
    fprintf(stderr, "Can't receive SINGLE_DATA_WITH_READ response!\n");
63
    exit(1);
64
  }
65
66
  register unsigned char ntimes = 0;
67
  while (res != sizeof (unsigned char) && ntimes++<MAX_READ_RETRIES) {
68
    fprintf(stderr, "%hhd %s() read-retries: %d\n", ntimes, __FUNCTION__, res);
69
    memset(recbuf, 0, sizeof (recbuf));
70
    // Read 1 byte of data 
71
    ReadFile(flasher_port, recbuf, sizeof (unsigned char), (DWORD *) & res, NULL);
72
    }
73
74
  if ((recbuf[0] & 0xfc) != SINGLE_DATA_WITH_READ) {
75
    fprintf(stderr, "Incorrect SINGLE_DATA_WITH_READ response! %x instead of %x\n", *recbuf, SINGLE_DATA_WITH_READ);
76
    exit(1);
77
  } else if (verbose) 
78
    fprintf(stdout, ">> \t%x\n", *recbuf & 3);
79
80
  return recbuf[0] & 1;
81
}

Ich poste noch die Ausgaben von der Konsole ...

Eine andere Frage/ Idee hätte ich noch:
Wenn ich eine dll daraus mache, dann könnte ich das Programm über die 
GUI laufen lassen. Es soll nämlich das Programm von einer JAVA GUI 
aufgerufen werden.
Ich habe die Serielle in JAVA so weit im Griff. wenn ich also statt über 
ReadFile und WriteFile einen anderen Weg hätte die Daten hin und her zu 
schaufeln,
dann könnte ich mal versuchen ob es dann funktioniert.
Hat jemand vielleicht einen Vorschlag, bzw. besser ein Beispiel dafür? 
Pipes, Queues, o.ä.?

Danke,

Zoran

von Peter II (Gast)


Lesenswert?

Zoran S. schrieb:
> Ich habe schon einiges versucht:
> wenn BytesRead != BytesToRead dann wiederhole ich ReadFile bis zu 10

das ist unsinn, entweder geht es beim ersten mal oder die verwendest zu 
kurze timeouts. Arbeite doch erstmal ohne Timeouts (das sollte das 
default sein). Dann blockiert das Read bis etwas gelesen wird.

auserdem muss  BytesRead != BytesToRead nicht falsch sein, wenn man mehr 
als 1 byte erwarten, dann kann es auch sein das er die zeichen nicht auf 
einmal liefert.

von Blackbird (Gast)


Lesenswert?

@Zoran St.:
Deinen Ansatz zum Senden und Empfangen halte ich für fehleranfällig. 
Richtig wäre das eventgetriggerte Empfangen von Bytes in einem eigenen 
Thread und Weiterleiten der Daten zu einem Haupt-Thread. Dort kann dann 
entschieden werden, wo die Daten (zeitlich) hingehören. Das Senden 
sollte auch eventgesteuert im Hintergrund in einem eigenen Thread 
ablaufen.
Damit ist Senden und Empfangen entkoppelt, Verzögerungen durch das OS 
und der seriellen Gegenstelle sind nicht mehr relevant.

Hier ein Beispiel, wie man auch ohne Threads einmal etwas senden kann 
und dann auf die Antwort wartet. Hier wird beim Senden im Hauptprogramm 
nicht auf den Abschluß der Aktion (das Ppysische Aussenden des letzten 
Bytes) gewartet, dafür wird die Zeit aber beim Empfangen durch eine 
Schleife wieder aufgebracht.
1
// Consolen-Programm zum Senden/Empfangen von Bytes über COM (9600-8N1)
2
// Es wird ein String als Start gesendet.
3
// Alle empfangenen Bytes werden zurückgesendet.
4
// OS: W95, W98, W98SE, WinME, WinNT, Win2000, WinXP (und höher)
5
// Note: Fast keine Fehlerbehandlung implementiert!
6
#include <windows.h>
7
#include <stdio.h>
8
9
#define COM_BUFFER_SIZE 256       // Read- und Write-Buffer-Size
10
#define BAUDRATE  CBR_9600 // 9600 Baud
11
#define BYTESIZE  8 
12
#define PARITY    NOPARITY
13
#define STOPBITS  ONESTOPBIT 
14
#define HELP_STRING TEXT("Aufruf mit: progname <COM-Port-Nummer>\r\n")
15
//char COMMAND1[] = {(0x02), (0x49), (0x05), (0x12), (0x03)};
16
char COMMAND1[] = HELP_STRING;
17
18
19
// Hauptprogramm: Aufruf mit: progname <COM-Port-Nummer>
20
int main (int argc, char **argv)
21
{
22
  DCB           dcb;    
23
  DWORD         iBytesWritten;
24
  BOOL          bRet      = true;
25
  DWORD         dwRead    = 0;
26
  DWORD         dwSetMask = EV_RXCHAR | EV_ERR; 
27
  DWORD         dwEvtMask; 
28
  OVERLAPPED    o;
29
  COMMTIMEOUTS  ct;
30
  unsigned char InString[COM_BUFFER_SIZE + 1];
31
  TCHAR         szCOM[6];
32
33
34
  if (argc == 2 &&                              // progname + COM-Port-Nummer ?
35
      atoi (argv[1]) > 0 && atoi (argv[1]) < 5) // COM1 ... COM4?
36
    wsprintf (szCOM, TEXT("COM%s"), argv[1]);   // String "basteln" ...
37
  else
38
  { 
39
    printf (TEXT("\r\nERROR:\t %s"), HELP_STRING);
40
    return (1); // und tschüß ...
41
  }
42
43
  memset (&o, 0, sizeof (OVERLAPPED)); // Struktur mit 0en füllen
44
  o.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); // einen Event setzten
45
46
  HANDLE hCom = CreateFile (szCOM, GENERIC_WRITE | GENERIC_READ, 0, NULL, 
47
                            OPEN_EXISTING, 0, NULL);                     
48
49
  if (hCom == INVALID_HANDLE_VALUE)
50
  { // Fehlerausgabe:
51
    LPVOID lpMsgBuf;
52
    FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 
53
                    FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
54
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
55
    MessageBox (NULL, (LPCTSTR)lpMsgBuf, "Error: CreateFile", MB_OK | MB_ICONINFORMATION);
56
    LocalFree (lpMsgBuf);
57
    return (1); // und tschüß ...
58
  }
59
  
60
  dcb.DCBlength = sizeof(DCB);  // Laenge des Blockes MUSS gesetzt sein!
61
  GetCommState (hCom, &dcb);    // COM-Einstellungen holen und aendern
62
  dcb.BaudRate  = BAUDRATE;     // Baudrate
63
  dcb.ByteSize  = BYTESIZE;     // Datenbits
64
  dcb.Parity    = PARITY;       // Parität
65
  dcb.StopBits  = STOPBITS;     // Stopbits
66
  SetCommState (hCom, &dcb);    // COM-Einstellungen speichern
67
  
68
  GetCommTimeouts (hCom, &ct);
69
  // Warte-Zeit [ms] vom Beginn eines Bytes bis zum Beginn des nächsten Bytes 
70
  ct.ReadIntervalTimeout         = 1000 / BAUDRATE * (dcb.ByteSize + 
71
                                                     (dcb.Parity == NOPARITY ? 0 : 1) + 
72
                                                     (dcb.StopBits == ONESTOPBIT ? 1 : 2)) * RECVBYTES;
73
  ct.ReadTotalTimeoutMultiplier  = 0;  // [ms] wird mit Read-Buffer-Size multipliziert
74
  ct.ReadTotalTimeoutConstant    = 50; // wird an ReadTotalTimeoutMultiplier angehängt
75
  ct.WriteTotalTimeoutMultiplier = 0;
76
  ct.WriteTotalTimeoutConstant   = 0;
77
  SetCommTimeouts (hCom, &ct);
78
79
  // Zwischenspeicher des serial-Drivers einstellen (für read und write):
80
  SetupComm (hCom, COM_BUFFER_SIZE, COM_BUFFER_SIZE);
81
  SetCommMask (hCom, dwSetMask); // Empfangssignale definieren
82
83
  // Byte(s) senden:
84
  if (!WriteFile (hCom, &COMMAND1, strlen (COMMAND1), &iBytesWritten, NULL)) // Senden der Bytes
85
  { // Fehlerausgabe:
86
    LPVOID lpMsgBuf;
87
    FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 
88
                    FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
89
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
90
    MessageBox (NULL, (LPCTSTR)lpMsgBuf, "Error: WriteFile", MB_OK | MB_ICONINFORMATION);
91
    LocalFree (lpMsgBuf);
92
  }
93
  else
94
    printf ("%d Byte(s) sent.", iBytesWritten);
95
96
  do  // in Endlos-Schleife auf Empfangssignale warten:
97
  {
98
    WaitCommEvent (hCom, &dwEvtMask, &o); // Event mit Empfangssignalen verknüpfen
99
100
    if (WAIT_OBJECT_0 == WaitForSingleObject (o.hEvent, INFINITE)) // warten bis Event
101
    {
102
      if (dwEvtMask & EV_RXCHAR) // Zeichen an RxD empfangen:
103
      {
104
        bRet = ReadFile (hCom, &InString, sizeof (InString), &dwRead, NULL);
105
106
        if (!bRet)
107
        { // Fehlerausgabe:
108
          LPVOID lpMsgBuf;
109
          FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 
110
                          FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
111
                          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
112
                          (LPTSTR) &lpMsgBuf, 0, NULL);
113
          MessageBox (NULL, (LPCTSTR)lpMsgBuf, "Error: ReadFile", 
114
                      MB_OK | MB_ICONINFORMATION);
115
          LocalFree (lpMsgBuf);
116
        }
117
        else
118
        { // Ausgabe (oder Verarbeitung) der empfangenen Bytes:
119
          InString[dwRead] = '\0'; // in "zero-ended"-String verwandeln
120
          printf (TEXT("\r\n\tRxD (%d Byte(s)): %s"), dwRead, InString);
121
          WriteFile (hCom, &InString, dwRead, &iBytesWritten, NULL); // Senden der Bytes
122
        }
123
      }
124
125
      if (dwEvtMask & EV_ERR) 
126
      {
127
        MessageBox (NULL, "Error empfangen", "Error: ReadFile", MB_OK);
128
        break; // Schleifen-Abbruch
129
      }
130
    }
131
  }
132
  while (1);
133
134
  CloseHandle (hCom);     // COM schließen
135
  CloseHandle (o.hEvent); // Event-Handle zurückgeben
136
137
  return (0);
138
}


Blackbird

von Peter II (Gast)


Lesenswert?

Blackbird schrieb:
> Richtig wäre das eventgetriggerte Empfangen von Bytes in einem eigenen
> Thread und Weiterleiten der Daten zu einem Haupt-Thread. Dort kann dann
> entschieden werden, wo die Daten (zeitlich) hingehören. Das Senden
> sollte auch eventgesteuert im Hintergrund in einem eigenen Thread
> ablaufen.

ich halte von den event gesteuerten ansatz wenig. Dann die meisten 
anwendungen warten ja auf eine Antwort.

Sende('Frage');
Antwort = Empfange();

wenn man jetzt mit events arbeitet, dann verkompilziert das die sache 
unnötig und bringt auch kein Vorteil.

SendeAsyn('Frage');
WarteAufEntwort();
Anwwort = AsynAntwort;

Das Programm blockiert in beiden Fälle an der Stelle, hier bringen 
events keine Vorteil. Klar könnte man jetzt das ganze noch als 
Statemaschine abbilden aber das finde ich viel schwerer zu warten.

Lieber alles Synchron machen, wenn die Hauptprogramm nicht blockieren 
soll dann alles in einen Worker-Thread auslagern dieser arbeitet dann 
intern ohne events.

von Zoran S. (zoran_s)


Lesenswert?

Peter II schrieb:
> das ist unsinn, entweder geht es beim ersten mal oder die verwendest zu
> kurze timeouts. Arbeite doch erstmal ohne Timeouts (das sollte das
> default sein). Dann blockiert das Read bis etwas gelesen wird.
>
Das habe ich schon probiert. Wenn die Timeouts auf 0 gesetzt sind, dann 
bleibt das Programm hängen. Darum habe ich das Timeout gesetzt und falls 
nichts gelesen wurde wird wiederholt. Meistens funktioniert es dann auch 
beim zweiten Mal lesen.
Da die Kommunikation eigentlich über USB geht (virtuelles COM) und dann 
ca. 1 MHz zwei Bytes geschickt werden, ist die Antwort auch schon 
innerhalb 2ms wieder da.
Zur Info: Es wird der Windows eigene Treiber usbser.sys verwendet.

> auserdem muss  BytesRead != BytesToRead nicht falsch sein, wenn man mehr
> als 1 byte erwarten, dann kann es auch sein das er die zeichen nicht auf
> einmal liefert.

ReadFile sollte eigentlich erst beenden wenn die gewünschte Anzahl an 
Bytes empfangen wurde, oder nicht?

von Peter II (Gast)


Lesenswert?

Zoran S. schrieb:
> ReadFile sollte eigentlich erst beenden wenn die gewünschte Anzahl an
> Bytes empfangen wurde, oder nicht?

da bin ich mir ebend nicht sicher, beim Netwerk ist es auf jeden fall so 
das das nicht immer alles ankommt.

> Das habe ich schon probiert. Wenn die Timeouts auf 0 gesetzt sind, dann
> bleibt das Programm hängen. Darum habe ich das Timeout gesetzt und falls
> nichts gelesen wurde wird wiederholt. Meistens funktioniert es dann auch
> beim zweiten Mal lesen.
dann hast du ein anders Problem. Das muss auf jeden Fall gehen.

Schreibe estmal ein Programm was 1 Zeichen senden und diesen wieder 
empfängt. Dann eine Bücke zwischen PIN 2 und 3 machen.


while(1)
  WriteFile( .. 'A' .. )
  ReadFile( ... );
  // Prüfe ob ReadFile 1 zeichen gelsen hat was ein 'A' ist
  // print OK sonst ERRROR
)

Dass muss erstmal ohne timeouts fehlerfrei durchlaufen.

von Zoran S. (zoran_s)


Lesenswert?

Hallo!

habe jetzt mal den code von Blackbird ausprobiert.

Leider bleibt das Programm dann irgendwann stehen.

Dann die Read-Write geschichte mit folgendem code:
1
/* 
2
 * File:   main.c
3
 * Author: zst
4
 *
5
 * Created on 02. November 2011, 11:33
6
 */
7
8
#include <stdio.h>
9
#include <windows.h>
10
11
HANDLE flasher_port;
12
char* com_port;
13
14
/*
15
 * 
16
 */
17
void comport_init(void) {
18
  int res;
19
  DCB dcb;
20
  unsigned char get_info[] = {'C', '5', '\n'};
21
  BYTE buf[30] = "\\\\.\\";
22
  COMMTIMEOUTS CommTimeouts;
23
  fprintf(stdout, "Opening %s\n", com_port);
24
  flasher_port = CreateFile(strcat(buf, com_port), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
25
    0, NULL);
26
27
  if (flasher_port == INVALID_HANDLE_VALUE) fprintf(stderr, "opening failed\n"), exit(1);
28
29
30
  //  Initialize the DCB structure.
31
  //  SecureZeroMemory(&dcb, sizeof(DCB));
32
  dcb.DCBlength = sizeof (DCB);
33
34
  if (GetCommState(flasher_port, &dcb)) {
35
    dcb.BaudRate = /*CBR_9600,*/CBR_115200;
36
      dcb.ByteSize = 8; //OCTET BYTE
37
      dcb.Parity = NOPARITY;
38
      dcb.StopBits = ONESTOPBIT;
39
      dcb.fInX = FALSE;
40
      dcb.fOutX = FALSE;
41
      dcb.fRtsControl = RTS_CONTROL_DISABLE;
42
  } else fprintf(stderr, "COM - mode set error\n"), exit(1);
43
44
45
  if (!SetCommState(flasher_port, &dcb)) fprintf(stderr, "COM - mode set error\n"), exit(1);
46
47
  if (GetCommTimeouts(flasher_port, &CommTimeouts)) {
48
    CommTimeouts.ReadIntervalTimeout = 0;
49
    CommTimeouts.ReadTotalTimeoutConstant = 0;
50
    CommTimeouts.ReadTotalTimeoutMultiplier = 300;
51
    CommTimeouts.WriteTotalTimeoutConstant = 0;
52
    CommTimeouts.WriteTotalTimeoutMultiplier = 300;
53
    if (!SetCommTimeouts(flasher_port, &CommTimeouts)) fprintf(stderr, "Timeout set error\n"), exit(1);
54
  }
55
}
56
57
58
59
int main(int argc, char** argv) {
60
  int arg = 0;
61
62
  for (arg = 1; arg < argc; arg++) {
63
    switch (toupper(argv[arg][1])) {
64
      case 'J':
65
        com_port = &argv[arg][2];
66
        break;
67
      default:
68
        break;
69
    }
70
  }
71
72
  comport_init();
73
  
74
  int countit = 0;
75
  unsigned char buffer = 'A';
76
  unsigned char recbuf;
77
  DWORD rxcnt = 0,txcnt = 0;
78
  int retValue = 0;
79
  while(1) {
80
    
81
    if (!WriteFile(flasher_port, &buffer, sizeof (unsigned char), &txcnt, NULL)) {
82
      fprintf(stderr, "Can't send Data!\n");
83
      retValue = 1;
84
      break;
85
    }
86
    else if (txcnt != sizeof (unsigned char)) {
87
      fprintf(stdout, "Sent %i instead of %i charachters!\n", txcnt, sizeof (unsigned char));
88
    }
89
    
90
    recbuf = 0;
91
    if (!ReadFile(flasher_port, &recbuf, sizeof (unsigned char), &rxcnt, NULL)) {
92
      retValue = 1;
93
      break;
94
    }
95
    else if (rxcnt != sizeof (unsigned char)) {
96
      fprintf(stdout, "Received %i instead of %i charachters!\n", rxcnt, sizeof (unsigned char));
97
    }
98
      
99
    if(recbuf != 'A')
100
      fprintf(stdout, "Loop %i error %c\n", countit, recbuf);
101
    
102
    if (countit%10000 == 0)
103
      fprintf(stdout, "%i Loops successfull!\n", countit);
104
    
105
    countit++;
106
  }
107
    
108
  fprintf(stdout, "Stopping at &i. Error %c\n", countit, recbuf);
109
  return retValue;
110
}

Hier die Ausgabe:
zst@zst-i5 /h/NetBeansProjects/LoopTest
$ LoopTest -jCOM4
Opening COM4
0 Loops successfull!
10000 Loops successfull!
20000 Loops successfull!
30000 Loops successfull!
40000 Loops successfull!
50000 Loops successfull!
60000 Loops successfull!
Received 0 instead of 1 charachters!
Loop 67742 error
70000 Loops successfull!
80000 Loops successfull!
Received 0 instead of 1 charachters!
Loop 87764 error
90000 Loops successfull!
100000 Loops successfull!
110000 Loops successfull!
120000 Loops successfull!
130000 Loops successfull!
140000 Loops successfull!
150000 Loops successfull!
160000 Loops successfull!
170000 Loops successfull!
Received 0 instead of 1 charachters!
Loop 173465 error
180000 Loops successfull!
Received 0 instead of 1 charachters!
Loop 182742 error
190000 Loops successfull!
200000 Loops successfull!
210000 Loops successfull!
220000 Loops successfull!
Received 0 instead of 1 charachters!
Loop 229614 error
230000 Loops successfull!
240000 Loops successfull!
250000 Loops successfull!
260000 Loops successfull!
270000 Loops successfull!
Received 0 instead of 1 charachters!
Loop 271759 error
280000 Loops successfull!
290000 Loops successfull!
300000 Loops successfull!
310000 Loops successfull!
320000 Loops successfull!
330000 Loops successfull!
340000 Loops successfull!
Received 0 instead of 1 charachters!
Loop 342993 error
...
usw.

Was kann das sein?

Ich könnte jetzt mal versuchen ein "retry" einzubauen. Vielleicht hilft 
das.
Andere Vorschläge?

Zoran

von Peter II (Gast)


Lesenswert?

Opening (null)
0 Loops successfull!
10000 Loops successfull!
20000 Loops successfull!
30000 Loops successfull!
40000 Loops successfull!
50000 Loops successfull!
60000 Loops successfull!
70000 Loops successfull!
80000 Loops successfull!
90000 Loops successfull!
100000 Loops successfull!
110000 Loops successfull!
120000 Loops successfull!
130000 Loops successfull!
140000 Loops successfull!
150000 Loops successfull!
160000 Loops successfull!
170000 Loops successfull!
180000 Loops successfull!
190000 Loops successfull!
200000 Loops successfull!
210000 Loops successfull!
220000 Loops successfull!
230000 Loops successfull!
240000 Loops successfull!
250000 Loops successfull!
260000 Loops successfull!
270000 Loops successfull!
280000 Loops successfull!
290000 Loops successfull!
300000 Loops successfull!
310000 Loops successfull!
320000 Loops successfull!
330000 Loops successfull!
340000 Loops successfull!
350000 Loops successfull!



also an dem Programm liegt es nicht. Du hast also ein Problem mit deiner 
Hardware order den Treibern.

Was soll das hier in deinem code?

BYTE buf[30] = "\\\\.\\";

für strings nimmt man char

besser du machst es mit

char buf[30];
snprintf(buf, "\\\\.\\%s",sizeof(buf)-1, com_port);
CreateFile(buf,...)

von Zoran S. (zoran_s)


Lesenswert?

Hallo!

Ich glaube, ich geb's auf.

bin gerade daheim und habe den LoopTest am Laptop getestet.
Nach 1.38 Mio. Loops kein einziger Fehler.

Ich habe noch zwei Rechner (1x XP und 1x Vista) an denen ich es morgen 
auch testen werde.
Falls dort auch keine Fehler auftreten dann liegt es an diesem PC.
Ob es das Chipset oder die CPU ist werde ich wohl nicht herauszufinden 
können.

Ansonsten kann ich nur noch den Treiber neu installieren und wenn es 
dann nicht klappt den ganzen Rechner neu aufsetzen.
Falls es dann immer noch nicht klappt wird Rechner getauscht.

Morgen gebe ich noch die Ergebnisse bekannt.

Danke euch allen für die Hilfe.

Zoran

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Warum ist das hier auskommentiert?

  //  Initialize the DCB structure.
  //  SecureZeroMemory(&dcb, sizeof(DCB));
  dcb.DCBlength = sizeof (DCB);

Wenn aus irgendeinem Grund SecureZeroMemory nicht verwendet werden 
kann, sollte memset verwendet werden.

von Peter II (Gast)


Lesenswert?

viel mir gerade auf:
1
if (!SetCommTimeouts(flasher_port, &CommTimeouts)) fprintf(stderr, "Timeout set error\n"), exit(1);

das ist auch ein Syntax der jenseits von gut und böse ist.

von Zoran S. (zoran_s)


Lesenswert?

Peter II schrieb:
> viel mir gerade auf:
>
>
1
> if (!SetCommTimeouts(flasher_port, &CommTimeouts)) fprintf(stderr,
2
> "Timeout set error\n"), exit(1);
3
>
>
> das ist auch ein Syntax der jenseits von gut und böse ist.

Weil ...?

von Peter II (Gast)


Lesenswert?

Zoran S. schrieb:
> Weil ...?

komma operator, für soetwas kann man schon eine extra zeile und {} 
verwenden.

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.