正点原子嵌入式linux驱动开发——Linux 串口RS232/485/GPS 驱动

串口是很常用的一个外设,在Linux下通常通过串口和其他设备或传感器进行通信,根据
电平的不同,串口分为TTL和RS232
。不管是什么样的接口电平,其驱动程序都是一样的,通过外接RS485这样的芯片就可以将串口转换为RS485信号,正点原子的STM32MP1开发板就是这么做的。对于正点原子的STM32MP1开发板而言有8个串口,四个同步串口(USART1、USART2、USART3和USART6),四个异步串口(UART4、UART5、UART7和UART8)

RS232和RS485接口连接到了STM32MP1的USART3接口上,通过跳线帽选择USART3作为RS232还是RS485。GPS模块是连接到UART5接口上,因此这些外设最终都归结为USART3和UART5的串口驱动。本章就来学习一下如何驱动STM32MP1开发板上的USART3串口和UART5,进而实现RS232、RS485以及GPS驱动

Linux下UART驱动框架

uart_driver注册/注销

同I2C、SPI一样,Linux也提供了串口驱动框架,只需要按照相应的串口框架编写驱动程序即可。串口驱动没有什么主机端和设备端之分,就只有一个串口驱动,而且这个驱动也
已经由ST官方编写好了,真正要做的就是在设备树中添加所要使用的串口节点信息
。当
系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生成/dev/ttySTMX(X=0….n)文件。

虽然串口驱动不需要自行实现,但是串口驱动框架还是需要了解的,uart_driver结构体表示UART驱动,uart_driver定义在 include/linux/serial_core.h文件中,内容如下:

uart_driver结构体

每个串口驱动都需要定义一个uart_driver,加载驱动的时候通过uart_register_driver函数向系统注册这个uart_driver,此函数原型如下:

int uart_register_driver(struct uart_driver *uart) 

函数参数和返回值含义如下:

  • uart:要注册的uart_driver。
  • 返回值:0,成功;负值,失败。

注销驱动的时候也需要注销掉前面注册的uart_driver,需要用到uart_unregister_driver函数,函数原型如下:

void uart_unregister_driver(struct uart_driver *uart) 

函数参数和返回值含义如下:

  • uart:要注销的uart_driver。
  • 返回值:无。

uart_port添加/移除

uart_port表示一个具体的port,uart_port定义在 include/linux/serial_core.h文件,内容如下(有省略):

uart_port结构体

uart_port中最主要的就是第240行的ops,ops包含了串口的具体驱动函数。每个UART都有一个uart_port,那么uart_port是怎么和uart_driver结合起来的呢?这里要用到uart_add_one_port函数,函数原型如下:

int uart_add_one_port(struct uart_driver *reg, struct uart_port *port) 

函数参数和返回值含义如下:

  • reg:此port对应的uart_driver。
  • port:要添加到uart_driver中的port。
  • 返回值:0,成功;负值,失败。

卸载UART驱动的时候也需要将uart_port从相应的uart_driver中移除,需要用到uart_remove_one_port函数,函数原型如下:

int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port) 

函数参数和返回值含义如下:

  • reg:要卸载的port所对应的uart_driver。
  • port:要卸载的uart_port。
  • 返回值:0,成功;负值,失败。

uart_ops实现

在上面讲解uart_port的时候说过,uart_port中的ops成员变量很重要,因为ops包含了针对UART具体的驱动函数,Linux系统收发数据最终调用的都是ops中的函数 。ops是uart_ops类型的结构体指针变量,uart_ops定义在include/linux/serial_core.h文件中,内容如下:

示例代码 46.1.3 uart_port 结构体
37 struct uart_ops {
38     unsigned int (*tx_empty)(struct uart_port *);
39     void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
40     unsigned int (*get_mctrl)(struct uart_port *);
41     void (*stop_tx)(struct uart_port *);
42     void (*start_tx)(struct uart_port *);
43     void (*throttle)(struct uart_port *);
44     void (8unthrottle)(struct uart_port *);
45     void (*send_xchar)(struct uart_port *, char ch);
46     void (*stop_rx)(struct uart_port *);
47     void (*enable_ms)(struct uart_port *);
48     void (*break_ctl)(struct uart_port *, int ctl);
49     int (*startup)(struct uart_port *);
50     void (*shutdown)(struct uart_port *);
51     void (*flush_buffer)(struct uart_port *);
52     void (*set_termios)(struct uart_port  *, struct ktermios *new, 
53                 struct ktermios *old);
54     void (*set_ldisc)(struct uart_port *, struct ktermios *);
55     void (*pm)(struct uart_port *, unsigned int state, 
56                     unsigned int oldstate);
57
58     /*
59      * Return a string describing the type of the port
60      */
61     const char *(*type)(struct uart_port *);
62
63     /*
64      * Release IO and memory resources used by the
65      * This includes iounmap if
66      */
67     void (*release_port)(struct uart_port *);
68
69     /*
70      * Request IO and memory resources used by the
71      * This includes iomapping the port if
72      */
73     int (*request_port)(struct uart_port *);
74     void (*config_port)(struct uart_port  *, int);
75     int (*verify_port)(struct uart_port *, struct serial_struct );
76     int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
77 #ifdef CONFIG_CONSOLE_POLL
78     int (*poll_init)(struct uart_port *);
79     void (*poll_put_char)(struct uart_port *, unsigned char);
80     int (*poll_get_char)(struct uart_port *);
81 #endif
82 };

UART驱动编写人员需要实现uart_ops,因为uart_ops是最底层的UART驱动接口,是实实在在的和UART寄存器打交道的。关于uart_ops结构体中的这些函数的具体含义请参考Documentation/serial/driver这个文档

UART驱动框架大概就是这些,接下来理论联系实际,看一下ST官方的UART驱动文件是如何编写的。

STM32MP1 UART驱动分析

UART的platform驱动框架

打开stm32mp151.dtsi文件,找到USART3对应的子节点,子节点内容如下所示:

usart3设备节点

重点看一下第2行的compatible属性值为“st,stm32h7-uart”。在linux源码中搜索这个值即可找到对应的UART驱动文件,此文件为drivers/tty/serial/stm32-usart.c,在此文件中可以找到如下内容:

示例代码 46.2.2 UART platform 驱动框架
1218 static const struct of_device_id stm32_match[] = {
1219     { .compatible = "st,stm32 uart", .data = &stm32f4_info},
1220     { .compatible = "st,stm32f7 uart", .data = &stm32f7_info},
1221     { .compatible = "st,stm32h7 uart", .data = &stm32h7_info},
1222     {},
1223 };
......
1668 static struct platform_driver stm32_serial_driver = {
1669     .probe = stm32_usart_serial_probe,
1670     .remove = stm32_usart_serial_remove,
1671     .driver = {
1672         .name = DRIVER_NAME,
1673         .pm = &stm32_serial_pm_ops,
1674         .of_match_table = of_match_ptr(stm32_match),
1675     },
1676 };
1677
1678 static int __init stm32_usart_init(void)
1679 {
1680 static char banner[] __initdata = "STM32 USART driver initialized";
1681     int ret;
1682
1683     pr_info("%s\n", banner);
1684
1685     ret = uart_register_driver(&stm32_usart_driver);
1686     if (ret)
1687         return ret;
1688
1689     ret = platform_driver_register(&stm32_serial_driver);
1690     if (ret)
1691         uart_unregister_driver(&stm32_usart_driver);
1692
1693     return ret;
1694 }
1695
1696 static void __exit stm32_usart_exit(void)
1697 {
1698     platform_driver_unregister(&stm32_serial_driver);
1699     uart_unregister_driver(&stm32_usart_driver);
1700 }
1701
1702 module_init(stm32_usart_init);
1703 module_exit(stm32_usart_exit);

可以看出STM32MP1的UART本质上是一个platform驱动,第1218-1223行,设备树所
使用的匹配表,第1221行的compatible属性值为“st,stm32h7-uart”。

第1668-1676行,platform驱动框架结构体stm32_serial_driver。

第1678-1694行,驱动入口函数,第1685行调用uart_register_driver函数向Linux内核注册uart_driver,在这里就是stm32_usart_driver。

第1696-1700行,驱动出口函数,第1699行调用uart_unregister_driver函数注销掉前面注册的uart_driver,也就是stm32_usart_driver。

uart_driver初始化

在stm32_usart_init函数中向Linux内核注册了stm32_usart_driver,stm32_usart_driver就是uart_driver类型的结构体变量,stm32_usart_driver定义如下:

stm32_usart_driver结构体

uart_port初始化与添加

当UART设备和驱动匹配成功以后stm32_usart_serial_probe函数就会执行,此函数的重点工作就是初始化uart_port,然后将其添加到对应的uart_driver中。在看stm32_usart_serial_probe函数之前先来看一下stm32_port结构体,stm32_port是ST为STM32MP1系列SOC定义的一个设备结构体,此结构体内部就包含了uart_port成员变量,stm32_port结构体内容如下所示(有
缩减):

stm32_port结构体

第258行,uart_port结构体成员变量:port。

第279行,这里定义了一个数组为stm32_ports,数组的类型为stm32_port结构体,数组的长度为8。这是因为STM32MP157最多只有8个串口,一个串口对应一个stm32_port,因此数组长度就是8。

接下来看一下stm32_usart_serial_probe函数,函数内容如下:

stm32_usart.c文件代码段

第1312行,调用stm32_usart_of_get_port函数,它主要是负责配置stm32_ports数组。

第1322行,调用stm32_usart_init_port函数,它主要是负责获取SOC UART外设首地址、
中断号、注册中断函数同时还设置uart_ops为stm32_uart_ops,stm32_uart_ops就是STM32MP1最底层的驱动函数集合。

第1374行,使用uart_add_one_port向uart_driver添加uart_port,在这个就是向stm32_usart_driver添加stm32port->port。

接下来看一下stm32_usart_of_get_port函数,因为stm32_usart_serial_probe函数会调用此函数来获取串口信息,这些串口信息会放到示例代码46.2.4中的stm32_ports数组里面。stm32_usart_of_get_port函数源码如下:

stm32_usart_of_get_port函数

第1197行,通过of_alias_get_id函数从设备树的aliases节点中获取“serial”相关的ID。打开stm32mp157d-atk.dts文件,当前此文件里面的aliases节点内容如下图所示:

aliases节点

从上图可以看出,此时alases节点里面只有一个serial0,对应STM32MP157的uart4。所以stm32_usart_of_get_port函数只能得到uart4这一个串口的信息,如果要使用其他的串口,那就必须向alases节点里面按照如下格式添加对应的串口信息

serialX=&串口名字;

这个X表示0-7,那是因为STM32MP1的串口只有8个。&后面的串口名字一定要对应设备树中具体的串口名,比如usart3、uart5等

第1206-1213行,获取对应的串口信息,然后保存到stm32_ports数组中,获取到的串口ID就是串口在数组中的索引。

第1214行,返回得到的串口信息。

接下来再来看一下stm32_usart_init_port函数,stm32_usart_serial_probe函数会调用此函数来初始化串口!函数源码如下所示:

stm32_usart_init_port函数

stm32_usart_init_port函数主要是负责获取SOC UART外设首地址、中断号、注册中断函数。重点是1149行设置uart_ops为stm32_uart_ops,stm32_uart_ops就是 STM32MP1最底层的驱动函数集合

stm32_uart_ops结构体变量

stm32_uart_ops就是uart_ops类型的结构体变量,保存了STM32MP1串口最底层的操作函
数,stm32_uart_ops定义如下:

stm32_uart_ops

stm32_uart_ops中的函数基本都是和STM32MP1的UART寄存器打交道的,这里就不去详细的分析了。简单的了解了STM32MP1的UART驱动以后再来学习一下,如何驱动正点原子STM32MP1开发板上的USART3接口和UART5接口。

硬件原理图分析

本实验要用到的STM32MP1的USART3接口和UART5接口,USART3连接RS485和RS232的公头,UART5连接GPS和RS232的母头。依次来看一下这个两个串口的硬件原理图。

RS232原理图

RS232原理图如下图所示:

RS232原理图

正点原子STM32MP157开发板一共有2个RS232串口,上图中COM1是母头,COM2为公头,这两个RS232串口都是通过SP3232这个芯片来实现

COM1母头连接到STM32MP1的UART5接口上,COM1和正点原子的ATK模块共用USART5,把JP5的1-3和2-4连接起来以后SP3232就和URAT5连接到一起了。 UART5_TX和UART5_RX分别接到了PB13和PB12这两个引脚上

COM2公头连接到了STM32MP1的USART3接口上,COM2和RS485共用USART3,把JP4的3-5和4-6连接起来以后SP3232就和USRAT3连接到一起了USART3_TX和USART3_RX分别接到了PD8和PD9这两个引脚上

RS485原理图

RS485和COM2共用USART3,将上图中JP4的3-5和4-6连接起来,这时候RS485就连接到了USART3上。RS485原理图如下图所示:

RS485原理图

RS485采用SP3485这颗芯片来实现,RO为数据输出端,RI为数据输入端,RE是接收使能信号(低电平有效),DE是发送使能信号(高电平有效)。在上图中RE和DE经过一系列的电路,最终通过RS485_RX来控制,这样可以省掉一个RS485收发控制IO,将RS485完全当作一个串口来使用,方便写驱动。

GPS原理图

正点原子有一款GPS+北斗定位模块,型号为ATK1218-BD,STM32MP157开发板留出了这款GPS定位模块的接口,接口原理图如下图所示:

ATK MODULE模块

前面讲解RS232原理图的时候说了,COM1和正点原子的ATK模块共用USART5接口,正点原子的ATK1218-BD这个模块用的就是ATK模块接口。如果要使用GPS模块,就要将RS232原理图中JP5的3-5和4-6连接起来。这样GPS模块就连接到了USART5上,USART5驱动成功以后就可以直接读取GPS模块数据了。从上图可以看出,ATK模块还有两个引脚GBC_KEY和GBC_LED分别连接到了STM32MP157的PC13和PI8上,这两个引脚是给其他模块准备的,GPS模块并没有用到。

RS232驱动编写

前面已经说过了,STM32MP1的UART驱动ST已经编写好了,所以不需要自行编写。要做的就是在设备树中添加USART3和UART5对应的设备节点即可。打开stm32mp157d-atk.dts文件,因为usart3和uasrt5的节点在stm32mp151.dtsi已经存在了,只要在stm32mp157d-atk.dts文件里面向这些节点追加一些内容即可,追加步骤如下:

添加usart3和uart5引脚信息

先在stm32mp15-pinctrl.dtsi文件看下没有usart3和uart5的引脚配置,以及引脚配置是
否是开发板对应的。默认情况下stm32mp15-pinctrl.dtsi里面是有usart3的引脚配置,但是
不是正点原子开发板所使用的PD8和PD9,所以不能使用。直接在stm32mp15-pinctrl.dtsi里面添加usart3和uart5这两个串口对应的引脚信息,内容如下:

示例代码 46.4.1 要追加的 pinmux 配置
1 usart3_pins_c: usart3-2 {
2     pins1 {
3         pinmux = <STM32_PINMUX('D', 8, AF7)>; /* USART3_TX */
4         bias-disable;
5         drive-push-pull;
6         slew-rate = <0>;
7     };
8     pins2 {
9         pinmux = <STM32_PINMUX('D', 9, AF7)>; /* USARTS_RX */
10         bias-disable;
11     };
12 };
13
14 uart5_pins_a: uart5-0 {
15     pins1 {
16         pinmux = <STM32_PINMUX('B', 13, AF14)>; /* UART5_TX */
17         bias-disable;
18         drive-push-pull;
19         slew-rate = <0>;
20     };
21     pins2 {
22         pinmux = <STM32_PINMUX('B', 12, AF14)>; /* UART5_RX */
23         bias-disable;
24     };
25 };

示例代码46.4.1里配置了两个pinmux分别为usart3_pins_c和uart5_pins_a。稍后向usart3和uart5中追加内容的时候就会用到这两个节点。

向usart3和uart5节点追加内容

还是在stm32mp157d-atk.dts文件中,在不是根节点下追加如下代码:

示例代码 46.4.2 串口的节点
1  &usart3 { 
2      pinctrl-names = "default"; 
3      pinctrl-0 = <&usart3_pins_c>; 
4      status = "okay"; 
5  }; 
6 
7  &uart5 { 
8     pinctrl-names = "default";
9       pinctrl-0 = <&uart5_pins_a>; 
10     status = "okay"; 
11 };

这里追加了两个串口,分别为uart5和usart3,追加的内容很简单都是使用了刚刚添加的pinmux配置。把status属性原来为“disabled”改为“okay”。

设置串口别名

之前UART驱动分析已经知道,驱动会读取aliases节点,添加的别名如下所示:

示例代码 46.4.3 串口的别名
1 aliases {
2     serial0 = &uart4;
3     serial1 = &uart5;
4     serial2 = &usart3;
5 };

serial0是uart4的别名,表示在系统启动生成一个名为“/dev/ttySTM0”的设备文件serial1就会生成“/dev/ttySTM1”如此类推,最多8个。serial0就是调试串口。

修改完成以后重新编译设备树并使用新的设备树启动Linux,如果设备树修改成功的话系统启动以后就会有如下图所示设备文件:

串口设备文件

ttySTM0为serial0,对应uart4;ttySTM1为serial1,对应uart5;ttySTM2为serial2,对应usart3。

移植minicom

minicom类似常用的串口调试助手,是Linux下很常用的一个串口工具,将minicom移植到开发板中,这样就可以借助minicom对串口进行读写操作

buildroot已经集成了minicom,所以只需要重新配置buildroot,使能minicom即可。首先跳转到buildroot的源码目录下,打开buildroot的图形化配置界面里配置以下选项:

-> Target packages
-> Hardware handling
[*] minicom

配置如下图所示:

使能minicom

保存buildroot的配置文件,输入命令“sudo make”重新编译文件系统。编译的时候要联网,因为buildroot在编译的时候需要从网上下载 minicom源码。当编译完成后,进入output/images目录,运行以下命令把文件系统替换进去:

cd output/images/ //进入到 output/images目录
sudo tar -axvf rootfs.tar -C /home/zuozhongkai/linux/nfs/rootfs //解压到 nfsroot目录

上述命令将buildroot中output/images/rootfs.tar这个压缩包解压到/home/zuozhongkai/linux/nfs/rootfs这个目录中,这个目录就是当前nfsroot目录,需要根据自己的实际情况解压到对应的目录文件中。

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

minicom版本号

从上图可以看出,此时minicom版本号为2.7.90,minicom版本号查看正常。输入如下命令打开minicom配置界面:

minicom -s

此时minicom配置界面就可以打开了,如下图所示:

minicom配置界面

如果出现如上图所示的界面,那么minicom就已经能够正常工作了。

RS232驱动测试

RS232连接设置

在测试之前要先将STM32MP1开发板的RS232接口与电脑连接起来,正点原子STM32MP1开发板上两个RS232接口如下图所示:

RS232接口

从上图中可以看出,正点原子开发板上有2个 RS232接口。这里要注意的是这两个RS232接口一个为公头,一个为母头,方便外接自己的设备。上图中左边的COM2为公头,可以通过JP4跳接到USART3上。右边的COM1为母头,可以通过JP5跳接到UART5上。本实验使用右边的COM1,所以需要将JP5的两个跳线帽接到上方,也就是将UART5与COM1连接起来

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

USB转DB9数据线

上图中所示的数据线是带有CH340芯片的,因此当连接到电脑以后就会出现一个COM口,这个COM口就是要使用的COM口。比如在正点原子教程中的电脑上就是COM11,在MobaXterm上新建一个连接,串口为COM11,波特率为115200。

minicom设置

在开发板中输入“minicom -s”,打开minicom配置界面,选中“Serial port setup”,如下图所示:

选中串口设置项

选中“Serial port setup”点击回车,进入设置菜单,如下图所示:

串口设置项

上图中有14个设置项目,分别对应A、B……N,比如第一个是选中串口UART5的串口文件为/dev/ttySTM1(因为设备别名serial1=&UART5),因此串口设置要设置为/dev/ttySTM1。设置方法就是按下键盘上的‘A’,然后输入“/dev/ttySTM1”即可,如下图所示:

串口设置文件设置

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

UART5设置

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

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

minicom帮助信息界面

从上图可以看出,minicom有很多快捷键,本实验打开minicom的回显功能,回显功能配置项为“local Echo on/off…E”,因此按下E即可打开/关闭回显功能。

RS232收发测试

发送测试

首先测试开发板通过UART5向电脑发送数据的功能,需要打开minicom的回显功能(不打开也可以,但是在minicom中看不到自己输入的内容),回显功能打开以后输入“AAAA”,如下图所示:

通过UART5向电脑发送“AAAA”

上图中的“AAAA”就是开发板通过UART5向电脑发送的数据,那么电脑的COM11就会接收到“AAAA”,MobaXterm中COM11收到的数据如下图所示:

电脑接收到开发板发送的数据

可以看出,开发板通过UART3向电脑发送数据正常,接下来测试开发板数据接收功能。

接收测试

接下来测试开发板的UART5接收功能,同样的,要先打开MobaXterm上COM11的本地回显,正点原子教程里面没有指导该功能,但是开发板是可以接收到在COM11上输入的字符。比如,这里输入‘123456’,此时开发板接收到的数据如下图所示:

开发板接收到发送的数据

UART5收发测试都没有问题,说明UART5驱动工作正常。如果要退出minicom,在minicom通信界面按下CRTL+A,然后按下X来关闭minicom。

RS485测试

前面已经说过了,STM32MP1开发板上的RS485接口连接到了USART3上,因此本质上就是个串口。 RS232实验已经将USART3的驱动编写好了,所以RS485实验就不需要编写任何驱动程序,可以直接使用minicom来进行测试

RS485连接设置

首先是设置JP4跳线帽,将1-3、2-4连接起来, RS485接口如下图所示:

RS485接口设置

一个板子是不能进行RS485通信测试的,还需要另一个RS485设备,比如另外一块STM32MP1开发板。这里可以使用正点原子出品的USB三合一串口转换器,支持USB转TTL、RS232和RS485,如下图所示:

正点原子USB三合一川口转换器

使用杜邦线将USB串口转换器的RS485接口和STM32MP157开发板的RS485连接起来,A接A,B接B,不能接错了!连接完成以后如下图所示:

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

串口转换器通过USB线连接到电脑上,用的是 CH340版本的,因此就不需要安装驱动,如果使用的是FT232版本的就需要安装相应的驱动。连接成功以后电脑就会有相应的COM口,比如教程中电脑上就是COM6,接下来就是测试。

RS485收发测试

RS485的测试和RS232一模一样!电脑上USB多合一转换器对应COM12。因为MobaXterm没有找到回显设置,因此这里为了方便观察,USB多合一转换器使用SecureCRT这个终端软件。使用SecureCRT创建一个COM12的连接,开发板使用USART3,对应的串口设备文件为/dev/ttySTM2,因此开发板使用minicom创建一个/dev/ttySTM2的串口连接。串口波特率都选择115200 8位数据位,1位停止位,关闭硬件和软件流控

RS485发送测试

首先测试开发板通过RS485发送数据,设置好minicom以后,同样输入“AAAA”,也就是通过RS485向电脑发送一串“AAAA”。如果RS485驱动工作正常的话,那么电脑就会介绍到开发板发送过来的“AAAA”,如下图所示:

RS485数据发送测试

从上图可以看出开发板通过RS485向电脑发送“AAAA”成功,说明RS485数据发送正常。

RS485接收测试

接下来测试一下RS485数据接收,电脑通过RS485向开发板发送“BBBB”,然后观察minicom是否能接收到“BBBB”。结果如下图所示:

RS485数据接收测试

从上图中可以看出开发板接收到电脑通过RS485发送过来的“BBBB”,说明RS485数据接收正常。

GPS测试

GPS连接设置

GPS模块大多数都是串口输出的,这里以正点原子出品的ATK118-BD模块为例,这是一款GSP+北斗的定位模块,如下图所示:

正点原子ATK1218-BD定位模块

首先要设置STM32MP1开发板上的JP5跳线帽,将UART5与ATK模块接口上的串口连接起来,如下图所示:

UART5跳线帽连到GPS

此时UART5_TX和UART5_RX已经连接到了开发板上的ATK MODULE上,直接将ATK1218-BD模块插到开发板上的ATK MODULE接口即可,开发板上的ATK MODULE接口是6脚的,而 ATK1218-BD模块是5脚的,因此需要靠下插(VCC对应 5V)!然后GPS需要接上天线,天线的接收头一定要放到户外,因此室内一般是没有GPS信号的。连接完成以后如下图所示:

GPS模块连接示意图

GPS数据接收测试

GPS都是被动接收定位数据的,模块接收定位卫星数据,然后计算出位置信息通过串口输出。所以要先设置minicom,UART5对应/dev/ttySTM1,串口设置要求如下:

  1. 波特率设置为38400,因为正点原子的ATK1218-BD模块默认波特率就是38400。
  2. 8位数据位,1位停止位。
  3. 关闭硬件和软件流控。

设置好以后如下图所示:

串口设置

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

GPS数据

总结

这一篇实验,其实就是对于STM32MP157开发板的串口的使用,这里驱动也是ST官方已经写好的,我们需要做的就是直接在pinctrl加节点写好GPIO的复用,然后在设备树里面添加对应的串口节点,并关联上pinctrl。还有就是在buildroot里面要配置一下minicom这个串口调试,方便测试串口。

之后的测试,RS485个人觉得可以关注一下,至于RS232可以看到其实现在用的已经很少了,就直接都是RS232转串口就可以了,比如精英板就是串口直接Type-C了,不会再用RS232。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/123313.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

97. 交错字符串

题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 解题思路&#xff1a;动态规划。 如果s1.length()s2.length ! s3.length()&#xff0c;直接返回false&#xff0c;否则使用动态规划求解。定义状态&#xff1a;dp[i][i]&#xff…

解决找不到vcruntime140.dll,无法继续执行代码方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到vcruntime140.dll”。这个错误通常发生在运行某些程序或游戏时&#xff0c;它会导致程序无法正常启动或运行。那么&#xff0c;找不到vcruntime140.dll&#xff0c;无法继续执行代码…

word中批注内容显示设置

在修改他人word时&#xff0c;有时候保存为pdf需要有选择性的显示&#xff0c;如下图&#xff0c;原始修改方式包括三种&#xff1a;批注、格式修改、删除添加&#xff0c;如下图所示&#xff1a; 有时候不想要格式设置说明&#xff0c;则只需要进行在审阅--显示标志--不勾选设…

【Java 进阶篇】解决Java Web应用中请求参数中文乱码问题

在Java Web应用开发中&#xff0c;处理请求参数时经常会遇到中文乱码的问题。当浏览器向服务器发送包含中文字符的请求参数时&#xff0c;如果不正确处理&#xff0c;可能会导致乱码问题&#xff0c;使得参数无法正确解析和显示。本文将详细探讨Java Web应用中请求参数中文乱码…

qt高精度定时器的使用停止线程应用

##线程停止 //线程停止应用 public: explicit WorkerThread(QObject *parent 0) :QThread(parent), m_bStopped(false){qDebug() << "Worker Thread : " << QThread::currentThreadId();}~WorkerThread(){stop();quit();wait();}void stop() {qDebug()…

共用体开发案例

有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。要求用同一个表格来处理。 #include <stdio.h>struct Person {char name[32];int age;char zhiYe;char addr[32];union {int class;…

JDBC与MySql数据库

一、系统开发前的环境准备 1.下载Mysql 下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/ 文件解压缩到本地 在此路径下新增my.ini文件以及新建data文件夹 编辑my.ini文件 配置环境变量 注意是编辑系统变量的Path 以管理员身份运行cmd 输入命令&#xff1a…

VDA到Excel方案介绍之自定义邮件接收主题

VDA标准是德国汽车工业协会&#xff08;Verband der Automobilindustrie&#xff0c;简称VDA&#xff09;制定的一系列汽车行业标准。这些标准包括了汽车生产、质量管理、供应链管理、环境保护、安全性能等方面的规范和指南。VDA标准通常被德国和国际上的汽车制造商采用&#x…

【机器学习】sklearn特征值选取与处理

sklearn特征值选取与处理 文章目录 sklearn特征值选取与处理1. 调用数据集与数据集的划分2. 字典特征选取3. 英文文本特征值选取4. 中文特征值选取5. 中文分词文本特征抽取6. TfidfVectorizer特征抽取7. 归一化处理8. 标准化处理9. 过滤低方差特征10. 主成分分析11. 案例&#…

制作自己的前端组件库并上传到npm上

最近实现了自己的一个前端组件库demo&#xff0c;目前只上传了几个小组件。话不多说&#xff0c;上图&#xff1a; 我分了三个项目&#xff1a;yt-ui组件库、使用文档、demo。线上地址如下&#xff1a; [yt-ui组件库](mhfwork/yt-ui - npm) [组件库使用文档](介绍 | mhfwork/y…

docker应用部署---Tomcat的部署配置

1. 搜索tomcat镜像 docker search tomcat2. 拉取tomcat镜像 docker pull tomcat3. 创建容器&#xff0c;设置端口映射、目录映射 # 在/root目录下创建tomcat目录用于存储tomcat数据信息 mkdir ~/tomcat cd ~/tomcatdocker run -id --namec_tomcat \ -p 8080:8080 \ -v $PWD:…

拓扑排序详解

拓扑排序 如果说最短路径是有环图的应用&#xff0c;那么拓扑排序就是无环图的应用。 拓扑排序介绍 我们会把施工过程、生产流程、软件开发、教学安排等都当成- -个项目工程来对待&#xff0c;所有的工程都可分为若干个“活动”的子工程。例如下图是我这非专业人士绘制的一张…

bbr 流相互作用图示

类似 AIMD 收敛图&#xff0c;给出 bbr 的对应图示&#xff1a; bbr 多流相互作用非常复杂&#xff0c;和右下角的 AIMD 相比&#xff0c;毫无美感&#xff0c;但是看一眼左下角的 bbr 单流情况&#xff0c;又过于简陋&#xff0c;而 bbr 的核心就基于这简陋的假设。 浙江温…

ChatGPT从入门到精通

目录 什么是ChatGPT&#xff1f;ChatGPT能帮我干什么&#xff1f;标题在哪里可以使用ChatGPT&#xff1f;什么是ILoveChatGPT&#xff08;IMYAI&#xff09;&#xff1f;标题如何拥有头像&#xff1f;如何获取更多对话次数&#xff1f;!标题如何提问GPT&#xff1f;如何正确地利…

【黑马程序员】mysql进阶再进阶篇笔记

64. 进阶-锁-介绍(Av765670802,P121) 为了应对不同场景 全局锁-所有表 表计锁 一张表 行级锁 一行数据 65. 进阶-锁-全局锁-介绍(Av765670802,P122) 66. 进阶-锁-全局锁-一致性数据备份(Av765670802,P123) 67. 进阶-锁-表级锁-表锁(Av765670802,P124) 读锁、写锁 68. 进阶…

SSD1306 oled显示屏的驱动SPI接口

有IIC接口 和SPI接口 还有8080,6080接口等 arduino SPI接口 直接使用u8g2库实现 //U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock*/ 13, /* data*/ 11, /* cs*/ 10, /* dc*/ 9, /* reset*/ 8); asrpro(SPI接口按下方修改&#xff0c;IIC接口官方有驱动&…

数据结构OJ题

目录 1.字符串左旋 2.字符串旋转结果 3.旋转数组 4.移除元素 本篇主要是讲解一些OJ题目。 1.字符串左旋 字符串左旋 实现一个函数&#xff0c;可以左旋字符串中的k个字符 例如&#xff1a; ABCD左旋一个字符得到BCDA ABCD左旋两个字符得到CDAB 方法1【暴力求解】 翻转1…

MSQL系列(八) Mysql实战-SQL存储引擎

Mysql实战-SQL存储引擎 前面我们讲解了索引的存储结构&#xff0c;BTree的索引结构&#xff0c;我们一般都知道Mysql的存储引擎有两种&#xff0c;MyISAM和InnoDB,今天我们来详细讲解下Mysql的存储引擎 文章目录 Mysql实战-SQL存储引擎1.存储引擎2.MyISAM的特点3. InnoDB的特…

Godot 官方2D C#重构(4):TileMap进阶使用

文章目录 前言完成内容项目节点结构TileMap设置图片资源备选图片添加物理碰撞添加y轴遮罩判断Y Sort Enable是干什么的&#xff1f; 脚本代码 前言 Godot 官方 教程 Godot 2d 官方案例C#重构 专栏 Godot 2d 重构 github地址 完成内容 项目节点结构 TileMap设置 图片资源 备选图…

Leetcode—21.合并两个有序链表【简单】

2023每日刷题&#xff08;十三&#xff09; Leetcode—21.合并两个有序链表 直接法实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* mergeTwoLists(struct ListNode* list1, struct…