STM32F103 LoRa代码整理实战:基于SX1278的无线通信架构与移植指南 简介正点原子LORA代码整理版面向STM32F103平台从原子哥原始工程中删去屏显、按键等外围代码重写后只保留LORA通信的收发核心适合需要快速搭建点对点无线链路、做数据透传或二次开发的嵌入式开发者。代码剥离了与通信无关的干扰逻辑让学习者能直接聚焦SPI配置、寄存器操作以及数据发送/接收的完整流程也可作为入门LORA的简洁参考模板。压缩包共5个文件包括2个头文件、2个C源文件及1个txt使用说明整体仅11KB结构精简头文件中包含引脚定义和寄存器宏C文件实现初始化、发送与中断接收逻辑txt文档则对工程移植方法做了简要指引。目前已有4164人学习下载。读者拿到后无需在庞大工程中翻找可快速将核心文件嵌入自己的F103项目同时通过阅读精简后的收发例程清晰理解LORA模块在单字节配置、数据校验和无线收发状态切换上的实现要点节省调试时间。 玩无线通信的都知道收发一对模块其实不难真正烦人的是调通之后的代码整理。我之前从网上东拼西凑了一套正点原子LORA代码能用是能用但工程结构乱得离谱。这次拿到这份“正点原子LORA_代码整理_适合F103.zip”整理得确实清爽直接在STM32F103上跑通省了我不少事。这篇就把这套代码的架构思路、关键模块、实操流程和踩坑记录全部拆开聊一聊给准备在F103上做LoRa通信的朋友一个参考。1. 整体设计思路这套代码到底解决了什么问题1.1 为什么选LoRa而不是蓝牙、WiFi或者NRF24L01先说选型。F103作为一颗老牌MCU本身不带无线外设所以外挂无线模块是常规操作。常见的方案有蓝牙、WiFi、NRF24L01和LoRa。蓝牙和WiFi的问题在于传输距离短穿墙能力差在户外或者工业现场基本撑不过50米。NRF24L01便宜但2.4G频段干扰严重而且同样是距离开外就掉包。LoRa用的是Sub-1GHz频段比如常见的410MHz到525MHz再加上扩频调制带来的接收灵敏度优势实际点对点通信距离在空旷环境能轻松到1到3公里。这个距离指标对于农业大棚、停车场、水电表集抄、工业数据采集这些场景来说是一个刚需。正点原子这套代码默认跑的就是SX1278芯片也就是国内最常见的Ra-01模块频率范围覆盖410到525MHz在F103上利用SPI接口控制整体接线非常简单。1.2 代码整理的核心目标这套代码包之所以值得单独写一篇是因为它把“能跑的Demo”升级成了“能当工程用的骨架”。原始的正点原子例程里其实包含了大量演示性的代码比如按键切换频率、OLED显示、串口打印调试信息等等对于刚入门的朋友来说很友好但真要移植到自己的项目里或者做二次开发这些内容反而成了包袱。这份整理版做的第一件事就是剥离。它把LoRa驱动、SPI底层、主逻辑拆开让每个文件只干一件事。第二件事是统一接口收发函数封装得干净利落你不需要去翻寄存器手册也不需要理解SX1278内部那一大堆寄存器地址直接调用API就能完成数据收发。第三件事是适配F103时钟树配置、SPI引脚定义、中断优先级这些硬件相关的内容全部调好下载就能跑。所以这套代码适合三类人一是刚接触LoRa想在F103上快速跑通通信的入门者二是已经跑通Demo想整理成自己工程骨架的进阶用户三是做产品原型验证不想在底层驱动上花太多时间的开发者。2. 核心代码模块拆解每一段代码都在干什么2.1 工程结构与文件职责划分拿到的代码包解压之后目录结构非常清晰。核心文件大致分为三层硬件抽象层、LoRa驱动层和应用层。/User /Core /Src main.c # 主程序入口收发逻辑 lora.c # LoRa驱动核心寄存器操作封装 spi.c # SPI底层配置F103硬件SPI /Inc lora.h # API接口声明参数定义 spi.h硬件抽象层就是spi.c负责F103的SPI外设初始化。LoRa模块和MCU之间的通信全部走SPISX1278内部有128个8位寄存器所有配置和收发数据都是通过读写这些寄存器完成的。spi.c里封装了最底层的SPI读写函数比如uint8_t SPI_ReadByte(uint8_t reg) { uint8_t data; // 拉低片选先写寄存器地址最高位为1表示读再读数据 CS_LOW(); SPI_SendByte(reg | 0x80); data SPI_SendByte(0x00); CS_HIGH(); return data; }这里有个细节值得说一下SX1278的读操作和写操作是通过寄存器地址的最高位区分的读操作要把地址的bit7置1写操作则保持bit7为0。如果这个细节没处理好会发现读回来的数据全是0xFF或者0x00通信彻底失效。2.2 LoRa驱动层的核心逻辑lora.c是整套代码的灵魂。里面封装了SX1278的初始化、频率设置、发射和接收等功能。初始化函数做的事情非常多但总结起来就三件事配置射频参数、配置扩频参数、配置数据包格式。射频参数里最重要的是频率。代码里默认配置的是433MHz这个频率在LoRa应用中属于合法免授权频段。SX1278的频率寄存器由FrFreq[2:0]三个字节组成计算公式是频率 (FrFreq[2] 16 | FrFreq[1] 8 | FrFreq[0]) * 61.03515625 Hz反推回去433MHz对应的寄存器值大概是0x6C4000严谨一点应该是433MHz / 61.03515625Hz 7094272换算成十六进制就是0x6C4000。代码里直接写了这个值但如果你需要改频段比如用470MHz或者490MHz的模块就需要自己重新计算。我用的时候会写一个小脚本算一下省得每次手动换算。扩频参数是LoRa调制方式的核心也是最容易让人懵的地方。三个关键参数参数默认值作用扩频因子SF7值越大灵敏度越高但速率越低信号带宽BW125kHz带宽越大速率越高但灵敏度降低编码率CR4/5纠错能力与传输开销的折中这三个参数直接决定了通信的速率和距离。SX1278的空中速率计算公式是SymbolRate BW / (2^SF) DataRate SF * (CR) * BW / (2^SF)拿默认参数SF7、BW125kHz来算DataRate 7 × 4/5 × 125000 / 128 ≈ 5468bps也就是大约5.5kbps。这个速率在无线通信里不算快但换来的是极高的灵敏度。SX1278在SF12、BW125kHz时灵敏度能做到-137dBm这意味着就算信号已经非常微弱依然能解调出有效数据。发射和接收的流程其实是对称的。发送的时候代码先切换到Standby模式写入数据缓冲区然后设置发送长度最后切换到TX模式等待发送完成。接收的时候则是配置RX模式然后依赖DIO0引脚触发外部中断。void LoRa_SendData(uint8_t *buf, uint8_t len) { LoRa_SetOpMode(LORA_STANDBY); // 先进入待机模式 LoRa_WriteBuffer(0x00, buf, len); // 写入FIFO LoRa_WriteReg(REG_PAYLOAD_LENGTH, len); // 设置发送长度 LoRa_SetOpMode(LORA_TX_MODE); // 切换到发射模式 // 等待DIO0引脚拉高表示发送完成 while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) RESET); }这一段代码看起来简单但实际踩坑的地方不少。比如REG_PAYLOAD_LENGTH这个寄存器只在单包模式设置为0x00而不是连续模式下才有效。如果模块被配置成了连续模式发送长度寄存器根本不会生效可能会出现发送数据混乱的问题。正点原子这套代码默认配置的是单包模式这也是LoRa在短数据透传场景下最常用的工作模式。2.3 应用层收发状态机main.c里的逻辑很直白它用了一个有限状态机来管理收发状态。初始化之后设备默认处于接收状态然后进入主循环。一旦收到数据就在接收中断里把状态切换到“已收到”主循环这边处理完数据之后再重新切回接收状态。这种设计省去了复杂的超时管理和阻塞查询整个程序在等待数据期间是空闲的MCU可以去做别的事情。如果你要在自己的项目里扩展功能比如同时驱动多个传感器或者处理其他外设事件这个状态机框架就是现成的基础。3. 实操流程从接线到双机通信跑通3.1 硬件接线与工程配置我实际用的硬件是正点原子Mini板STM32F103RCT6加两块Ra-01模组。接线方式非常固定就六根线LoRa模块引脚STM32F103引脚VCC3.3VGNDGNDNSS片选PB0SCK时钟PB13MOSIPB15MISOPB14加上DIO0接PB1作为收发完成的中断信号线硬件部分就结束了。这套接线方案用的是STM32的SPI2外设代码里已经配置好了。首次下载代码之前需要确认一下工程里的芯片型号和你的开发板一致。正点原子的F103系列有RCT6、RBT6、ZET6等不同型号Flash大小和引脚数量不同但SPI2这组引脚基本都是复用的。如果你的板子引脚定义和这份代码不一致改起来也不难把spi.c里对应的GPIO口换一下就行。3.2 参数配置与实测数据代码默认配置的收发参数我建议先不要动等通信链路通了之后再去调整。默认配置下我实测的一组数据比较适合入门参考测试项结果传输速率约5.5kbps空旷可视距离1.2km左右不掉包穿墙场景两堵砖墙传输距离300m左右单包最大有效载荷255字节如果把扩频因子从7调到12灵敏度有非常明显的提升在相同穿墙场景下距离能多出将近一倍但代价是速率从5.5kbps直接跌到几百bps。这里就体现出一个工程判断的问题你的业务数据量是多少要求的传输时延是多少得在配置参数之前先想清楚。如果是采集温湿度这种小数据量场景用较低的速率换更远的距离是完全划算的。3.3 上电验证与串口调试在验证收发之前先确认模块有没有正常工作。SX1278在上电后会有一个自检流程如果SPI通信正常读取REG_VERSION寄存器地址是0x42会返回0x12。代码里把这个校验加在了初始化流程中如果读不到0x12程序会卡在初始化阶段。这一步能帮你快速区分问题是出在硬件接线还是软件配置上。验证收发我推荐用一个最简单的方案A板每隔一秒发送一个自增的计数包B板收到之后原样回传A板收到回传之后通过串口打印。这样在串口助手上就能看到完整的收发链路状态。实际上这套代码的主逻辑就是干这个事跑起来之后你只需要打开串口调试助手把后端串口的波特率设为115200就能看到连续的收发日志。4. 常见问题与排查技巧实录4.1 两块板子收发失败最可能的原因是什么这是我被问得最多的一个问题。收发失败的背后90%以上出在频率配置不一致上。LoRa通信要求收发双方的核心参数完全匹配这个匹配不只是频率一样还包括扩频因子、带宽、编码率、同步字SyncWord都要一致。任何一个参数对不上接收方的解调器都无法锁定信号。先查同步字。SX1278有一个8位的同步字寄存器默认值是0x12这个值必须相同它相当于通信双方约定的“口令”。然后是频率寄存器代码里的默认值是433MHz如果有一块模块的晶振精度偏差太大频率误差会大到直接失联。这种情况下你可以通过读REG_RSSI_VALUE寄存器看接收端的噪声底如果RSSI值和理论噪声底差了很远基本都是频率没对准。4.2 发送成功但接收端一直没有中断发送方已经确认数据发出去了但接收端的中断一直没有触发这种问题排查起来最容易走弯路。我当时的排查路径是这样先确认DIO0引脚的中断配置是否正确F103的外部中断线需要开启AFIO时钟并且要配置引脚为浮空输入。然后确认接收端是否正确进入RX模式很多人会在初始化之后忘记调用LoRa_SetOpMode(LORA_RX_MODE)结果接收端一直待在Standby模式自然收不到数据。还有一个容易被忽略的坑是FIFO溢出的问题。SX1278的FIFO缓冲区是256字节如果一帧数据超过了缓冲区大小多余的数据会被丢弃。代码里如果习惯性地把REG_PAYLOAD_LENGTH设置为255甚至256一旦加上头部开销实际写入的字节就会超过FIFO容量导致数据错乱或者地址回卷。实际项目里除非协议需要否则单包长度建议控制在100字节以内既省心又不容易出问题。4.3 传输距离远不如标称值很多朋友实测下来发现距离和厂家宣称的几公里差得远这个首先得看测试环境。空旷可视距离确实能做到几公里但只要你中间有遮挡物体哪怕只是一排树木信号衰减都会非常明显。其次就是天线因素我见过不少新手把模块的天线座空着或者接了根长度不对的天线去测距离结果自然是惨不忍睹。还有一个经验是不要为了追求速率把BW调得太大。SX1278虽然支持125kHz、250kHz、500kHz三档带宽但带宽越大接收灵敏度越低。在125kHz下实测灵敏度比500kHz高大约6dB这个差距换算到距离上是非常可观的。所以距离优先的场景老老实实用125kHz带宽扩频因子能调多大调多大。4.4 模块发烫甚至烧毁大概率是供电问题Ra-01这种模块在发射瞬间峰值电流可以达到120mA左右如果你用开发板上的3.3V稳压器去供电稳压器的输出能力跟不上电压会被拉垮轻则模块复位重则稳压器过热保护。我建议LoRa模块单独供电或者用电流余量充足的LDO。尤其是用电池供电的场景一定要在模块电源入口加一个大一点的电容比如100uF电解电容并联一个0.1uF陶瓷电容用来吸收发射瞬间的电流尖峰。4.5 常见问题速查表问题现象排查方向解决方案初始化卡死读取版本号失败SPI接线错误或模块供电不足检查NSS/SCK/MOSI/MISO接线确认模块电压收发失败频率、SF、BW、同步字不一致逐项核对参数尤其是同步字发送完成但接收无中断接收端未进入RX模式或DIO0中断未配置检查初始化流程和外部中断配置距离严重缩水天线问题或带宽设置过大换匹配频段天线降低BW到125kHz模块发热或重启发射峰值电流导致电压跌落单独供电加电容滤波数据乱码波特率不对或单包过长核对串口波特率缩短单包长度5. 代码二次开发如何在这个骨架上增加自己的功能这套代码整理包最大的价值其实在于扩展。拿LoRa做数据采集项目时通常需要把MCU采集到的数据打包成协议帧再通过LoRa发送出去。我的经验是直接利用LoRa_SendData这个API在它外面再包一层协议帧解析的代码比如自定义一个简单的帧格式包含帧头、数据长度、数据域、CRC校验。typedef struct { uint8_t header; // 帧头 0xAA uint8_t len; // 数据长度 uint8_t data[128]; // 数据域 uint8_t crc; // CRC校验 } LoraFrame_t;发送端填充这个结构体然后调用LoRa_SendData((uint8_t *)frame, frame.len 3)就能完成一帧数据的发送。接收端收到原始数据后再按相同格式解析完成协议的封包和解包。这个模式虽然简单但足够覆盖大部分传感器数据回传的需求。如果要做多节点组网比如多个终端上传数据到一个网关那就需要做轻量级的时分复用。让每个终端使用不同的延时窗口发送数据避免数据碰撞。LoRa本身有CAD信道活动检测功能可以检测当前信道是否有信号在传输利用这个功能做一个简单的LBT先听后发机制能有效降低多节点同时发送时的冲突概率。这套代码里并没有封装CAD相关函数但寄存器手册里写得非常清楚加上也很简单值得动手试一试。LoRa通信最大的特点是“鱼与熊掌不可兼得”距离、速率、功耗三者之间只能选其二。我在实际项目里习惯先把业务数据量算出来反推需要的速率上限再在这个上限之下尽量调高扩频因子换取传输余量。每次调参数的时候我都会把收发两端的RSSI打出来看一下这比盲调靠谱得多。最后再分享一个小技巧。LoRa模块调试过程中如果手头只有一块F103和一块模块可以用电脑串口配合USB转TTL模块把LoRa模块的收发引到电脑上然后用串口助手去模拟另一端的设备。一块板子也能把整个收发链路验证完省去只有一套硬件时调不了通信的尴尬。本文还有配套的精品资源点击获取