對于 RS485 ,大家應(yīng)該都很熟悉了,在 modbus 協(xié)議中最常用。
但最近魚鷹在調(diào)試 RS485 時遇到了不少問題。
首先,我們知道 RS485 屬于半雙工,同一時刻,只能收或者發(fā)。
像這種情況,我們需要一種機制控制它的收發(fā),既可以是軟件,也可以是硬件。
硬件
一般RS485芯片,都會提供一個控制引腳完成收發(fā)工作。
這個引腳可能由用戶控制,也可能由芯片自動控制(一般比較貴),還有第三種可能是,設(shè)計一種自動收發(fā)的電路(應(yīng)該沒有超脫三界之外的吧)。
自動收發(fā)電路很多,有用三極管控制的,也有用緩沖器的(好像是吧),電路大致如下:
圖來源于網(wǎng)絡(luò)
因為 UART_TX 空閑電平為高,因此三極管導(dǎo)通,RE 接地,默認(rèn)為發(fā)。
但是我還看過一種用 S8550 實現(xiàn)的電路,基極接地,三極管默認(rèn)(空閑)不導(dǎo)通。
我不是電子工程師,無法判斷兩者電路的好壞,但我感覺默認(rèn)不導(dǎo)通更好,功耗更低,另外即使引腳未配置(剛上電,肯定沒那么快配置),那么也不會影響到總線上的其它節(jié)點通信,這個涉及到各個 485 節(jié)點上電時序問題。
這種電路好像因為三極管開關(guān)頻率限制,無法做到很高頻率(一般115200,市面上購買的模塊基本都是類似這種電路)。
總之一句話,電路需要默認(rèn)處于接收狀態(tài),保證不會干擾總線通信,否則只要其中一個節(jié)點默認(rèn)狀態(tài)不是接收,那么無法通信(因此,如果無法通信,不如查一查是否有節(jié)點狀態(tài)不對,而不是首先懷疑芯片燒掉了)。
另外其中一個節(jié)點發(fā)送,其它節(jié)點必須保持接收,不要發(fā)送數(shù)據(jù),否則數(shù)據(jù)肯定不是你想要的。
這種有頻率限制,那是否有那種頻率沒限制,同時不需要買貴的 RS485 自動收發(fā)芯片的方案呢?
還真有,這要看你的 MCU 芯片本身是否支持了。
比如 IMX93,就可以實現(xiàn)(參考 IMX93RM.pdf,62.3.4)。
官網(wǎng)論壇也有對此實現(xiàn)方法:
https://community.nxp.com/t5/i-MX-Processors/RS485-iMX93/m-p/1896764#M225674
特別注意這里使用 RTS 引腳,而不是 CTS(其它芯片可能是用RTS)。
設(shè)備樹配置如下(一般在arch/arm64/boot/dts/freescale/xxx 下,引腳描述文件
pinctrl_uart7: uart7grp {fsl,pins =
;};&lpuart7 { uart-has-rtscts; rts-gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>; linux,rs485-enabled-at-boot-time;
// rs485 abilitata fin da subito al boot rs485-rts-active-high; rs485-rts-delay = <1 1>;};
最終的效果是:
來源官網(wǎng)論壇
當(dāng)你發(fā)送數(shù)據(jù)時,DE 引腳會被芯片自動控制電平狀態(tài)(默認(rèn)為低電平),從而實現(xiàn)不需要由用戶控制的自動收發(fā)功能,并且沒有頻率限制(但受串口自身頻率限制)。
軟件
說完硬件,再說軟件。
其實很多軟件或驅(qū)動是會帶上 DE 引腳的控制(硬件挖坑,軟件負(fù)責(zé)填坑),比如如果你的開發(fā)板不支持上面的功能,那大概率這顆芯片的Linux串口驅(qū)動會帶上該功能。
還有開源軟件 libmodbus 也考慮了這個引腳的控制。只是從效率和省心的角度,當(dāng)然是自動收發(fā)爽了。
為了使用 485 的功能,不管 MCU 是否支持,都需要使能 485 模式,只是可能參數(shù)上需要進(jìn)行調(diào)整。下面是硬件支持的配置:
#include #include #include #include #include int fd = open("/dev/ttyLP7", O_RDWR); if (fd < 0) { } struct serial_rs485 rs485conf = {0}; /* 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); /* Set delays for RTS if needed */ rs485conf.delay_rts_before_send = 0; rs485conf.delay_rts_after_send = 0; /* Enable full-duplex mode if supported */ // rs485conf.flags |= SER_RS485_RX_DURING_TX; if (ioctl(fd, TIOCSRS485, &rs485conf) < 0) { /* Error handling */ }
Python代碼
port = serial.Serial(port="/dev/ttyLP6", baudrate=115200, timeout=2, write_timeout=2)port.rs485_mode = serial.rs485.RS485Settings()
shell 腳本,注意關(guān)閉回顯:
stty -F /dev/ttyLP6 115200 cs8 -cstopb -parenb -echoecho "dddd" > /dev/ttyLP6
系統(tǒng)
從系統(tǒng)角度,我們也要知道一些基礎(chǔ)知識。
比如,串口設(shè)備名稱一般是 /dev/tty*。
通過命令 udevadm info 可以反查看該設(shè)備的硬件(設(shè)備樹)信息:
udevadm info /dev/tty* |grep DEVPATH=/devices/platform
輸出:
DEVPATH=/devices/platform/soc/107d001000.serial/tty/ttyAMA10
這樣我們可以知道,ttyAMA10 設(shè)備是芯片上的 107d001000 串口。
另外,我們可以通過系統(tǒng)啟動信息查看掛載的串口:
dmesg | grep -i fsl-lpuart
剛插入的設(shè)備不知道掛載在哪個/dev/tty* 目錄下,同樣可以通過上面的類似命令找到:
dmesg | grep -i tty
設(shè)備樹節(jié)點查看路徑:
/sys/firmware/devicetree/base/soc@xx/bus@xxx/serial@xxxx
腳本直接控制 IO:
gpioset gpiochip0 14=1 gpioset gpiochip0 14=0
這里面的 gpiochip0 和 14 也不是隨便選的,并且 chip 的選擇和我們常規(guī)的想法不同,具體可看 SDK 文檔說明。
設(shè)備樹編譯:
dtc -I dts -O dtb -o /path/to/your-device-tree.dtbo /path/to/your-device-tree.dts
總之,要在 Linux 環(huán)境中調(diào)試好串口驅(qū)動,遠(yuǎn)比 MCU 環(huán)境更復(fù)雜,需要掌握知識也更多。