linux - 串口无响应

标签 linux serial-port serial-communication termios

我编写了一个使用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
我注意到,即使我手动写入序列号,此txrx字段值在程序运行期间也不会更改。
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/

相关文章:

unit-testing - 我可以使用模拟对象来模拟串口吗?

c - Modbus RTU 从站未响应 Modbus RTU 主站

communication - 计算 modbus RTU 3.5 字符时间

读/写 NETLINK 套接字会失败吗?

c++ - QT串口发送一个字节

c - 无法在 C 中打印十六进制字节

ftp - 使用什么串行文件传输协议(protocol)?

java - 在 Java 中, "5/0"语句不会在我的 Linux 机器上触发 SIGFPE 信号,为什么?

linux - 使用 AWK 查找排除某些行的列的平均值

c - 如何查找给定文件是符号(软)链接还是硬链接(hard link)