Linux termios изменение первого символа после чтения последовательного порта()

моя настройка termios изменяет первый символ, считанный из последовательного порта с помощью read (). У меня есть микроконтроллер, говорящий с коробкой linux. Микроконтроллер отвечает на команды, отправленные с компьютера linux. Настройка выглядит следующим образом:

  • микроконтроллер (PIC24F) RS485 порт RS485 к USB конвертер Ubuntu PC.

когда я запускаю терминальную программу, такую как Cutecom, все работает по плану. Я отправляю командный символ на PIC, и я получаю ответ, однако, когда я использую программу командной строки, первый символ изменяется. Вот мой код:

#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 %dFINISHn",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; 
}

вот пример результатов, которые я получаю.

  • когда PIC отправляет "00000n" выход: чтение: 6 начало-16 48 48 48 48FINISH
  • когда PIC отправляет "23456n" выход: чтение: 6 начало-14 51 52 53 54FINISH
  • когда PIC отправляет "34567n" выход: чтение: 6 начало-14 52 53 54 55FINISH
  • когда PIC отправляет "45678n" выход: чтение: 6 начало-12 53 54 55 56FINISH
  • когда PIC отправляет "56789n", вывод: чтение: 6 начало-12 54 55 56 57FINISH

по какой-то причине первый символ становится испорченным некоторыми настройками termios. Это должны быть настройки termios, поскольку те же тестовые входы, что и выше, возвращаются точно при запуске Cutecom. Я читал страницы руководства снова и снова, пробуя все различные настройки на элементе управления вводом, но независимо от того, что я вообще не качает эту проблему.

для легкого исправления я могу просто перенести свои данные через 1 символ, но хочу избежать этого.

кто-нибудь испытал такую проблему или имеет представление, что с ней делать?

большое спасибо.

28/3/13 Отличное предложение Остин. Для тех, кто интересуется здесь, есть два выхода:

  • сначала настройки termios в моей программе

    скорость 38400 БОД; строки 0; столбцы 0; строка = 0; intr =; quit =; erase =; kill =; eof = ; eol =; eol2 =; swtch =; start =; stop = ; susp =; rprnt =; werase = ; lnext = ; flush =; min = 0; time = 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 вt0 ff0 до -исиг icanon -iexten -Эхо -echoe -echok -echonl -noflsh -сайт xcase -костановить -echoprt -echoctl -echoke

  • и настройки, которые использует cutecom

    скорость 38400 БОД; строки 0; столбцы 0; строка = 0; intr = ^C; quit = ^; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; swtch = начало = ^м; остановка = ^с; Г = ^З; rprnt = ^Р; werase = ^W; lnext = ^V; flush = ^O; min = 60; time = 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 вt0 ff0 до -исиг -icanon -iexten -Эхо -echoe -echok -echonl -noflsh -сайт xcase -костановить -echoprt -echoctl -echoke

Я все еще прохожу через все это и обновлю сообщение, когда я добьюсь прогресса.

29/3/13 Все та же проблема. Я даже нашел исходный код для Cutecom и следил за настройками termios, которые они используют. Проблема все еще существует. Этот первый персонаж испорчен!!!!

  • вот настройки Termios из моей программы. По какой-то причине не удается установить flush.

    скорость 38400 БОД; строки 0; столбцы 0; строка = 0; intr = ^?; quit = ^; erase = ^H; kill = ^U; eof = ^D; eol = ; eol2 = ; swtch = начало = ^м; остановка = ^с; Г = ^З; rprnt = ^Р; werase = ^W; lnext = ^V; flush =; min = 60; time = 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 вt0 ff0 до -исиг -icanon -iexten -Эхо -echoe -echok -echonl -noflsh -сайт xcase -костановить -echoprt -echoctl -echoke

  • и мой новый код:

    #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 replyn");
        res = read(tty_fd, buf, 255);       
        printf("Read:%d START%d %d %d %d %dFINISHn",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
    }
    

Я не понимаю, как можно сделать или где я должен забрать его отсюда.

2 ответов


Я не вижу ничего явно неправильного в быстром сканировании вашего кода. Возможно, вы захотите рассмотреть возможность перехода на unsigned char buf[] Если вы ожидаете работать с 8-битными значениями.

поскольку у вас есть рабочая программа в Cutecom, вы можете использовать их настройки termios в качестве ссылки для отладки вашей собственной программы.

С Cutecom работает на /dev/ttyUSB0, выполните следующую команду в другом терминале, чтобы сбросить настройки tty:

stty -a -F /dev/ttyUSB0

сделайте то же самое при запуске программы и ищите различия между этими двумя конфигурациями. Попробуйте настроить параметры терминала в вашей программе, чтобы точно соответствовать тем, что сообщается для Cutecom.

обновление:

поскольку исправление настроек termios не решило проблему, вот еще несколько вещей, которые нужно попробовать. Я бы рискнул предположить, что где-то есть проблема времени. При вводе текста на консоли Cutecom вы отправляете 1 символ за раз на свое устройство со многими миллисекундами между письмена. При использовании вашей программы Полный буфер символов будет отправлен после ввода команды, причем символы будут отправляться обратно так быстро, как позволяет драйвер. Возможно, ваша программа PIC не может обрабатывать время потока данных или ожидает два стоп-бита, например, вместо одного, что приводит к некоторым странным кодам возврата.

вероятно, лучшее место для начала-вернуться к источнику. Возьмите осциллограф или логический анализатор и убедитесь, что отправляемые данные ПИЦ, в принципе, правильно. Вам нужно будет понять формы сигналов битового уровня, позволяющие запускать и останавливать биты. Сравнить осциллограммы для обоих Cutecom и ваша программа. При использовании логического анализатора убедитесь, что используемые часы являются некоторыми высокими кратными вашей скорости передачи данных. например, множитель 32.

другой способ отладки-использовать strace чтобы убедиться, что символы, возвращаемые драйвером, на самом деле неверны, и это не проблема с вашей программой. Используя strace, вы будете увидеть сырое читает/пишет программы и возвращенного ядра. Использовать strace -o ~/tmp/strace_output.txt -ttt -xx your_program для сброса всех системных вызовов во время работы программы. Иногда просто процесс stracing программы замедлит его достаточно, чтобы показать ошибки синхронизации. Вы можете сравнить время чтения / записи с strace из Cutecom. Просто для тестирования вы можете добавить свой собственный write() функция, которая отправляет строку, но задерживает небольшое количество между каждым символом.


Я, наконец, понял. Вот что исправило это:

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

проблема исправлена, но не на 100% уверена, почему она делала то, что было. Проблема заключалась в том, что моя программа добавляла \n к записи микроконтроллера. Когда я сделал strace на Cutecom и моей программе, я обнаружил, что Cutecom пишет "1"только тогда, когда моя программа будет писать "1\n". Я просто не думал об этом достаточно, потому что при использовании Cutecom для отправки символ введите, например, 1 в приглашении пользователя и нажмите клавишу Ввод. На стороне PIC моя программа выглядит так:

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

когда символ прибывает на UART RX, генерируется прерывание. Я устанавливаю "флаг" в этой процедуре обслуживания прерываний, а затем обслуживаю полученную команду в main(). Не уверен, как первый символ был изменен, как это было, но похоже, что произошла некоторая перезапись или прерывание записи из-за "case '\n' :".

простое исправление в закончите и даже извлекли некоторые ценные уроки, работая с системой Linux и отладкой. Спасибо всем, кто предложил предложения. Для тех, кто хочет начать взаимодействовать с Linux box и micro controller, вышеуказанные коды могут быть полезны для начала работы.