【问题标题】:How to change the baudrate of a serial port at runtime using C?如何使用 C 在运行时更改串行端口的波特率?
【发布时间】:2021-06-09 04:32:31
【问题描述】:

我使用 Debian 9 LXDE 为基于 aarch64 的 SoC (Rockchip RK3399) 编写了一些 C 代码,用于从 GPS 模块接收数据。 GPS 模块连接到我的 SoC 中的 "ttyS4" 端口。我创建了一个 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;
}

我使用 GpsPortInit 初始化 UART 一次,然后如果我收到任何更改波特率的请求,我会使用 set_baudrate 进行更改。

【问题讨论】:

  • 我怀疑你的处理器有串口。然而,微控制器很可能有一个。但是,既然您谈到 pthread,您似乎更愿意拥有某种形式的 Linux PC。如果你告诉我们你使用的是什么系统,我就不用猜了。
  • 这是一个基于 aarch64 的处理器(rockchip rk3399)与 debian 9 LXDE
  • 如果您需要更改波特率,通常您必须关闭所有通信。是您正在使用的 termios 库还是其他什么?
  • 所有通信都是指文件描述符?是的,我正在使用 termios lib。
  • 自从我使用该库以来一直存在,但现在有了澄清,其他人应该能够给出答案。我冒昧地通过编辑将详细信息添加到问题中。

标签: c linux pthreads uart baud-rate


【解决方案1】:

如何设置端口的新波特率?

在 Linux 中,用户空间无法直接访问 UART 等硬件,因此您的程序只能使用串行终端和 termios API。
您使用 /dev/ttyS4(而不是名为 /dev/uart4 的设备节点)证实了这一点。

我应该 pthread_exit() 接收线程,初始化 UART 端口,然后重新启动线程吗?

没关系。
pthread 只是从 termios 缓冲区“读取”,而不是直接访问任何硬件。
但是,您的程序需要健壮并且能够处理可能出现的错误消息。

或者我应该关闭 fd 并使用新的波特率初始化 UART 端口而不退出线程?

关闭文件描述符会拒绝您的程序进一步访问串行终端,所以这没有意义。
用户空间无法直接访问 UART 等硬件,因此您的程序只能使用 termios API。

或者有没有其他简单的方法或功能将UART设置为端口?

使用 termios API 改变串口终端的波特率,例如:

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;
}

以上内容应该类似于您用于“使用 termios 库初始化 UART 端口(波特率、奇偶校验、停止位等)...”的初始化。
也就是说,上面的代码遵循Setting Terminal Modes Properly .


请注意,tcsetattr() 调用可以使用参数 TCSAFLUSH 而不是 TCSANOW。根据termios.h man page,这将“在输出耗尽时更改属性;同时刷新待处理的输入”。
然而,等待输出耗尽似乎没有必要,因为更改波特率意味着 当前 波特率与串行链路的另一端不兼容。可能正在传输 IOW 垃圾,并且可能正在接收垃圾。
所以立即更改波特率(即使用 TCSANOW),然后丢弃接收缓冲区的内容。


附录:审查您的初始化代码

您的初始化代码质量低下,不可移植,并且可能无法可靠地设置串行终端。
从清零的 termios 结构(而不​​是现有值)开始与 Setting Terminal Modes Properly 中所述的推荐做法相反 和Serial Programming Guide for POSIX Operating Systems

请参阅this answer 以获得正确的串行终端初始化到阻塞非规范模式的简明代码。

这是下面建议的波特率更改功能:

请注意,我的回答中已更新例程以增强错误报告。
您忽略了准确地展示您如何使用/调用此例程。

我使用 GpsPortInit 初始化 UART 一次,然后如果我收到任何更改波特率的请求,我会使用 set_baudrate 进行更改。

您如何与串行链路的另一端协调任何波特率变化?

您也忽略了描述您的测试程序、您使用的波特率、另一端的设置方式。
这些遗漏中的任何一个都可以揭示为什么这个 set_baudrate() 例程“不起作用” 对您来说。

【讨论】:

  • 我尝试了使用您指定的函数更改波特率的函数,但没有杀死线程或关闭文件描述符,但它不起作用。
  • "... 但它不工作" -- 很抱歉听到这个消息,但你必须弄清楚你做错了什么。我已经测试了例程,它按预期执行。你怎么知道你的“初始化UART端口”代码是正确的?发布您的代码。您"尝试'做什么?发布您的代码。描述您的测试程序。您使用什么波特率?另一端设置如何?通过编辑上面的原始帖子添加这些详细信息,而不是通过 cmets .
猜你喜欢
  • 2015-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-08
  • 2016-01-11
  • 2011-07-31
  • 1970-01-01
相关资源
最近更新 更多