简体   繁体   English

RS485:设备的不适当的ioctl

[英]RS485: Inappropriate ioctl for device

I am using the following code to access the RS485 slave but I get the error: 我正在使用以下代码访问RS485从站,但出现错误:

Error reading ioctl port (25): Inappropriate ioctl for device 读取ioctl端口(25)时出错:设备的ioctl不适当

My code is as follows: 我的代码如下:

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

I took the source code from https://www.kernel.org/doc/Documentation/serial/serial-rs485.txt . 我从https://www.kernel.org/doc/Documentation/serial/serial-rs485.txt获取了源代码。 I am developing my application on the raspberry pi and am connected to the FTDI USB Serial device using Quad RS232-HS chip. 我正在树莓派上开发应用程序,并使用Quad RS232-HS芯片连接到FTDI USB串行设备。 What might be the source of the error?\\ 错误的根源可能是什么?\\

When USB I connect the USB device, output for 当我连接USB设备时,输出为

dmesg dmesg

is as follow: 如下:

[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

I think it might add some value for others facing similar issues in the future to elaborate a bit more on Rodney's answer from a hardware side. 我认为,从硬件方面详细说明罗德尼的答案可能会为将来面临类似问题的其他人增加一些价值。

Although I came here looking for the answer to the thread question (why was I getting an ioctl error trying to activate the RS485 mode on pyserial (which I was actually calling from pyModbus) I found the answer to that question above. Plain and simple, and to provide a shorter answer than Rodney's: you can't. You won't be able to activate the RS485 mode on your FTDI because there is no function to use the RTS signal as a drive enable for your RS485 chip. 尽管我来这里是为了寻找线程问题的答案(为什么我在尝试激活pyserial上的RS485模式时遇到ioctl错误(实际上是我从pyModbus调用的),但我在上面找到了该问题的答案。简单明了,并且提供的答案比Rodney的答案短:您不能。您将无法激活FTDI上的RS485模式,因为没有使用RTS信号作为RS485芯片的驱动使能的功能。

Some devices, like those using the atmel_serial driver do implement this functionality, but you won't find that many. 某些设备(例如使用atmel_serial驱动程序的设备)确实实现了此功能,但您找不到很多。 Digging into this issue you'll find people saying you can write your own driver based on one of those that support the RS485 mode (see, for instance: automatically changing RTS for RS-485 communication , but in my view they are wrong) 深入研究这个问题,您会发现有人说您可以基于支持RS485模式的驱动程序之一编写自己的驱动程序(例如,请参见: 自动更改用于RS-485通讯的RTS ,但在我看来,这是错误的)

Now that you have the bad news, you can ask yourself why would you need to use the RS485 mode? 现在您有个坏消息,您可以问自己为什么要使用RS485模式? I imagine that, like me, you were trying to establish a half-duplex link between two devices using a two wire RS485 setup (which is surprisingly frequent in many industrial devices where you have a master-slave configuration, where you have one side sending a request for data and the other side duly responding, as you do with Modbus). 我想像我一样,您正在尝试使用两线RS485设置在两个设备之间建立半双工链接(令人惊讶的是,在许多工业设备中,您具有主从配置,并且您需要一侧发送就像Modbus一样,对数据的请求以及对方的正确响应)。

If you are looking for a software solution you can still take the RTS signal high and low manually before starting and after finishing transmitting from each side. 如果您正在寻找软件解决方案,则仍然可以在开始和结束从每一侧传输之后手动将RTS信号设为高电平和低电平。 This solution will work for testing and just for fun in most environments (at least those I tried, it even works with Python on a Raspberry Pi using a GPIO line to control the Drive Enable/~Read Enable signals) but I would not use it if you want to get good reliability. 该解决方案将在大多数环境中进行测试并使其有趣(至少在我尝试过的环境中,它甚至可以在Raspberry Pi上与Python一起使用GPIO线来控制Drive Enable /〜Read Enable信号),但我不会使用它如果您想获得良好的可靠性。 With any non-real time OS there is no warranty you will get the correct timing and some devices are quite picky and will report a timeout error. 对于任何非实时操作系统,都没有保修,您将获得正确的时间,并且某些设备非常挑剔,并且会报告超时错误。 If you are free to choose both sides (or all of them if you have more than two stations) of the bus you can always increase your tiemout everywhere and hope for the best. 如果您可以自由选择公交车的两侧(如果您有两个以上的车站,则可以全部选择),您可以随时随地增加平局 ,并希望最好。

Good news is, as Rodney said, on the FTDI chip you have the TXDEN signal available on pin 13 so you can use that right away. 正如罗德尼所说,好消息是,在FTDI芯片上,引脚13上有可用的TXDEN信号,因此您可以立即使用它。 Unfortunately, most cheap USB to serial adaptor PCBs don't have that signal easily accessible on the board. 不幸的是,大多数廉价的USB转串行适配器PCB板上的信号都不容易访问。 That's what happened to my Sparkfun board. 这就是我的Sparkfun董事会发生的事情。 If you are not up for the challenge of attaching a cable directly to the SMD IC you can do what I did: download mprog 3.5 from FTDI, connect to the EPROM, under I/O Controls select TXDEN instead of TXLED and click save to write the updated firmware to the chip. 如果您没有挑战直接将电缆连接到SMD IC的挑战,则可以执行我的操作:从FTDI下载mprog 3.5,连接到EPROM,在I / O Controls下,选择TXDEN而不是TXLED,然后单击Save以写入芯片的更新固件。 Then you can easily attach a cable to the LED pad and use it as your Drive Enable/~Read Enable signal (see pictures, the scope snapshot shows it works nicely). 然后,您可以轻松地将电缆连接到LED焊盘,并将其用作驱动器启用/〜读取启用信号(参见图片,示波器快照显示它很好地工作)。

Modified FTDI board with TXDEN signal on TXLED 修改后的FTDI板,在TXLED上带有TXDEN信号

Scope capture of the modified circuit: blue is TX, yellow TXDEN 修改电路的示波器捕获:蓝色是TX,黄色TXDEN

Lastly, if you are unlucky enough to run into other chips that don't have this option (TXDEN), like the PL2303 you can always go old school and build a hardware TXDEN with a 555 timer (see here: http://www.embeddedsys.com/subpages/resources/images/documents/microsys_art_RS485.pdf ). 最后,如果您不幸遇到其他没有此选项的芯片(TXDEN),例如PL2303,您可以随时学习,并使用555定时器构建硬件TXDEN(请参见此处: http:// www .embeddedsys.com / subpages / resources / images / documents / microsys_art_RS485.pdf )。 I tested this circuit and it works fine, at least at 9600 bps (note that I had to use a 39K resistor instead of a 3.9K, I think that's a typo but I might be wrong, I did not go into fine details, I just needed something quick and dirty to pair with my FTDI. 我测试了此电路,它至少在9600 bps时工作正常(请注意,我必须使用39K电阻器而不是3.9K电阻,我认为这是一个错字,但我可能是错的,我没有详细介绍,我只是需要快速又脏的东西来与我的FTDI配对。

As mentioned in the comment by @Richard Chambers you are using the structure uninitialised. 如@Richard Chambers在评论中所述,您正在使用未初始化的结构。 You should do 你应该做

struct serial_rs485 rs485conf = {0};

However, as discussed that didn't solve the problem. 但是,正如所讨论的,并不能解决问题。

Quoting from the Kernel document you referenced 引用您引用的内核文档

Some CPUs/UARTs (eg, Atmel AT91 or 16C950 UART) contain a built-in half-duplex mode capable of automatically controlling line direction by toggling RTS or DTR signals. 某些CPU / UART(例如Atmel AT91或16C950 UART)包含内置的半双工模式,该模式能够通过切换RTS或DTR信号来自动控制线路方向。 That can be used to control external half-duplex hardware like an RS485 transceiver... 可以用来控制外部半双工硬件,例如RS485收发器...

However this device has a dedicated pin called TXDEN for that. 但是,该设备具有专用的引脚TXDEN。 Quoting from the Data sheet section 4.3.3 引用数据表第4.3.3节

With RS485, the transmitter is only enabled when a character is being transmitted from the UART. 使用RS485时,仅在从UART发送字符时启用发送器。 The TXDEN pins on the FT4232H are provided for exactly that purpose, and so the transmitter enables are wired to the TXDEN FT4232H上的TXDEN引脚正是为此目的而提供的,因此发送器使能端已连接到TXDEN

and you can see that the RS232 outputs RTS / DTR not connected in RS485, this is in contrast to devices where RTS becomes TXDEN which would require an intervention by the driver. 并且您会看到RS232输出未在RS485中连接的RTS / DTR,这与RTS变为TXDEN的设备(需要驾驶员干预)形成对比。

If we pick apart struct serial_rs485 we can see that it is mostly about controlling RTS for this purpose. 如果我们分开struct serial_rs485我们可以看到它主要是为了控制RTS。 Since this device has a dedicated TXDEN pin, those control fields are irrelevant. 由于该器件具有专用的TXDEN引脚,因此这些控制字段无关紧要。

Another flag that is used in this structure: 此结构中使用的另一个标志:

  /* Set this flag if you want to receive data even whilst sending data */
rs485conf.flags |= SER_RS485_RX_DURING_TX;

And yes looking at the circuit, what you put out on the line you will also receive. 是的,看看电路,您在线路上的输出也会收到。 Looks like you cannot turn that off. 看来您无法关闭它。 RS485 is multidrop, so you should be filtering out messages that were not addressed to you anyway. RS485是多点通信,因此您应该过滤掉未发送给您的消息。 The fact that some of those messages may have originated from you doesn't matter. 这些消息中的某些消息可能源于您的事实并不重要。

Finally (and firstly) we have this 最后(首先)我们有这个

/* Enable RS485 mode: */
rs485conf.flags |= SER_RS485_ENABLED;

That's really saying "enable all the RS485-specific stuff that's controlled by this structure". 这就是说“启用由该结构控制的所有特定于RS485的东西”。 Except that we've just made all that stuff irrelevant, so enabling it has no effect. 除了我们只是使所有这些东西无关紧要之外,因此启用它没有任何作用。

This is why the ioctl is not implemented for that UART. 这就是为什么未为该UART实现ioctl的原因。

You have a number of options, these are just suggestions so pick what suits you 您有很多选择,这些只是建议,因此请选择适合您的选择

  • Delete this section of the initializtion code as it's not needed 删除不需要的初始化代码部分
  • Compile it conditionally #if RS485_IOCTLS 有条件地编译它#if RS485_IOCTLS
  • Run it conditionally 有条件地运行
  • Treat that errno value ENOTTY as indicating that, in this context the ioctl was not required and you can in fact proceed as if there was no error 将那个errnoENOTTY视为表明,在这种情况下不需要ioctl ,实际上您可以像没有错误一样继续操作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM