COM Port Bits senden
-
Hallo,
ich möchte einzelne Bits an meinen COM-Port senden. Im Prinzip 0-0-0-0-0-0-0-1 mit jeweils 200ms Abstand.
Mein Ansatz wäre jetzt:
#include <iostream> #include <conio.h> #include <windows.h> int main(int argc, char* argv[]) { char INBUFFER[2]; // Create Input Buffer bool Zero = 0x00; bool One = 0x01; /* Komplement berechnen char Komplement = '\xd5' ^ '\xff'; printf("%d\r\n",Komplement); */ DWORD bytes_read = 1; // Number of bytes read from port DWORD bytes_written = 1; // Number of bytes written to the port HANDLE comport = NULL; // Handle COM port int bStatus; // Status indicator DCB comSettings; // Contains various port settings COMMTIMEOUTS CommTimeouts; // Contains various COM timeouts // Open COM port if ((comport = CreateFile("\\\\.\\COM4", // open com4: GENERIC_READ | GENERIC_WRITE, // for reading and writing 0, // exclusive access NULL, // no security attributes OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { printf("CreateFile() funktioniert nicht\r\n"); // error processing code goes here system("pause"); exit(0); } CommTimeouts.ReadIntervalTimeout = -1; // Timeout in ms CommTimeouts.ReadTotalTimeoutMultiplier = 0; // Timeout in ms CommTimeouts.ReadTotalTimeoutConstant = 0; // Timeout in ms CommTimeouts.WriteTotalTimeoutMultiplier = 0; // Timeout in ms CommTimeouts.WriteTotalTimeoutConstant = 65000; // Timeout in ms bStatus = SetCommTimeouts(comport,&CommTimeouts); // Set COM timeouts if (bStatus != 0) { printf("SetCommTimeouts() funktioniert nicht\r\n"); // error processing code goes here } GetCommState(comport, &comSettings); // Getting COM state comSettings.BaudRate = 9600; // COM setting comSettings.StopBits = ONESTOPBIT; // COM setting comSettings.ByteSize = 7; // COM setting comSettings.Parity = ODDPARITY; // COM setting comSettings.fParity = FALSE; // COM setting bStatus = SetCommState(comport, &comSettings); // Setting COM state if (bStatus == 0) { printf("SetCommState() funktioniert nicht.\r\n"); // error processing code goes here } Sleep(5); bStatus = WriteFile(comport, &Zero,1,&bytes_written,NULL); Sleep(200); bStatus = WriteFile(comport, &Zero,1,&bytes_written,NULL); Sleep(200); bStatus = WriteFile(comport, &Zero,1,&bytes_written,NULL); Sleep(200); bStatus = WriteFile(comport, &Zero,1,&bytes_written,NULL); Sleep(200); bStatus = WriteFile(comport, &Zero,1,&bytes_written,NULL); Sleep(200); bStatus = WriteFile(comport, &Zero,1,&bytes_written,NULL); Sleep(200); bStatus = WriteFile(comport, &Zero,1,&bytes_written,NULL); Sleep(200); bStatus = WriteFile(comport, &One,1,&bytes_written,NULL); printf("Sending: %d\r\n", 0x46); bStatus = ReadFile(comport,&INBUFFER,2,&bytes_read,NULL); printf("Receiving: %d <-> %d\r\n", INBUFFER[0], INBUFFER[1]); system("pause"); CloseHandle(comport); return 0; }
... allerdings ist WriteFile() nur für Bytes ausgelegt, kann ich irgendwie auch einzelne Bits senden? Der Code scheint so jedenfalls nicht zu funktionieren.
Danke & Gruß
COM
-
200ms Abstand zwischen jedem Bit?
Wenn man das Muster einfach als 1-Byte sendet, wäre das eine Bitrate von 5 bits/s, aber vermutlich kann man typische COM-Ports gar nicht so langsam einstellen?
Zudem werden Start/Stoppbits da stören (und bei deinem Versuch zusätzlich noch der Pufferspeicher der COM-Schnittstelle und des Betriebssystems)Ich würde nen Mikrocontroller dazwischenpacken und den das in das passende Format umsetzen lassen:
[PC] ---(COM1 oder USB)---> [Atmega/PIC Mikrocontroller] ---> (Bits im Abstand 200ms)
Pfusch-Möglichkeit wäre auch das Muster statt auf dem TX-Pin (mit WriteFile) auf einem der Handshake-Leitungen (DTR/DTS) auszugeben und den Pin als TX-Pin zu nutzen, die kann man einzeln an/aus schalten:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363254.aspx
-
Es geht darum, das KW1281-Protokoll zu initialisieren (Fahrzeugdiagnose).
Laut KW1281 Standard muss man mit 5 Baud initialisieren und mit 9600 Baud weiterarbeiten, man kann denke ich die Baud-Rate nicht einfach während einer Verbindung ändern, daher müsste man ein künstliches Delay von 200ms einfügen.
http://blafusel.de/obd/obd2_kw1281.html
Die erste Methode ist einfach, hat aber den Nachteil, daß sie nicht hundertprozentig akkurat ist. Ich hatte teilweise den Verdacht, daß nicht alle Daten protokolliert wurden. Allerdings findet man so heraus, wie die 5Bd Initialisierung abgewickelt wird: Statt die Geschwindigkeit an der Schnittstelle zu wechseln, wird diese gleich auf die gewünschte Datenrate für die folgende Kommunikation eingestellt (bspw. 9600Bd) und dann wird mit SET_BREAK die Übertragung simuliert:
IOCTL_SERIAL_SET_BREAK_ON
IOCTL_SERIAL_SET_RTS
IOCTL_SERIAL_CLR_DTR
IOCTL_SERIAL_SET_BREAK_OFF -> Start-Bit?
IOCTL_SERIAL_CLR_RTS
IOCTL_SERIAL_SET_BREAK_ON -> 0
IOCTL_SERIAL_SET_RTS
IOCTL_SERIAL_SET_BREAK_ON -> 0
IOCTL_SERIAL_SET_RTS
IOCTL_SERIAL_SET_BREAK_ON -> 0
IOCTL_SERIAL_SET_RTS
IOCTL_SERIAL_SET_BREAK_ON -> 0
IOCTL_SERIAL_SET_RTS
IOCTL_SERIAL_SET_BREAK_ON -> 0
IOCTL_SERIAL_SET_RTS
IOCTL_SERIAL_SET_BREAK_ON -> 0
IOCTL_SERIAL_SET_RTS
IOCTL_SERIAL_SET_BREAK_ON -> 0
IOCTL_SERIAL_SET_RTS
IOCTL_SERIAL_SET_BREAK_OFF -> 1
IOCTL_SERIAL_CLR_RTS
IOCTL_SERIAL_SET_DTRZwischen den einzelnen Umschaltungen liegen genau 200ms, woraus sich dann die 5Bd (1000ms/5bps) ergeben. Im Beispiel wird die Adresse 1 angesprochen. Dezimal 1 entspricht Binär 000 0001.
Der jenige hat auch ein Tool in Delphi (?) dafür geschrieben, aber ich kann nicht viel mit dem Code anfangen.
http://sourceforge.net/projects/wbh-diag/files/Version 1.30/wbh-diag_frontend.zip/download
Danke schonmal, vielleicht kannst du oder jemand anders mir bei dem Problem weiterhelfen.
-
So habe mal aus WBHDiag von Blafusel.de die entsprechenden Zeilen kopiert.
function kw1281_wakeup (adresse : Integer) : Boolean; var i,u : Integer; erfolg, geoeffnet : boolean; uart,txt : String; baud : Integer; begin erfolg := false; // muß so umständlich sein, da "if kw1281_wakeup = true" die funktion rekursiv aufruft - argh! WriteDebugFile ('IBT', InterByteTime); WriteDebugFile ('BDT', InterBlockTime); WriteDebugFile ('ECU Adresse', adresse); Form1.StatusBar1.Panels[0].Text := LoadStr (langprefix + 22); // Statusbar Verbindunsgversuch statuspanel0 := 1; try geoeffnet := open_comport(); if (geoeffnet) then begin Form1.ComPort1.Timeouts.ReadInterval := -1; // Die drei Einstellungen führen dazu, daß nur Form1.ComPort1.Timeouts.ReadTotalConstant := 1000; // 1000 ms auf ein Zeichen im Puffer gewartet wird Form1.ComPort1.Timeouts.ReadTotalMultiplier := -1 ; u:=1; if not PublicVersion then Form1.Memo1.Lines.Add ('OPEN'); Form1.ComPort1.ClearBuffer(True, True); Form1.ComPort1.WriteStr(chr(13)); // CR uart := uartRead(false); // entweder > oder ? if (uart = '') then // keine Antwort begin Application.MessageBox (PChar(LoadStr (langprefix + 306)), PChar(LoadStr (langprefix + 301))); // "Kein Kontakt zum Diagnoseinterface herstellbar." end else begin uartRead(false); // wenn ? dann das > verwerfen Form1.ComPort1.WriteStr(chr(13)); // CR uart := uartRead(false); // entweder > oder ? uartRead(false); // wenn ? dann das > verwerfen if (uart <> '') then // Zeichen empfangen => richtiger Port begin i := 0; repeat if not PublicVersion then Form1.Memo1.Lines.Add ('ATZ'); Form1.ComPort1.WriteStr('ATZ'+chr(13)); uartRead(false); // das CR verwerfen uart := uartRead(false); uartRead(false); // das > verwerfen inc (i); until ((AnsiContainsText(uart, 'WBH-Diag Pro')) OR (i>5)); // bis Info aber nur maximal 5x if (AnsiContainsText(uart, 'WBH-Diag Pro')) then // WBH gefunden begin Form1.StatusBar1.Panels[2].Text := uart; //LoadStr (langprefix + 22); // Statusbar Verbindunsgversuch if not PublicVersion then Form1.Memo1.Lines.Add ('ATIBT' + IntToHex(InterByteTime, 2)); Form1.ComPort1.WriteStr('ATIBT' + IntToHex(InterByteTime, 2) + chr(13)); repeat uart := uartRead(false); until ((uart = 'ERROR') OR (uart = 'DATA ERROR') OR (uart = '>') ); if not PublicVersion then Form1.Memo1.Lines.Add ('ATBDT' + IntToHex(InterBlockTime, 2)); Form1.ComPort1.WriteStr('ATBDT' + IntToHex(InterBlockTime, 2) + chr(13)); repeat uart := uartRead(false); until ((uart = 'ERROR') OR (uart = 'DATA ERROR') OR (uart = '>') ); case BaudrateIniFile of 1200 : baud := 1; 2400 : baud := 2; 4800 : baud := 3; 9600 : baud := 4; 14400 : baud := 5; else baud := 0; end; if not PublicVersion then Form1.Memo1.Lines.Add ('ATN' + IntToStr(baud)); Form1.ComPort1.WriteStr('ATN' + IntToStr(baud) + chr(13)); repeat uart := uartRead(false); until ((uart = 'ERROR') OR (uart = 'DATA ERROR') OR (uart = '>') OR (LeftStr(uart, 7) = 'CONNECT')); if not PublicVersion then Form1.Memo1.Lines.Add ('ATD' + IntToHex(adresse, 2)); Form1.ComPort1.WriteStr('ATD' + IntToHex(adresse, 2) + chr(13)); repeat uart := uartRead(false); until ((uart = 'ERROR') OR (uart = 'DATA ERROR') OR (uart = '>') OR (LeftStr(uart, 7) = 'CONNECT')); if (LeftStr(uart, 7) = 'CONNECT') then // Connect OK begin case StrToInt(Copy(uart,10,1)) of // Aus der Connectmeldung die Geschwindigkeit extrahieren 1 : txt := '1200'; 2 : txt := '2400'; 3 : txt := '4800'; 4 : txt := '9600'; 5 : txt := '14400'; end; Form1.StatusBar1.Panels[1].Text := txt + ' Baud'; // Statusbar Form1.StatusBar1.Panels[0].Text := LoadStr (langprefix + 23); // Verbunden statuspanel0 := 2; case StrToInt(RightStr(uart,1)) of // Aus der Connectmeldung das Protokoll extrahieren 1 : begin txt := 'KW 1281'; protokoll := 1; end; 2 : begin txt := 'KW 2000'; protokoll := 2; end; end; Form1.StatusBar1.Panels[3].Text := LoadStr (langprefix + 24) + txt; // Protokoll erfolg := true; end ; end // if "WBH-Diag..." else begin Application.MessageBox (PChar(LoadStr (langprefix + 305)), PChar(LoadStr (langprefix + 301))); // "Kein Kontakt zum Diagnoseinterface herstellbar." end; end // if falscher Port (kein CR) else begin Application.MessageBox (PChar(LoadStr (langprefix + 306)), PChar(LoadStr (langprefix + 301))); // "Kein Kontakt zum Diagnoseinterface herstellbar." end; end; // keine Antwort auf 1. Anfrage end else // COM Port nicht zu öffnen begin kw_Disconnect; end; if (erfolg = false) AND geoeffnet then begin Form1.StatusBar1.Panels[0].Text := LoadStr (langprefix + 6); // Statusbar keine Verbindung statuspanel0 := 0; Form1.StatusBar1.Panels[1].Text := ''; // keine Baudrate Form1.StatusBar1.Panels[2].Text := ''; // kein Chip Form1.StatusBar1.Panels[3].Text := ''; // kein Protkoll Form1.ComPort1.Close; end; kw1281_wakeup := erfolg; except end; end;
-
##########
##########
##########
So und hier noch eine Funktion kwp..wakeupfunction kw1281_wakeup (adresse : Integer) : Boolean; var ecu0, ecu1, ecu2, ecu3, i, bit, start : Integer; erfolg : boolean; baudrate: Integer; parity : Integer; zeit : integer; TickValue : DWORD; begin erfolg := false; // muß so umständlich sein, da "if kw1281_wakeup = true" die funktion rekursiv aufruft - argh! baudrate := 0; // Delphi kann lokale variablen nicht bei der Deklaration initialisieren :-( if Form1.CheckBox8.Checked then begin zeit := 196; // eigentlich 200 aber bei USB Interface MUSS 195-196 end else begin zeit := 200; end; WriteDebugFile ('Timing', zeit); WriteDebugFile ('IBT', InterByteTime); WriteDebugFile ('BDT', InterBlockTime); WriteDebugFile ('ECU Adresse', adresse); begin if (BaudrateIniFile > 0) then start := 0 else start := 1; for i:=start to 5 do begin case i of 0 : begin baudrate := BaudrateIniFile; Delay (1500); // warten vor nächstem Baudratenversuch (Wert + 1 Sekunde = VAG-COM) end; 1 : begin baudrate := 10400; //10400 end; 2 : begin Delay (1500); // warten vor nächstem Baudratenversuch (Wert + 1 Sekunde = VAG-COM) baudrate := 9600; end; 3 : begin Delay (1500); // warten vor nächstem Baudratenversuch (Wert + 1 Sekunde = VAG-COM) baudrate := 4800; end; 4 : begin Delay (1500); // warten vor nächstem Baudratenversuch (Wert + 1 Sekunde = VAG-COM) baudrate := 2400; end; 5 : begin Delay (1500); // warten vor nächstem Baudratenversuch (Wert + 1 Sekunde = VAG-COM) baudrate := 1200; end; // 5 : baudrate := 19200; end; Form1.StatusBar1.Panels[0].Text := LoadStr (langprefix + 22); // Statusbar Verbindunsgversuch Form1.StatusBar1.Panels[1].Text := IntToStr(baudrate) + ' Baud'; // Statusbar if (open_comport (baudrate) = true) then begin // Invertierte Signal-Logik // TXD = K-Leitung // RTS = L-Leitung DTR (1); // für Jeffs Interface: Eingangs Opto nach Masse ziehen. // Delay (3); // muß nicht sein, aber theoretisch braucht die Umschltung bei USB 3ms. TXD (0); RTS (0); Delay (2000); // Vorbereitung HIGH (MUSS 2000 für Kombiinstr. T4 sein) DTR (0); TXD (1); RTS (1); Delay (zeit); // Start-Bit 1->0 LOW // Bits der Adresse durchlaufen LSB->HSB aber nur 128 möglich (7 Bit) parity := 0; for bit:=7 downto 1 do begin if (TestBit (adresse, bit) = true) then begin TXD (0); RTS (0); Delay (zeit); // HIGH parity := parity + 1; end else begin TXD (1); RTS (1); Delay (zeit); // LOW end; end; // Odd Parity if ((parity MOD 2) = 0) then // true=gerade begin TXD (0); RTS (0); Delay (zeit); // HIGH = mache Stream ungerade, Odd Parity end else begin TXD (1); RTS (1); Delay (zeit); // LOW end; DTR(1); // für Jeffs Interface TXD (0); RTS (0); Delay (zeit); // Stop-Bit CLEARBUFFER; // nicht ganz logisch, muß aber sein, da nach den TXDs 2 Bytes im Puffer sind. TickValue := GetTickCount; // ms seit Windows Start while ((INBUFFER = 0) and (GetTickCount-TickValue < 500)) do // solange kein Zeichen und noch keine 500ms vergangen sind asm nop //Application.ProcessMessages; end; // Delay (250); // 250 if INBUFFER >0 then // Zeichen im Puffer = init klappte vermutlich begin // ECU wake-up Bestätigung empfangen: Form1.Memo1.Lines.Add ('+++'); ecu0 := myReadByte; // Sync-Byte Form1.Memo1.Lines.Add (IntToHex(ecu0,1)); WriteDebugFile ('Sync', ecu0); if (ecu0 = $55) then begin erfolg := true; ecu1 := myReadByte; // KB1 WriteDebugFile ('KB1', ecu1); Form1.Memo1.Lines.Add (IntToHex(ecu1,1)); ecu2 := myReadByte; // KB2 WriteDebugFile ('KB2', ecu2); Form1.Memo1.Lines.Add (IntToHex(ecu2,1)); Form1.Memo1.Lines.Add ('---'); Delay (30); SENDBYTE ($FF-ecu2); myReadByte; // InterByteTime Inv. KB senden, ECHO ignorieren // 30 VAG-COM ecu3 := ((ecu2 and $7F) * 128) + (ecu1 and $7F); // Protokollbezeichner berechnen WriteDebugFile ('Proto', ecu3); if (ecu3 = 1281) then begin Form1.StatusBar1.Panels[0].Text := LoadStr (langprefix + 23); // Verbunden Form1.StatusBar1.Panels[2].Text := LoadStr (langprefix + 24) + 'KW ' + IntToStr (ecu3); // Protokoll end else begin erfolg := false; Application.MessageBox(Pchar ('KW ' + IntToStr (ecu3) + ' ' + LoadStr (langprefix + 53)),'',MB_OK or MB_ICONEXCLAMATION); end; break; // Schleife beenden end end; end else // COM Port nicht zu öffnen => Abbruch Durchlauf Baudraten begin kw_Disconnect; break; end; CLOSECOM(); // zu machen vor nächstem Aufbau mit neuer Baudrate end; // for end; // if verbinden if (erfolg = false) then begin Form1.StatusBar1.Panels[0].Text := LoadStr (langprefix + 6); // Statusbar keine Verbindung Form1.StatusBar1.Panels[1].Text := ''; // keine Baudrate Form1.StatusBar1.Panels[2].Text := ''; // kein Protkoll CLEARBUFFER; CLOSECOM(); // Delay (1500); end; kw1281_wakeup := erfolg; end;
-
Hmm, interessanter Ansatz. Mit Break ändert er quasi den Pegel auf der Leitung und simuliert 0/1.
IOCTL_SERIAL_SET_BREAK_ON ist für DeviceIoControl() gedacht, man kann aber auch direkt EscapeCommFunction(comporthandle,SETBREAK) nehmen
...allerdings sind die 200ms dort von der verbauten COM-Schnittstelle abhängig, die könnte auch mehr oder weniger ms nehmen.
-
Ja, dass mit dem Timeout wird auch erwähnt:
zeit := 196; // eigentlich 200 aber bei USB Interface MUSS 195-196
Also ist BREAK immer ein künstliches Delay für ein 0-Bit?
-
Ja, ein Break setzt die Leitung für ein paar ms auf den Ruhepegel (logisch 0)
-
Ich schau mal, was sich daraus machen lässt. Danke