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

基本知识:

RS-232与RS-485的区别在于:
1、传输方式不同。 RS-232采取不平衡传输方式,即所谓单端通讯. 而RS485则采用平衡传输,即差分传输方式。
2、传输距离不同。RS-232适合本地设备之间的通信,传输距离一般不超过20m。而RS-485的传输距离
为几十米到上千米。
3、RS-232 只允许一对一通信,而RS-485 接口在总线上是允许连接多达128个收发器。
RS-232被定义为一种在低速率串行通讯中增加通讯距离的单端标准。 
RS:是推荐标准的英文缩写
RS-232采取不平衡传输方式,即所谓单端通讯.收、发端的数据信号是相对于信号地,如从DTE设备发出的数据在使用DB25连接器时是2脚相对7脚(信号地)的电平。 
典型的RS-232信号在正负电平之间摆动,在发送数据时,发送端驱动器输出正电平在+5~+15V,负电平在-5~-15V电平。当无数据传输时,线上为TTL,从开始传送数据到结束,线上电平从TTL电平到RS-232电平再返回TTL电平。 
接收器典型的工作电平在+3~+12V与-3~-12V。由于发送电平与接收电平的差仅为2V至3V左右,所以其共模抑制能力差,再加上双绞线上的分布电容,其传送距离最大为约15米,最高速率为20kb/s。RS-232是为点对点(即只用一对收、发设备)通讯而设计的,其驱动器负载为3~7kΩ。所以RS-232适合本地设备之间的通信。 
RS-485与RS-232不一样,数据信号采用差分传输方式,也称作平衡传输,它使用一对双绞线,将其中一线定义为A,另一线定义为B,通常情况下,发送驱动器A、B之间的正电平在+2~+6V,是一个逻辑状态,负电平在-2~6V,是另一个逻辑状态。另有一个信号地C,在RS-485中还有一“使能”端。“使能”端是用于控制发送驱动器与传输线的切断与连接。当“使能”端起作用时,发送驱动器处于高阻状态,称作“第三态”,即它是有别于逻辑“1”与“0”的第三态。 接收器也作与发送端相对的规定,收、发端通过平衡双绞线将AA与BB对应相连,当在收端AB之间有大于+200mV的电平时,输出正逻辑电平,小于-200mV时,输出负逻辑电平。接收器接收平衡线上的电平范围通常在200mV至6V之间。

         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.最后编译内核,将内核烧写到板子

你可能感兴趣的:(将S3C6410的RS232改为RS485(修改内核驱动))