Problem mit der rs232



  • Hi
    Ich schicke von einem PC (server) Daten über die rs232 an einen andreren PC (Client)

    Da ich vor ca 2 monaten auf Linux umgestiegen bin, kann ich die client-software nicht mehr gebrauchen.
    Also muss eine neuer client her, der unter Linux läuft (und NUR unter Linux ;))

    Der code aus dem windows-client:

    HANDLE init()
    {
     HANDLE h_com = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0 , NULL);
     DCB dcb;
     COMMTIMEOUTS cto;
    
     GetCommState(h_com, &dcb);
    
     dcb.BaudRate = 115200;
     dcb.ByteSize = 8;
     dcb.Parity   = NOPARITY;
     dcb.StopBits = ONESTOPBIT;
    
     SetCommState(h_com, &dcb);
    
     GetCommTimeouts(h_com,&cto);
    
     cto.ReadTotalTimeoutConstant=0;
     cto.ReadTotalTimeoutMultiplier=0;
    
     SetCommTimeouts(h_com,&cto);
    
     return h_com; 
    }
    
    char getc(HANDLE hcom)
    {
     static unsigned long int counter = 0;
     counter++;
    
     unsigned long nBytesRead;
     char input[2];
    
     ReadFile(hcom, input, 1, &nBytesRead, NULL);
     return input[0];
    }
    

    Mein Problem ist jetzt, das nach Linux zu portieren. Ich habe schon das ganze internet durchsucht, und bereits 3 ansetze verfolgt, aber ich habe immer das selbe Problem: Ich bekomme nur ein teil der Daten!

    115200Baud kann doch nicht zu schnell sein!?

    Meine Frage: WIE Muss ich die RS232 Initialisieren, und wie lese ich ALLE daten??

    Zur Hardware: Die PCs sind per nullmodem verbunden



  • schau mal in die FAQ



  • Danke für diese sehr hilfreiche antwort, rüdiger 🙄

    RedEagle schrieb:

    [..]Ich habe schon das ganze internet durchsucht, und bereits 3 ansetze verfolgt,[..]

    Vorhin hatte ich für ca 10 Versuche das glück, dass ich alle informationen bekommen habe. Nachdem ich aus optischen gründen ein printf entfernte, habe ich nichts mehr ausgeben können. Als das printf wider da war, konnte ich auch ausgeben.

    do
     {
      rs232::ReadByteEx(&chr);
      if(chr == 0x00) break;
      if(chr == '\n')
      {
       printf("\n[%4X] ",ID); //DIESES PRINTF
       continue;
      }
      printf("%c",chr);
     }
     while(true);
    

    Da ich NICHTS an den eigentlichen rs232-routinen geändert habe. und dennoch kurzzeitig daten vollständig empfangen wurden gehe ich mal davon aus, das MEIN code soweit richtig ist.
    ABER: Warum verursacht das entfernen des o.g. printf's ein derartiges chaos??
    UND: Warum funktioniert es plötzlich?? (wenn auch unregelmäßig)

    Kann es sein, dieses blöde KDevelop mist baut??
    (Ich habe den Compiler auf "optimized" eingestellt)

    ps. Ich verwende openSuSE 10.1 - x86-64



  • Zunächst einmal finde ich den Windows-Quelltext nicht wirklich hilfreich, wenn Du eine Frage zu deinem Linux-Programm hast. Du solltest sinnvoller Weise die entscheidenen Stellen aus dem Linux-Quelltext posten.
    Der zweite Programmauszug ist so kurz, dass man daran kaum das Problem lokalisieren kann, das ja offenbar in der nicht geposteten Funktion rs232::ReadByteEx() liegt.

    Trotzdem hier eine Vermutung, wo das Problem liegt: Der Rechner ist wahrscheinlich nicht zu langsam, sondern zu schnell. Ein read() von /dev/ttyS0 wartet unter Umständen nicht, bis die gewünschte Anzahl von Zeichen eingelesen wurde, sondern kehrt mit einer geringeren Anzahl Zeichen (vielleicht sogar 0) zurück, wenn der FIFO-Puffer des UART-Bausteins leer ist. Abhilfe: Mit select() und einem sinnvollen Timeout-Wert arbeiten, so lange, bis alle Zeichen gelesen wurden.



  • Init

    int OpenAdrPort(char* sPortNumber)
    {
         char sPortName[64];
         sprintf(sPortName, "/dev/ttyS%s", sPortNumber);
    
         CloseAdrPort();
    
        fd = open(sPortName, O_RDWR | O_NOCTTY | O_NDELAY);
        if (fd < 0)
        {
            printf("open error %d %s\n", errno, strerror(errno));
        }
        else
        {
            fcntl(fd, F_SETFL, 0); //added by RedEagle (03.07.07)
    
            struct termios my_termios;
            printf("fd is %d\n", fd);
            tcgetattr(fd, &my_termios);
    
            tcflush(fd, TCIFLUSH);
    
            my_termios.c_cflag = B115200 | CS8 |CREAD | CLOCAL | HUPCL;
    
            cfsetospeed(&my_termios, B115200);
            cfsetispeed(&my_termios, B115200);
            tcsetattr(fd, TCSANOW, &my_termios);
    
            printf("new cflag=%08x\n", my_termios.c_cflag); //1cb2
            printf("new oflag=%08x\n", my_termios.c_oflag); //5
            printf("new iflag=%08x\n", my_termios.c_iflag); //0
            printf("new lflag=%08x\n", my_termios.c_lflag); //0
            printf("new line =%02x\n", my_termios.c_line);  //0
        }
        return fd;
    }
    
    bool Init()
    {
     int fd = OpenAdrPort("0");
     if(fd < 0) return false;
    
     hexlog = fopen("./hexlog.txt","w");
     if(hexlog == NULL) return false;
    
     return true;
    }
    

    Das ganze zeug zum lesen

    int ReadAdrPort(char* psResponse, int iMax)
    {
        int iIn;
        if (fd < 1)
        {
            printf(" port is not open\n");
            return -1;
        }
        strncpy (psResponse, "N/A", iMax<4?iMax:4);
        iIn = read(fd, psResponse, iMax);
    
        if (iIn < 0)
        {
            if (errno == EAGAIN)
            {
                    return 0;
            }
            else
            {
                    printf("read error %d %s\n", errno, strerror(errno));
            }
        }
        else
        {
            psResponse[iIn<iMax?iIn:iMax] = '\0';
        }
    
        return iIn;
    }
    
    bool ReadByte(char *retval)
    {
     static char str[10]={0,0,0,0,0,0,0,0,0,0};
     static int  strpos;
     int timeout=1000;
    
     //noch was drinne?
     if(str[strpos] != 0)
     {
      *retval=str[strpos];
      strpos++;
      return true;
     }
     //leer
     strpos=0;
    
     while(ReadAdrPort(str,1) <= 0) //Bis was empfangen wird
     {
      if(!(--timeout)) return false;
     }
    
     //Fehler?
     if(strcmp(str,"N/A")==0)
     {
      strpos=0;
      for(int i=0; i<10; i++)str[i]=0;
      return false;
     }
    
     *retval = str[strpos];
     strpos++;
     return true;
    }
    
    bool ReadByteEx(char *retval)
    {
     char tmp;
     while(!ReadByte(&tmp));
     *retval = tmp;
    
     //loggen
     charcnt++;
     fprintf(hexlog, "%X", tmp);
     if(charcnt%8)fprintf(hexlog, " ");
     else         fprintf(hexlog, "\n");
    
     return true;
    }
    


  • Wie schon vermutet: Die Schleife in ReadByte (Zeilen 17 bis 20) ist höchstgradig von der Performance des Rechners abhängig.
    Wie gesagt: am besten arbeitest Du mit der Funktion select() und einem "echten" Timeout.
    Außerdem liest Du in der Funktion ReadByte immer nur das erste Zeichen von str ein (Zeile 17) - wie kann darin jemals eine längere Zeichenkette ("N/A", Zeile 23) stehen?



  • Martin G schrieb:

    [...]wie kann darin jemals eine längere Zeichenkette ("N/A", Zeile 23) stehen?

    strncpy(psResponse, "N/A", iMax<4?iMax:4);

    Da ich jedesmal die Konfiguration ausgebe, habe ich diese Kopiert, als es lief, und die Initialisierung geändert:

    my_termios.c_cflag=0x1CB2;
    my_termios.c_oflag=0x5;
    my_termios.c_iflag=0;
    my_termios.c_lflag=0;
    my_termios.c_line =0;
    
            tcflush(fd, TCIFLUSH);
    
            my_termios.c_cflag = B115200 | CS8 |CREAD | CLOCAL | HUPCL;
    
            cfsetospeed(&my_termios, B115200);
            cfsetispeed(&my_termios, B115200);
            tcsetattr(fd, TCSANOW, &my_termios);
    

    Sieht zwar scheiße aus, aber es läuft perfekt!


Anmelden zum Antworten