Как изменить скорость последовательного порта во время выполнения с помощью C?

Я написал код C для SoC на базе aarch64 (Rockchip RK3399) с Debian 9 LXDE, чтобы получать данные от модуля GPS. Модуль GPS подключен к порту ttyS4 в моей SoC. Я создал поток pthread для получения данных от модуля GPS. Я использую библиотеку termios. Итак, мой поток выглядит следующим образом:

  • Инициализируйте порт UART (скорость передачи, четность, стоповые биты и т. Д.).
  • Создайте поток для получения данных из модуля.

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

Как мне установить новую скорость передачи для порта? Должен ли я pthread_exit() получить поток, инициализировать порт UART, а затем снова запустить поток?

Или мне просто закрыть fd и инициализировать порт UART с новой скоростью передачи, не выходя из потока?

Или есть какой-либо другой простой способ или функция для установки UART на порт?

Мой код инициализации:

int Gpsfd;
struct termios Gps_termios, Gps_old;
void GpsPortInit(void)
{
    char path[12] = "/dev/ttyS4";

    //open GSM_termios for tx/rx
    Gpsfd = open(path, O_RDWR | O_NOCTTY);
    if (Gpsfd < 0)
        printf("port failed to open\n");

    //save current attributes
    tcgetattr(Gpsfd, &Gps_old);
    bzero(&Gps_termios, sizeof(Gps_termios));

    Gps_termios.c_cflag = CLOCAL | CREAD | CS8;

    if (!strcmp(g_sParameters.RS232BaudRate, "9600"))
    {
        Gps_termios.c_cflag |= B9600;
    }
    else if (!strcmp(g_sParameters.RS232BaudRate, "19200"))
    {
        Gps_termios.c_cflag |= B19200;
    }
    else if (!strcmp(g_sParameters.RS232BaudRate, "57600"))
    {
        Gps_termios.c_cflag |= B57600;
    }
    else if (!strcmp(g_sParameters.RS232BaudRate, "115200"))
    {
        Gps_termios.c_cflag |= B115200;
    }

    Gps_termios.c_iflag = IGNPAR;
    Gps_termios.c_oflag = 0;
    Gps_termios.c_lflag = 0;

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

    //clean the line and set the attributes
    tcflush(Gpsfd, TCIFLUSH);
    tcsetattr(Gpsfd, TCSANOW, &Gps_termios);
}

Это функция изменения скорости передачи, предложенная ниже:

int set_baudrate(speed_t speed)
{
    //struct termios tty;

    if (tcgetattr(Gpsfd, &Gps_termios) < 0) {
        printf("Error from tcgetattr1: %s\n", strerror(errno));
        return -1;
    }
    cfsetospeed(&Gps_termios, speed);
    cfsetispeed(&Gps_termios, speed);
    if (tcsetattr(Gpsfd, TCSANOW, &Gps_termios) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    tcflush(Gpsfd, TCIOFLUSH);  /* discard buffers */

    return 0;
}

Я инициализирую UART один раз с помощью GpsPortInit, и после этого, если я получаю запрос на изменение скорости передачи, я меняю его с помощью set_baudrate.


person Vibhu    schedule 11.03.2021    source источник
comment
Сомневаюсь, что у вашего процессора есть последовательный порт. Однако микроконтроллер, скорее всего, будет иметь такой. Но поскольку вы говорите о pthreads, может показаться, что вы предпочитаете какой-нибудь Linux-ПК. Если бы вы сказали нам, какую систему вы используете, мне бы не пришлось гадать.   -  person Lundin    schedule 11.03.2021
comment
Это процессор на базе aarch64 (rockchip rk3399) с debian 9 LXDE   -  person Vibhu    schedule 11.03.2021
comment
Обычно вам необходимо закрыть все коммуникации, если вам нужно изменить скорость передачи. Вы используете termios lib или что-то еще?   -  person Lundin    schedule 11.03.2021
comment
Под всем сообщением вы имеете в виду файловый дескриптор? Да, я использую termios lib.   -  person Vibhu    schedule 11.03.2021
comment
Прошло много времени с тех пор, как я использовал эту библиотеку, но теперь, после уточнений, кто-то другой сможет дать ответ. Я взял на себя смелость добавить детали к вопросу с правкой.   -  person Lundin    schedule 11.03.2021
comment
@Lundin, к вашему сведению, в современном мире мы почти потеряли чистые процессоры, в настоящее время все они являются SoC. Поэтому я был бы удивлен, если бы увидел кристалл без серийного номера.   -  person 0andriy    schedule 02.04.2021
comment
@ 0andriy Ты хочешь сказать, что x86_64 имеет встроенный UART?   -  person Lundin    schedule 06.04.2021
comment
@Lundin, те, которые являются SoC (когда PCH находится в одном пакете). И да, большинству из них это нравится (на стороне клиента, на стороне сервера это зависит, есть чипы, которые включают PCH в том же пакете).   -  person 0andriy    schedule 06.04.2021


Ответы (1)


Как мне установить новую скорость передачи для порта?

В Linux пользовательское пространство не имеет прямого доступа к оборудованию, такому как UART, поэтому ваша программа ограничена использованием последовательного терминала и API-интерфейса termios.
Это подтверждается использованием вами / dev / ttyS4 < / strong> (а не узел устройства с именем / dev / uart4).

Должен ли я pthread_exit () получать поток, инициализировать порт UART, а затем снова запускать поток?

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

Или мне просто закрыть fd и инициализировать порт UART с новой скоростью передачи, не выходя из потока?

Закрытие файлового дескриптора лишит вашу программу дальнейшего доступа к последовательному терминалу, так что это не имеет смысла.
Пользовательское пространство не имеет прямого доступа к оборудованию, такому как UART, поэтому ваша программа ограничена использованием API termios.

Или есть какой-либо другой простой способ или функция для установки UART на порт?

Используйте API-интерфейс termios для изменения скорости последовательного терминала, например:

int set_baudrate(int fd, speed_t speed)
{
    struct termios tty;
    int rc1, rc2;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }
    rc1 = cfsetospeed(&tty, speed);
    rc2 = cfsetispeed(&tty, speed);
    if ((rc1 | rc2) != 0 ) {
        printf("Error from cfsetxspeed: %s\n", strerror(errno));
        return -1;
    }
    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    tcflush(fd, TCIOFLUSH);  /* discard buffers */

    return 0;
}

Вышеупомянутое должно быть похоже на инициализацию, которую вы использовали для инициализации порта UART (скорость передачи, четность, стоповые биты и т. Д.) ... с использованием библиотеки termios.
То есть приведенный выше код соответствует правильной настройке режимов терминала .


Обратите внимание, что вызов tcsetattr () может использовать аргумент TCSAFLUSH вместо TCSANOW. Это изменит атрибуты, когда вывод истощится; также очистить ожидающий ввод в соответствии с termios.h man page.
Однако ожидание выхода вывода кажется излишним, поскольку изменение скорости передачи подразумевает, что текущая скорость передачи несовместима с другим концом последовательного канала. Мусор IOW предположительно передается, а мусор предположительно принимается.
Поэтому немедленно измените скорость передачи (т. Е. Используйте TCSANOW), а затем удалите содержимое приемного буфера.


Приложение: проверьте код инициализации

Ваш код инициализации имеет низкое качество, не переносится и не может надежно настроить последовательный терминал.
Начало с обнуленной структурой termios (вместо существующих значений) противоречит рекомендуемой практике, описанной в Правильная настройка режимов терминала и Руководство по последовательному программированию для операционных систем POSIX.

См. этот ответ для краткого кода для правильной инициализации последовательного терминала для блокировки неканонического режима.

Это функция изменения скорости передачи, предложенная ниже:

Обратите внимание, что в моем ответе процедура была обновлена ​​для улучшения отчетов об ошибках.
Вы не показываете, как именно вы используете / вызываете эту процедуру.

Я инициализирую UART один раз с помощью GpsPortInit, а после этого, если я получаю запрос на изменение скорости передачи, я меняю его с помощью set_baudrate.

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

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

person sawdust    schedule 11.03.2021
comment
Я попробовал изменить скорость передачи данных, используя указанную вами функцию, не убивая поток и не закрывая файловый дескриптор, но она не работает. - person Vibhu; 12.03.2021
comment
... но это не работает - Мне жаль слышать это, но вам придется выяснить, что вы делаете не так. Я протестировал эту процедуру, и она работает, как ожидалось. Откуда вы знаете, что ваш код инициализации порта UART правильный? Разместите свой код. Что вы пробовали? Разместите свой код. Опишите ваши процедуры тестирования. Какие скорости передачи вы используете? Как устроен другой конец? Добавьте эти данные, отредактировав исходный пост выше, а не в комментариях. - person sawdust; 12.03.2021
comment
@Vibhu - см. Обновленный ответ. - person sawdust; 13.03.2021