
简介面向STM32F103开发者的LoRa通信精简代码包从正点原子原工程中剥离屏幕显示等冗余模块重写后仅保留无线收发核心逻辑适合入门LoRa点对点通信、快速移植到自有项目的嵌入式爱好者与工程师。压缩包体积仅约11KB共包含5个文件以C源文件与头文件为主2个.c、2个.h另附1个使用说明txt结构清晰直接配合正点原子F103开发板或最小系统即可对照验证。代码中整合了系统时钟、基础外设初始化及LoRa模块收发流程去除了无关界面代码便于专注于无线链路调试、发送接收模式切换与参数配置等关键环节使用说明txt对文件构成和调用关系做了简要交代可减少上手时的摸索成本。目前该资源已被4164人学习下载适合需要一份精简、可直接参考的LoRa驱动模板的开发者也适合用在遥控、传感数据回传、多点组网前期验证等场景。 拿到这个“正点原子LORA_代码整理_适合F103.zip”压缩包的时候我第一反应是松了口气。之前被正点原子原版LoRa例程里面那一堆分散的文件和“看起来能用但不知道从哪下手”的代码折腾过几次这次整理过的版本明显清爽多了。LoRa这玩意在嵌入式无线通信里属于那种“参数看着简单、用起来全是细节”的东西尤其配上STM32F103这种老而弥坚的MCU很多刚从串口通信入门的朋友会卡在“为什么我的两块板子就是收不到数据”这一步。这篇博文就把这套整理代码的来龙去脉、硬件接线、模式切换逻辑以及实际调试中容易踩的坑一次说清楚适合手里有正点原子LoRa模块和F103开发板、正准备把无线收发跑通的玩家。1. 这份代码包到底替你省了哪些事先说LoRa本身。很多人一听LoRa就以为是某种“高级蓝牙”其实它属于无线电扩频通信技术特点是低速率、远距离、抗干扰强特别适合传感器数据上报、遥控开关、环境监测这类不追求高带宽但要求穿墙远的场景。正点原子的ATK-LORA-01模块用的就是SX1278方案模块和单片机之间走的是串口透传协议——说人话就是你往模块的串口丢一段数据它对端模块的串口就能原样吐出来中间射频部分全部封装好了。既然模块自己搞定了无线收发那F103这边要写的代码本质上就只有三块串口驱动、模式控制引脚的操作、AUX状态引脚的判断。原版官方例程之所以让人看得头皮发麻是因为它把所有功能都揉在一起uart中断、定时器、按键扫描、OLED显示全堆在一个工程里你想单独摘出“无线透传”反而不知道要删哪些。这份整理包做的第一件事就是把代码精简到“能跑透传的最小集合”保留了必要的底层初始化删掉了花里胡哨的演示逻辑同时对关键函数加了注释。对于只想尽快跑通收发、再往自己项目里移植的人来说这个方向是对的。另外整理包针对F103做了时钟和串口适配。你会发现代码里用的是标准库而非HAL库这倒不是正点原子偷懒而是F103的标准库生态太成熟了网上能找到大量现成参考初学者对照寄存器手册查起来也更直观。代码包默认用的是USART2还是USART3我记不太清了反正不同版本打包习惯不一样拿到手第一件事就是打开usart.c确认你板子上实际接的是哪个串口。这个习惯很重要因为很多人移植失败就是没改串口号。2. 接线和模块引脚逻辑这一步错了后面全白搭ATK-LORA-01模块的引脚不算多但每一个都有讲究。除了VCC和GND剩下几个关键引脚分别是M0、M1、RXD、TXD、AUX。M0和M1是模式配置引脚它们的高低电平组合直接决定模块工作在透传模式、WOR模式还是配置模式RXD和TXD就是串口收发AUX是状态指示引脚模块在空闲时输出高电平忙的时候拉低这个引脚在代码里必须处理否则你根本不知道模块什么时候把配置命令处理完。和F103开发板连接的时候正点原子通常建议用USART2或USART3来和模块通信因为USART1经常被调试串口占用。我自己测试用的是一块F103ZET6板子接线方式如下模块引脚F103引脚说明VCC3.3V或5V推荐3.3V有些模块5V也能用但先看手册GNDGND必须共地否则串口电平参考点不一致M0任意GPIO如PB12模式控制由单片机输出高低电平M1任意GPIO如PB13模式控制和M0配合使用RXD单片机TX引脚注意是交叉连接不是同名相连TXD单片机RX引脚交叉连接AUX任意GPIO如PB14状态输入配置为输入模式接线这里最容易出的两个问题一是串口交叉连反了二是VCC供电不稳。LoRa模块在发射瞬间电流峰值能到一百多毫安甚至更高如果你用开发板自带的AMS1117 3.3V稳压器同时板子上还挂着OLED、传感器很容易出现电压跌落导致模块死机或丢包。我实测下来最稳的做法是给模块单独用一块LDO供电或者至少保证电源线尽可能短、线径别太细。杜邦线太长也会带来干扰天线附近尤其不要飞乱七八糟的线。3. 核心代码逻辑拆解串口转发、模式切换、AUX判断整理包的代码核心并不复杂但有几个关键点值得单独拿出来讲。首先是串口的开启。以下代码是典型的标准库初始化方式注意这里要使能串口接收中断因为LoRa模块收到数据后是通过TXD引脚送给单片机的单片机必须能在中断里及时把数据取走否则会被下一个字节覆盖。void USART2_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); // TX - PA2 推挽复用输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // RX - PA3 浮空输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); USART_InitStructure.USART_BaudRate 115200; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART2, USART_InitStructure); // 开启接收中断 USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); NVIC_InitStructure.NVIC_IRQChannel USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); USART_Cmd(USART2, ENABLE); }其次是M0/M1的模式切换。正点原子这个模块的模式定义比较直接M00、M10是透传模式模块上电默认就是这里也是日常收发最常用的模式M01、M10是WOR模式也就是低功耗唤醒接收M00、M11是配置模式所有AT指令都在这个模式下发送。切换模式本身很简单拉高拉低GPIO就行但里面的门道在于切换时机——必须在AUX引脚为高电平模块空闲的时候操作否则上一条指令可能还没处理完就被下一组配置覆盖了。void LORA_Mode_Set(uint8_t mode) { while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) RESET); // 等待AUX拉高确保空闲 switch (mode) { case MODE_TRANSPARENT: // 模式0透传 GPIO_ResetBits(GPIOB, GPIO_Pin_12); GPIO_ResetBits(GPIOB, GPIO_Pin_13); break; case MODE_WOR: // 模式1WOR唤醒 GPIO_SetBits(GPIOB, GPIO_Pin_12); GPIO_ResetBits(GPIOB, GPIO_Pin_13); break; case MODE_CONFIG: // 模式2配置 GPIO_ResetBits(GPIOB, GPIO_Pin_12); GPIO_SetBits(GPIOB, GPIO_Pin_13); break; default: break; } }然后是发送函数。透传模式下的发送其实就是把单片机要发的内容原封不动写入串口寄存器LoRa模块收到串口数据后自动打包射频发出去。整理包里可能会看到类似的代码void LORA_SendData(uint8_t *data, uint16_t len) { while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) RESET); // 模块忙则不发送 for (uint16_t i 0; i len; i) { while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) RESET); USART_SendData(USART2, data[i]); } }这个“发之前检查AUX”的习惯建议保留。虽然单片机和模块之间是串口直连没有硬件流控但模块在射频发射的过程中AUX会被拉低如果你此时往串口狂塞数据模块内部的缓冲区可能溢出表现出来就是丢帧。加一个电平判断代码整体速度影响很小但稳定性明显提升。接收部分则是串口中断这是LoRa模块往单片机方向传数据的入口。收到一个字节就存进缓冲区等收到一帧完整数据后置一个标志位主循环里轮询这个标志位进行业务处理。整理包用的多半是这种常见的“接收缓冲标志位”框架好处是逻辑简单不占用CPU坏处是自己需要定义帧格式和分包策略这部分在下一节详细说。void USART2_IRQHandler(void) { if (USART_GetITStatus(USART2, USART_IT_RXNE) ! RESET) { uint8_t ch USART_ReceiveData(USART2); if (rx_index RX_BUFFER_SIZE) { rx_buffer[rx_index] ch; } USART_ClearITPendingBit(USART2, USART_IT_RXNE); } }为什么很多人在这一步收到的是乱码十有八九是发送端和接收端波特率不一致。LoRa模块内部无线传输速率和串口波特率是两个概念串口波特率决定单片机和模块之间交换数据的速度两边必须一致而无线空中速率是模块射频参数里单独配置的空中速率低了传输距离远但单包传输时间长。整理包默认115200串口波特率没问题关键是两块板子的串口波特率都要改一致。4. 实测中的坑丢包、乱码、收不到数据一条龙排查我在调这套代码的时候几乎把新手能踩的坑都踩了一遍这里直接说排查路径比单纯给结论有用得多。现象一两块板子离得很近但接收端一个字节都收不到。先别怀疑代码用USB转TTL把LoRa模块单独接电脑用串口助手向模块发数据看模块能不能通过无线发给另一块板子。这能快速区分是单片机侧的问题还是模块配置的问题。我遇到过的情况是模块被之前的人改过信道地址和另一块不在同一个无线信道上收不到纯属正常。恢复出厂设置可以解决大部分这类问题。现象二偶尔能收到但数据有乱码或者中间缺字节。优先检查串口中断是否有更高优先级的中断在频繁抢占——比如F103的定时器中断、外部中断。串口接收是逐字节的如果中断响应不及时来了第二字节但第一字节还没被取走RXNE标志会被覆盖表现出来就是丢字节。整理包虽然已经把代码精简了但如果你往工程里加了其他中断处理一定要把串口中断优先级调高或者至少在中断里只做最快的数据读取操作不要放耗时的函数。现象三发送端明明调用了LORA_SendData但模块就是不发。这个坑多半出在AUX电平判断上。模块上电后内部要跑初始化此时AUX是低电平如果代码在main函数里立刻调用发送函数while (AUXRESET)会死等。如果AUX一直不拉高大概率是模块根本没进工作状态或者硬件接触不良。用万用表量AUX引脚电压是最快的定位方法。另外讲一个很容易被忽略的点模块的供电电流。LoRa模块标称工作电流可能看着不高但它在射频发射时瞬态电流很大如果你的F103开发板是USB供电而且USB线又细又长线阻会造成电压跌落模块在发射瞬间复位表现出来就是“发送端按一下按键接收端收到一组乱码后彻底没反应”。这个问题的隐蔽性极高因为静态测量电压一切正常。我后来用一个470uF电解电容并在模块的VCC和GND之间问题立刻消失。这个电容太关键了建议直接在开发板电源输出口那里并联一个。5. 把整理好的代码改成自己的透传项目代码跑通之后就该琢磨怎么改成自己的东西了。一个最常见的改造方向是把串口接收到的数据从“全部存进缓冲区”改成“按协议帧解析”。LoRa透传本身不关心你发的是什么所以你需要自己定义帧头、帧尾、长度、校验。我建议的最小帧格式是帧头0xAA 0x55、数据长度1字节、数据N字节、累加和校验1字节。接收端在中断里不断往环形缓冲区写主循环里查找帧头并校验这样做可靠性远高于固定长度的裸数据。另一个改造重点是低功耗。如果你的项目用电池供电LoRa模块的WOR模式就必须用起来了。WOR模式下模块会周期性监听无线信道牺牲一点响应延迟换取大部分时间休眠这个功耗能低到微安级别。整理包里一般会保留WOR模式切换的函数但要真正跑起来还需要注意两块模块必须设置完全相同的空中速率和唤醒周期否则发送端发出的唤醒码对端根本听不到。我把两块板子的WO R周期都配成500ms实际测下来发送端发数据后最迟半秒到1秒内对端能收到对于一个定时上报的应用场景完全够用。还有一点是关于天线。很多朋友测试距离的时候发现只有几十米就收不到第一反应是LoRa也不过如此。先检查天线有没有接好模块上的IPEX座子有没有扣紧SMA天线有没有拧到位。另外不要把模块贴着金属表面放天线周围要保持一定净空。在开阔可视环境下SX1278加标准天线做到几百米甚至上公里都正常室内隔几堵墙也有几十米如果远低于这个数先怀疑天线和供电而不是怀疑LoRa技术本身。按我个人经验这套整理代码最让我觉得省心的点是把原来散落在多个文件里的LoRa相关操作收敛成了一个lora.c改配置、改串口、查状态都在这一个文件里完成。我后来把这块代码从一个F103工程移植到另一块GD32F103兼容芯片上基本就是改了一下时钟树和串口引脚两小时搞定。对还在学习阶段的朋友建议拿到代码包后不要急着复制粘贴先把发送、接收、模式切换三块函数的调用关系画出来——这个动作看着耽误时间却是你脱离“调通就跑”走向“真正会改”的关键一步。很多时候我和别人说LoRa难难的不是无线本身而是你不知道数据从一个串口到另一个串口之间中间到底发生了什么。本文还有配套的精品资源点击获取