Rockchip USB转485_学习

  • #RS232 / RS485  简介

    • #RS232

    • #RS485

    • #R485与RS232比较

  • #开发

    • #DTS配置

    • #驱动开发

    • #POSIX规范API

  • #HAL层以上APP层

    • #APK call JNI

    • #APP

  • #调试

    • #log开启

  • #RS232/RS485/RS422常见问题

#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严格控制数据命令,这是通信倍率调慢一些(不是调节波特率)

Rockchip USB转485_学习_02