我正在编写一些软件来处理 Beaglebone 系统的串行端口读/写。操作系统是 Debian 9。我正在用 --std=gnu99
用 C 编写代码。
这是我的代码:
// reference
// https://www.cmrr.umn.edu/~strupp/serial.html
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
int open_port(void)
{
int fd;
fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY);
// removing O_NDELAY no difference
if(fd == -1)
{
perror("open_port: Unable to open /dev/ttyS1 - ");
}
else
{
fcntl(fd, F_SETFL, 0);
}
return fd;
}
int main()
{
// open fd for serial port
int fd = open_port();
// set baud rate
struct termios options;
// get current options
tcgetattr(fd, &options);
// set input and output baud
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
// enable reciever and set local mode
// CLOCAL: ignore modem control lines
// CREAD: enable reciever
options.c_cflag |= (CLOCAL | CREAD);
// set partity bit
//options.c_cflag &= PARENB;
options.c_cflag &= ~PARENB;
// use even parity
//options.c_cflag &= ~PARODD;
// use only 1 stop bit
options.c_cflag &= ~CSTOPB;
// set character size to 8 bit
options.c_cflag &= ~CSIZE;
options.c_cflag &= CS8;
// disable flow control
//options.c_cflag &= ~CNEW_RTSCTS; // does not work?
// note: check local options, some may be required
// disable canonical input (use raw)
// disable echoing of characters
// disable echoing of erase characters
// disable signals SIGINTR SIGSUSP SIGDSUSP, SIGQUIT
// input characters are passed through exactly as recieved
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// disable parity check
options.c_iflag &= IGNPAR;
// disable flow control (software)
options.c_iflag &= ~(IXON | IXOFF | IXANY);
// set raw output, no output processing
options.c_oflag &= ~OPOST;
// set options
tcsetattr(fd, TCSANOW, &options);
char data[] = {0x01, 0x03, 0x00, 0x00};
int n = write(fd, data, 4);
if(n =! 4)
{
printf("write fail %d\n", n);
}
char buffer[40];
n = read(fd, buffer, 2);
if(n != 2)
{
printf("read fail %d\n", n);
}
if(buffer[0] == 0x03 && buffer[1] == 0x00)
{
}
else
{
printf("uart error\n");
}
char data2[] = {0x00, 0x00, 0x00, 0x01};
n = write(fd, data2, 4);
if(n != 4)
{
printf("write fail %d\n", n);
}
n = read(fd, buffer, 2);
if(n != 2)
{
printf("read fail %d\n", n);
}
if(buffer[0] == 0x03 && buffer[1] == 0x00)
{
}
else
{
printf("uart error\n");
}
printf("process complete\n");
// to close
close(fd);
}
我遇到的问题是调用 read()
不要阻止。这不是期望的行为。这些调用应该阻塞,直到收到 2 个字节的数据。
我的猜测是我在某处错误配置了一个选项。但是我不知道错误在哪里,根据我的研究,这应该是在阻塞模式下阅读。 (fcntl(fd, F_SETFL, 0);
)
最佳答案
The issue I have is calls to
read()
do not block.
这实际上是一个结论,而不是一个观察。
大概您的程序正在报告零字节读取,这在技术上不是错误。
... from what I have researched, this should be reading in blocking mode. (
fcntl(fd, F_SETFL, 0);
)
正确,您已经为阻塞模式配置了文件描述符。
My guess would be I have misconfigured an option somewhere.
是的,您的 termios 配置不完整。 (它使用正确的位运算,但遗憾的是不完整且有错误,请参阅附录。)
当配置为非规范模式时,您还必须指定 VMIN 和 VTIME 参数。
如果未指定(如在您的代码中),VMIN = 0 和 VTIME = 0 的默认值可能会生效,这相当于非阻塞读。
请注意,read() 系统调用中的字节长度对完成行为的影响很小(除了限制要返回的字节数)。
从 man 的 read(2) 页面:
read() attempts to read up to
count
bytes from file descriptorfd
into the buffer starting atbuf
.
因此 count
参数实际上是一个最大值。
实际读取的字节数可能介于零和 count
之间,具体取决于为 VMIN 和 VTIME 配置的值(以及可用的实际数据)。
参见 Linux Blocking vs. non Blocking Serial Read更多细节。
要在每个系统调用中至少读取两个字节(没有时间限制),请在 termios 配置中包含以下内容:
options.c_cc[VMIN] = 2;
options.c_cc[VTIME] = 0;
在 tcsetattr() 系统调用之前插入到您的程序中的上述两个语句将导致您的程序永远阻塞,直到每个 read(fd, buffer, 2) 中可以返回恰好两个字节)
语句。
read(fd, buffer, 4)
语句也将永远阻塞,并返回 2、3 或 4 字节的数据(取决于与数据接收和缓冲相关的程序时序)。
附录
事实证明,您的 termios 初始化中存在一些错误。
正如发布的那样,您的程序既不能正确传输也不能正确接收任何数据,并且您没有检测到这些错误或忽略了它们。
您程序中的以下语句抹杀了所有先前的c_cflag
操作,并无意中将波特率重新配置为B0 并将字符大小重新配置为CS5 :
options.c_cflag &= CS8;
正确的语法是:
options.c_cflag |= CS8;
声明
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
应该用更多属性扩展以匹配cfmakeraw():
options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
程序中的以下语句禁用 c_iflag
中除 IGNPAR 之外的所有属性,并使 IGNPAR 属性处于未知/模糊状态:
// disable parity check
options.c_iflag &= IGNPAR;
要匹配cfmakeraw(),应该改成:
options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL);
通过上述所有更正,我可以让您的程序按预期执行。
关于linux - 文件描述符处于阻塞模式,但 read() 未阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56926117/