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

来源:互联网 发布:陕西师大网络教育官网 编辑:程序博客网 时间:2024/05/21 11:18

基本知识:

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

原创粉丝点击