搜索
bottom↓
回复: 1

【正点原子Linux连载】第六十三章Linux RS232/485/GPS驱动实验--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南

[复制链接]

出0入234汤圆

发表于 2020-7-10 10:57:08 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2020-10-26 12:04 编辑

1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html  
4)对正点原子Linux感兴趣的同学可以加群讨论:
876919289 QQ群头像.png
5)关注正点原子公众号,获取最新资料


100846rel79a9p4uelap24.jpg

100846f1ce1fg14zbg0va4.png

第六十三章Linux RS232/485/GPS驱动实验


        串口是很常用的一个外设,在Linux下通常通过串口和其他设备或传感器进行通信,根据电平的不同,串口分为TTL和RS232。不管是什么样的接口电平,其驱动程序都是一样的,通过外接RS485这样的芯片就可以将串口转换为RS485信号,正点原子的I.MX6U-ALPHA开发板就是这么做的。对于正点原子的I.MX6U-ALPHA开发板而言, RS232、RS485以及GPS模块接口通通连接到了I.MX6U的UART3接口上,因此这些外设最终都归结为UART3的串口驱动。本章我们就来学习一下如何驱动I.MX6U-ALPHA开发板上的UART3串口,进而实现RS232、RS485以及GSP驱动。



63.1 Linux下UART驱动框架
        1、uart_driver注册与注销
        同I2C、SPI一样,Linux也提供了串口驱动框架,我们只需要按照相应的串口框架编写驱动程序即可。串口驱动没有什么主机端和设备端之分,就只有一个串口驱动,而且这个驱动也已经由NXP官方已经编写好了,我们真正要做的就是在设备树中添加所要使用的串口节点信息。当系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生成/dev/ttymxcX(X=0….n)文件。
虽然串口驱动不需要我们去写,但是串口驱动框架我们还是需要了解的,uart_driver结构体表示UART驱动,uart_driver定义在include/linux/serial_core.h文件中,内容如下:
示例代码63.1.1 uart_driver结构体
  1. 295struct uart_driver {
  2. 296struct module                *owner;                        /* 模块所属者        */
  3. 297constchar                *driver_name;        /* 驱动名字                */
  4. 298constchar        *dev_name;        /* 设备名字                */
  5. 299int                major;        /* 主设备号                */
  6. 300int                minor;        /* 次设备号                */
  7. 301int                nr;        /* 设备数                */
  8. 302struct console          *cons;                /* 控制台                */
  9. 303
  10. 304/*
  11. 305      * these are private; the low level driver should not
  12. 306      * touch these; they should be initialised to NULL
  13. 307      */
  14. 308struct uart_state   *state;
  15. 309struct tty_driver   *tty_driver;
  16. 310};
复制代码

        每个串口驱动都需要定义一个uart_driver,加载驱动的时候通过uart_register_driver函数向系统注册这个uart_driver,此函数原型如下:
int uart_register_driver(struct uart_driver *drv)
函数参数和返回值含义如下:
drv:要注册的uart_driver。
        返回值:0,成功;负值,失败。
        注销驱动的时候也需要注销掉前面注册的uart_driver,需要用到uart_unregister_driver函数,函数原型如下:
  1. void uart_unregister_driver(struct uart_driver *drv)
复制代码

函数参数和返回值含义如下:
drv:要注销的uart_driver。
        返回值:无。
        2、uart_port的添加与移除
        uart_port表示一个具体的port,uart_port定义在include/linux/serial_core.h文件,内容如下(有省略):
示例代码63.1.2 uart_port结构体
  1. 117struct uart_port {
  2. 118     spinlock_t              lock;                /* port lock                         */
  3. 119unsignedlong        iobase;        /* in/out[bwl]                 */
  4. 120unsignedchar __iomem   *membase;        /* read/write[bwl]         */
  5. ......
  6. 235conststruct uart_ops   *ops;
  7. 236unsignedint        custom_divisor;
  8. 237unsignedint        line;        /* port index                         */
  9. 238unsignedint        minor;
  10. 239     resource_size_t     mapbase;        /* for ioremap                 */
  11. 240     resource_size_t     mapsize;
  12. 241struct device       *dev;        /* parent device                 */
  13. ......
  14. 250};
复制代码

        uart_port中最主要的就是第235行的ops,ops包含了串口的具体驱动函数,这个我们稍后再看。每个UART都有一个uart_port,那么uart_port是怎么和uart_driver结合起来的呢?这里要用到uart_add_one_port函数,函数原型如下:
  1. int uart_add_one_port(struct uart_driver          *drv,
  2.                                   struct uart_port                 *uport)
复制代码

函数参数和返回值含义如下:
        drv:此port对应的uart_driver。
        uport:要添加到uart_driver中的port。
        返回值:0,成功;负值,失败。
        卸载UART驱动的时候也需要将uart_port从相应的uart_driver中移除,需要用到uart_remove_one_port函数,函数原型如下:
  1. int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
复制代码

函数参数和返回值含义如下:
        drv:要卸载的port所对应的uart_driver。
        uport:要卸载的uart_port。
        返回值:0,成功;负值,失败。
        3、uart_ops实现
        在上面讲解uart_port的时候说过,uart_port中的ops成员变量很重要,因为ops包含了针对UART具体的驱动函数,Linux系统收发数据最终调用的都是ops中的函数。ops是uart_ops类型的结构体指针变量,uart_ops定义在include/linux/serial_core.h文件中,内容如下:
示例代码63.1.3 uart_ops结构体
  1. 49struct uart_ops {
  2. 50unsignedint(*tx_empty)(struct uart_port *);
  3. 51void(*set_mctrl)(struct uart_port *,unsignedint mctrl);
  4. 52unsignedint(*get_mctrl)(struct uart_port *);
  5. 53void(*stop_tx)(struct uart_port *);
  6. 54void(*start_tx)(struct uart_port *);
  7. 55void(*throttle)(struct uart_port *);
  8. 56void(*unthrottle)(struct uart_port *);
  9. 57void(*send_xchar)(struct uart_port *,char ch);
  10. 58void(*stop_rx)(struct uart_port *);
  11. 59void(*enable_ms)(struct uart_port *);
  12. 60void(*break_ctl)(struct uart_port *,int ctl);
  13. 61int        (*startup)(struct uart_port *);
  14. 62void(*shutdown)(struct uart_port *);
  15. 63void(*flush_buffer)(struct uart_port *);
  16. 64void(*set_termios)(struct uart_port *,struct ktermios *new,
  17. 65struct ktermios *old);
  18. 66void(*set_ldisc)(struct uart_port *,struct ktermios *);
  19. 67void(*pm)(struct uart_port *,unsignedint state,
  20. 68unsignedint oldstate);
  21. 69
  22. 70/*
  23. 71   * Return a string describing the type of the port
  24. 72   */
  25. 73constchar*(*type)(struct uart_port *);
  26. 74
  27. 75/*
  28. 76   * Release IO and memory resources used by the port.
  29. 77   * This includes iounmap if necessary.
  30. 78   */
  31. 79void(*release_port)(struct uart_port *);
  32. 80
  33. 81/*
  34. 82   * Request IO and memory resources used by the port.
  35. 83   * This includes iomapping the port if necessary.
  36. 84   */
  37. 85int(*request_port)(struct uart_port *);
  38. 86void(*config_port)(struct uart_port *,int);
  39. 87int(*verify_port)(struct uart_port *,struct serial_struct *);
  40. 88int(*ioctl)(struct uart_port *,unsignedint,unsignedlong);
  41. 89 #ifdef CONFIG_CONSOLE_POLL
  42. 90int(*poll_init)(struct uart_port *);
  43. 91void(*poll_put_char)(struct uart_port *,unsignedchar);
  44. 92int(*poll_get_char)(struct uart_port *);
  45. 93 #endif
  46. 94};
复制代码

        UART驱动编写人员需要实现uart_ops,因为uart_ops是最底层的UART驱动接口,是实实在在的和UART寄存器打交道的。关于uart_ops结构体中的这些函数的具体含义请参考Documentation/serial/driver这个文档。
        UART驱动框架大概就是这些,接下来我们理论联系实际,看一下NXP官方的UART驱动文件是如何编写的。
63.2 I.MX6U UART驱动分析
        1、UART的platform驱动框架
        打开imx6ull.dtsi文件,找到UART3对应的子节点,子节点内容如下所示:
示例代码63.2.1 uart3设备节点
  1. 1  uart3: serial@021ec000 {
  2. 2        compatible ="fsl,imx6ul-uart",
  3. 3"fsl,imx6q-uart","fsl,imx21-uart";
  4. 4        reg =<0x021ec0000x4000>;
  5. 5        interrupts =<GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
  6. 6        clocks =<&clks IMX6UL_CLK_UART3_IPG>,
  7. 7<&clks IMX6UL_CLK_UART3_SERIAL>;
  8. 8        clock-names ="ipg","per";
  9. 9        dmas =<&sdma 2940>,<&sdma 3040>;
  10. 10       dma-names ="rx","tx";
  11. 11       status ="disabled";
  12. 12};
复制代码

        重点看一下第2,3行的compatible属性,这里一共有三个值:“fsl,imx6ul-uart”、“fsl,imx6q-uar”和“fsl,imx21-uart”。在uboot源码中搜索这三个值即可找到对应的UART驱动文件,此文件为drivers/tty/serial/imx.c,在此文件中可以找到如下内容:
示例代码63.2.2 UART platform驱动框架
  1. 267staticstruct platform_device_id imx_uart_devtype[]={
  2. 268{
  3. 269.name ="imx1-uart",
  4. 270.driver_data =(kernel_ulong_t)&imx_uart_devdata[IMX1_UART],
  5. 271},{
  6. 272.name ="imx21-uart",
  7. 273.driver_data =(kernel_ulong_t)
  8. &imx_uart_devdata[IMX21_UART],
  9. 274},{
  10. 275.name ="imx6q-uart",
  11. 276.driver_data =(kernel_ulong_t)
  12. &imx_uart_devdata[IMX6Q_UART],
  13. 277},{
  14. 278/* sentinel */
  15. 279}
  16. 280};
  17. 281 MODULE_DEVICE_TABLE(platform, imx_uart_devtype);
  18. 282
  19. 283staticconststruct of_device_id imx_uart_dt_ids[]={
  20. 284{.compatible ="fsl,imx6q-uart",.data =
  21. &imx_uart_devdata[IMX6Q_UART],},
  22. 285{.compatible ="fsl,imx1-uart",.data =
  23. &imx_uart_devdata[IMX1_UART],},
  24. 286{.compatible ="fsl,imx21-uart",.data =
  25. &imx_uart_devdata[IMX21_UART],},
  26. 287{/* sentinel */}
  27. 288};
  28. ......
  29. 2071staticstruct platform_driver serial_imx_driver ={
  30. 2072.probe            = serial_imx_probe,
  31. 2073.remove          = serial_imx_remove,
  32. 2074
  33. 2075.suspend         = serial_imx_suspend,
  34. 2076.resume          = serial_imx_resume,
  35. 2077.id_table        = imx_uart_devtype,
  36. 2078.driver           ={
  37. 2079.name         ="imx-uart",
  38. 2080.of_match_table = imx_uart_dt_ids,
  39. 2081},
  40. 2082};
  41. 2083
  42. 2084staticint __init imx_serial_init(void)
  43. 2085{
  44. 2086int ret = uart_register_driver(&imx_reg);
  45. 2087
  46. 2088if(ret)
  47. 2089return ret;
  48. 2090
  49. 2091    ret = platform_driver_register(&serial_imx_driver);
  50. 2092if(ret !=0)
  51. 2093        uart_unregister_driver(&imx_reg);
  52. 2094
  53. 2095return ret;
  54. 2096}
  55. 2097
  56. 2098staticvoid __exit imx_serial_exit(void)
  57. 2099{
  58. 2100    platform_driver_unregister(&serial_imx_driver);
  59. 2101    uart_unregister_driver(&imx_reg);
  60. 2102}
  61. 2103
  62. 2104 module_init(imx_serial_init);
  63. 2105 module_exit(imx_serial_exit);
复制代码

        可以看出I.MX6U的UART本质上是一个platform驱动,第267~280行,imx_uart_devtype为传统匹配表。
        第283~288行,设备树所使用的匹配表,第284行的compatible属性值为“fsl,imx6q-uart”。
        第2071~2082行,platform驱动框架结构体serial_imx_driver。
        第2084~2096行,驱动入口函数,第2086行调用uart_register_driver函数向Linux内核注册uart_driver,在这里就是imx_reg。
        第2098~2102行,驱动出口函数,第2101行调用uart_unregister_driver函数注销掉前面注册的uart_driver,也就是imx_reg。
        2、uart_driver初始化
        在imx_serial_init函数中向Linux内核注册了imx_reg,imx_reg就是uart_driver类型的结构体变量,imx_reg定义如下:
示例代码63.2.3 imx_reg结构体变量
  1. 1836staticstruct uart_driver imx_reg ={
  2. 1837.owner                  = THIS_MODULE,
  3. 1838.driver_name            = DRIVER_NAME,
  4. 1839.dev_name               = DEV_NAME,
  5. 1840.major                  = SERIAL_IMX_MAJOR,
  6. 1841.minor                  = MINOR_START,
  7. 1842.nr                     = ARRAY_SIZE(imx_ports),
  8. 1843.cons                   = IMX_CONSOLE,
  9. 1844};
复制代码

        3、uart_port初始化与添加
        当UART设备和驱动匹配成功以后serial_imx_probe函数就会执行,此函数的重点工作就是初始化uart_port,然后将其添加到对应的uart_driver中。在看serial_imx_probe函数之前先来看一下imx_port结构体,imx_port是NXP为I.MX系列SOC定义的一个设备结构体,此结构体内部就包含了uart_port成员变量,imx_port结构体内容如下所示(有缩减):
示例代码63.2.4 imx_port结构体
  1. 216struct imx_port {
  2. 217struct uart_port          port;
  3. 218struct timer_list          timer;
  4. 219unsignedint        old_status;
  5. 220unsignedint        have_rtscts:1;
  6. 221unsignedint        dte_mode:1;
  7. 222unsignedint        irda_inv_rx:1;
  8. 223unsignedint        irda_inv_tx:1;
  9. 224unsignedshort        trcv_delay;/* transceiver delay */
  10. ......
  11. 243unsignedlong        flags;
  12. 245};
复制代码

        第217行,uart_port成员变量port。
        接下来看一下serial_imx_probe函数,函数内容如下:
示例代码63.2.5 serial_imx_probe函数
  1. 1969staticint serial_imx_probe(struct platform_device *pdev)
  2. 1970{
  3. 1971struct imx_port *sport;
  4. 1972void __iomem *base;
  5. 1973int ret =0;
  6. 1974struct resource *res;
  7. 1975int txirq, rxirq, rtsirq;
  8. 1976
  9. 1977    sport = devm_kzalloc(&pdev->dev,sizeof(*sport), GFP_KERNEL);
  10. 1978if(!sport)
  11. 1979return-ENOMEM;
  12. 1980
  13. 1981    ret = serial_imx_probe_dt(sport, pdev);
  14. 1982if(ret >0)
  15. 1983        serial_imx_probe_pdata(sport, pdev);
  16. 1984elseif(ret <0)
  17. 1985return ret;
  18. 1986
  19. 1987    res = platform_get_resource(pdev, IORESOURCE_MEM,0);
  20. 1988    base = devm_ioremap_resource(&pdev->dev, res);
  21. 1989if(IS_ERR(base))
  22. 1990return PTR_ERR(base);
  23. 1991
  24. 1992    rxirq = platform_get_irq(pdev,0);
  25. 1993    txirq = platform_get_irq(pdev,1);
  26. 1994    rtsirq = platform_get_irq(pdev,2);
  27. 1995
  28. 1996    sport->port.dev =&pdev->dev;
  29. 1997    sport->port.mapbase = res->start;
  30. 1998    sport->port.membase = base;
  31. 1999    sport->port.type = PORT_IMX,
  32. 2000    sport->port.iotype = UPIO_MEM;
  33. 2001    sport->port.irq = rxirq;
  34. 2002    sport->port.fifosize =32;
  35. 2003    sport->port.ops =&imx_pops;
  36. 2004    sport->port.rs485_config = imx_rs485_config;
  37. 2005    sport->port.rs485.flags =
  38. 2006        SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;
  39. 2007    sport->port.flags = UPF_BOOT_AUTOCONF;
  40. 2008    init_timer(&sport->timer);
  41. 2009    sport->timer.function = imx_timeout;
  42. 2010    sport->timer.data     =(unsignedlong)sport;
  43. 2011
  44. 2012    sport->clk_ipg = devm_clk_get(&pdev->dev,"ipg");
  45. 2013if(IS_ERR(sport->clk_ipg)){
  46. 2014        ret = PTR_ERR(sport->clk_ipg);
  47. 2015        dev_err(&pdev->dev,"failed to get ipg clk: %d\n", ret);
  48. 2016return ret;
  49. 2017}
  50. 2018
  51. 2019    sport->clk_per = devm_clk_get(&pdev->dev,"per");
  52. 2020if(IS_ERR(sport->clk_per)){
  53. 2021        ret = PTR_ERR(sport->clk_per);
  54. 2022        dev_err(&pdev->dev,"failed to get per clk: %d\n", ret);
  55. 2023return ret;
  56. 2024}
  57. 2025
  58. 2026    sport->port.uartclk = clk_get_rate(sport->clk_per);
  59. 2027if(sport->port.uartclk > IMX_MODULE_MAX_CLK_RATE){
  60. 2028        ret = clk_set_rate(sport->clk_per, IMX_MODULE_MAX_CLK_RATE);
  61. 2029if(ret <0){
  62. 2030            dev_err(&pdev->dev,"clk_set_rate() failed\n");
  63. 2031return ret;
  64. 2032}
  65. 2033}
  66. 2034    sport->port.uartclk = clk_get_rate(sport->clk_per);
  67. 2035
  68. 2036/*
  69. 2037     * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
  70. 2038     * chips only have one interrupt.
  71. 2039     */
  72. 2040if(txirq >0){
  73. 2041        ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint,0,
  74. 2042                       dev_name(&pdev->dev), sport);
  75. 2043if(ret)
  76. 2044return ret;
  77. 2045
  78. 2046        ret = devm_request_irq(&pdev->dev, txirq, imx_txint,0,
  79. 2047                       dev_name(&pdev->dev), sport);
  80. 2048if(ret)
  81. 2049return ret;
  82. 2050}else{
  83. 2051        ret = devm_request_irq(&pdev->dev, rxirq, imx_int,0,
  84. 2052                       dev_name(&pdev->dev), sport);
  85. 2053if(ret)
  86. 2054return ret;
  87. 2055}
  88. 2056
  89. 2057    imx_ports[sport->port.line]= sport;
  90. 2058
  91. 2059    platform_set_drvdata(pdev, sport);
  92. 2060
  93. 2061return uart_add_one_port(&imx_reg,&sport->port);
  94. 2062}
复制代码

        第1971行,定义一个imx_port类型的结构体指针变量sport。
        第1977行,为sport申请内存。
        第1987~1988行,从设备树中获取I.MX系列SOCUART外设寄存器首地址,对于I.MX6ULL的UART3来说就是0X021EC000。得到寄存器首地址以后对其进行内存映射,得到对应的虚拟地址。
        第1992~1994行,获取中断信息。
        第1996~2034行,初始化sport,我们重点关注的就是第2003行初始化sport的port成员变量,也就是设置uart_ops为imx_pops,imx_pops就是I.MX6ULL最底层的驱动函数集合,稍后再来看。
        第2040~2055行,申请中断。
        第2061行,使用uart_add_one_port向uart_driver添加uart_port,在这里就是向imx_reg添加sport->port。
        4、imx_pops结构体变量
        imx_pops就是uart_ops类型的结构体变量,保存了I.MX6ULL串口最底层的操作函数,imx_pops定义如下:
示例代码63.2.6 imx_pops结构体
  1. 1611staticstruct uart_ops imx_pops ={
  2. 1612.tx_empty                   = imx_tx_empty,
  3. 1613.set_mctrl                  = imx_set_mctrl,
  4. 1614.get_mctrl                  = imx_get_mctrl,
  5. 1615.stop_tx                    = imx_stop_tx,
  6. 1616.start_tx                   = imx_start_tx,
  7. 1617.stop_rx                    = imx_stop_rx,
  8. 1618.enable_ms                  = imx_enable_ms,
  9. 1619.break_ctl                  = imx_break_ctl,
  10. 1620.startup                    = imx_startup,
  11. 1621.shutdown                   = imx_shutdown,
  12. 1622.flush_buffer                = imx_flush_buffer,
  13. 1623.set_termios                 = imx_set_termios,
  14. 1624.type                       = imx_type,
  15. 1625.config_port                 = imx_config_port,
  16. 1626.verify_port                 = imx_verify_port,
  17. 1627 #if defined(CONFIG_CONSOLE_POLL)
  18. 1628.poll_init                   = imx_poll_init,
  19. 1629.poll_get_char          = imx_poll_get_char,
  20. 1630.poll_put_char          = imx_poll_put_char,
  21. 1631 #endif
  22. 1632};
复制代码

        imx_pops中的函数基本都是和I.MX6ULL的UART寄存器打交道的,这里就不去详细的分析了。简单的了解了I.MX6U的UART驱动以后我们再来学习一下,如何驱动正点原子I.MX6U-ALPHA开发板上的UART3接口。
63.3 硬件原理图分析
        本实验要用到的I.MX6U的UART3接口,I.MX6U-ALPHA开发板上RS232、RS485和GPS这三个接口都连接到了UART3上,我们依次来看一下这三个模块的原理图。
        1、RS232原理图
        RS232原理图如图63.3.1所示:
image002.gif

图63.3.1 RS232原理图

        从图63.3.1可以看出,RS232电平通过SP3232这个芯片来实现,RS232连接到了I.MX6U的UART3接口上,但是要通过JP1这个跳线帽设置。把JP1的1-3和2-4连接起来以后SP3232就和UART3连接到了一起。
        2、RS485原理图
        RS485原理图如图63.3.2所示:
image004.gif

图63.3.2 RS485原理图

        RS485采用SP3485这颗芯片来实现,RO为数据输出端,RI为数据输入端,RE是接收使能信号(低电平有效),DE是发送使能信号(高电平有效)。在图63.3.2中RE和DE经过一系列的电路,最终通过RS485_RX来控制,这样我们可以省掉一个RS485收发控制IO,将RS485完全当作一个串口来使用,方便我们写驱动。
        3、GPS原理图
        正点原子有一款GPS+北斗定位模块,型号为ATK1218-BD,I.MX6U-ALPHA开发板留出了这款GPS定位模块的接口,接口原理图如图63.3.3所示:
image006.gif

图63.3.3 ATK MODULE模块。

        从图63.3.3可以看出,GPS模块用的也是UART3,因此UART3驱动成功以后就可以直接读取GPS模块数据了。
63.4 RS232驱动编写
        前面我们已经说过了,I.MX6U的UART驱动NXP已经编写好了,所以不需要我们编写。我们要做的就是在设备树中添加UART3对应的设备节点即可。打开imx6ull-alientek-emmc.dts文件,在此文件中只有UART1对应的uart1节点,并没有UART3对应的节点,因此我们可以参考uart1节点创建uart3节点。
        1、UART3IO节点创建
        UART3用到了UART3_TXD和UART3_RXD这两个IO,因此要先在iomuxc中创建UART3对应的pinctrl子节点,在iomuxc中添加如下内容:
示例代码63.4.1 UART3引脚pinctrl节点
  1. 1 pinctrl_uart3: uart3grp {
  2. 2     fsl,pins =<
  3. 3         MX6UL_PAD_UART3_TX_DATA__UART3_DCE_TX       0X1b0b1
  4. 4         MX6UL_PAD_UART3_RX_DATA__UART3_DCE_RX       0X1b0b1
  5. 5>;
  6. 6};
复制代码

        最后检查一下UART3_TX和UART3_RX这两个引脚有没有被用作其他功能,如果有的话要将其屏蔽掉,保证这两个IO只用作UART3,切记!!!
        2、添加uart3节点
        默认情况下imx6ull-alientek-emmc.dts中只有uart1和uart2这两个节点,如图63.4.1所示:
image008.jpg

图63.4.1uart1和uart2节点

        uart1是UART1的,在正点原子的I.MX6U-ALPHA开发板上没有用到UART2,而且UART2默认用到了UART3的IO,因此需要将uart2这个节点删除掉,然后加上UART3对应的uart3,uart3节点内容如下:
示例代码63.4.2 UART3对应的uart3节点
  1. 1&uart3 {
  2. 2     pinctrl-names ="default";
  3. 3     pinctrl-0=<&pinctrl_uart3>;
  4. 4     status ="okay";
  5. 5};
复制代码

        完成以后重新编译设备树并使用新的设备树启动Linux,如果设备树修改成功的话,系统启动以后就会生成一个名为“/dev/ttymxc2”的设备文件,ttymxc2就是UART3对应的设备文件,应用程序可以通过访问ttymxc2来实现对UART3的操作。
63.5 移植minicom
        minicom类似我们常用的串口调试助手,是Linux下很常用的一个串口工具,将minicom移植到我们的开发板中,这样我们就可以借助minicom对串口进行读写操作。
        1、移植ncurses
        minicom需要用到ncurses,依次需要先移植ncurses,如果前面已经移植好了ncurses,那么这里就不需要再次移植了,只需要在编译minicom的时候指定ncurses库和头文件目录即可。
        首先在ubuntu中创建一个目录来存放我们要移植的文件,比如我在/home/zuozhongkai/linux/IMX6ULL目录下创建了一个名为“tool”的目录来存放所有的移植文件。然后下载ncurses源码,我们已经将ncurses源码放到了开发板光盘中,路径为:1、例程源码-》7、第三方库源码-》ncurses-6.0.tar.gz,将ncurses-6.0.tar.gz拷贝到Ubuntu中创建的tool目录下,然后进行解压,解压命令如下:
tar -vxzf ncurses-6.0.tar.gz        
        解压完成以后就会生成一个名为“ncurses-6.0”的文件夹,此文件夹就是ncureses的源码文件夹。在tool目录下新建名为“ncurses”目录,用于保存ncurses编译结果,一切准备就绪以后就可以编译ncureses库了。进入到ncureses源码目录下,也就是刚刚解压出来的ncurses-6.0目录中,首先是配置ncureses,输入如下命令:
./configure --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/ncurses --host=arm-linux-gnueabihf --with-shared
        configure就是配置脚本,--prefix用于指定编译结果的保存目录,这里肯定将编译结果保存到我们前面创建的“ncurses”目录中。--hsot用于指定编译器前缀,这里设置为“arm-linux-gnueabihf”。配置命令写好以后点击回车键,等待配置完成,配置成功以后如图63.5.1所示:
image010.jpg

图63.5.1 配置成功

配置成功以后输入“make”命令开始编译,编译成功以后如图63.5.2所示:
image012.jpg

图63.5.2 编译成功

        编译成功以后输入“makeinstall”命令安装,安装的意思就是将编译出来的结果拷贝到--pfefix指定的目录里面去。安装成功以后如图63.5.3所示:
image014.jpg

图63.5.3 安装成功

        安装成功以后查看一下前面创建的“ncurses”文件夹,会发现里面多了一些东西,如图63.5.4所示:
image016.jpg

图63.5.4 编译出来的结果

        我们需要将图63.5.4中include、lib和share这三个目录中存放的文件分别拷贝到开发板根文件系统中的/usr/include、/usr/lib和/usr/share这三个目录中,如果哪个目录不存在的话请自行创建!!拷贝命令如下:
  1. sudo cp lib/* /home/zuozhongkai/linux/nfs/rootfs/usr/lib/ -rfa
  2. sudo cp share/* /home/zuozhongkai/linux/nfs/rootfs/usr/share/ -rfa
  3. sudo cp include/* /home/zuozhongkai/linux/nfs/rootfs/usr/include/ -rfa
复制代码

        然后在开发板根目录的/etc/profile(没有的话自己创建一个)文件中添加如下所示内容:
示例代码63.5.1 /etc/provile文件
  1. 1 #!/bin/sh
  2. 2 LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
  3. 3 export LD_LIBRARY_PATH
  4. 4
  5. 5 export TERM=vt100
  6. 6 export TERMINFO=/usr/share/terminfo
复制代码

        2、移植minicom
        继续移植minicom,获取minicom源码,我们已经放到了开发板光盘中了,路径为:1、例程源码-》7、第三方库源码-》minicom-2.7.1.tar.gz。将minicom-2.7.1.tar.gz拷贝到ubuntu中的/home/zuozhongkai/linux/IMX6ULL/tool目录下,然后在tool目录下新建一个名为“minicom”的子目录,用于存放minicom编译结果。一切准备好以后就可以编译minicom了,先解压minicom,命令如下:
  1. tar -vxzf minicom-2.7.1.tar.gz
复制代码

        解压完成以后会生成一个叫做minicom-2.7.1的文件夹,这个就是minicom的源码,进入到此目录中,然后配置minicom,配置命令如下:
  1. cd minicom-2.7.1/                        //进入minicom源码目录
  2. ./configure CC=arm-linux-gnueabihf-gcc --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/
  3. minicom --host=arm-linux-gnueabihf CPPFLAGS=-I/home/zuozhongkai/linux/IMX6ULL/tool/
  4. ncurses/include  LDFLAGS=-L/home/zuozhongkai/linux/IMX6ULL/tool/ncurses/lib -enable-cfg-dir=/etc/minicom                                //配置
复制代码

        CC表示要使用的gcc交叉编译器,--prefix指定编译出来的文件存放目录,肯定要存放到我们前面创建的minicom目录中。--host指定交叉编译器前缀,CPPFLAGS指定ncurses的头文件路径,LDFLAGS指定ncurses的库路径。
        配置成功的话如图63.5.5所示:
image018.jpg

图63.5.5 配置成功

        配置成功以后执行如下命令编译并安装:
  1. make
  2. makeinstall
复制代码

        编译安装完成以后,前面创建的minicom目录内容如图63.5.6所示:
image020.jpg

图63.5.6 minicom安装编译结果

        将minicom目录中bin子目录下的所有文件拷贝到开发板根目录中的/usr/bin目录下,命令如下:
  1. sudo cp bin/* /home/zuozhongkai/linux/nfs/rootfs/usr/bin/
复制代码

        完成以后在开发板中输入“minicom-v”来查看minicom工作是否正常,结果如图63.5.7所示:
image022.jpg

图63.5.7 minicom版本号

        从图63.5.7可以看出,此时minicom版本号为2.7.1,minicom版本号查看正常。输入如下命令打开minicom配置界面:
minicom-s
        结果是打不开minicom配置界面,提示如图63.5.8所示信息:
image024.jpg

图63.5.8 minicom打开失败

        从图63.5.8可以看出,minicom异常嚣张,竟然让我们“Goaway”,这能容忍?!必须要治一下。解决方法很简单,新建/etc/passwd文件,然后在passwd文件里面输入如下所示内容:
示例代码63.5.2 /etc/passwd文件
  1. 1 root:x:0:0:root:/root:/bin/sh
复制代码

        完成以后重启开发板!
完成以后重启开发板!
完成以后重启开发板!
开发板重启以后再执行“minicom-s”命令,此时minicom配置界面就可以打开了,如图63.5.9所示:
image026.jpg

图63.5.9 mincom配置界面

        如果能出现图63.5.9所示界面,那么就说明mincom工作正常了。
63.6 RS232驱动测试
63.6.1 RS232连接设置
        在测试之前要先将I.MX6U-ALPHA开发板的RS232与电脑连接起来,首先设置JP1跳线帽,如图63.6.1.1所示:
image028.jpg

图63.6.1.1 UART3        跳线帽设置

        跳线帽设置好以后使用RS232线将开发板与电脑连接起来,这里建议使用USB转DB9(RS232)数据线,比如正点原子售卖的CH340方案的USB转公头DB9数据线,如图63.6.1.2所示:
image030.jpg

图63.6.1.2 USB转DB9数据线

        图63.6.1.2中所示的数据线是带有CH340芯片的,因此当连接到电脑以后就会出现一个COM口,这个COM口就是我们要使用的COM口。比如在我的电脑上就是COM9,在SecureCRT上新建一个连接,串口为COM9,波特率为115200。
63.6.2 minicom设置
        在开发板中输入“minicom-s”,打开minicom配置界面,然后选中“Serialportsetup”,如图63.6.2.1所示:
image032.jpg

图63.6.2.1 选中串口设置项

        选中“Serialportsetup”以后点击回车,进入设置菜单,如图63.6.2.2所示:
image034.jpg

图63.6.2.2 串口设置项

        图63.6.2.2中有7个设置项目,分别对应A、B……G,比如第一个是选中串口,UART3的串口文件为/dev/ttymxc2,因此串口设置要设置为/dev/ttymxc2。设置方法就是按下键盘上的‘A’,然后输入“/dev/ttymxc2”即可,如图63.6.2.3所示:
image036.gif

图63.6.2.3 串口设备文件设置

        设置完以后按下回车键确认,确认完以后就可以设置其他的配置项。比如E设置波特率、数据位和停止位的、F设置硬件流控的,设置方法都一样,设置完以后如图63.6.2.4所示:
image038.gif

图63.6.2.4 UART3设置

        都设置完成以后按下回车键确认并退出,这时候会退回到如图63.6.2.1所示的界面,按下ESC键退出图63.6.2.1所示的配置界面,退出以后如图63.6.2.5所示:
image040.jpg

图63.6.2.5 minicom串口界面

        图63.6.2.2就是我们的串口调试界面,可以看出当前的串口文件为/dev/ttymxc2,按下CTRL-A,然后再按下Z就可以打开minicom帮助信息界面,如图63.6.2.6所示:
image042.jpg

图63.6.2.6 minicom帮助信息界面

        从图63.6.2.6可以看出,minicom有很多快捷键,本实验我们打开minicom的回显功能,回显功能配置项为“local Echoon/off..E”,因此按下E即可打开/关闭回显功能。
63.6.3 RS232收发测试
        1、发送测试
首先测试开发板通过UART3向电脑发送数据的功能,需要打开minicom的回显功能(不打开也可以,但是在minicom中看不到自己输入的内容),回显功能打开以后输入“AAAA”,如图63.6.3.1所示:
image044.gif

图63.6.3.1 通过UART3向电脑发送“AAAA”

图63.6.3.1中的“AAAA”相当于开发板通过UART3向电脑发送“AAAA”,那么COM9就会接收到“AAAA”,SecureCRT中COM9收到的数据如图63.6.3.2所示:
image046.gif

图63.6.3.2 电脑接收到开发板发送过来的数据

        可以看出,开发板通过UART3向电脑发送数据正常,那么接下来就测试开发板数据接收功能。
        2、接收测试
        接下来测试开发板的UART3接收功能,同样的,要先打开SecureCRT上COM9的本地回显,否则的话你在COM9上输出的内容会看不到,但是实际上是已经发送给了开发板。选中SecureCRT的Options->Session Options->Adavnced,打开回话配置界面,然后选中“Localecho”,如图63.6.3.3所示:
image048.gif

图63.6.3.3 打开SecureCRT的本地回显

SecureCRT设置好以后向开发板发送一个“BBBB”,在SecureCRT的COM9上输入“BBBB”,如图63.6.3.3所示:
image050.gif

图63.6.3.3 电脑向开发板发送“BBBB”

        此时开发板的minicom就会接收到发送过来的“BBBB”,如图63.6.3.4所示:
image052.gif

图63.6.3.4 开发板接收到发送过来的数据

        UART3收发测试都没有问题,说明我们的UART3驱动工作正常。如果要退出minicom的话,在minicom通信界面按下CRTL+A,然后按下X来关闭minicom。关于minicom的使用我们这里讲的很简单,大家可以在网上查找更加详细的minicom使用教程。
63.7 RS485测试
        前面已经说过了,I.MX6U-ALPHA开发板上的RS485接口连接到了UART3上,因此本质上就是个串口。RS232实验我们已经将UART3的驱动编写好了,所以RS485实验就不需要编写任何驱动程序,可以直接使用minicom来进行测试。
63.7.1 RS485连接设置
        首先是设置JP1跳线帽,将3-5、4-6连接起来,如图63.7.1.1所示:
image054.jpg

图63.7.1.1 RS485接口设置

        一个板子是不能进行RS485通信测试的,还需要另一个RS485设备,比如另外一块I.MX6U-ALPHA开发板。这里推荐大家使用正点原子出品的USB三合一串口转换器,支持USB转TTL、RS232和RS485,如图63.7.1.2所示:
image056.jpg

图63.7.1.2正点原子USB三合一串口转换器

        使用杜邦线将USB串口转换器的RS485接口和I.MX6U-ALPHA开发板的RS485连接起来,A接A,B接B,不能接错了!连接完成以后如图63.7.1.3所示:
image058.jpg

图63.7.1.3串口转换器和开发板RS485连接示意图

        串口转换器通过USB线连接到电脑上,我用的是CH340版本的,因此就不需要安装驱动的,如果使用的是FT232版本的就需要安装相应的驱动。连接成功以后电脑就会有相应的COM口,比如我的电脑上就是COM10,接下来就是测试。
63.7.2 RS485收发测试
        RS485的测试和RS232一模一样!USB多合一转换器的COM口为10,因此使用SecureCRT创建一个COM10的连接。开发板使用UART3,对应的串口设备文件为/dev/ttymxc2,因此开发板使用minicom创建一个/dev/ttymxc2的串口连接。串口波特率都选择115200,8位数据位,1位停止位,关闭硬件和软件流控。
        1、RS485发送测试
        首先测试开发板通过RS485发送数据,设置好minicom以后,同样输入“AAAA”,也就是通过RS485向电脑发送一串“AAAA”。如果RS485驱动工作正常的话,那么电脑就会介绍到开发板发送过来的“AAAA”,如图63.7.2.1所示:
image060.gif

图63.7.2.1 RS485数据发送测试

        从图63.7.2.1可以看出开发板通过RS485向电脑发送“AAAA”成功,说明RS485数据数据发送正常。
        2、RS485接收测试
        接下来测试一下RS485数据接收,电脑通过RS485向开发板发送“BBBB”,然后观察minicom是否能接收到“BBBB”。结果如图63.7.2.2所示:
image062.gif

图63.7.2.2 RS485数据接收测试

        从图63.7.2.1可以看出开发板接收到电脑通过RS485发送过来的“BBBB”,说明RS485数据接收也正常。
63.8 GPS测试
63.8.1 GPS连接设置
        GPS模块大部分都是串口输出的,这里以正点原子出品的ATK1218-BD模块为例,这是一款GSP+北斗的定位模块,模块如图63.8.1.1所示:
image064.gif

[size=12.6316px]
图63.8.1.1正点原子ATK1218-BD定位模块

        首先要将I.MX6U-ALPHA开发板上的JP1跳线帽拔掉,不能连接RS232或RS485,否则会干扰到GSP模块。UART3_TX和UART3_RX已经连接到了开发板上的ATK MODULE上,直接将ATK1218-BD模块插到开发板上的ATK MODULE接口即可,开发板上的ATK MODULE接口是6脚的,而ATK1218-BD模块是5脚的,因此需要靠左插!然后GPS需要接上天线,天线的接收头一定要放到户外,因此室内一般是没有GPS信号的。连接完成以后如图63.8.1.2所示:
image066.jpg

图63.8.1.2 GPS模块连接示意图

63.8.2 GPS数据接收测试
        GPS我们都是被动接收定位数据的,因此打开minicom,设置/dev/ttymxc2,串口设置要求如下:
        ①、波特率设置为38400,因为正点原子的ATK1218-BD模块默认波特率就是38400。
        ②、8位数据位,1位停止位。
        ③、关闭硬件和软件流控。
设置好以后如图63.8.2.1所示:

image068.jpg        
图63.8.2.1串口设置



        设置好以后就可以静静的等待GPS数据输出,GPS模块第一次启动可能需要几分钟搜星,等搜到卫星以后才会有定位数据输出。搜到卫星以后GPS模块输出的定位数据如图63.8.2.2所示:
image070.gif

图63.8.2.2 GPS数据



阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出16170入6148汤圆

发表于 2020-7-10 11:56:14 来自手机 | 显示全部楼层
打赏!

庆祝论坛“打赏”功能实施, 现在开始发技术主题,可以获得打赏
https://www.amobbs.com/thread-5735948-1-1.html
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 17:11

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表