我编写了一个使用termios与串行端口通信的程序,该程序将以非阻塞模式读取串行端口,并在读取数据后将响应写入串行端口。如果没有从串行端口读取数据,程序将执行其他操作,在下一个循环中,程序将再次读取串行端口。
现在的问题是,有时消失了,也许是几分钟,甚至是几个小时,串行端口不再响应我的程序。即使我执行echo 'HB\n' > /dev/ttyUSB0
(然后串行端口应响应“HACK”),它也不再响应。
我什至不知道串行端口何时“死”,我没有任何线索..它“死”没有计时。
这是我的配置:
/// set local mode options
//tminfo.c_lflag |= ICANON;
tminfo.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/// set control mode options
tminfo.c_cflag |= (CLOCAL | CREAD);
tminfo.c_cflag |= HUPCL;
// set hardware flow control
tminfo.c_cflag &= ~CRTSCTS;
// set how many bits in a character
tminfo.c_cflag &= ~CSIZE;
tminfo.c_cflag |= CS8;
// set parity mode (default to odd validation), this option (PARENB) will both enable input and output parity checking
tminfo.c_cflag &= ~PARENB; // we don't need prity checking now
/// set input mode options
// set input parity checking
tminfo.c_iflag &= ~INPCK;
tminfo.c_cflag &= ~CSTOPB;
/// set output mode options
tminfo.c_oflag &= ~OPOST;
tminfo.c_cc[VMIN] = 1;
tminfo.c_cc[VTIME] = 1;
/// set line speed, defaults to 38400bps, both for input and output
// this call will set both input and output speed
cfsetspeed(&tminfo, B38400);
在这种情况下很难调试串口。我真的不知道是什么原因导致串行端口“死机”。我快疯了...
可能是什么原因?任何帮助将不胜感激!
更新:
当串口“死”时,其配置为:
speed 38400 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>;
start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 1;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel
-iutf8
-opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
更新2
/proc/tty/driver/ar933x-uart
我注意到,即使我手动写入序列号,此
tx
和rx
字段值在程序运行期间也不会更改。serinfo:1.0 driver revision:
0: uart:AR933X UART mmio:0x18020000 irq:11 tx:169 rx:0 RTS|DTR|CD
/proc/tty/driver/serial
serinfo:1.0 driver revision:
0: uart:unknown port:00000000 irq:0
1: uart:unknown port:00000000 irq:0
2: uart:unknown port:00000000 irq:0
3: uart:unknown port:00000000 irq:0
4: uart:unknown port:00000000 irq:0
5: uart:unknown port:00000000 irq:0
6: uart:unknown port:00000000 irq:0
7: uart:unknown port:00000000 irq:0
8: uart:unknown port:00000000 irq:0
9: uart:unknown port:00000000 irq:0
10: uart:unknown port:00000000 irq:0
11: uart:unknown port:00000000 irq:0
12: uart:unknown port:00000000 irq:0
13: uart:unknown port:00000000 irq:0
14: uart:unknown port:00000000 irq:0
15: uart:unknown port:00000000 irq:0
/proc/tty/driver/usbserial
usbserinfo:1.0 driver:2.0
0: module:pl2303 name:"pl2303" vendor:067b product:2303 num_ports:1 port:1 path:usb-ehci-platform-1
,下面是更详细的代码...
int Serial::openup(const char *devfile) {
if(-1 == (devfds = open(devfile, O_RDWR | O_NOCTTY ))) {
perror(strerror(errno));
return -1;
}
// set device file io mode to nonblock
//int oldflags = fcntl(devfds, F_GETFL);
//fcntl(devfds, F_SETFL, oldflags | O_NONBLOCK);
// get terminal's attributes
tcgetattr(devfds, &tminfo);
memset(&tminfo, 0, sizeof(struct termios));
/// set local mode options ///
//tminfo.c_lflag |= ICANON;
tminfo.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | IEXTEN);
/// set control mode options ///
tminfo.c_cflag |= (CLOCAL | CREAD);
// disable hardware flow control
tminfo.c_cflag &= ~CRTSCTS;
// set how many bits in a character
tminfo.c_cflag &= ~CSIZE;
tminfo.c_cflag |= CS8;
// we don't need prity checking
tminfo.c_cflag &= ~PARENB;
tminfo.c_cflag &= ~CSTOPB;
/// set input mode options ///
// disable input parity checking, this
tminfo.c_iflag &= ~(INPCK | PARMRK | IGNBRK | BRKINT | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
/// set output mode options ///
//tminfo.c_oflag |= (OPOST | ONLCR);
tminfo.c_oflag &= ~OPOST; // ***
tminfo.c_cc[VMIN] = 0; // ***
tminfo.c_cc[VTIME] = 1; // ***
/// set line speed, defaults to 38400bps, both for input and output ///
// this call will set both input and output speed
cfsetspeed(&tminfo, B38400);
if(-1 == tcsetattr(devfds, TCSANOW, &tminfo)) {
perror(strerror(errno));
return -1;
}
return 0;
}
int Serial::serve() {
char buffer[256] = {0};
/*
struct timeval timeo;
timeo.tv_sec = 0;
timeo.tv_usec = 2 * 1000;
select(0, NULL, NULL, NULL, &timeo);
*/
//print_trace("ready to read data from serial port.\n");
int read_count = 0;
if((read_count = read_line(devfds, buffer, 256))) {
print_trace("read line: %d bytes, %s\n", read_count, buffer);
if(0 == strncmp(buffer, "S", 1)) {
// do some operation
} else if(0 == strncmp(buffer, "N", 1)) {
// do some operation
}
} else {
//print_trace("read no data.\n");
}
// TODO: test only, for find out the reason of serial port 'dead' problem
tcflush(devfds, TCIFLUSH);
}
还有其他模块可以写入串口的功能
int Serial::write_to_zigbee_co(const char *msg) {
int write_count = 0;
int len = strlen(msg);
struct timeval timeo;
timeo.tv_sec = 0;
timeo.tv_usec = 20 * 1000;
select(0, NULL, NULL, NULL, &timeo);
tcflush(devfds, TCOFLUSH);
if(len == (write_count = write(devfds, msg, len))) {
} else {
tcflush(devfds, TCOFLUSH);
}
return write_count;
}
最佳答案
串行端口不只是突然“死”。
突然的“死”或无响应的串行链接的典型原因是不需要的流量控制。您似乎已禁用了硬件流控制,但是尚未禁用软件流控制,并且尚未正确配置原始模式。
您的初始化需求
tminfo.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF);
同时清除
IEXTEN
中的c_lflag
,并清除
PARENB
中的c_cflag
。考虑使用
cfmakeraw()
函数来简化原始模式的初始化。Raw mode
cfmakeraw() sets the terminal to something like the "raw" mode of the old Version 7
terminal driver: input is available character by character, echoing is disabled, and
all special processing of terminal input and output characters is disabled. The
terminal attributes are set as follows:
termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios_p->c_cflag &= ~(CSIZE | PARENB);
termios_p->c_cflag |= CS8;
ADDENDUM
修改后的termios设置看起来还可以。
我要尝试的下一步是确定串行链路的哪一侧出故障。一端不接收还是一端不发送?
您可以尝试使用串行端口驱动程序维护的指标。如果双方都在运行Linux,则应检查/proc/tty/drivers 中的文件。串行端口分流器将报告每个端口上的接收和发送字节计数。在测试之前和失败之后比较Rx和Tx计数。
如果您无法从CC2530端获取任何统计信息,则可能需要一个串行链接监视器。除了专用的测试仪器外,您还可以使用带有两个串行端口的PC来进行设置。将端口A连接到主机,将端口B连接到CC2530,以使此PC成为“中间人”。然后,您将必须编写程序以将端口A的接收数据重新传输到端口B,并将端口B的RxD重新传输到端口A的TxD。
重新传输的数据(两个通道)也必须显示或记录。目的是确定串行链路的哪一侧出故障。一旦确定,则必须确定是接收还是发送问题。
要么
您可以发布更多代码(完整的
open()
,初始化例程以及读写逻辑),以供所有人检查。附录2
您发布的代码存在一些问题。
初始化代码
// get terminal's attributes
tcgetattr(devfds, &tminfo);
memset(&tminfo, 0, sizeof(struct termios));
此代码获取termios数据,然后将其清零!
您需要删除
memset()
语句。阅读代码
if((read_count = read_line(devfds, buffer, 256))) {
串行端口已初始化为非规范(也称为原始)模式,但是这里是
read_line()
,它是规范的输入操作。我不知道当您设置原始模式并尝试读取行时会发生什么,但是如果读取操作挂起,我不会感到惊讶。
您需要评估通过此串行链接在这两个设备之间交换的数据类型。
每个消息是否都由ASCII文本组成,并且每行以换行符结尾?
如果为"is",则可以使用规范模式和
read_line()
。否则,您应该使用非规范模式和
read()
syscall,并编写代码以解析接收到的数据。if((read_count = read_line(devfds, buffer, 256))) {
...
} else {
//print_trace("read no data.\n");
}
当
read_line()
返回错误(-1)时,此代码会将其视为良好的返回,并尝试处理接收缓冲区中的陈旧数据或垃圾数据。如果有任何读取错误,则不会检测到它们,也不会报告。tcflush(devfds, TCIFLUSH);
IMO,您正在滥用
tcflush()
。可能有一些罕见的情况,但是通常您不应该丢弃任何数据,除非您实际对其进行了分析并且知道它是垃圾数据。您应该删除此tcflush()
语句。编写代码
select(0, NULL, NULL, NULL, &timeo);
在写之前执行时间延迟是用户空间中的一个可疑操作。在具有调度和抢占功能来中断实际执行时间的多任务环境中,用户空间程序很少需要向每个
write()
系统调用添加这样的固定延迟。tcflush(devfds, TCOFLUSH);
tcflush()
的另一种可疑(错误)用法。这应该被删除。
if(len == (write_count = write(devfds, msg, len))) {
} else {
tcflush(devfds, TCOFLUSH);
}
tcflush()
的另一种可疑/不当使用。应该用更好的恢复代码代替。短写的可能性不大。最有可能返回的是完整写计数或错误返回(-1)。您需要检查错误返回(-1)和
errno
变量。 (您还需要对其他系统调用(例如tcgetattr()
)执行此操作。您需要阅读所使用的每个系统调用的手册页,以了解可以返回什么。)
关于linux - 串口无响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20654755/