3109|0

4

帖子

0

TA的资源

一粒金砂(初级)

楼主
 

i.MX27处理器实现串口扩展 [复制链接]

                摘要:介绍在i.MX27芯片上利用16C652芯片来扩展串口的方案,详细阐述i.MX27芯片与16C652芯片之间的接口设计、CPLD设计、驱动设计。

关键词: i.MX27; 16C652; 串口扩展; ARM9嵌入式系统;工业控制。

 引 言

在工业控制中需要的大量的现场控制总线,如CAN,Profibus,MODBUS,RS485/RS422,RS232.同时在工业控制,仪器仪表,医疗器械等非消费类领域,嵌入式处理器占到绝大部分,而作为控制中枢的嵌入式处理器串口往往只有三、四个,为了实现对多个外设的控制,需要对串口进行扩展。ARM芯片是目前在嵌入式系统中应用得最多的一种处理器内核可运行linuxWINCEVxWORKS等操作系统拥有包括LCD、串口、网络通讯、存储芯片等大量外围接口。开发板平台使用成都莱得的FlexG1工控板.

注:本文章只介绍扩展2个串口的设计,需要扩展4个串口选用 16C654即可,如果需要增加更多的串口,增加16C654即可增加UART扩展个数,设计思路完全相同。

 硬件设计

1 串口扩展硬件图

在图1,电路图由4部分组成:i.MX27处理器、CPLD16C652DB9连接器。其中i.MX27处理器和DB9连接器没有在图中画出来。片选选用i.MX27CS4,起始地址为:0xD400 0000

,地址信号A0A1A2决定了16C652不同的寄存器。

 CPLD设计

CPLD实现如下几个功能:电平转换:由于i.MX27WEIM总线为1.8V,需要CPLD实现1.8V-3.0V的电平转换;UART片选、读写信号:16C652的两个串口的片选信号由CS4_BGPIO_CS4产生。GPIO_CS40,以及CS4_B0UART_CSA片选有效;GPIO_CS41,以及CS4_B0UART_CSB片选有效;

详细CPLD代码如下:

library ieee;

use ieee.std_logic_1164.all;

 

entity UART is

    port(

         A:in std_logic_vector(0 to 2);

         UART_A:out std_logic_vector(0 to 2);

         D:inout std_logic_vector(0 to 7);

         UART_D:inout std_logic_vector(0 to 7);

        

         GPIO_CS4:in std_logic;

         CS4_B:in std_logic;

         OE_B:in std_logic;

         RW_B:in std_logic;

        

         UART_CSA_B:out std_logic;

         UART_CSB_B:out std_logic;

         UART_OE_B:out std_logic;

         UART_RW_B:out std_logic

        

);

 

end;

 

architecture control of UART is

 

begin

 

-- Chip Select Outputs

 

    UART_CSA_B <= '0' when (CS4_B = '0' and GPIO_CS4 = '0') else '1';

    UART_CSB_B <= '0' when (CS4_B = '0' and GPIO_CS4 = '1') else '1';

-- level shift 

   

   UART_A<=A;

  

   UART_OE_B <= '0' when (OE_B = '0' and (UART_CSA = '0' or UART_CSB = '0')) else '1';

   UART_RW_B <= '0' when (RW_B = '0' and (UART_CSA = '0' or UART_CSB = '0')) else '1';

 

process(D,UART_D,UART_OE_B,UART_RW_B,UART_OE_B)

  begin

    if (UART_OE_B = '0') and (UART_RW_B = '1') then

       D <=  UART_D;

    elsif (UART_OE_B = '1') and (UART_RW_B = '0') then

       UART_D <= D;

   

    else

          D<="ZZZZZZZZ";

    end if;                

end process;

  

end control;

 软件的设计

    sc16c652是一款集成了2路标准异步串行收发器的串口扩展芯片,也就是通常所说的8250兼容串口。它的操作方法和寄存器功能与8250完全兼容,因此我们可以用基于linux内经典的8250驱动来驱动sc16c652,只需要根据硬件的设计,稍微修改一下标准的8250驱动,下面我们就在8250驱动的基础上根据硬件的设计来实现sc16c652的驱动程序

 

    首先来看看硬件上是如何来实现双串口的收发流程的。根据cpld程序的逻辑,当驱动访问 0xD400_0000----0xD5FF_FFFF 32M地址空间的时候,CS4_B引脚会自动变为低电平。然后可以通过控制GPIO_CS4来选择串口号,当GPIO_CS4为高电平的时候,CPLD的输出为低的时候UART_CSB_B为低,选中串口A,当GPIO_CS4为低电平的时候,CPLD输出UART_CSA_B为低,选择串口B,这样就实现了串口号的逻辑选择,然后数据接收采用中断方式,我们硬件设计如下

MX27_PIN_SSI1_FS:      gpio 输出 做串口芯片的复位引脚,下降沿复位100us

MX27_PIN_SSI1_TXDAT  gpio 输入 禁止上拉 上升沿触发 串口1的接收中断引脚

MX27_PIN_SSI1_RXDAT  gpio 输入 禁止上拉 上升沿触发 串口2的接收中断引脚

由以下两个引脚来决定选中那个串口号

MX27_PIN_SSI1_CLK    gpio 输出也就是GPIO_CS4 可以用来选择串口号 [0--->串口1 1-->串口2]

CS4_B: 输出,当我们0xD400_0000----0xD5FF_FFFF这段地址的时候,始终保持为低电平

 

硬件设计清楚后,然后,我们来设计软件部分,首先需要在板级初始化文件 arch/arm/mach-mx27/mx27mdk27v0.c中,做如下初始化

1.根据硬件设计来分配串口的资源数据

 

/*!

 * The serial port definition structure. The fields contain:

 * {UART, CLK, PORT, IRQ, FLAGS}

 */

static struct plat_serial8250_port serial_platform_data[] = {

    {

     .membase = (void __iomem *)(CS4_BASE_ADDR_VIRT), //驱动中必须用内核虚拟地址来访问寄存器

     .mapbase = (unsigned long)(CS4_BASE_ADDR), //寄存器实际的物理地址

     .irq = IOMUX_TO_IRQ(MX27_PIN_SSI1_TXDAT), //获得该GPIO引脚所对应的中断编号,mx27的每个GPIO口都可以配置为中断模式

     .uartclk = 14745600,   //根据实际外接的晶振来设置

     .regshift = 1, //根据硬件设计和WEIM口数据总线位数来决定

     .iotype = UPIO_MEM, //按字节方式读写寄存器

     .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,

     /*.pm = serial_platform_pm, */

     },

     {

     .membase = (void __iomem *)(CS4_BASE_ADDR_VIRT + 0x10),

     .mapbase = (unsigned long)(CS4_BASE_ADDR + 0x10),

     .irq = IOMUX_TO_IRQ(MX27_PIN_SSI1_RXDAT),

     .uartclk = 14745600,

     .regshift = 1,

     .iotype = UPIO_MEM,

     .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,

     /*.pm = serial_platform_pm, */

     },

    {},

};

 

/*!

 * REVISIT: document me

 */

static struct platform_device serial_device = {

    .name = "serial8250",

    .id = 0,

    .dev = {

        .platform_data = serial_platform_data,

        },

};

2.初始化相关各个引脚的功能和初始化状态,复位串口芯片,添加和注册一个平台驱动设备

static int __init mxc_init_extuart(void)

{

    int ret = 0;

//MX27_PIN_SSI1_FS GPIO用,做串口复位引脚

    ret = gpio_request_mux(MX27_PIN_SSI1_FS, GPIO_MUX_GPIO); 

    if (ret) {

        printk(KERN_ERR "Request MUX SSI1_FS failed.\n");

    }

    //设置为输出

    mxc_set_gpio_direction(MX27_PIN_SSI1_FS, 0);

    //给串口芯片一个复位信号,重新复位芯片

    mxc_set_gpio_dataout(MX27_PIN_SSI1_FS, 1); 

    udelay(1000);

    mxc_set_gpio_dataout(MX27_PIN_SSI1_FS, 0);

//    MX27_PIN_SSI1_TXDAT 做串口1的接收中断引脚

    ret = gpio_request_mux(MX27_PIN_SSI1_TXDAT, GPIO_MUX_GPIO); 

    if (ret) {

        printk(KERN_ERR "Request MUX SSI1_TXDAT failed.\n");

    }

    //设置为输入

    mxc_set_gpio_direction(MX27_PIN_SSI1_TXDAT, 1);

    //禁止上拉

    gpio_set_puen(MX27_PIN_SSI1_TXDAT, 0);

    set_irq_type(IOMUX_TO_IRQ(MX27_PIN_SSI1_TXDAT), IRQT_RISING);

//    MX27_PIN_SSI1_CLK 做片选输出引脚也就是原理图上的GPIO_CS4

    ret = gpio_request_mux(MX27_PIN_SSI1_CLK, GPIO_MUX_GPIO);

    if (ret) {

        printk(KERN_ERR "Request MUX SSI1_CLK failed.\n");

    }

//设置为输出

    mxc_set_gpio_direction(MX27_PIN_SSI1_CLK, 0);

    mxc_set_gpio_dataout(MX27_PIN_SSI1_CLK, 1);

//    MX27_PIN_SSI1_RXDAT 做串口2的接收中断引脚

    ret = gpio_request_mux(MX27_PIN_SSI1_RXDAT, GPIO_MUX_GPIO); 

    if (ret) {

        printk(KERN_ERR "Request MUX SSI1_RXDAT failed.\n");

    }

//做输入用

    mxc_set_gpio_direction(MX27_PIN_SSI1_RXDAT, 1);

    gpio_set_puen(MX27_PIN_SSI1_RXDAT, 0);

    set_irq_type(IOMUX_TO_IRQ(MX27_PIN_SSI1_RXDAT), IRQT_RISING);

//注册平台设备

    ret = platform_device_register(&serial_device);

    if (ret < 0) {

        pr_info("Error: register external uart failure\n");

    }

    return ret;

}

}

3.编写驱动程序

drivers/serial/8250.c就是8250兼容芯片的驱动程序,只需要根据硬件特性,修改该文件即可

 

1.在读取和设置寄存器的时候,根据串口编号来控制GPIO_CS4的输出,根据CPLD中预设的逻辑,当GPIO_CS4输出高电平的时候,片选选中串口1,输出低电平的时候,片选选中串口2,所以我们做出如下修改

static unsigned int serial_in(struct uart_8250_port *up, int offset)

{

        unsigned int tmp;

        unsigned long flags;

        unsigned int ret;

        int locked = spin_trylock_irqsave(&up->port.lock, flags);

        offset = map_8250_in_reg(up, offset) << up->port.regshift;

 

       

#if 1  

        if (up->port.line <= 1) {

                mxc_set_gpio_dataout(MX27_PIN_SSI1_CLK, (up->port.line == 0) ? 0 : 1);

        } else {

                printk(KERN_ERR "sc16c652 has just 2 port but line index is %d\n", up->port.line);

                if (locked) {

                        spin_unlock_irqrestore(&up->port.lock, flags);

                }

                return -EINVAL;

        }

#endif        

 }      

static void serial_out(struct uart_8250_port *up, int offset, int value)

{

        /* Save the offset before it's remapped */

        int save_offset = offset;

        unsigned long flags;

        int locked = spin_trylock_irqsave(&up->port.lock, flags);

        offset = map_8250_out_reg(up, offset) << up->port.regshift;

 

       

#if 1  

        if (up->port.line <= 1) {

                mxc_set_gpio_dataout(MX27_PIN_SSI1_CLK, up->port.line);

    } else {

                printk(KERN_ERR "sc16c652 has just 2 port but line index is %d\n", up->port.line);

                if (locked) {

                        spin_unlock_irqrestore(&up->port.lock, flags);

                }

                return;

        }

#endif 

   

 

}

在模块初始化函数中,最好在复位一下芯片

static int __init serial8250_init(void)

{

    mxc_set_gpio_dataout(MX27_PIN_SSI1_CLK, 0);

    mxc_set_gpio_dataout(MX27_PIN_SSI1_FS, 1);

    udelay(100);

    mxc_set_gpio_dataout(MX27_PIN_SSI1_FS, 0);

}

这样就完成了sc16c552的串口驱动,驱动加载成功后的设备文件名是 /dev/ttyS0 /dev/ttyS1,也可以通过修改udev的规则文件来修改设备文件名

 

驱动加载成功后,打印的调试信息如下

Serial: 8250/16550 driver $Revision: 1.1.1.1 $ 2 ports, IRQ sharing disabled

serial8250.0: ttyS0 at MMIO 0xd4000000 (irq = 150) is a ST16650V2

serial8250.0: ttyS1 at MMIO 0xd4000010 (irq = 149) is a ST16650V2

驱动侦测出来的设备类型是 ST16650V2,这个芯片和SC16C552是一样的

 

关于如何来调试 :

   进入控制台后,使用cat /proc/tty/driver/serial("serial"8250默认的平台驱动名),看看IO地址,中断是否设置正常,如果有错就继续改改相关位置的代码即可。

 结束语

串口扩展在很多嵌入式处理器上都可以实现,在工业上使用串口需要大数据量,长时间的串口通信,不丢数据。

文章版权属于成都莱得科技有限责任公司所有,转载请注明出处。

网址:www.nidetech.com,联系电话:18080873876,技术交流QQ1460879610

 

 

点赞 关注

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表