Файловый дескриптор находится в режиме блокировки, но read () не блокирует - PullRequest
2 голосов
/ 08 июля 2019

Я пишу некоторое программное обеспечение для обработки чтения / записи последовательного порта для системы Beaglebone. ОС - Debian 9. Я пишу код на C с --std=gnu99.

Вот мой код:

// reference
// https://www.cmrr.umn.edu/~strupp/serial.html

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>

int open_port(void)
{    
    int fd;

    fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY);
    // removing O_NDELAY no difference
    if(fd == -1)
    {
        perror("open_port: Unable to open /dev/ttyS1 - ");
    }
    else
    {
        fcntl(fd, F_SETFL, 0);
    }

    return fd;
}

int main()
{

    // open fd for serial port
    int fd = open_port();


    // set baud rate
    struct termios options;
    // get current options
    tcgetattr(fd, &options);
    // set input and output baud
    cfsetispeed(&options, B115200);
    cfsetospeed(&options, B115200);
    // enable reciever and set local mode
    // CLOCAL: ignore modem control lines
    // CREAD: enable reciever
    options.c_cflag |= (CLOCAL | CREAD);

    // set partity bit
    //options.c_cflag &= PARENB;
    options.c_cflag &= ~PARENB;
    // use even parity
    //options.c_cflag &= ~PARODD;
    // use only 1 stop bit
    options.c_cflag &= ~CSTOPB;
    // set character size to 8 bit
    options.c_cflag &= ~CSIZE;
    options.c_cflag &= CS8;
    // disable flow control
    //options.c_cflag &= ~CNEW_RTSCTS; // does not work?

    // note: check local options, some may be required
    // disable canonical input (use raw)
    // disable echoing of characters
    // disable echoing of erase characters
    // disable signals SIGINTR SIGSUSP SIGDSUSP, SIGQUIT
    // input characters are passed through exactly as recieved
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

    // disable parity check
    options.c_iflag &= IGNPAR;

    // disable flow control (software)
    options.c_iflag &= ~(IXON | IXOFF | IXANY);

    // set raw output, no output processing
    options.c_oflag &= ~OPOST;

    // set options
    tcsetattr(fd, TCSANOW, &options);

    char data[] = {0x01, 0x03, 0x00, 0x00};
    int n = write(fd, data, 4);
    if(n =! 4)
    {
        printf("write fail %d\n", n);
    }

    char buffer[40];
    n = read(fd, buffer, 2);
    if(n != 2)
    {
        printf("read fail %d\n", n);
    }
    if(buffer[0] == 0x03 && buffer[1] == 0x00)
    {

    }
    else
    {
        printf("uart error\n");
    }

    char data2[] = {0x00, 0x00, 0x00, 0x01};
    n = write(fd, data2, 4);
    if(n != 4)
    {
        printf("write fail %d\n", n);
    }

    n = read(fd, buffer, 2);
    if(n != 2)
    {
        printf("read fail %d\n", n);
    }
    if(buffer[0] == 0x03 && buffer[1] == 0x00)
    {

    }
    else
    {
        printf("uart error\n");
    }

    printf("process complete\n");

    // to close
    close(fd);
}

У меня проблема с вызовами read() , не блокирующими . Это не желаемое поведение. Эти вызовы должны блокироваться, пока не будут получены 2 байта данных.

Я думаю, что я где-то неправильно настроил опцию. Однако я не знаю, где ошибка, и из того, что я исследовал, это должно быть чтение в режиме блокировки. (fcntl(fd, F_SETFL, 0);)

1 Ответ

1 голос
/ 08 июля 2019

У меня проблема с вызовами read() , не блокирующими .

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

... из того, что я исследовал, это должно быть чтение в режиме блокировки. (fcntl(fd, F_SETFL, 0);)

Правильно, дескриптор файла сконфигурирован для режима блокировки.

Я думаю, что я где-то неправильно настроил опцию.

Да, ваша конфигурация termios неполная. (Он использует правильные побитовые операции, но, к сожалению, он неполон и содержит ошибки, см. ДОБАВЛЕНИЕ.)
При настройке на неканонический режим необходимо также указать параметры VMIN и VTIME .
Если не указано (как в вашем коде), вероятно, будут действовать значения по умолчанию VMIN = 0 и VTIME = 0, что эквивалентно неблокирующему чтению.


Обратите внимание, что длина байта в системном вызове read () оказывает минимальное влияние на поведение завершения (кроме ограничения количества возвращаемых байтов).
Со страницы man для читайте (2) :

read () пытается прочитать до count байтов из файлового дескриптора fd в буфер, начиная с buf.

Следовательно, параметр count фактически является максимальным.
Фактическое число считанных байтов может быть от нуля до count в зависимости от значений, настроенных для VMIN и VTIME (и фактических доступных данных).

См. Блокирование Linux против последовательных неблокирующих операций. Прочитайте для более подробной информации.


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

options.c_cc[VMIN] = 2;
options.c_cc[VTIME] = 0;

Вышеуказанные два оператора, вставленные в вашу программу перед системным вызовом tcsetattr () , будут вызывать постоянную блокировку вашей программы до тех пор, пока в каждом операторе read(fd, buffer, 2) не будет возвращено ровно два байта.
Оператор read(fd, buffer, 4) также будет блокировать навсегда и возвращать 2, 3 или 4 байта данных (в зависимости от времени программы в отношении приема и буферизации данных).


ADDENDUM

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

Следующий оператор в вашей программе стирает все предыдущие операции c_cflag и непреднамеренно перенастраивает скорость передачи в B0 и размер символа в CS5 :

    options.c_cflag &= CS8;

Правильный синтаксис:

    options.c_cflag |= CS8;

Заявление

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

должно быть расширено за счет большего количества атрибутов для соответствия cfmakeraw () :

    options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);

Следующая инструкция в вашей программе отключает каждый атрибут в c_iflag, за исключением IGNPAR , и оставляет атрибут IGNPAR в неизвестном / неоднозначном состоянии:

    // disable parity check
    options.c_iflag &= IGNPAR;

Чтобы соответствовать cfmakeraw () , его следует изменить на:

    options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
                       | INLCR | IGNCR | ICRNL);

Со всеми упомянутыми исправлениями я могу заставить вашу программу работать как положено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...