ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从零玩转LoRa

从零玩转LoRa 前阵子有个项目需求——要在两栋楼之间传数据距离大概一两公里中间隔着几堵墙和一个停车场。第一反应是用WiFi但实际上其中一个楼没有WiFi可以接入后来想用4G模块但这个距离似乎太浪费了并且相比之下使用成本也更高一点。蓝牙更不用想根本不合适。然后一个朋友让我试试LoRa。我之前只听说过LoRa这个词知道是个远距离的无线技术但从来没实际用过。上网一搜好家伙模组品牌一大堆芯片型号一堆数字字母组合看得人头大。后来在找模组的时候发现了深圳大夏龙雀科技有一款DX-LR20-900M22S的LoRa模组。研究了几天从一无所知到成功跑通两板互发数据整个过程踩了不少坑但也学到了很多东西。这篇文章就把我的开发经验分享出来希望对同样想入门LoRa的朋友有所帮助。一、LoRa到底是什么1.1 一句话解释LoRaLoRa是一种低功耗局域网无线通信技术特点是传得远、功耗低、穿透强代价是传输速率低。打个比方WiFi就像快递的当日达速度快但覆盖范围小蓝牙像是闪送更近更快但范围更小而LoRa就像邮政的平信虽然慢了点但能寄到很远的地方而且发信人几乎不费什么力气。1.2 LoRa的核心优势特性LoRaWiFi蓝牙(BLE)通信距离2~15km50~100m10~50m穿透能力强弱很弱功耗极低高低传输速率0.3~50kbps150~600Mbps1~2Mbps适用场景远距离低频数据高速网络近距设备互联LoRa不适合传视频、图片这种大数据量的东西但对于传感器数据采集、远程开关控制、定位信标这类小数据、远距离、低功耗的场景简直是量身定制。1.3 LoRa调制原理LoRa用了三种核心技术来实现远距离传输① 扩频技术普通无线信号就像一个人在大喊声音大但容易被干扰。LoRa的扩频技术像是把一句话拆成一首歌用不同的音调来传递信息。即使环境噪声很大也能从旋律中辨认出有用信号。具体来说LoRa使用线性调频信号—— 频率从低到高或从高到低线性变化。每个数据符号对应一个特定的起始频率偏移接收端通过比对收到的Chirp与参考Chirp的频率差来解码数据。② 扩频因子扩频因子决定了每个数据符号占用的时间。SF越大每个符号持续越久抗干扰能力越强传得越远但速率越低。可以理解为说得越慢对方越能听清。LLCC68芯片支持的SF范围带宽(BW)支持的SF范围125 kHzSF5 ~ SF9250 kHzSF5 ~ SF10500 kHzSF5 ~ 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待机电流180nAFIFO256ByteSPI速率0~10Mbps模块PCB设计原理图实拍图说实话6.5mA的接收电流和180nA的待机电流在远距离通信模组里算非常优秀的。这意味着如果用电池供电模组可以长时间监听而不耗电。三、快速上手30分钟跑通通信3.1 准备工作你需要准备两块DX-LR20-900M模块LoRa通信至少需要一发一收两根Type-C数据线两根天线SMA或IPEX均可取决于你接哪个座子PC电脑Keil5开发环境 STM32F1xx HAL库串口助手软件如SSCOM3.2 引脚连接LR20模组通过SPI与STM32通信引脚分配如下功能STM32引脚说明NSSPA4SPI片选SCKPA5SPI时钟MISOPA6SPI数据输出模组→MCUMOSIPA7SPI数据输入MCU→模组RESETGPIO模组复位BUSYGPIO模组状态指示DIO1GPIO中断引脚收发完成通知TX_ENGPIO发射使能射频开关RX_ENGPIO接收使能射频开关串口引脚PA9(TX) / PA10(RX)默认波特率9600。3.3 默认程序工作流程开发板出厂程序的核心逻辑非常清晰上电初始化 → LoRa参数配置BW125kHz, SF9, 功率22dBm→ 进入接收模式每隔1秒自动发送一次0xAA测试数据收到数据后通过串口打印并触发LED闪烁串口输入的数据也可以通过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发送给模组。参数选择心得BW125kHz SF9是我多次测试后选的组合。SF越大传得越远但速率越低SF9在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有事发生了。整个中断处理流程如下DIO1上升沿触发 → 外部中断ISR中设置IrqFired true读取中断状态到radioFlag主循环调用DX_Lora_RadioIrqProcess()检查中断标志根据radioFlag的位判断是哪种事件发送完成/接收完成/超时/CRC错误等执行对应的回调函数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 只需修改两个文件文件1sx126x_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; }文件2UserConfig.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时序确保CPOL0, CPHA0Mode 0最高10MHz电平匹配LR20是3.3V5V MCU需要电平转换NSS控制软件控制CS引脚不要用硬件NSSDIO1中断配置为上升沿触发的外部中断BUSY检测发送SPI命令前必须检查BUSY为低RESET低电平复位至少保持200μs六、通信实测到底能传多远6.1 测试环境条件配置频率920MHz带宽125kHz扩频因子SF9编码率4/6发射功率22dBm天线SMA胶皮天线天线高度1.5m手持测试数据0xAA每秒发送一次6.2 测试结果场景距离RSSISNR丢包率空旷操场500m-7580%空旷操场1km-8550%空旷操场2km-9520%空旷操场3km-105-1~2%室内穿1堵墙50m-8060%室内穿2堵墙30m-952~1%室内穿3堵墙15m-110-3~5%地下车库200m-1000~3%实测结论在空旷环境下2公里以内基本零丢包穿墙能力比WiFi强太多。3公里以后信号衰减明显但对传感器数据这种小数据量的场景偶尔丢一两个包影响也不大可以加重传机制。手册标的8公里是在理想环境天线3米高、无遮挡下测的实际使用中2~3公里是比较靠谱的覆盖范围。6.3 功率对距离的影响我用串口指令功能修改了发射功率做了对比测试功率1km丢包率2km丢包率22dBm0%0%17dBm0%~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在整个过程中给我留下了不错的印象二次开发较易Semtech官方驱动HAL库适配移植只需要改两个文件性能可靠实测2公里零丢包穿墙能力远超WiFi功耗友好6.5mA接收电流适合电池供电场景如果你也在找一个远距离、低功耗、易上手的无线通信方案不妨试试LORA试试DX-LR20。从跑通第一包数据开始你会发现LORA是真的好用。
返回列表