I'm trying to use a Beaglebone Black running Angstrom (3.8 kernel) to communicate with devices on a half-duplex RS-485 network at 9600-N-8-1.
I'm trying to use an RS-485 Breakout board similar to this one: https://www.sparkfun.com/products/10124, except the chip is a MAX3485 http://www.maximintegrated.com/datasheet/index.mvp/id/1079. I bought the board pre-assembled with pins and a terminal strip. A friend of mine tested it with an oscilloscope and declared that the RS-485 board does work. The board has five pins that connect to the BBB. 3-5V (Power), RX-I, TX-O, RTS, and GND.
I've disabled HDMI support on the BBB so that the UART4_RTSn
and UART4_CTSn
pins will be available.
mkdir /mnt/boot
mount /dev/mmcblk0p1 /mnt/boot
nano /mnt/boot/uEnv.txt
#change contents of uEnv.txt to the following:
optargs=quiet capemgr.disable_partno=BB-BONELT-HDMI,BB-BONELT-HDMIN
Then I've found an overlay to enable UART-4 with RTS/CTS control:
/*
* Modified version of /lib/firmware/BB-UART4-00A0.dtbo to add RTS so we can reset Arduinos
*/
/dts-v1/;
/plugin/;
/ {
compatible = "ti,beaglebone", "ti,beaglebone-black";
part-number = "BB-UART4-RTS";
version = "00A0";
exclusive-use = "P9.13", "P9.11", "P9.15", "P8.33", "P8.35", "uart4";
fragment@0 {
target = <0xdeadbeef>;
__overlay__ {
pinmux_bb_uart4_pins {
pinctrl-single,pins = <
0x070 0x26 /* P9_11 = UART4_RXD = GPIO0_30, MODE6 */
0x074 0x06 /* P9_13 = UART4_TXD = GPIO0_31, MODE6 */
/* need to enable both RTS and CTS, if we only turn on RTS then driver gets confused */
0x0D0 0x26 /* P8_35 = UART4_CTSN = lcd_data12, MODE6 */
0x0D4 0x06 /* P8_33 = UART4_RTSN = lcd_data13, MODE6 */
/* 0x040 0x0F /* P9_15 = GPIO1_16 = GPIO48, MODE7 failed attempt to put DTR on gpio */
>;
linux,phandle = <0x1>;
phandle = <0x1>;
};
};
};
fragment@1 {
target = <0xdeadbeef>;
__overlay__ {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <0x1>;
};
};
__symbols__ {
bb_uart4_pins = "/fragment@0/__overlay__/pinmux_bb_uart4_pins";
};
__fixups__ {
am33xx_pinmux = "/fragment@0:target:0";
uart5 = "/fragment@1:target:0"; /* Not a mistake: UART4 is named uart5 */
};
__local_fixups__ {
fixup = "/fragment@1/__overlay__:pinctrl-0:0";
};
};
Compiled and Enabled the overlay:
cd /lib/firmware
dtc -O dtb -o BB-UART4-RTS-00A0.dtbo -b 0 -@ BB-UART4-RTS-00A0.dts
echo BB-UART4-RTS:00A0 > /sys/devices/bone_capemgr.*/slots
Hooked up the 485 board to the BB like this
3-5V to P9_05 (VDD_5V)
RX-I to P9_13 (UART4_TXD)
TX-O to P9_11 (UART4_RXD)
RTS to P8_33 (UART4_RTSn)
GND to P9_01 (DGND)
In python I'm trying to use the serial port like this:
import serial
ser = serial.Serial('/dev/ttyO4', baudrate=9600, rtscts=True)
ser.write(list_of_byte_dat)
I know the program works because when I use a USB to RS-485 converter on /dev/ttyUSB0
and set rtscts=False
the communication works in both directions just fine. But I can't get communication to work correctly using the RS-485 board.
I have two issues with the RS-485 board, both deal with RTS.
The RTS on the board works backwards from the way I expect it to. When I apply voltage on the RTS pin of the rs485 board the RTS led on the board goes off and the board will not transmit. When I remove voltage from the RTS pin the RTS led turns on and the board will transmit. How do I reverse the polarity of the UART_RTSn pin on the BBB?
Temporary solution: I've made a small bone script program that uses UART4_RTSn pin as input. It turns on a different GPIO when the UART4_RTSn pin is off and turns off that same GPIO pin when the UART4_RTSn pin is on. Then hooked up the RTS pin on the rs485 board to the GPIO pin instead of the UART4_RTSn pin.
This seems to be a poor solution, but it does make the RTS on the RS485 board come on at the correct time when echoing to the /dev/ttyO4
from the command line.
How can I change the polarity of the UART4_RTSn
pin either by adjusting the hardware configuration or by changing the configuration in pyserial?
This brings me to the second issue
As I stated in problem 1 the UART4_RTSn
pin will work automatically (but backwards) for me when echoing a value to the tty port like this:
echo -en '\x02\xFD\xCD......' > /dev/ttyO4
This will make the UART4_RTSn
led blink while the data is being transmitted. If I have it setup without the bonescript mentioned above, then it will be on normally and blink off while transmitting. If I use my bonescript hack then it will be off normally and blink on while transmitting (which is what I want). However this only works when using echo from the command line. When I use python and setup the serial port the UART4_RTSn
pin becomes inactive. It will not blink while transmitting. As soon as I make the statement in python:
ser = serial.Serial('/dev/ttyO4', baudrate=9600, rtscts=True)
The UART4_RTSn
pin shuts off and stays off. It does not blink when sending information using ser.write(stuff)
. As a result the rs485 board is not enabled for transmission. How do I get the UART4_RTSn
pin to work automatically in pyserial? I've tried setting rtscts=False
and it did not work.
I am able to use ser.setRTS(True)
or ser.setRTS(False)
to manually toggle the pin value so I know I'm using the correct pin and that it is being recognized. But I don't want to toggle the UART4_RTSn pin directly. I want it to work automatically when the serial port is transmitting data and it does when using echo, but not in Python.
Any help would be greatly appreciated.
RTS is normally an active low signal, I suspect the reason that you see data being transmitted with echo
is that it is not using RTS/CTS (leaving it high) and so is only able to transmit data.
According to a post on http://www.raspberrypi.org/phpBB3/viewtopic.php?f=26&t=29408
If you enable hardware flow control (CRTSCTS in "man termios", or "stty crtscts -F /dev/ttyAMA0", or pySerial rtscts=True), then sending will take place only when CTS is asserted. RTS will be asserted except when the kernel input buffer is full. The kernel input buffer is about one page or 4KB, so your application has to get well behind with its reads before RTS actually changes.
So check that CTS is being asserted (pulled to ground) outside of your board. However I don't think this will give you the right control over RTS that you need.
So, for your application you should disable hardware flow control (rtscts=False
) and manually control RTS with setRTS(1)
before a write and setRTS(0)
afterwards.
If you are still not seeing data get through to the device, try swapping the A & B wires - A/B labelling is (frustratingly) not consistent across RS485 devices. It is better to use D+/D- labelling if possible in your own applications.
Use ioctl to change the logic level...
98 struct serial_rs485 rs485conf;
99
100 /* Enable RS485 mode: */
101 rs485conf.flags |= SER_RS485_ENABLED;
102
103 /* Set logical level for RTS pin equal to 1 when sending: */
104 rs485conf.flags |= SER_RS485_RTS_ON_SEND;
105 /* or, set logical level for RTS pin equal to 0 when sending: */
106 rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND);
107
108 /* Set logical level for RTS pin equal to 1 after sending: */
109 rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
110 /* or, set logical level for RTS pin equal to 0 after sending: */
111 rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);
112
113 /* Set rts delay before send, if needed: */
114 rs485conf.delay_rts_before_send = ...;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With