來源:公眾號(hào)【魚鷹談單片機(jī)】
本筆記將收錄到一條龍代碼倉庫
聽說,你被一條龍服務(wù)過?
對(duì)于?RS485 ,大家應(yīng)該都很熟悉了,在 modbus 協(xié)議中最常用。
但最近魚鷹在調(diào)試 RS485 時(shí)遇到了不少問題。
首先,我們知道 RS485 屬于半雙工,同一時(shí)刻,只能收或者發(fā)。
像這種情況,我們需要一種機(jī)制控制它的收發(fā),既可以是軟件,也可以是硬件。
硬件
一般RS485芯片,都會(huì)提供一個(gè)控制引腳完成收發(fā)工作。
這個(gè)引腳可能由用戶控制,也可能由芯片自動(dòng)控制(一般比較貴),還有第三種可能是,設(shè)計(jì)一種自動(dòng)收發(fā)的電路(應(yīng)該沒有超脫三界之外的吧)。
自動(dòng)收發(fā)電路很多,有用三極管控制的,也有用緩沖器的(好像是吧);因?yàn)?UART_TX 空閑電平為高,因此三極管導(dǎo)通,RE 接地,默認(rèn)為發(fā)。
但是我還看過一種用 S8550 實(shí)現(xiàn)的電路,基極接地,三極管默認(rèn)(空閑)不導(dǎo)通。
我不是電子工程師,無法判斷兩者電路的好壞,但我感覺默認(rèn)不導(dǎo)通更好,功耗更低,另外即使引腳未配置(剛上電,肯定沒那么快配置),那么也不會(huì)影響到總線上的其它節(jié)點(diǎn)通信,這個(gè)涉及到各個(gè) 485 節(jié)點(diǎn)上電時(shí)序問題。
這種電路好像因?yàn)槿龢O管開關(guān)頻率限制,無法做到很高頻率(一般115200,市面上購買的模塊基本都是類似這種電路)。
總之一句話,電路需要默認(rèn)處于接收狀態(tài),保證不會(huì)干擾總線通信,否則只要其中一個(gè)節(jié)點(diǎn)默認(rèn)狀態(tài)不是接收,那么無法通信(因此,如果無法通信,不如查一查是否有節(jié)點(diǎn)狀態(tài)不對(duì),而不是首先懷疑芯片燒掉了)。
另外其中一個(gè)節(jié)點(diǎn)發(fā)送,其它節(jié)點(diǎn)必須保持接收,不要發(fā)送數(shù)據(jù),否則數(shù)據(jù)肯定不是你想要的。
這種有頻率限制,那是否有那種頻率沒限制,同時(shí)不需要買貴的 RS485 自動(dòng)收發(fā)芯片的方案呢?
還真有,這要看你的 MCU 芯片本身是否支持了。
比如 IMX93,就可以實(shí)現(xiàn)(參考 IMX93RM.pdf,62.3.4)。
官網(wǎng)論壇也有對(duì)此實(shí)現(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 = <
MX93_PAD_GPIO_IO08__LPUART7_TX 0x31e // TX
MX93_PAD_GPIO_IO09__LPUART7_RX 0x31e // RX
MX93_PAD_GPIO_IO11__LPUART7_RTS_B 0x31e // DE
>;
};
&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>;
};
當(dāng)你發(fā)送數(shù)據(jù)時(shí),DE 引腳會(huì)被芯片自動(dòng)控制電平狀態(tài)(默認(rèn)為低電平),從而實(shí)現(xiàn)不需要由用戶控制的自動(dòng)收發(fā)功能,并且沒有頻率限制(但受串口自身頻率限制)。
?軟件
說完硬件,再說軟件。
其實(shí)很多軟件或驅(qū)動(dòng)是會(huì)帶上 DE 引腳的控制(硬件挖坑,軟件負(fù)責(zé)填坑),比如如果你的開發(fā)板不支持上面的功能,那大概率這顆芯片的Linux串口驅(qū)動(dòng)會(huì)帶上該功能。
還有開源軟件 libmodbus 也考慮了這個(gè)引腳的控制。只是從效率和省心的角度,當(dāng)然是自動(dòng)收發(fā)爽了。
為了使用 485 的功能,不管 MCU 是否支持,都需要使能 485 模式,只是可能參數(shù)上需要進(jìn)行調(diào)整。下面是硬件支持的配置:
#include?<linux/serial.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
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 -echo
echo?"dddd"?>?/dev/ttyLP6
系統(tǒng)
從系統(tǒng)角度,我們也要知道一些基礎(chǔ)知識(shí)。
比如,串口設(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)啟動(dòng)信息查看掛載的串口:
dmesg | grep -i fsl-lpuart
剛插入的設(shè)備不知道掛載在哪個(gè)/dev/tty* 目錄下,同樣可以通過上面的類似命令找到:
dmesg?|?grep?-i?tty
設(shè)備樹節(jié)點(diǎn)查看路徑:
/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ū)動(dòng),遠(yuǎn)比 MCU 環(huán)境更復(fù)雜,需要掌握知識(shí)也更多。
希望本篇筆記對(duì)大家填坑有所幫助,歡迎一鍵三連支持魚鷹。