Problem mit der seriellen Schnittstelle



  • Hallo, ich habe ein kleines Problem mit der seriellen Schnittstelle unter Linux. Und zwar habe ich ein serielles Gerät, welches bei einer bestimmten Zeichenfolge (data[] im Code unten) eine bestimmte Aktion durchführt. Das Gerät arbeitet mit 19.200 Baud, 8 Datenbits, gerader Parität und einem Stoppbit. Dazu habe ich folgenden Code geschrieben:

    #include <stdio.h>
    #ifdef WIN32
    #include <windows.h>
    HANDLE h = INVALID_HANDLE_VALUE;
    #else
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <termios.h>
    int fd = -1;
    struct termios oldtio;
    #endif
    
    int serial_init() {
    #ifdef WIN32
        DCB dcb;
    
        if (h != INVALID_HANDLE_VALUE)
            return 1;
    
        h = CreateFile(L"COM1", GENERIC_WRITE, 0, NULL,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (h == INVALID_HANDLE_VALUE)
            return 2;
    
        if (!SetupComm(h, 1024, 1024)) {
            CloseHandle(h);
            return 3;
        }
    
        ZeroMemory(&dcb, sizeof(DCB));
        dcb.DCBlength = sizeof(DCB);
        dcb.BaudRate = 19200;
        dcb.fBinary = 1;
        dcb.ByteSize = 8;
        dcb.Parity = EVENPARITY;
        dcb.StopBits = ONESTOPBIT;
    
        if (!SetCommState(h, &dcb)) {
            CloseHandle(h);
            return 4;
        }
    #else
        struct termios tio;
    
        if (fd != -1)
            return 1;
    
        fd = open("/dev/ttyS0", O_WRONLY|O_NOCTTY|O_NONBLOCK);
        if (fd < 0)
            return 2;
    
        tcgetattr(fd, &oldtio);
    
        memset(&tio, 0, sizeof(struct termios));
        tio.c_cflag = B19200|CS8|PARENB|CLOCAL;
        tio.c_iflag = 0;
        tio.c_oflag = 0;
        tio.c_lflag = 0;
        tcflush(fd, TCOFLUSH);
        tcsetattr(fd, TCSANOW, &tio);
    #endif
        return 0;
    }
    
    int serial_write(unsigned char *data, int len) {
        int count = 0;
    #ifdef WIN32
        WriteFile(h, data, len, &count, NULL);
    #else
        count = write(fd, data, len);
    #endif
        return count;
    }
    
    void serial_exit() {
    #ifdef WIN32
        if (h != INVALID_HANDLE_VALUE)
            CloseHandle(h);
    #else
        if (fd >= 0) {
            tcsetattr(fd, TCSANOW, &oldtio);
            close (fd);
        }
    #endif
    }
    
    int main(void) {
        unsigned char data[] = { 0xFD, 0x00, 0x00, 0x12, 0x7C, 0x98, 0x00, 0x00, 0x00, 0x00,
            0x06, 0x73, 0x00, 0x0D, 0xFF, 0xA7, 0xCA };
    
        printf("serial_init() = %d\n", serial_init());
        printf("serial_write() = %d\n", serial_write(data, 17));
        serial_exit();
    
        return 0;
    }
    

    Unter Windows funktioniert das Ganze einwandfrei, sprich das serielle Gerät tut, was ihm gesagt wird. Unter Linux passiert leider nichts (obwohl die Ausgaben bei beiden Betriebssystemen auf der Standardausgabe identisch sind). Der Datenversand funktioniert definitiv, ich kann die versendeten Daten mit einem anderen PC auslesen. Daher vermute ich, dass es irgendetwas mit den Schnittstelleneinstellungen zu tun hat.

    Nur wo genau ist mein Fehler? Wie kann ich das gegebenenfalls besser debuggen?

    Danke vorab!



  • In Zeile 56 fehlt auf jeden Fall noch ein "| CREAD", um auch das Lesen von Daten zu aktivieren (ist 'ne Linux-Besonderheit).
    Außerdem würde ich die Baudrate nicht über das c_cflag einstellen, sondern über die dafür vorgesehenen Funktionen cfsetospeed() und cfsetispeed(). Diese Funktionen müsstest Du vor dem Aufruf von tcsetattr() einfügen.



  • Das CREAD Flag hatte ich bislang nur bei lesenden Funktionen gesetzt, allerdings ändert das leider auch nichts. Genausowenig wie das Setzen der Geschwindigkeit über cfset[i|o]speed(). 😞


Anmelden zum Antworten