简介:本资源是一个基于STM32F103C8T6微控制器与LoRa无线模块的嵌入式物联网通信完整项目,面向嵌入式初学者、STM32开发者及IoT实践者,解决远距离(理论达3km)、低功耗传感器数据采集与无线回传的核心问题,适用于环境监测、智能农业等典型场景。压缩包共189个文件,以34个.h头文件和32个.c源码为主,涵盖STM32标准外设库(如stm32f10x_usart、stm32f10x_gpio等)驱动、LoRa模块SPI通信接口、ADC传感器采集及系统时序控制;辅以.o目标文件、.axf/.hex可执行镜像、.uvprojx工程配置及调试相关文件,结构完整,可直接Keil MDK编译运行。资源大小1.96MB,轻量易上手。目前已有4197人学习下载,提供从硬件连接、固件开发到实测验证的全链路参考,含清晰模块划分、中断服务实现与基础电源管理逻辑,是掌握LoRa+STM32协同开发的高实用性入门范例。
1. LORA完成.rar:一个能直接烧进STM32F103C8T6跑通的LoRa点对点通信最小闭环
你手上有块蓝丸(STM32F103C8T6)、一对SX1278模块、几根杜邦线,但连上串口调试助手只看到乱码或“AT ERROR”,查了三天资料发现不是驱动没初始化,就是SPI时序没对齐,更别说收发同步和低功耗唤醒——别硬扛了,这个「LORA完成.rar」就是为这种场景打包的:它不是教学Demo,不是半成品框架,而是一个从硬件接线定义、CubeMX配置导出、LoRa寄存器级初始化、到应用层收发状态机全写死的可运行工程。我拿它在某高校嵌入式课设里带过12组学生,平均25分钟完成首次双向通信;它不依赖HAL_Delay阻塞延时,用SysTick做毫秒调度;不靠AT指令“假装智能”,所有LoRa参数(中心频点、扩频因子、编码率、前导码长度)都在lora_config.h里明确定义、注释完整。适合刚焊完板子想验证物理层连通性,也适合需要快速集成LoRa到现有STM32项目里的中级开发者——你不需要懂LoRa PHY层细节,但得愿意看懂lora_transmit()里那几行关键寄存器写操作。
2. 工程结构与核心模块拆解:从CubeMX生成到LoRa寄存器映射
2.1 CubeMX配置要点:SPI+GPIO+SysTick三线并行
这个工程基于STM32CubeMX 6.12生成,MCU型号锁定为STM32F103C8T6(64KB Flash / 20KB RAM),关键外设配置如下:
- SPI1:主模式,波特率预分频器=4(即APB2=72MHz → SPI CLK=18MHz),CPOL=0, CPHA=0,NSS软件管理(
GPIO_PIN_SET拉高禁用,GPIO_PIN_RESET拉低使能)。注意:SX1278的MISO引脚必须接在SPI1_MISO(PA6),否则读寄存器永远返回0xFF。 - GPIO:PB0→DIO0(中断输入,上升沿触发),PB1→RESET(低电平复位,上电后需拉高保持),PA8→NSS(片选,软件控制),PB10→LED(收发状态指示)。
- SysTick:配置为1ms中断,用于非阻塞延时(如
lora_wait_tx_done()中轮询DIO0前的等待窗口)。
提示:CubeMX里务必关闭
Generate peripheral initialization as a pair of 'xxx_GPIO_Init()' and 'xxx_Msp_init()' functions选项,否则生成的MX_SPI1_Init()会覆盖手动写的SPI时序微调代码。
生成代码后,工程目录结构精简到仅5个核心文件夹:
Core/ ├── Inc/ │ ├── main.h │ ├── lora_driver.h ← LoRa底层驱动头文件 │ ├── lora_config.h ← 频点/扩频因子/功率等可配参数 │ └── sx1278_reg.h ← SX1278全部寄存器地址宏定义(0x00~0x4F) ├── Src/ │ ├── main.c ← 主循环:按键触发发送,DIO0中断触发接收 │ ├── lora_driver.c ← 核心:init / send / recv / set_freq 等函数 │ ├── sx1278_hal.c ← 底层SPI读写封装(含NSS控制) │ └── delay_systick.c ← SysTick非阻塞延时实现 Drivers/ ├── CMSIS/ └── STM32F1xx_HAL_Driver/2.2 SX1278寄存器级初始化:为什么必须跳过LoRa模式自动校准
lora_driver.c中的SX1278_Init()函数是整个通信链路的起点,它不调用任何HAL库的“高级抽象”,而是逐字节写寄存器。关键步骤如下:
// 步骤1:软复位SX1278(写0x01寄存器为0x00) SX1278_WriteReg(REG_OP_MODE, RF_OPMODE_SLEEP); // 步骤2:配置LoRa模式(非FSK!必须显式设置) SX1278_WriteReg(REG_OP_MODE, RF_OPMODE_SLEEP | RF_OPMODE_LONGRANGEMODE_ON); // 步骤3:关闭自动频率捷变(避免干扰已设频点) SX1278_WriteReg(REG_FRF_MSB, (uint8_t)(FRF_433_0MHZ >> 16)); SX1278_WriteReg(REG_FRF_MID, (uint8_t)(FRF_433_0MHZ >> 8)); SX1278_WriteReg(REG_FRF_LSB, (uint8_t)FRF_433_0MHZ); // 步骤4:设置LoRa参数(SF7, BW125kHz, CR4/5) SX1278_WriteReg(REG_MODEM_CONFIG_1, (RF_LORA_MODEM_CONFIG1_BW_125 | RF_LORA_MODEM_CONFIG1_CODINGRATE_4_5 | RF_LORA_MODEM_CONFIG1_IMPLICIT_HEADER_OFF)); SX1278_WriteReg(REG_MODEM_CONFIG_2, (RF_LORA_MODEM_CONFIG2_SF_7 | RF_LORA_MODEM_CONFIG2_RX_PAYLOAD_CRC_ON | RF_LORA_MODEM_CONFIG2_SYMB_TIMEOUT_MSB)); // 步骤5:配置DIO0中断映射到RxDone/TxDone SX1278_WriteReg(REG_DIO_MAPPING_1, (RF_DIOMAPPING1_DIO0_00 | // DIO0 = RxDone RF_DIOMAPPING1_DIO1_00 | // DIO1 = Not used RF_DIOMAPPING1_DIO2_00)); // DIO2 = Not used这段代码的玄学在于:不能省略REG_OP_MODE两次写入。第一次写SLEEP是为了确保芯片处于已知状态;第二次写LONGRANGEMODE_ON才是进入LoRa模式。如果只写一次,SX1278可能卡在FSK模式,导致后续所有LoRa寄存器配置失效——这是新手翻车最高发的点。
2.3 收发状态机设计:用DIO0中断驱动而非轮询
传统做法是在while(1)里不断读DIO0电平判断是否收到数据,但这样CPU空转耗电且无法响应其他任务。本工程采用中断驱动状态机:
// 在stm32f1xx_it.c中 void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); // PB0对应EXTI0 } // 在lora_driver.c中注册回调 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_0) { if (lora_state == LORA_STATE_RX) { // 进入接收完成处理流程 lora_rx_done_handler(); } else if (lora_state == LORA_STATE_TX) { // 进入发送完成处理流程 lora_tx_done_handler(); } } }lora_rx_done_handler()内部执行:
- 读取
REG_IRQ_FLAGS清除中断标志; - 读取
REG_RX_NB_BYTES获取有效载荷长度; - 调用
SX1278_ReadBuffer()从FIFO读取数据; - 切换LED状态并打印接收内容到串口。
这种设计让主循环彻底解放,可同时处理传感器采样、LCD刷新等任务,实测在100ms周期内仍能稳定收发。
3. 编译与烧录全流程:Keil MDK-ARM v5.37下零配置启动
3.1 Keil工程配置关键项
打开LORA_COMPLETE.uvprojx后,需确认以下三项(缺一不可):
Target页:
Xtal(MHz)设为8(外部晶振频率,匹配原理图);Use MicroLIB勾选(减小printf体积,避免半主机);Code Generation→Optimization Level设为Level 3(启用内联,减少函数调用开销)。
Output页:
Create HEX File勾选(方便用ST-Link Utility烧录);Browse Information勾选(便于调试时查看变量地址)。
C/C++页:
Define栏填入:USE_STDPERIPH_DRIVER, STM32F10X_MD;Include Paths添加:.\Core\Inc;.\Drivers\CMSIS\Device\ST\STM32F1xx\Include;.\Drivers\CMSIS\Include。
注意:若编译报错
'__weak' undefined,说明core_cm3.h未被正确包含,检查Include Paths是否漏掉.\Drivers\CMSIS\Include。
3.2 硬件接线对照表(按本工程定义)
| STM32F103C8T6 | SX1278模块 | 信号方向 | 备注 |
|---|---|---|---|
| PA4 (SPI1_NSS) | NSS | MCU→Module | 必须软件控制,勿接硬件NSS |
| PA5 (SPI1_SCK) | SCK | MCU→Module | 时钟线,走线尽量短 |
| PA6 (SPI1_MISO) | MISO | Module→MCU | 输入线,接错则读寄存器全0xFF |
| PA7 (SPI1_MOSI) | MOSI | MCU→Module | 输出线 |
| PB0 | DIO0 | Module→MCU | 中断输入,必须接EXTI0 |
| PB1 | RESET | MCU→Module | 上电后需拉高,否则持续复位 |
| 3.3V | VCC | MCU→Module | 模块供电,勿用5V! |
| GND | GND | 共地 | 所有GND必须单点连接 |
实测血泪经验:若接收端始终无响应,先用万用表量PB0电压——正常待机应为3.3V(DIO0悬空高电平),收到数据瞬间跌至0V并维持约10μs。若一直是0V,说明SX1278未初始化成功或DIO0焊反。
3.3 串口调试配置与首包验证
使用CH340/CP2102等USB转TTL模块,接线:
- STM32 PA9 → USB-TTL TX
- STM32 PA10 → USB-TTL RX
- 共地
串口工具(如XCOM、SSCOM)设置:
- 波特率:115200
- 数据位:8
- 停止位:1
- 校验位:None
- 流控:None
上电后串口输出首行:
[LoRa] Init OK, Freq=433.0MHz, SF=7, BW=125kHz此时按下开发板KEY_UP(默认PA0),发送端将发出固定字符串:
TX: [0x01][0x02][0x03][0x04] Hello LoRa! #001接收端收到后回显:
RX: [0x01][0x02][0x03][0x04] Hello LoRa! #001 (RSSI=-42, SNR=8.2)其中RSSI(接收信号强度)和SNR(信噪比)值来自REG_PKT_RSSI_VALUE和REG_PKT_SNR_VALUE寄存器,是判断链路质量的核心依据。
4. 常见问题排查:5条真实踩坑记录与速查方案
4.1 现象:串口打印[LoRa] Init FAIL,或无任何输出
原因:SPI通信失败,最常见为PA6(MISO)接错或虚焊;其次为SX1278未上电(VCC量不到3.3V);第三为RESET引脚被意外拉低(用万用表测PB1对地电压应为3.3V)。
解决:
- 断电,用万用表二极管档测PA6与SX1278 MISO焊点是否导通;
- 上电,测SX1278模块VCC引脚电压;
- 测PB1电压,若为0V,检查RESET电路是否被其他器件短路。
4.2 现象:发送端显示TX Done,但接收端无任何响应,DIO0无跳变
原因:频点不一致(发送端设433MHz,接收端误设470MHz);或扩频因子SF不匹配(发送SF7,接收SF12);或两模块天线未接/损坏。
解决:
- 查
lora_config.h中FRF_433_0MHZ宏定义,两端必须完全一致; - 查
REG_MODEM_CONFIG_2写入值,SF位必须相同; - 用频谱仪或另一台LoRa设备扫频确认发射频点。
4.3 现象:接收端偶尔收到数据,但RSSI波动极大(-30dBm到-90dBm跳变)
原因:天线阻抗不匹配,或PCB走线过长未做50Ω阻抗控制;SX1278的ANT引脚未接π型匹配网络。
解决:
- 换用标准SMA接口+吸盘天线测试;
- 若用板载PCB天线,检查
ANT引脚到天线焊盘间是否有0Ω电阻或磁珠,移除后直连; - 在
lora_config.h中降低输出功率:#define TX_OUTPUT_POWER 2(单位dBm,原值17)。
4.4 现象:连续发送10包后,第11包开始丢包率陡增
原因:SX1278 FIFO未清空,上一包接收中断未执行完,新包覆盖旧数据。本工程虽用中断,但若主循环中有长延时(如HAL_Delay(1000)),会导致中断服务函数堆积。
解决:
- 删除所有
HAL_Delay()调用,改用delay_ms(1000)(基于SysTick); - 在
lora_rx_done_handler()末尾添加__DSB(); __ISB();内存屏障指令,防止编译器优化导致FIFO读取顺序错乱。
4.5 现象:Keil编译通过,但烧录后程序不运行,串口无输出
原因:Flash起始地址错误。本工程.sct分散加载文件指定LR_IROM1 0x08000000 0x00010000,若用ST-Link Utility烧录时选错地址(如从0x08002000开始),则复位向量表丢失。
解决:
- 在Keil中右键工程 →
Options for Target→Utilities→Settings→Flash Download,确认Reset and Run已勾选; - 若用ST-Link Utility,烧录前点击
Target→Setting→Connect Mode选Under Reset,再点击Connect。
5. 二次开发改造指南:从点对点到多节点组网的3个关键跃迁
5.1 添加设备地址过滤:实现1拖N星型拓扑
当前工程是纯广播模式,所有节点收到即处理。要升级为星型网络(1个网关+多个终端),需在应用层加入地址字段。修改步骤:
- 扩展数据帧结构:在
lora_driver.h中定义新结构体
typedef struct { uint8_t dst_addr; // 目标地址(0xFF为广播) uint8_t src_addr; // 源地址(本机地址,由lora_config.h定义) uint8_t cmd_id; // 命令类型(0x01=心跳,0x02=传感器数据) uint8_t payload[32]; // 实际数据 } lora_frame_t;- 修改发送函数:在
lora_transmit()中填充地址字段
lora_frame_t frame; frame.dst_addr = DEST_ADDR; // 如0x01表示发给网关 frame.src_addr = LOCAL_ADDR; // 如0x0A表示本机地址 frame.cmd_id = CMD_HEARTBEAT; memcpy(frame.payload, "PING", 4); SX1278_SendFrame((uint8_t*)&frame, sizeof(frame));- 增强接收解析:在
lora_rx_done_handler()中增加地址校验
lora_frame_t *rx_frame = (lora_frame_t*)rx_buffer; if (rx_frame->dst_addr != LOCAL_ADDR && rx_frame->dst_addr != 0xFF) { return; // 地址不匹配,丢弃 } // 后续处理...提示:地址字段必须放在帧头,因为SX1278的隐式头模式(Implicit Header)不支持动态长度,所以帧结构必须固定。
5.2 集成低功耗休眠:用STOP模式实现μA级待机电流
STM32F103C8T6的STOP模式可将电流降至2.5μA,但需注意:
- 退出STOP需用EXTI中断(如DIO0);
- 退出后HSI需重新校准,故
SystemCoreClockUpdate()必须调用; - 所有GPIO需配置为模拟输入或上拉/下拉以降低漏电。
改造代码片段(在main.c中):
void enter_stop_mode(void) { __HAL_RCC_PWR_CLK_ENABLE(); // 使能PWR时钟 HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1); // PB0作为唤醒源 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 退出后自动执行,需重置系统时钟 SystemCoreClockUpdate(); }然后在lora_rx_done_handler()末尾调用enter_stop_mode(),即可实现“收完即睡”。
5.3 对接传感器数据:ADC+LoRa联合采样时序控制
以采集NTC温度传感器为例,需保证ADC采样与LoRa发送不冲突:
- ADC使用DMA+定时器触发,采样周期1s;
- LoRa发送安排在ADC转换完成中断中,避免主循环阻塞;
- 关键约束:SX1278在TX模式下禁止访问SPI总线,故ADC DMA完成中断中不能调用
SX1278_SendFrame()。
解决方案:用标志位+状态机解耦
volatile uint8_t adc_ready_flag = 0; void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { adc_value = HAL_ADC_GetValue(&hadc1); adc_ready_flag = 1; } // 在主循环中轮询 if (adc_ready_flag && lora_state == LORA_STATE_IDLE) { prepare_lora_payload(adc_value); lora_transmit(); adc_ready_flag = 0; }这样ADC和LoRa完全异步,实测1s采样周期下,温度数据上传延迟稳定在120ms以内。
从那以后我每次接到LoRa硬件需求,第一件事就是解压这个「LORA完成.rar」,把lora_config.h里的频点改成客户指定值,再按接线表焊线,20分钟内必通第一包。它不炫技,不堆砌RTOS,就用最原始的寄存器操作守住物理层底线——这恰恰是嵌入式开发最该有的样子。希望帮到你。
本文还有配套的精品资源,点击获取