2013-03-28 9 views
6

Mój termios setup modyfikuje pierwszy znak odczytany z portu szeregowego za pomocą read(). Mam mikrokontroler rozmawiający z linuxem. Mikrokontroler reaguje na polecenia wysyłane z maszyny linuxowej. Konfiguracja jest następująca:Monitory Linuxa modyfikujące pierwszy znak po odczytaniu portu szeregowego()

  • mikrokontrolera (PIC24F) port RS485 < -> RS485 konwerter USB < -> Ubuntu PC.

Po uruchomieniu programu terminalowego, takiego jak Cutecom, wszystko działa zgodnie z planem. Wysyłam do PIC znak komend i otrzymuję odpowiedź, jednak kiedy używam programu wiersza poleceń, modyfikowany jest pierwszy znak. Oto mój kod:

#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; 
} 

Oto przykład wyników, które otrzymuję.

  • Po PIC wysyła "00000 \ n" wyjście jest w: 6 START-16 48 48 48 48FINISH
  • Gdy PIC wysyła "23456 \ n" wyjście w: 6 START- 14 51 52 53 54FINISH
  • Gdy PIC wysyła "34567 \ n", wyjście to: Odczyt: 6 START-14 52 53 54 55 FIŃSKI
  • Gdy PIC wysyła "45678 \ n", wyjście ma postać: Odczyt: 6 START-12 53 54 55 56FINISH
  • Gdy PIC wysyła "56789 \ n", wyjście to: Odczyt: 6 START-12 54 55 56 57FINISH

Z jakiegoś powodu pierwsza postać zostaje popsuta przez jakieś ustawienia. Muszą to być ustawienia termios, ponieważ te same wejścia testowe powyżej są zwracane dokładnie po uruchomieniu Cutecom. Czytałem strony podręcznika w kółko, próbując wszystkich różnych ustawień kontroli wprowadzania, ale bez względu na to, co robię, nie można potrząsnąć tym problemem.

Dla łatwej poprawki mogę po prostu przesunąć moje dane o 1 znak, ale chcę tego uniknąć.

Czy ktoś napotkał taki problem lub ma jakiś pomysł, co z tym zrobić?

Wielkie dzięki.

28/3/13 Świetna propozycja Austin.Dla tych, którzy są zainteresowani są tu dwa wyjścia:

  • Pierwsze są ustawienia termios w moim programie

    prędkością 38400 bodów; wiersze 0; kolumny 0; line = 0; intr =; quit =; erase =; kill =; eof =; eol =; eol2 =; swtch =; start =; stop =; susp =; rprnt =; werase =; lnext =; flush =; min = 0; czas = 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

  • A ustawienia cutecom używają prędkości 38400 b/d; wiersze 0; kolumny 0; line = 0; intr =^C; quit =^\; erase =^?; kill =^U; eof =^D; eol =; eol2 =; swtch =; start =^Q; stop =^S; susp =^Z; rprnt =^R; werase =^W; lnext =^V; flush =^O; min = 60; czas = 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

Nadal sprawdzam to wszystko i zaktualizuję wpis, gdy będę robił postępy.

29/3/13 Nadal mają ten sam problem. Znalazłem kod źródłowy Cutecom i zastosowałem ustawienia termios, których używają. Wciąż istnieje problem. Ta pierwsza postać jest uszkodzona !!!!

  • Oto ustawienia Termiosa z mojego programu. Z jakiegoś powodu nie można ustawić koloru.

    prędkość 38400 bodów; wiersze 0; kolumny 0; line = 0; intr =^?; quit =^\; erase =^H; kill =^U; eof =^D; eol =; eol2 =; swtch =; start =^Q; stop =^S; susp =^Z; rprnt =^R; werase =^W; lnext =^V; flush =; min = 60; czas = 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

  • a mój nowy kod:

    #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 
    } 
    

i a Jestem całkowicie zagubiony co do tego, co można zrobić lub skąd powinienem go stąd zabrać.

+0

Czy istnieje powód, dla którego raportowane są tylko pierwsze 5 bajtów przeczytanych, gdy przeczytano, że 6 zostało przeczytanych? Czy masz środki do monitorowania, co jest wysyłane przez kombinację RS-482 i USB, aby sprawdzić, czy mikrokontroler rzeczywiście wysyła oczekiwane bajty lub czy informacja jest modyfikowana, zanim przejdzie przez przewód? Nie pracowałem z RS-482 (ale pamiętam RS-232, niezbyt sympatycznie), ale czy istnieją znaki kontrolne lub ramka wiadomości, która może przeszkadzać? To tak, jakby pierwszy przeczytany bajt został zmodyfikowany przez 'expected & 0xEE' ... –

+0

Cześć Jonathan, powodem, dla którego nie raportuję 6-tego bajtu jest to, że jest \ n. Ponieważ używam trybu canonical dla termios, odczyt powraca, gdy trafi \ n. Byłbym patrząc na drugą stronę termios, gdyby nie pomyślne działanie mojej konfiguracji przy użyciu Cutecom (która działa na tym samym komputerze z Linuksem). Wiem, że założenia są niebezpieczne, ale w tej sytuacji myślę, że bezpiecznie jest założyć mój PIC, konwerter 485-na-USB działa dobrze, ponieważ działa tak jak powinien w Cutecom. Czy mógłbyś wyjaśnić "oczekiwane i 0xEE", ponieważ nie jestem pewien, skąd pochodzi. Pozdrowienia. –

+0

Inne przeprowadzone przeze mnie testy konsekwentnie pokazały, że 64 odejmowano od pierwszego znaku. Jeśli dodałem 64 do wyniku read() będzie przechowywać w buf [0], otrzymam poprawną wartość ASCII. Mylące jest to, że wartość ASCII dla poprawnej wartości nie ma ustawionego szóstego bitu. Tak więc myślenie, że gdzieś a & = 0x1011111 zostało wykonane, nie wspiera tego pomysłu. Próbowałem kilku operacji na wartości, ale nie mogłem zidentyfikować żadnego wzorca. –

Odpowiedz

2

Nie widzę niczego wyraźnie nie tak z szybkim skanowaniem kodu. Możesz rozważyć przejście na unsigned char buf[], jeśli spodziewasz się pracy z 8-bitowymi wartościami.

Ponieważ masz działający program w Cutecom, możesz użyć ich ustawień termios jako odniesienia do debugowania własnego programu.

Z Cutecom działa na /dev/ttyUSB0, uruchom następujące polecenie w innym terminalu zrzucić ustawień tty:

stty -a -F /dev/ttyUSB0 

Zrób to samo, gdy uruchomiony program i szukać różnic pomiędzy dwóch konfiguracjach. Spróbuj skonfigurować ustawienia terminala w swoim programie, aby dokładnie pasowały do ​​raportów zgłoszonych dla Cutecom.

Aktualizacja:

Ponieważ ustalające ustawienia termios nie został rozwiązany, oto kilka kolejnych rzeczy spróbować. Zaryzykowałbym przypuszczenie, że gdzieś jest problem z timingiem. Pisząc na konsoli Cutecom, wysyłasz po jednym znaku na urządzenie z wieloma milisekundami między znakami. Podczas korzystania z programu, pełny bufor znaków zostanie wysłany po wprowadzeniu polecenia, a znaki będą wysyłane do siebie tak szybko, jak pozwala na to kierowca. Może twój program PIC nie poradzi sobie z synchronizacją strumienia danych, lub oczekuje na przykład dwóch bitów stopu zamiast jednego, powodując dziwne kody powrotu.

Prawdopodobnie najlepsze miejsce do rozpoczęcia znajduje się z powrotem u źródła. Zdobądź oscyloskop lub analizator logiczny i sprawdź, czy dane wysyłane przez PIC są poprawne. Będziesz musiał zrozumieć przebiegi poziomu bitów, pozwalając na bity rozpoczęcia i zakończenia. Porównaj przebiegi dla Cutecom i twojego programu. Jeśli korzystasz z analizatora stanów logicznych, upewnij się, że używany zegar to pewna wysoka wielokrotność szybkości transmisji. np. mnożnik 32.

Innym sposobem debugowania jest użycie strace do sprawdzenia, czy znaki zwrócone przez sterownik są w rzeczywistości nieprawidłowe i to nie jest problem z programem. Korzystając z strace, będziesz mógł zobaczyć surowe odczyty/zapisy twojego programu i to, co jest zwracane przez jądro. Użyj polecenia strace -o ~/tmp/strace_output.txt -ttt -xx your_program, aby zrzucić wszystkie wywołania systemowe podczas działania programu. Czasami sam proces zatrzymania programu spowolni go wystarczająco, aby pokazać błędy dotyczące czasu. Można porównać czas odczytu/zapisu z wartością strace Cutecom. Tylko do testowania, możesz dodać własną funkcję write(), która wysyła ciąg, ale opóźnia niewielką ilość pomiędzy poszczególnymi postaciami.

+0

To był dobry punkt wyjścia do porównania obu ustawień, ale po zmianie kodu, aby dopasować ustawienia, nadal nie mogę go uruchomić. –

+0

@MitchGulliver Dzięki za aktualizację, szkoda, że ​​do tej pory się nie udało. Zaktualizowałem swoją odpowiedź, podając więcej informacji, które mogą pomóc w debugowaniu. –

+0

Dzięki Austin Phillips. Muszę zrobić trochę pracy w zespole, ale kiedy ponownie popracuję nad tym projektem, spróbuję twojej sugestii. Być może nadszedł czas na zakup także zakresu. Kiedy robię postępy, zaktualizuję swój post. –

0

W końcu to rozwiązałem. Naprawiono to:

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

Problem jest naprawiony, ale nie jest w 100% pewny, dlaczego robił to, co było. Problemem był mój program dodający \ n do zapisu mikrokontrolera. Kiedy zrobiłem krok w obu Cutecom i moim programie, odkryłem, że Cutecom pisze tylko "1", podczas gdy mój program napisałby "1 \ n". Po prostu nie pomyślałem o tym wystarczająco, ponieważ używając Cutecom do wysłania znaku, który wpisałeś na przykład 1 w monicie użytkownika, a następnie naciśnij enter. Po stronie PIC mój program wygląda następująco:

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; 
    } 
} 

Gdy postać pojawia się na UART RX generowane jest przerwanie. Ustawię "flagę" w tej procedurze obsługi przerwań, a następnie obsługuję odebrane polecenie w main().Nie wiem, jak modyfikowany był pierwszy znak, ale wygląda na to, że wystąpiło przerwanie nadpisywania lub zapisu z powodu "sprawy" \ n ":".

Prosta naprawa, a nawet nauka cennych lekcji z systemem Linux i debugowanie. Dziękujemy wszystkim, którzy zaoferowali sugestie. Każdemu, kto chce zacząć od połączenia z linuxem i mikrokontrolerem, powyższe kody mogą być pomocne w rozpoczęciu pracy.

Powiązane problemy