我编写了一个与串口通信的程序,使用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
的/ proc / TTY /驱动器/ 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 /驱动器/串行
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 /驱动器/ 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;
}
答案 0 :(得分:2)
串口不会突然“死”
突然“死”或无响应的串行链路的典型原因是不需要的流量控制。您似乎已禁用硬件流控制,但尚未禁用软件流控制,并且未正确配置原始模式。
您的初始化需要
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;
<强>附录强>
您修改后的termios设置看起来没问题。
我将尝试的下一步是确定串行链路的哪一侧出错。一端不接收或一端不传输?
您可以尝试使用串行端口驱动程序维护的指标。如果双方都在运行Linux,那么您应该检查 / proc / tty / drivers 中的文件。串行端口潜水员将报告每个端口上的接收和发送字节计数。比较测试前和测试失败后的Rx和Tx计数。
如果您无法从CC2530端获取任何统计数据,则可能需要串行链路监视器。除了专用的测试仪器,您还可以使用带有两个串行端口的PC进行设置。将端口A连接到主机,将端口B连接到CC2530,这样这台PC就是“中间人”。然后,您必须编写一个程序,将端口A的接收数据重新传输到端口B,将端口B的RxD重新传输到端口A的TxD。
还必须显示或记录重新传输的数据(两个通道)。目的是确定串行链路的哪一侧出现故障。一旦确定,那么你必须弄清楚它是接收还是传输问题。
OR
您可以发布更多代码(完整的open()
,初始化例程以及读取和写入逻辑),供所有人查看。
您发布的代码存在一些问题。
初始化代码
// 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文本组成,每行由换行符终止?
如果“是”,那么您可以使用规范模式和read_line()
否则,您应该使用非规范模式和read()
系统调用,并编写代码来解析收到的数据。
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()
。您需要阅读用于了解可返回内容的每个系统调用的手册页。)