Die serielle Schnittstelle




    Hier soll beschreiben werden wie die serielle Schnittstelle via C/C++ angesteuert wird.  Am Ende habe ich einen Quellcode mit den Com-Tools für Windows angehängt. Darin ist ein kleines Demoprogramm enthalten das zeigt wie zwei Computer mit einander verbunden werden können. Ein weiteres Beispiel zeigt wie alle COM-Schnittellen aufgelistet werden können, und bestimmt werden kann welche Ports von USB-Geräten stammen.

    Die Schnittstelle besteht aus mindestens 3 Leitungen. (GND RxD und TxD). GND ist die Masseverbindung, über TxD werden die Daten gesendet und bei RxD empfangen. Die Daten werden asynchron übertragen. Das heißt zwischen Sender und Empfänger gibt es kein Synchronisationssignal. Der Beginn der Datenübertragung wird durch ein Startbit auf der TxD Leitung eingeleitet. Dieses Bit ist immer 0 (low). Ein logisches 0 Signal entspricht einem Pegel von +15 bis +3 Volt. Ein logisches 1 Signal entspricht einem Pegel von -15 bis -3 Volt. Nach dem Startbit folgen die Datenbits, beginnend mit dem niederwertigsten Bit. Es können 5 bis 8 Datenbits übertragen werden, üblich sind aber 7 oder 8 Datenbits. Nach den Datenbits kann ein Paritybit folgen. Dieses kann auf vier Arten erfolgen. Gerade Parität dann wird das Paritybit auf 1 gesetzt wenn eine ungerade Anzahl an logischen 1'en in den Datenbits vorhanden war. Ungerade Parität dann wird das Paritybit auf 1 gesetzt wenn eine gerade Anzahl an logischen 1'en in den Datenbits vorhanden war. Außerdem kann das Paritybit immer auf 1 oder auf 0 gesetzt werden. Die Übertragung wird durch ein Stopbit beendet. Dieses ist immer 1 und ist ein, ein-ein-halb oder zwei Bits lang. Die Baudrate bestimmt wie viele Bits je Sekunde ausgegeben werden.


    Die Steckerbelegung sieht so aus:

    Durch setzen der RTS (ready to send) Leitung auf 1 wird signalisiert das die Schnittstelle bereit zum senden ist. Über CTS (clear to send) wird empfangen ob die Gegenseite bereit zum senden ist. Durch setzen der DTR (date terminal ready) Leitung auf 1 wird signalisiert das die Schnittstelle betriebsbereit ist. Über DSR (data set ready) wird empfangen ob die Gegenseite bereit ist. Für eine Verbindung zwischen zwei PC's braucht man ein 7-poliges Null-Modem Kabel. Die 3 polige Variante ist die Sparversion und funktioniert in den meisten Fällen auch:

 


Programmierung der seriellen Schnittstelle

Auf die serielle Schnittstelle kann man unter Windows zugreifen über die Datei-IO Funktionen:

Die Einstellungen der serielle Schnittstelle erfolgen über die API-Funktionen:

So öffnet man die Schnittstelle:

DCB          sDcb;
HANDLE
      hFile;
COMMTIMEOUTS sTo;


  
hFile=CreateFile("\\\\.\\COM1",GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if(hFile==INVALID_HANDLE_VALUE)
return
0;

memset(&sDcb,0,sizeof(sDcb));
sDcb.DCBlength       = sizeof(sDcb);
sDcb
.BaudRate        = 9600;   
// Baudrate
sDcb.fParity         = FALSE;
sDcb.fBinary         = TRUE;
sDcb.Parity          = NOPARITY;
// Kein Paritybit
sDcb.StopBits        = ONESTOPBIT;
sDcb.fOutxCtsFlow    = FALSE;
sDcb.fOutxDsrFlow    = FALSE;
sDcb.fDtrControl     = DTR_CONTROL_ENABLE;
sDcb.fRtsControl     = RTS_CONTROL_ENABLE;
sDcb.fDsrSensitivity = FALSE;
sDcb.fAbortOnError   = FALSE;
sDcb.ByteSize        = 8;      
// 8 Datenbits

if(!SetCommState(hFile,&sDcb))
     {
    
CloseHandle(hFile);
     return 0;
 
    }

sTo.ReadIntervalTimeout = MAXDWORD; // 0 ms Read-Tomeout
sTo.ReadTotalTimeoutMultiplier = 0;
sTo.ReadTotalTimeoutConstant   = 0;
sTo.WriteTotalTimeoutMultiplier= 1; // 1*2 ms Write Timeout
sTo.WriteTotalTimeoutConstant  = 2;
if(!SetCommTimeouts((HANDLE)hFile,&sTo))
     {
     CloseHandle(hFile);
     return 0;
 
   }

So liest und schreibt man auf die Schnittstelle:

DWORD dwCount;
char  cData[16];

ReadFile
(hFile,cData,16,&dwCount,0);
WriteFile(hFile,cData,16,&dwCount,0);

    

Die Pegel auf der RTS und DTR Leitung können beliebig gesetzt werden, es sind dazu nur die Variabelen bDtrOn und bRtsOn im folgenden Beispiel richtig zu setzen:

...


bool  bDtrOn=1, bRdsOn=1;

memset(&sDcb, 0 ,sizeof(sDcb));
sDcb.DCBlength = sizeof(sDcb);
GetCommState(hFile,&sDcb);

sDcb.fDtrControl = (bDtrOn)? DTR_CONTROL_ENABLE:DTR_CONTROL_DISABLE;
sDcb.fRtsControl = (bRdsOn)? RTS_CONTROL_ENABLE:RTS_CONTROL_DISABLE;

SetCommState(hFile,&sDcb);

...



    Tools für die serielle Schnittstelle

    Die Tools bestehen aus acht Funktionen:

    ComInit          Initialisieren
    ComExit       
      Deinitialisieren
    ComOpen       
      Schnittstelle öffnen
    ComClose       
      Schnittstelle schließen
    ComRead       
      Daten lesen
    ComWrite      
      Daten schreiben
    ComGetReadCount  Abfragen wieviele Bytes im Empfangspuffer sind
    ComGetWriteCount Abfragen wieviele Bytes im Sendepuffer sind

    Um ein COM-Port ansprechen zu können muss mit ComInit den Treiber zuerst initialisiert werden. Anschließend öffnet man mit ComOpen das COM-Port. Mit ComRead und ComWrite können jetzt Daten einlesen und ausgeben werden. 

    Hier ist ein kleines Beispiel wie man die Com-Tools anwendet:  

    #include "ComTools.h"

    #define COM2  1

    int
    main()
    {
    char cData[16]="Hallo"
    ;
    int  i;

      ComInit ();              // Initialisierung
      ComOpen (COM2,19200);    // COM2 öffnen
      ComWrite(COM2,cData,5);  // 5 Byte schreiben
    i=ComRead (COM2,cData,16); // 16 Byte lesen
      ComClose(COM2);          // COM2 schließen
      ComExit ();              // Deinitialisierung

    printf("\nEs wurden %i Zeichen empfangen.\n",i);

    return 0;
    }

    Download der Com-Tools  Com-Tools samt einem kleinen Demo-Programm das zeigt wie man zwei PC's mit einem Null-Modem-Kabel verbindet.


    Die seriellen Schnittstellen auflisten

    Hier sind ist eine Funktionen mit der man die seriellen Schnittstellen auflisten kann. Dabei wird auch angezeigt ob die COM-Schnittstelle von einem USB-Grerät stammt. Außerdem werden die Device-Namen und Beschreibungen angezeigt. Das ganze läuft über die Funktion ComEnumPorts. Hier ein Beispiel:

    #include "ComEnumPorts.h"
    
    #include <conio.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(int argc, char* argv[])
    {
    int      i,iCount;
    ComInfo *pInfo;
    
    
        iCount = ComEnumPorts(NULL,0);  // Zählen der Ports
    if(!iCount)
        {
        printf("\n\nNo Ports found.\n\n");
        getch();
        return -1;
        }
    
    
    pInfo  = new ComInfo[iCount];	   // Port Informationen holen
    iCount = ComEnumPorts(pInfo,iCount); 
    
    for(i=0;i<iCount;i++)
        {
        printf("\nPort %i",i);
        printf("\n\tPort = %s",pInfo[i].cPortName);
        printf("\n\tName = %s",pInfo[i].cFriendlyName);
        printf("\n\tPath = %s",pInfo[i].cDevPath);
        printf("\n\tDesk = %s",pInfo[i].cPortDesc);
        printf("\n\tUSB  = %i",pInfo[i].bUsbDevice);
        printf("\n");
        }
    
    delete pInfo;
    
    getch(); 
    
    return 0;
    }

 

Dieser Source-Code funktioniert unter WinXP,Win2000,WinNT und Win98. Er erzeugt eine solche Ausgabe:

Port 0
       Port = COM1
       Name = Kommunikationsanschluss (COM1)
       Path = \\?\acpi#pnp0501#1#{86e0d1e0-8089-11d0-9ce4-08003e301f73}
       Desk = Kommunikationsanschluss
       USB  = 0

Port 1
       Port = COM2
       Name = Kommunikationsanschluss (COM2)
       Path = \\?\acpi#pnp0501#2#{86e0d1e0-8089-11d0-9ce4-08003e301f73}
       Desk = Kommunikationsanschluss
       USB  = 0

Port 2
       Port = COM3
       Name = Prolific USB-to-Serial Comm Port (COM3)
       Path = \\?\usb#vid_067b&pid_2303#5&29c90e1d&0&2#{86e0d1e0-8089-11d0-9c
       Desk = Prolific USB-to-Serial Comm Port
       USB  = 1
 

Unter Port wird der Name der Schnittstelle ausgegeben. Unter Name wird eine Beschreibung des Ports angezeigt. Path ist jener Pfad mit dem über CreateFile die Schnittstelle geöffnet werden kann. Desk ist eine allgemeine Beschreibung ohne der Angabe des Portnamens. Hier kann man den Source-Code downloaden.

 



    Anton Zechner