串口无响应

时间:2013-12-18 09:53:57

标签: 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 /驱动器/ 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 /驱动器/串行

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;
}

1 个答案:

答案 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(),初始化例程以及读取和写入逻辑),供所有人查看。



附录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文本组成,每行由换行符终止?
如果“是”,那么您可以使用规范模式和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()。您需要阅读用于了解可返回内容的每个系统调用的手册页。)