news 2026/9/9 13:46:05

STM32F103 LoRa代码整理实战:基于SX1278的无线通信架构与移植指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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之间的通信全部走SPI,SX1278内部有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)

拿默认参数SF=7、BW=125kHz来算,DataRate = 7 × 4/5 × 125000 / 128 ≈ 5468bps,也就是大约5.5kbps。这个速率在无线通信里不算快,但换来的是极高的灵敏度。SX1278在SF=12、BW=125kHz时灵敏度能做到-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.3V
GNDGND
NSS(片选)PB0
SCK(时钟)PB13
MOSIPB15
MISOPB14

加上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模块的收发引到电脑上,然后用串口助手去模拟另一端的设备。一块板子也能把整个收发链路验证完,省去只有一套硬件时调不了通信的尴尬。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 13:45:44

GPU利用率99%却训练慢?用PyTorch Profiler与Kineto Trace定位性能瓶颈

我刚开始做GPU性能优化那阵子&#xff0c;就遇到过一件让我特别困惑的事&#xff1a;一块训练卡&#xff0c;nvidia-smi里显示的利用率已经到 99%&#xff0c;任谁看都是“满负荷运转”&#xff0c;可训练一个 step 的实际耗时就是压不下去。后来我借着 PyTorch Profiler 背后的…

作者头像 李华
网站建设 2026/9/9 13:45:33

RESTful API设计最佳实践:Python后端实战指南

作为一个常年写Python后端的人&#xff0c;我见过太多“能跑就行”的接口了&#xff1a;有的是随手用Flask写几个路由&#xff0c;URL命名随心所欲&#xff0c;动词名词混在一起用&#xff1b;有的是所有接口统一返回{"code": 0, "msg": "success&quo…

作者头像 李华
网站建设 2026/9/9 13:44:47

把 KernelSU 刷进你的手机:从自检到救砖的全流程

把 KernelSU 刷进你的手机&#xff1a;从自检到救砖的全流程 【免费下载链接】KernelSU A Kernel based root solution for Android 项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU KernelSU 是一个基于内核的 Android root 方案&#xff0c;它修改的是内核…

作者头像 李华
网站建设 2026/9/9 13:41:09

Java abstract关键字深度解析:抽象类、多态与模板方法实战

1. 抽象解决的不是语法问题&#xff0c;而是代码组织的"信任问题"我面试过不少Java候选人&#xff0c;十有八九都能背出"抽象类不能被实例化"这句标准答案。但真问到"abstract到底解决了什么问题"时&#xff0c;往往就卡住了。这种状态其实很危险…

作者头像 李华
网站建设 2026/9/9 13:41:01

OA办公系统源码测试实录:从环境部署到可用性评估

简介&#xff1a;一套基于.NET框架的OA办公系统源码&#xff0c;面向需要部署办公自动化平台的企业技术团队&#xff0c;也适合有.NET基础并希望二次开发的工程师。系统覆盖签发管理、公文起草、收发文办理、文件传阅、公文中转、公文校核、档案管理、日程安排、网络会议、通讯…

作者头像 李华
网站建设 2026/9/9 13:40:24

ESP-IDF v6.0 发版指南:MbedTLS v4 加密栈迁移与芯片支持矩阵

ESP-IDF v6.0 发版指南&#xff1a;MbedTLS v4 加密栈迁移与芯片支持矩阵 【免费下载链接】esp-idf Espressif IoT Development Framework. Official development framework for Espressif SoCs. 项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf ESP-IDF v6.…

作者头像 李华