2

I'm trying to make SC16IS752 (SPI to UART converter) work in RS485 mode by using device tree overlay (without running C application at boot).

I took original overlay source for SC16IS752 from official sources at github:

sc16is752-spi1-overlay.dts

Without adding linux,rs485-enabled-at-boot-time; line it works like this: (RTS is always HIGH)

no device tree flag

I changed fragment@1 part like this and added RS485 (I also changed clock freq because I'm using 1.843200MHz crystal in my hardware, not 14.xxxxMHz):

fragment@1 {
    target = <&spi1>;
    frag1: __overlay__ {
        #address-cells = <1>;
        #size-cells = <0>;
        pinctrl-names = "default";
        pinctrl-0 = <&spi1_pins &spi1_cs_pins>;
        cs-gpios = <&gpio 18 1>;
        status = "okay";

        /* RS485 SUPPORT */
        linux,rs485-enabled-at-boot-time;
        rs485-rts-delay = <0 0>;
        /* RS485 SUPPORT END */

        sc16is752: sc16is752@0 {
            compatible = "nxp,sc16is752";
            reg = <0>; /* CE0 */
            clocks = <&sc16is752_clk>;
            interrupt-parent = <&gpio>;
            interrupts = <24 2>; /* IRQ_TYPE_EDGE_FALLING */
            gpio-controller;
            gpio-cells = <2>;
            spi-max-frequency = <4000000>;

            /* I also tried to put it here */
            /* but RTS is always HIGH */
            /* RS485 SUPPORT */
            /* linux,rs485-enabled-at-boot-time; */
            /* rs485-rts-delay = <0 0>; */
            /* RS485 SUPPORT END */

            sc16is752_clk: sc16is752_clk {
                compatible = "fixed-clock";
                #clock-cells = <0>;
                clock-frequency = <1843200>;
            };
        };
    };
};

I compiled dts file, I put it to /boot/overlays and I have added proper line in /boot/config.txt to use that overlay.

RTS line now is always LOW (without flag it was HIGH - see image above):

device tree flag set

So this flag has been read by kernel/driver, but it RTS does nothing when I'm sending data. It should work like this:

enter image description here

Last screenshot with working RS485 mode was taken after I turned RS485 mode in C program like this:

#include <fcntl.h>
#include <unistd.h>
#include <linux/serial.h>

/* Include definition for RS485 ioctls: TIOCGRS485 and TIOCSRS485 */
#include <sys/ioctl.h>

int main(int argc, char *artv[]){

        /* Open your specific device (e.g., /dev/mydevice): */
        int fd = open ("/dev/ttySC0", O_RDWR);
        if (fd < 0) {
                /* Error handling. See errno. */
                return -1;
        }

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

        rs485conf.delay_rts_before_send = 0;
        rs485conf.delay_rts_after_send = 0; // zero! nie obsługiwane przez SC16IS752

        if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) {
                /* Error handling. See errno. */
                return -2;
        }

        /* Use read() and write() syscalls here... */

        /* Close the device when finished: */
        if (close (fd) < 0) {
                /* Error handling. See errno. */
                return -3;
        }

        return 0;
}

So, the question is how to do it without C program?

Why setting rs485 mode in device tree is not enough?

I'm suspecting that the driver has no default configuration, and it has zeroes in both SER_RS485_RTS_ON_SEND and SER_RS485_RTS_AFTER_SEND flags. If this is true - (extra question) should I report issue with this driver in this situation?

Useful resources:


Edit/added later:

I also noticed, that when my overlay was applied at boot and I run this code (it is supposed to read flags from port, I'm not sure if it is correct).

    /* Reading rs485conf struct from port */
    if (ioctl (fd, TIOCGRS485, &rs485conf) < 0) {
            /* Error handling. See errno. */
            return -2;
    }

    printf("Before: ");
    binprintf(rs485conf.flags); // function that prints int as binary
    printf("\n");

all flags are empty. So... overlay changed RTS state after boot, but flags on port are zeroes? I don't understand.

0

In case anyone finds this question, like I did while trying to get a SC16IS752 working on I2C on a RPi, the answer is, in brief:

  • The sc16is7xx.c driver doesn't have any code calling uart_get_rs485_mode() as described in serial_core.c and which appears in several other serial drivers;
  • the device tree property rs485-rts-active-low appears only to have been added to RPi 5.3 and later kernels (see this commit).

To address the first issue, I have made a couple of small changes to the sc16is7xx.c driver, see this commit. The uart_get_rs485_mode() function picks up the relevant properties from the device tree and writes a struct serial_rs485, the same as used for the TIOCSRS485 ioctl. The changes haven't been regression tested, but it is currently working reliably for me on my breadboarded SC16IS752 interface.

This commit is based on the 5.4 kernel, so supports the rs485-rts-active-low property and therefore works nicely with my MAX3072 line drivers.

Here is a part of my overlay file:

fragment@1 {
    target = <&i2c_arm>;
    __overlay__ {
        #address-cells = <1>;
        #size-cells = <0>;
        status = "okay";

        sc16is752: sc16is752@48 {
            compatible = "nxp,sc16is752";
            reg = <0x48>; /* i2c address */
            clocks = <&sc16is752_clk>;
            interrupt-parent = <&gpio>;
            interrupts = <18 2>; /* IRQ_TYPE_EDGE_FALLING */
            gpio-controller;
            #gpio-cells = <2>;
            i2c-max-frequency = <400000>;
            linux,rs485-enabled-at-boot-time;
            rs485-rts-active-low;
        };
    };
};

And here is a scope image showing it working:

I2C and RS485 signalling

For reference: D8=SCL, D9=SDA, D10=_IRQ, D11=TXD, D12=_RTS, D13=RXD, Analog1=one RS485 line, Analog2=RXD (same as D13). The command response is coming from a remote ModBus device. Note that _RTS is high (3.3V) during transmit, as there is a double negation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.