☰
SX126x LoRa驱动移植实战:从STM32初始化到CAD低功耗收包
2026/9/26 13:48:31 网站建设 项目流程

简介:面向STM32平台的SX126x系列LoRa芯片驱动源码包,适配SX1261、SX1262、SX1268等新一代LoRa芯片,涵盖芯片初始化、数据收发、错误处理、状态管理等完整驱动模块,适合有一定嵌入式基础、正在评估或部署该系列芯片的开发者。压缩包内共611个文件,以261个C源码、328个头文件为主,并配有少量Keil工程文件、链接脚本、PDF说明文档及bin示例,整体大小3.64MB,目录结构清晰,便于按需筛选。已有2370人学习下载。C源码负责具体驱动逻辑,头文件定义寄存器地址与接口,Keil工程可直接打开编译,省去手动建工程的时间。开发者可快速完成移植,大幅缩短LoRa功能开发周期;源码对低功耗模式、频点设置、扩频因子、编码率等参数均有封装,并覆盖STM32L0/L1/L4等多系列HAL库,方便在不同项目中复用。压缩包内的CAD示例工程演示了信道空闲检测流程,适合物联网网关、远距离传感器等低功耗场景,能够帮助嵌入式工程师快速完成驱动的调试与二次开发。

1. 换芯片最怕驱动翻车:SX126x为什么不能照搬SX1278的代码

新来的同事把SX1278的驱动改了两行寄存器地址就烧到SX1262的板子上,结果射频指示灯亮着,网关一个字节都收不到。这事很典型,凡是照着SX127x那套寄存器思维去读SX126x数据手册的人,第一次上电多半要交学费。SX126x是Semtech新一代LoRa收发芯片,覆盖150MHz到960MHz频段,支持LoRa与FSK双调制,常出现在LoRaWAN节点、手持终端和环境监测网关里。注意搜“LoRa微调”搜出来的多半是AI模型训练,那跟射频芯片完全是两个方向。下面把SX126x驱动源码的分层思路、STM32最小收发例程、参数配置顺序以及几个真实踩坑点一次讲透,适合正在做芯片替换选型、准备把协议栈radio层换成SX126x驱动的嵌入式工程师。

2. SX126x驱动源码怎么拆:命令状态机、HAL层和Busy握手

2.1 从SX127x到SX126x:变的不是寄存器地址,是操作范式

SX127x系列的操作方式很直接:往寄存器地址写值,配置调制方式、频率、功率,再往FIFO写数据,切发送或接收模式,完事。寄存器手册就是一本地址字典,驱动源码本质上是“地址+掩码+移位”的堆积。SX126x把这套玩法整个翻了一个面。

SX126x内部是一个命令状态机,主机通过SPI发送操作码(opcode)和参数来驱动状态切换。操作码后面跟随的参数有时多达十几字节,而且大部分命令需要芯片内部的无线电状态机处理完才能接收下一条命令。芯片用一个专用的Busy引脚告诉主机“我正忙着,别打扰我”。这套设计带来了一个质变:驱动源码不再是对着寄存器位逐个操作,而是变成“拼装命令参数、等待Busy释放、发操作码、等结果”的流程控制。

还有一个关键差异。SX127x的LoRa和FSK调制分别使用不同的寄存器组,切调制方式要重配一大片寄存器。SX126x直接用SetPacketType命令在LoRa和FSK之间切换,所有调制参数归口到SetModulationParams、包格式归口到SetPacketParams。可维护性好了,但前提是驱动架构必须跟着改,拿老代码硬套必然翻车。

2.2 驱动源码的标准分层:命令层、平台层和Radio抽象

拿到一份SX126x驱动源码,先别急着看细节,第一件事是认清分层。常见做法是把源码拆成三层:平台抽象层(HAL)、命令层、Radio抽象层。

平台抽象层处理的是SPI收发、NSS拉高拉低、Busy电平读取、DIO1中断回调注册,以及RST复位引脚操作。这一层是整个驱动里唯一跟具体MCU绑死的部分,从STM32换到ESP32,只改这里。命令层把SX126x数据手册里的每一条命令封装成函数:参数拼装、Busy等待、发送opcode、解析返回值,都在这一层完成。Radio抽象层则面向协议栈业务,提供Init、Send、Receive、Wakeup这类高层接口,LoRaWAN协议栈里的radio驱赶通常就是这一层要实现的接口。

很多人在移植官方LoRaMac-node的radio驱动时找不到方向,就是因为没分清这三层:改频率参数去改了Radio抽象层,改SPI时钟却去动了命令层,最后出问题回溯困难。下面的代码是平台抽象层里最基础的SPI字节收发,所有命令都建立在它之上。

// sx126x_hal.c 平台相关实现,以STM32的HAL库为例 uint8_t SX126xHAL_ReadWriteByte(uint8_t byte) { uint8_t rx = 0; // 单字节SPI收发,SCK速率先压到1MHz,链路稳定后再往上提 HAL_SPI_TransmitReceive(&hspi2, &byte, &rx, 1, HAL_MAX_DELAY); return rx; }

这里有一个参数要留意:hspi2对应的SPI外设,CPOL和CPHA必须匹配SX126x手册要求的SPI模式4。极性反了,命令发出去芯片根本不会理你,而Busy引脚可能一直拉低,让你误以为是芯片坏了。SCK速率建议初始化阶段控制在1MHz,SX126x的SPI标称能到16MHz,但走线长、电平转换器慢时高速跑容易丢字节,先低速跑通再提速是射频驱动移植的基本操作。

2.3 一条命令的完整生命周期:读Busy、发opcode、收Status

理解SX126x驱动的核心,是搞懂一条命令从软件到射频芯片之间的完整旅程。以设置待机模式为例,整个过程分成三个严格的阶段。

第一阶段,主机把NSS拉低,表示“我要占用SPI总线”。第二阶段,主机必须等到Busy引脚变为低电平,才能发送命令。很多初学者忽略这个步骤,直接把操作码写进SPI,运气好能成,但在芯片内部状态机还忙时,这条命令会被直接吞掉。第三阶段,主机发送操作码和参数,随后NSS拉高,命令进入执行队列,芯片会在Busy引脚上出现一段高电平脉冲,表示正在执行。

// sx126x_cmd.c 命令层:写命令的统一入口 static void SX126x_WriteCommand(SX126x_t *radio, uint8_t opcode, uint8_t *params, uint16_t len) { // 1. 拉低NSS,占用SPI总线 SX126xHAL_SetNss(0); // 2. 等待Busy释放,超时保护防止死等 uint32_t timeout = 10000; while (SX126xHAL_GetBusy()) { if (--timeout == 0) break; } // 3. 发送操作码和参数 SX126xHAL_ReadWriteByte(opcode); for (uint16_t i = 0; i < len; i++) { SX126xHAL_ReadWriteByte(params[i]); } // 4. 释放总线 SX126xHAL_SetNss(1); }

很多人移植驱动时卡在“等Busy”这一步。Busy引脚必须接MCU的一个普通GPIO输入,不能悬空,也不能直接接地。因为SX126x上电瞬间总线状态不稳定,上拉电阻和足够长的等待时间缺一不可。上面的超时保护也不是随便写的:射频芯片因为校准失败或电源跌落而异常时,Busy可能永远拉高,没有超时机制的话整个系统直接死机。养成每个等待都加超时的习惯,能给后面省下大量排错时间。

3. 在STM32上跑通第一版SX126x驱动:SPI、初始化和收发例程

3.1 管脚与SPI配置:时钟从1MHz起步,DIO映射别接错

SX126x需要主机连接的引脚比SX127x少很多,典型配置是六根:NSS、SCK、MOSI、MISO、RST、BUSY,外加一个DIO1用于中断通知。DIO2和DIO3在大多数应用中不接MCU,DIO2被配置为射频开关控制后输出给外部射频前端,DIO3则用于控制TCXO供电。这正是SX126x省引脚的地方,但也埋了一个坑:如果板子上DIO2没接到射频开关,而驱动里又把DIO2当普通GPIO去初始化,射频通路死活打不开。

用STM32CubeMX配置SPI时,我会把SPI模式设为Mode 0(CPOL=0,CPHA=1在英文手册对应的是SPI Mode 0),数据宽度8位,高位在前。MISO上最好加一个10kΩ上拉,因为部分模块在SPI空闲时会输出高阻,上拉能让电平稳定。初始化顺序上,先初始化GPIO和SPI外设,再拉低RST做复位,然后才能开始发命令。

// main.c 硬件初始化片段 void lora_hw_init(void) { // MX_SPI2_Init 里已经把 SCK=1MHz 配置好 // NSS、BUSY、RST、DIO1 全部配置为 GPIO // 复位脉冲:低电平保持10ms,保证芯片完成上电复位 HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET); HAL_Delay(10); HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET); HAL_Delay(10); }

这里的关键是RST引脚复位后至少要等10ms再操作SPI,SX126x内部上电复位和晶振起振需要时间。有些模块用的是TCXO版本,复位后还需要额外延时让DIO3控制的电源稳定输出。硬件上如果RST引脚没有RC延时电路,软件里的10ms延时就是唯一保障,别省。

3.2 初始化序列:复位、校准、调制参数和包参数的先后

SX126x的初始化顺序在数据手册里没有单页汇总,得从命令列表里自己整理。实践下来稳定的顺序是:复位、SetStandby、Calibrate校准、SetPacketType选LoRa、SetRfFrequency设频率、SetModulationParams设调制参数、SetPacketParams设包参数、SetDio2AsRfSwitchCtrl开启射频开关控制,最后SetTxParams设发射功率。

// radio_init.c 完整的初始化序列 void lora_radio_init(uint32_t freq_hz) { SX126x_t radio = {0}; // 1. 进入待机模式,使用RC时钟,功耗低且启动快 SX126x_SetStandby(&radio, SX126x_STDBY_RC); // 2. 校准全部模块,官方推荐0x7F表示全部校准 SX126x_Calibrate(&radio, 0x7F); // 3. 选择LoRa调制,SX126x的PacketType决定后面所有参数含义 SX126x_SetPacketType(&radio, PACKET_TYPE_LORA); // 4. 配置射频频率,这里传的是实际频率值,寄存器换算在内部做 SX126x_SetRfFrequency(&radio, freq_hz); // 5. 调制参数:SF7, BW125kHz, CR4/5, 不开低数据率优化 ModulationParams_t mod = { .SpreadingFactor = LORA_SF7, .Bandwidth = LORA_BW_125, .CodingRate = LORA_CR_4_5, .LowDatarateOptimize = 0 }; SX126x_SetModulationParams(&radio, &mod); // 6. 包参数:8字节前导码、显式包头、开启CRC、IQ不反转 PacketParams_t pkt = { .PreambleLength = 8, .HeaderType = LORA_PACKET_EXPLICIT, // 显式包头方便调试 .PayloadLength = 16, // 固定长度模式下有意义 .CrcOn = 1, .InvertIQ = 0 }; SX126x_SetPacketParams(&radio, &pkt); // 7. DIO2复用为射频开关控制,很多翻车事故就是漏了这一步 SX126x_SetDio2AsRfSwitchCtrl(&radio, true); }

几个参数要重点解释。Calibrate命令会在Busy上产生一个较长的低电平等待,有些实现不等Calibrate完成就继续配参数,导致后续命令被丢弃。SetPacketType放在SetRfFrequency之前没有严格强制关系,但放前面可以避免不同调制下频率配置语义的混淆。SetDio2AsRfSwitchCtrl在复位后必须重新配置,因为芯片复位会把该选项清回默认值。PacketParams里的PayloadLength在显式包头模式下只是预留值,实际长度由每包自带的头部字段决定,这对后面调试非常友好。

3.3 最小收发例程:发送与接收的临界条件

初始化完成后,发送一包数据和接收一包数据的代码并不复杂。发送的完整动作是:把payload写入FIFO,配置DIO1中断源为TX_DONE,调用SetTx,再等中断。

// lora_send.c 发送一包数据,阻塞等待发送完成 uint8_t lora_send(uint8_t *buf, uint8_t len) { // 1. 数据写入芯片FIFO,从偏移0开始 SX126x_WriteBuffer(&radio, 0, buf, len); // 2. 清掉历史中断,防止残留标志导致误判 SX126x_ClearIrqStatus(&radio, SX126x_IRQ_TX_DONE); // 3. 把DIO1映射为TX_DONE中断 SX126x_SetDioIrqParams(&radio, SX126x_IRQ_TX_DONE, SX126x_IRQ_TX_DONE, 0, 0); // 4. 启动发送,timeout=0表示无超时限制 SX126x_SetTx(&radio, 0); // 5. 轮询DIO1引脚,等待发送完成 while (!HAL_GPIO_ReadPin(DIO1_GPIO_Port, DIO1_Pin)) { // 实际产品建议加超时,防止射频芯片异常死等 } return 0; }

SetTx的timeout参数很多人理解错,这里单位是符号时间(symbol time),不是毫秒,也不是字节数。LoRa模式下符号时间由SF和BW共同决定。0表示不启动超时,发送一旦开始就一直进行到结束,适合发送固定短包。如果你在协议栈里看到有人把timeout填成0xFFFFFF,那是给接收用的持续监听模式,两者语义不同,混用会出奇怪问题。

接收最小实现,是把DIO1映射为RX_DONE,然后调用SetRx并传入接收超时。

// lora_recv.c 接收一包数据,单次接收模式 uint8_t lora_recv(uint8_t *buf, uint8_t *len) { // 1. 清中断,映射RX_DONE SX126x_ClearIrqStatus(&radio, SX126x_IRQ_RX_DONE | SX126x_IRQ_CRC_ERROR); SX126x_SetDioIrqParams(&radio, SX126x_IRQ_RX_DONE, SX126x_IRQ_RX_DONE, 0, 0); // 2. 启动单次接收,timeout=0表示只收一次,收不到就回到STDBY SX126x_SetRx(&radio, 0); // 3. 等DIO1变高 while (!HAL_GPIO_ReadPin(DIO1_GPIO_Port, DIO1_Pin)) {} // 4. 读取中断状态,判断是正常接收还是CRC错误 uint16_t irq = SX126x_GetIrqStatus(&radio); if (irq & SX126x_IRQ_CRC_ERROR) return 1; // 5. 从FIFO读出数据 SX126x_ReadBuffer(&radio, 0, buf, len); return 0; }

这个接收函数的关键边界在于:SetRx的timeout=0是“单次接收”模式,不是“无限接收”。很多从SX127x迁过来的代码会习惯性把接收放在一个while(1)循环里,每次处理完再调用一次SetRx。而在SX126x上,单次接收收到一包后芯片自动回到待机,必须再次调用SetRx才能继续收下一包。如果想一直挂在接收状态,要把timeout设置为0xFFFFFF,但代价是芯片无法自动进入休眠,功耗会明显高出几个量级。

4. 把射频参数调到链路能通:频率、调制、功率的配置顺序与取值

4.1 频率怎么算:0.953674Hz/LSB和寄存器反推

SX126x的SetRfFrequency命令接收的是一个与频率成正比的整数值,不是频率本身。芯片内部用32MHz晶振作为参考,经过分频和倍频后生成射频频率,寄存器值和实际频率的关系如下:

// freq_calc.c 频率寄存器换算 uint32_t freq_to_reg(uint32_t freq_hz) { // SX126x参考晶振32MHz,寄存器2^25计数范围内对应整个频段 // 1 LSB 对应的频率是 32e6 / 33554432 = 0.953674 Hz double reg = (double)freq_hz * 33554432.0 / 32000000.0; return (uint32_t)(reg + 0.5); // 四舍五入,减少频偏 }

反过来,寄存器值换算成实际频率是 reg * 32e6 / 33554432。很多人把寄存器值按1Hz/LSB去理解,设置433MHz时算出了整整偏了接近46Hz的寄存器值,这个误差在调试窄带通信时足以让接收端灵敏度下降几个dB。实际部署中,更常见的问题是两边模块频率配置不一致:一个433000000Hz按整数算,另一个433000000.2Hz按浮点算,发射频率误差虽小,但如果接收端用了高Q值滤波器,灵敏度损失在临界距离上会被放大。

设置频率时还有一点容易被忽略:必须等芯片进入STDBY模式后再写频率,不要等芯片正在收发过程中去改。典型做法是每次频率变更前先调用SetStandby,改完后再根据需要进入发送或接收状态。这与SX127x的操作习惯一致,但很多SX126x的命令实现里少了这步,导致频率写在错误的状态机上。

4.2 调制参数组态:SF/BW/CR的取值对链路预算的影响

调制参数是LoRa物理层的核心,SX126x里通过SetModulationParams一次配齐:扩频因子、带宽、编码率,外加低数据率优化开关。常见的取值组合如下。

场景扩频因子SF带宽BW编码率CR空闲信道数据率适用距离
密集城区快速上报7125kHz4/5约5.4kbps1~2km视距
郊区综合覆盖9125kHz4/5约1.7kbps3~5km
远距离低速率12125kHz4/8约0.3kbps5km以上

SF增大一倍,有效数据率约减半,但接收灵敏度提升约3dB。带宽从125kHz加倍到250kHz,灵敏度反而下降约3dB,因为噪声基底变宽。CR是纠错编码,4/5对应每个4bit数据附加1bit冗余,4/8冗余更高,抗干扰更强但吞吐更低。这三个参数在通信双方的驱动里必须完全一致,否则接收方解调器无法捕获信号。

低数据率优化(LowDatarateOptimize)是SX126x里一个很容易配错的参数。当符号时间超过约16ms时,LoRa调制中原本用于定时的前导码会受晶振漂移影响,此时必须开启这个优化。判定条件不复杂:SF12、BW125kHz时符号时间约32.8ms,必须开;SF12、BW500kHz时符号时间约8.2ms,可以关。很多环境监测节点在SF12+BW125配置下没开LDO,导致室内短距离通信正常、户外远距离丢包率暴涨,就是这个参数在作怪。

// 计算符号时间,用于决定LowDatarateOptimize double symbol_time_us = (double)(1UL << spreading_factor) / bandwidth_hz * 1000000.0; if (symbol_time_us > 16000) { mod_params.LowDatarateOptimize = 1; // 符号时间大于16ms必须开 } else { mod_params.LowDatarateOptimize = 0; }

这个参数需要在通信双方都配成相同值,否则接收端会因为在错误的定时窗口内采样而完全收不到。低于10kbps的场景我都建议按上面这个公式现场算,别凭经验拍脑袋。

4.3 发射功率别信寄存器:PA选择与实测校准

SetTxParams命令里的功率参数单位是dBm,取值范围-9dBm到+22dBm,但芯片内部实际配置的是PA的驱动幅度。SX126x有两组PA:PA_LP低功率模式用于-9dBm到+14dBm,PA_HP高功率模式用于+14dBm以上。选错PA会导致输出功率严重失真,比如在PA_HP下设置+10dBm,实际量测可能达到+17dBm,发射谱带外杂散超标。

// tx_power.c 根据功率选择PA并设置 void lora_set_tx_power(int8_t dbm) { uint8_t pa_sel = 0; // 默认PA_LP uint8_t params[2] = {0}; if (dbm > 14) { pa_sel = SX126x_PA_HP; // 高功率必须选HP } params[0] = pa_sel; params[1] = (uint8_t)dbm; SX126x_SetTxParams(&radio, params[0], params[1]); }

实际部署中最大的坑还不是PA选择,而是“设置成功≠实测达标”。驱动里写+20dBm,频谱仪上看可能是+18dBm或者+21dBm,这与板子的射频匹配网络、天线阻抗、供电电压都有关系。尤其是电池供电的LoRa节点,电池电压跌到3.3V以下时,PA的输出会明显压缩,标称22dBm根本推不上去。

正确的做法是在开发阶段就用频谱仪做一次功率校准表,把“驱动参数→实测功率”整理成一张表贴在产品文档里。量产时驱动里的功率参数只作为期望值,真正以频谱仪或功率计的量测为准。没有频谱仪时,至少用带峰值检波的接收模块对比RSSI,也能发现明显偏差。

5. SX126x驱动移植避坑指南:5个烧板废时间的真实案例

5.1 DIO2没配成射频开关,天线端口量不到功率

现象:初始化明明成功了,TX_DONE中断也正常触发,但频谱仪在天线端口只能看到非常弱的泄漏信号,输出功率比设置值低了20dB以上。

原因:SX126x的输出级后面通常接了一个射频开关,用于在发射和接收之间切换通路。这个开关由DIO2控制,但DIO2默认被配置为通用中断引脚。如果不调用SetDio2AsRfSwitchCtrl使能,射频开关始终处于接收通路,发射信号被完全隔离。

解决:在初始化序列里加入SX126x_SetDio2AsRfSwitchCtrl(&radio, true),必须在每次复位后重新调用,因为复位会把该配置恢复到默认状态。

5.2 跳过Busy握手,程序卡死在“等状态机”

现象:程序运行到第一次调用SetStandby或Calibrate时停滞,调试器显示代码卡在一个while循环里。换一片芯片也一样,且与电压无关。

原因:SX126x在执行部分命令时,内部状态机需要持续几毫秒到十几毫秒的时间。早期移植时如果只发送操作码而不等待Busy释放,芯片会直接丢弃后续命令。更隐蔽的是NSS时序问题:NSS拉高必须给足时间,太快的连续访问会让芯片误判帧边界。

解决:严格按照“拉低NSS→等Busy低→发数据→拉高NSS”的顺序执行。增加超时保护,超时后打印错误标志位而不是死等。SPI时钟先降到1MHz验证时序,排除走线干扰后再提速。

5.3 频率计算差一点点,扫频仪看到偏了400kHz

现象:两块模块配置同样的“433000000”,用频谱仪测量发射中心频率却是433.4MHz左右,误差达到400kHz量级。接收端开同样带宽偶尔能收到,但RSSI比标称差很多。

原因:直接把十进制频率值当成寄存器值写入SetRfFrequency。寄存器1LSB对应约0.953674Hz,400kHz误差意味着寄存器值差了约42万,这正好是把频率和寄存器值混为一谈的典型数量级。

解决:用4.1节的公式换算,并且在驱动源码里把换算函数单独抽出,加上单元测试。每次改频率,都用这个函数计算后再下发,不要手工预计算。

5.4 收发两端的IQ极性不一致,CRC永远过不了

现象:节点A和节点B用同一套驱动代码,A发给B正常,反过来B发给A时CRC全部失败。检查SF、BW、CR、频率全部一致,无线环境也干净。

原因:LoRa调制中有IQ极性反转机制,用于区分上行和下行链路,防止同频自干扰。驱动里的InvertIQ参数在收发两端不一致时,接收端的解调星座图翻转,数据符号能解出来但CRC校验码对不上。

解决:自组网场景中,把收发两端的InvertIQ固定为0;LoRaWAN场景严格按协议设置上行0下行1。改动后两端必须重新上电同时生效。

5.5 FixedLength模式的包长度错配,收到一堆空包

现象:使用固定长度包模式,PayloadLength配成16,但上层协议每一包实际长度在8到32字节之间变化。接收端经常收到CRC错误,或者RX_DONE中断触发但读出来的数据全是0xFF。

原因:固定长度模式下,芯片严格按照PayloadLength配置的字节数接收。发送端实际发送的包长小于配置值时,接收端会继续在噪声里搜数据补满长度,最终CRC必然错误。显式包头模式则每包自带长度信息,不存在这个问题。

解决:开发调试阶段无脑用显式包头模式,让芯片自动解析长度。等协议完全固定、每包长度确实不变时再改固定长度,能在极端低功耗场景省下几个字节的解调时间。

6. 让节点学会“先听后发”:用CAD把接收功耗降下来

6.1 CAD参数怎么设:符号数、带宽和灵敏度取舍

CAD(Channel Activity Detection)是SX126x独有的信道活动检测功能,芯片在极低功耗下监听前导码,检测到LoRa信号才唤醒主机进入正式接收。CAD一次的电流在微安级,相比持续RX的十几毫安,差距是数量级的。SetCadParams命令的核心参数是CAD符号数,通常配2到4个符号:配得太少,误检率升高;配得太多,检测周期变长,会漏掉短前导码信号。

// cad_init.c 初始化CAD参数 uint8_t cad_params[7]; cad_params[0] = 4; // CAD符号数,4个符号兼顾灵敏度和误检 cad_params[1] = LORA_SF7; // 必须与正式接收的SF一致 cad_params[2] = LORA_BW_125; // 带宽一致 cad_params[3] = LORA_CR_4_5; // 编码率一致 cad_params[4] = 0; // 无CAD超时 cad_params[5] = 0; // 保留参数 SX126x_SetCadParams(&radio, cad_params); SX126x_SetDioIrqParams(&radio, SX126x_IRQ_CAD_DETECTED, SX126x_IRQ_CAD_DETECTED | SX126x_IRQ_CAD_OK | SX126x_IRQ_CAD_TIMEOUT, 0, 0);

CAD的参数有一个强制要求:SF和BW必须与即将接收的报文完全一致,否则检测不到。编码率影响较小,但建议保持一致。如果系统里有两种SF配置,比如SF7和SF10交替使用,CAD只能对其中一种生效,需要在切换SF后重新调用SetCadParams。

6.2 用DIO1中断处理CAD结果:不靠轮询省下主控时间

CAD完成后芯片会通过DIO1上报结果,主控在中断里判断到底是检测到信号还是超时。检测到信号就立刻调用SetRx进入正式接收,检测超时则直接回Sleep等待下一轮唤醒,全程不需要主控轮询。

// cad_irq.c DIO1中断里处理CAD结果 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == DIO1_Pin) { uint16_t irq = SX126x_GetIrqStatus(&radio); if (irq & SX126x_IRQ_CAD_DETECTED) { // 检测到LoRa前导码,立刻开接收窗口 SX126x_SetRx(&radio, 0); } else { // 没信号,回睡眠等待下一个唤醒周期 SX126x_SetSleep(&radio, SX126x_SLEEP_RC_XOSC); } } }

这个流程是典型低功耗LoRa节点的收包策略:主控定时休眠,周期性唤醒做一次CAD,没有下行数据就继续睡。配合周期计时器,一套双向通信节点的平均电流可以压到10uA以下。我最早给环境监测节点做这套逻辑时,就是漏了DIO2的射频开关配置,CAD检测正常却收不到实际数据,最后用万用表量射频开关的供电才定位到问题。那之后我养成了一个习惯:每一版SX126x驱动跑通的第一件事,先做一个回读自检,确认Busy握手和命令通路正常再调射频,别直接拿实物去怼距离。希望这些思路和代码能帮你省下当初我交的那一天。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询