串口操作上的C读取调用阻塞

Posted

技术标签:

【中文标题】串口操作上的C读取调用阻塞【英文标题】:C read call blocking on serial port operation 【发布时间】:2011-11-15 19:28:28 【问题描述】:

我正在尝试在 Linux 中编写一个 C 程序,以通过串行端口从微控制器发送和接收数据。作为测试,我已将微控制器配置为立即回显所有发送的字符。我已经验证这在 minicom 中有效,也可以通过使用“cat”和“echo”来发送和接收数据。

但是,当我尝试在 C 程序中执行相同操作时,我的读取调用会永远阻塞。我将串行端口设置为非规范模式,最小值为“1”,时间为“0”。我的 minicom 测试证明微控制器在输入字符时会返回字符,因此我希望 read 在 write 调用发送字符后返回。我已经将我的代码与几个在线示例进行了比较,但我没有发现任何我遗漏的东西。我已经尝试了以下代码的几种排列,但没有成功。有人能发现问题吗?

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>

#define UART_SPEED B115200

char buf[512];

void init_serial (int fd)

    struct termios termios;
    int res;

    res = tcgetattr (fd, &termios);
    if (res < 0) 
        fprintf (stderr, "Termios get error: %s\n", strerror (errno));
        exit (-1);
    

    cfsetispeed (&termios, UART_SPEED);
    cfsetospeed (&termios, UART_SPEED);

    termios.c_iflag &= ~(IGNPAR | IXON | IXOFF);
    termios.c_iflag |= IGNPAR;

    termios.c_cflag &= ~(CSIZE | PARENB | CSTOPB | CREAD | CLOCAL);
    termios.c_cflag |= CS8;
    termios.c_cflag |= CREAD;
    termios.c_cflag |= CLOCAL;

    termios.c_lflag &= ~(ICANON | ECHO);
    termios.c_cc[VMIN] = 1;
    termios.c_cc[VTIME] = 0;

    res = tcsetattr (fd, TCSANOW, &termios);
    if (res < 0) 
        fprintf (stderr, "Termios set error: %s\n", strerror (errno));
        exit (-1);
    


int main (int argc, char **argv)

    int fd;
    int res;
    int i;

    if (argc < 2) 
        fprintf (stderr, "Please enter device name\n");
        return -1;
    

    fd = open (argv[1], O_RDWR | O_NOCTTY);
    if (fd < 0) 
        fprintf (stderr, "Cannot open %s: %s\n", argv[1], strerror(errno));
        return -1;
    

    init_serial (fd);

    res = write (fd, "P=20\r\n", 6);
    if (res < 0) 
        fprintf (stderr, "Write error: %s\n", strerror(errno));
        return -1;
    
    tcdrain (fd);

    res = read (fd, buf, 512);
    printf ("%d\n", res);
    if (res < 0) 
        fprintf (stderr, "Read error: %s\n", strerror(errno));
        return -1;
    

    for (i=0; i<res; i++) 
        printf ("%c", buf[i]);
    

    return 0;

【问题讨论】:

值得使用环回串行电缆确认。上述代码对我有用。 由于它似乎对 Andrew 有效,我建议您尝试调整控制线 (CRTSCTS)。另外:也许远端对平价比你更挑剔,也没有回应。如果您仍然拥有一个接线盒:使用它。 【参考方案1】:

您可能想要插入一些延迟,或者循环等待输入。

设置比特率后,某些类型的 UART 硬件以新的速度需要一两个字符来同步到新的速度。前几个字符可能在写入时丢失了。

在六个字符写入后,立即发出读取,超时时间为 0.1 秒。 write() 中的所有字符可能并非在read() 之前完成传输,更不用说远程设备响应的任何时间了。

例如,一种解决方案是:

init_serial (fd);
usleep (100000);   // delay 0.1 seconds (Linux) so term parameters have time to change

res = write (fd, "P=20\r\n", 6);
if (res < 0) 
    fprintf (stderr, "Write error: %s\n", strerror(errno));
    return -1;

tcdrain (fd);
usleep (250000);  // delay 0.25 for device to respond and return data

res = read (fd, buf, 512);

另一种方法是继续阅读,直到有足够数量的字符到达或经过合理的时间。

【讨论】:

感谢您的反馈。但是,在 TIME=0 和 MIN=1 的情况下,我的读取调用被阻止等待任何反馈。它没有返回 0(根本没有返回),所以循环不会有帮助。不过,让连续剧稳定的更多角色似乎是个好主意。我会尝试增加写入大小,看看是否有帮助。 你是对的,是延误!我延迟了 0.5 秒,但没有用,但每次 2 秒似乎都有效。我将实施一个更智能的测试来等待连接准备好,但是这个简单的测试已经证明了这个问题。我真的很惊讶 Linux 以这种方式工作(需要您手动等待,同时丢弃 IO),但我很高兴解决了它。再次感谢! @FazJaxton:例如,通过让 Linux 表现出出色的响应能力(这正是原因所在),您可以将其用作模拟 Windows 行为的基本基础。

以上是关于串口操作上的C读取调用阻塞的主要内容,如果未能解决你的问题,请参考以下文章

VC串口通信问题

串口传输用不用使用多线程 为啥

Posix PIPE 上的非阻塞读取和阻塞写入

如果没有用minicom打开一次,则无法从串口读取

STM32CubeMX-硬件IIC读取AT24C02(阻塞中断DMA三种方式)

串口异步IO