Linux 由于未知原因读取串行端口块

Linux 由于未知原因读取串行端口块,linux,embedded,serial-port,embedded-linux,termios,Linux,Embedded,Serial Port,Embedded Linux,Termios,我正在尝试在Linux下使用termios框架通过UART(usbserial)连接一个非接触式智能卡读卡器。该代码在PC上运行良好,但当我交叉编译并在ARM9目标上试用时,它可以打开设备,甚至可以将命令写入设备,但读取命令会无限期阻塞。下面是代码段: int mifare_rdr_init(struct mifare_1K * ptr, char *rdr_devnode) { bzero(ptr, sizeof(struct mifare_1K)); //

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

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;
}
这是代码的修改部分,用于在my init()函数中打开端口。两个变化:

1) O_NDELAY被添加到open()调用中的标志中,该调用忽略数据载波检测(DCD)线路,以查看另一端是否已连接并准备好进行通信。这是最初用于调制解调器,我不需要,事实上,我没有它,因为我使用的是usbserial。但该标志还进一步调用read()和write()作为非阻塞。值得注意的是,我原以为可以通过向termios struct的cflag添加CLOCAL来解决这个问题,我已经尝试过了,但没有成功

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

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

不管怎样,我在网上找到了一些信息。有人解释吗???当然,当我找到答案后,我会把它贴出来


干杯:)

您可以检查三件事:

规范模式/非规范模式混合1 你把规范模式和非规范模式混为一谈:

ptr->serialdev.c_lflag = ( ICANON );
// ...
ptr->serialdev.c_cc[VMIN] = 1;
术语(3)
手册页说明了有关VMIN的内容:

VMIN非规范读取的最小字符数

所以很明显,你的超时时间不会按你想象的方式工作

规范模式/非规范模式混合2 此外,手册页还进一步说明如下:

这些符号下标值都不同,除了VTIME, VMIN可能分别与VEOL、VEOF具有相同的值。非- 规范模式—特殊字符的含义由超时替换 意思有关VMIN和VTIME的说明,请参阅的说明 下面是非规范模式

因此,请检查这两个平台的这些常量的定义是否不同。可能是,EOL/EOF逻辑可能被错误的设置破坏。EOL和EOF migh都会导致从
read
返回

未初始化
c\u cc

您的代码未显示
c_cc
数组的正确初始化。您既没有读取现有设置,也没有为规范模式所需的值提供合适的默认值。到目前为止显示的代码甚至没有清除这些值。因此,可能会使用不可预测的值

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

在我的案例中,原因是缺少RTS标志。Telegesis需要CRTSCTS流量控制,在没有看到RTS的情况下不会向Raspberry发送任何数据。这里令人费解的方面是:a)相同的代码在PC上运行得很好,b)第一次插入Telegesis时在Raspberry上运行得很好,但是在随后打开/dev/ttyUSB0时,Raspberry看不到任何数据

出于某种原因,在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)
  {
    // handle error
  }
}
#包括
//...
int rtscts=0;
如果(ioctl(fd、TIOCMGET和rtscts)!=0)
{
//处理错误
}
否则如果(!(rtscts和TIOCM_RTS))
{
rtscts |=TIOCM|U RTS;
如果(ioctl(fd、TIOCMSET和rtscts)!=0)
{
//处理错误
}
}

我确实注意到,在您的情况下,您不使用流控制,因此上述内容很可能不适用。正是您的问题和minicom的工作方式让我们找到了问题的解决方案,所以感谢您

可能不是问题,但您可能希望将
CLOCAL
添加到
c\u cflag
。最好使用
tcgetattr
来获取当前设置(例如控制字符),而不是用0覆盖这些设置。您可能还想尝试使用
tcdrain
来确保您的程序等待数据传输。嗨,Hasturkun…我没有检查,因为我认为没有人对此感兴趣。不管怎样,我检查了你的建议(tcdrain(),CLOCAL)…同样的结果。但事实上,它在PC上运行,而不是在目标上运行,这让我很感兴趣。欢迎更多建议…将在更新时发布。“但这在PC上运行,而不是在目标上运行……”很简单,您的代码不符合POSIX,所以不要期望它是可移植的。您好,A.H.,您对VMIN的看法是正确的。在规范模式下不需要它,所以我通过注释对问题进行了编辑。但是关于初始化结构,您可以看到我正在将包含termios结构的结构归零,因此我正在将整个结构初始化为零,我不会让它挂起。@aditya:对不起,我没有注意到
bzero
。但这加强了一点,即规范模式所需的控制字符没有配置。非常正确,它们应该已经初始化。我想指出的是,在我的一次试验中,我确实初始化了VEOL、VEOL2和VEOF,因为我认为read()阻止了bcos,但没有得到默认的终端“\n”,但没有用。您是回答的人,所以谢谢:)…很高兴我的问题帮助了您。我现在时间有点紧,所以
#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
  }
}