Navigation

    全志在线开发者论坛

    • Register
    • Login
    • Search
    • Categories
    • Tags
    • 在线文档
    • 社区主页

    T113上uart转485功能实现

    MR Series
    4
    5
    3511
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • zznzzn
      zznzzn LV 6 last edited by

      sunxi-uart.c
      sunxi-uart.h

      如下是T113在linux下实现uart转485,patch可以参考附件,dts的配置如下:

      uart1: uart@05000400 {
      		status = "okay";
      		uart1_type = <2>;
      		uart1_rs485=<1>;
      		uart1_485fl=<0>;
      		uart1_485oe=<&pio PG 1 1 0xffffffff 0xffffffff 0>;
      	};
      

      patch如下:

      diff --git a/arch/arm/boot/dts/sun8iw20p1.dtsi b/arch/arm/boot/dts/sun8iw20p1.dtsi
      index 6e51b72..4d3fb29 100644
      --- a/arch/arm/boot/dts/sun8iw20p1.dtsi
      +++ b/arch/arm/boot/dts/sun8iw20p1.dtsi
      @@ -613,8 +613,11 @@
       			clock-names = "uart1";
       			resets = <&ccu RST_BUS_UART1>;
       			uart1_port = <1>;
      -			uart1_type = <4>;
      +			uart1_type = <2>;
       			status = "okay";
      +			uart1_rs485=<1>;
      +			uart1_485fl=<0>;
      +			uart1_485oe=<&pio PG 1 1 0xffffffff 0xffffffff 0>;			
       		};
       
       		uart2: uart@2500800 {
      diff --git a/drivers/tty/serial/sunxi-uart.c b/drivers/tty/serial/sunxi-uart.c
      index 395b1bd..f013ad2 100644
      --- a/drivers/tty/serial/sunxi-uart.c
      +++ b/drivers/tty/serial/sunxi-uart.c
      @@ -43,6 +43,10 @@
       #include <linux/of.h>
       #include <linux/of_irq.h>
       #include <linux/of_address.h>
      +
      +#include <linux/delay.h>
      +#include <uapi/linux/sched/types.h>
      +#include <linux/of_gpio.h>
       #include "sunxi-uart.h"
       
       #if defined(CONFIG_SERIAL_SUNXI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
      @@ -191,6 +195,28 @@ static inline void sw_uart_disable_ier_thri(struct uart_port *port)
       	}
       }
       
      +
      +/********************************************************************/
      +/* cet,liudachuan: rs485 delayed rts release control, not used now */
      +# if 0
      +static enum hrtimer_restart uart_set_rs485(struct hrtimer *u_timer)
      +{
      +	struct sw_uart_port *sw_uport = container_of(u_timer, struct sw_uart_port, uart_timer);
      +	if(sw_uport == NULL)
      +	{
      +		SERIAL_DBG("faile to get sw_uport\n", __func__);
      +		return HRTIMER_NORESTART;
      +	}
      +	
      +	if(sw_uport->rs485_en){
      +		gpio_direction_output(sw_uport->rs485oe_io.gpio, sw_uport->rs485_fl);
      +	}
      +
      +	return HRTIMER_NORESTART;
      +}
      +#endif
      +/***********************************************************************************/
      +
       static unsigned int sw_uart_handle_rx(struct sw_uart_port *sw_uport, unsigned int lsr)
       {
       	unsigned char ch = 0;
      @@ -272,23 +298,66 @@ static unsigned int sw_uart_handle_rx(struct sw_uart_port *sw_uport, unsigned in
       	return lsr;
       }
       
      +
      +
      +/******************************************************************************/
      +/* cet,liudachuan: add sw-rs485 support */
      +#define BITS_PER_SERIAL_BYTE	10
      +#define NANOSECOND_PER_USECONDS	1000
      +#define USECONDS_PER_SECOND		1000000
      +static void sw_uart_rs485_cal_tout(struct uart_port *port, unsigned int lcr)
      +{
      +	struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
      +	unsigned int bits_per_byte;
      +	if (lcr & SUNXI_UART_LCR_PARITY) /*有奇偶的时候,byte=start+8+parity=10*/
      +		bits_per_byte = 10;
      +	else    /*无奇偶的时候,byte=start+8=9*/
      +		bits_per_byte = 9;
      +
      +	if (sw_uport->baud != 0) {
      +		sw_uport->tout = 
      +			(bits_per_byte * USECONDS_PER_SECOND) / (sw_uport->baud) * NANOSECOND_PER_USECONDS;
      +	}
      +	else {
      +		sw_uport->tout = 0;
      +	}
      +}
      +
       static void sw_uart_stop_tx(struct uart_port *port)
       {
      -#if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
       	struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
      +
      +#if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
       	struct sw_uart_dma *uart_dma = sw_uport->dma;
       
       	if (uart_dma->use_dma & TX_DMA)
       		sw_uart_stop_dma_tx(sw_uport);
       #endif
       	sw_uart_disable_ier_thri(port);
      +	/* cet,liudachuan: delay 1 byte transmit time then turn off rs485 tx-en */
      +	if (sw_uport->rs485_en) {
      +	   	// hrtimer_start(&sw_uport->uart_timer, sw_uport->tout, HRTIMER_MODE_REL);
      +		unsigned char lsr = 0;
      +		while ((lsr & SUNXI_UART_LSR_BOTH_EMPTY) != SUNXI_UART_LSR_BOTH_EMPTY) {
      +			// cpu_relax();
      +			udelay(10);
      +			lsr = serial_in(port, SUNXI_UART_LSR);
      +		};
      +		gpio_direction_output(sw_uport->rs485oe_io.gpio, sw_uport->rs485_fl);
      +	}
       }
       
       static void sw_uart_start_tx(struct uart_port *port)
       {
      -#if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
       	struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
      +	/* cet,liudachuan: rs485 rts = 1 */
      +	// serial_out(port, sw_uport->mcr & (~SUNXI_UART_MCR_RTS), SUNXI_UART_MCR);
      +	if(sw_uport->rs485_en){
      +		// udelay(20);
      +		gpio_direction_output(sw_uport->rs485oe_io.gpio, !sw_uport->rs485_fl);
      +	}
       
      +#if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
       	if (!((sw_uport->dma->use_dma & TX_DMA) && sw_uport->dma->tx_dma_used))
       #endif
       		sw_uart_enable_ier_thri(port);
      @@ -342,8 +411,14 @@ static void sw_uart_handle_tx(struct sw_uart_port *sw_uport)
       		uart_write_wakeup(&sw_uport->port);
       		spin_lock(&sw_uport->port.lock);
       	}
      -	if (uart_circ_empty(xmit))
      -		sw_uart_stop_tx(&sw_uport->port);
      +
      +	/* cet,liudachuan: add sw-rs485 support */
      +	// if (uart_circ_empty(xmit))
      +	//     sw_uart_stop_tx(&sw_uport->port);
      +	if (!sw_uport->rs485_en) {
      +		if (uart_circ_empty(xmit))
      +			sw_uart_stop_tx(&sw_uport->port);
      +	}
       }
       
       static unsigned int sw_uart_modem_status(struct sw_uart_port *sw_uport)
      @@ -354,7 +429,7 @@ static unsigned int sw_uart_modem_status(struct sw_uart_port *sw_uport)
       	sw_uport->msr_saved_flags = 0;
       
       	if (status & SUNXI_UART_MSR_ANY_DELTA && sw_uport->ier & SUNXI_UART_IER_MSI &&
      -	    sw_uport->port.state != NULL) {
      +		sw_uport->port.state != NULL) {
       		if (status & SUNXI_UART_MSR_TERI)
       			sw_uport->port.icount.rng++;
       		if (status & SUNXI_UART_MSR_DDSR)
      @@ -724,6 +799,25 @@ static enum hrtimer_restart sw_uart_report_dma_rx(struct hrtimer *rx_hrtimer)
       
       #endif
       
      +
      +
      +/**
      + * 串口中断实测14-135us,数据发完后gic_handle_irq两次进入中断,第二次才真正进入串口中断,导致整个延迟变大
      + * 通过提高中断线程优先级为4以上,可以解决,时间缩短到(-5-35us),这里优先级设置为1
      + * cet,ljt 2021.10
      +*/
      +static inline void sw_uart_irq_set_pri(struct sw_uart_port *sw_uport)
      +{
      +    struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO - 2 };
      +	if (sw_uport->irq_pri_promoted)
      +		return;
      +	if (sched_setscheduler(current, SCHED_FIFO, &param) < 0)
      +	{
      +		SERIAL_MSG("sched_setscheduler set error\n");
      +	}
      +	sw_uport->irq_pri_promoted = 1;
      +}
      +
       static irqreturn_t sw_uart_irq(int irq, void *dev_id)
       {
       	struct uart_port *port = dev_id;
      @@ -731,6 +825,9 @@ static irqreturn_t sw_uart_irq(int irq, void *dev_id)
       	unsigned int iir = 0, lsr = 0;
       	unsigned long flags;
       
      +	/* if we hasn't promote the irq priority, set priority to 97 */
      +	//sw_uart_irq_set_pri(sw_uport);
      +
       	spin_lock_irqsave(&port->lock, flags);
       
       	iir = serial_in(port, SUNXI_UART_IIR) & SUNXI_UART_IIR_IID_MASK;
      @@ -1072,7 +1169,7 @@ static void sw_uart_flush_buffer(struct uart_port *port)
       }
       
       static void sw_uart_set_termios(struct uart_port *port, struct ktermios *termios,
      -			    struct ktermios *old)
      +				struct ktermios *old)
       {
       	struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
       	unsigned long flags;
      @@ -1119,6 +1216,12 @@ static void sw_uart_set_termios(struct uart_port *port, struct ktermios *termios
       	dlh = quot >> 8;
       	SERIAL_DBG("set baudrate %d, quot %d\n", baud, quot);
       
      +	/* cet,liudachuan: add sw rs485 support */
      +	sw_uport->baud = baud;
      +	if (sw_uport->rs485_en) {
      +		sw_uart_rs485_cal_tout(port, lcr);
      +	}
      +
       	spin_lock_irqsave(&port->lock, flags);
       	uart_update_timeout(port, termios->c_cflag, baud);
       
      @@ -1379,7 +1482,7 @@ static int sw_uart_ioctl(struct uart_port *port, unsigned int cmd,
       }
       
       static void sw_uart_pm(struct uart_port *port, unsigned int state,
      -		      unsigned int oldstate)
      +			  unsigned int oldstate)
       {
       #if IS_ENABLED(CONFIG_EVB_PLATFORM)
       	int ret;
      @@ -1727,7 +1830,7 @@ static void sw_console_putchar(struct uart_port *port, int c)
       }
       
       static void sw_console_write(struct console *co, const char *s,
      -			      unsigned int count)
      +				  unsigned int count)
       {
       	struct uart_port *port = NULL;
       	struct sw_uart_port *sw_uport;
      @@ -1909,6 +2012,9 @@ static int sw_uart_probe(struct platform_device *pdev)
       	char uart_para[16] = {0};
       	const char *uart_string;
       	int ret = -1;
      +	int rs485_en = 0;         //2020.09.24 2¨??? ¨?¨a?¨?RS485????
      +	int rs485_fl = 0;
      +
       	struct device_node *apk_np = of_find_node_by_name(NULL, "auto_print");
       	const char *apk_sta = NULL;
       #if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
      @@ -2052,6 +2158,45 @@ static int sw_uart_probe(struct platform_device *pdev)
       	else
       		sw_uport->card_print = false;
       
      +	/* cet,liudachuan: add sw-rs485 support */
      +	/************ Modify by wangshunfan @2021.01.11 for this part *************/
      +	/* Get rs485 enable flag */
      +	snprintf(uart_para, sizeof(uart_para), "uart%d_rs485", pdev->id);
      +	ret = of_property_read_u32(np, uart_para, &rs485_en);
      +	if (ret) {
      +		SERIAL_MSG("error to get uart%d_rs485\n", pdev->id);
      +			rs485_en = 0;
      +	}
      +	sw_uport->rs485_en = rs485_en;
      +
      +	//SERIAL_DBG("caid rs485_en = %d port = %d", rs485_en, pdev->id);
      +	if (sw_uport->rs485_en) {
      +		snprintf(uart_para, sizeof(uart_para), "uart%d_485fl", pdev->id);
      +		ret = of_property_read_u32(np, uart_para, &rs485_fl);
      +		if (ret) {
      +			SERIAL_MSG("error to get tuart%d_rs485\n", pdev->id);
      +				rs485_fl = 0;
      +		}
      +		sw_uport->rs485_fl = rs485_fl;
      +
      +		/* Get rs485 enable control pin, request and init gpio */
      +		snprintf(uart_para, sizeof(uart_para), "uart%d_485oe", pdev->id);
      +		sw_uport->rs485oe_io.gpio = of_get_named_gpio_flags(np, uart_para, 0,
      +			(enum of_gpio_flags *)(&(sw_uport->rs485oe_io)));
      +		if (gpio_is_valid(sw_uport->rs485oe_io.gpio)){
      +			gpio_request(sw_uport->rs485oe_io.gpio, NULL);
      +			gpio_direction_output(sw_uport->rs485oe_io.gpio, sw_uport->rs485_fl);
      +		}
      +
      +		/* Init hrtimer for rs485 and set timeout callback function */
      +		// hrtimer_init(&sw_uport->uart_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
      +		// sw_uport->uart_timer.function = uart_set_rs485;
      +
      +		sw_uport->irq_pri_promoted = 0;
      +		SERIAL_MSG("uart%d rs485 enabled\n", pdev->id);
      +	}
      +	/**************** end of modify by wangshunfan for this part ****************/
      +
       	pdata->used = 1;
       	port->iotype = UPIO_MEM;
       	port->type = PORT_SUNXI;
      @@ -2129,7 +2274,14 @@ static int sw_uart_remove(struct platform_device *pdev)
       	struct sw_uart_port *sw_uport = platform_get_drvdata(pdev);
       
       	SERIAL_DBG("release uart%d port\n", sw_uport->id);
      -#if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
      +
      +	/* cet,liudachuan: add sw-rs485 support */
      +	if (sw_uport->rs485_en) {
      +		gpio_free(sw_uport->rs485oe_io.gpio);	/* free gpio of rs485 */
      +		// hrtimer_cancel(&sw_uport->uart_timer);	/* canel hrtimer      */
      +	}
      +
      +#ifdef CONFIG_SERIAL_SUNXI_DMA
       	sw_uart_release_dma_tx(sw_uport);
       	sw_uart_release_dma_rx(sw_uport);
       #endif
      diff --git a/drivers/tty/serial/sunxi-uart.h b/drivers/tty/serial/sunxi-uart.h
      index 565acbf..ab096d6 100644
      --- a/drivers/tty/serial/sunxi-uart.h
      +++ b/drivers/tty/serial/sunxi-uart.h
      @@ -22,6 +22,8 @@
       #include <linux/dmaengine.h>
       #include <linux/reset.h>
       //include <linux/serial_core.h>
      +#include <linux/ktime.h>
      +#include <linux/sunxi-gpio.h>
       
       /* SUNXI UART PORT definition*/
       #define PORT_MAX_USED	PORT_LINFLEXUART  /* see include/uapi/linux/serial_core.h */
      @@ -106,6 +108,16 @@ struct sw_uart_port {
       	struct serial_rs485 rs485conf;
       	bool card_print;
       	bool throttled;
      +
      +    /* cet,liudachuan: add rs485 support(not hardware's rs485) */
      +    struct gpio_config rs485oe_io;
      +    unsigned int rs485_en;
      +    unsigned int rs485_fl;
      +    struct hrtimer uart_timer;
      +    unsigned int baud;
      +    int irq_pri_promoted;
      +    /* Added by wangshunfan @2021.01.11 for hrtimer timeout */
      +	ktime_t tout;
       };
       
       /* register offset define */
      @@ -191,6 +203,8 @@ struct sw_uart_port {
       #define SUNXI_UART_LSR_OE         (BIT(1))
       #define SUNXI_UART_LSR_DR         (BIT(0))
       #define SUNXI_UART_LSR_BRK_ERROR_BITS 0x1E /* BI, FE, PE, OE bits */
      +/* cet,liudachuan: add BOTH_EMPTY macro */
      +#define SUNXI_UART_LSR_BOTH_EMPTY   (SUNXI_UART_LSR_TEMT | SUNXI_UART_LSR_THRE)
       /* Modem Status Register */
       #define SUNXI_UART_MSR_DCD        (BIT(7))
       #define SUNXI_UART_MSR_RI         (BIT(6))
      
      M 1 Reply Last reply Reply Quote Share 3
      • M
        memory LV 7 @zznzzn last edited by

        @zznzzn
        感谢楼主分享。

        不过我没有看出来为什么要这么修改, 用UART自带的RTS控制MAX485的EN引脚不可以吗?

        zznzzn 1 Reply Last reply Reply Quote Share 0
        • zznzzn
          zznzzn LV 6 @memory last edited by

          @memory 在 T113上uart转485功能实现 中说:

          用UART自带的RTS控制MAX485的EN引脚不可以吗?

          想请教一下

          这个操作有相关的例程可以学习吗

          whycan 1 Reply Last reply Reply Quote Share 0
          • whycan
            whycan晕哥 LV 9 @zznzzn last edited by

            @zznzzn

            board.dts 把 对应的 uart 配置成4线模式,然后用RTS接485芯片的 EN引脚

            参考这个帖子: T113-S3 rts 发送控制时序

            但是从这个帖子看,时序还有点问题.

            K 1 Reply Last reply Reply Quote Share 0
            • K
              kakatoo LV 4 @whycan last edited by

              @whycan 帖子上有30ms的时延 这个有点太长了,有点坑。

              1 Reply Last reply Reply Quote Share 0
              • 1 / 1
              • First post
                Last post

              Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

              行为准则 | 用户协议 | 隐私权政策