STM32F103ZET6结合CubeMX的GPS例程:串口DMA与NMEA解析实战

发布时间:2026/10/15 13:26:06

STM32F103ZET6结合CubeMX的GPS例程:串口DMA与NMEA解析实战 简介STM32F103ZET6结合CUBE的GPS例程是一份面向嵌入式初学者的完整工程资源核心解决如何借助STM32CubeMX生成底层初始化代码并通过UART与GPS模块交互、解析NMEA协议数据的问题。压缩包共188个文件大小约8.58MB其中以HAL库C源码/头文件、Keil工程文件uvprojx、ioc为主同时包含hex、axf等编译结果方便直接烧录与对照学习。已有220人学习使用适合正在练习CubeMX配置、串口通信和传感器数据解析的开发者。例程完整覆盖时钟树、GPIO/UART初始化、接收中断以及ATK_MO1218模块NMEA/BIN报文解析等环节在Keil中打开MDK-ARM工程可清晰看到从初始化到数据处理的调用结构对理解STM32 HAL库驱动组织、GPS数据处理流程有比较直接的参考价值。1. STM32F103ZET6 结合 CubeMX 的 GPS 例程串口读出的不只是经纬度STM32F103ZET6 结合 CubeMX 的 GPS 例程本质上是要让这枚 72MHz 的单片机用串口读懂 GPS 模块不断吐出的 NMEA 句子再从中拆出经纬度、UTC 时间和定位状态。很多人以为这只是接几根线、printf 打印的事但真正动手会发现波特率、DMA 缓冲区、帧同步、天线馈电这些细节任何一处没处理好都会让你查半天。这里写的是我实际调试这类例程的完整路径适合正在做定位小车、户外采集设备的从业者也适合对 CubeMX 串口 DMA 机制想理解得更透的人。你会从 CubeMX 配置开始一路走到解析代码再看到 5 条真实调试验证过的踩坑记录。2. CubeMX 配置 USART1 与 DMAGPS 串口接收骨架先搭对先说明GPS 模块输出的 UART TTL 电平不是 RS232更不是 USB。接线只需要四根GPS TX 接单片机 RXGPS RX 接单片机 TXGND 接 GND供电按模块要求接 3.3V 或 5V。我一般推荐用 USART1因为 F103ZET6 的 PA9/PA10 引脚角度好而且 USART1 挂在 APB2 总线上时钟配置直观。2.1 选串口引脚与时钟为什么先从 9600 波特率开始市面上的入门级 GPS 模块出厂默认波特率基本都是 9600对应 8 位数据位、无校验、1 位停止位。但 CubeMX 新建工程时串口默认经常是 115200如果不改过来GPS 在线时会收到乱码或空白。正确做法是先把 USART1 的 Baud Rate 设为 9600其他保持默认。GPS 模块即使支持 115200也是靠配置命令切换的例程先按 9600 来调试通了再提速。时钟方面RCC 的 HSE 选 Crystal/Ceramic ResonatorSYS 的 Debug 选 Serial Wire避免下载器占用 PA13/PA14。生成代码时 USART1 时钟来源是 PCLK272MHzCubeMX 自动计算分频系数不需要手动算波特率寄存器。这里有两个坑一是 HSE 没配置程序会跑得很奇怪二是 Debug 忘了选 Serial Wire会导致下载器第二次连不上芯片。CubeMX 生成的初始化函数长这样后面所有串口行为都取决于这几个参数static void MX_USART1_UART_Init(void) { huart1.Instance USART1; huart1.Init.BaudRate 9600; huart1.Init.WordLength UART_WORDLENGTH_8B; huart1.Init.StopBits UART_STOPBITS_1; huart1.Init.Parity UART_PARITY_NONE; huart1.Init.Mode UART_MODE_TX_RX; huart1.Init.HwFlowCtl UART_HWCONTROL_NONE; huart1.Init.OverSampling UART_OVERSAMPLING_16; if (HAL_UART_Init(huart1) ! HAL_OK) { Error_Handler(); } }WordLength 必须是 8BParity 无StopBits 1这对应 GPS 模块的 8N1 格式。Mode 一定要选 TX_RX因为单片机不仅收 GPS 数据还要发配置命令比如关闭某些无用语句、修改更新率。HwFlowCtl 不需要GPS 模块没有硬件流控脚。2.2 在 CubeMX 里勾选 USART DMA 中断完整操作步骤如果直接在 Pinout 视图点 PA9 选 USART1_TX、PA10 选 USART1_RX或者点 USART1 的 Mode 下拉选 Asynchronous系统会自动分配引脚。然后打开 DMA Settings 页签Add 一个 USART1_RXDirection 选 Peripheral To MemoryMode 选 Circular。为什么选 Circular因为 GPS 数据连续不断Circular 模式让 DMA 循环往缓冲区写不需要收到一帧就重新启动一次 DMA。实际操作步骤在 Categories 里选 Connectivity - USART1Mode 选 Asynchronous。Baud Rate 填 9600其余默认。切到 DMA SettingsAdd USART1_RX。DMA 参数Mode 选 CircularData Width 都选 BytePriority 随意。在 NVIC Settings 勾选 USART1 global interrupt 和 DMA interrupt。时钟 Configuration 里把 HCLK 设到 72MHz然后生成代码。配置完成后CubeMX 会生成 MX_DMA_Init 和 MX_USART1_UART_Initmain 函数会在 while 循环前调用它们。注意 MX_USART1_UART_Init 必须在 MX_DMA_Init 之后调用因为 HAL_UART_Init 内部会检查 DMA 句柄的初始化状态。你不需要手动改 stm32f1xx_hal_msp.c那里的 GPIO 和 DMA 关联已经由 CubeMX 自动完成。有人会问不用 DMA直接在 while 循环里 HAL_UART_Receive 一行行读不行吗不行。9600 波特率下一个字符约 1.04mswhile 循环里做其他事情很容易错过字节尤其是有 OLED 刷新或算法任务时会丢数据。逐字节中断也行但每次进中断只收一个字节CPU 开销比 DMA 大一截。所以用 DMA 配合空闲中断是主流做法。DMA 接收的启动代码放在 main 函数里#define GPS_BUF_SIZE 256 uint8_t gps_rx_buf[GPS_BUF_SIZE]; HAL_UARTEx_ReceiveToIdle_DMA(huart1, gps_rx_buf, GPS_BUF_SIZE);HAL_UARTEx_ReceiveToIdle_DMA 是 HAL 库的扩展接口它会在 DMA 传输过程中检测到总线空闲时触发回调。最后一个参数 256 是缓冲区上限一个 NMEA 句子最长 82 字节256 完全够用。如果用的 HAL 库版本比较老没有这个扩展接口可以手动把接收配置成 HAL_UART_Receive_DMA 定长模式但帧边界要自己用空闲中断处理逻辑会多一点。2.3 生成代码后的关键文件main.c、usart.c、gpio.c 里哪些不能动CubeMX 生成代码后你并不需要修改 usart.c 和 gpio.c。这两个文件里的引脚复用配置、DMA 关联是自动生成的手动改动会在下次重新生成时被覆盖。我一般把 GPS 解析代码单独放在 gps.c/gps.hmain.c 里只保留 DMA 启动和标志位处理。gpio.c 会把 PA9/PA10 配置成复用推挽输出和输入模式。注意 CubeMX 生成的 AFIO 配置是针对串口的不要再去调用 HAL_GPIO_Init 覆盖它们。如果你的 GPS 模块有使能脚可以在 CubeMX 里把它配成推挽输出初始电平按模块手册要求给。到这一步串口骨架已经搭好芯片能接收字节也知道什么时候一帧结束。下一章处理最核心的问题怎么把缓冲区里的字节变成经纬度。3. 解析 NMEA 0183 帧从 $GNGGA 到十进制经纬度的关键代码NMEA 0183 是 GPS 模块输出的“报文语言”。每条语句以 $ 开头、以 \r\n 结尾字段用逗号分隔。普通定位用两条语句就够GGA经纬度、定位状态、卫星数和 RMC速度、航向、日期。注意模块型号不同输出前缀可能是 GP、GL、GN所以匹配时只找 GGA/RMC 子串不要认死 GPGGA。3.1 NMEA 帧格式GGA 与 RMC 的关键字段定位一条完整的 GNGGA 语句$GNGGA,112233.00,3112.3456,N,12134.5678,E,1,12,1.0,12.3,M,0.0,M,,*4A字段依次是 UTC 时间112233 表示 11 时 22 分 33 秒、纬度3112.3456度分格式、纬度方向 N/S、经度12134.5678度分格式、经度方向 E/W、定位状态0 无效1 单点定位2 差分、卫星数、HDOP、海拔、大地水准面高度、差分数据、校验和。RMC 语句$GNRMC,112233.00,A,3112.3456,N,12134.5678,E,0.0,0.0,280324,,,D,V*2D第 2 个字段 A/V 表示数据有效或无效速度字段单位是节航向字段单位是度。解析时必须按逗号位置取字段不要用 strtok因为 strtok 会破坏原字符串且不可重入。我自己写了一个“按序号取字段”的辅助函数通过遍历逗号返回第 n 个字段的指针。3.2 用 DMA 缓冲区组装完整帧状态机逐字节处理的做法DMA 缓冲区里同时可能有半帧、整帧、下一帧的开头所以解析前需要状态机做帧同步。基本逻辑是等待 $ 符号然后一直收集到 \n得到一行完整字符串。下面这段可以直接放进串口回调里喂字节。#define GPS_LINE_MAX 128 typedef struct { char buf[GPS_LINE_MAX]; uint16_t len; uint8_t ready; // 完整一行到达后置 1 } gps_line_t; typedef enum { GPS_IDLE, GPS_RECEIVING } gps_sm_state_t; static gps_sm_state_t gps_sm GPS_IDLE; void gps_rx_byte(uint8_t ch, gps_line_t *line) { switch (gps_sm) { case GPS_IDLE: if (ch $) { line-len 0; line-buf[line-len] ch; line-ready 0; gps_sm GPS_RECEIVING; } break; case GPS_RECEIVING: if (ch \n) { line-buf[line-len] \0; line-ready 1; gps_sm GPS_IDLE; } else if (ch \r) { /* 忽略回车等 \n */ } else if (line-len GPS_LINE_MAX - 1) { line-buf[line-len] ch; } else { /* 超长丢弃当前帧 */ gps_sm GPS_IDLE; } break; default: gps_sm GPS_IDLE; break; } }状态机的要点GPS_IDLE 状态下只认 $一旦收到就进入接收状态接收时遇到 \n 表示一帧结束立刻把字符串封口并置 ready。缓冲区长度 128NMEA 最长 82 字节留了余量。如果中间出现异常字符导致长度超限直接回到 IDLE不等待换行这样能主动丢弃坏行。状态机本身不解析只负责把完整的一行放到 line 里真正的解析放到主循环避免在中断里做浮点运算。在 HAL_UARTEx_RxEventCallback 回调里把 DMA 收到的字节逐个喂给上述状态机gps_line_t gps_line; void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if (huart-Instance USART1) { for (uint16_t i 0; i Size; i) { gps_rx_byte(gps_rx_buf[i], gps_line); } HAL_UARTEx_ReceiveToIdle_DMA(huart1, gps_rx_buf, GPS_BUF_SIZE); } }注意如果使用 Circular DMA回调里的 Size 是当前这轮传输的字节数不代表缓冲区绝对位置。上一帧数据还没取走新数据可能覆盖所以必须逐字节同步而不是直接拿整个缓冲区做解析。另外回调最后要重新启动一次 ReceiveToIdle_DMA如果只在初始化时启动一次第二次触发回调后缓冲区就不会再搬数据了。很多例程跑几秒后停止输出就是这个原因。有的做法是直接在收到 \n 后调用解析函数这样能省一道主循环查询但解析里如果用 atof 转浮点会占用一段时间。为了让中断里尽量轻量我习惯只置 ready主循环看到 ready 再解析。3.3 度分格式转十进制度数学公式与代码GPS 模块输出的是度分格式。3112.3456 表示 31 度 12.3456 分经度 12134.5678 表示 121 度 34.5678 分。转换公式是十进制度 度 分 / 60。区分度宽的方法整数部分有 4 位是纬度前 2 位为度有 5 位是经度前 3 位为度。下面这段代码适合直接用于 F103double nmea_to_dec(char *field) { char *dot strchr(field, .); if (!dot) return 0.0; int n dot - field; double deg_part 0.0; double min_part 0.0; if (n 4) { char deg_str[4] {0}; memcpy(deg_str, field, 2); deg_part atoi(deg_str); min_part atof(field 2); } else if (n 5) { char deg_str[4] {0}; memcpy(deg_str, field, 3); deg_part atoi(deg_str); min_part atof(field 3); } return deg_part min_part / 60.0; }代码说明strchr 定位小数点n 是整数部分长度。纬度的 n 为 4经度的 n 为 5。int 部分复制后转整数小数部分连同剩余的分一起用 atof 读出。比如 1234.5678field2 得到 34.5678正好是分。最后再根据 N/S/E/W 决定符号。GGA 解析函数可以这样写把一行字符串按逗号分割成字段typedef struct { double lat; double lon; uint8_t fixed; } gps_point_t; gps_point_t gps_point; void gps_parse_gngga(char *line) { if (strstr(line, GGA) NULL) return; char *p[14]; uint8_t idx 0; p[idx] line; for (char *q line; *q idx 14; q) { if (*q ,) { *q \0; p[idx] q 1; } } if (idx 10) return; int state atoi(p[6]); gps_point.fixed (state 1 || state 2); if (!gps_point.fixed) return; gps_point.lat nmea_to_dec(p[2]); if (p[3][0] S) gps_point.lat -gps_point.lat; gps_point.lon nmea_to_dec(p[4]); if (p[5][0] W) gps_point.lon -gps_point.lon; }这段代码把字符串里的逗号替换成 \0然后 p 数组里每个元素指向对应字段。p[2] 是纬度字符串p[3] 是纬度方向p[4] 是经度字符串p[5] 是经度方向p[6] 是定位状态。注意 line 必须是状态机里那块独立缓冲区不能直接拿 DMA 缓冲区解析否则逗号被替换成 \0 后DMA 数据就坏了。只有在定位成功后才更新 gps_point否则保留上一次值主循环只管打印。4. 组装最小 GPS 例程printf 重定向、主循环解析与模拟数据验证有了串口接收和帧解析剩下是把它们接进 main 函数。这章给出一份可以直接抄的骨架以及不需要 GPS 模块也能验证的模拟方法。4.1 printf 重定向到 USART1让调试输出不再依赖串口助手F103 上想用 printf 输出需要重写 fputc。CubeMX 生成的 HAL 库不会自动对接 C 库 stdout必须手动加几行#include stdio.h int fputc(int ch, FILE *f) { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, 0xFFFF); return ch; }HAL_UART_Transmit 的第三个参数是超时时间0xFFFF 表示等待发送完成。printf 每写一个字符就调用一次 fputc性能不高但调试足够。特别提醒不要在串口中断回调里调用 printf阻塞发送会让中断无法退出最终系统卡死。GCC 工具链下还要在链接器里加上 -u _printf_float 和 nano.specs否则 printf 打印浮点数会变成空白。CubeIDE 默认启用部分 nano但浮点支持要手动确认。4.2 最小可运行例程的代码骨架从启动到打印经纬度主循环的任务顺序查询行缓冲是否 ready、解析 GGA、打印坐标。下面这段骨架可以直接放进工程volatile uint8_t gps_frame_ready 0; // 也可以直接用 gps_line.ready void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if (huart-Instance USART1) { for (int i 0; i Size; i) { gps_rx_byte(gps_rx_buf[i], gps_line); } gps_frame_ready 1; HAL_UARTEx_ReceiveToIdle_DMA(huart1, gps_rx_buf, GPS_BUF_SIZE); } } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); HAL_UARTEx_ReceiveToIdle_DMA(huart1, gps_rx_buf, GPS_BUF_SIZE); while (1) { if (gps_line.ready) { gps_line.ready 0; gps_parse_gngga(gps_line.buf); if (gps_point.fixed) { printf(lat%.6f lon%.6f\r\n, gps_point.lat, gps_point.lon); } } } }注意 gps_rx_buf 是 DMA 缓冲区gps_line 是状态机自己的行缓冲两者不能混用。gps_line.ready 在状态机收到 \n 时置位主循环里清空后再解析。gps_point 是上一节定义的全局结构体解析函数每收到一行有效 GGA 就会更新它。如果打印出来 lat/lon 都是 0先检查 gps_point.fixed 是否为 1。GPS 模块没有定位时GGA 的状态字段是 0解析函数不会更新坐标这是正常的。不要为了看到数据去掉 fixed 判断否则你会得到一堆无意义的零值。回调里的 gps_frame_ready 其实可省这里保留是为了让你将来扩展逻辑时有个入口。实际主循环完全可以用 gps_line.ready 作为触发条件。要注意的是如果主循环里做了耗时操作比如刷新 OLED状态机仍然在中断里同步收字节不会丢数据但如果耗时超过缓冲区长度DMA 仍然可能覆盖所以解析和业务逻辑越简洁越好。4.3 用串口模拟器验证解析不接 GPS 也能看到经纬度在没有 GPS 模块或室内无信号时用 USB 转 TTL 模块从 PC 发模拟数据。接线方式USB 转 TTL 的 TX 接 PA10GND 接 GND。不需要接 RX因为只是单向发送。PC 串口助手设置为 9600 8N1发送这行$GNGGA,112233.00,3112.3456,N,12134.5678,E,1,12,1.0,12.3,M,0.0,M,,*4A串口助手发送框通常要勾选“追加回车换行”否则程序永远等不到 \n。发送后STM32 会在 USART1 TX 上打印转换结果。计算机用另一个 USB 转 TTL 接 PA9 看输出如果看到 lat31.206426、lon121.576130说明整个链路通了。模拟数据的校验和不对也没关系解析例程没有做校验。但如果你想验证校验逻辑可以用在线工具重新计算。模拟发送的好处是可以在室内反复测试解析边界比如改变小数位、去掉定位状态等坏处是没法模拟模块上电后的冷启动过程那个只能真机测试。还可以连续发送多帧比如把上面这行粘贴十遍观察程序是否每一帧都解析。DMA 缓冲区只有 256 字节9600 波特率下连续 10 行需要 1 秒多状态机会逐字节切出 10 个完整行。如果其中某一行因为供电或干扰是残缺的状态机能自动丢弃并继续下一行。可以在状态机里加一个超时清帧逻辑收到 $ 后 200 毫秒内没有结束符就丢弃这个超时在真实场景和模拟都很有用。5. GPS 例程避坑指南波特率、供电、PPS 与天线的 5 条排错记录这一章按“现象 → 原因 → 解决”的顺序写每一条都来自实际调试覆盖大多数入门项目会碰到的问题。5.1 串口一片空白或全是乱码先怀疑波特率别先怀疑代码现象程序烧写后串口终端什么都收不到或者收到 0x00、0x0A 这样的乱码。原因GPS 模块出厂默认 9600CubeMX 工程默认可能是 115200两边波特率不一致。另一层原因是 PC 端串口助手波特率与 STM32 发送端不一致。解决先检查 CubeMX 里 USART1 的 BaudRate确认 9600再看 PC 串口助手的波特率必须一致。如果模块之前被人配置成其他波特率可以用逻辑分析仪抓模块 TX 的波形数一帧的位宽来估算实际波特率。血泪经验不要一上来就翻软件代码先把“模块、CubeMX、串口助手”三处波特率对齐。5.2 模块指示灯亮但一直无定位天线馈电和冷启动时间现象GPS 模块带电TX 引脚有波形但程序始终打印 fixed0卫星数 0。原因天线没接好或没馈电。很多模块的天线接口旁边有 ANT 馈电引脚如果不接 VCC有源天线内的低噪声放大器不工作信号到不了芯片。另一个原因是冷启动时间不够首次定位需要 30 秒到 5 分钟在室内尤其难搜到星。解决检查天线馈电引脚是否接到模块指定的电源引脚把天线吸在窗边或开阔处在调试信息里打印卫星数字段看到卫星数从 0 增长到 8 以上说明模块在正常工作。如果卫星数一直是 0优先怀疑天线而不是代码。5.3 PPS 引脚悬空或误接干扰解析只接 TX、RX、GND现象单测 TX 波形正常接上单片机后偶尔解析出错经纬度跳变到错误值。原因PPS 引脚在定位后输出规律的 1Hz 脉冲电平约 3.3V它和 TX 引线距离太近会串扰或者直接把 PPS 误接到另一个串口的 RX导致中断里收到干扰脉冲影响解析。解决GPS 和单片机之间只连三根线TX、RX、GND。PPS 引脚不接或用跳线单独接到 LED 做指示灯。如果长线不可避免在 GPS TX 到单片机 RX 之间串一个 1kΩ 电阻能明显减少反射振铃。教训不要为了省事把 PPS 和 TX 塞进同一个排线座。5.4 DMA 缓冲区覆盖导致经纬度跳变状态机解析而不是整个缓冲区解析现象例程跑几分钟后打印的经纬度偶尔出现几百公里外的跳变。原因Circular DMA 不会等待主循环处理如果主循环耗时超过一个字符间隔缓冲区会覆盖未处理的字节。最典型的是你把 printf、OLED 刷新都放在主循环里分析一帧数据还没跑完下一帧已经写进了同一块缓冲区。解决不要在 DMA 缓冲区上直接做 strstr 和逗号分割改用逐字节状态机一行收到 \n 后立刻把整行放到独立缓存再由主循环解析。另一个办法是环形队列但成本更高。实测 9600 波特率下状态机加主循环解析完全跑得动不会丢数据。这个坑在功能多起来后最容易爆发因为主循环稍慢一点覆盖就发生了。5.5 在中断回调里做 printf 和解析系统卡死或看门狗复位现象一收到 GPS 数据程序就死机或者看门狗周期复位。原因在 HAL_UARTEx_RxEventCallback 回调里调用 printfprintf 内部阻塞发送到 USART1而 USART1 中断还挂着新数据又进来最终导致中断无法退出或堆栈溢出。解决回调里不要做 printf不要做浮点格式化也不要调用任何可能重入 HAL 库发送函数的代码。逐字节喂状态机是轻量操作可以放在中断里解析、打印、屏幕刷新全部移到主循环。核心原则中断里只存数据、置标志不在中断里“消费”数据。6. 进阶玩法PPS 时间同步、坐标偏移与低功耗定位这一节讲三个增强点让例程不只是打印经纬度而是能真正用于记录、上传和低功耗设备。6.1 用 PPS 校准本地秒计数给定位日志加时间戳GPS 模块定位成功后PPS 引脚会输出一个精确到微秒级的秒脉冲。把 PPS 接到 STM32 的外部中断引脚比如 PA0配置成上升沿触发。每次中断到来时读取 TIM2 的计数值再结合 NMEA 里的 UTC 时间字段就能为每帧数据打上一个误差小于 1 毫秒的时间戳。做法在 EXTI 回调里把本地秒计数器归整到整秒注意 PPS 电平是 3.3V直接进 PA0 没问题但建议加一个 10kΩ 下拉电阻防止上升沿毛刺误触发。6.2 WGS-84 转 GCJ-02你知道经纬度不代表地图能直接用GPS 模块输出的是 WGS-84 坐标而国内地图应用普遍使用 GCJ-02 坐标系。直接拿 WGS-84 丢进地图 API位置会偏出 300 米左右。这个偏移不是随机误差是两套坐标系定义不同。如果系统只是自用查看轨迹不接入地图可以不处理但要做车辆上报必须转换。常见做法是移植开源的坐标转换库输入 WGS-84 经纬度输出 GCJ-02 经纬度。转换公式本身不是几行内能写完的建议直接找成熟实现并先用几个已知点做实测验证。6.3 低功耗GPS 模块的信号控制与 MCU 睡眠配合电池供电时GPS 模块是耗电大户。常见做法是让模块按需工作用一个 GPIO 控制模块的电源10 秒上一次电定位 2 秒后断电。另一个做法是给模块发送 PMTK 命令降低更新率$PMTK220,2000*2C\r\n参数 2000 表示每 2000 毫秒输出一次。配合 STM32 的 Stop 模式可以在等待 PPS 时入睡PPS 上升沿唤醒。唤醒后第一件事是清空 DMA 接收计数因为睡眠期间模块可能已经吐了几帧数据不清理会串帧。这个方案的坑在于模块重新定位也需要时间冷启动接近 30 秒所以不能简单硬关电源通常使用模块的 backup 模式或只关天线馈电。最后说一个我自己的习惯调试 GPS 例程时先把模块放到窗台让天线对着天空再接入烧录线程序里第一时间打印模块原始 NMEA 语句而不是打印解析后的坐标。因为原始语句能看出模块是否输出正常解析后的坐标要叠加一堆假设。如果你从这个习惯开始很多坑会自动消失。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/15 13:26:06

ResNet中downsample的原理、形态与梯度影响分析

1. 为什么ResNet的downsample不是“简单下采样”,而是个精密开关?第一次在某高校实验室跑通ResNet-18时,我盯着BasicBlock里那个downsample参数发了三分钟呆——它既不是卷积层,也不是池化层,更不是可学习参数&#xf…

2026/10/15 13:21:00

Context Mode深度解析:从交互范式到工程落地的完整避坑指南

做过几年C端应用或AI产品的人,大概率都碰过同一个词:context-mode。翻译成大白话就是“上下文模式”,但这个词在很多团队里其实是被轻拿轻放的——产品经理把它当成一个“记住用户上次操作”的开关,后端同学把它当作一个“传参数”…

2026/10/15 13:21:00

每日日报。

一、今日完成工作完成网络购物系统需求分析,梳理业务实体:用户、收货地址、商家、商品、商品规格、购物车、订单、订单明细、售后、平台管理员,共 10 个实体。完成数据库表结构设计,编写 MySQL 建表 SQL 脚本,整合全部…

2026/10/15 14:36:47

OpenShell:跨平台命令行环境统一配置与管理实战

算起来,我搞命令行环境这事差不多有七八年了。从最早只会敲ls到现在整天泡在终端里,最烦的不是命令记不住,而是每换一台机器、每换一个终端工具,所有配置都要从头来一遍。后来我实在受不了这种重复劳动,干脆把自己的一…

2026/10/15 14:36:47

普通人如何抓住DeepSeek红利?提示词驱动的实战方法论

简介:清华大学《普通人如何抓住DeepSeek红利》PDF,由清华大学新闻与传播学院新媒体研究中心等团队发布,面向希望借助DeepSeek提升工作、学习与生活效率的普通用户与开发者。文档系统梳理了DeepSeek的AGI定位、开源推理模型DeepSeek-R1的技术特…

2026/10/15 14:36:47

Spring Boot中使用EasyExcel实现高效大数据量导出

1. 项目背景与核心痛点 在Java企业级应用开发中,Excel导出功能几乎是每个后台管理系统都会遇到的常规需求。但当数据量达到十万甚至百万级时,传统的Apache POI方案就会暴露出严重的内存溢出(OOM)风险。我曾经维护过一个物流管理系…

2026/10/15 14:36:47

带复选框的下拉框实现指南:Qt/WPF/WinForms多选ComboBox开发

简介:本资源是一个面向Windows Forms开发者的可复用自定义控件实现方案,专为解决标准ComboBox不支持多选的痛点而设计。通过继承ComboBox并集成CheckBox控件,开发者可快速获得具备勾选功能的下拉多选组件,适用于权限配置、标签筛选…

2026/10/15 14:31:47

Intel SGX入门:硬件级可信执行环境原理与落地实践

1. 从一个真实困惑说起:为什么需要关注SGX我第一次接触Intel SGX是在做一个跨平台数据协作项目的时候。当时的需求很朴素:多个参与方想联合计算一个统计结果,但谁都不愿意把原始数据交出去。我们试过同态加密,性能损耗大到没法接受…

2026/10/14 11:31:03

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/14 17:41:07

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/15 13:42:16

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/15 0:02:22

防关联浏览器哪个好?三款产品实测差异解析

新手做多店铺,最纠结的就是防关联浏览器选哪个。飞跨、网易、站斧这三款被问得最多,先给结论:要一体化省心、长期便宜,选站斧;要网易招牌和稳,选网易;预算紧做东南亚 Lazada,选飞跨。…

2026/10/15 0:02:22

影刀RPA深度实战:大模型Fine-tuning定制RPA专用Prompt与指令生成

影刀RPA深度实战:大模型Fine-tuning定制RPA专用Prompt与指令生成 一、技术背景 大模型(LLM)在通用场景下表现强大,但当我们将 GPT-4 或 Qwen 直接用于生成影刀RPA操作指令和 XPath 定位时,准确率通常只有 40%-60%。原因…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑