RS485:设备的 ioctl 不合适
RS485: Inappropriate ioctl for device
我正在使用以下代码访问 RS485 从站,但出现错误:
Error reading ioctl port (25): Inappropriate ioctl for device
我的代码如下:
#include <linux/serial.h>
#include <sys/ioctl.h>
int fd = open ("/dev/ttyUSB0", O_RDWR);
if (fd < 0) {
printf("Error Opening\n");
exit(0);
}
struct serial_rs485 rs485conf;
/* Enable RS485 mode: */
rs485conf.flags |= SER_RS485_ENABLED;
/* Set logical level for RTS pin equal to 1 when sending: */
rs485conf.flags |= SER_RS485_RTS_ON_SEND;
/* set logical level for RTS pin equal to 0 after sending: */
rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);
/* Set rts delay before send, if needed: */
rs485conf.delay_rts_before_send = 0;
/* Set rts delay after send, if needed: */
rs485conf.delay_rts_after_send = 0;
/* Set this flag if you want to receive data even whilst sending data */
rs485conf.flags |= SER_RS485_RX_DURING_TX;
if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) {
fprintf( stderr, "Error reading ioctl port (%d): %s\n", errno, strerror( errno ));
exit(0);
}
//TODO read and write
/* Close the device when finished: */
if (close (fd) < 0) {
fprintf( stderr, "Error closing device connection (%d): %s\n", errno, strerror( errno ));
}
我从 https://www.kernel.org/doc/Documentation/serial/serial-rs485.txt 获取了源代码。我正在 raspberry pi 上开发我的应用程序,并使用 Quad RS232-HS 芯片连接到 FTDI USB 串行设备。错误的来源可能是什么?\
当USB连接USB设备时,输出为
dmesg
如下:
[16865.640038] usb 3-2: new high-speed USB device number 10 using xhci_hcd
[16865.780365] usb 3-2: New USB device found, idVendor=0403, idProduct=6011
[16865.780367] usb 3-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[16865.780369] usb 3-2: Product: Quad RS232-HS
[16865.780370] usb 3-2: Manufacturer: FTDI
[16866.377940] usbcore: registered new interface driver usbserial
[16866.377969] usbcore: registered new interface driver usbserial_generic
[16866.377994] usbserial: USB Serial support registered for generic
[16866.384018] usbcore: registered new interface driver ftdi_sio
[16866.384045] usbserial: USB Serial support registered for FTDI USB Serial Device
[16866.384203] ftdi_sio 3-2:1.0: FTDI USB Serial Device converter detected
[16866.384247] usb 3-2: Detected FT4232H
[16866.384373] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB0
[16866.384399] ftdi_sio 3-2:1.1: FTDI USB Serial Device converter detected
[16866.384431] usb 3-2: Detected FT4232H
[16866.384727] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB1
[16866.384751] ftdi_sio 3-2:1.2: FTDI USB Serial Device converter detected
[16866.384786] usb 3-2: Detected FT4232H
[16866.384897] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB2
[16866.384917] ftdi_sio 3-2:1.3: FTDI USB Serial Device converter detected
[16866.384950] usb 3-2: Detected FT4232H
[16866.385385] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB3
如@Richard Chambers 的评论中所述,您使用的是未初始化的结构。你应该做
struct serial_rs485 rs485conf = {0};
然而,正如所讨论的那样并没有解决问题。
引用您引用的内核文档
Some CPUs/UARTs (e.g., Atmel AT91 or 16C950 UART) contain a built-in
half-duplex mode capable of automatically controlling line direction by
toggling RTS or DTR signals. That can be used to control external
half-duplex hardware like an RS485 transceiver...
但是,此设备有一个专用引脚,称为 TXDEN。引用 Data sheet 节 4.3.3
With RS485, the transmitter
is only enabled when a character is being transmitted from the UART. The TXDEN pins on the FT4232H are
provided for exactly that purpose, and so the transmitter enables are wired to the TXDEN
并且您可以看到 RS232 输出 RTS / DTR 未连接到 RS485,这与 RTS 变为 TXDEN 的设备形成对比,后者需要驱动程序干预。
如果我们分开来看struct serial_rs485
,我们可以看到它主要是为了这个目的而控制RTS。由于此设备具有专用的 TXDEN 引脚,因此这些控制字段无关紧要。
此结构中使用的另一个标志:
/* Set this flag if you want to receive data even whilst sending data */
rs485conf.flags |= SER_RS485_RX_DURING_TX;
是的,看看电路,你在线上输出的东西你也会收到。看起来你不能把它关掉。 RS485 是多点的,所以你应该过滤掉那些不是发给你的信息。事实上,其中一些消息可能来自您并不重要。
终于(也是第一次)我们有了这个
/* Enable RS485 mode: */
rs485conf.flags |= SER_RS485_ENABLED;
那是真的 "enable all the RS485-specific stuff that's controlled by this structure"。除了我们刚刚让所有这些东西变得无关紧要,所以启用它没有任何效果。
这就是为什么没有为该 UART 实现 ioctl 的原因。
您有多种选择,这些只是建议,请选择适合您的选择
- 删除这部分初始化代码,因为它不需要
- 条件编译
#if RS485_IOCTLS
- 运行有条件
- 将
errno
值 ENOTTY
视为表明,在这种情况下 ioctl
不是必需的,实际上您可以像没有错误一样继续进行
我认为从硬件方面详细阐述 Rodney 的回答可能会为将来面临类似问题的其他人增加一些价值。
虽然我来这里是为了寻找线程问题的答案(为什么我在尝试激活 pyserial 上的 RS485 模式时收到 ioctl 错误(我实际上是从pyModbus) 我在上面找到了这个问题的答案。简单明了,并提供比罗德尼的答案更短的答案:你不能。你将无法在你的 FTDI 上激活 RS485 模式,因为没有功能可以使用RTS 信号作为 RS485 芯片的驱动使能。
一些设备,例如那些使用 atmel_serial 驱动程序的设备确实实现了此功能,但您不会找到那么多。深入研究这个问题,您会发现有人说您可以基于其中一个支持 RS485 模式的驱动程序编写自己的驱动程序(例如,参见:automatically changing RTS for RS-485 communication,但在我看来他们是错误的)
既然你有坏消息,你可以问问自己为什么需要使用RS485模式?我想像我一样,您正在尝试使用双线 RS485 设置在两个设备之间建立 half-duplex link(这在您拥有 master-slave 的许多工业设备中非常频繁)配置,其中一侧发送数据请求,另一侧及时响应,就像使用 Modbus 一样)。
如果您正在寻找软件解决方案,您仍然可以在开始之前和从每一方完成传输之后手动将 RTS 信号设置为高电平和低电平。该解决方案将在大多数环境中用于测试和娱乐(至少我尝试过的那些,它甚至可以在 Raspberry Pi 上与 Python 一起使用 GPIO 线来控制驱动器启用/~读取启用信号) 但如果您想获得良好的可靠性,我不会使用它。任何 non-real 时间 OS 都无法保证您会获得正确的时间,并且某些设备非常挑剔,并且会报告 timeout 错误。如果您可以自由选择公交车的两侧(如果您有两个以上的车站,则可以选择全部)您可以随时增加 tiemout 并希望最好。
好消息是,正如 Rodney 所说,在 FTDI 芯片上,引脚 13 上有可用的 TXDEN 信号,因此您可以立即使用它。不幸的是,最便宜的 USB 转串口
适配器 PCB 板上的信号不容易访问。我的 Sparkfun 开发板就是这样。如果你不想接受将电缆直接连接到 SMD IC 的挑战,你可以按照我所做的去做:从 FTDI 下载 mprog 3.5,连接到 EPROM,在 I/O Controls select TXDEN 下,而不是TXLED 并点击保存将更新的固件写入芯片。然后您可以轻松地将电缆连接到 LED 焊盘并将其用作您的驱动器启用/~读取启用信号(参见图片,示波器快照显示它工作得很好)。
Modified FTDI board with TXDEN signal on TXLED
Scope capture of the modified circuit: blue is TX, yellow TXDEN
最后,如果您不幸 运行 进入其他没有此选项 (TXDEN) 的芯片,例如 PL2303,您可以老老实实地构建一个带有 555 定时器的硬件 TXDEN (参见此处:http://www.embeddedsys.com/subpages/resources/images/documents/microsys_art_RS485.pdf)。我测试了这个电路,它工作正常,至少在 9600 bps(请注意,我必须使用 39K 电阻而不是 3.9K,我认为这是一个错字,但我可能是错的,我没有详细说明,我只需要一些快速而肮脏的东西来与我的 FTDI 配对。
我正在使用以下代码访问 RS485 从站,但出现错误:
Error reading ioctl port (25): Inappropriate ioctl for device
我的代码如下:
#include <linux/serial.h>
#include <sys/ioctl.h>
int fd = open ("/dev/ttyUSB0", O_RDWR);
if (fd < 0) {
printf("Error Opening\n");
exit(0);
}
struct serial_rs485 rs485conf;
/* Enable RS485 mode: */
rs485conf.flags |= SER_RS485_ENABLED;
/* Set logical level for RTS pin equal to 1 when sending: */
rs485conf.flags |= SER_RS485_RTS_ON_SEND;
/* set logical level for RTS pin equal to 0 after sending: */
rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);
/* Set rts delay before send, if needed: */
rs485conf.delay_rts_before_send = 0;
/* Set rts delay after send, if needed: */
rs485conf.delay_rts_after_send = 0;
/* Set this flag if you want to receive data even whilst sending data */
rs485conf.flags |= SER_RS485_RX_DURING_TX;
if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) {
fprintf( stderr, "Error reading ioctl port (%d): %s\n", errno, strerror( errno ));
exit(0);
}
//TODO read and write
/* Close the device when finished: */
if (close (fd) < 0) {
fprintf( stderr, "Error closing device connection (%d): %s\n", errno, strerror( errno ));
}
我从 https://www.kernel.org/doc/Documentation/serial/serial-rs485.txt 获取了源代码。我正在 raspberry pi 上开发我的应用程序,并使用 Quad RS232-HS 芯片连接到 FTDI USB 串行设备。错误的来源可能是什么?\
当USB连接USB设备时,输出为
dmesg
如下:
[16865.640038] usb 3-2: new high-speed USB device number 10 using xhci_hcd
[16865.780365] usb 3-2: New USB device found, idVendor=0403, idProduct=6011
[16865.780367] usb 3-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[16865.780369] usb 3-2: Product: Quad RS232-HS
[16865.780370] usb 3-2: Manufacturer: FTDI
[16866.377940] usbcore: registered new interface driver usbserial
[16866.377969] usbcore: registered new interface driver usbserial_generic
[16866.377994] usbserial: USB Serial support registered for generic
[16866.384018] usbcore: registered new interface driver ftdi_sio
[16866.384045] usbserial: USB Serial support registered for FTDI USB Serial Device
[16866.384203] ftdi_sio 3-2:1.0: FTDI USB Serial Device converter detected
[16866.384247] usb 3-2: Detected FT4232H
[16866.384373] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB0
[16866.384399] ftdi_sio 3-2:1.1: FTDI USB Serial Device converter detected
[16866.384431] usb 3-2: Detected FT4232H
[16866.384727] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB1
[16866.384751] ftdi_sio 3-2:1.2: FTDI USB Serial Device converter detected
[16866.384786] usb 3-2: Detected FT4232H
[16866.384897] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB2
[16866.384917] ftdi_sio 3-2:1.3: FTDI USB Serial Device converter detected
[16866.384950] usb 3-2: Detected FT4232H
[16866.385385] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB3
如@Richard Chambers 的评论中所述,您使用的是未初始化的结构。你应该做
struct serial_rs485 rs485conf = {0};
然而,正如所讨论的那样并没有解决问题。
引用您引用的内核文档
Some CPUs/UARTs (e.g., Atmel AT91 or 16C950 UART) contain a built-in half-duplex mode capable of automatically controlling line direction by toggling RTS or DTR signals. That can be used to control external half-duplex hardware like an RS485 transceiver...
但是,此设备有一个专用引脚,称为 TXDEN。引用 Data sheet 节 4.3.3
With RS485, the transmitter is only enabled when a character is being transmitted from the UART. The TXDEN pins on the FT4232H are provided for exactly that purpose, and so the transmitter enables are wired to the TXDEN
并且您可以看到 RS232 输出 RTS / DTR 未连接到 RS485,这与 RTS 变为 TXDEN 的设备形成对比,后者需要驱动程序干预。
如果我们分开来看struct serial_rs485
,我们可以看到它主要是为了这个目的而控制RTS。由于此设备具有专用的 TXDEN 引脚,因此这些控制字段无关紧要。
此结构中使用的另一个标志:
/* Set this flag if you want to receive data even whilst sending data */
rs485conf.flags |= SER_RS485_RX_DURING_TX;
是的,看看电路,你在线上输出的东西你也会收到。看起来你不能把它关掉。 RS485 是多点的,所以你应该过滤掉那些不是发给你的信息。事实上,其中一些消息可能来自您并不重要。
终于(也是第一次)我们有了这个
/* Enable RS485 mode: */
rs485conf.flags |= SER_RS485_ENABLED;
那是真的 "enable all the RS485-specific stuff that's controlled by this structure"。除了我们刚刚让所有这些东西变得无关紧要,所以启用它没有任何效果。
这就是为什么没有为该 UART 实现 ioctl 的原因。
您有多种选择,这些只是建议,请选择适合您的选择
- 删除这部分初始化代码,因为它不需要
- 条件编译
#if RS485_IOCTLS
- 运行有条件
- 将
errno
值ENOTTY
视为表明,在这种情况下ioctl
不是必需的,实际上您可以像没有错误一样继续进行
我认为从硬件方面详细阐述 Rodney 的回答可能会为将来面临类似问题的其他人增加一些价值。
虽然我来这里是为了寻找线程问题的答案(为什么我在尝试激活 pyserial 上的 RS485 模式时收到 ioctl 错误(我实际上是从pyModbus) 我在上面找到了这个问题的答案。简单明了,并提供比罗德尼的答案更短的答案:你不能。你将无法在你的 FTDI 上激活 RS485 模式,因为没有功能可以使用RTS 信号作为 RS485 芯片的驱动使能。
一些设备,例如那些使用 atmel_serial 驱动程序的设备确实实现了此功能,但您不会找到那么多。深入研究这个问题,您会发现有人说您可以基于其中一个支持 RS485 模式的驱动程序编写自己的驱动程序(例如,参见:automatically changing RTS for RS-485 communication,但在我看来他们是错误的)
既然你有坏消息,你可以问问自己为什么需要使用RS485模式?我想像我一样,您正在尝试使用双线 RS485 设置在两个设备之间建立 half-duplex link(这在您拥有 master-slave 的许多工业设备中非常频繁)配置,其中一侧发送数据请求,另一侧及时响应,就像使用 Modbus 一样)。
如果您正在寻找软件解决方案,您仍然可以在开始之前和从每一方完成传输之后手动将 RTS 信号设置为高电平和低电平。该解决方案将在大多数环境中用于测试和娱乐(至少我尝试过的那些,它甚至可以在 Raspberry Pi 上与 Python 一起使用 GPIO 线来控制驱动器启用/~读取启用信号) 但如果您想获得良好的可靠性,我不会使用它。任何 non-real 时间 OS 都无法保证您会获得正确的时间,并且某些设备非常挑剔,并且会报告 timeout 错误。如果您可以自由选择公交车的两侧(如果您有两个以上的车站,则可以选择全部)您可以随时增加 tiemout 并希望最好。
好消息是,正如 Rodney 所说,在 FTDI 芯片上,引脚 13 上有可用的 TXDEN 信号,因此您可以立即使用它。不幸的是,最便宜的 USB 转串口 适配器 PCB 板上的信号不容易访问。我的 Sparkfun 开发板就是这样。如果你不想接受将电缆直接连接到 SMD IC 的挑战,你可以按照我所做的去做:从 FTDI 下载 mprog 3.5,连接到 EPROM,在 I/O Controls select TXDEN 下,而不是TXLED 并点击保存将更新的固件写入芯片。然后您可以轻松地将电缆连接到 LED 焊盘并将其用作您的驱动器启用/~读取启用信号(参见图片,示波器快照显示它工作得很好)。
Modified FTDI board with TXDEN signal on TXLED
Scope capture of the modified circuit: blue is TX, yellow TXDEN
最后,如果您不幸 运行 进入其他没有此选项 (TXDEN) 的芯片,例如 PL2303,您可以老老实实地构建一个带有 555 定时器的硬件 TXDEN (参见此处:http://www.embeddedsys.com/subpages/resources/images/documents/microsys_art_RS485.pdf)。我测试了这个电路,它工作正常,至少在 9600 bps(请注意,我必须使用 39K 电阻而不是 3.9K,我认为这是一个错字,但我可能是错的,我没有详细说明,我只需要一些快速而肮脏的东西来与我的 FTDI 配对。