C read 调用阻塞串口操作

发布于 2024-12-04 06:38:20 字数 2199 浏览 2 评论 0原文

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

然而,当我尝试在 C 程序中执行相同操作时,我的读取调用将永远阻塞。我将串行端口设置为非规范模式,MIN 为“1”,TIME 为“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;
}

I am trying to write a C program in Linux to send and receive data from a microcontroller over the serial port. As a test, I have configured the microcontroller to immediately echo all characters sent. I have verified that this works in minicom and also by using "cat" and "echo" to send and receive data.

However, when I try to do the same in a C program, my read call blocks forever. I am setting the serial port to non-canonical mode, with a MIN of '1' and TIME of '0'. My minicom test proves that the microcontroller is returning characters as they are typed, so I expect read to return after the write call has sent characters. I have compared my code to several online examples, and I haven't found anything that I am missing. I have tried several permutations of the code below with no luck. Can someone spot the problem?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

孤独难免 2024-12-11 06:38:20

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

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

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

例如,一种解决方案是:

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

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

You might want to insert some delays, or loop waiting for input.

After setting the bit rate, some types of UART hardware takes one or two characters at the new speed to synchronize to the new speed. It is possible the first few characters are being lost on the write.

After the six character write, the read is issued immediately with a 0.1 second timeout. It is possible that not all the characters from the write() have finished being transmitted before the read(), let alone any time for the remote device to respond.

For example, one solution is:

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

Another approach would be to continue reading until a sufficient number of characters arrive or a reasonable amount of time passes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文