0

I have been working on multiple projects of Embedded Linux from last 5 years but there is a small problem when I use UART. I am using STM32MP13F with MYIR board. The UART is connected to half duplex RS485. So I need to set/reset the RE pin before and after writing the uart tx data. When I write on UART like

ssize_t bytesWritten = write(uartFd_, data, length);

I need to check if the buffer is clear. I tried it via

tcdrain(uartFd_); // did not worked
// also below flags check did not worked
if(ioctl(uartFd_, TIOCSERGETLSR, &status) == -1){
    retVal = true;
}
if(status & TIOCSER_TEMT){
    retVal = true;
}

I also tried to access the UART from direct memory but it did not worked.

At last resort I have to put the delay

usleep((length-1)*86.8056); // 86.80 is calculated for 115200 baudrate.

to check if the tx has been completed before setting the RE pin. I believe in Linux UART TX flag is not cleared as it may the flag is been written to some file and when my program reads from that file, there is a delay. The responding system replies back instantly as it get the request so we cannot afford this delay.

I even tried to acccess the UART directly from memory but still same issue. May be making a kernel module that use UART as RS485 might help but still not sure about it.

Have you guys tried any solution of such scenarios of UART using like RS485 with Linux?

8
  • I can remember having to manage a half duplex modem, and I ended in using an old MS/DOS box to have full direct access to the UART. In Linux, the correct way would be a dedicated driver, but I was not experimented enough in Linux driver programming (and still am not) to go that way. Commented Oct 6 at 11:46
  • There are RS485 transceivers with "Automatic Direction Control" so maybe consider a hardware solution? Commented Oct 6 at 13:12
  • 3
    The stm32-usart Linux driver seems to support RS-485 already. It can be configured at run-time using the TIOCSRS485 and TIOCGRS485 ioctls (see RS485 Serial Communications), and default RS-485 configuration properties can be set in the device tree node for the UART (see rs485.yaml). Commented Oct 6 at 14:23
  • 1
    Please do not cross-post. As that other question is off-topic on SE/EE, I'd suggest to focus on this question. Commented Oct 6 at 19:43
  • This question is similar to: automatically changing RTS for RS-485 communication. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. Commented Oct 6 at 20:37

1 Answer 1

3

It's not "your job" to handle the line driver's "transmit enable" signal, that's for the driver and peripheral to deal with - hopefully even in hardware (i.e: driver configures peripheral and then doesn't do anything else).

Additionally, delays from within userspace will likely not do what you expect - you may well end up cutting your transmission short or stamping on the start of a response.


Linux has a well defined interface for dealing with RS-485 connections, documented here. If this isn't supported, then it would be worth implementing support for it in the driver (or possibly contacting the BSP provider and/or ST).

You can either set the interface up using stty, for example:

stty -F /dev/mydevice rs485 rs485rtsonsend

Or directly in your application, this sample is directly from the documentation linked above:

#include <linux/serial.h>

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

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

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;
/* or, set logical level for RTS pin equal to 0 when sending: */
rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND);

/* Set logical level for RTS pin equal to 1 after sending: */
rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
/* or, 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 = ...;

/* Set rts delay after send, if needed: */
rs485conf.delay_rts_after_send = ...;

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

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

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

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

But where we link the RE pin i.e. a random GPIO say PC13 to the RS485 driver?

If the implementation is good, you shouldn't need to care... it should ideally be connected to the UART peripheral, or the driver may be connected to a GPIO pin (via the device tree). If it's not, then you'll need to inspect the board schematics.


This is my answer, copied from the Electrical Engineering site, here

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.