我试图从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-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的教程,使用BeagleBone的python,我已经让串口正常工作(在python中)。在这一点上,我确定我&#39; 做错了什么;问题是什么。我更喜欢使用C ++而不是python,因此能够很好地实现它。
答案 0 :(得分:1)
您的程序以非阻塞模式打开串行终端。
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错误,与非阻止读取()一致。
手册页描述了当&#34;文件描述符...被标记为非阻塞(O_NONBLOCK)时,将发生此错误,并且读取将阻止&#34;。
读取()系统调用&#34;会阻止&#34;因为没有数据来满足读取请求。
如果您坚持使用非阻塞模式,那么您的程序必须能够应对这种情况,这不是错误,而是临时/暂时状态。
但是,对于多任务系统中的典型程序,阻塞模式是更简单和优选的操作模式
您的程序应该如前所述进行修改。
串行终端的初始化存在许多问题。
tcgetattr(tty4, &oldtio); // save current serial port settings
永远不会检查 tcgetattr()和 tcsetattr()系统调用的返回值是否有错误。
bzero(&newtio, sizeof(newtio)); // clear struct for new settings
从空的termios结构开始几乎总是一个坏主意。它似乎适用于某些系统,但它不是可移植的代码
初始化termios结构的正确方法是使用 tcgetattr()中的值。
见Setting Terminal Modes Properly。
由于它已被调用,因此您需要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属性。
您正在使用其菜单将termios属性恢复为 minicom 的要求。