RK平臺 USB轉RS485

RS232 / RS485 簡介

RS232

RS-232是美國電子工業聯盟(EIA)制定的串行數據通信的接口標準,原始編號全稱是EIA-RS-232(簡稱232,RS232)。它被廣泛用於計算機串行接口外設連接。
RS-232C標準,其中EIA(Electronic Industry Association)代表美國電子工業聯盟,RS(Recommended standard)代表推薦標準,232是標識號,C代表RS232的第三次修改(1969年),在這之前,還有RS232B、RS232A.
在RS-232標準中,字符是以一串行的比特串來一個接一個的串列(serial)方式傳輸,優點是傳輸線少,配線簡單,發送距離可以較遠。
最常用的編碼格式是異步起停(asynchronous start-stop)格式,它使用一個起始比特後面緊跟7或8個數據比特(bit),然後是可選的奇偶校驗比特,最後是一或兩個停止比特。所以發送一個字符至少需要10比特,帶來的一個好的效果是使全部的傳輸速率,發送信號的速率以10劃分。

表中列出的是被較多使用的RS-232中的信號和管腳分配:

       DE-9 Male(Pin Side)                   DE-9 Female (Pin Side)
         -------------                          -------------
         \ 1 2 3 4 5 /                          \ 5 4 3 2 1 /
          \ 6 7 8 9 /                            \ 9 8 7 6 /
           ---------                              ---------
信號 DB-25 DE-9 EIA/TIA 561 Yost
公共接地 7 5 4 4,5
發送數據(TD、TXD) 2 3 6 3
接受數據(RD、RXD) 3 2 5 6
數據終端準備(DTR) 20 4 3 2
數據準備好(DSR) 6 6 1 7
請求發送(RTS) 4 7 8 1
清除發送(CTS) 5 8 7 8
數據載波檢測(DCD) 8 1 2 7
振鈴指示(RI) 22 9 1
腳位 簡寫 意義 說明
Pin1 DCD Data Carrier Detect 調制解調器通知計算機有載波被偵測到。
Pin2 RXD Receiver 接收數據。
Pin3 TXD Transmit 發送數據。
Pin4 DTR Data Terminal Ready 計算機告訴調制解調器可以進行傳輸。
Pin5 GND Ground 地線。
Pin6 DSR Data Set Ready 調制解調器告訴計算機一切準備就緒。
Pin7 RTS Request To Send 計算機要求調制解調器將數據提交。
Pin8 CTS Clear To Send 調制解調器通知計算機可以傳數據過來。
Pin9 RI Ring Indicator 調制解調器通知計算機有電話進來。

串行通信在軟件設置裏需要做多項設置,最常見的設置包括波特率(Baud)、奇偶校驗(Parity Check)和停止位(Stop Bit)

RS485

RS485是由EIA(Electronic Industry Association,美國電子工業協會)於1983年在RS-422基礎上制定併發布的一種串行通信平衡式數據發送標準,
經通訊工業協會(TIA)修訂後命名爲TIA/EIA-485-A。滿足RS485標準的收發器採用差分傳輸方式(Differential Driver Mode),數據最高傳輸速率爲10Mbps,最大通信距離約爲1219m。

用纜線兩端的電壓差值來表示傳遞信號,不同的電壓差分別標識爲邏輯1及邏輯0。兩端的電壓差最小爲0.2V以上時有效,任何不大於12V或者不小於-7V的差值對接受端都被認爲是正確的。

RS485具有支持多節點(32個節點),傳輸距離遠(最大1219m),接收靈敏度高(200mV電壓),連接簡單(在構成通信網絡時,僅需要一對雙絞線作傳輸線),能抑制共模干擾(差分傳輸),
成本低廉等特點,在多站、遠距離通信等多種工控環境中獲得了廣泛應用。

R485與RS232比較

  • RS485相比RS232具有抑制共模干擾、傳輸距離長等優點,所以許多大型的工業設備都採用RS485進行串口通訊。
  • RS485採用的是差分信號,所以在進行串口通訊時,只能採用半雙工的工作方式,必須使用1個或2個I/O口來控制RS485的發送和接收狀態

開發

DTS配置

項目中主芯片RS232 RS485不夠 需要通過USB 擴展RS232(silicon CP2105) ,RS232再轉RS485 (thvd1500) RS232轉RS485,RE/DE 控住輸出 ,項目中通過GPIO控制

rs485-thvd1500{
    status = "okay";
    compatible = "ti,thvd1500-gpio";
    thvd1500-gpio1 = <&pca0 1 1>;
    thvd1500-gpio2 = <&pca0 2 1>;
};

2路RS485 控制腳

驅動開發

協議轉換

drivers/usb/serial/cp210x.c
/*
 * cp210x_get_config
 * Reads from the CP210x configuration registers
 * 'size' is specified in bytes.
 * 'data' is a pointer to a pre-allocated array of integers large
 * enough to hold 'size' bytes (with 4 bytes to each integer)
 */
static int cp210x_get_config(struct usb_serial_port *port, u8 request,
        unsigned int *data, int size)
{  
	...
}


/*
 * cp210x_set_config
 * Writes to the CP210x configuration registers
 * Values less than 16 bits wide are sent directly
 * 'size' is specified in bytes.
 */
static int cp210x_set_config(struct usb_serial_port *port, u8 request,
        unsigned int *data, int size)
{

	....
}

static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
{                        
    int result;          
                                                            
    result = cp210x_set_config_single(port, CP210X_IFC_ENABLE,
                                UART_ENABLE);
    if (result) {        
        dev_err(&port->dev, "%s - Unable to enable UART\n", __func__);
        return result;
    }                    
                         
    /* Configure the termios structure */                                                                                                      
    cp210x_get_termios(tty, port);
                         
    /* The baud rate must be initialised on cp2104 */
    if (tty)             
        cp210x_change_speed(tty, port, NULL);
                         
    return usb_serial_generic_open(tty, port);
}  

通過設置termios類型的數據結構中的值,可以對終端接口進行控制。

使能控制:

   if (gpio_is_valid(gpio))                        
    {                                               
    ┊   devm_gpio_request_one(&pdev->dev, gpio,GPIOF_OUT_INIT_HIGH, "thvd1500-gpio1");
    ┊   thvd1500_priv1.gpio_num = gpio;             
    ┊   thvd1500_priv1.active_high = flag;          
    ┊   thvd1 = proc_create_data("thvd1", 0777, thvd1500_dir, &thvd_proc_fops1,(void *)&thvd1500_priv1);
    ....    

POSIX規範API

標準接口:

#include <termios.h>

speed_t cfgetispeed(const struct termios *);
speed_t cfgetospeed(const struct termios *);
int cfsetispeed(struct termios *, speed_t speed);
int cfseospeed(struct termios *, speed_t speed);

這些API 作用於termios結構。
需要先調用tcgetattr()獲得termios結構,再調用以上函數設置終端速度,最後調用tcsetattr()使設置生效。

其他API

#include <termios.h>
int tcdrain(int fd);讓調用程序一直等待,直到所有排隊的輸出都發送完畢
int tcflow(int, int flowtype);暫停或重新開始輸出
int tcflush(int fd, int in_out_selector);清空輸入,輸出或兩者都清空

HAL層以上APP層

APK call JNI

JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open
  (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags)
{
	int fd;
	speed_t speed;
	jobject mFileDescriptor;

	/* Configure device */
	{
		struct termios cfg;
		LOGD("Configuring serial port");
		if (tcgetattr(fd, &cfg))
		{
			LOGE("tcgetattr() failed");
			close(fd);
			/* TODO: throw an exception */
			return NULL;
		}

		cfmakeraw(&cfg);
		cfsetispeed(&cfg, speed);
		cfsetospeed(&cfg, speed);

		if (tcsetattr(fd, TCSANOW, &cfg))
		{
			LOGE("tcsetattr() failed");
			close(fd);
			/* TODO: throw an exception */
			return NULL;
		}
	}
	...
	return mFileDescriptor;
}

Java 中調用JNI
    // JNI
    private native static FileDescriptor open(String path, int baudrate,
                                              int flags);

調用相關API ,配置termios結構數據

APP

public class SerialPort {
 public SerialPort(File device, int baudrate, int flags)
            throws SecurityException, IOException {

		/* Check access permission */
        if (!device.canRead() || !device.canWrite()) {
           ...
        }
        mFd = open(device.getAbsolutePath(), baudrate, flags);/*調用JNI總open*/
        if (mFd == null) {
            Log.e(TAG, "native open returns null");
            throw new IOException();
        }
        mFileInputStream = new FileInputStream(mFd);
        mFileOutputStream = new FileOutputStream(mFd);
    }

...
}

構建類

	/*
     * 打開串口,接收數據
     * 通過串口,接收單片機發送來的數據
     */
    public void openSerialPort() {
        try {
            serialPort = new SerialPort(new File(TTYUSB0), 115200, 0);
            //調用對象SerialPort方法,獲取串口中"讀和寫"的數據流
            inputStream = serialPort.getInputStream();
            outputStream = serialPort.getOutputStream();

            serialPort1 = new SerialPort(new File(TTYUSB1), 115200, 0);
            //調用對象SerialPort方法,獲取串口中"讀和寫"的數據流
            inputStream1 = serialPort1.getInputStream();
            outputStream1 = serialPort1.getOutputStream();

            Log.i(TAG, "打開串口");

        } catch (IOException e) {
            e.printStackTrace();
        }

        //getSerialPort();
    }

通過流讀取數據

調試

log開啓

開啓USB轉RS232 dev_dbg
config文件添加

CONFIG_DEBUG_FS=y
CONFIG_DYNAMIC_DEBUG=y 

kernel debug <linux/device.h> 打印級別

#define KERN_EMERG    KERN_SOH "0"    /* system is unusable */
#define KERN_ALERT    KERN_SOH "1"    /* action must be taken immediately */
#define KERN_CRIT    KERN_SOH "2"    /* critical conditions */
#define KERN_ERR    KERN_SOH "3"    /* error conditions */
#define KERN_WARNING    KERN_SOH "4"    /* warning conditions */
#define KERN_NOTICE    KERN_SOH "5"    /* normal but significant condition */
#define KERN_INFO    KERN_SOH "6"    /* informational */
#define KERN_DEBUG    KERN_SOH "7"    /* debug-level messages */
#define KERN_DEFAULT    KERN_SOH "d"    /* the default kernel loglevel*/

kernel/printk.c

/* printk's without a loglevel use this.. */
 
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
 
int console_printk[4] = {
              DEFAULT_CONSOLE_LOGLEVEL,       /* console_loglevel 控制檯日誌級別,優先級高於該值的消息將在控制檯顯示*/
              DEFAULT_MESSAGE_LOGLEVEL,       /* default_message_loglevel */
              MINIMUM_CONSOLE_LOGLEVEL,      /* minimum_console_loglevel 最低的控制檯日誌級別 */
              DEFAULT_CONSOLE_LOGLEVEL,       /* default_console_loglevel */
};

查看打印級別 和修改打印級別。

rk3399_mid:/ # cat /proc/sys/kernel/printk   
7       4       1       7
rk3399_mid:/ # echo 1 4 1 7 >/proc/sys/kernel/printk
rk3399_mid:/ # cat /proc/sys/kernel/printk                                     
1       4       1       7

RS232/RS485/RS422常見問題

問題一 :A廠的屏可以和設備通信,換成B廠的屏就通信不上了。
方向:
1) 首先確認一下接線是否正確了,RX和TX是否兼容。
2) 地線是否沒有接。
3) 除了RX,TX,GND,是否還有其它引腳需要短接的。
4) 通信協議是否一致或不完善,波特率是否一樣。

問題二: 設備A是RS232,設備B是RS422,沒有轉換設備 怎麼處理?

設備A是RS422接口,但是隻有RS232通信可以測試通信。需要將RS422轉成RS232進行通信,兩者都是全雙工的,接收和發送都是同時到的,而RS422只是以一種差分信號進行傳輸。
將422的Rx+與232的TX接,422的RX-與232的GND接。將422的TX+與232的RX接,422的TX-與232的GDN接。

問題三: RS232接口通信OK ,RS485通信也OK,但是使用RS232轉RS485通信就不穩定。
RS232全雙工,RS485半雙工,應用層發送/接受數據時,RS485不能同時收/發,需要Master嚴格控制數據命令,這是通信倍率調慢一些(不是調節波特率)

在這裏插入圖片描述

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