嵌入式系統應用中實現RS485的方向切換

RS485接口具有良好的抗噪聲干擾性能、長傳輸距離和多站能力等特點,使其成爲工業控制的首選串行接口嵌入式系統中也廣泛採用RS485接口作爲設備控制的串行接口。RS485採用兩線差分的接線方式進行串口數據的傳輸。由於發送和接收都是採用這兩根差分線進行,因此它是半雙工工作模式。基於RS485的特點,分別講述了通過硬件方式和軟件方式來實現RS485發送和接收方向的切換,重點解決了DM8168嵌入式平臺上軟件實現RS485方向切換的功能。

RS485總線是工業應用中非常成熟的技術,是現代通信技術的工業標準之一。RS485總線用於多站互連十分方便,用一對雙絞線即可實現,採用平衡發送和差分接收,即在發送端驅動器將TTL電平信號轉換成差分信號輸出,在接收端接收器將差分信號變成TTL電平,因此具有抗共模干擾的能力。根據RS485標準,傳送數據速率達100 kb/s時通信距離可達1200 m。

RS485在嵌入式系統中的應用非常廣泛。嵌入式系統可以通過RS485接口來控制終端設備。由於RS485是半雙工模式,因此發送和接收的方向切換需要我們的關注和研究。如果方向切換方式選擇不好可能會導致RS485驅動能力下降、軟件執行效率下降,甚至導致系統異常等問題。

本文分別給出硬件實現RS485方向切換和軟件實現RS485方向切換兩種方式。兩種方式各有優點,硬件方式控制起來比較簡單。軟件方式的驅動能力更好,但是和嵌入式平臺關係比較密切,不同的平臺都需要調試和驗證。

1 硬件方式控制RS485方向

圖1所示爲硬件控制RS485的電路圖。電路中使用2N7002LT1G MOS場效晶體管把UART_TXD_485這個MCU輸出的RS485發送信號邏輯取反後送給RS485芯片的RE/DE PIN腳。控制的原理是,當UART_TXD_485輸出低電平時RS485芯片的DE使能;輸出高電平時RE使能。默認情況下UART_TXD_485是高電平,RS485芯片處於接收狀態。發送數據時,UART_TXD_485上面有高低電平信號變化,低電平信號通過RS485芯片SP3072EENL/TR直接輸出,高電平信號通過外部上下拉電阻來控制。

這種方法的優點是控制簡單,軟件不需要做額外的工作,控制RS485像控制RS232一樣。但是這種方法的缺點是驅動能力可能不足,由於這種控制方法沒有完全發揮出RS485驅動芯片自身的驅動能力,輸出信號依賴於外部上下拉電阻,因此在複雜環境下,譬如很多負載需要控制時,就會存在驅動能力不足的問題。但是在一些簡單的環境或者軟件實現較複雜的平臺下,使用這種方法還是切實可行的。

圖1 硬件控制RS485電路圖
圖1 硬件控制RS485電路圖

2 軟件方式控制RS485方向

2.1 驅動能力分析

在複雜的RS485控制環境下,用上面介紹的硬件方式來控制RS485的方向會存在比較突出的驅動能力不足的問題。修改上述控制方法,將TTL這一側的2線控制改爲3線控制,就是將收發控制信號不用當前的/TXD來控制,而從主控分出一根GPIO線來控制收發。

按照輸出電流計算,3線控制方式相對用2線控制的總線上下拉作爲輸出的方式,其驅動能力提高了25~50倍(不同廠家不同型號有差異),如果輔以終端電阻靈活配置的措施,RS485的驅動能力將完全不是問題。表1是兩種控制方式驅動能力的對比。

2.2 軟硬件環境

圖2 軟件控制方法中的硬件設計
圖2 軟件控制方法中的硬件設計

軟件控制方法採用圖2的硬件設計,圖中很突出的修改是使用MCU的GPIO來控制RE和DE。RS485芯片的供電採用5 V供電,提高驅動能力。RS485芯片的RE和DE控制使用MCU的GPIO輸出高低電平來控制。簡單來說就是,在RS485進行數據傳輸時,通過GPIO來控制傳輸方向。這裏採用的MCU是TI公司的DM8168處理器來實現軟件的RS485切換功能。軟件版本使用UBoot2010.06和linux2.6.37。用軟件來實現RS485的收發,儘量要保證執行效率;要達到上面的目的就需要對串口驅動進行調試,使用串口驅動用到的軟件資源和串口控制器本身的硬件資源來實現RS485的控制。

表1 軟件和硬件控制方式驅動能力的對比
表1 軟件和硬件控制方式驅動能力的對比

2.3 UBoot代碼修改

需要修改的文件:

① board/ti/ti8168/evm.c

② drivers/serial/ns16550.c

③ include/configs/ti8168_evm.h

ti8168_evm.h文件中增加切換宏定義:

#define CONFIG_RS485_DIR_SW 1

evm.c文件中增加切換函數:

void rs485_dir_sw(int rs485_dir){

if (rs485_dir ==0)

_raw_writel(RS485_DIR_MASK, TI81XX_GPIO1_CLEARDATAOUT);

else

_raw_writel(RS485_DIR_MASK, TI81XX_GPIO1_SETDATAOUT);

}

s16550.c串口驅動文件中增加RS485方向控制:

void NS16550_putc(NS16550_t com_port, char c){

#ifdef CONFIG_RS485_DIR_SW

rs485_dir_sw(1);

#endif

……//此處代碼省略

#ifdef CONFIG_RS485_DIR_SW

while((serial_in(&com_port->lsr) & UART_LSR_TEMT) == 0)

rs485_dir_sw(0);

#endif

}

  其中UART_LSR_TEMT表示發送BUF和移位寄存器爲空。默認情況下RS485是接收狀態,一旦要發送數據,就把RS485切換爲發送狀態。發送完數據後,等待發送BUF和移位寄存器爲空,然後切換回接收狀態,這裏無需使用timeout。

2.4 Linux代碼修改

需要修改的文件:

① arch/arm/machomap2/bordti8168evm.c

② drivers/serial/omapserial.c

③ include/linux/serial_core.h

serial_core.h文件,uart_port結構體中增加set_rs485_direction函數指針,用於執行RS485的方向切換void (*set_rs485_direction)(int rs485_dir);原本考慮在uart_ops結構體中增加的,但是這個結構體是常量類型,對它不作改動,因此加到了uart_port結構體中。在該文件中添加相關宏定義和函數指針類型用於函數註冊:

#define SET_RS485_RX0

#define SET_RS485_TX1

typedef void (*set_rs485_direction_t)(int rs485_dir);//用於函數註冊

omapserial.c文件主要做了如下幾點改動:

① 添加omap_rs485_dir_fun全局的函數指針。

static set_rs485_direction_t omap_rs485_dir_fun[OMAP_MAX_HSUART_PORTS]={NULL, NULL, NULL, NULL, NULL, NULL}

② 外部驅動利用omap_rs485_dir_fun_reg註冊函數對omap_rs485_dir_fun進行賦值。

void omap_rs485_dir_fun_reg(int port_num, set_rs485_direction_t rs485_dir_fun){

if (port_num>=OMAP_MAX_HSUART_PORTS)

printk(KERN_ERR “%s, port_num error max is %d, but %d \\n”, __FUNCTION__, OMAP_MAX_HSUART_PORTS-1, port_num);

omap_rs485_dir_fun[port_num]= rs485_dir_fun;

}

EXPORT_SYMBOL(omap_rs485_dir_fun_reg);

③ serial_omap_probe函數中對控制程序中用到的up->port.set_rs485_direction進行賦值。

up->port.set_rs485_direction= omap_rs485_dir_fun[pdev->id];

④ 默認情況下RS485處於接收狀態。

⑤ serial_omap_enable_ier_thri函數中把RS485切換爲發送狀態。

static incline void serial_omap_enable_ier_thri(struct uart_omap_port *up){

if (!(up->ier & UART_IER_THRI)) {

/* rs485 dir change to tx */

if (up->port.set_rs485_direction != NULL)

up->port.set_rs485_direction(SET_RS485_TX);

……//此處代碼省略

}

}

⑥ serial_omap_stop_tx函數中把RS485切換爲接收狀態。

static void serial_omap_stop_tx(struct uart_omap_port *port){

……//此處代碼省略

if (up->ier & UART_IER_THRI) {

up->ier &= ~UART_IER_THRI;

serial_out(up, UART_IER, up->ier);

/* rs485 dir change to rx */

if (port->set_rs485_direction != NULL)

port->set_rs485_direction(SET_RS485_RX);

}

}

⑦ transmit_chars更改一下,原先的代碼是當沒有更多的字符要發送(環形緩衝爲空)時需要關閉發送中斷,這時串口控制器發送BUF和移位寄存器中還是有數據的,這些數據串口控制器自動發送完成後纔算結束,由於已經關閉了發送中斷,因此發送結束後就沒有中斷產生了。但是RS485切換方向需要等到完全發送完成後才能進行。因此對transmit_chars函數做了修改。調用serial_omap_stop_tx函數前判斷髮送BUF和移位寄存器是否爲空,如果爲空就可以切換方向了。簡而言之,延後了發送中斷的關閉時間。

static void transmit_chars(struct uart_omap_port *up){

……//此處代碼省略

if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {

if (up->port.ops->tx_empty(&up->port)==0)

return;//added for last transmit

serial_omap_stop_tx(&up->port);

return;

}

……//此處代碼省略

if (uart_circ_empty(xmit)) {

if (up->port.ops->tx_empty(&up->port)==0)

return;//added for last transmit

serial_omap_stop_tx(&up->port);

}

}

⑧ arch/arm/machomap2/boardti8168evm.c文件在ti8168_evm_init函數中調用omap_rs485_dir_fun_reg函數註冊RS485切換函數。

2.5 實驗結果分析

上述軟件修改有如下幾個優點:不增加硬件開銷;不增加和使用任何硬件資源;不增加軟件開銷;不影響軟件執行效率;硬件控制是電信號控制,方向切換和TX綁定;軟件控制是整個發送緩衝區完成發送後再進行方向切換,控制實現上更加合理。

對軟件切換RS485做了基本的測試,情況如下:

① 控制檯操作。整個啓動打印信息正常。UBoot和Kernel下控制效果和硬件控制一樣,可以很流暢地進行命令的輸入和回顯,串口終端增加輸入字符間的延時後可以進行配置的粘貼。內核在115 200和38 400下分別進行測試OK。

② 內核下加大負責進行大數據量的發送。增加負載,開多個ping包進程(產生大量中斷)、Nand Flash的操作、CPU佔有率接近100%條件下,通過RS485輸出大量數據,沒有亂碼,校驗OK。

③ 極高的實時性。

由於本文給出的軟件實現方式是基於Linux內核實現的,因此很好地保證了方向控制的實時性。實際結果顯示,DM8168數據發送完成到產生方向控制信號之間的時間在25 μs左右,幾乎可以忽略不計。而有些設計在用戶空間使用應用程序進行方向切換的方法會導致20 ms以上的延時,導致了一系列異常問題的產生。

結語

本文詳細描述了RS485方向控制的硬件和軟件兩種實現方式。兩種控制方式各有特點,硬件控制方式實現簡單,不需要軟件干預,對軟件而言RS485串口收發就像RS232一樣簡單。軟件控制方式可以極大地提高整個RS485線路的驅動能力,本文給出的基於Linux內核的控制方法又很好地保證了RS485方向切換的實時性,滿足了實用性要求。這兩種方式在很多場合已經得到了很好的應用和驗證。特別是軟件實現方式,可以擴展到更多的應用場合,譬如複雜的多主、多從的RS485使用環境,軟件控制可以根據自己的需求來實現整個RS485線路不同的數據流向,可以規避某個設備對RS485鏈路上異常信號的干擾,給實際應用帶來了很多的便利性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章