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.
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?
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
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.
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.
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.
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.
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
voidflush_serial_cache(void){
2
inti;
3
if(serial_cache_cnt){
4
countit++;
5
//Send the cache via SERIAL but update the cache header before
fprintf(stderr,"Incorrect SINGLE_DATA_WITH_READ response! %x instead of %x\n",*recbuf,SINGLE_DATA_WITH_READ);
76
exit(1);
77
}elseif(verbose)
78
fprintf(stdout,">> \t%x\n",*recbuf&3);
79
80
returnrecbuf[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
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.
@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)
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.
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?
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.
Hallo!
habe jetzt mal den code von Blackbird ausprobiert.
Leider bleibt das Programm dann irgendwann stehen.
Dann die Read-Write geschichte mit folgendem code:
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
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.