Skip to content

kinetis/serial: Enable RTS as GPIO for RS485CONTROL#18417

Open
ghnotgood wants to merge 1 commit intoapache:masterfrom
ghnotgood:master
Open

kinetis/serial: Enable RTS as GPIO for RS485CONTROL#18417
ghnotgood wants to merge 1 commit intoapache:masterfrom
ghnotgood:master

Conversation

@ghnotgood
Copy link
Contributor

@ghnotgood ghnotgood commented Feb 20, 2026

When using CONFIG_UART?_RS485CONTROL, RTS pin is used as a transmit enable pin, i.e., it is set high when sending data and low otherwise. PIN_UART?_RTS is defined in the board.h file as the appropriate ALT functionality of the chip's port.

However, it may happen that PIN_UART?_RTS is wired to another pin of the chip that does not support the RTS as ALT functionality of the UART? in question. This commit addresses such a situation.

When UART?_RS485CONTROL_RTSISGPIO is set in menuconfig for the given UART?, it is expected that the PIN_UART?_RTS is defined as GPIO_OUTPUT, and the PIN_UART?_RTS is set high when sending data and low otherwise.

Summary

When working on NuttX port for our board, I found out that RTS of our UARTs 1, 2, and 3 are wired to Kinetis K60's pins that do not support RTS as an alternate functionality of the given UARTs. It looks like that in the original system of our board, RTSs were wired to GPIOs that were set to high when sending data and low otherwise. This is not considered in NuttX -- it is expected that only pins that support ALT functionality are used.

The commit in this PR allows any chip's pin that is defined as PIN_UART?_RTS and configured as GPIO_OUTPUT to work as RTS for the RS485CONTROL.

Impact

  • There is new UART configuration option UART?_RS485CONTROL_RTSISGPIO for each ? in 0 to 5. Each option depends on the appropriate UART?_RS485CONTROL.

  • The up_send procedure of the kinetis_serial.c is changed. If the configuration option is enabled for the given UART, the appropriate GPIO is set high before up_serialout and set low after 150 us delay when up_serialout returned.

Testing

On our board I work on, I configured CONFIG_UART1_RS485CONTROL_RTSISGPIO=y and CONFIG_UART2_RS485CONTROL_RTSISGPIO=y. UART0 is mapped to /dev/ttyS1 and UART1 is mapped to /dev/ttyS2. Then, I run the following test code:

static const char *devusart1 = "/dev/ttyS1";
static const char *devusart2 = "/dev/ttyS2";

int main(int argc, char **argv)
{
        _info("test from %s to %s", devusart1, devusart2);

        int from = open(devusart1, O_RDWR);
        if (0 > from) {
                _err("ERROR: failed to open %s: %s", devusart1, strerror(errno));
                return 1;
        } else {
                _info("from is %d", from);
        }   

        int to = open(devusart2, O_RDWR);
        if (0 > to) {
                _err("ERROR: failed to open %s: %s", devusart2, strerror(errno));
                return 1;
        } else {
                _info("to is %d", to);
        }   

        int i, r;
        uint8_t sb, rb; 

        for (i = 0; i < 260; i++) {
                sb = i;

                r = write(from, &sb, 1); 

                _info("(%d.) wrote %d byte(s): %d", i, r, sb);
                _info("now, try to read it");

                r = read(to, &rb, 1); 

                _info("read %d byte(s): %d", r, rb);

                rb++;

                _info("incremented what was read, sending back");

                r = write(to, &rb, 1); 

                _info("back %d byte(s): %d", r, rb);
                _info("now, try to back-read it");

                r = read(from, &sb, 1); 

                _info("back-read %d byte(s): %d", r, sb);

                if (1 + i == sb && 1 + i == rb) {
                        _info("GOOD %d -> %d == %d", i, sb, rb);
                } else {
                        _info("BAAD %d -> %d == %d", i, sb, rb);
                }   
        }   

        close(from);
        close(to);
        return 0;
}

and checked that the program finishes and the printed output makes sense.

When using CONFIG_UART?_RS485CONTROL, RTS pin is set high when sending
data and low otherwise. PIN_UART?_RTS is defined in the board.h file as
the appropriate ALT functionality of the chip's port.

However, it may happen that PIN_UART?_RTS is wired to another pin of the
chip that does not support the RTS as ALT functionality of the UART?  in
question. This commit addresses such a situation.

When UART?_RS485CONTROL_RTSISGPIO is set in menuconfig for the given
UART?, it is expected that the PIN_UART?_RTS is defined as GPIO_OUTPUT,
and the PIN_UART?_RTS is set high when sending data and low otherwise.

Signed-off-by: Jiri Vlasak <jvlasak@elektroline.cz>
@github-actions github-actions bot added Arch: arm Issues related to ARM (32-bit) architecture Size: M The size of the change in this PR is medium labels Feb 20, 2026
@cederom
Copy link
Contributor

cederom commented Feb 21, 2026

Thank you @ghnotgood :-)

  • Can you please provide runtime build and test logs? Build log can be short build invocation and summary to show that firmware builds fine. Runtime log should contain commands and their results from boot, uname -a, and your testing application run and execution.
  • So you are manually asserting the RTS pin before and deasserting after sending data. Are you sure that receive mode is available right after boot? Do you deassert RTS pin (low) by the custom board init or this is done by up_setup()?

{
/* We need some time before RTS is set low. 150 us works for LTM2881. */

up_udelay(150);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to disable the GPIO once UART_S1_TC interrupt is received instead of magic 150 us wait.

@ghnotgood
Copy link
Contributor Author

So we discussed with @michallenc and collegues offline. (Thanks for the discussion!) The problem with up_udelay(150) is that 150 us works for a single character on 115200 and that a delay in an interrupt handler is not good.

However, an oscilloscope shows that UART_S1_TC does not trigger deterministically and I was not able to find out how to generate interrupt after the sending of the bits is done.

The idea behind this PR is to set an GPIO to 1 when sending (up_send procedure) and set the GPIO to 0 when finished. I have explored the following potential solutions for setting GPIO to 0:

  • up_udelay in the handler is not good
  • spawn LPWORK thread with up_udelay could work, but some HPWORK may delay it
  • spawn HPWORK thread with up_udelay sets GPIO to 0 before the bits are actually sent
  • spawn HPWORK thread with up_udelay after 1 tick delay sets GPIO to 0 after 12 ms because it depends on USEC_PER_TICK
  • the same as above for HPWORK with nxsched_usleep

The current idea is that I will try to use Kinetis' timers (FTM or PIT or maybe LPTMR), but I have no experience with that and I am afraid that the lower-half drivers are not implemented. If anyone has any idea, I am happy for suggestions.

@cederom I will include the required logs next time, sorry.

So you are manually asserting the RTS pin before and deasserting after sending data. Are you sure that receive mode is available right after boot?

This PR applies only for CONFIG_UART?_RS485CONTROL, where RTS is used as RS-485 transmit enable. It should not affect receiving.

Do you deassert RTS pin (low) by the custom board init or this is done by up_setup()?

I expect something like the following in the include/board.h:

#define PIN_UART0_RTS (GPIO_OUTPUT | PIN_PORTC | PIN18)

which is implicilty the same as:

#define PIN_UART0_RTS (GPIO_OUTPUT | GPIO_OUTPUT_ZERO | PIN_PORTC | PIN18)

but that is a responsibility of a developer, I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: arm Issues related to ARM (32-bit) architecture Size: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants