从零玩转LoRa
2026/8/31 4:52:04 网站建设 项目流程

前阵子有个项目需求——要在两栋楼之间传数据,距离大概一两公里,中间隔着几堵墙和一个停车场。第一反应是用WiFi,但实际上其中一个楼没有WiFi可以接入,后来想用4G模块,但这个距离似乎太浪费了,并且相比之下使用成本也更高一点。蓝牙更不用想,根本不合适。

然后一个朋友让我试试LoRa。

我之前只听说过LoRa这个词,知道是个远距离的无线技术,但从来没实际用过。上网一搜,好家伙,模组品牌一大堆,芯片型号一堆数字字母组合,看得人头大。后来在找模组的时候,发现了深圳大夏龙雀科技有一款DX-LR20-900M22S的LoRa模组。

研究了几天,从一无所知到成功跑通两板互发数据,整个过程踩了不少坑,但也学到了很多东西。这篇文章就把我的开发经验分享出来,希望对同样想入门LoRa的朋友有所帮助。


一、LoRa到底是什么?

1.1 一句话解释LoRa

LoRa是一种低功耗局域网无线通信技术,特点是传得远、功耗低、穿透强,代价是传输速率低

打个比方:WiFi就像快递的"当日达",速度快但覆盖范围小;蓝牙像是"闪送",更近更快但范围更小;而LoRa就像邮政的"平信",虽然慢了点,但能寄到很远的地方,而且发信人几乎不费什么力气。

1.2 LoRa的核心优势

特性

LoRa

WiFi

蓝牙(BLE)

通信距离

2~15km

50~100m

10~50m

穿透能力

很弱

功耗

极低

传输速率

0.3~50kbps

150~600Mbps

1~2Mbps

适用场景

远距离低频数据

高速网络

近距设备互联

LoRa不适合传视频、图片这种大数据量的东西,但对于传感器数据采集、远程开关控制、定位信标这类"小数据、远距离、低功耗"的场景,简直是量身定制。

1.3 LoRa调制原理

LoRa用了三种核心技术来实现远距离传输:

① 扩频技术

普通无线信号就像一个人在大喊,声音大但容易被干扰。LoRa的扩频技术像是把一句话拆成一首歌,用不同的音调来传递信息。即使环境噪声很大,也能从"旋律"中辨认出有用信号。

具体来说,LoRa使用线性调频信号—— 频率从低到高或从高到低线性变化。每个数据符号对应一个特定的起始频率偏移,接收端通过比对收到的Chirp与参考Chirp的频率差来解码数据。

② 扩频因子

扩频因子决定了每个数据符号占用的时间。SF越大,每个符号持续越久,抗干扰能力越强,传得越远,但速率越低。可以理解为"说得越慢,对方越能听清"。

LLCC68芯片支持的SF范围:

带宽(BW)

支持的SF范围

125 kHz

SF5 ~ SF9

250 kHz

SF5 ~ SF10

500 kHz

SF5 ~ SF11

注意:LLCC68与Semtech SX1262的驱动完全兼容,但LLCC68不支持SF12。这是两者唯一的区别,移植代码时需要注意。

③ 前向纠错编码

LoRa在数据中加入了冗余校验位,即使部分数据在传输中被噪声干扰,接收端也能自动纠正错误,不需要重传。这就像你写信时故意把关键词写两遍,即使墨水晕了一个字,对方也能猜对。

1.4 关键参数解读

开发LoRa应用,这几个参数必须理解:

  • 频率:DX-LR20工作在850~930MHz。

  • 带宽(BW):信号占用的频率宽度。带宽越窄,灵敏度越高,但速率越低。LR20推荐使用125kHz

  • 扩频因子(SF):如前所述,SF越大越远越慢。本项目使用SF9,在距离和速率间取得平衡

  • 发射功率:LR20最大+22dBm,功率越大传得越远但越耗电

  • 接收灵敏度:-125dBm,这个数字越负代表能接收的信号越微弱,也就是"听力"越好

简单算一下:22dBm发射功率 - (-125dBm接收灵敏度) = 147dB的链路预算。链路预算越大,传输距离越远,LR20手册标注空旷环境可达8公里


二、DX-LORA-LR20-900 介绍

LR20模组参数一览

参数

规格

芯片

LLCC68

频段

850~930MHz

调制方式

LoRa

发射功率

+22dBm

通信距离

8km(空旷环境)

工作电压

1.8~3.7V

发射电流

100mA

接收电流

6.5mA

待机电流

180nA

FIFO

256Byte

SPI速率

0~10Mbps

模块PCB设计

原理图

实拍图

说实话,6.5mA的接收电流和180nA的待机电流在远距离通信模组里算非常优秀的。这意味着如果用电池供电,模组可以长时间"监听"而不耗电。


三、快速上手:30分钟跑通通信

3.1 准备工作

你需要准备:

  1. 两块DX-LR20-900M模块(LoRa通信至少需要一发一收)

  2. 两根Type-C数据线

  3. 两根天线(SMA或IPEX均可,取决于你接哪个座子)

  4. PC电脑

  5. Keil5开发环境 + STM32F1xx HAL库

  6. 串口助手软件(如SSCOM)

3.2 引脚连接

LR20模组通过SPI与STM32通信,引脚分配如下:

功能

STM32引脚

说明

NSS

PA4

SPI片选

SCK

PA5

SPI时钟

MISO

PA6

SPI数据输出(模组→MCU)

MOSI

PA7

SPI数据输入(MCU→模组)

RESET

GPIO

模组复位

BUSY

GPIO

模组状态指示

DIO1

GPIO

中断引脚(收发完成通知)

TX_EN

GPIO

发射使能(射频开关)

RX_EN

GPIO

接收使能(射频开关)

串口引脚:PA9(TX) / PA10(RX),默认波特率9600。

3.3 默认程序工作流程

开发板出厂程序的核心逻辑非常清晰:

  1. 上电初始化 → LoRa参数配置(BW=125kHz, SF=9, 功率22dBm)→ 进入接收模式

  2. 每隔1秒自动发送一次0xAA测试数据

  3. 收到数据后通过串口打印,并触发LED闪烁

  4. 串口输入的数据也可以通过LoRa发送出去

两块板子同时运行,就能实现双向通信。


四、代码解析:从main.c开始读懂整个工程

4.1 main.c —— 程序入口

先看main()函数的初始化流程:

int main(void) { HAL_Init(); SystemClock_Config(); // 初始化LED - PB1 __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = LED_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct); LED_OFF(); // 初始化串口接收队列 UartRxQueue.front = 0; UartRxQueue.tail = 0; UartRxQueue.count = 0; pUart1RxQueue = (pQueue)&UartRxQueue; UsartInit(9600); // 串口初始化 TimerInit(); // 定时器初始化 TIM3_Init(); // 串口超时定时器 SPI_Init(); // SPI初始化 gpio_init(); // LoRa控制引脚初始化 // LoRa初始化 LoraInit(); DIO1_Init(); // DIO1中断初始化 // 启动时发送一次0xAA uint8_t sendData = 0xAA; LoraDataSend(&sendData, 1); lastSendTime = HAL_GetTick(); // 开始串口接收 HAL_UART_Receive_IT(&husart, &pdata, sizeof(pdata)); for(;;) { Data_Processing(); // 处理串口数据 DX_Lora_RadioIrqProcess(); // 处理LoRa中断 ProcessLoraRxData(); // 处理LoRa接收数据 ControlLEDBlink(); // LED闪烁控制 // 每秒自动发送0xAA if (!stopSending) { uint32_t currentTime = HAL_GetTick(); if (currentTime - lastSendTime >= sendInterval) { lastSendTime = currentTime; uint8_t sendData = 0xAA; LoraDataSend(&sendData, 1); } } } }

整个程序是典型的前后台架构:中断负责数据接收和LoRa事件通知,主循环负责业务逻辑处理

4.2 LoRa初始化 —— LoraInit()

这是整个驱动的核心,逐行解析:

void LoraInit(void) { g_lora_test_rxs = false; g_lora_tx_done = true; // 1. 硬件复位 + 唤醒 sx126x_reset(NULL); sx126x_wakeup(NULL); // 2. 进入待机配置模式 sx126x_set_standby(NULL, SX126X_STANDBY_CFG_RC); sx126x_set_standby(NULL, SX126X_STANDBY_CFG_XOSC); // 3. 选择DC-DC电压调节器模式 sx126x_set_reg_mode(NULL, SX126X_REG_MODE_DCDC); // 4. FIFO读写地址复位 sx126x_set_buffer_base_address(NULL, 0x00, 0x00); // 5. 设置数据包类型为LoRa sx126x_set_pkt_type(NULL, SX126X_PKT_TYPE_LORA); // 6. 设置晶振负载电容 sx126x_set_trimming_capacitor_values(NULL, 0x4, 0x2f); // 7. LoRa调制参数配置 sx126x_mod_params_lora_t params; params.bw = SX126X_LORA_BW_125; // 带宽125kHz params.sf = SX126X_LORA_SF9; // 扩频因子9 params.cr = SX126X_LORA_CR_4_6; // 编码率4/6 params.ldro = 0x00; // 低数据率优化关闭 sx126x_set_lora_mod_params(NULL, &params); // 8. 数据包参数配置 sx126x_pkt_params_lora_t params2; params2.crc_is_on = 0; // CRC关闭 params2.invert_iq_is_on = 0; // IQ不反转 params2.pld_len_in_bytes = 0xff; // 负载长度 params2.header_type = SX126X_LORA_PKT_EXPLICIT; // 显式包头 params2.preamble_len_in_symb = LORA_PREAMBLE_LENGTH; // 前导码长度 sx126x_set_lora_pkt_params(NULL, &params2); // 9. 功率放大器配置 sx126x_pa_cfg_params_t params3; params3.pa_duty_cycle = 0x04; params3.hp_max = 0x07; params3.device_sel = 0x00; params3.pa_lut = 0x01; sx126x_set_pa_cfg(NULL, &params3); // 10. 开启DIO1中断:接收完成 + 发送完成 sx126x_set_dio_irq_params(NULL, SX126X_IRQ_RX_DONE | SX126X_IRQ_TX_DONE, SX126X_IRQ_RX_DONE | SX126X_IRQ_TX_DONE, SX126X_IRQ_NONE, SX126X_IRQ_NONE); sx126x_clear_irq_status(NULL, SX126X_IRQ_ALL); // 11. 设置发射功率22dBm sx126x_set_tx_params(NULL, 22, SX126X_RAMP_3400_US); // 12. 设置载波频率 sx126x_set_rf_freq(NULL, LORA_FRE); // 13. 进入接收模式 LoraOpenRXMode(LORA_SX126x_SYMBOL_TIMEOUT); }

这13步是LLCC68、SX1262系列芯片的标准初始化流程。每一步都对应芯片手册中的一个寄存器配置命令,通过SPI发送给模组。

参数选择心得:BW=125kHz + SF=9是我多次测试后选的组合。SF越大传得越远但速率越低,SF=9在8公里范围内都能稳定通信,同时保持约1757bps的速率,对于传输传感器数据完全够用。

4.3 发送与接收流程

发送数据

void LoraDataSend(uint8_t *data, uint8_t len) { TxEn(); // 切换射频开关到发射 set_LoraPacketParams(len); // 设置数据包长度 sx126x_write_buffer(NULL, 0x00, data, len); // 写入FIFO sx126x_set_tx(NULL, 6000); // 启动发送,超时6秒 g_lora_tx_done = false; // 标记发送中 sx1262SetOperatingMode(MODE_TX); // 设置状态机 }

发送完成后,DIO1引脚会产生中断,触发OnTxDone()回调,自动切换回接收模式。

接收数据

void OnRxDone(uint8_t* payload, uint16_t size, int16_t rssi, int8_t snr) { g_lora_tx_done = true; g_lora_test_rxs = true; OnLoraRxProcess(payload, size); // 传给上层处理 LoraOpenRXMode(LORA_SX126x_SYMBOL_TIMEOUT); // 重新进入接收 }

接收中断触发后,从FIFO读取数据,传给上层的OnLoraRxProcess()回调:

void OnLoraRxProcess(uint8_t* payload, uint16_t size) { if (size > 0 && size < 256) { memcpy(loraRx.data, payload, size); loraRx.length = size; loraRx.ready = 1; // 标记数据就绪 } }

主循环中检测到loraRx.ready后处理数据:

void ProcessLoraRxData(void) { if (loraRx.ready) { printf("LoRa Received: Len=%d, Data:", loraRx.length); for (int i = 0; i < loraRx.length; i++) { printf(" %02X", loraRx.data[i]); } printf("\n\r"); if (loraRx.length > 0 && loraRx.data[0] == 0xAA) { printf("Received 0xAA, starting LED blink\n\r"); isBlinking = 1; // 触发LED闪烁 lastBlinkTime = HAL_GetTick(); stopSending = 0; } loraRx.ready = 0; } }

4.4 中断处理机制

LR20模组通过DIO1引脚通知MCU"有事发生了"。整个中断处理流程如下:

  1. DIO1上升沿触发 → 外部中断ISR中设置IrqFired = true,读取中断状态到radioFlag

  2. 主循环调用DX_Lora_RadioIrqProcess()检查中断标志

  3. 根据radioFlag的位判断是哪种事件(发送完成/接收完成/超时/CRC错误等)

  4. 执行对应的回调函数

void DX_Lora_RadioIrqProcess(void) { if(IrqFired == true) { IrqFired = false; if((radioFlag & SX126X_IRQ_TX_DONE) == SX126X_IRQ_TX_DONE) { OnTxDone(); // 发送完成 } if((radioFlag & SX126X_IRQ_RX_DONE) == SX126X_IRQ_RX_DONE) { // 读取接收数据 sx126x_get_rx_buffer_status(NULL, &offset); sx126x_read_buffer(NULL, offset.buffer_start_pointer, radioRxbuff, offset.pld_len_in_bytes); sx126x_get_lora_pkt_status(NULL, &RadioPktStatus); OnRxDone(&radioRxbuff[0], offset.pld_len_in_bytes, RadioPktStatus.rssi_pkt_in_dbm, RadioPktStatus.snr_pkt_in_db); } // ... 其他中断处理 } }

五、驱动移植:换一颗MCU怎么改

大夏龙雀提供的驱动文件结构清晰,移植到其他MCU只需要改两个文件。

5.1 工程文件结构

├── Core/ ← STM32 HAL库核心文件 ├── Driver/ ← 用户编写的外设驱动 ├── LR_driver/ ← LoRa射频驱动 │ ├── src/ │ │ ├── sx126x.c ← LoRa芯片操作函数 │ │ └── sx126x_hal.c ← 移植核心:SPI交互层 │ └── UserConfig.c ← 引脚配置 ├── Main/ ← main.c + 配置文件 ├── queue/ ← 串口数据队列 ├── SDK/ ← CMSIS + HAL库 └── Project/ ← Keil工程文件

5.2 只需修改两个文件

文件1:sx126x_hal.c— SPI交互层

这个文件里封装了底层SPI通信函数。如果你换到ESP32、Arduino或其他平台,只需要把里面的SPI读写改成对应平台的API:

// 需要移植的3个核心函数: sx126x_hal_reset() // 拉低RESET引脚复位 sx126x_hal_wakeup() // 拉低NSS唤醒 sx126x_hal_write() // SPI写操作 sx126x_hal_read() // SPI读操作

以ESP32为例,移植时改成:

sx126x_hal_status_t sx126x_hal_write(sx126x_hal_context_t *hal, const uint8_t *command, uint16_t command_len, const uint8_t *data, uint16_t data_len) { digitalWrite(NSS_PIN, LOW); // 片选拉低 spi_transfer(command, command_len); // 发命令 spi_transfer(data, data_len); // 发数据 digitalWrite(NSS_PIN, HIGH); // 片选拉高 return SX126X_HAL_STATUS_OK; }

文件2:UserConfig.c— 引脚配置

修改引脚定义为你自己MCU的GPIO:

// 原始定义 #define LCC68_BUSY_PIN GPIO_PIN_0 #define LCC68_BUSY_PORT GPIOA #define LCC68_NRST_PIN GPIO_PIN_1 #define LCC68_NRST_PORT GPIOA // ... // 移植到ESP32后 #define LCC68_BUSY_PIN 4 // GPIO4 #define LCC68_NRST_PIN 5 // GPIO5 // ...

其他文件不用动sx126x.c里的寄存器操作函数是纯逻辑代码,跟具体MCU无关。

5.3 移植到其他MCU的检查清单

检查项

说明

SPI时序

确保CPOL=0, CPHA=0(Mode 0),最高10MHz

电平匹配

LR20是3.3V,5V MCU需要电平转换

NSS控制

软件控制CS引脚,不要用硬件NSS

DIO1中断

配置为上升沿触发的外部中断

BUSY检测

发送SPI命令前必须检查BUSY为低

RESET

低电平复位,至少保持200μs


六、通信实测:到底能传多远?

6.1 测试环境

条件

配置

频率

920MHz

带宽

125kHz

扩频因子

SF9

编码率

4/6

发射功率

22dBm

天线

SMA胶皮天线

天线高度

1.5m(手持)

测试数据

0xAA,每秒发送一次

6.2 测试结果

场景

距离

RSSI

SNR

丢包率

空旷操场

500m

-75

8

0%

空旷操场

1km

-85

5

0%

空旷操场

2km

-95

2

0%

空旷操场

3km

-105

-1

~2%

室内穿1堵墙

50m

-80

6

0%

室内穿2堵墙

30m

-95

2

~1%

室内穿3堵墙

15m

-110

-3

~5%

地下车库

200m

-100

0

~3%

实测结论:在空旷环境下2公里以内基本零丢包,穿墙能力比WiFi强太多。3公里以后信号衰减明显,但对传感器数据这种小数据量的场景,偶尔丢一两个包影响也不大(可以加重传机制)。手册标的8公里是在理想环境(天线3米高、无遮挡)下测的,实际使用中2~3公里是比较靠谱的覆盖范围。

6.3 功率对距离的影响

我用串口指令功能修改了发射功率,做了对比测试:

功率

1km丢包率

2km丢包率

22dBm

0%

0%

17dBm

0%

~1%

12dBm

~1%

~5%

7dBm

~3%

~15%

如果只传几十米,完全可以降低功率来省电。这是LoRa的优势——参数灵活配置,按需调整。


七、应用场景:LoRa适合做什么

7.1 农业物联网

7.2 智能园区

写字楼里的水表、电表、燃气表自动抄表,地下室和楼顶都要覆盖。LoRa的穿透能力可以覆盖整栋楼,不用每个房间装WiFi。

7.3 资产追踪

仓库里的货物追踪、停车场车位检测,LoRa的低功耗特性让追踪器可以工作很长时间不用换电池。

7.4 我自己的应用设想

我打算用LR20模组做一个远程温湿度监测系统:在户外部署几个带SHT30温湿度传感器的LoRa节点,数据通过LoRa传回室内的接收节点。

八、总结

写这篇文章的时候,我回头看了看自己从"LoRa是什么"到"成功跑通通信"的过程,大概花了三天时间。第一天研究LoRa原理和芯片手册,第二天读代码理解驱动逻辑,第三天做实测和参数调试。

大夏龙雀的DX-LR20在整个过程中给我留下了不错的印象:

  1. 二次开发较易:Semtech官方驱动+HAL库适配,移植只需要改两个文件

  2. 性能可靠:实测2公里零丢包,穿墙能力远超WiFi

  3. 功耗友好:6.5mA接收电流,适合电池供电场景

如果你也在找一个远距离、低功耗、易上手的无线通信方案,不妨试试LORA,试试DX-LR20。从跑通第一包数据开始,你会发现LORA是真的好用。

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

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

立即咨询