问题描述
我正在尝试从BeagleBone Black的串行端口(/dev/ttyS4
)读取,但是我认为这通常适用于所有Linux设备.
I'm attempting to read from a serial port (/dev/ttyS4
) on a BeagleBone Black, but I think(?) this should apply to all Linux devices in general.
当前,我可以设置9600的波特率和8N1数据的minicom
以正确地从串行端口读取.但是,如果我尝试直接cat /dev/ttyS4
,则终端中没有任何显示.我的代码也执行此操作,并返回Resource temporarily unavailable
错误,我怀疑这是cat
命令正在发生的情况.
Currently, I can set up minicom
with a baud rate of 9600 and 8N1 data to read from the serial port correctly. However, if I attempt to directly cat /dev/ttyS4
, nothing shows up in my terminal. My code also does this, and returns a Resource temporarily unavailable
error, which I suspect is what is happening with the cat
command.
如果运行stty -F /dev/ttyS4
,则会得到以下输出(据我所知,该输出与我的minicom
设置一致):
If I run stty -F /dev/ttyS4
, I get the following output (which, as far as I can tell, is consistent with my minicom
settings):
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
恢复工作(似乎没有任何更改).
An interesting note is that when I have minicom
open, if I start my program, minicom will stop printing anything, and stay that way even if I stop my program. I need to open the serial settings again (Ctrl-A
, P
) and close it for minicom
to resume working (it appears that nothing was changed).
我的代码如下:
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的BeagleBone使用python的教程,我已经使串行端口正常工作(在python中).在这一点上,我确定我在做错了什么.问题是什么.我更喜欢使用C ++而不是python,因此能够正常工作会很不错.
I've gotten the serial port to work correctly (in python) by following Adafruit's tutorial for using python with the BeagleBone. At this point I'm certain that I'm doing something wrong; the question is what. I would much prefer using C++ over python, so it'd be great to get that working.
推荐答案
您的程序以非阻塞模式打开串行终端.
Your program opens the serial terminal in nonblocking mode.
int tty4 = open("/dev/ttyS4", O_RDWR | O_NOCTTY | O_NDELAY);
非阻塞I/O,特别是读取操作,需要在程序中进行其他特殊处理.由于您忽略了此模式,并且您的程序无法正确处理此模式,因此可以将其视为错误.
Nonblocking I/O, especially read operations, requires additional, special handling in a program.Since you neglect to mention this mode, and your program has no capability to properly process this mode, this could be considered a bug.
从 open()调用中删除 O_NDELAY 选项,或插入fcntl(tty4, F_SETFL, 0)
语句以返回到阻止模式.
Either remove O_NDELAY option from the open() call, or insert a fcntl(tty4, F_SETFL, 0)
statement to revert back to blocking mode.
这是一个EAGAIN错误,与无阻塞的 read()一致.
手册页描述了以下错误,当文件描述符...被标记为非阻塞(O_NONBLOCK),并且读取将阻塞"时,将发生此错误.
read()系统调用将阻止",因为没有数据可以满足读取请求.
That's an EAGAIN error, which is consistent with a nonblocking read().
The man page describes this error will occur when "the file descriptor ... has been marked nonblocking (O_NONBLOCK), and the read would block".
The read() syscall "would block" because there is no data to satisfy the read request.
如果您坚持使用非阻塞模式,则您的程序必须能够应付这种情况,这不是错误,而是临时/暂时状态.
但是对于多任务系统中的典型程序,阻塞模式是更简单,更可取的操作模式.
您的程序应按前面提到的进行修改.
If you insist on using nonblocking mode, then your program must be able to cope with this condition, which is not an error but a temporary/transient status.
But blocking mode is the simpler and preferred mode of operation for typical programs in a multitasking system.
Your program should be modified as previously mentioned.
串行终端的初始化存在许多问题.
There are numerous issues with the initialization of the serial terminal.
tcgetattr(tty4, &oldtio); // save current serial port settings
tcgetattr()和 tcsetattr()系统调用的返回值从不检查错误.
The return values from the tcgetattr() and tcsetattr() syscalls are never checked for errors.
bzero(&newtio, sizeof(newtio)); // clear struct for new settings
从空的termios结构开始几乎总是一个坏主意.它可能在某些系统上似乎可以运行,但是它不是可移植的代码.
初始化termios结构的正确方法是使用 tcgetattr()中的值.
请参阅正确设置终端模式.
由于已经调用了它,因此您只需newtio = oldtio
复制该结构.
Starting with an empty termios structure is almost always a bad idea. It may appear to work on some systems, but it is not portable code.
The proper method for initializing a termios structure is to use values from tcgetattr().
See Setting Terminal Modes Properly.
Since it is already called, all you need is newtio = oldtio
to copy the structure.
newtio.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ICANON;
更改这些标志的正确方法不是启用常量,而是启用或禁用各个属性.
对于规范模式,以下内容就足够了:
Rather than assignment of constants, the proper method of changing these flags is to enable or disable the individual attributes.
The following should suffice for canonical mode:
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;
以下是设置波特率的首选方法:
The following is the preferred method for setting the baudrate:
cfsetospeed(&newtio, B9600);
cfsetispeed(&newtio, B9600);
如果未指定任何显着属性,则使用现有设置.
这可能导致不稳定的程序行为,例如有时它有用,有时却没有.
If any salient attributes are left unspecified, the existing settings are used.
This can lead to erratic program behavior, e.g. sometimes it works, sometimes it doesn't.
串行终端不能在多个进程之间共享.
一些termios属性必须在没有共享端口概念的串行设备驱动程序中实现.最新的termios属性对设备有效.
在 minicom 启动之后执行程序时,您正在破坏 minicom 期望与之一起执行的termios属性.
您正在通过使用其菜单将termios属性恢复为 minicom 的要求.
The serial terminal is not intended for sharing among more than one process.
Some of the termios attributes have to be implemented in the serial device driver, which has no concept of sharing the port. The most recent termios attributes are in effect for the device.
When you execute your program after minicom has started, you are clobbering the termios attributes that minicom expects to execute with.
You are restoring the termios attributes to minicom's requirements by using its menu.
这篇关于Linux串行读取块minicom的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!