主题 : 友善6410开发板RS232改RS485(内核驱动修改) 复制链接 | 浏览器收藏 | 打印
级别: 骑士
UID: 29604
精华: 9
发帖: 171
金钱: 1830 两
威望: 366 点
贡献值: 9 点
综合积分: 522 分
注册时间: 2010-10-01
最后登录: 2016-04-13
楼主  发表于: 2012-10-21 22:53

 友善6410开发板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芯片的收发状态。因为友善已经实现了,所以本文不讨论这种方案,友善的支持485的开发板光盘里有原理图,这里就不提供了。

2.IO口控制:单片机很多使用此方案,因为实现起来简单,但是用在ARM上就不简单了,本文就介绍此方案。
单片机控制的话,大概是这么个流程:一开始是接收状态,数据发送前先置高,并稍作延时,等待485芯片初始化为发送状态,然后开始发送,发送完成后稍作延时,等待发送完成,然后置低进入接收状态。
ARM中虽然需要修改内核驱动,而且还要占用额外的IO口,但仍然是一个可行的方案,经测试各种波特率都使用正常,连续几天使用过程中也挺稳定。详细请看具体实现方法。

具体实现方法:
485一般会用在定制的产品上,所以估计是Tiny6410用比较多吧?210的开发板还没有尝试,但一般2.6内核的话大同小异,如果有在210上改成功的,可以共享一下以供参考。
范例所用的开发板使用友善的Tiny6410,当然Mini6410也行,并且适用于android和linux的内核。
示范改2个串口为485,分别是COM0和COM2,因为COM1是5线串口改了可惜所以跳过。如果你懂得了如何改2个串口,那么改成1个或改成3个都没问题了吧。
这里举例用TI的485芯片:sn65lbc184,当然其它的485芯片也行。
芯片手册
http://wenku.baidu.com/view/32fbd336b90d6c85ec3ac6f7.html?st=1

我用的电路如下图,COM0使用SPICS/GPC7来控制,COM2使用SPIMOSI/GPC6来控制
  


最终实现的效果:
1.和一般的232串口一样使用,应用层无需改变,占用的2个GPIO口就不能再做其它用了。
2.用户LED3,在COM0发送时亮(低电平),接收时灭(高电平),用户LED4,在COM2发送时亮(低电平),接收时灭(高电平)。
注意:
Mini6410的用户LED不知道是不是和Tiny6410的定义一样?Mini6410如果不一样可以自行更改,LED只是为了更好的看到COM0和COM2的通讯状态,如果不需要可以把LED控制部分的代码删除。

以下主要内容参考了网上一篇文章的源代码,想提供原文的链接但无奈搜索引擎上已经找不到了,现在搜到的都是一些提问贴或无用的不全的文章-_-

以下针对友善6410的内核版本:2.6.36,不同版本不保证能够成功。
需要修改内核源代码路径下的drivers/serial/samsung.c
1.因为要操作GPIO,所以要引用相关头文件
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>

2.把static struct uart_ops s3c24xx_serial_ops复制一份,并修改成如下,有3处需要修改为自定义的函数。
static struct uart_ops s3c24xx_485_ops = {
    .pm        = s3c24xx_serial_pm,
    .tx_empty    = s3c24xx_serial_tx_empty,
    .get_mctrl    = s3c24xx_serial_get_mctrl,
    .set_mctrl    = s3c24xx_serial_set_mctrl,
    .stop_tx    = s3c24xx_485_stop_tx,//这个
    .start_tx    = s3c24xx_485_start_tx,//这个
    .stop_rx    = s3c24xx_serial_stop_rx,
    .enable_ms    = s3c24xx_serial_enable_ms,
    .break_ctl    = s3c24xx_serial_break_ctl,
    .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,
};
然后更改
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
    [0] = {//这里是COM0
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX0,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_485_ops,//指向刚才修改的ops
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 0,
        }
    },
    [1] = {//这里是COM1,不修改
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX1,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_serial_ops,
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 1,
        }
    },
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2

    [2] = {//这里是COM2
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX2,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_485_ops, //指向刚才修改的ops
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 2,
        }
    },

3.定义s3c24xx_485_startup
static int s3c24xx_485_startup(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);
    int ret;
    #if defined(CONFIG_S5P_UART_DMA_EN)
        unsigned int ucon;
    #endif
//==========================================================
    if (port->line == 0) { //COM0(GPC7)->LED3(GPK6)
        s3c_gpio_cfgpin(S3C64XX_GPK(6), S3C_GPIO_OUTPUT);//GPK6/LED3配置成输出状态
        gpio_set_value(S3C64XX_GPK(6),1);//GPK6/LED3默认灭
    //----------------------------------------------------------
        s3c_gpio_cfgpin(S3C64XX_GPC(7), S3C_GPIO_OUTPUT);//COM0(GPC7)配置成输出状态
        gpio_set_value(S3C64XX_GPC(7), 0);//COM0(GPC7)默认0,常接收
    }
    if (port->line == 2) { //COM2(GPC6)->LED4(GPK7)
        s3c_gpio_cfgpin(S3C64XX_GPK(7), S3C_GPIO_OUTPUT);//LED4(GPK7)配置成输出状态
        gpio_set_value(S3C64XX_GPK(7),1);//LED4(GPK7)默认灭
    //----------------------------------------------------------
        s3c_gpio_cfgpin(S3C64XX_GPC(6), S3C_GPIO_OUTPUT);//COM2(GPC6)配置成输出状态
        gpio_set_value(S3C64XX_GPC(6), 0);//COM2(GPC6)默认0,常接收
    }
    //==========================================================
    dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
        port->mapbase, port->membase);

    rx_enabled(port) = 1;

    ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
              s3c24xx_serial_portname(port), ourport);

    if (ret != 0) {
        printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
        return ret;
    }

    ourport->rx_claimed = 1;

    dbg("requesting tx irq...\n");

    tx_enabled(port) = 1;

    ret = request_irq(ourport->tx_irq, s3c24xx_485_tx_chars, 0,
              s3c24xx_serial_portname(port), ourport);

    if (ret) {
        printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
        goto err;
    }

    ourport->tx_claimed = 1;

    dbg("s3c24xx_serial_startup ok\n");
    //===============================================================================================
    #if defined(CONFIG_S5P_UART_DMA_EN)
    
        if (s3c2410_dma_request(ourport->dma_ch_tx, &s5p_uart_dma_client, NULL)) {
            printk(KERN_ERR  "unable to get DMA channel for UART tx.\n" );
            ourport->dma_ch_tx = 0;
            goto err;
        }
           s3c2410_dma_set_buffdone_fn(ourport->dma_ch_tx, s5pc1xx_uart_tx_buffdone);
        s3c2410_dma_set_opfn(ourport->dma_ch_tx,  NULL);
    
        s3c2410_dma_devconfig(ourport->dma_ch_tx, S3C2410_DMASRC_MEM, 0, (port->mapbase + S3C2410_UTXH) );
        s3c2410_dma_config(ourport->dma_ch_tx, S3C_DMA_XFER_BYTE, 0);
        s3c2410_dma_setflags(ourport->dma_ch_tx, S3C2410_DMAF_AUTOSTART);
    
    //for rx
        if (s3c2410_dma_request(ourport->dma_ch_rx, &s5p_uart_dma_client_rx, NULL)) {
            printk(KERN_ERR  "unable to get DMA channel for UART rx.\n" );
            ourport->dma_ch_rx = 0;
            goto err;
        }
        s3c2410_dma_set_buffdone_fn(ourport->dma_ch_rx, s5pc1xx_uart_rx_buffdone);
        s3c2410_dma_set_opfn(ourport->dma_ch_rx,  NULL);
    
        s3c2410_dma_devconfig(ourport->dma_ch_rx, S3C2410_DMASRC_HW, 0, (port->mapbase + S3C2410_URXH) );
        s3c2410_dma_config(ourport->dma_ch_rx, S3C_DMA_XFER_BYTE, 0);
        s3c2410_dma_setflags(ourport->dma_ch_rx, S3C2410_DMAF_AUTOSTART);
    
        ucon = rd_regl(port, S3C2410_UCON);
        ucon &= ~(S3C2410_UCON_RXDMAMODE_MASK);
           ucon |= S3C2410_UCON_RXDMAMODE_SIG1;
        wr_regl(port, S3C2410_UCON, ucon);
    
        ucon = rd_regl(port, S3C2410_UFCON);
        ucon &= ~(3<<4);
        wr_regl(port, S3C2410_UFCON, ucon);
    
        ourport->rx_buf.buf = 0;
        ourport->rx_buf.buf = (unsigned char *)dma_alloc_coherent(NULL, UART_RX_SIZE, &rx_buf_dma_handle, GFP_KERNEL);
        ourport->rx_buf.head = 0;
        ourport->rx_buf.tail = 0;
    
        s3c2410_dma_enqueue(ourport->dma_ch_rx, (void *) ourport, (dma_addr_t) rx_buf_dma_handle, UART_RX_SIZE/2);
        s3c2410_dma_enqueue(ourport->dma_ch_rx, (void *) ourport, (dma_addr_t) (rx_buf_dma_handle + (UART_RX_SIZE/2)) , UART_RX_SIZE/2);
    
        init_timer( &(ourport->rx_dma_timer) );
    
        ourport->rx_dma_timer.data = (unsigned long)(ourport);
        ourport->rx_dma_timer.function = (void *)s5p_serial_rx_dma_timeout;
        ourport->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
        add_timer(&(ourport->rx_dma_timer));
    //for rx
    #endif
    //===============================================================================================
    /* the port reset code should have done the correct
     * register setup for the port controls */

    return ret;

err:
    s3c24xx_serial_shutdown(port);
    return ret;
}

4.定义s3c24xx_485_tx_chars,英文注释的内容是关键
static irqreturn_t s3c24xx_485_tx_chars(int irq, void *id)
{
    struct s3c24xx_uart_port *ourport = id;
    struct uart_port *port = &ourport->port;
    struct circ_buf *xmit = &port->state->xmit;
    int count = 256;
    //------------------------------------------------------
    if (port->line == 0) { //COM0(GPC7)->LED3(GPK6)
        gpio_set_value(S3C64XX_GPK(6),0);//发送前LED1灯亮
        gpio_set_value(S3C64XX_GPC(7),1);//发送前改为发送状态
    }
    if (port->line == 2) { //COM2(GPC6)->LED4(GPK7)
        gpio_set_value(S3C64XX_GPK(7),0);//发送前LED1灯亮
        gpio_set_value(S3C64XX_GPC(6),1);//发送前改为发送状态
    }
    //------------------------------------------------------
    if (port->x_char) {
        wr_regb(port, S3C2410_UTXH, port->x_char);
        port->icount.tx++;
        port->x_char = 0;
        goto out;
    }

    /* if there isnt anything more to transmit, or the uart is now
     * stopped, disable the uart and exit
    */

    if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
        s3c24xx_485_stop_tx(port);
        goto out;
    }

    /* try and drain the 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);
        port->icount.tx++;
    }

    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
        uart_write_wakeup(port);

    if (uart_circ_empty(xmit))
        s3c24xx_485_stop_tx(port);

out:
    return IRQ_HANDLED;
}

5.最后定义s3c24xx_485_start_tx和s3c24xx_485_stop_tx
static void s3c24xx_485_start_tx(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);
    //------------------------------------------------------
    if (port->line == 0) { //COM0(GPC7)->LED3(GPK6)
        gpio_set_value(S3C64XX_GPK(6),0);//发送前LED1灯亮
        gpio_set_value(S3C64XX_GPC(7),1);//发送前改为发送状态
    }
    if (port->line == 2) { //COM2(GPC6)->LED4(GPK7)
        gpio_set_value(S3C64XX_GPK(7),0);//发送前LED1灯亮
        gpio_set_value(S3C64XX_GPC(6),1);//发送前改为发送状态
    }
    //------------------------------------------------------
    #if defined(CONFIG_S5P_UART_DMA_EN)
        if (!ourport->dma_busy) {
            if (port->flags & UPF_CONS_FLOW)
                s3c24xx_serial_rx_disable(port);
            tx_enabled(port) = 1;
            s5pc1xx_uart_enqueue(ourport);
        }
    #else
        if (!tx_enabled(port)) {
            if (port->flags & UPF_CONS_FLOW)
                s3c24xx_serial_rx_disable(port);
    
            enable_irq(ourport->tx_irq);
            tx_enabled(port) = 1;
        }
    #endif
}

static void s3c24xx_485_stop_tx(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);
    int iDelay = 0;
    if (tx_enabled(port)) {
        disable_irq_nosync(ourport->tx_irq);
        tx_enabled(port) = 0;
        if (port->flags & UPF_CONS_FLOW)
            s3c24xx_serial_rx_enable(port);
    }
    
    while(!s3c24xx_serial_txempty_nofifo(port))//如果发送没完成,则延时
    {
        mdelay(3);
        iDelay++;
        if (iDelay > 16)
            break;
    }
    
    //------------------------------------------------------
    if (port->line == 0) { //COM0(GPC7)->LED3(GPK6)
        gpio_set_value(S3C64XX_GPK(6),1);//发送完成后灭
        gpio_set_value(S3C64XX_GPC(7),0);//发送完成后改为接收状态
    }    
    if (port->line == 2) { //COM2(GPC6)->LED4(GPK7)
        gpio_set_value(S3C64XX_GPK(7),1);//发送完成后灭
        gpio_set_value(S3C64XX_GPC(6),0);//发送完成后改为接收状态
    }    
    //------------------------------------------------------

}

6.最后编译内核,如果你已经编译过内核,则需要删除samsung.o再重新编译

附件提供修改过的驱动文件以及没有修改过的原文件,仅供参考。
本部分内容设定了隐藏,需要回复后才能看到

最后,能找到的一些参考资料
https://groups.google.com/forum/?fromgroups=#!topic/linux-kernel-proxy/q2GrrKo_fik
http://www.mcu123.com/news/article/interface/rs232/200607/15.html
级别: 新手上路
UID: 95776
精华: 0
发帖: 12
金钱: 60 两
威望: 12 点
贡献值: 0 点
综合积分: 24 分
注册时间: 2013-08-24
最后登录: 2014-05-15
1楼  发表于: 2013-08-26 13:24
Thanks.....