我需要在Linux上的串口或套接字上等待n个字节的数据(已知计数)。 目前我使用带轮询的循环,测量时间并减少超时:
static int int_read_poll(int fd, uint8_t *buffer, size_t count, int timeout)
{
struct pollfd pfd;
int rc;
pfd.fd = fd;
pfd.events = POLLIN;
rc = poll(&pfd, 1, timeout);
if (rc < 0) {
perror("poll");
return 0;
}
if (rc > 0) {
if (pfd.revents & POLLIN) {
rc = read(fd, buffer, count);
return rc;
}
}
return 0;
}
static int int_read_waitfor(int fd, uint8_t *buffer, size_t count, int timeout)
{
int rc;
struct timespec start, end;
int delta_ms;
int recv = 0;
do {
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
rc = int_read_poll(fd, buffer + recv, count - recv, timeout);
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
delta_ms = (end.tv_nsec - start.tv_nsec) / 1000000;
if (!rc || (rc < 0)) return 0;
recv += rc;
timeout -= delta_ms;
if (timeout <= 0)
return 0;
} while (recv != count);
return recv;
}
在串行端口上,poll会在每个字节上返回并导致多次迭代。
有没有更优雅的方法来解决这个问题?
我知道根据波特率,超时可能不会在该代码部分中减少。计算纳秒可能是更好的方法。
答案 0 :(得分:0)
答案 1 :(得分:0)
感谢所有人提供的宝贵提示!
经过一些测试后,我终于决定不使用信号,因为一旦我将函数移植到库中或将它们作为源发布,它们可能会干扰应用程序。
我最终找到了一个使用poll和termios(只有四个系统调用)的简洁解决方案:
static int int_read_waitfor(int fd, uint8_t *buffer, size_t count, int timeout)
{
struct termios tm;
struct pollfd pfd;
int rc;
tcgetattr(fd, &tm);
tm.c_cc[VTIME] = 1; // inter-character timeout 100ms, valid after first char recvd
tm.c_cc[VMIN] = count; // block until n characters are read
tcsetattr(fd, TCSANOW, &tm);
pfd.fd = fd;
pfd.events = POLLIN;
rc = poll(&pfd, 1, timeout);
if (rc > 0) {
rc = read(fd, buffer, count);
if (rc == -1) {
perror("read");
return 0;
}
return rc;
}
return 0;
}
与通常基于数据包的网络套接字不同,串行端口(n.b:非规范模式)是基于字符的。期望对每个到达的字符迭代具有轮询的循环,特别是在低波特率下。
我的应用程序我通过串行线路将命令发送到设备并等待答案。 如果没有收到回答,将发生超时,我们可能会重试。
termios选项&#34; VMIN&#34;很方便,因为我可以指定我喜欢的人物数量。通常读取将阻塞,直到n个字符到达。
如果没有答案,该命令将永远阻止。
termios选项&#34; VTIME&#34;与VMIN结合&gt; 0表示以十分之一为单位的字符间超时(1 = 100ms)。这很方便,但只有在收到第一个字符后才会开始超时。否则,字符间超时就没有意义了。
因此,如果我只使用termios选项,则读取将阻止从属串行设备死机。
为了避免这个问题,我在阅读前使用民意调查。 一旦第一个字符到达(轮询以rc = 1返回),我开始阅读。 &#34; VTIME&#34;也是有效的,并将强制执行100ms的间距时间(可能的最低设置)。
作为奖励,优化了超时处理:
假设超时为400毫秒