将S3C2416的RS232改为RS485(修改内核驱动)

基本知识:

         ARM一般会使用电平转换芯片把串口TTL电平转换成相应的232或485电平信号。与232稍有不同的是,一般的485是半双工的,意思就是说同一时刻要么处于接收状态要么处于发送状态。485芯片有许多种,但基本大同小异,一般有2个引脚给用户来控制收发状态。当RE为低电平时,485芯片数据输入有效(低电平接收状态);当DE为高电平时,485芯片数据输出有效(高电平发送状态)。在半双工使用中,通常可以将这两个脚直接相连,然后通过输出的高低电平就可以让485芯片在接收和发送状态之间转换了。


方案:

不管怎样的方案都需要一个485芯片,不同的是收发状态的控制,可以由电路本身控制,也可以接到CPU的IO口由驱动来控制。

1.电路本身控制:市面上可以买到的485转换器都使用这种方案,简单来讲,就是从485芯片的DI脚(接到ARM的TXD)取得收发状态,进而设置485芯片的收发状态。

2.IO口控制:单片机很多使用此方案,因为实现起来简单,但是用在ARM上就不简单了,本文就介绍此方案。

单片机控制的话,大概是这么个流程:一开始是接收状态,数据发送前先置高,并稍作延时,等待485芯片初始化为发送状态,然后开始发送,发送完成后稍作延时,等待发送完成,然后置低进入接收状态。

在ARM中虽然需要修改内核驱动,而且还要占用额外的IO口,但仍然是一个可行的方案,经测试各种波特率都使用正常,连续几天使用过程中也挺稳定。详细请看具体实现方法。


具体实现方法:

S3C2416共有4个串口,UART0、UART1、UART2、UART3。其中UART0用于调试用,将剩余三个改为RS485。

最终实现的效果:和一般的232串口一样使用,应用层无需改变,占用的3个GPIO口就不能再做其它用了。

使用用的内核版本为Linux 3.8.5

需要修改内核源代码路径下的 drivers/tty/serial/samsung.c


1.因为要操作GPIO,所以要引用相关头文件

#include <asm/gpio.h>
#include <mach/regs-gpio.h> 

2.把static struct uart_ops s3c24xx_serial_ops复制一份,并修改成如下,有3处

//2014-8-26 add by whl
//在s3c24xx_serial_ports中,定义了串口相关操作——s3c24xx_serial_ops
static struct uart_ops s3c24xx_485_ops = {
	.pm		= s3c24xx_serial_pm,	 //电源管理
	.tx_empty	= s3c24xx_serial_tx_empty,	//发送缓存区空
	.get_mctrl	= s3c24xx_serial_get_mctrl,	 //得到modem控制设置
	.set_mctrl	= s3c24xx_serial_set_mctrl,	//设置modem控制
	.stop_tx	= s3c24xx_485_stop_tx,	//停止发送
	.start_tx	= s3c24xx_485_start_tx,	 //开始发送
	.stop_rx	= s3c24xx_serial_stop_rx,	//停止接受
	.enable_ms	= s3c24xx_serial_enable_ms,	 //modem状态中断使能
	.break_ctl	= s3c24xx_serial_break_ctl,	//控制break信号的传输
	.startup	= s3c24xx_485_startup,	 //启动端口
	.shutdown	= s3c24xx_serial_shutdown,	 //禁止端口
	.set_termios	= s3c24xx_serial_set_termios,	//设置端口参数
	.type		= s3c24xx_serial_type,				  //返回描述特定端口的常量字符串指针
	.release_port	= s3c24xx_serial_release_port,	 //释放端口所占的内存和资源	
	.request_port	= s3c24xx_serial_request_port,	 //申请端口所需的内存和资源
	.config_port	= s3c24xx_serial_config_port,	 //配置端口
	.verify_port	= s3c24xx_serial_verify_port,	//验证端口
#ifdef CONFIG_CONSOLE_POLL
	.poll_get_char = s3c24xx_serial_get_poll_char,
	.poll_put_char = s3c24xx_serial_put_poll_char,
#endif
};

然后修改:

//whl: uart驱动的结构为:

static struct uart_driver s3c24xx_uart_drv = {
	.owner		= THIS_MODULE,
	.driver_name	= "s3c2410_serial",	//whl: 驱动名,在/proc/tty/driver/目录下显示的名字
	.nr		= CONFIG_SERIAL_SAMSUNG_UARTS,	 //whl: uart的端口数
	.cons		= S3C24XX_SERIAL_CONSOLE,
	.dev_name	= S3C24XX_SERIAL_NAME,	//whl:  设备名——ttySAC
	.major		= S3C24XX_SERIAL_MAJOR, //whl:  主设备号——204
	.minor		= S3C24XX_SERIAL_MINOR, //whl:  次设备号——64
};
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
	[0] = {//端口0
		.port = {
			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
			.iotype		= UPIO_MEM, //(2)
			.uartclk	= 0,	 //时钟值
			.fifosize	= 16,	 //FIFO缓存区大小
			.ops		= &s3c24xx_serial_ops,	//串口相关操作
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 0,	 //线路
		}
	},
	[1] = {	//端口1
		.port = {
			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
			.iotype		= UPIO_MEM,
			.uartclk	= 0,
			.fifosize	= 16,
			.ops		= &s3c24xx_485_ops,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 1,
		}
	},
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
	[2] = {//端口2
		.port = {
			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
			.iotype		= UPIO_MEM,
			.uartclk	= 0,
			.fifosize	= 16,
			.ops		= &s3c24xx_485_ops,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 2,
		}
	},
#endif
#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
	[3] = {//端口3
		.port = {
			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
			.iotype		= UPIO_MEM,
			.uartclk	= 0,
			.fifosize	= 16,
			.ops		= &s3c24xx_485_ops,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 3,
		}
	}
#endif
};


3.定义s3c24xx_485_startup

//启动端口

static int s3c24xx_485_startup(struct uart_port *port)
{
	struct s3c24xx_uart_port *ourport = to_ourport(port);
	int ret;
	dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
	    port->mapbase, port->membase);
	rx_enabled(port) = 1;	//接收数据使能
	//申请接收数据中断,s3c24xx_serial_rx_chars为中断处理函数
	ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
			  s3c24xx_serial_portname(port), ourport);
	if (ret != 0) {
		dev_err(port->dev, "cannot get irq %d\n", ourport->rx_irq);
		return ret;
	}
	ourport->rx_claimed = 1;	//标志
	dbg("requesting tx irq...\n");
	tx_enabled(port) = 1;	//发送数据使能
	//申请发送数据中断,s3c24xx_serial_tx_chars为中断处理函数
	ret = request_irq(ourport->tx_irq, s3c24xx_485_tx_chars, 0,
			  s3c24xx_serial_portname(port), ourport);
	if (ret) {
		dev_err(port->dev, "cannot get irq %d\n", ourport->tx_irq);
		goto err;
	}
	ourport->tx_claimed = 1;	 //标志
	dbg("s3c24xx_serial_startup ok\n");
	/* the port reset code should have done the correct
	 * register setup for the port controls */
	//============================================================
	//add by whl 2014-8-26
	  if (port->line == 1)
	   {
		 s3c_gpio_cfgpin(S3C2410_GPH(9), S3C2410_GPIO_OUTPUT);
		 gpio_set_value(S3C2410_GPH(9), 0);
	   }
	 //串口2 对应的端口初始化 add  by whl
	  if (port->line == 2)
	   {
	     s3c_gpio_cfgpin(S3C2410_GPH(4), S3C2416_GPH4_TXD2);
		 s3c_gpio_setpull(S3C2410_GPH(4), 1);
		 s3c_gpio_cfgpin(S3C2410_GPH(5), S3C2416_GPH5_RXD2);
		 s3c_gpio_setpull(S3C2410_GPH(5), 1);
		 s3c_gpio_cfgpin(S3C2410_GPH(8), S3C2410_GPIO_OUTPUT);
		 gpio_set_value(S3C2410_GPH(8), 0);
	   }
	  if (port->line == 3)
	   {
		 s3c_gpio_cfgpin(S3C2410_GPE(5), S3C2410_GPIO_OUTPUT);
		 gpio_set_value(S3C2410_GPE(5), 0);
		 //printk("%s: channl 3 low\n", __func__);
	   }
	//============================================================
	return ret;
 err:
	s3c24xx_serial_shutdown(port);
	return ret;
}

4.定义s3c24xx_485_tx_chars

{

	struct s3c24xx_uart_port *ourport = id;
	struct uart_port *port = &ourport->port;
	struct circ_buf *xmit = &port->state->xmit;
	unsigned long flags;
	int count = 256;  //一次最多发送256个字符
	//add by whl 2014-8-26
	//printk("s3c24xx_485_tx_chars port->line=%d\n", port->line);
	  if (port->line == 1)
	   {
		 s3c_gpio_cfgpin(S3C2410_GPH(9), S3C2410_GPIO_OUTPUT);
		 gpio_set_value(S3C2410_GPH(9), 1);
	   }
	  if (port->line == 2)
	   {
		 s3c_gpio_cfgpin(S3C2410_GPH(8), S3C2410_GPIO_OUTPUT);
		 gpio_set_value(S3C2410_GPH(8), 1);
	   }
	  if (port->line == 3)
	   {
		 s3c_gpio_cfgpin(S3C2410_GPE(5), S3C2410_GPIO_OUTPUT);
		 gpio_set_value(S3C2410_GPE(5), 1);
		 //printk("%s: channl 3 high\n", __func__);
	   }
	spin_lock_irqsave(&port->lock, flags);
	if (port->x_char) {	 //如果有待发送的字符,则发送
		wr_regb(port, S3C2410_UTXH, port->x_char);
		port->icount.tx++;
		port->x_char = 0;
		goto out;
	}
	/* if there isn't anything more to transmit, or the uart is now
	 * stopped, disable the uart and exit
	*/
	 //如果没有更多的字符需要发送,或者uart的tx停止,则停止uart并退出
	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
		s3c24xx_485_stop_tx(port);
		goto out;
	}
	/* try and drain the buffer... */
	//尝试把环形buffer中的数据发空
	while (!uart_circ_empty(xmit) && count-- > 0) {
		if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
			break;
		wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
		//printk("head = %d, tail=%d\n", xmit->head, xmit->tail);
		port->icount.tx++;
	}
	//如果环形缓存中剩余的字符少于WAKEUP_CHARS,唤醒上层
	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
		spin_unlock(&port->lock);
		uart_write_wakeup(port);
		spin_lock(&port->lock);
	}
	if (uart_circ_empty(xmit)) //如果发送环形buffer为空
		s3c24xx_485_stop_tx(port);	 //停止发送
 out:
	spin_unlock_irqrestore(&port->lock, flags);
	return IRQ_HANDLED;
}

5.定义s3c24xx_485_start_tx

static void s3c24xx_485_start_tx(struct uart_port *port)

{
	struct s3c24xx_uart_port *ourport = to_ourport(port);
	//add by whl 2014-8-26
	  if (port->line == 1)
	   {
		 s3c_gpio_cfgpin(S3C2410_GPH(9), S3C2410_GPIO_OUTPUT);
		 gpio_set_value(S3C2410_GPH(9), 1);
	   }
	  if (port->line == 2)
	   {
		 s3c_gpio_cfgpin(S3C2410_GPH(8), S3C2410_GPIO_OUTPUT);
		 gpio_set_value(S3C2410_GPH(8), 1);
	   }
	  if (port->line == 3)
	   {
		 s3c_gpio_cfgpin(S3C2410_GPE(5), S3C2410_GPIO_OUTPUT);
		 gpio_set_value(S3C2410_GPE(5), 1);
		 //printk("%s: channl 3 high\n", __func__);
	   }
	if (!tx_enabled(port)) {
		if (port->flags & UPF_CONS_FLOW)
			s3c24xx_serial_rx_disable(port);
		if (s3c24xx_serial_has_interrupt_mask(port))
			__clear_bit(S3C64XX_UINTM_TXD,
				portaddrl(port, S3C64XX_UINTM));
		else
			enable_irq(ourport->tx_irq);
		tx_enabled(port) = 1;
	}
}


6.定义s3c24xx_485_stop_tx

static void s3c24xx_485_stop_tx ( struct uart_port * port )
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);
    
    if (tx_enabled(port)) {
        if (s3c24xx_serial_has_interrupt_mask(port))
            __set_bit(S3C64XX_UINTM_TXD,
                      portaddrl(port, S3C64XX_UINTM));
        else
            disable_irq_nosync(ourport->tx_irq);
        tx_enabled(port) = 0;
        if (port->flags & UPF_CONS_FLOW)
            s3c24xx_serial_rx_enable(port);
    }
    //add by whl 2014-8-26
#if 0
    printk("before===>\n");
    printk("rd_regl(port, S3C2410_UFSTAT)=%x\n", rd_regl(port, S3C2410_UFSTAT)); //0x700
    printk("rd_regl(port, S3C2410_UTRSTAT)=%x\n", rd_regl(port, S3C2410_UTRSTAT));//0x00
    mdelay(10);
    printk("after msleep(10)===>\n");
    printk("rd_regl(port, S3C2410_UFSTAT)=%x\n", rd_regl(port, S3C2410_UFSTAT));//0x00
    printk("rd_regl(port, S3C2410_UTRSTAT)=%x\n", rd_regl(port, S3C2410_UTRSTAT));//0x6
#endif
 while( (rd_regl(port, S3C2410_UFSTAT))&(0x3F<<8) ||
 (!((rd_regl(port, S3C2410_UTRSTAT))&0x06)) ){
 mdelay(1);
 }
 mdelay(1);

    if (port->line == 1)
    {
        s3c_gpio_cfgpin(S3C2410_GPH(9), S3C2410_GPIO_OUTPUT);
        gpio_set_value(S3C2410_GPH(9), 0);
    }
    if (port->line == 2)
    {
        s3c_gpio_cfgpin(S3C2410_GPH(8), S3C2410_GPIO_OUTPUT);
        gpio_set_value(S3C2410_GPH(8), 0);
    }
    if (port->line == 3)
    {
        s3c_gpio_cfgpin(S3C2410_GPE(5), S3C2410_GPIO_OUTPUT);
        gpio_set_value(S3C2410_GPE(5), 0);
        //printk("%s: channl 3 low\n", __func__);
    }
    
}

7.最后编译内核将内核烧写到板子

你可能感兴趣的:(串口,驱动,RS485)