Последовательная связь в Ubuntu: сбой при чтении, а затем все сразу

Я пишу программу, которая работает на одноплатном компьютере MIO-3260 с сервером Ubuntu 14.04 и взаимодействует с диском AMC DPRANIE C100A400. Программа отправляет строку шестнадцатеричных кодов на диск и должна получать ответ на каждое отправленное сообщение. Когда я пробую это в realTerm на Windows, это работает хорошо, поэтому я не думаю, что это проблема с дисководом. Однако, когда я пытаюсь читать с последовательного порта, read() возвращает -1 почти все время, пока внезапно в, казалось бы, случайной точке я не получаю массивный дамп сообщений одновременно.

Я использую termios для настройки последовательного порта. Вот мой код для этого. Я попытался настроить его в конфигурации блокировки, но если я это сделаю, код просто зависает на неопределенное время при первом чтении ().

int fd;
fd = open("/dev/ttyS0",O_RDWR | O_NOCTTY | O_NDELAY);
struct termios SerialPortSettings;

tcgetattr(fd, &SerialPortSettings); //get current settings of serial port
cfsetispeed(&SerialPortSettings,B115200);//set input baud rate
cfsetospeed(&SerialPortSettings,B115200);//set output baud rate
SerialPortSettings.c_cflag &= ~PARENB;//clear parity bit (no parity)
SerialPortSettings.c_cflag &= ~CSTOPB;//Stop bits = 1
SerialPortSettings.c_cflag &= ~CSIZE;//clears the mask
SerialPortSettings.c_cflag |= CS8; //set data bits = 8
SerialPortSettings.c_cflag &= ~CRTSCTS; //turn off hardwar based flow ctrl
SerialPortSettings.c_cflag |= CREAD | CLOCAL;//Turn on the reciever

SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); //Turn off software
//based flow control
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);//Non-canonical mode
SerialPortSettings.c_oflag &= ~OPOST;//no output processing
//set the termios struct now
if(tcsetattr(fd,TCSANOW,&SerialPortSettings) != 0)
    printf("\n ERROR: setting attributes");
else
    printf("\n Baudrate = 115200 \t Stopbits = 1 \t Parity = none");

Для чтения из последовательного порта мой код выглядит следующим образом:

uint8_t buf[1024];//Rx buffer
int bytes_read;
bytes_read = read(fd,&buf,1024);
if(bytes_read != -1){
        for(int i=0;i<bytes_read;i++)    /*printing only the received characters*/
                printf("%02X\t",buf[i]);
        puts("\n");
}

Вот пример сообщения, которое я должен получить {0xA5 0xFF 0x10 0x01 0x00 0x00 0xD4 0x11}. Это должно быть около 8-14 байт. Вместо этого я получаю огромное количество сразу и ни одного в другое время (например, я только что получил 810 байт сразу после отправки 946 команд без ответа).

Последние несколько дней я устранял неисправности и понятия не имею, что происходит. Иногда я запускаю его, и он отвечает большую часть времени, а затем загадочным образом просто останавливается, а затем периодически возвращается.

Дайте мне знать, если есть больше информации, которую я могу предоставить.

Любая помощь могла бы быть полезна!

ОБНОВЛЕНИЕ: я также подключил свой ноутбук к последовательному порту, чтобы я мог следить за передачами с помощью RealTerm, и я проверил, что команды отправляются MIO-3260 правильно и на них корректно реагирует привод. Таким образом, проблема, кажется, когда я пытаюсь читать из порта.

1 ответ

Решение

Я попытался настроить его в конфигурации блокировки, но если я это сделаю, код просто зависает на неопределенное время при первом чтении ().

Результаты, которые вы описываете, похожи на блокирование канонического режима (когда вы действительно хотите (блокирование) необработанного режима).
Предполагается, что read() блокируется до получения символа EOL (конец строки).

Однако, когда я пытаюсь читать с последовательного порта, read() возвращает -1 почти все время, пока внезапно в, казалось бы, случайной точке я не получаю массивный дамп сообщений одновременно.

Результаты, которые вы описываете, похожи на неблокирующий канонический режим (когда вы действительно хотите (блокирующий) необработанный режим).
Если в буфере нет данных (то есть полной строки), read() вернет -1, а errno (который вы не потрудитесь исследовать) устанавливается в -EAGAIN.
Но когда двоичные данные совпадают по совпадению с символом EOL (конец строки), выполняется условие для удовлетворения канонического чтения (), и буферизованные данные возвращаются.

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

SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);//Non-canonical mode 

ICANON находится в c_lflag член, а не в c_iflag,
Таким образом, заявление должно быть

SerialPortSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Non-canonical mode 

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


Кроме того, для необработанного режима необходимо определить элементы VMIN и VTIME.
Например:

SerialPortSettings.c_cc[VMIN]  = 1;
SerialPortSettings.c_cc[VTIME] = 1;

Другой ошибкой в ​​вашем коде является использование указателя на адрес массива (т.е. адрес адреса), когда адреса массива будет достаточно.

bytes_read = read(fd,&buf,1024);

должно быть просто

bytes_read = read(fd, buf, 1024);

ДОПОЛНЕНИЕ

Код OP замечательно похож на этот вопрос, который имеет идентичное утверждение о неверном termios.
Этот плакат в конечном итоге решил свою собственную проблему, но ошибочно приписал исправление добавлению (не относящемуся к делу) флага ECHONL и не осознавая, что он действительно исправлял имя члена структуры.


ДОБАВЛЕНИЕ 2

Похоже, источником этой ошибки c_iflag и ICANON является руководство по последовательному порту из xanthium.in. Автор был уведомлен более двух лет назад об ошибке, но не исправил ее.

Другие вопросы по тегам