简介这份资源面向嵌入式开发初学者与物联网无线通信方向的工程师提供基于STM32F103单片机通过SPI接口驱动SX1278 LoRa模块的完整软件设计源代码帮助读者快速搭建远距离低功耗无线通信原型解决从硬件初始化到数据收发全流程的实现问题。压缩包共442个文件约14.89MB以c与h源码文件为核心配合o、d、crf等编译中间文件以及uvprojx、uvoptx工程配置、hex烧录文件、map映射与lst列表等覆盖STM32标准外设库、SPI驱动、SX1278寄存器配置及中断处理等模块。目前已有106人学习下载。代码涵盖SPI接口初始化、LoRa频率带宽扩频因子与编码率配置、数据包收发、中断响应及通信错误恢复等关键环节并保留调试信息便于状态监测与问题定位可作为物联网、远程监测与数据采集项目的可复用软件框架缩短研发周期并降低维护成本。1. 从一块 SX1278 说起这套 STM32F103 SPI 驱动源码到底能干什么如果你手上正捏着一块 SX1278 模块面对着 STM32F103 最小系统板想让它俩通过 SPI 把数据发出去却卡在寄存器配置、时序对不上、收发没反应这些环节那这套源码就是冲着你来的。它是一份基于 STM32F103 标准外设库的完整工程用 SPI 接口驱动 SX1278 LoRa 模块覆盖了从 GPIO 初始化、SPI 时序配置、SX1278 寄存器读写到 LoRa 模式收发的基础流程。适合两类人一是刚接触 LoRa 通信、想找个能跑的参考工程快速验证硬件通路的嵌入式新手二是手头有现成 F103 板子、需要把 SX1278 集成进自己项目、不想从零啃数据手册的熟手。源码结构不复杂但该有的都有——SPI 底层、SX1278 驱动层、主循环收发逻辑拿到手改改引脚就能用。2. 硬件链路与 SPI 模式选型为什么 SX1278 必须跑在 Mode 02.1 先看硬件连接F103 与 SX1278 的引脚对应关系SX1278 模块对外一般引出 8 根线VCC、GND、NSS、SCK、MOSI、MISO、RESET、DIO0。其中 SPI 四线NSS、SCK、MOSI、MISO接 STM32F103 的 SPI 外设RESET 和 DIO0 用普通 GPIO 控制。我一般这样分配SX1278 引脚STM32F103 引脚功能说明VCC3.3V供电绝对不能接 5VGNDGND共地NSSPA4片选软件控制SCKPA5SPI1 时钟MOSIPA7SPI1 主出从入MISOPA6SPI1 主入从出RESETPB0复位控制DIO0PB1中断输入收发完成标志这里有个血泪经验SX1278 是 3.3V 器件F103 最小系统板上如果只有 5V 输出必须加电平转换或者确认模块自带 LDO。直接 5V 怼上去模块当场发烫给你看。2.2 SPI 模式为什么锁定 Mode 0SX1278 的数据手册写得很清楚SPI 接口支持 Mode 0CPOL0CPHA0和 Mode 3CPOL1CPHA1。但实际调试中Mode 0 是最稳的。原因在于 SX1278 在 NSS 拉低后第一个 SCK 上升沿采样 MOSI 上的地址字节Mode 0 正好满足“空闲低电平、上升沿采样”的时序要求。如果你配成 Mode 1 或 Mode 2读回来的寄存器值全是 0x00 或 0xFF这就是典型的时序翻车。配置 SPI1 的关键参数SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; // 全双工 SPI_InitStructure.SPI_Mode SPI_Mode_Master; // 主机模式 SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; // 8位数据 SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; // 空闲低电平 SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; // 第一个边沿采样 SPI_InitStructure.SPI_NSS SPI_NSS_Soft; // 软件片选 SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_8; // 72M/89MHz SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; // 高位先出 SPI_InitStructure.SPI_CRCPolynomial 7; SPI_Init(SPI1, SPI_InitStructure); SPI_Cmd(SPI1, ENABLE);逻辑说明SPI_BaudRatePrescaler_8 把 72MHz 的 PCLK2 分频到 9MHzSX1278 的 SPI 最高支持 10MHz9MHz 留了余量。SPI_NSS_Soft 表示片选由代码手动拉低拉高不依赖硬件 NSS 引脚这样更灵活。SPI_FirstBit_MSB 是 SX1278 要求的它先收地址高字节。参数怎么改如果你的 F103 主频不是 72MHz分频系数要重新算保证 SCK 不超过 10MHz。如果通信距离近但误码率高先把分频系数加大到 16 或 32降速换稳定。2.3 软件片选与硬件片选的实际差别这套源码用的是软件片选也就是 PA4 当普通 GPIO 用读写寄存器前手动拉低读完拉高。硬件片选是 SPI 外设自动控制 NSS 引脚省代码但灵活性差。SX1278 的寄存器操作是“地址字节 数据字节”的连续时序中间不能有 NSS 抖动。软件片选能精确控制 NSS 在整段传输期间保持低电平硬件片选在某些 F103 型号上会在字节间自动拉高导致 SX1278 状态机复位读出来全是错。所以常见做法是SX1278 驱动一律用软件片选。3. SX1278 寄存器读写与 LoRa 模式初始化从复位到进入收发状态3.1 寄存器读写函数SPI 传输的原子操作SX1278 的所有配置都通过寄存器完成。读寄存器和写寄存器的区别在地址字节的最高位写操作地址最高位为 0读操作地址最高位为 1。源码里通常这样实现u8 SX1278_ReadReg(u8 addr) { u8 temp; SX1278_NSS_LOW(); // 片选拉低 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(SPI1, addr 0x7F); // 读操作最高位清零 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); temp SPI_I2S_ReceiveData(SPI1); // 丢弃第一个返回的 dummy 字节 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(SPI1, 0x00); // 发送 dummy 字节产生时钟 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); temp SPI_I2S_ReceiveData(SPI1); // 第二个字节才是寄存器值 SX1278_NSS_HIGH(); return temp; } void SX1278_WriteReg(u8 addr, u8 value) { SX1278_NSS_LOW(); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(SPI1, addr | 0x80); // 写操作最高位置一 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(SPI1, value); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) SET); // 等总线空闲 SX1278_NSS_HIGH(); }逻辑说明读操作时第一个发送的地址字节会同时从 MISO 收回一个无效字节必须丢弃第二个发送的 0x00 才是用来产生时钟、把寄存器值移出来的。写操作简单地址加数据两个字节发完等 BSY 标志清零再拉高片选。参数说明addr 是 SX1278 寄存器地址范围 0x00 到 0x70。value 是要写入的 8 位数据。注意读操作里 addr 0x7F 是确保最高位为 0写操作 addr | 0x80 是确保最高位为 1这两个位操作搞反了读出来全是 0。3.2 LoRa 模式初始化流程六步进入待发状态SX1278 上电后默认是 FSK/OOK 模式要切到 LoRa 模式需要按顺序配置。源码里的初始化流程一般是第一步复位。拉低 RESET 至少 100 微秒再拉高等 5 毫秒让芯片稳定。第二步检查版本号。读 RegVersion地址 0x42正常应该返回 0x12。如果返回 0x00 或 0xFF说明 SPI 通信根本没通别往下走了回去查接线和 SPI 配置。第三步进入睡眠模式。写 RegOpMode0x01设置 LongRangeMode 位为 1切到 LoRa 模式同时保持 Sleep 状态。第四步配置调制参数。包括载波频率、扩频因子、带宽、编码率。这些参数决定了通信距离和速率。第五步配置 FIFO 和中断。设置 FIFO 基地址、DIO0 映射为 TxDone 或 RxDone。第六步切换到待机模式。写 RegOpMode 为 LoRa Standby准备收发。void SX1278_Init(void) { SX1278_Reset(); // 硬件复位 if (SX1278_ReadReg(0x42) ! 0x12) { // 验证版本号 return; // 通信失败直接返回 } SX1278_WriteReg(0x01, 0x80); // Sleep LoRa 模式 SX1278_WriteReg(0x06, 0x6C); // 载波频率 433MHz 高字节 SX1278_WriteReg(0x07, 0x80); // 载波频率 433MHz 中字节 SX1278_WriteReg(0x08, 0x00); // 载波频率 433MHz 低字节 SX1278_WriteReg(0x09, 0x80); // 输出功率 17dBm SX1278_WriteReg(0x1E, 0x74); // SF7, BW125kHz, CR4/5 SX1278_WriteReg(0x1F, 0x00); // 关闭频率跳变 SX1278_WriteReg(0x20, 0x00); // 关闭前导码检测 SX1278_WriteReg(0x21, 0x08); // 前导码长度 8 SX1278_WriteReg(0x22, 0xFF); // 有效载荷长度 255 SX1278_WriteReg(0x39, 0x34); // 同步字 SX1278_WriteReg(0x40, 0x00); // FIFO 基地址 SX1278_WriteReg(0x01, 0x81); // Standby LoRa 模式 }逻辑说明0x01 寄存器写 0x80 是 Sleep 模式加 LoRa 位写 0x81 是 Standby 模式加 LoRa 位。0x06 到 0x08 是载波频率433MHz 对应的值是 0x6C8000计算公式是 Frf (Frf_reg * 32000000) / 524288。0x1E 寄存器里 SF7、BW125kHz、CR4/5 是常用的平衡配置距离和速率兼顾。参数怎么改想传得更远就把 SF 加大到 9 或 12但速率会降。想传得快就把带宽加到 250kHz但灵敏度会降。输出功率 0x80 对应 17dBm如果模块带 PA 可以写到 0xFF 对应 20dBm但注意散热。3.3 发送与接收DIO0 中断和轮询两种方式发送数据时先把数据写进 FIFO地址 0x00然后写 RegOpMode 切到 TX 模式。等 DIO0 引脚变高表示发送完成再切回 Standby。接收时切到 RX 连续模式DIO0 变高表示收到数据读 RegIrqFlags0x12确认是 RxDone然后从 FIFO 读数据。源码里一般用轮询方式简单直接void SX1278_SendData(u8 *buffer, u8 size) { SX1278_WriteReg(0x01, 0x81); // Standby SX1278_WriteReg(0x0E, 0x00); // FIFO 写指针归零 SX1278_WriteReg(0x0D, 0x00); // FIFO 读指针归零 for (u8 i 0; i size; i) { SX1278_WriteReg(0x00, buffer[i]); // 写数据到 FIFO } SX1278_WriteReg(0x22, size); // 设置有效载荷长度 SX1278_WriteReg(0x01, 0x83); // TX 模式 while (SX1278_ReadReg(0x12) 0x08 0); // 等 TxDone 标志 SX1278_WriteReg(0x12, 0x08); // 清除 TxDone 标志 SX1278_WriteReg(0x01, 0x81); // 回到 Standby }逻辑说明0x0E 和 0x0D 是 FIFO 读写指针发送前归零确保从 FIFO 头部开始写。0x22 设置发送长度必须和实际写入的字节数一致否则对方收到的长度不对。0x01 写 0x83 是 TX 模式芯片自动发送发完 DIO0 拉高。轮询 0x12 寄存器的 TxDone 位清标志后回 Standby。注意如果发送大包数据FIFO 只有 256 字节超过要分包。另外发送期间不要频繁读寄存器SPI 操作会干扰发送状态机。4. 避坑与排查SX1278 调试中最容易翻车的五个点4.1 读版本号返回 0x00 或 0xFF现象初始化时读 0x42 寄存器返回值不是 0x12而是 0x00 或 0xFF。原因SPI 通信根本没建立。可能是 MISO 和 MOSI 接反了或者 NSS 没拉低或者 SPI 模式配错。也有可能是 SX1278 供电不足3.3V 电压被拉低到 2.5V 以下。解决先用万用表量 SX1278 的 VCC 引脚确认在 3.3V 左右。然后检查 SPI 四根线有没有虚焊。最后用示波器看 SCK 有没有波形如果没有说明 SPI 外设没使能或者 GPIO 复用没配。常见做法是写一个最简单的 SPI 回环测试短接 MOSI 和 MISO发什么收什么先确认 STM32 的 SPI 本身是好的。4.2 发送完成但接收端没反应现象发送端 TxDone 标志正常置位但接收端一直收不到数据。原因收发双方的频率、扩频因子、带宽、同步字必须完全一致。任何一项不同都收不到。另外如果发送端功率设得太低或者天线没接信号根本发不出去。解决先把双方参数打印出来逐项对比。频率计算公式再算一遍确保 0x06 到 0x08 写入的值对应同一个频率。同步字 0x39 寄存器双方都要设成一样的值。天线必须接不接天线发射会损坏模块。如果距离近还是收不到把扩频因子调到 12带宽调到 125kHz牺牲速率换灵敏度。4.3 DIO0 中断不触发现象配置了 DIO0 映射为 TxDone 或 RxDone但中断服务函数一直不进。原因DIO0 引脚在 F103 上配置成了普通输入但没开外部中断或者中断优先级没设或者 DIO0 映射寄存器 0x40 没配对。解决检查 GPIO 配置DIO0 对应的引脚要配成浮空输入或上拉输入并且使能 EXTI 中断线。NVIC 里要打开对应的中断通道。0x40 寄存器DioMapping1的 bit6 和 bit7 控制 DIO0 映射00 是 RxDone01 是 TxDone10 是 CadDone11 是 CadDetected。发数据时映射成 TxDone收数据时映射成 RxDone不能搞混。4.4 通信距离短得离谱现象官方标称几公里实际只能通几十米。原因天线匹配没做好或者模块输出功率没设对或者周围有强干扰源。SX1278 对天线阻抗很敏感433MHz 下需要 50 欧姆匹配。解决先用频谱仪看发射频谱确认功率和频率都对。天线用弹簧天线的话长度要接近四分之一波长433MHz 对应约 17 厘米。如果板子上有匹配网络检查电感电容值。软件上把输出功率寄存器 0x09 写到 0xFF但注意如果模块没有 PA写再大也没用。另外把带宽降到 62.5kHz 或 125kHz扩频因子升到 10 以上灵敏度能到 -130dBm 左右。4.5 SPI 速率太高导致误码现象低速通信正常一提高 SPI 时钟就出现寄存器读写错误。原因SX1278 的 SPI 最高速率是 10MHz但实际布线长、有干扰时9MHz 都可能不稳。另外F103 的 SPI 在 72MHz 主频下分频系数最小是 2也就是 36MHz远超 SX1278 上限。解决分频系数至少设到 8也就是 9MHz。如果还不稳设到 16 或 32。SPI 速率降低对 LoRa 通信本身没影响因为 LoRa 的空中速率远低于 SPI 速率。常见做法是初始化时用低速配置完再切高速但 SX1278 没必要全程 9MHz 足够。5. 进阶技巧用 DIO0 中断替代轮询以及一个验证 SPI 时序的土办法轮询方式写起来简单但 CPU 一直空转等标志功耗高实时性也差。实际项目里我一般把 DIO0 接到 F103 的 EXTI 线上用中断方式处理收发完成事件。具体做法是DIO0 接 PB1配置 EXTI1 为上升沿触发NVIC 使能 EXTI1_IRQn。中断服务函数里读 0x12 寄存器判断是 TxDone 还是 RxDone清标志置一个全局变量通知主循环。这样主循环可以去干别的事不用死等。void EXTI1_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line1) ! RESET) { u8 irq SX1278_ReadReg(0x12); // 读中断标志 if (irq 0x08) { // TxDone SX1278_WriteReg(0x12, 0x08); // 清 TxDone g_tx_done 1; } if (irq 0x40) { // RxDone SX1278_WriteReg(0x12, 0x40); // 清 RxDone g_rx_done 1; } EXTI_ClearITPendingBit(EXTI_Line1); } }逻辑说明0x12 寄存器的 bit3 是 TxDonebit6 是 RxDone。读出来判断后分别清标志置全局变量。主循环里检查 g_tx_done 或 g_rx_done处理完清零。注意中断里不要做太多 SPI 操作读一个寄存器就够了数据读取放到主循环。参数说明EXTI_Line1 对应 PB1如果 DIO0 接的是其他引脚中断线要跟着改。上升沿触发是因为 SX1278 的 DIO0 在事件发生时输出高电平。另外分享一个验证 SPI 时序的土办法拿逻辑分析仪抓 SCK、MOSI、MISO、NSS 四根线发一个读 0x42 寄存器的操作看波形。正常的时序是 NSS 拉低后SCK 出现 16 个脉冲前 8 个脉冲 MOSI 上是 0x42MISO 上是无效字节后 8 个脉冲 MOSI 上是 0x00MISO 上是 0x12。如果 SCK 脉冲数不对或者 MISO 上一直是高阻那问题就在 SPI 配置或硬件连接上。这个办法比盲猜快得多从那以后我每次调新板子都先抓一遍波形确认时序对了再往下写代码。希望帮到你。本文还有配套的精品资源点击获取