提问人:Ernest3.14 提问时间:1/25/2017 最后编辑:sawdustErnest3.14 更新时间:9/2/2021 访问量:1923
Linux 串行读取块 minicom
Linux serial read blocks minicom
问:
我正在尝试从 BeagleBone Black 上的串行端口 () 读取,但我认为(?)这应该适用于所有 Linux 设备。/dev/ttyS4
目前,我可以设置波特率为 9600 和 8N1 数据以正确从串口读取。但是,如果我尝试直接,我的终端中不会显示任何内容。我的代码也这样做,并返回一个错误,我怀疑这是命令发生的情况。minicom
cat /dev/ttyS4
Resource temporarily unavailable
cat
如果我运行,我会得到以下输出(据我所知,这与我的设置一致):stty -F /dev/ttyS4
minicom
speed 9600 baud; line = 0;
intr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>; start = <undef>; stop = <undef>; susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>; flush = <undef>;
-brkint -imaxbel
-opost -onclr
-isig -iexten -echo -echoe -echok -echoctl -echoke
一个有趣的说明是,当我打开时,如果我启动我的程序,minicom 将停止打印任何内容,即使我停止我的程序也会保持这种状态。我需要再次打开串行设置(,)并关闭它以恢复工作(似乎没有任何更改)。minicom
Ctrl-A
P
minicom
我的代码如下:
int main() {
std::cout << "Starting..." << std::endl;
std::cout << "Connecting..." << std::endl;
int tty4 = open("/dev/ttyS4", O_RDWR | O_NOCTTY | O_NDELAY);
if (tty4 < 0) {
std::cout << "Error opening serial terminal." << std::endl;
}
std::cout << "Configuring..." << std::endl;
struct termios oldtio, newtio;
tcgetattr(tty4, &oldtio); // save current serial port settings
bzero(&newtio, sizeof(newtio)); // clear struct for new settings
newtio.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ICANON;
tcflush(tty4, TCIFLUSH);
tcsetattr(tty4, TCSANOW, &newtio);
std::cout << "Reading..." << std::endl;
while (true) {
uint8_t byte;
int status = read(tty4, &byte, 1);
if (status > 0) {
std::cout << (char)byte;
} else if (status == -1) {
std::cout << "\tERROR: " << strerror(errno) << std::endl;
}
}
tcsetattr(tty4, TCSANOW, &oldtio);
close(tty4);
}
编辑:我已经按照 Adafruit 的教程将 python 与 BeagleBone 一起使用,使串行端口正常工作(在 python 中)。在这一点上,我确信我做错了什么;问题是什么。我更喜欢使用 C++ 而不是 python,所以让它工作会很棒。
答:
程序以非阻塞模式打开串行终端。
int tty4 = open("/dev/ttyS4", O_RDWR | O_NOCTTY | O_NDELAY);
非阻塞 I/O,尤其是读取操作,需要在程序中进行额外的特殊处理。 由于您忽略了提及此模式,并且您的程序无法正确处理此模式,因此这可能被视为一个错误。
从 open() 调用中删除O_NDELAY选项,或插入语句以恢复到阻塞模式。fcntl(tty4, F_SETFL, 0)
我的代码也这样做,并返回一个错误,
Resource temporarily unavailable
这是一个 EAGAIN 错误,与非阻塞 read() 一致。
手册页描述了当“文件描述符...已被标记为非阻塞 (O_NONBLOCK),并且读取将阻塞”。
read() syscall “会阻止”,因为没有数据可以满足读取请求。
如果您坚持使用非阻塞模式,那么您的程序必须能够应对这种情况,这不是错误,而是临时/瞬态状态。
但是阻塞模式是多任务系统中典型程序的更简单和首选的操作模式。
如前所述,应修改您的程序。
串行终端的初始化存在许多问题。
tcgetattr(tty4, &oldtio); // save current serial port settings
从不检查 tcgetattr() 和 tcsetattr() 系统调用的返回值是否存在错误。
bzero(&newtio, sizeof(newtio)); // clear struct for new settings
从空的 termios 结构开始几乎总是一个坏主意。它可能看起来在某些系统上工作,但它不是可移植代码。
初始化 termios 结构的正确方法是使用 tcgetattr() 中的值。
请参阅正确设置终端模式。
由于它已经被调用,您只需要复制结构即可。newtio = oldtio
newtio.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ICANON;
更改这些标志的正确方法是启用或禁用各个属性,而不是分配常量。
对于规范模式,以下内容就足够了:
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8; /* 8-bit characters */
newtio.c_cflag &= ~PARENB; /* no parity bit */
newtio.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
newtio.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
newtio.c_lflag |= ICANON | ISIG; /* canonical input */
newtio.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
newtio.c_iflag &= ~INPCK;
newtio.c_iflag |= ICRNL;
newtio.c_iflag &= ~(INLCR | IGNCR | IUCLC | IMAXBEL);
newtio.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */
newtio.c_oflag &= ~OPOST;
以下是设置波特率的首选方法:
cfsetospeed(&newtio, B9600);
cfsetispeed(&newtio, B9600);
如果未指定任何显著属性,则使用现有设置。
这可能会导致不稳定的程序行为,例如,有时它有效,有时它不起作用。
一个有趣的说明是,当我打开 minicom 时,如果我启动我的程序,minicom 将停止打印任何内容,即使我停止我的程序也会保持这种状态。我需要再次打开串行设置(Ctrl-A、P)并关闭它以使 minicom 恢复工作(似乎没有任何更改)。
串行终端不适用于在多个进程之间共享。
一些 termios 属性必须在串行设备驱动程序中实现,该驱动程序没有共享端口的概念。最新的 termios 属性对设备有效。
当您在 minicom 启动后执行程序时,您正在破坏 minicom 期望执行的 termios 属性。
您正在使用 minicom 的菜单将 termios 属性恢复到 minicom 的要求。
评论