Не могу прочитать с последовательного порта, если не открыли один раз с Minicom - PullRequest
0 голосов
/ 02 сентября 2018

Я реализовал блокировку чтения из последовательного порта в C. Моя цель - прочитать, какие блоки блокируются до поступления новых данных.

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

typedef struct
{
    int fd;
    se_serial_speed_t speed;
    se_serial_parity_t parity;
    bool flow_control;
}se_serial_t;

int se_serial_constructor(se_serial_t** self, char* serial_port)
{
    int fd;

    if(NULL != *self)
    {
        return ERR_NNULL_PTR;
    }
    else if(0 != access(serial_port, F_OK))
    {
        ERRNO("Serial port is not available");
        return ERR_ILLEGAL_PARAM;
    }
    else
    {
        if(-1 == (fd = open(serial_port, O_RDWR | O_NOCTTY)))
        {
            ERRNO("Error opening %s in rw mode", serial_port);
            return ERR_OFILE_FAIL;
        }
        else if(NULL == (*self = malloc(sizeof(se_serial_t))))
        {
            ERROR("Error allocating memory for Serial");
            return ERR_MALLOC_FAIL;
        }
        (*self)->fd = fd;
    }
    return ERR_OK;
}

int se_serial_configure_interface(se_serial_t* self, se_serial_speed_t speed, se_serial_parity_t parity, bool flow_control)
{
    struct termios options;

    if(NULL == self)
    {
        return ERR_NULL_PTR;
    }
    else
    {
        if(0 != tcgetattr(self->fd,  &options))
        {
            ERRNO("Unable to get serial port current configuration");
        }
        if(0 != cfsetospeed(&options, speed))
        {
            ERRNO("Unable to set serial port output speed");
        }
        if(0 != cfsetispeed(&options, speed))
        {
            ERRNO("Unable to set serial port input speed");
        }

        switch(parity)
        {
        case SE_SERIAL_PARITY_8N1:
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;
            options.c_cflag &= ~CSIZE;
            options.c_cflag |= CS8;
            break;
        case SE_SERIAL_PARITY_7E1:
            options.c_cflag |= PARENB;
            options.c_cflag &= ~PARODD;
            options.c_cflag &= ~CSTOPB;
            options.c_cflag &= ~CSIZE;
            options.c_cflag |= CS7;
            break;
        case SE_SERIAL_PARITY_7O1:
            options.c_cflag |= PARENB;
            options.c_cflag |= PARODD;
            options.c_cflag &= ~CSTOPB;
            options.c_cflag &= ~CSIZE;
            options.c_cflag |= CS7;
            break;
        case SE_SERIAL_PARITY_7S1:
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;
            options.c_cflag &= ~CSIZE;
            options.c_cflag |= CS8;
            break;
        default:
            WARNING("Unable to set serial port parity");
            break;
        }

        if(flow_control)
            options.c_cflag |= CRTSCTS;
        else
            options.c_cflag &= ~CRTSCTS;

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

        if(0 != tcsetattr(self->fd, TCSANOW, &options))
        {
            ERRNO("Error configuring serial port");
            return ERR_SERIAL_CONF_FAIL;
        }
        self->speed = speed;
        self->parity = parity;
        self->flow_control = flow_control;
    }
    return ERR_OK;
}

int se_serial_read(se_serial_t* self, uint8_t* buffer, int size)
{
    int bytes_read = 0;
    int ret;

    if(NULL == self)
    {
        return ERR_NULL_PTR;
    }
    else
    {
        while(bytes_read < size)
        {
            if(0 > (ret = read(self->fd, &(buffer[bytes_read]), size - bytes_read)))
            {
                ERROR("Error reading from %s : %d\n", self->serial_port, ret);
                return ERR_RFILE_FAIL;
            }
            bytes_read += ret;
        }
        size = bytes_read;
    }

    return size;
}

Устройство, с которым я общаюсь, отправляет кадр из 11 байтов каждую секунду после загрузки.

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

se_serial_t* serial = NULL;
uint8_t buffer[1024] = {0};
int ret = 0;
int i;

if(0 > (ret = se_serial_constructor(&serial, "/dev/ttyUSB0")))
{
    ERROR("Error creating serial : %d", ret);
    return ERR_SERIAL_CREATION_FAIL;
}
else if(0 > (ret = se_serial_configure_interface(serial, SE_SERIAL_SPEED_B115200, SE_SERIAL_PARITY_8N1, false)))
{
    ERROR("Error configuring serial interface : %d", ret);
    return ERR_SERIAL_CONFIG_FAIL;
}

while(1)
{
    if(0 > (ret = se_serial_read(serial, buffer, 11)))
    {
        ERROR("Error reading from serial : %d", ret);
        return ret;
    }
    else
    {
        for(i=0;i<ret;i++)
        {
            printf("%02x ", buffer[i]);
        }
        printf("\n");
    }
}

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

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

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

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

Кто-нибудь знает, почему я так поступаю?

1 Ответ

0 голосов
/ 03 сентября 2018

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

Совсем не странно.
Это четкое указание на то, что инициализация вашей программой последовательного терминала является неполной и зависит от уже существующей инициализации, которая подходит.
(КСТАТИ "странно" - это основанное на мнении описание, которое не содержит никакой технической информации для отладки.)

Режимом последовательного терминала по умолчанию обычно является канонический режим (для передачи текста) после загрузки системы.
Поэтому (непреднамеренное) каноническое read () последовательного терминала будет блокироваться до тех пор, пока не встретится символ завершения строки (например, символ новой строки или 0x0A).
Ваша программа будет блокироваться навсегда, если источник никогда не отправит какой-либо символ завершения строки.
Вы можете подтвердить это с помощью команды stty -F /dev/ttyUSB0 -a и обнаружить, что атрибут icanon не имеет дефиса перед ним.

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

Если вашей программе требуется неканонический режим, она должна явно настроить этот режим, а не полагаться на ранее существовавшую конфигурацию.
Так как для неканонического режима следует также установить или очистить множество других связанных атрибутов, макрос cfmakeraw () является самым простым редактором вашего кода.
Вставить

cfmakeraw(&options);

между вашей скоростью передачи и конфигурацией "четности".
Обратите внимание, что использование 7-битного фрейма данных может привести к повреждению, если данные не являются исключительно текстом ASCII, поэтому поддержка этих трех режимов в вашей программе неуместна.

Другое существенное упущение - включение приемника и настройка локального режима:

options.c_cflag |= (CLOCAL | CREAD);

Устройство, с которым я общаюсь, отправляет кадр из 11 байтов

Кстати, использование "frame" в контексте последовательного терминала неуместно. В асинхронной последовательной связи каждый символ или байт обрамлен. Ваши ссылки на «фрейм» будут более уместно называть сообщением, пакетом или дейтаграммой.

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