正点原子imx6ull-mini-Linux驱动之Linux RS232/485/GPS 驱动实验(23)
错误1:我一直找不到为什么我的minicom用不了,编译啥的都通过了,原来是我的密码文件命名错了,我就习以为常的命名为password,谁知道应该是passwd,所以以后该复制的还是复制,不然就容易找不到源头在哪。
串口是很常用的一个外设,在 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 驱动。
1:Linux 下 UART 驱动框架
1.1:uart_driver 注册与注销
同 I2C、SPI 一样,Linux 也提供了串口驱动框架,我们只需要按照相应的串口框架编写驱 动程序即可。串口驱动没有什么主机端和设备端之分,就只有一个串口驱动,而且这个驱动也 已经由 NXP 官方已经编写好了,我们真正要做的就是在设备树中添加所要使用的串口节点信 息。当系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生成 /dev/ttymxcX(X=0….n)文件。 虽然串口驱动不需要我们去写,但是串口驱动框架我们还是需要了解的,uart_driver 结构 体表示 UART 驱动,uart_driver 定义在 include/linux/serial_core.h 文件中,内容如下:
295 struct uart_driver {
296 struct module *owner; /* 模块所属者 */
297 const char *driver_name; /* 驱动名字 */
298 const char *dev_name; /* 设备名字 */
299 int major; /* 主设备号 */
300 int minor; /* 次设备号 */
301 int nr; /* 设备数 */
302 struct console *cons; /* 控制台 */
303
304 /*
305 * these are private; the low level driver should not
306 * touch these; they should be initialised to NULL
307 */
308 struct uart_state *state;
309 struct tty_driver *tty_driver;
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 函数, 函数原型如下:
void uart_unregister_driver(struct uart_driver *drv)
函数参数和返回值含义如下: drv:要注销的 uart_driver。 返回值:无。
1.2:uart_port 的添加与移除
uart_port 表示一个具体的 port,uart_port 定义在 include/linux/serial_core.h 文件,内容如下 (有省略):
117 struct uart_port {
118 spinlock_t lock; /* port lock */
119 unsigned long iobase; /* in/out[bwl] */
120 unsigned char __iomem *membase; /* read/write[bwl] */
......
235 const struct uart_ops *ops;
236 unsigned int custom_divisor;
237 unsigned int line; /* port index */
238 unsigned int minor;
239 resource_size_t mapbase; /* for ioremap */
240 resource_size_t mapsize;
241 struct device *dev; /* parent device */
......
250 };
uart_port 中最主要的就是第 235 行的 ops,ops 包含了串口的具体驱动函数,这个我们稍后 再看。每个 UART 都有一个 uart_port,那么 uart_port 是怎么和 uart_driver 结合起来的呢?这里 要用到 uart_add_one_port 函数,函数原型如下:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
函数参数和返回值含义如下:
drv:此 port 对应的 uart_driver。
uport:要添加到 uart_driver 中的 port。
返回值:0,成功;负值,失败。
卸载 UART 驱动的时候也需要将 uart_port 从相应的 uart_driver 中移除,需要用到 uart_remove_one_port 函数,函数原型如下:
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
函数参数和返回值含义如下
: drv:要卸载的 port 所对应的 uart_driver。
uport:要卸载的 uart_port。
返回值:0,成功;负值,失败。
1.3:uart_ops 实现
在上面讲解 uart_port 的时候说过,uart_port 中的 ops 成员变量很重要,因为 ops 包含了针 对 UART 具体的驱动函数,Linux 系统收发数据最终调用的都是 ops 中的函数。ops 是 uart_ops 类型的结构体指针变量,uart_ops 定义在 include/linux/serial_core.h 文件中,内容如下:
49 struct uart_ops {
50 unsigned int (*tx_empty)(struct uart_port *);
51 void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
52 unsigned int (*get_mctrl)(struct uart_port *);
53 void (*stop_tx)(struct uart_port *);
54 void (*start_tx)(struct uart_port *);
55 void (*throttle)(struct uart_port *);
56 void (*unthrottle)(struct uart_port *);
57 void (*send_xchar)(struct uart_port *, char ch);
58 void (*stop_rx)(struct uart_port *);
59 void (*enable_ms)(struct uart_port *);
60 void (*break_ctl)(struct uart_port *, int ctl);
61 int (*startup)(struct uart_port *);
62 void (*shutdown)(struct uart_port *);
63 void (*flush_buffer)(struct uart_port *);
64 void (*set_termios)(struct uart_port *, struct ktermios *new,
65 struct ktermios *old);
66 void (*set_ldisc)(struct uart_port *, struct ktermios *);
67 void (*pm)(struct uart_port *, unsigned int state,
68 unsigned int oldstate);
69
70 /*
71 * Return a string describing the type of the port
72 */
73 const char *(*type)(struct uart_port *);
74
75 /*
76 * Release IO and memory resources used by the port.
77 * This includes iounmap if necessary.
78 */
79 void (*release_port)(struct uart_port *);
80
81 /*
82 * Request IO and memory resources used by the port.
83 * This includes iomapping the port if necessary.
84 */
85 int (*request_port)(struct uart_port *);
86 void (*config_port)(struct uart_port *, int);
87 int (*verify_port)(struct uart_port *, struct serial_struct *);
88 int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
89 #ifdef CONFIG_CONSOLE_POLL
90 int (*poll_init)(struct uart_port *);
91 void (*poll_put_char)(struct uart_port *, unsigned char);
92 int (*poll_get_char)(struct uart_port *);
93 #endif
94 };
UART 驱动编写人员需要实现 uart_ops,因为 uart_ops 是最底层的 UART 驱动接口,是实 实在在的和 UART 寄存器打交道的。关于 uart_ops 结构体中的这些函数的具体含义请参考 Documentation/serial/driver 这个文档。 UART 驱动框架大概就是这些,接下来我们理论联系实际,看一下 NXP 官方的 UART 驱动文件是如何编写的。
2:I.MX6U UART 驱动分析
2.1:UART 的 platform 驱动框架
打开 imx6ull.dtsi 文件,找到 UART3 对应的子节点,子节点内容如下所示:
1 uart3: serial@021ec000 {
2 compatible = "fsl,imx6ul-uart",
3 "fsl,imx6q-uart", "fsl,imx21-uart";
4 reg = <0x021ec000 0x4000>;
5 interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
6 clocks = <&clks IMX6UL_CLK_UART3_IPG>,
7 <&clks IMX6UL_CLK_UART3_SERIAL>;
8 clock-names = "ipg", "per";
9 dmas = <&sdma 29 4 0>, <&sdma 30 4 0>;
10 dma-names = "rx", "tx";
11 status = "disabled";
12 };
重点看一下第 2,3 行的 compatible 属性,这里一共有三个值:“fsl,imx6ul-uart”、“fsl,imx6q-uar”和“fsl,imx21-uart”。在 linux 源码中搜索这三个值即可找到对应的 UART 驱动文件,此文 件为 drivers/tty/serial/imx.c,在此文件中可以找到如下内容:
267 static struct platform_device_id imx_uart_devtype[] = {
268 {
269 .name = "imx1-uart",
270 .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX1_UART],
271 }, {
272 .name = "imx21-uart",
273 .driver_data = (kernel_ulong_t)
&imx_uart_devdata[IMX21_UART],
274 }, {
275 .name = "imx6q-uart",
276 .driver_data = (kernel_ulong_t)
&imx_uart_devdata[IMX6Q_UART],
277 }, {
278 /* sentinel */
279 }
280 };
281 MODULE_DEVICE_TABLE(platform, imx_uart_devtype);
282
283 static const struct of_device_id imx_uart_dt_ids[] = {
284 { .compatible = "fsl,imx6q-uart", .data =
&imx_uart_devdata[IMX6Q_UART], },
285 { .compatible = "fsl,imx1-uart", .data =
&imx_uart_devdata[IMX1_UART], },
286 { .compatible = "fsl,imx21-uart", .data =
&imx_uart_devdata[IMX21_UART], },
287 { /* sentinel */ }
288 };
......
2071 static struct platform_driver serial_imx_driver = {
2072 .probe = serial_imx_probe,
2073 .remove = serial_imx_remove,
2074
2075 .suspend = serial_imx_suspend,
2076 .resume = serial_imx_resume,
2077 .id_table = imx_uart_devtype,
2078 .driver = {
2079 .name = "imx-uart",
2080 .of_match_table = imx_uart_dt_ids,
2081 },
2082 };
2083
2084 static int __init imx_serial_init(void)
2085 {
2086 int ret = uart_register_driver(&imx_reg);
2087
2088 if (ret)
2089 return ret;
2090
2091 ret = platform_driver_register(&serial_imx_driver);
2092 if (ret != 0)
2093 uart_unregister_driver(&imx_reg);
2094
2095 return ret;
2096 }
2097
2098 static void __exit imx_serial_exit(void)
2099 {
2100 platform_driver_unregister(&serial_imx_driver);
2101 uart_unregister_driver(&imx_reg);
2102 }
2103
2104 module_init(imx_serial_init);
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.2:uart_driver 初始化
在 imx_serial_init 函数中向 Linux 内核注册了 imx_reg,imx_reg 就是 uart_driver 类型的结 构体变量,imx_reg 定义如下:
1836 static struct uart_driver imx_reg = {
1837 .owner = THIS_MODULE,
1838 .driver_name = DRIVER_NAME,
1839 .dev_name = DEV_NAME,
1840 .major = SERIAL_IMX_MAJOR,
1841 .minor = MINOR_START,
1842 .nr = ARRAY_SIZE(imx_ports),
1843 .cons = IMX_CONSOLE,
1844 };
2.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 结构体内容如下所示(有缩减):
216 struct imx_port {
217 struct uart_port port;
218 struct timer_list timer;
219 unsigned int old_status;
220 unsigned int have_rtscts:1;
221 unsigned int dte_mode:1;
222 unsigned int irda_inv_rx:1;
223 unsigned int irda_inv_tx:1;
224 unsigned short trcv_delay; /* transceiver delay */
......
243 unsigned long flags;
245 };
第 217 行,uart_port 成员变量 port。 接下来看一下 serial_imx_probe 函数,函数内容如下:
1969 static int serial_imx_probe(struct platform_device *pdev)
1970 {
1971 struct imx_port *sport;
1972 void __iomem *base;
1973 int ret = 0;
1974 struct resource *res;
1975 int txirq, rxirq, rtsirq;
1976
1977 sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
1978 if (!sport)
1979 return -ENOMEM;
1980
1981 ret = serial_imx_probe_dt(sport, pdev);
1982 if (ret > 0)
1983 serial_imx_probe_pdata(sport, pdev);
1984 else if (ret < 0)
1985 return ret;
1986
1987 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1988 base = devm_ioremap_resource(&pdev->dev, res);
1989 if (IS_ERR(base))
1990 return PTR_ERR(base);
1991
1992 rxirq = platform_get_irq(pdev, 0);
1993 txirq = platform_get_irq(pdev, 1);
1994 rtsirq = platform_get_irq(pdev, 2);
1995
1996 sport->port.dev = &pdev->dev;
1997 sport->port.mapbase = res->start;
1998 sport->port.membase = base;
1999 sport->port.type = PORT_IMX,
2000 sport->port.iotype = UPIO_MEM;
2001 sport->port.irq = rxirq;
2002 sport->port.fifosize = 32;
2003 sport->port.ops = &imx_pops;
2004 sport->port.rs485_config = imx_rs485_config;
2005 sport->port.rs485.flags =
2006 SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;
2007 sport->port.flags = UPF_BOOT_AUTOCONF;
2008 init_timer(&sport->timer);
2009 sport->timer.function = imx_timeout;
2010 sport->timer.data = (unsigned long)sport;
2011
2012 sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
2013 if (IS_ERR(sport->clk_ipg)) {
2014 ret = PTR_ERR(sport->clk_ipg);
2015 dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
2016 return ret;
2017 }
2018
2019 sport->clk_per = devm_clk_get(&pdev->dev, "per");
2020 if (IS_ERR(sport->clk_per)) {
2021 ret = PTR_ERR(sport->clk_per);
2022 dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);
2023 return ret;
2024 }
2025
2026 sport->port.uartclk = clk_get_rate(sport->clk_per);
2027 if (sport->port.uartclk > IMX_MODULE_MAX_CLK_RATE) {
2028 ret = clk_set_rate(sport->clk_per, IMX_MODULE_MAX_CLK_RATE);
2029 if (ret < 0) {
2030 dev_err(&pdev->dev, "clk_set_rate() failed\n");
2031 return ret;
2032 }
2033 }
2034 sport->port.uartclk = clk_get_rate(sport->clk_per);
2035
2036 /*
2037 * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
2038 * chips only have one interrupt.
2039 */
2040 if (txirq > 0) {
2041 ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0,
2042 dev_name(&pdev->dev), sport);
2043 if (ret)
2044 return ret;
2045
2046 ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0,
2047 dev_name(&pdev->dev), sport);
2048 if (ret)
2049 return ret;
2050 } else {
2051 ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0,
2052 dev_name(&pdev->dev), sport);
2053 if (ret)
2054 return ret;
2055 }
2056
2057 imx_ports[sport->port.line] = sport;
2058
2059 platform_set_drvdata(pdev, sport);
2060
2061 return uart_add_one_port(&imx_reg, &sport->port);
2062 }
第 1971 行,定义一个 imx_port 类型的结构体指针变量 sport。
第 1977 行,为 sport 申请内存。
第 1987~1988 行,从设备树中获取 I.MX 系列 SOC UART 外设寄存器首地址,对于 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。
2.4:imx_pops 结构体变量
imx_pops 就是 uart_ops 类型的结构体变量,保存了 I.MX6ULL 串口最底层的操作函数, imx_pops 定义如下:
1611 static struct uart_ops imx_pops = {
1612 .tx_empty = imx_tx_empty,
1613 .set_mctrl = imx_set_mctrl,
1614 .get_mctrl = imx_get_mctrl,
1615 .stop_tx = imx_stop_tx,
1616 .start_tx = imx_start_tx,
1617 .stop_rx = imx_stop_rx,
1618 .enable_ms = imx_enable_ms,
1619 .break_ctl = imx_break_ctl,
1620 .startup = imx_startup,
1621 .shutdown = imx_shutdown,
1622 .flush_buffer = imx_flush_buffer,
1623 .set_termios = imx_set_termios,
1624 .type = imx_type,
1625 .config_port = imx_config_port,
1626 .verify_port = imx_verify_port,
1627 #if defined(CONFIG_CONSOLE_POLL)
1628 .poll_init = imx_poll_init,
1629 .poll_get_char = imx_poll_get_char,
1630 .poll_put_char = imx_poll_put_char,
1631 #endif
1632 };
imx_pops 中的函数基本都是和 I.MX6ULL 的 UART 寄存器打交道的,这里就不去详细的 分析了。简单的了解了 I.MX6U 的 UART 驱动以后我们再来学习一下,如何驱动正点原子 I.MX6U-ALPHA 开发板上的 UART3 接口。
3:RS232 驱动编写
前面我们已经说过了,I.MX6U 的 UART 驱动 NXP 已经编写好了,所以不需要我们编写。我们要做的就是在设备树中添加 UART3 对应的设备节点即可。打开 imx6ull-alientek-emmc.dts 文件,在此文件中只有 UART1 对应的 uart1 节点,并没有 UART3 对应的节点,因此我们可以 参考 uart1 节点创建 uart3 节点。
3.1:UART3 IO 节点创建
UART3 用到了 UART3_TXD 和 UART3_RXD 这两个 IO,因此要先在 iomuxc 中创建 UART3 对应的 pinctrl 子节点,在 iomuxc 中添加如下内容:
1 pinctrl_uart3: uart3grp {
2 fsl,pins = <
3 MX6UL_PAD_UART3_TX_DATA__UART3_DCE_TX 0X1b0b1
4 MX6UL_PAD_UART3_RX_DATA__UART3_DCE_RX 0X1b0b1
5 >;
6 };
最后检查一下 UART3_TX 和 UART3_RX 这两个引脚有没有被用作其他功能,如果有的话 要将其屏蔽掉,保证这两个 IO 只用作 UART3,切记!!!
3.2:添加 uart3 节点
默认情况下 imx6ull-alientek-emmc.dts 中只有 uart1 和 uart2 这两个节点,如图 63.4.1 所示:
uart1 是 UART1 的,在正点原子的 I.MX6U-ALPHA 开发板上没有用到 UART2,而且 UART2 默认用到了 UART3 的 IO,因此需要将 uart2 这个节点删除掉,然后加上 UART3 对应的 uart3, uart3 节点内容如下:
1 &uart3 {
2 pinctrl-names = "default";
3 pinctrl-0 = <&pinctrl_uart3>;
4 status = "okay";
5 };
完成以后重新编译设备树并使用新的设备树启动 Linux,如果设备树修改成功的话,系统 启动以后就会生成一个名为“/dev/ttymxc2”的设备文件,ttymxc2 就是 UART3 对应的设备文 件,应用程序可以通过访问 ttymxc2 来实现对 UART3 的操作
4:移植 minicom
minicom 类似我们常用的串口调试助手,是 Linux 下很常用的一个串口工具,将 minicom 移植到我们的开发板中,这样我们就可以借助 minicom 对串口进行读写操作。
4.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”的文件夹,此文件夹就是 ncurese 的源码文 件夹。在 tool 目录下新建名为“ncurses”目录,用于保存 ncurses 编译结果,一切准备就绪以后 就可以编译 ncureses 库了。进入到 ncureses 源码目录下,也就是刚刚解压出来的 ncurses-6.0 目 录中,首先是配置 ncureses,输入如下命令:
./configure --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/ncurses --host=arm-linuxgnueabihf --target=arm-linux-gnueabihf --with-shared --without-profile --disable-stripping --without-progs --with-manpages --without-tests
configure 就是配置脚本,--prefix 用于指定编译结果的保存目录,这里肯定将编译结果保存 到我们前面创建的“ncurses”目录中。--host 用于指定编译器前缀,这里设置为 “arm-linuxgnueabihf”,--target 用于指定目标,这里也设置为“arm-linux-gnueabihf”。配置命令写好以后点 击回车键,等待配置完成,配置成功以后如图 63.5.1 所示:
配置成功以后输入“make”命令开始编译,编译成功以后如图 63.5.2 所示:
pfefix 指定的目录里面去。安装成功以后如图 63.5.3 所示:
安装成功以后查看一下前面创建的“ncurses”文件夹,会发现里面多了一些东西,如图 63.5.4 所示:
我们需要将图 63.5.4 中 include、lib 和 share 这三个目录中存放的文件分别拷贝到开发板根 文件系统中的/usr/include、/usr/lib 和/usr/share 这三个目录中,如果哪个目录不存在的话请自行 创建!!拷贝命令如下:
sudo cp lib/* /home/zuozhongkai/linux/nfs/rootfs/usr/lib/ -rfa
sudo cp share/* /home/zuozhongkai/linux/nfs/rootfs/usr/share/ -rfa
sudo cp include/* /home/zuozhongkai/linux/nfs/rootfs/usr/include/ -rfa
然后在开发板根目录的/etc/profile(没有的话自己创建一个)文件中添加如下所示内容:
1 #!/bin/sh
2 LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
3 export LD_LIBRARY_PATH
4
5 export TERM=vt100
6 export TERMINFO=/usr/share/terminfo
4.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, 命令如下:
tar -vxzf minicom-2.7.1.tar.gz
解压完成以后会生成一个叫做 minicom-2.7.1 的文件夹,这个就是 minicom 的源码,进入到 此目录中,然后配置 minicom,配置命令如下:
cd minicom-2.7.1/ //进入 minicom 源码目录
./configure CC=arm-linux-gnueabihf-gcc --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/
minicom --host=arm-linux-gnueabihf CPPFLAGS=-I/home/zuozhongkai/linux/IMX6ULL/tool/
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 所示:
配置成功以后执行如下命令编译并安装:
make
make install
编译安装完成以后,前面创建的 minicom 目录内容如图 63.5.6 所示:
将 minicom 目录中 bin 子目录下的所有文件拷贝到开发板根目录中的/usr/bin 目录下,命令 如下:
sudo cp bin/* /home/zuozhongkai/linux/nfs/rootfs/usr/bin/
完成以后在开发板中输入“minicom -v”来查看 minicom 工作是否正常,结果如图 63.5.7 所 示:
从图 63.5.7 可以看出,此时 minicom 版本号为 2.7.1,minicom 版本号查看正常。输入如下 命令打开 minicom 配置界面:
minicom -s
结果是打不开 minicom 配置界面,提示如图 63.5.8 所示信息:
从图 63.5.8 可以看出,minicom 异常嚣张,竟然让我们“Go away”,这能容忍?!必须要 治一下。解决方法很简单,新建/etc/passwd 文件,然后在 passwd 文件里面输入如下所示内容:
1 root:x:0:0:root:/root:/bin/sh
完成以后重启开发板!
完成以后重启开发板!
完成以后重启开发板!
开发板重启以后再执行“minicom -s”命令,此时 minicom 配置界面就可以打开了,如图 63.5.9 所示:
如果能出现图 63.5.9 所示界面,那么就说明 mincom 工作正常了。
当然mini板子没有232,好像只有485和can
5:RS232 驱动测试
5.1:RS232 连接设置
在测试之前要先将 I.MX6U-ALPHA 开发板的 RS232 与电脑连接起来,首先设置 JP1 跳线 帽,如图 63.6.1.1 所示:
跳线帽设置好以后使用 RS232 线将开发板与电脑连接起来,这里建议使用 USB 转 DB9(RS232)数据线,比如正点原子售卖的 CH340 方案的 USB 转公头 DB9 数据线,如图 63.6.1.2 所示:
图 63.6.1.2 中所示的数据线是带有 CH340 芯片的,因此当连接到电脑以后就会出现一个 COM 口,这个 COM 口就是我们要使用的 COM 口。比如在我的电脑上就是 COM9,在 SecureCRT 上新建一个连接,串口为 COM9,波特率为 115200。
5.2:minicom 设置
在开发板中输入“minicom -s”,打开 minicom 配置界面,然后选中“Serial port setup”,如 图 63.6.2.1 所示:
选中“Serial port setup”以后点击回车,进入设置菜单,如图 63.6.2.2 所示:
图 63.6.2.2 中有 7 个设置项目,分别对应 A、B……G,比如第一个是选中串口,UART3 的 串口文件为/dev/ttymxc2,因此串口设置要设置为/dev/ttymxc2。设置方法就是按下键盘上的‘A’, 然后输入“/dev/ttymxc2”即可,如图 63.6.2.3 所示:
设置完以后按下回车键确认,确认完以后就可以设置其他的配置项。比如 E 设置波特率、 数据位和停止位的、F 设置硬件流控的,设置方法都一样,设置完以后如图 63.6.2.4 所示:
都设置完成以后按下回车键确认并退出,这时候会退回到如图 63.6.2.1 所示的界面,按下 ESC 键退出图 63.6.2.1 所示的配置界面,退出以后如图 63.6.2.5 所示:
图 63.6.2.2 就是我们的串口调试界面,可以看出当前的串口文件为/dev/ttymxc2,按下 CTRLA,然后再按下 Z 就可以打开 minicom 帮助信息界面,如图 63.6.2.6 所示
从图 63.6.2.6 可以看出,minicom 有很多快捷键,本实验我们打开 minicom 的回显功能, 回显功能配置项为“local Echo on/off..E”,因此按下 E 即可打开/关闭回显功能。
5.3:RS232 收发测试
5.3.1:发送测试
首先测试开发板通过 UART3 向电脑发送数据的功能,需要打开 minicom 的回显功能(不打 开也可以,但是在 minicom 中看不到自己输入的内容),回显功能打开以后输入“AAAA”,如 图 63.6.3.1 所示:
图 63.6.3.1 中的“AAAA”相当于开发板通过 UART3 向电脑发送“AAAA”,那么 COM9 就会接收到“AAAA”,SecureCRT 中 COM9 收到的数据如图 63.6.3.2 所示:
5.3.2:接收测试
接下来测试开发板的 UART3 接收功能,同样的,要先打开 SecureCRT 上 COM9 的本地回 显,否则的话你在 COM9 上输出的内容会看不到,但是实际上是已经发送给了开发板。选中 SecureCRT 的 Options->Session Options->Adavnced,打开回话配置界面,然后选中“Local echo”, 如图 63.6.3.3 所示:
SecureCRT设置好以后向开发板发送一个“BBBB”,在SecureCRT的COM9上输入“BBBB”, 如图 63.6.3.3 所示:
此时开发板的 minicom 就会接收到发送过来的“BBBB”,如图 63.6.3.4 所示:
UART3 收发测试都没有问题,说明我们的 UART3 驱动工作正常。如果要退出 minicom 的 话,在 minicom 通信界面按下 CRTL+A,然后按下 X 来关闭 minicom。关于 minicom 的使用我 们这里讲的很简单,大家可以在网上查找更加详细的 minicom 使用教程。
总而言之,这个串口3的驱动不用我们写,只需配置设备树,和配置一些其他文件。驱动程序及测试APP都是我们移植的minicom来完成,那我们做了什么呢。我们做的工作就是配置设备树和移植软件。这个硬件也不是我们操心的地方,硬件由硬件工程师来做。串口同信的驱动是一致的,区别在于电平转换芯片上。
6:RS485 测试
前面已经说过了,I.MX6U-ALPHA 开发板上的 RS485 接口连接到了 UART3 上,因此本质 上就是个串口。RS232 实验我们已经将 UART3 的驱动编写好了,所以 RS485 实验就不需要编 写任何驱动程序,可以直接使用 minicom 来进行测试。
6.1:RS485 连接设置
首先是设置 JP1 跳线帽,将 3-5、4-6 连接起来,如图 63.7.1.1 所示:
mini板子上有这个485,但为了485又去买一个转接器是不是有点为了一口醋去饱一顿饺子的嫌疑。
一个板子是不能进行 RS485 通信测试的,还需要另一个 RS485 设备,比如另外一块 I.MX6UALPHA开发板。这里推荐大家使用正点原子出品的USB三合一串口转换器,支持USB转TTL、 RS232 和 RS485,如图 63.7.1.2 所示:
使用杜邦线将 USB 串口转换器的 RS485 接口和 I.MX6U-ALPHA 开发板的 RS485 连接起 来,A 接 A,B 接 B,不能接错了!连接完成以后如图 63.7.1.3 所示
串口转换器通过 USB 线连接到电脑上,我用的是 CH340 版本的,因此就不需要安装驱动 的,如果使用的是 FT232 版本的就需要安装相应的驱动。连接成功以后电脑就会有相应的 COM 口,比如我的电脑上就是 COM10,接下来就是测试。
6.2:RS485 收发测试
RS485 的测试和 RS232 一模一样!USB 多合一转换器的 COM 口为 10,因此使用 SecureCRT 创建一个 COM10 的连接。开发板使用 UART3,对应的串口设备文件为/dev/ttymxc2,因此开发 板使用 minicom 创建一个/dev/ttymxc2 的串口连接。串口波特率都选择 115200,8 位数据位,1 位停止位,关闭硬件和软件流控。
6.2.1:RS485 发送测试
首先测试开发板通过 RS485 发送数据,设置好 minicom 以后,同样输入“AAAA”,也就是 通过 RS485 向电脑发送一串“AAAA”。如果 RS485 驱动工作正常的话,那么电脑就会接收到 开发板发送过来的“AAAA”,如图 63.7.2.1 所示:
从图 63.7.2.1 可以看出开发板通过 RS485 向电脑发送“AAAA”成功,说明 RS485 数据数 据发送正常
6.2.2:RS485 接收测试
接下来测试一下 RS485 数据接收,电脑通过 RS485 向开发板发送“BBBB”,然后观察 minicom 是否能接收到“BBBB”。结果如图 63.7.2.2 所示:
从图 63.7.2.1 可以看出开发板接收到电脑通过 RS485 发送过来的“BBBB”,说明 RS485 数 据接收也正常。
7:GPS 测试
7.1:GPS 连接设置
GPS 模块大部分都是串口输出的,这里以正点原子出品的 ATK1218-BD 模块为例,这是 一款 GSP+北斗的定位模块,模块如图 63.8.1.1 所示:
首先要将 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 所 示
7.2:GPS 数据接收测试
原子哥没用北斗我是有点疑问的,听说北斗的模块才几块钱,是不是程序复杂啊,不晓得
GPS 我们都是被动接收定位数据的,因此打开 minicom,设置/dev/ttymxc2,串口设置要求 如下: ①、波特率设置为 38400,因为正点原子的 ATK1218-BD 模块默认波特率就是 38400。 ②、8 位数据位,1 位停止位。 ③、关闭硬件和软件流控。 设置好以后如图 63.8.2.1 所示:
设置好以后就可以静静的等待 GPS 数据输出,GPS 模块第一次启动可能需要几分钟搜星, 等搜到卫星以后才会有定位数据输出。搜到卫星以后 GPS 模块输出的定位数据如图 63.8.2.2 所 示
咱不买alpha板子因为咱省钱,咱不买串口转换器因为咱省钱,咱不买GPS定位模块因为咱省钱,但是该移植这个minicom还是要做的
本文仅在记录学习正点原子imx6ull-mini开发板的过程,不做他用。
相关文章:

正点原子imx6ull-mini-Linux驱动之Linux RS232/485/GPS 驱动实验(23)
错误1:我一直找不到为什么我的minicom用不了,编译啥的都通过了,原来是我的密码文件命名错了,我就习以为常的命名为password,谁知道应该是passwd,所以以后该复制的还是复制,不然就容易找不到源头…...

用户上下文打通+本地缓存Guava
文章目录 🌞 Sun Frame:SpringBoot 的轻量级开发框架(个人开源项目推荐)🌟 亮点功能📦 spring cloud模块概览常用工具 🔗 更多信息1.设计1.链路流程2.详细设计 2.网关过滤器获取唯一标识放到Hea…...

Windows图形界面(GUI)-MFC-C/C++ - 树形视图(Tree Control) - CTreeCtrl
公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 树形视图(Tree Control) - CTreeCtrl 创建和初始化 添加和删除项 获取和设置项属性 操作项 项选择变化 项双击 项展开 示例代码 树形视图(Tree Control) - CTreeCtrl 创建和初始…...

C语言 --- 枚举、位运算
(一)枚举 1.概念:枚举是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围 2.作用:a.提高代码可读性;b.提高代码的安全性 3.枚举类型: enum 枚举名 { 列举各种值 //枚举元素或枚…...

12322222222
当您和老王不在同一个网段时,您们之间的通信需要通过路由器来实现。这是因为不同的网段被视为不同的网络,而路由器的作用之一就是连接不同的网络并负责数据包的转发。下面是详细的通信流程: 本地网络通信尝试:您的设备࿰…...

知识改变命运:Java 语言 【可变参数】
可变参数 概念:Java允许一个类中多个同名同功能但是参数不同的方法,封装为一个方法。 基本语法: 访问修饰符 返回值 方法名 (数据类型...参数名) { ...... }案例:写一个类名DyMethod 方法名sum 计算两个整数和,三个整…...

Spring及相关框架的重要的问题
Java框架 问题一:Spring框架中的单例bean是线程安全的吗? 看下图,不能被修改的成员变量就是无状态的类,无状态的类没有线程安全问题,所以在开发中尽量避免可修改的成员变量。 回答:不是线程安全的…...

Linux Vim教程
Linux Vim 教程 Vim(Vi IMproved)是一个强大的文本编辑器,广泛用于编程和系统管理。本文将带你全面了解 Vim 的基础使用、常用命令、高级功能等。 1. 安装 Vim 在大多数 Linux 发行版中,Vim 已经预装。如果没有,可以…...

【学习笔记】多进程信号量控制
目录 1、CreateSemaphore 2、ReleaseSemaphore 3、CreateEvent 4、SetEvent 5、WaitForSingleObject 程序案例1: 程序案例2: 1、CreateSemaphore 创建一个计数信号量对象,成功时返回信号量对象的句柄;失败时返回NULL&…...

Redis与Memorycache的区别
Redis与Memorycache主要是持久线程和持久化的区别 1、从性能方面来说: Redis是单线程的,优点是CPU开销小,省去多线程线程之间切换的开销,但是相对于Memorycache来说海量数据的相对较低 Memorycache使用了多线程技术,数…...

docker和Helm Chart的基本命令和操作
一、docker基本命令和操作 1. docker login【登录】 登录 docker client,登录成功之后会显示 Login Succeeded。 docker login登陆到指定的镜像仓库,docker pull 和 docker push 操作都需要预先执行 docker login 操作; 指令:&a…...

Node中的CSRF攻击和防御
Node中的CSRF攻击和防御 假设有一个网上银行系统,用户可以通过该系统进行转账操作。转账功能的URL可能是这样的: https://www.bank.com/transfer?toAccount123456&amount1000当用户登录到银行系统,并在浏览器中访问这个URL时ÿ…...

CSS 多按钮根据半圆弧度排列
需求 多个按钮根据弧度,延边均匀排列。 实现 HTML 分两级;第一级,外层定义按钮的 compose-container 宽度;第二级,按钮集合,使用方法 styleBtn(index),根据索引计算; <div c…...

【Linux】网络编程套接字Scoket:UDP网络编程
目录 一、了解UDP协议 二、了解端口和IP地址 三、套接字概述与Socket的概念 四、Socket的类型 五、 Socket的信息数据结构 六、网络字节序与主机字节序的互相转换 七、地址转换函数 八、UDP网络编程流程及相关函数 socket函数 bind函数 recvfrom函数 sendto函数 …...

基于模糊PID控制器的puma560机器人控制系统的simulink建模与仿真
目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 PUMA 560机器人的数学模型 4.2 PID控制原理 4.3 模糊PID控制器的设计 5.完整工程文件 1.课题概述 基于模糊PID控制器的puma560机器人控制系统的simulink建模与仿真,对比传统的PID控制器…...

C语言文件操作超详解
文章目录 1. 为什么使用文件2. 什么是文件2. 1 程序文件2. 2 数据文件2. 3 文件名3. 二进制文件和文本文件? 4. 文件的打开和关闭4. 1 流和标准流4. 1. 1 流4. 1. 2 标准流 4. 2 文件指针4. 3 文件的打开和关闭 5. 文件的顺序读写5. 1 顺序读写函数介绍5. 2 对比一组函数: 6. …...

表字段显示tip
需求背景: 生成的报表,前端只展示字段名称,计算逻辑没有解释,使用方频繁“骚扰”,实在受不了,增加一个字段tip,实现效果(下图): 代码 结合使用el-table-colu…...

十二、享元模式
文章目录 1 基本介绍2 案例2.1 Digit 接口2.2 Color 枚举2.3 BigDigit 类2.4 DigitFactory 类2.5 Client 类2.6 Client 类的测试结果2.7 总结 3 各角色之间的关系3.1 角色3.1.1 Flyweight ( 抽象享元 )3.1.2 ConcreteFlyweight ( 具体享元 )3.1.3 UnsharedFlyweight ( 非享元 )…...

黑马Java零基础视频教程精华部分_18_Arrays各种方法
系列文章目录 文章目录 系列文章目录Arrays简介Arrays各种方法toString代码示例binarySearch代码示例copyOf代码示例copyOfRange和fill代码示例sort代码示例 Arrays简介 操作数组的工具类。 Arrays各种方法 toString代码示例 int[]arr{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; //to…...

RAG私域问答场景超级详细方案(第一期方案)[1]:工业级别构建私域问答(知识处理、知识召回排序、搜索问答模块)
RAG私域问答场景整体夏详细方案(第一期方案):工业级别构建私域问答(知识处理、知识召回排序、搜索问答模块) 大模型性能的跳阶式增长给文本摘要、信息检索、信息抽取、语义问答等自然语言处理任务带来了卓越的性能提升。同时,LangChain 作为一种基于 LLM 的框架,能够快速…...

【AI在医疗领域的应用】AI在疾病诊断、个性化治疗等领域的应用
AI在医疗领域的应用 AI在疾病诊断、个性化治疗等领域的应用 引言 人工智能(AI)技术正在迅速改变各个行业,而医疗领域无疑是AI应用最广泛、影响最深远的领域之一。AI在医疗中的应用不仅能够提高诊断的准确性和效率,还为个性化治疗…...

SpEL结合AOP示例
AOP不用多说,是spring框架的两大基石之一。SpEL是Spring Expression Language的缩写,意为Spring表达式语言,,其支持在运行时查询和操作对象图提供了更加丰富的功能,最特别的是方法调用与字符串模板功能。熟悉js的es6语…...

【Linux:环境变量】
目录 命令行参数: 环境变量: 命令行参数: argv是一个char*类型的数组,里面存放着字符、字符串的指针地址,且该数组必定是以NULL结尾 命令行中启动的进程都是Bash的子进程,命令行参数的存在本质上就是通过…...

8月9日笔记
8月9日笔记 什么是代理? “代理”通常指的是“网络代理”,它是一种特殊的网络服务,允许一个网络终端(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。代理服务器作为中间人…...

API 签名认证:AK(Access Key 访问密钥)和 SK(Secret Key 私密密钥)
API签名认证 在当今的互联网时代,API作为服务与服务、应用程序与应用程序之间通信的重要手段,其安全性不容忽视。你是否遇到过需要在HTTP请求中加入访问密钥(ak)和私密密钥(sk)的情况?是不是担心这些敏感信息会被拦截或者泄露?本…...

Redis 单机和集群环境部署教程
目录 一、Redis 单机环境部署1. 环境准备2. 安装 Redis2.1 安装依赖2.2 下载并编译 Redis2.3 配置 Redis2.4 设置 Redis 为系统服务 3. Redis 配置选项详解4. 注意事项 二、Redis 集群环境部署1. 环境准备2. 安装 Redis3. 配置 Redis 集群3.1 配置文件调整3.2 启动 Redis 实例3…...

华为hcip-big data 学习笔记《一》大数据应用开发总指导
一、大数据应用开发总指导 1. 前言 随着大数据技术的飞速发展和大数据应用的不断普及,大数据已经成为当今时代最热门的话题之一。不过对于大数据的了解,很多人还只是停留在表面,提到大数据,很多人只是直到它是最新的科技&#x…...

用户画像架构图
背景 本文讲述下实现一个画像平台的架构图 架构图 这里面的人群圈选我们这里主要采用ck和spark,不过也有很多使用es,如果使用es的话,需要把标签的数据也存储到es的表中,类似我们这里放到ck的表中一样,这样就可以通过…...

37.x86游戏实战-XXX遍历怪物数组
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 工具下载: 链接:https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…...

go语言中map为什么不会自动初始化?
go语言中map为什么不会自动初始化? 在Go语言中,map类型不会自动初始化的原因在于其设计哲学和类型系统。以下是具体原因: 零值设计:Go语言中的每种类型都有一个零值,例如整型的零值是0,布尔型的零值是fals…...