Beaglebone Black UART RS485 communication

Hello Everyone out there,
I am using Beagle-bone black and i have a PCB connected to it, in which i have RS485 driver and i want to transmit and receive modbus frame in c code.
in this i have to set enable pin of rs485 high before transmit and low after transmit but the problem is that transmission function is running but before transmitting full frame pin sets low in between them i just want 4.2milisecond delay but i am not able to do this, i tried mutex, sleep(), usleep(), nanosleep() function but nothing is happening according to my requirement can anyone have idea how to solve this issue?

I don’t use 485 and have not verified for accuracy the code I just found on the internet and posted. However do use termios.h with uart/usb.

If this works then build upon on it
You will need to use these in addition to the code posted.


It is best to use the simplest code to verify you actually do have an open com channel, then move forward one small step at a time.

Test that and if it works keep on moving forward.
Do the threading last after you have Tx and Rx working. Besure to test your arrays, strings, buffers, parsing before moving on to thread. Any issue will be very difficult to find when the workers are active.

#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>

void main (void)
    int fd;
    fd = open ("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
    if (fd == -1)
        printf ("\n Error! in Opening ttyUSB0 ");
        printf ("\n ttyUSB0 Opened Successfully ");

    struct termios SerialPortSettings;
    tcgetattr (fd, &SerialPortSettings);
    cfsetispeed (&SerialPortSettings, B4800);
    cfsetospeed (&SerialPortSettings, B4800);
    SerialPortSettings.c_cflag &= PARENB;
    SerialPortSettings.c_cflag &= ~CSTOPB;
    SerialPortSettings.c_cflag &= ~CSIZE;
    SerialPortSettings.c_cflag |= CS7;
    SerialPortSettings.c_cflag &= ~CRTSCTS;
    SerialPortSettings.c_cflag |= CREAD | CLOCAL;
    SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);
    SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    SerialPortSettings.c_oflag &= ~OPOST;
    SerialPortSettings.c_cc [VMIN] = 1;
    SerialPortSettings.c_cc [VTIME] = 0;
    if ((tcsetattr (fd, TCSANOW, &SerialPortSettings)) != 0)
        printf ("\n ERROR ! in Setting attributes");
        printf ("\n BaudRate = 9600 \n StopBits = 1 \n Parity = none");

    int RTS_flag, DTR_flag;
    RTS_flag = TIOCM_RTS;
    DTR_flag = TIOCM_DTR;
    ioctl (fd, TIOCMBIC, &RTS_flag);
    ioctl (fd, TIOCMBIC, &DTR_flag);

    tcflush (fd, TCIFLUSH);
    char write_buffer [] = "///?01!";
    write (fd, write_buffer, sizeof (write_buffer));

I have tried it, but it is not meeting my requirements. When I check the signal in the DSO, I find out that the enable pin is not going low exactly after the transmission. Sometimes there is a significant delay, and at other times, the delay is minimal and also the pin goes low even before the transmission is complete, and sometimes it happens very late.

You might have to optimize it.
Break it down into read only then test, then do write tests.
I am of no help on this since I don’t use 485, be sure you are using fflush() and you might have to try fsync(). All is a just a guess on this. Are you using file descriptor or file pointer?

Additionally, check your scope, how is it being triggered? Using the decoder on the scope are you able to decode a hex value byte?

Another thought, if you have a usb to ttl adapter connect that and then prove out your code using the uart/USB. When you can Tx and Rx reconfigure termios for RS-485. At least you will know your code is working and now you can move forward with other possibilities. Device tree conflict, it is hard to say until you know for fact you can at least have comm both ways on the uart.

I tried all, but nothing is working.
May be this is happening because AM335x is multiprocessing and when i run C program, functions of that program runs not sequentially it does not wait one function to run completely.

Just doing a basic read and write you don’t need a semaphore or a lock because the FD file descriptor is not being altered. The UART is full duplex so you can send and receive at the same time.

How far along are you, are you able to send a string out. You need a base line and sending a string will indicate that you have termios.h configured properly. Start out very simple then build up on that.

Are you sure that board will do RS485 natively, that is a differential pair for tx and rx.

Yes, i am able to send frame(i checked it in non-solated DSO). after transmission the enable pin(BBB P9_27) have to set low, that time the problem arises pin is not setting low properly.

i have transmission function and enable pin function separately. let me explain you the flow

enable pin high → transmission function → pin low → receive function.

facing issues with the enable pin going low; sometimes it goes low too quickly, and other times it’s going low late.

I think you will struggle with trying to control the pin separately if you need precise timings.

If the signal is going low too soon, are you taking into account the time is takes to shift out your data before delaying for the 4.2ms ? Also the problem could be, just because the write call returns, does not mean the data has actually started to be transmitted. It may just be buffered up… You don’t mention any baud rates.

As for the signal going low too late, you only mention 4.2ms. What is the minimum and maximum time ? If you need exactly 4.2ms, it is highly unlikely that you will achieve that running C on top of Linux. Using one of the PRU’s would certainly work however. Using usleep() to delay can work, but the delay you specify is only a minimum and could be a lot longer. So again if you need precise timings this approach won’t work.

Have you thought about using a different RS485 transceiver and have the receive permanently enabled ? It would mean that you also receive every byte you send, which can be useful, especially if you have multiple masters in the bus, but you then don’t need the strict timings.

I tried with 3 baud rates and they are 9600, 19200, 115200
currently i am not using delay in my code. i tried to put delay(usleep(4200)) between transmission() and enable pin low() but the timing of the delay varies, it is not accurate

Write some code to directly control that pin. Control it so you have a pre-determined duty cycle then monitor that continuous pulse stream on a scope. Go into the frequency domain using FFT and you can see the approximate accuracy of your generated pulse. If the delta in frequency is larger than your current spec its not going to ever happen.

If that is the case consider doing what @benedict.hewson suggested with the PRU. However, pretty sure that you will still need a RS485 transceiver on the board.

I resolved the problem by putting delay in Tx function.
thank you everyone for attention.

i have one more question i am adding it in link please help me if anyone can.

1 Like