Linux串行读取阻止minicom

Linux serial read blocks minicom

本文关键字:minicom 读取 Linux      更新时间:2023-10-16

我正试图从BeagleBone Black上的串行端口(/dev/ttyS4)读取,但我认为(?)这应该适用于所有Linux设备。

目前,我可以设置波特率为9600的minicom和8N1数据,以便正确地从串行端口读取。但是,如果我尝试直接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-AP)并关闭它,以便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()系统调用"将阻止";因为没有数据来满足读取请求。

如果您坚持使用非阻塞模式,那么您的程序必须能够处理这种情况,这不是错误,而是临时/瞬态状态
但是,对于多任务系统中的典型程序,阻塞模式是更简单且首选的操作模式
您的程序应该按照前面提到的进行修改。


串行终端的初始化存在许多问题。


tcgetattr(tty4, &oldtio);   // save current serial port settings

从不检查tcgetattr()tcpetattr()系统调用的返回值是否存在错误。


bzero(&newtio, sizeof(newtio)); // clear struct for new settings

从一个空的白蚁结构开始几乎总是一个坏主意。它可能在某些系统上运行,但它不是可移植的代码
初始化termios结构的正确方法是使用tcgetattr()中的值
请参阅正确设置终端模式
由于它已经被调用,您只需要newtio = oldtio来复制结构。


newtio.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
改这些标志的正确方法不是指定常量,而是启用或禁用各个属性
以下内容应足以满足规范模式:
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属性恢复到其要求。