未知原因读取串行端口块 [英] reading serial port blocks for unknown reason

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

问题描述

我正在尝试使用 Linux 下的 termios 框架通过 UART(usbserial)连接非接触式智能卡读卡器.该代码在 PC 上运行良好,但是当我在 ARM9 目标上进行交叉编译并试用时,它能够打开设备甚至将命令写入设备,但读取命令会无限期地阻塞.这是代码片段:

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 '
'.
    // 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
",4);
    if( chars_written < 0 ) {
        perror("Failed to write serial device ");
        return 1;
    }
        printf("cmd sent, read version...
");   // 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.
");    // this doesn't print...
    return 0;
}

mifare_1K 结构包含串行设备的文件描述符、termios 结构和我正在使用的各种缓冲区.我提到的设备是usb-to-serial(模块:ftdi_sio)设备.在termios的Canonical模式下配置在38400@8-N-1.

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 ' ', so its better handled in Canonical mode since it reads the device till ' ' is received (pls correct me if I'm wrong).

我首先调用 init() fn,然后调用 get_rdr_version().字符串cmd sent, read version..."被打印出来,所以我知道它可以写,但不打印字符串reading done".之后.

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.

另一件事是,如果我卸下读卡器并将该端口连接到另一台 PC 上的 gtkterm(串行端口终端程序),我不会在该 gtkterm 上收到$1V "??!!.然后经过一点 RnD,我发现如果我重新启动连接读卡器的系统,那么只有我在另一个 Gtkterm 上得到那个 cmd "$1V ".如果我在不重新启动的情况下重试,则在该 Gkterm 上看不到该 cmd...一个线索,但尚未弄清楚.

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 " 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 " 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.

是否类似于将 cmd 写入设备文件,但未排入实际设备?有什么办法可以检查这个吗?

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.

更新:

好的,我已经通过稍微修改代码来让它工作,如下所示.

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

这是在我的 init() 函数中打开端口的代码的修改部分.两个变化:

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

1) O_NDELAY 被添加到 open() 调用的标志中,它忽略数据载波检测 (DCD) 线,以查看另一端是否已连接并准备好进行通信.这本来是用于MODEM的,我不需要,事实上,因为我使用的是usbserial,所以我根本没有它.但是这个标志也会进一步调用 read() 和 write() 作为非阻塞.需要注意的是,我原以为可以通过将 CLOCAL 添加到 termios 结构的 cflag 来解决这个问题,我尝试过但没有成功.

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) 恢复进一步 read() 和 write() 调用的阻塞行为.

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

这个组合非常适合我.唯一我没有发布这个作为答案的原因是我还不明白为什么它在没有这种修改的情况下在 PC 上工作,因为它是相同的硬件.事实上,我可以使用 minicom 从 ARM9 TARGET 上的智能卡读卡器读取数据,但我的程序不能.我去查一下FT232BL的文档,看看DCD默认是什么状态.

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.

无论如何,我在 POSIX 操作系统的串行编程指南 .解释任何人???当然,我找到答案后会发布..

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..

干杯:)

推荐答案

刚刚在带有 Telegesis USB 模块的 Raspberry Pi 上遇到了相同的症状,我将其添加为另一个数据点.

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

在我的情况下,原因是缺少 RTS 标志.Telegesis 需要 CRTSCTS 流控制,并且不会在没有看到 RTS 的情况下向 Raspberry 发送任何数据.这里令人困惑的方面是 a) 相同的代码在 PC 上运行良好,b) 在第一次插入 Telegesis 时它在 Raspberry 上运行良好,但在随后打开/dev/ttyUSB0 时不会看到任何数据由覆盆子.

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.

出于某种原因,似乎在 ARM 上,RTS 标志在设备关闭时被清除,但没有再次设置,而在 x86/x64 上,RTS 标志保持设置.此处的修复只是设置 RTS 标志(如果尚未设置) - 例如

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

我确实注意到,在您的情况下,您没有使用流量控制,因此上述内容很可能不适用.然而,正是您的问题和提到 minicom 的工作使我们找到了解决我们问题的方法 - 所以谢谢您!

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天全站免登陆