我试图从BeagleBone Black的串行端口( /dev/ttyS4
)读取,但是我认为(?)这个应该适用于所有的Linux设备。
目前,我可以用9600和8N1的波特率设置minicom
从串口读取正确的数据。 但是,如果我试图直接cat /dev/ttyS4
,在terminal上什么也没有显示。 我的代码也这样做,并返回一个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
),然后closures它以继续工作(看起来没有任何改变)。
我的代码如下:
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); }
编辑:我已经得到的串行端口正常工作(在Python中)遵循Adafruit的教程与BeagleBone使用python。 在这一点上,我确信我做错了什么; 问题是什么。 我更喜欢用Python来使用C ++,所以能够很好地工作。
您的程序以非阻塞模式打开串行终端。
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()和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),然后关闭它以继续工作(看起来没有任何改变)。
串行终端不适用于多个进程之间的共享。
一些termios属性必须在串行设备驱动程序中实现,因为它没有共享端口的概念。 最近的termios属性对设备有效。
当你在minicom启动后执行你的程序时,你正在破坏minicom期望执行的termios属性。
您正在使用其菜单将termios属性恢复到minicom的要求。