读取串口块由于未知的原因 [英] reading serial port blocks for unknown reason

查看:232
本文介绍了读取串口块由于未知的原因的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Linux下的termios框架,通过UART(usbserial)连接一个无接触式智能卡读卡器。该代码在PC上正常工作,但是当我交叉编译并尝试在ARM9目标上时,它能够打开设备,甚至可以将命令写入设备,但是读取命令无限期地阻止。以下是代码片段:

  int mifare_rdr_init(struct mifare_1K * ptr,char * rdr_devnode)
{
bzero(ptr,sizeof(struct mifare_1K)); //零整个结构
//打开串口设备
int fd = open(rdr_devnode,O_RDWR | O_NOCTTY);
if(fd == -1){
perror(无法打开串行设备);
return 1;
}
ptr-> serialfd = fd; //保存文件描述符

ptr-> serialdev.c_iflag = 0; // no i / p flags
ptr-> serialdev.c_oflag = 0; // o / p flags
ptr-> serialdev.c_cflag =(CS8 | CREAD | B38400); // 8位,接收使能,波特率为rdr
ptr-> serialdev.c_lflag =(ICANON); // CANONICAL mode,表示读取直到换行符'\\\
'。
//控制字符
//在下面的A.H建议下面的行下注释,因为在CANONICAL模式下不需要
// ptr-> serialdev.c_cc [VMIN] = 1; //只读至少一个接收到的字符后读取解除阻塞。

//刷新所有i / o垃圾数据(如果存在)
tcflush(ptr-> serialfd,TCIOFLUSH);

int ret = 0;
//应用设置
ret = tcsetattr(ptr-> serialfd,TCSANOW,& ptr-> serialdev);
if(ret == -1){
perror(tcsetattr()failed);
return 2;
}
return 0;
}

int get_mifare_rdr_version(struct mifare_1K * ptr,char * data)
{
//如果存在,则清空所有i / o垃圾数据
tcflush (ptr-> serialfd,TCIOFLUSH);

int chars_written = write(ptr-> serialfd,$ 1V \\\
,4);
if(chars_written< 0){
perror(无法写入串行设备);
return 1;
}
printf(cmd sent,read version ... \\\
); //这个打印,所以我知道cmd发送...
int chars_read = read(ptr-> serialfd,ptr-> data_buf,14);
if(chars_read< 0){
perror(无法读取串行设备);
return 2;
}
//将数据复制到用户缓冲区
printf(reading done.\\\
); //这不打印...
return 0;
}

mifare_1K结构包含串行设备,termios结构和各种我正在使用的缓冲区我提到的设备是usb-to-serial(module:ftdi_sio)设备。它被配置在38400 @ 8-N-1在规范模式的termios。



规范模式,因为读者的回应结束在'\',所以它更好处理在Canonical模式,因为它读取设备,直到'\'被接收(如果我错了,请纠正我)。



首先我调用init()fn然后get_rdr_version()。字符串cmd发送,读取版本...被打印,所以我知道它能够写,但不打印字符串阅读完成。之后,另一件事情是,如果我删除读卡器并将该端口连接到另一台PC上的gtkterm(串口终端程序),我不会收到 $ 1V \\\
那个gtkterm ?? 。然后经过一点RnD,我发现如果我重新启动阅读器连接读取器的系统,那么只有我在另一个Gtkterm中得到该cmd$ 1V \\\
。如果我再次尝试重新启动,那个cmd没有在Gkterm上看到...一个线索,但是havnt想出来了。



是这样的东西cmd被写入设备文件,但是没有被排入实际的设备?有什么办法可以这样吗?



任何帮助深深感激,因为我已经停留在这个fr了一段时间.... thnks。



更新:



好的,我已经通过修改代码来工作,如下所示。

  //打开串行设备
int fd = open(rdr_devnode,O_RDWR | O_NOCTTY | O_NDELAY); // O_NDELAY忽略DCD线的状态,这之后的所有读/写调用将为非阻塞
fcntl(fd,F_SETFL,0); //恢复读/写阻塞行为
if(fd == -1){
perror(无法打开串口设备);
return 1;
}

这是在我的init中打开端口的代码的修改部分)功能。两个更改:



1)O_NDELAY被添加到open()调用中的标志,它忽略数据载体检测(DCD)行,看看另一端是否连接,准备交流与否。这是原来用于MODEM的,我不需要,其实我根本就没有使用usbserial。但是这个标志还使得read()和write()进一步调用为非阻塞。值得注意的是,我曾经以为这是通过将CLOCAL添加到我已经试过但没有工作的termios结构体的cflag来处理的。



2)fcntl fd,F_SETFL,0)恢复进一步的read()和write()调用的阻止行为。



这个组合对我来说是很好的工作。因为我没有发布这个作为答案的原因是,我还不明白为什么它在PC上没有这个修改,因为它是相同的硬件。事实上,我可以使用 minicom 从智能卡阅读器读取ARM9 TARGET上的数据,但不能使用我的程序。我将检查FT232BL文档,以查看默认情况下是否有DCD的状态。



无论如何,我发现这一点信息在 POSIX操作系统的串行编程指南。解释任何人?当然,我会发现答案,当我找到它。



干杯:)

解决方案

在使用Telegesis USB模块的Raspberry Pi上遇到了相同的症状,我将其作为另一个数据点。



我的案件原因证明是一个丢失的RTS标志。 Telegesis希望CRTSCTS流量控制,并且不会在没有看到RTS的情况下向Raspberry发送任何数据。令人困惑的是,a)同样的代码在PC上工作正常,b)第一次Telegesis被插入时,它在Raspberry上工作得很好,但是在/ dev / ttyUSB0的后续开头,没有数据被看到由于某些原因,似乎在ARM上,RTS标志在设备关闭时被清除,但不会再次设置,而在x86 / x64上,RTS旗标设置。这里的修复只是设置RTS标志(如果还没有) - 例如

  #include< sys / ioctl。 h取代; 
// ...
int rtscts = 0;
if(ioctl(fd,TIOCMGET,& rtscts)!= 0)
{
// handle error
}
else if(!(rtscts& TIOCM_RTS))
{
rtscts | = TIOCM_RTS;
if(ioctl(fd,TIOCMSET,& rtscts)!= 0)
{
//句柄错误
}
}

我注意到,在您的情况下,您不使用流量控制,因此上述可能不太适用。这是你的问题,并提到minicom的工作,导致我们发现我们的问题的解决方案 - 所以谢谢你!


I am trying to interface a contact-less smart card reader over UART (usbserial) using termios framework under Linux. The code works fine on the PC, but when I cross-compile and try it out on an ARM9 target, it is able to open the device and even writing the command to the device, but the read command blocks indefinitely. Here is the code snippet :

int mifare_rdr_init(struct mifare_1K * ptr, char *rdr_devnode)
{   
    bzero(ptr, sizeof(struct mifare_1K));           // zero the entire structure
    // open serial device
    int fd = open(rdr_devnode, O_RDWR|O_NOCTTY );
    if (fd == -1) {
    perror("Failed to open serial device ");
    return 1;
    }
    ptr->serialfd = fd;                 // save file descriptor

    ptr->serialdev.c_iflag = 0;                 // no i/p flags
    ptr->serialdev.c_oflag = 0;                 // o/p flags
    ptr->serialdev.c_cflag = ( CS8 | CREAD | B38400 );      // 8 bits, receive enable, baud for rdr
    ptr->serialdev.c_lflag = ( ICANON );                // CANONICAL mode, means read till newline char '\n'.
    // control chars 
        // commented below line as suggested by A.H below, since it's not needed in CANONICAL mode
    // ptr->serialdev.c_cc[VMIN] = 1;               // read unblocks only after at least one received char.

    // flush all i/o garbage data if present
    tcflush(ptr->serialfd,TCIOFLUSH);

    int ret = 0;
    // apply settings
    ret = tcsetattr(ptr->serialfd,TCSANOW,&ptr->serialdev);
    if (ret == -1) {
        perror("tcsetattr() failed ");
        return 2;
    }
    return 0;
    }

int get_mifare_rdr_version(struct mifare_1K *ptr, char *data)
{
    // flush all i/o garbage data if present
    tcflush(ptr->serialfd,TCIOFLUSH);

    int chars_written = write(ptr->serialfd,"$1V\n",4);
    if( chars_written < 0 ) {
        perror("Failed to write serial device ");
        return 1;
    }
        printf("cmd sent, read version...\n");   // this prints, so I know cmd sent...
    int chars_read = read(ptr->serialfd,ptr->data_buf,14);
    if( chars_read < 0 ) {
        perror("Failed to read serial device ");
        return 2;
    }
    // copy data to user buffer
        printf("reading done.\n");    // this doesn't print...
    return 0;
}

The mifare_1K structure contains the file descriptor for serial device, termios structure, and various buffers I am using. The device as I mentioned is usb-to-serial ( module: ftdi_sio ) device. It's configured at 38400@8-N-1 in Canonical mode of termios.

Canonical mode because the responses from the reader end in '\n', so its better handled in Canonical mode since it reads the device till '\n' is received (pls correct me if I'm wrong).

First I call init() fn and then get_rdr_version(). The string "cmd sent, read version..." is printed so I know its able to write, but does not print string "reading done." after that.

Another thing is that if I remove the card reader and connect that port to gtkterm (serial port terminal program) on another PC, I do not receive the "$1V\n" on that gtkterm ??!! . Then after a little RnD I found out that if I reboot the system on which the reader is connected reader then only I get that cmd "$1V\n" on the other Gtkterm. If I try again without a reboot, that cmd is not seen on that Gkterm...a clue, but havnt figured it out yet.

Is it something like that the cmd is written to the device file, but not drained to the actual device ? Is there any way to chk this ?

Any help is deeply appreciated since I've been stuck on this fr some time now....thnks.

UPDATE :

Ok, I have got it working by modifying the code a little as shown below.

// open serial device
    int fd = open(rdr_devnode, O_RDWR|O_NOCTTY|O_NDELAY );  // O_NDELAY ignores the status of DCD line, all read/write calls after this will be non-blocking
    fcntl(fd,F_SETFL,0);   // restore read/write blocking behavior
    if (fd == -1) {
    perror("Failed to open serial device ");
    return 1;
    }

This is the modified section of the code which open the port in my init() function. Two changes :

1) O_NDELAY is added to the flags in open() call, which ignores Data Carrier Detect (DCD) line to see if the other end is connected and ready for communication or not. This was used originally for MODEMs, which I did not need, in fact, I do not have it at all since I am using usbserial. But this flag also makes further calls to read() and write() as non-blocking. Point to note, I had thought this would be taken care of by adding CLOCAL to cflag of termios struct, which I had tried but did not work.

2) fcntl(fd,F_SETFL,0) restores the blocking behaviour of further read() and write() calls.

This combo is working perfectly for me. The only reason I am not posting this as an answer is that I do not yet understand why it worked on PC without this modification, since it was same hardware. In fact, I was able to read data from the smart card reader ON ARM9 TARGET using minicom , but not my program. I am going to check FT232BL docs to see if what is the status of DCD by default.

Anyway, I found this bit of info on Serial programming Guide for POSIX operating systems . Explanation anyone ??? Of course, I will post the answer when I find it out..

Cheers :)

解决方案

Having just encountered the same symptoms on a Raspberry Pi with a Telegesis USB module, I'm adding this as another data point.

In my case the cause turned out to be a missing RTS flag. The Telegesis expects CRTSCTS flow control, and would not send any data to the Raspberry without seeing the RTS. The perplexing aspects here were that a) the same code works just fine on a PC, and b) it worked fine on the Raspberry the first time the Telegesis was plugged in, but on subsequent openings of /dev/ttyUSB0 no data would be seen by the Raspberry.

For some reason, it seems that on the ARM the RTS flag gets cleared on device close, but not set again, whereas on x86/x64 the RTS flag stays set. The fix here is simply to set the RTS flag if it isn't already - e.g.

#include <sys/ioctl.h>
//...
int rtscts = 0;
if (ioctl (fd, TIOCMGET, &rtscts) != 0)
{
  // handle error
}
else if (!(rtscts & TIOCM_RTS))
{
  rtscts |= TIOCM_RTS;
  if (ioctl (fd, TIOCMSET, &rtscts) != 0)
  {
    // handle error
  }
}

I do note that in your case you don't use flow control, so the above may very well not be applicable. It was your question and the mentioning that minicom worked that led us to discover the solution to our problem however - so thank you for that!

这篇关于读取串口块由于未知的原因的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆