#c #linux #serial-port
#c #linux #последовательный порт
Вопрос:
Я пытаюсь прочитать / записать последовательные данные в Linux, используя программирование на C. Я могу успешно открыть порт и установить атрибуты. Но когда я пытаюсь прочитать последовательные данные, read()
системный вызов ожидает неопределенно долго. Не могу понять, почему это происходит.
Для тестирования последовательной связи я использую arduino с пропускной способностью 9600 бод и конфигурацией 8N1.
Вот мой код:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
int init(char *port);
void reads(int fd);
int main(int argc, char *argv[]){
int fd;
fd = init(argv[1]);
fprintf(stdout, "nn####### DATA ########nn");
reads(fd);
close(fd);
return EXIT_SUCCESS;
}
int init(char *port){
int fd;
struct termios topt;
fd = open(port, O_RDWR | O_NONBLOCK);
if(fd < 0){
fprintf(stderr, "init error: can't open port %sn!", port);
exit(1);
}
if(tcgetattr(fd, amp;topt) < 0){
fprintf(stderr, "init error: can't get terios attributes!n");
exit(1);
}
topt.c_cflag amp;= ~CRTSCTS;
topt.c_cflag |= CREAD | CLOCAL;
topt.c_cflag amp;= ~(IXON | IXOFF | IXANY);
topt.c_cflag amp;= ~(ICANON | ECHO | ECHOE | ISIG);
topt.c_cflag amp;= ~OPOST;
cfsetispeed(amp;topt, B9600);
cfsetospeed(amp;topt, B9600);
topt.c_cflag amp;= ~PARENB;
topt.c_cflag amp;= ~CSTOPB;
topt.c_cflag amp;= ~CSIZE;
topt.c_cflag |= CS8;
topt.c_cc[VMIN] = 1;
topt.c_cc[VTIME] = 0;
tcsetattr(fd, TCSANOW, amp;topt);
if(tcsetattr(fd, TCSAFLUSH, amp;topt) < 0){
fprintf(stderr, "init error: can't set attributes!n");
exit(1);
}
}
void reads(int fd){
char rdbuff[1];
while(read(fd, rdbuff, 1) > 0){
fprintf(stdout, "%c", rdbuff[0]);
}
}
Спасибо!
Комментарии:
1. О, ваша
init
функция не возвращает файловый дескриптор! Ваш компилятор должен был предупредить вас об этом.2.
O_NONBLOCK
означает неблокирующий режим. Если чтение или запись не могут быть выполнены немедленно (дляread()
нет последовательных данных или маленький последовательный аппаратный буфер заполненwrite()
), они вернутся-1
сerrno == EAGAIN
илиerrno == EWOULDBLOCK
. Он используется только тогда, когда вам нужно выполнить много действий, и вы хотите выполнить мультиплексирование между этими разными действиями, не будучи «заблокированным» в какой-то моментread()
илиwrite()
просто потому, что они не могут быть выполнены прямо сейчас .3. Кроме того, при использовании serial (или сетевого оборудования, если на то пошло) вы никогда не должны предполагать, что
read(fd, data, n)
orwrite(fd, data, n)
возвращает либо ошибку, либоn
; они могут и часто будут возвращать некоторое положительное целое число, меньшееn
(но не 0; в этом случае они возвращают-1
сerrno==EINTR||errno==EAGAIN||errno==EWOULDBLOCK
вместо этого). Это называется короткими отсчетами . При последовательном или сетевом чтении и записи вы всегда должны использовать цикл, который считывает / записывает остальные данные, если вызов возвращает короткое значение.4. при компиляции. всегда включайте все предупреждения, затем исправляйте эти предупреждения. для
gcc
минимального использования:-Wall -Wextra -pedantic
я также использую:-Wconversion -std=gnu99
5. @AshfaqurRahman: в неблокирующем режиме вы используете select() или poll() , чтобы определить, в каких дескрипторах есть данные для чтения или в которые они могут быть записаны без блокировки. Я думаю, я мог бы привести пример небольшой программы, которая делает это, записывая данные, считанные со стандартного ввода, в последовательный порт, а данные, считанные с последовательного порта, в стандартный вывод, с терминалом в режиме raw (каждое нажатие клавиши отправляется немедленно), если в этом есть интерес.