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_DTR

    Zwischen 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..wakeup

    function 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


Anmelden zum Antworten