2013-03-28 11 views
6

La configurazione del termios sta modificando il primo carattere letto dalla porta seriale usando read(). Ho un microcontrollore che parla con una macchina Linux. Il microcontrollore risponde ai comandi inviati dalla macchina Linux. La messa a punto è la seguente:Termios di Linux che modificano il primo carattere dopo la lettura della porta seriale()

  • microcontrollore (PIC24F) porta RS485 < -> RS485 al convertitore USB < -> PC Ubuntu.

Quando eseguo un programma terminale come Cutecom, tutto funziona come pianificato. Invio un carattere di comando al PIC e ottengo comunque una risposta quando uso il mio programma da riga di comando per modificare il primo carattere. Ecco il mio codice:

#include <string.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <termios.h> 

#define DEVICE "/dev/ttyUSB0" 
#define SPEED B38400 

int main() 
{ 
    struct termios tio; //to hold serial port settings 
    struct termios stdio; //so we can accept user input 
    struct termios old_stdio; //save the current port settings 
    int tty_fd; //file descriptor for serial port 
    int res, n, res2, read1, wri; 
    char buf[255]; 
    char buf2[255]; 

    //save the current port settings 
    tcgetattr(STDOUT_FILENO,&old_stdio); 

    //setup serial port settings 
    bzero(&tio, sizeof(tio)); 
    tio.c_iflag = 0; 
    tio.c_iflag = IGNPAR | IGNBRK | IXOFF; 
    tio.c_oflag = 0; 
    tio.c_cflag = CS8 | CREAD | CLOCAL; //8n1 see termios.h 
    tio.c_lflag = ICANON; 

    //open the serial port 
    tty_fd=open(DEVICE, O_RDWR | O_NOCTTY); 

    //set the serial port speed to SPEED 
    cfsetospeed(&tio,SPEED); 

    //apply to the serial port the settings made above 
    tcsetattr(tty_fd,TCSANOW,&tio); 

    for(n = 5; n > 0; n--) 
    { 
    printf("Please enter a command: "); 
    (void)fgets(buf2, 255, stdin); 
    (void)write(tty_fd, buf2, strlen(buf2));     
    printf("Ok. Waiting for reply."); 
    res = read(tty_fd, buf, 255);  
    printf("Read:%d START%d %d %d %d %dFINISH\n",res,buf[0],buf[1],buf[2],buf[3], 
    buf[4]);    
    } 

    //close the serial port 
    close(tty_fd); 

    //restore the original port settings 
    tcsetattr(STDOUT_FILENO,TCSANOW,&old_stdio); 

    return EXIT_SUCCESS; 
} 

Ecco un esempio di risultati che sto ottenendo.

  • Quando il PIC invia "00000 \ n" l'uscita è: Read: 6 MESSA IN 16 48 48 48 48FINISH
  • Quando il PIC invia "23456 \ n" l'uscita è: Read: 6 START- 14 51 52 53 54FINISH
  • Quando il PIC invia "34567 \ n" l'uscita è: Lettura: 6 INIZIO-14 52 53 54 55FINISH
  • Quando il PIC invia "45678 \ n" l'uscita è: Read: 6 START-12 53 54 55 56FINISH
  • Quando il PIC invia "56789 \ n" l'uscita è: Leggi: 6 START-12 54 55 56 57FINISH

Per qualche motivo il primo personaggio viene incasinato da qualche impostazione di termios. Devono essere le impostazioni del termios in quanto gli stessi ingressi di test sopra vengono restituiti esattamente quando eseguo Cutecom. Ho letto le pagine del manuale più e più volte, provando tutte le diverse impostazioni sul controllo di input, ma non importa ciò che faccio non può scuotere questo problema.

Per una soluzione facile, posso spostare i miei dati su 1 carattere ma voglio evitare di farlo.

Qualcuno ha riscontrato un tale problema o ha qualche idea su cosa fare al riguardo?

Molte grazie.

28/3/13 Grande suggerimento Austin.Per coloro che sono interessati ecco i due uscite:

  • primi sono le impostazioni termios nel mio programma

    velocità 38400 baud; righe 0; colonne 0; riga = 0; intr =; esci =; cancella =; kill =; eof =; eol =; eol2 =; swtch =; inizio =; stop =; susp =; rprnt =; werase =; lnext =; flush =; min = 0; tempo = 0; -parenb -parodd CS8 -hupcl -cstopb cread clocal -crtscts ignbrk -brkint ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon IXOFF -iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 Tab0 bs0 VT0 ff0 -isig icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke

  • E il impostazioni che cutecom utilizza

    velocità 38400 baud; righe 0; colonne 0; riga = 0; intr =^C; esci =^\; cancella =^?; kill =^U; eof =^D; eol =; eol2 =; swtch =; start =^Q; stop =^S; susp =^Z; rprnt =^R; werase =^W; lnext =^V; flush =^O; min = 60; tempo = 1; -parenb -parodd CS8 hupcl -cstopb cread clocal -crtscts ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8 -opost -olcuc - ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 Tab0 bs0 VT0 ff0 -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke

Sto ancora esaminando tutto e aggiornerò il post quando avrò fatto progressi.

29/3/13 Ancora lo stesso problema. Ho persino trovato il codice sorgente su Cutecom e ho seguito le impostazioni del termios che usano. Ancora il problema esiste. Quel primo personaggio è corrotto !!!!

  • Ecco le impostazioni di Termios dal mio programma. Impossibile impostare lo svuotamento per qualche motivo.

    velocità 38400 baud; righe 0; colonne 0; riga = 0; intr =^?; esci =^\; cancella =^H; kill =^U; eof =^D; eol =; eol2 =; swtch =; start =^Q; stop =^S; susp =^Z; rprnt =^R; werase =^W; lnext =^V; flush =; min = 60; tempo = 1; -parenb -parodd CS8 hupcl -cstopb cread clocal -crtscts ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8 -opost -olcuc - ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 Tab0 bs0 VT0 ff0 -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke

  • e il mio nuovo codice:

    #include <stdlib.h> 
    #include <stdio.h> 
    #include <unistd.h> 
    #include <fcntl.h> 
    #include <termios.h> 
    #include <sys/ioctl.h> 
    
    #define DEVICE "/dev/ttyUSB0" 
    #define SPEED B38400 
    
    int main() 
    { 
    struct termios tio; //to hold serial port settings 
    struct termios stdio; //so we can accept user input 
        struct termios old_stdio; //save the current port settings 
        int tty_fd; //file descriptor for serial port 
        int retval, res, n, res2, read1, wri; 
        char buf[255]; 
        char buf2[255]; 
    
    
        tty_fd = open(DEVICE, O_RDWR | O_NDELAY); 
        if(tty_fd < 0) 
        { 
         perror(DEVICE); 
         exit(-1); 
        } 
        printf("Init 1 complete.\n"); 
    
        tcflush(tty_fd, TCIOFLUSH); 
    
        int f = fcntl(tty_fd, F_GETFL, 0); 
        fcntl(tty_fd, F_SETFL, f & ~O_NDELAY); 
    
        retval = tcgetattr(tty_fd, &old_stdio); 
        if(retval != 0) 
        { 
         perror(DEVICE); 
         exit(-1); 
        } 
        printf("Init 2 complete.\n"); 
    
        struct termios newtio; 
        retval = tcgetattr(tty_fd, &newtio); 
        if(retval != 0) 
        { 
         perror(DEVICE); 
         exit(-1); 
        } 
        printf("Init 3 complete.\n"); 
    
        cfsetospeed(&newtio, SPEED); 
        cfsetispeed(&newtio, SPEED); 
    
        newtio.c_cflag = (newtio.c_cflag & ~CSIZE) | CS8; 
        newtio.c_cflag |= CLOCAL | CREAD; 
        newtio.c_cflag &= ~(PARENB | PARODD); 
        newtio.c_cflag &= ~CRTSCTS; 
        newtio.c_cflag &= ~CSTOPB; 
    
        newtio.c_iflag = IGNBRK; 
        newtio.c_iflag &= ~(IXON | IXOFF | IXANY); 
    
        newtio.c_lflag = 0; 
    
        newtio.c_oflag = 0; 
    
        newtio.c_cc[VTIME] = 1; 
        newtio.c_cc[VMIN] = 60; 
        newtio.c_cc[VINTR] = 127; 
        newtio.c_cc[VQUIT] = 28; 
        newtio.c_cc[VERASE] = 8; 
        newtio.c_cc[VKILL] = 21; 
        newtio.c_cc[VEOF] = 4; 
        newtio.c_cc[VSTOP] = 19; 
        newtio.c_cc[VSTART] = 17; 
        newtio.c_cc[VSUSP] = 26; 
        newtio.c_cc[VREPRINT] = 18; 
        newtio.c_cc[VFLSH] = 15; 
        newtio.c_cc[VWERASE] = 23; 
        newtio.c_cc[VLNEXT] = 22; 
    
    
        retval = tcsetattr(tty_fd, TCSANOW, &newtio); 
        if(retval != 0) 
        { 
         perror(DEVICE); 
         exit(-1); 
        } 
        printf("Init 4 complete.\n"); 
    
        int mcs = 0; 
        ioctl(tty_fd, TIOCMGET, &mcs); 
        mcs |= TIOCM_RTS; 
        ioctl(tty_fd, TIOCMSET, &mcs); 
    
        retval = tcgetattr(tty_fd, &newtio); 
        if(retval != 0) 
        { 
         perror(DEVICE); 
         exit(-1); 
        } 
        printf("Init 5 complete.\n"); 
    
        newtio.c_cflag &= ~CRTSCTS; 
    
        retval = tcsetattr(tty_fd, TCSANOW, &newtio); 
        if(retval != 0) 
        { 
         perror(DEVICE); 
         exit(-1); 
        } 
        printf("Init 6 complete.\n"); 
    
    
        for(n = 5; n > 0; n--) 
        { 
        printf("Please enter a command: "); 
        (void)fgets(buf2, 255, stdin); 
        (void)write(tty_fd, buf2, strlen(buf2)); 
        printf("Ok. Waiting for reply\n"); 
        res = read(tty_fd, buf, 255);  
        printf("Read:%d START%d %d %d %d %dFINISH\n",res,buf[0],buf[1],buf[2], buf[3], 
        buf[4]);    
        } 
    
        //restore the original port settings 
        tcsetattr(tty_fd, TCSANOW, &old_stdio); 
    
        close(tty_fd); 
    
        return EXIT_SUCCESS; //return all good 
    } 
    

ho un Sono completamente perso per ciò che può essere fatto o dove dovrei prenderlo da qui.

+0

C'è una ragione per cui stai segnalando solo i primi 5 byte letti quando la lettura dice che sono stati letti 6? Avete i mezzi per monitorare ciò che viene inviato tramite la combinazione RS-482 e USB per vedere se il microcontrollore invia realmente i byte che vi aspettate o se le informazioni vengono modificate prima che vengano trasmesse sul filo? Non ho lavorato con RS-482 (ma ricordo RS-232, non troppo appassionatamente), ma ci sono caratteri di controllo o frame dei messaggi che potrebbero essere d'intralcio? È come se il primo byte letto fosse modificato da 'expected & 0xEE' ... –

+0

Ciao Jonathan, il motivo per cui non sto riportando il sesto byte è perché è un \ n. Siccome sto usando la modalità canonica per i termios, la lettura ritorna quando colpisce a \ n. Guarderei dall'altra parte dei termios se non fosse per il buon funzionamento del mio setup usando Cutecom (che è in esecuzione sulla stessa macchina Linux). So che le ipotesi sono pericolose, ma in questa situazione penso che sia sicuro assumere il mio PIC, il convertitore da 485 a USB funziona bene, perché funziona come dovrebbe all'interno di Cutecom. Potresti per favore elaborare "expected & 0xEE" perché non sono sicuro da dove provenga. Saluti. –

+0

Altri test che ho eseguito in modo coerente hanno mostrato che 64 venivano sottratti dal primo carattere. Se aggiungessi 64 al risultato read() verrebbe archiviato in buf [0], otterrei il valore ASCII corretto. La cosa confusa su questo era il valore ASCII per il valore corretto non aveva il sesto bit impostato. Quindi, pensando che da qualche parte un & = 0x1011111 è stato eseguito, non ho supportato quell'idea. Ho provato alcune operazioni sul valore ma non ho potuto identificare alcun tipo di pattern. –

risposta

2

Non riesco a vedere nulla di evidentemente sbagliato con una scansione rapida del codice. Potresti prendere in considerazione la possibilità di passare a unsigned char buf[] se prevedi di lavorare con valori a 8 bit.

Dato che in Cutecom esiste un programma funzionante, è possibile utilizzare le impostazioni dei termios come riferimento per eseguire il debug del proprio programma.

Con Cutecom esecuzione su /dev/ttyUSB0, eseguire il seguente comando in un altro terminale per scaricare le impostazioni TTY:

stty -a -F /dev/ttyUSB0 

fare lo stesso quando l'esecuzione del programma e cercare le differenze tra le due configurazioni. Prova a configurare le impostazioni del terminale nel tuo programma in modo che corrispondano esattamente a quelle riportate per Cutecom.

Aggiornamento:

Dato che fissa le impostazioni termios non ha risolto il problema, qui ci sono alcune ulteriori cose da provare. Rischerei di indovinare che c'è un problema di temporizzazione da qualche parte. Quando si digita sulla console di Cutecom, si inviano 1 carattere alla volta sul dispositivo con molti millisecondi tra i caratteri. Quando si utilizza il programma, verrà inviato un buffer completo di caratteri dopo l'immissione di un comando, con i caratteri inviati in sequenza il più velocemente possibile. Forse il tuo programma PIC non è in grado di gestire i tempi del flusso di dati, o si aspetta due bit di stop, ad esempio invece di uno, risultando in alcuni codici di ritorno strani.

Probabilmente il miglior punto di partenza è tornato alla fonte. Prendi possesso di un oscilloscopio o di un analizzatore logico e verifica che i dati inviati dal PIC siano effettivamente corretti. Dovrai capire le forme d'onda a livello di bit, consentendo l'avvio e l'arresto dei bit. Confronta le forme d'onda sia per Cutecom che per il tuo programma. Se si utilizza un analizzatore logico, assicurarsi che l'orologio utilizzato sia un elevato multiplo della velocità di trasmissione. ad esempio un moltiplicatore 32.

Un altro modo per eseguire il debug è utilizzare strace per verificare che i caratteri restituiti dal driver siano effettivamente errati e che non si tratti di un problema con il programma. Usando strace, sarete in grado di vedere le letture/scritture non elaborate del vostro programma e ciò che viene restituito dal kernel. Utilizzare strace -o ~/tmp/strace_output.txt -ttt -xx your_program per scaricare tutte le chiamate di sistema mentre il programma è in esecuzione. A volte, solo il processo di avvicinamento di un programma lo rallenterà abbastanza da mostrare errori di temporizzazione. È possibile confrontare i tempi della lettura/scrittura con uno strace di Cutecom. Solo per i test, è possibile aggiungere la propria funzione write() che invia una stringa ma ritarda una piccola quantità tra ciascun carattere.

+0

Questo è stato un buon punto di partenza per confrontare le due impostazioni, ma avendo cambiato il mio codice per adattarlo alle impostazioni, non riesco ancora a farlo funzionare. –

+0

@ MitchGulliver Grazie per l'aggiornamento, peccato che non abbia funzionato fino ad ora. Ho aggiornato la mia risposta con alcune ulteriori informazioni che potrebbero aiutare nel debug. –

+0

Grazie Austin Phillips. Ho bisogno di fare un po 'di lavoro unificato, ma quando lavorerò di nuovo a questo progetto proverò il tuo suggerimento. Potrebbe essere il momento di acquistare anche un ambito. Quando avrò dei progressi aggiornerò il mio post. –

0

Ho finalmente risolto. Questo è stato risolto:

  • (vuoto) write (tty_fd, buf2, 1);

Il problema è stato risolto ma non sicuro al 100% sul motivo per cui stava facendo quello che era. Il problema era il mio programma che aggiungeva un \ n alla scrittura del microcontrollore. Quando ho fatto lo strace sia su Cutecom che sul mio programma, ho scoperto che Cutecom scrive '1' solo mentre il mio programma scriveva "1 \ n". Semplicemente non ci ho pensato abbastanza perché quando usi Cutecom per inviare un carattere digitato per esempio 1 nel prompt utente e poi premi invio. Sul lato PIC mio programma si presenta così:

while(1) 
{ 
    WATCHDOG(); 

    if(flag == 1) 
    { 
     char *start = str2; 
     RS485_TXEN1(); 
     indicator = UART1_getch(); //get character sent from PC 
     switch(indicator) 
     { 
      case '1' :      
        UART1_putstr("00000\n"); 
        DAC_Write(DAC_CH_2, 4095); 
        break; 
      case '2' :      
        UART1_putstr("23456\n"); 
        DAC_Write(DAC_CH_2, 0); 
        break; 
      case '3' : 
        UART1_putstr("34567\n"); 
        break; 
      case '4' :      
        UART1_putstr("45678\n"); 
        break; 
      case '\n' : 
        UART1_putch('\n'); 
        break; 
      default :   
        UART1_putstr("56789\n");     
        break; 
     } 
     RS485_RXEN1(); 
     flag = 0; 
    } 
} 

Quando un personaggio arriva sulla RX UART viene generato un interrupt. Ho impostato "flag" in questa routine di servizio di interrupt e quindi servizio il comando ricevuto in main().Non sono sicuro di come il primo carattere sia stato modificato come se fosse, ma sembra che ci sia stato qualche sovrascrittura o interruzione di scrittura a causa di "caso" \ n ":".

Correzione semplice alla fine e anche imparato alcune preziose lezioni lavorando con un sistema Linux e il debug. Grazie a tutti coloro che hanno offerto suggerimenti. Per chiunque desideri iniziare a interfacciare una macchina Linux e un microcontrollore, i codici sopra riportati potrebbero essere utili per iniziare.

Problemi correlati