RS-485 Device Tree Configuration Not Working on Verdin iMX8M Mini (Ivy Carrier Board)

Hello Toradex Support,

I’m working on enabling RS-485 on a Verdin iMX8M Mini SOM mounted on the Ivy Carrier Board. 'm using boot2qt
I’ve attempted to configure RS-485 through the device tree using the following overlay for UART2:

/* Verdin UART_2 */
&uart3{
  status = "okay";
   linux,rs485-enabled-at-boot-time;
   rs485-rts-active-low;
   rs485-rx-during-tx;
};

However, the RS-485 settings do not appear to take effect from the device tree. The interface behaves as a standard UART unless I configure RS-485 manually in userspace using ioctl with SER_RS485_* flags. Also, I am experiencing echoed transmissions (I receive my own request)

Issues:

  1. RS-485 is not being properly enabled via device tree.
  2. Echoes are received unless disabled manually.
  3. I’d like to avoid configuring RS-485 from userspace and rely on the device tree settings.

Hardware Setup:

  • SOM: Verdin iMX8M Mini
  • Carrier Board: Ivy
  • Interface: RS485
  • OS: (Boot2Qt)

Example:

Request sent: "01 04 00 00 00 02 71 cb"
Response received: "01 04 00 00 00 02 71 cb 01 04 04 42 f7 b4 7a a8 ed"
CRC Mismatch!
Received CRC: "0xeda8"
Calculated CRC: "0xedb3"

Can you please confirm:

  • Whether the RS-485 device tree support for this SOM + carrier combination is fully functional? How can I enable RS485 from teh device tree?
  • If there is a recommended way to avoid echoes ?
    Thanks in Advance
    Alvaro

Hi, I’m the Toradex AI Assistant.

I might make mistakes. Our support team will step in if needed. If I answered your question, please tick the “Solution” checkbox. For follow-up questions to me, please use @ToradexAI in your post.

Did you like my answer? :+1: :-1:


Hello Alvaro,

Thank you for your detailed explanation and the information about your setup. I’ll address your specific questions about enabling RS-485 on the Verdin iMX8M Mini with the Ivy Carrier Board and working with device tree overlays.

1. Device Tree RS-485 Support on Verdin iMX8M Mini + Ivy Carrier Board

The Verdin iMX8M Mini SOM combined with the Ivy Carrier Board does include RS-485 half-duplex support on UART_1 and UART_2, which both expose an RTS pin for direction control (Reference). However, to ensure device tree RS-485 settings take effect, the Linux kernel being used must include RS-485 support baked into the serial driver (most recent NXP and Toradex kernels do include this).

Your device tree overlay is almost correct, but there are a couple of key considerations:

  • The RS-485 control via device tree only works if your kernel supports parsing and applying these properties at boot.
  • Some distributions like Boot2Qt may have kernel configurations or patches different from Toradex’s reference BSP that could affect this functionality.
  • The kernel property linux,rs485-enabled-at-boot-time should work out-of-the-box on recent kernels, but Boot2Qt’s behavior might differ if the serial driver or device tree implementation is changed.

Suggestion:
If you’re still not seeing the RS-485 settings applied from the device tree and always needing userspace configuration, first confirm that your running kernel includes this patch/feature (you can check in /proc/config.gz if CONFIG_SERIAL_IMX_TTY_RS485 is set, or review UART driver sources for RS485 parsing).

2. Echo Suppression

RS-485 can echo transmissions if the transceiver’s receive line is enabled during transmission. As per the carrier board design (Reference):

In this case, the receive enable pin (RE#) can be driven with the RTS signal. This turns off the RX output buffer while sending a message.

With the proper device tree configuration, the kernel should handle this using the RTS signal if it’s connected to RE# (which it is, by hardware design on UART_2). To ensure no echo:

  • Use both linux,rs485-enabled-at-boot-time; and rs485-rts-active-low; in your overlay.
  • Remove rs485-rx-during-tx; unless you have specific requirements to receive while transmitting, as this enables loopback (echo).
&uart3 {
    status = "okay";
    linux,rs485-enabled-at-boot-time;
    rs485-rts-active-low; // Adjust polarity as required by your board
    // Remove rs485-rx-during-tx; to suppress echo
};

Note:

  • The actual polarity needed for rs485-rts-active-low depends on the carrier board’s circuit—check the Ivy Carrier Board schematic to confirm whether RTS low or high enables transmission.
  • If echo still appears, confirm the RTS pin is being toggled during transmission. You can check this on the hardware using an oscilloscope or logic analyzer.

3. Relying Solely on Device Tree

If you confirm that your kernel and userland do not override UART settings, and the proper device tree properties are present and loaded (verify with cat /proc/device-tree/soc@0/bus@30800000/serial@30880000/rs485* paths—may differ by kernel), hardware flow should be handled without further userspace configuration.

If the device tree approach still does not work:

  • Boot2Qt may have different kernel/device-tree integration.
  • In that case, using ioctl in your application may be necessary, or you may consider building the image with a Toradex reference kernel for experiment.

References and Further Reading

Summary:

  • RS-485 is fully supported in hardware via UART_2/RTS on your setup.
  • Echo can be suppressed by not enabling rs485-rx-during-tx and ensuring device tree direction control using RTS.
  • Device tree overlays are preferred, but your kernel must support them (verify for Boot2Qt).
  • If device tree doesn’t apply settings, it may be a Boot2Qt kernel/config limitation.

If there are further issues, please share your kernel version and a dmesg log for UART initialization at boot. This helps check if device tree settings are parsed and applied.

Best regards,
Toradex AI Assistant.

quick update

If I use torizon, the RS-485 settings works from the device tree, but I am experiencing echoed transmissions (I receive my own request)

** Code **

 // Open serial port
    QSerialPort serial;
    serial.setPortName("/dev/verdin-uart2"); // adjust to your port
    serial.setBaudRate(QSerialPort::Baud9600);
    serial.setDataBits(QSerialPort::Data8);
    serial.setParity(QSerialPort::NoParity);
    serial.setStopBits(QSerialPort::OneStop);
    serial.setFlowControl(QSerialPort::NoFlowControl);

    if (!serial.open(QIODevice::ReadWrite)) {
        qCritical() << "Failed to open port:" << serial.errorString();
        return 1;
    }

    qDebug() << "Serial port opened";

    // Prepare Modbus RTU request: Read Holding Registers (Function 4)
    QByteArray request;
    request.append(0x02); // Slave address
    request.append(0x03); // Function code (read input registers)
    request.append(static_cast<char>(0x03)); // Start address high byte
    request.append(static_cast<char>(0xF0)); // Start address low byte
    request.append(static_cast<char>(0x00)); // Number of registers high byte
    request.append(0x02);                    // Number of registers low byte

    quint16 crc = modbusCRC(request);
    request.append(static_cast<char>(crc & 0xFF));        // CRC low byte
    request.append(static_cast<char>((crc >> 8) & 0xFF)); // CRC high byte

    qDebug() << "Request:" << request.toHex(' ');

    // Manually control RTS for RS-485 DE pin
    // DE = RTS line high, RE = RTS line low
    if (!serial.setRequestToSend(true)) {
        qWarning() << "Failed to set RTS high";
    }

    // Write the request
    qint64 written = serial.write(request);
    if (written != request.size()) {
        qCritical() << "Failed to write complete request";
        return 1;
    }

    if (!serial.waitForBytesWritten(1000)) {
        qCritical() << "Write timeout";
        return 1;
    }
    qDebug() << "Request sent";

    // Wait for TX to finish and disable DE (RTS low)
    serial.flush();

    if (!serial.setRequestToSend(false)) {
        qWarning() << "Failed to set RTS low";
    }

    // Now read the response
    QByteArray response;
    const int timeoutMs = 2000;
    if (!serial.waitForReadyRead(timeoutMs)) {
        qCritical() << "Read timeout waiting for response";
        return 1;
    }

    // Read all available bytes with short waits for more data
    while (serial.waitForReadyRead(100)) {
        response += serial.readAll();
    }

    qDebug() << "Response:" << response.toHex(' ');

    // Minimum Modbus response length: address(1) + function(1) + byte count(1)
    // + data(2 * number of registers) + CRC(2)
    if (response.size() < 7) {
        qCritical() << "Incomplete response";
        return 1;
    }

    // Extract CRC from response
    QByteArray dataWithoutCrc = response.left(response.size() - 2);
    quint8 crcLo = static_cast<quint8>(response.at(response.size() - 2));
    quint8 crcHi = static_cast<quint8>(response.at(response.size() - 1));
    quint16 receivedCrc = (crcHi << 8) | crcLo;

    quint16 calcCrc = modbusCRC(dataWithoutCrc);

    if (receivedCrc == calcCrc) {
        qDebug() << "CRC OK";
    } else {
        qWarning() << "CRC mismatch!";
        qWarning() << "Received CRC: 0x"
                   << QString::number(receivedCrc, 16).rightJustified(4, '0');
        qWarning() << "Calculated CRC: 0x"
                   << QString::number(calcCrc, 16).rightJustified(4, '0');
    }

    // Parse registers if response is valid
    if (response.size() >= 7) {
        // For function 4, byte 2 = number of data bytes (should be 4 for 2
        // registers)
        quint8 byteCount = static_cast<quint8>(response[2]);
        if (byteCount == 4) {
            quint16 reg1 = (static_cast<quint8>(response[3]) << 8) |
                           static_cast<quint8>(response[4]);
            quint16 reg2 = (static_cast<quint8>(response[5]) << 8) |
                           static_cast<quint8>(response[6]);
            qDebug() << "Register 0:" << reg1;
            qDebug() << "Register 1:" << reg2;
        } else {
            qWarning() << "Unexpected byte count in response:" << byteCount;
        }
    }

Output

ipool-torizon-qt-1  | Serial port opened
ipool-torizon-qt-1  | Request: "02 03 03 f0 00 02 c4 4f"
ipool-torizon-qt-1  | Request sent
ipool-torizon-qt-1  | Response: "02 03 03 f0 00 02 c4 4f 02 03 04 5c 29 41 23 7b 22"
ipool-torizon-qt-1  | CRC mismatch!
ipool-torizon-qt-1  | Received CRC: 0x "227b"
ipool-torizon-qt-1  | Calculated CRC: 0x "2260"
ipool-torizon-qt-1  | Unexpected byte count in response: 3

Plase any suggestion will be apreciated