简介:面向 LoRa 物联网开发者的 SX1268 驱动工程,基于 LoRaMac-node 协议栈,提供 C 语言实现的端到端通信代码,实测设备与服务器间 10KM 距离传输正常,适合需要快速搭建远距离低功耗链路的嵌入式工程师。压缩包共 2000 个文件,以 .c 源码、.h 头文件、.sisc 工程文件为主,另有 .ld 链接脚本、.txt 说明、.md 文档及少量 PDF 参考,总计 19.02MB;源码内含 LoRaMac.c、STM32L4/L1/L0 系列 HAL 驱动等,覆盖协议栈、射频控制与硬件抽象层。资料已有 565 人学习下载,对于正在调试 SX1268 或研究 LoRaWAN 入网、收发流程的开发者,可直接对照驱动代码修改信道、数据速率与扩频因子,并借助 STM32 工程模板减少移植工作量。整体目录结构清晰,既能作为学习 LoRa 物理层与 MAC 层的参考,也可在自有硬件上二次开发。
1. 这块板子要跑 LoRaMac-node:为什么官方 C 代码不能直接烧给 SX1268
拿到一块板载 SX1268 的 LoRa 模块,第一件事往往不是焊天线,而是面对 LoRaMac-node 这一大坨 C 语言驱动代码不知道从哪下手。LoRaMac-node 是 LoRa 生态里最常被拉下来的开源协议栈,里面包含了完整的 LoRaWAN MAC 状态机、sx126x 系列 radio 驱动和 board 适配层,但仓库默认的 target 是 SX1261/SX1262 评估板。SX1268 作为 470-510MHz 频段模块里的主力芯片,寄存器虽然和 SX1262 高度重合,板级配置却要自己改。这篇笔记就讲怎么把这套 C 驱动改造成 SX1268 能跑的落地版本:先分层看代码,再动板级引脚,然后调频点和功率,最后用两个模块在桌面上把链路打通。
2. 移植前先读代码:LoRaMac-node 的分层结构与 SX1268 的板级接线
2.1 目录分层:知道 sx126x.c 不用动,boards 和 mac 才是主要战场
LoRaMac-node 仓库顶层的代码结构基本是四块:projects 目录放各 IDE 和工具链的工程入口,里面有 GCC、Keil、IAR 的工程文件;src/radio 下面是 sx126x.c 和 sx126x.h,负责 SX1261/SX1262/SX1268 三款芯片的命令封装,比如 SetPacketType、SetRfFrequency、SetTxParams 这些底层 API;src/mac 是 LoRaWAN 协议栈的主体,入网、确认、重传、ADR 这些状态机都在 LoRaMac.c 里跑;src/boards 是真正需要动手的地方,board.c 里要实现 SPI 读写、GPIO 读写、定时器 tick 和串口打印。
SX1268 和 SX1262 的寄存器级命令基本一致,差异主要在频率范围和个别射频参数上,所以拿到源码后不必大改 sx126x.c。真正的移植顺序是:先改 board.h 的引脚定义,再实现 board.c 里的 BSP 函数,最后在 LoRaMac 初始化的地方把 region 选成 CN470、把频点写成 470-510MHz 之间的实际值。这个顺序反了会出现一种常见场面:radio 层调得很欢,但板级时序不对,芯片根本不响应,最后浪费一整天查 SPI。
另外要留意 src/boards 下不同评估板的代码不能直接套用。Nucleo 板、SX1262DVK 板、你自己的小板子,GPIO 端口、SPI 外设、时钟树都不一样。我一般会把 board.c 里所有硬件相关函数重新梳理一遍,不是只改引脚号,还要确认 SPI 的片选极性、DIO1 是否复用到其他外设、RESET 是不是被调试口占用。
2.2 引脚映射:RESET、BUSY、DIO1、DIO2 四根线决定驱动能不能起来
SX1268 与 MCU 之间除了标准 SPI 的 SCK/MOSI/MISO/NSS,还有四根控制线必须接对。RESET 用于复位芯片,低电平有效;BUSY 是状态输出,表示芯片正在处理上一条命令;DIO1 是中断输出,发送完成、接收完成、前导码检测都会在这里拉电平;DIO2 在不少模块上负责控制射频开关或给 TCXO 供电。LoRaMac-node 的 board.h 里会用一组宏把引脚定义出来,常见做法是直接改这些宏,而不是去动 board.c 里的逻辑。
/* board.h 中针对 SX1268 模块的引脚宏定义(以 STM32 为例) */ #define RADIO_RESET_PORT GPIOB #define RADIO_RESET_PIN GPIO_PIN_12 #define RADIO_BUSY_PORT GPIOB #define RADIO_BUSY_PIN GPIO_PIN_13 #define RADIO_DIO1_PORT GPIOB #define RADIO_DIO1_PIN GPIO_PIN_10 #define RADIO_DIO2_PORT GPIOB #define RADIO_DIO2_PIN GPIO_PIN_11这些宏会被 board.c 里的底层函数引用,接线时最容易被忽略的是 BUSY。SX1268 的 BUSY 引脚必须配置为输入,SPI 每发一条命令前都要等待 BUSY 拉低。如果这个引脚忘了配、或者被初始化成了推挽输出,重启后芯片不会响应任何命令,现象就是 LoRaMac 初始化直接卡死。DIO2 的 TXCO 控制也要看模块原理图:有些模块内部已经把 DIO2 接到了射频开关,有些则是外部 TCXO 供电脚,配置错了会表现为发射功率明显偏低或接收灵敏度异常。
有些国产模块把 DIO1 和 BUSY 做在同一个排针上,拿到的转接板上丝印还容易标反。我会在写 board.c 前先用万用表量一下引脚到模块焊盘的连接关系,没有把握就用杜邦线飞线验证,避免把时间耗在“代码没病、接线有病”上。
2.3 用 HAL 库实现 SPI 时序:忙等待、NSS 拉低与字节交接
常见做法是在 board.c 里写一个 RadioSpiReadWrite 函数,SX1268 的所有命令都通过它进出。核心点有三个:片选拉低前要确认 BUSY 处于空闲;命令和参数一次性传输;结束后把 NSS 拉高。贴一段用 STM32 HAL 阻塞模式封装的读写:
/* radio_spi.c:用 STM32 HAL 阻塞模式封装的 SPI 全双工读写 */ uint8_t RadioSpiReadWrite(uint8_t *txBuf, uint8_t *rxBuf, uint16_t size) { // 每发一条命令前先等 BUSY 低电平,避免命令被芯片丢弃 WaitRadioBusy(); HAL_GPIO_WritePin(RADIO_NSS_PORT, RADIO_NSS_PIN, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(&hspi2, txBuf, rxBuf, size, 1000); HAL_GPIO_WritePin(RADIO_NSS_PORT, RADIO_NSS_PIN, GPIO_PIN_SET); return 0; }WaitRadioBusy 里要加一个超时上限,不能死等。有些板子 BUSY 在复位后会有长达几百微秒的高电平,卡在这里是正常的,但超过 10ms 还拉不低就有问题了,这时需要检查复位是否生效、BUSY 引脚是否接反。SPI 时钟建议从保守值起步:SX1268 手册允许的 SPI 速率很高,但国产模块对毛刺的容忍度不一,时钟超过 8MHz 后误码率可能上升,我一般先把分频放到 4-5MHz 把功能跑通,再逐步提速。
SPI 模式和极性也要对上,多数 SX1268 模块跑的是 SPI Mode 0(CPOL=0,CPHA=0)。这种细节往往不会写在 LoRaMac-node 的文档里,而是在芯片数据手册的 SPI 章节里。如果你是从 STM32 标准外设库或 Arduino 库搬过来的代码,注意确认相位极性,默认值不一样会表现为“能写寄存器但读回来全是 0xFF”。
2.4 配置 DIO1 外部中断:任意沿触发比固定上升沿更省心
DIO1 在发送完成、接收完成、接收超时等多个事件时都会动作。LoRaMac-node 的 radio 层通过 RadioIrqProcess 去读芯片的中断事件寄存器,前提是 DIO1 能把 MCU 从主循环里唤醒。用 HAL 库配置 EXTI 时,我建议中断触发方式用“任意沿”而不是只配上升沿。
/* radio_gpio.c:DIO1 外部中断初始化 */ void RadioDio1ExtiInit(void) { GPIO_InitTypeDef gpio = {0}; gpio.Pin = RADIO_DIO1_PIN; gpio.Mode = GPIO_MODE_IT_RISING_FALLING; gpio.Pull = GPIO_NOPULL; HAL_GPIO_Init(RADIO_DIO1_PORT, &gpio); HAL_NVIC_SetPriority(EXTI15_10_IRQn, 5, 0); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); }中断回调里只做一件事:通知协议栈去处理。不要在回调里做日志输出、延时、或调用可能阻塞的 HAL 函数,否则会影响接收窗口的时间精度。
/* stm32xxx_it.c 中的 EXTI 回调 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == RADIO_DIO1_PIN) { RadioIrqProcess(); // 读取事件标志并清中断 } }RadioIrqProcess 内部会访问 SPI,所以这个回调所在的中断优先级不能太高。LoRaMac 的定时器中断如果被 DIO1 抢占,接收窗口可能偏移;反过来 DIO1 被定时器中断卡住,又可能漏掉 RX_DONE。一般把 DIO1 的外部中断优先级放在普通业务中断级别,确保主循环和定时器能有喘息空间。
3. 入网与发射参数:CN470 频段、功率与 ADR 的取舍
3.1 CN470 的 96 个上行信道:频率不是填一个数那么简单
LoRaWAN 在中国区域常用的是 CN470 频段,覆盖 470-510MHz。SX1268 的 RF 频率寄存器虽然是 32 位,但 LoRaMac-node 对频道的管理是基于 region 配置表的,不是简单写一个 490000000 了事。协议栈默认会按 96 个上行信道加 4 个下行信道来组织频点,每个信道间隔 200kHz。入网时的 Join 过程会在这些信道里跳,如果只配置了一个频点,网关那边会收不到你的入网请求。
/* 入网前设置本机使用的频点和信道,单位 Hz */ LoRaMacSetChannelFrequency(0, 490000000); // 上行信道 0 使用 490MHz LoRaMacSetChannelFrequency(1, 490200000); // 上行信道 1 使用 490.2MHz这里有个参数含义要讲清楚:LoRaMacSetChannelFrequency 的第一个参数是信道编号,第二个是绝对频率。往后如果要在不同信道间跳频,需要按 region 表把所有期望使用的信道都设置一遍。很多国产模块出厂默认频点在 490MHz 附近,但如果你的应用只在室内测试,其实把信道 0 到信道 7 都设成同一个物理频点也合法,只是抗干扰能力变差。
实际项目里我更倾向于只放开一小段信道,比如 490-491MHz 之间的 5 个信道,在 LoRaMac 初始化后调用 LoRaMacSetChannelFrequency 逐个设过去。这样做的好处是现场排查时干扰源容易定位,坏处是如果网关配置了完整 96 信道的跳频表,入网响应可能要等较长时间。
3.2 TX 功率与 ADR:开发板标的 22dBm 不等于模块真能发出去
SX1268 的最大发射功率可以从寄存器层面配到 +22dBm,但实际能出多少取决于模块的 PA 电路、电源能力和天线匹配。LoRa 模块行业里有个不写进数据手册的共识:标称 22dBm 的模块,在高频段或电池供电场景下实际输出可能只有 17-19dBm。C 代码里写多大功率要跟模块厂家确认,而不是照着评估板源码抄。
/* 设置发射功率,单位为 dBm,0 到 22 可调 */ RadioSetTxConfig(MODEM_LORA, 20, 0, 0, 0, 0, false); RadioSetModem(MODEM_LORA); RadioSetRfFrequency(490000000);RadioSetTxConfig 的第一个参数是调制方式,第二个是功率。这里如果写 20,芯片内部会按 0.5dBm 步进去设置 DAC 和 PA 寄存器。LoRaMac-node 自带的 SX1262 评估板默认使用 22dBm,如果你的模块没有好的散热条件,连续发射几包后频率会漂移,表现为网关收到包但 RSSI 不稳定。我一般先把功率调到 17dBm 做链路摸底,确认通信距离再往上加。
ADR 也要谨慎对待。ADR(Adaptive Data Rate)依赖网关节点的链路余量反馈,如果网关没有开 ADR 或没有正确上报链路余量,终端会把 SF 从 12 一路降到 7,距离优势就没了。做桌面回环测试时,最好先把 ADR 关死,用固定 SF12 验证无线链路,等到接入真实网关再做 ADR 决策。
/* 固定使用 SF12/BW125,关闭 ADR,适合回环测试 */ LoRaMacSetAdrOn(false); RadioSetTxConfig(MODEM_LORA, 20, 0, 0, SF12, BW125, CR48, 8, 0, false);参数里 SF12 和 BW125 是 LoRa 调制的核心组合,红色代码块中 LoRa 调制参数顺序是:调制方式、功率、频率偏移、调制参数、包参数。如果传参顺序错位,最直观的现象是发射出去之后对端收到的 SNR 异常。
3.3 Class 模式、RX 窗口与占空比:SX1268 的低功耗不是光靠 Sleep 指令
LoRaWAN 终端最常用的是 Class A:发送完成后打开 RX1 和 RX2 两个接收窗口。RX1 在发送结束后 1 秒开启,RX2 在 RX1 后 1 秒开启。SX1268 在发射时会自动进入接收状态,如果 MCU 没有在这个时间窗口里把射频切到 RX,就会错过下行 ACK。LoRaMac-node 的状态机里,这个窗口由板级定时器管理,所以 board.c 的 tick 精度直接影响入网成功率。
/* Class A 模式下设置接收窗口偏移,单位毫秒 */ LoRaMacSetRx2Frequency(505000000); LoRaMacSetRx2Channel(505000000);RX2 的频率在 CN470 里通常固定为 505MHz,如果你把上行信道改到了 490MHz,下行 RX2 也要对应调整。这个参数很多人不改,结果入网时发送成功但收不到 Join Accept,排查半天发现在 505MHz 上监听的网关根本没往下发。
占空比那块很容易忽略。CN470 频段有占空比限制,LoRaMac 提供了 DutyCycle 相关的开关和上限设置。如果代码里开启了占空比控制,连续发送会触发协议栈的禁止发射状态,表现为串口日志里出现“Duty cycle”相关打印,主循环还在跑但不发数据。现场测试时我会先按法规要求把占空比参数改大或直接关闭,等调通后再把真实限制加回去。
/* 关闭占空比限制,仅用于实验室测试 */ LoRaMacDutyCycleOn(false); LoRaMacSetMaxDutyCycle(0);这两个函数在不同版本的 LoRaMac-node 里名字可能略有差异,但语义一致。注意关闭占空比限制后,实际生产和认证测试还是要按当地法规来,实验室图方便可以,不要带着这个配置去现场长期跑。
3.4 日志与状态观察:把 ACK、重传、RSSI 打出来,调试才有着力点
LoRaMac-node 默认自带一套打印机制,但很多人在板级移植时没把串口重定向接好,导致 LORAMAC_PRINTF 全部哑火。在调驱动阶段,至少要保证三行日志能看到:入网状态、发送完成、ACK 是否收到。给一段在主循环里观察状态的简例:
/* 主循环中的状态观察点 */ switch (LoRaMacGetState()) { case LORAMAC_IDLE: debug_log("MAC idle, RSSI=%d dBm\n", (int16_t)RadioGetRssiInst()); break; case LORAMAC_TX_ONGOING: debug_log("TX ongoing\n"); break; case LORAMAC_RX_TIMEOUT: debug_log("RX timeout, will retry\n"); break; }RadioGetRssiInst 返回的是当前信道瞬时 RSSI,空闲时能看到底噪水平。如果底噪在 -105dBm 以上,说明现场干扰较大,同频干扰会让随机数退避白白浪费很多重传机会。日志的优先级也很重要,串口打印放在高优先级里会拖慢 LoRaMac 的定时器,一般建议用一个环形缓冲,主循环里统一输出。
4. 离了网关也能验证:两个 SX1268 模块的裸 Radio 回环
4.1 绕过 LoRaWAN 协议:直接调 radio 层 API,减少变量
在把 LoRaWAN 协议栈完整跑起来之前,先用裸 radio 做点对点回环,能快速确认 SX1268 的驱动层、SPI 时序和天线通道是好的。做法是绕过 LoRaMac 协议,直接调用 sx126x.c 里暴露出来的 Radio API。两个模块一个配成发送,一个配成接收,在同一频点同一参数下互发。
/* 模块 A:初始化并进入连续接收模式 */ RadioInit(); RadioSetModem(MODEM_LORA); RadioSetRfFrequency(490000000); RadioSetRxConfig(MODEM_LORA, SF12, BW125, CR48, 8, 0, 0, 0, 0, false); RadioRx(0); // 0 表示持续监听/* 模块 B:初始化后发送一包 12 字节数据 */ RadioInit(); RadioSetModem(MODEM_LORA); RadioSetRfFrequency(490000000); RadioSetTxConfig(MODEM_LORA, 17, 0, 0, SF12, BW125, CR48, 8, 0, false); RadioSend("hello sx1268", 12);这里的关键是 RadioSetRxConfig 和 RadioSetTxConfig 的参数要完全对得上。收发参数不一致时,接收端会卡在“检测不到前导码”或“CRC 校验失败”,日志里看不到任何有效包。RF 频率也要一致,SX1268 的频率寄存器是 24 位步进,如果发送 490.000MHz 接收 490.001MHz,看起来差得不多,但实际已经超出了 LoRa 解调带宽的一小部分,误码率会急剧上升。
4.2 回环收发与 RSSI 读数:用数值判断链路余量
回环测试的目的不是“能收到”就好,而是要拿到 RSSI 和 SNR 来评估链路余量。接收端在收到一包有效数据后,通过 RadioGetPacketStatus 可以读到这包数据时的 RSSI 和 SNR。发送端也可以用 RadioGetRssiInst 看当前信道的底噪。
/* 接收回调里读取 RSSI 与 SNR */ RadioPacketStatus_t st; RadioGetPacketStatus(&st); debug_log("RSSI=%d dBm, SNR=%d\n", st.Rssi, st.Snr);把两个模块放在同一张桌子上,RSSI 通常在 -30 到 -50dBm 之间,这属于“信号过载”区域,反而可能因为接收饱和而出现误码。正确做法是把发射功率降到 2dBm 甚至加衰减器,让接收端 RSSI 落在 -70 到 -90dBm 区间,才更接近真实使用场景。
4.3 参数配对对照表:SF、BW、CRC、Preamble 四要素
回环中最常见的问题是改了一端参数忘了另一端。下面的表列了必须保持一致的四个要素,每次改参数都照着核对一遍:
| 参数 | 发送端 | 接收端 | 不一致的后果 |
|---|---|---|---|
| SF | 12 | 12 | 接收端检测不到前导码 |
| BW | 125kHz | 125kHz | 频谱失配,解码失败 |
| CRC | 开 | 开 | 收包被丢弃,统计不到 |
| Preamble | 8 | 8 | 短包前导码识别超时 |
CRC 这个坑最隐蔽。LoRa 的 CRC 是在物理层做的,如果发送端开了 CRC 而接收端没开,接收端能解出数据但上层协议可能收到错包;反过来发送端没开 CRC 而接收端开了,接收端会直接丢包。SX1268 的裸 radio 测试里,我一定会在 RadioSetRxConfig 和 RadioSetTxConfig 里保持 CRC 的开关状态一致。
4.4 把回环固化成自测函数:后续改驱动不用反复手敲命令
每次改完 board.c 或更新封装库后,都要重新验证一次驱动没被改坏。我会把回环测试做成一个独立的自测函数,编译时用一个宏开关控制,方便随时调用。
/* 自测函数:周期发送并统计收包成功率 */ void RadioLoopbackTest(uint32_t count) { uint32_t ok = 0, fail = 0; for (uint32_t i = 0; i < count; i++) { RadioSend("test", 4); if (RadioGetPacketStatus(&st) == 0) ok++; else fail++; HAL_Delay(100); } debug_log("loopback ok=%d fail=%d\n", ok, fail); }这个函数没有依赖 LoRaWAN 状态机,只要驱动层 SPI 通、DIO1 中断通,它就能跑。如果这个自测都过不了,就别急着去查 LoRaMac 协议栈的入网问题,回头先把驱动层修好。这类“小步验证”的习惯能省下大量无效调试时间。
5. SX1268 驱动调试避坑:五个现场案例与排查顺序
5.1 现象一:初始化卡死,LoRaMac 停在等待 BUSY
现象:程序烧进去后,板子没有任何输出,调试器看到代码卡在 WaitRadioBusy 的 while 循环里不退出。
原因:最常见的是 BUSY 引脚配置错误。很多国产模块的 BUSY 是开漏输出,MCU 那边必须配置为上拉输入,如果配成了推挽输出,两者会互相打架,电平永远拉不低。也有的是 RESET 时序没有满足,芯片根本没完成复位。
解决:先断开所有线,用示波器看 BUSY 引脚在复位后的波形;确认 MCU 的 GPIO 模式为输入上拉;把 RESET 拉低 5ms 再拉高,等待 10ms 再调 SPI。最后用 SX126xGetStatus 读取状态寄存器,能读到正常返回码再往下走。
5.2 现象二:DIO1 中断不触发,发送完成后主循环干等
现象:RadioSend 执行后,DIO1 引脚没有产生任何电平变化,协议栈一直停在发送未完成状态。
原因:SX1268 的中断事件没有映射到 DIO1。radio 层初始化时有一段代码负责设置 DIO IRQ 掩码,如果这段被跳过或参数写错,芯片内部已经完成发送,但不会通过 DIO1 通知 MCU。
解决:重新调用 RadioSetDioIrqParams,把 TX_DONE、RX_DONE、RX_TIMEOUT 这几个关键事件使能。代码形式类似这样:
/* 把发送完成和接收完成事件映射到 DIO1 */ RadioSetDioIrqParams(SX126X_IRQ_TX_DONE | SX126X_IRQ_RX_DONE | SX126X_IRQ_RX_TIMEOUT, SX126X_IRQ_TX_DONE | SX126X_IRQ_RX_DONE | SX126X_IRQ_RX_TIMEOUT);5.3 现象三:发送成功,但接收端报 CRC 错误
现象:回环测试中,接收端的中断里能读到 RX_DONE,但读取 packet status 时 CRC 状态是错的,上层收不到完整包。
原因:收发双方的 CRC 开关不一致,或者 Preamble 长度不匹配。还有一种情况是 SX1268 处于可变长度包模式,而发送端用了固定长度模式,导致接收端在解析长度字段时错乱。
解决:对照 4.3 节的参数表逐项核对。把两端都设为固定长度模式,长度值保持一致;CRC 开关都打开;Preamble 设成同一个值。改完再测,如果还报 CRC 错误,用示波器抓 SPI 命令,确认发送端的 CRC 配置命令确实写进去了。
5.4 现象四:模块进入睡眠后再唤醒,第一包数据发不出去
现象:低功耗产品在定时唤醒后,LoRaMac 状态机恢复,但 RadioSend 调用后没有 TX_DONE 中断,整个系统像被冻住。
原因:SX1268 从 Sleep 模式唤醒后,射频前端的状态寄存器没有恢复到工作模式,调制参数、频点、包配置可能处于复位后的默认值。协议栈的定时器虽然唤醒了 MCU,但 radio 的上下文没有重建。
解决:唤醒后先调用 RadioSetTxConfig 和 RadioSetRfFrequency 重新初始化,再执行发送。更稳妥的做法是在进 Sleep 前把整个 radio 上下文保存下来,唤醒后按顺序恢复。不要在唤醒后直接调用 RadioSend,寄存器里残留的无意义数据会造成发射异常。
5.5 现象五:串口日志乱码或第一行日志缺失
现象:驱动正常,入网也能成功,但串口打印出来的调试信息是乱码,或者前几行日志被吞掉。
原因:board.c 里的系统时钟没有按实际晶振配置,串口波特率对不上;也有的是 printf 重定向没有实现,导致 LoRaMac-node 内部打印走了默认的 semihosting 方式。
解决:先确认板级时钟树,外部晶振是 8MHz 还是 25MHz 都要写进时钟初始化里。把串口改为直接调用 HAL_UART_Transmit 实现的 debug 输出函数,不走半主机模式。日志的问题虽然不影响无线链路,但会把协议栈状态机观察渠道堵死,排查问题时非常吃亏。
6. 现场回归的最后一步:24 小时连续对打验证套路
把协议栈调通、入网成功、回环测完,还不能直接交付。我自己的习惯是做一个 24 小时连续对打验证,把两个 SX1268 模块放在实际安装位置,一个做终端,一个做简易网关,跑一整夜看数据。验证的核心指标不是单包成功率,而是长时间运行时有没有“假死”。LoRaMac 的定时器依赖 MCU tick,如果 tick 溢出或中断优先级配置不当,连续运行几小时后会出现协议栈不调度的情况。
具体做法:终端每秒发一包 12 字节的非确认数据,网关端记录每秒是否收到,每 10 分钟汇总一次成功率、平均 RSSI、丢包次数。日志里一旦看到连续 10 包以上丢失,就停下来抓现场。这个压力比真实业务场景大很多,真实业务可能一分钟才发一包,每秒一包能把驱动层的资源竞争问题提前暴露出来。
除了可靠性,还要看频率漂移。连续发射会让 SX1268 的晶振升温,如果模块没有 TCXO,频点会逐渐偏离。对打日志里的 RSSI 会缓慢下降,然后突然恢复到正常,这种周期性的波动多半是晶振温漂。用 24 小时数据画一条曲线,比单次测试更能说明问题。
我早年调 SX1268 时吃过一次亏:回环测试只跑了半小时就以为没问题,结果产线连续工作了 5 个小时后,终端开始频繁掉线,原因就是 DIO1 中断和 LoRaMac 定时器中断优先级配置不当,长时间运行后中断嵌套累积导致接收窗口错过。后来把验证时间拉长到 24 小时,这类问题基本都能暴露出来。做长测时,日志要带上时间戳,不能用 “RSSI=-70” 这种无上下文输出。将场景、功率、SF、日志间隔都固定下来,复现问题才能有的放矢。希望这篇笔记能帮你少走几步弯路,把时间留给真正难缠的射频问题。
本文还有配套的精品资源,点击获取