一、先明確核心目標:修改要解決什么問題?
RS-485是半雙工通信,需要1個“方向控制GPIO”(高電平=發送,低電平=接收)。傳統方案中,應用層必須手動:
1.發送前設置GPIO高電平→切發送模式;
2.發送完成后等數據發完→再設GPIO低電平→切接收模式;
若時序錯(如沒等數據發完就切接收),必然丟包。
修改核心是:讓內核在“發送數據”的關鍵節點自動控制這個GPIO,應用層只需要調用write()發數據,無需管方向切換。

二、逐文件拆解修改:改了什么?為什么這么改?
1.設備樹修改(rk3576-evb1.dtsi):給UART綁定485控制GPIO
|
+&uart5 {
+ status = "okay";
+ pinctrl-names = "default";
+ 485_ctrl_gpio = <&gpio3 RK_PD6 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&uart5m0_xfer>;
+};
+
+&uart11 {
+ status = "okay";
+ pinctrl-names = "default";
+ 485_ctrl_gpio = <&gpio3 RK_PD7 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&uart11m0_xfer>;
+};
|
改了什么?
?給uart5和uart11兩個串口,各加1個485_ctrl_gpio屬性:
?uart5綁定gpio3的PD6引腳,uart11綁定gpio3的PD7引腳;
?GPIO_ACTIVE_HIGH表示:GPIO高電平時,485進入“發送模式”。
?啟用串口(status = "okay")并指定引腳配置(pinctrl-0 = <&uart5m0_xfer>)。
為什么這么改?
?設備樹是“硬件與驅動的橋梁”:需要告訴內核“哪個UART對應哪個GPIO”,否則驅動不知道該控制哪個引腳;
?后續驅動代碼(8250_dw.c)會通過of_get_named_gpio讀取這個屬性,建立UART與485控制GPIO的關聯。
2.驅動初始化修改(8250_dw.c):讀取GPIO配置并初始化
+#include+#includestatic int dw8250_probe(struct platform_device *pdev)struct device *dev = &pdev->dev;struct dw8250_data *data;struct resource *regs;+ struct device_node *nd = dev->of_node;int irq;int err;u32 val;+ int gpio_ctrl;static int dw8250_probe(struct platform_device *pdev)data->data.dma.fn = dw8250_fallback_dma_filter;data->pdata = device_get_match_data(p->dev);p->private_data = &data->data;+ gpio_ctrl = of_get_named_gpio(nd, "485_ctrl_gpio", 0);+ if (gpio_ctrl > 0)+ {+ data->flags = 0xabcd;+ data->dir_gpio_pin = gpio_ctrl;+ gpio_direction_output(gpio_ctrl, 0);+ gpio_set_value(gpio_ctrl, 0);+ }
改了什么?
1.新增頭文件:gpio.h和of_gpio.h是內核操作GPIO的必備接口;
2.讀取設備樹GPIO:通過of_get_named_gpio(nd, "485_ctrl_gpio", 0),從設備樹讀取你定義的485_ctrl_gpio引腳號;
3.初始化GPIO狀態:
?若讀取到有效GPIO(gpio_ctrl > 0),給dw8250_data結構體設標記(data->flags = 0xabcd,用于后續識別“這是485串口”);
?存儲GPIO引腳號(data->dir_gpio_pin = gpio_ctrl);
?設GPIO為輸出模式(gpio_direction_output),并初始化為低電平(gpio_set_value(gpio_ctrl, 0))→初始是“接收模式”,避免上電就誤發。
為什么這么改?
?這是“驅動層與硬件建立連接”的關鍵:設備樹只是“聲明”,驅動需要通過probe函數“讀取聲明并初始化硬件”;
?初始設為低電平(接收模式)是安全設計:防止設備上電時GPIO隨機電平導致485總線被占用,干擾其他設備。
3.數據結構擴展(8250_dwlib.h):存儲485控制狀態
struct dw8250_data {#endifunsigned int skip_autocfg:1;unsigned int uart_16550_compatible:1;+ int flags;+ int dir_gpio_pin;};
改了什么?
在dw8250_data結構體(RK平臺UART驅動的核心數據結構)中,新增兩個字段:
?flags:標記是否為485串口(用0xabcd作為識別值);
?dir_gpio_pin:存儲485方向控制GPIO的引腳號。
為什么這么改?
?內核驅動的“數據結構是狀態的載體”:dw8250_data原本只存UART基礎配置,現在要控制485,必須新增字段存儲“是否是485”和“控制哪個GPIO”;
?后續發送數據時(8250_port.c),需要通過這個結構體獲取GPIO信息,才能控制方向。
4.發送邏輯修改(8250_port.c):自動切換收發方向
+// #include "8250.h"+#include "8250_dwlib.h"+#include+#includevoid serial8250_tx_chars(struct uart_8250_port *up)struct uart_port *port = &up->port;struct circ_buf *xmit = &port->state->xmit;int count;+ struct dw8250_data* p_data = (struct dw8250_data*)(port->private_data);void serial8250_tx_chars(struct uart_8250_port *up)}count = up->tx_loadsz;+ if(0xabcd == p_data->flags)+ {+ if (gpio_get_value(p_data->dir_gpio_pin) != 1)+ {+ gpio_set_value(p_data->dir_gpio_pin, 1);+ printk("this uart is 485, set rts gpio %d value 1n", p_data->dir_gpio_pin);+ }+ }void serial8250_tx_chars(struct uart_8250_port *up)if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))- __stop_tx(up);+ {+ __stop_tx(up);+ if(0xabcd == p_data->flags)+ {+ unsigned int lsr;+ int loop_count = 200;+ while(loop_count)+ {+ loop_count--;+ lsr=serial_port_in(port,UART_LSR);+ if(((lsr & UART_LSR_TEMT) == UART_LSR_TEMT))+ break;+ mdelay(1);+ }+ if(loop_count<0)+ {+ printk("timeout wait 485 send %dn",p_data->dir_gpio_pin);+ }++ gpio_set_value(p_data->dir_gpio_pin, 0);+ printk("this uart is 485, set rts gpio %d value 0n", p_data->dir_gpio_pin);++ }+ }
改了什么?
這是最核心的“自動控制”邏輯,分兩個階段:
1.發送前:切到發送模式:
?先通過port->private_data拿到dw8250_data結構體(之前在probe函數中綁定);
?檢查flags == 0xabcd(確認是485串口),且GPIO當前不是高電平→設GPIO為高電平(gpio_set_value(1));
?打印日志,提示“485串口已切發送模式”。
1.發送后:切回接收模式:
?當發送緩沖區為空(uart_circ_empty(xmit)),先調用__stop_tx停止發送;
?然后循環檢查UART的LSR寄存器(serial_port_in(port,UART_LSR)):
等待UART_LSR_TEMT位(發送移位寄存器空)→確保硬件已把最后1個字節發完(避免數據殘留);
最多等200ms(loop_count=200),超時打印錯誤日志;
?最后設GPIO為低電平(gpio_set_value(0)),切回接收模式,打印日志。
為什么這么改?
?解決傳統應用層控制的“時序痛點”:應用層無法精確判斷“硬件是否真的發完數據”,而內核能直接讀UART寄存器(LSR),確保數據發完再切接收;
?200ms超時是容錯設計:防止硬件異常時GPIO一直處于發送模式,阻塞總線。
三、修改帶來的3個核心優勢(純代碼層面總結)
1.應用層徹底解放:無需再寫GPIO控制代碼(如ioctl設GPIO電平、猜延時等),調用write()發數據即可,內核自動搞定方向切換;
2.時序絕對精準:通過讀取UART硬件寄存器(LSR_TEMT)判斷發送完成,比應用層usleep(靠經驗猜延時)可靠100%,不會丟包;
3.硬件適配靈活:若換485控制引腳,只需改設備樹(dtsi)的485_ctrl_gpio,驅動和應用層無需動→符合“硬件與軟件解耦”的內核設計思想。
四、開發者需注意的2個細節
1.GPIO引腳唯一性:uart5用GPIO3_PD6、uart11用GPIO3_PD7,需確保這兩個GPIO沒被其他硬件(如SPI、I2C)占用,否則會導致引腳沖突;
2.超時參數調整:loop_count=200(200ms)是通用值,若485波特率極低(如2400),1個字節發送時間長,可適當增大loop_count,避免超時誤判。
-
內核
+關注
關注
4文章
1467瀏覽量
42869 -
RS-485
+關注
關注
11文章
748瀏覽量
86688 -
rk3576
+關注
關注
1文章
265瀏覽量
1546
發布評論請先 登錄
米爾RK3576和RK3588怎么選?-看這篇就夠了
【米爾RK3576開發板評測】+項目名稱值得購買的米爾RK3576開發板
RK3576 vs RK3588:為何越來越多的開發者轉向RK3576?
Mpp支持RK3576么
RK這2款旗艦芯片RK3588 PK RK3576,誰是最優選
【作品合集】米爾RK3576開發板測評
【作品合集】靈眸科技EASY EAI Orin Nano(RK3576)開發板測評
新品體驗 | RK3576開發板
RK3576單板發布倒計時:RK3399與RK3576對比
RK3588與RK3576區別解析
瑞芯微RK3576與RK3576S有什么區別,性能參數配置與型號差異解析
RK3576內核485控制引腳修改解析
評論