adi*_*tya 5 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)); // 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;
}
Run Code Online (Sandbox Code Playgroud)
mifare_1K 结构包含串行设备的文件描述符、termios 结构和我正在使用的各种缓冲区。我提到的设备是 usb-to-serial(模块:ftdi_sio)设备。它在 termios 的 Canonical 模式下配置为 38400@8-N-1。
规范模式因为来自阅读器的响应以 '\n' 结尾,所以它在规范模式下处理得更好,因为它读取设备直到收到 '\n'(如果我错了,请纠正我)。
首先我调用 init() fn,然后调用 get_rdr_version()。打印了字符串“cmd sent, read version...”,所以我知道它可以写,但不打印字符串“reading done”。在那之后。
另一件事是,如果我移除读卡器并将该端口连接到另一台 PC 上的 gtkterm(串口终端程序),我在该 gtkterm 上没有收到“$1V\n”??!!. 然后经过一些 RnD,我发现如果我重新启动读取器连接读取器的系统,那么只有我在另一个 Gtkterm 上得到那个 cmd "$1V\n"。如果我在没有重新启动的情况下再试一次,那个 cmd 在那个 Gkterm 上看不到……一个线索,但还没有弄清楚。
是不是 cmd 被写入设备文件,但没有被排空到实际设备?有什么办法可以解决这个问题吗?
任何帮助都深表感谢,因为我已经坚持了一段时间了......谢谢。
更新 :
好的,我通过稍微修改代码使其工作,如下所示。
// 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;
}
Run Code Online (Sandbox Code Playgroud)
这是在我的 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 操作系统的串行编程指南中找到了一些信息。解释任何人???当然,当我发现时,我会发布答案..
干杯:)
您可以检查三件事:
您正在混合规范模式和非规范模式:
ptr->serialdev.c_lflag = ( ICANON );
// ...
ptr->serialdev.c_cc[VMIN] = 1;
Run Code Online (Sandbox Code Playgroud)
联机termios(3)
帮助页中有关 VMIN 的说明:
VMIN非规范读取 的最小字符数。
很明显,你的超时不会像你想象的那样起作用。
此外,联机帮助页进一步说明如下:
这些符号下标值都是不同的,除了 VTIME、 VMIN 可能分别与 VEOL、 VEOF具有相同的值之外。在非规范模式下,特殊字符含义被超时含义取代。关于VMIN和VTIME的解释,请参见下面非规范模式的描述。
因此,请检查这些常量的定义对于您的两个平台是否不同。EOL/EOF 逻辑可能会被错误的设置破坏。EOL 和 EOF 都可能导致从read
.
c_cc
您的代码未显示c_cc
数组的正确初始化。您既不读取现有设置,也不为规范模式所需的值提供合适的默认值。到目前为止显示的代码甚至没有清除这些值。因此,可能会使用不可预测的值。