--- tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(高级篇)V2.0.1 — LoRa章节 + 配套代码13~14" author: "尚硅谷研究院" date: 2026-07-16 created: 2026-07-15 --- # LoRa远距离通信 > **用生活理解**:LoRa 就像"对讲机里的长跑冠军"——说话速度很慢(50bps),但声音能传出几公里,而且电池用几年不换。适合田野里的温度传感器每隔一小时报一次数。LoRa 是在水下吹气泡——气泡(信号)虽然升得慢,但能飘很远。 --- ## LPWAN 技术概览 LPWAN = Low Power Wide Area Network(低功耗广域网),是 IoT 中连接远距离、低功耗设备的无线技术。 | 技术 | 频段 | 速率 | 优点 | 缺点 | |------|------|------|------|------| | **LoRa** | 470~510MHz(CN) / 868MHz(EU) / 915MHz(US) | 0.3~50Kbps | 私有化部署、成本低 | 速率低 | | NB-IoT | 运营商授权频段 | ~250Kbps | 覆盖广、运营商维护 | 需 SIM 卡、有月费 | | Sigfox | 868/915MHz | ~100bps | 超低功耗 | 速率极低、依赖 Sigfox 网络 | | LTE-M | 运营商授权频段 | ~1Mbps | 速率较高 | 模组贵、功耗较高 | --- ## LoRa 扩频技术原理 LoRa 使用**CSS(Chirp Spread Spectrum,啁啾扩频)**调制。普通无线信号像"喊话"(功率集中在一小段频率),LoRa 像"吹口哨从低到高再从高到低"(信号散布在宽频带上)。 ### 关键参数 | 参数 | 可取值范围 | 说明 | 对性能的影响 | |------|-----------|------|-------------| | **SF** (扩频因子) | 6~12 | 每个 Chirp 符号代表的位数 | SF↑ → 灵敏度↑ → 速率↓ | | **BW** (带宽) | 125 / 250 / 500 KHz | 调制信号的频率宽度 | BW↑ → 速率↑ → 灵敏度↓ | | **CR** (编码率) | 4/5 ~ 4/8 | 前向纠错(FEC)冗余度 | CR↑ → 抗干扰↑ → 冗余↑ | **典型参数速查表**: | SF | BW(KHz) | 速率(bps) | 灵敏度(dBm) | 参考距离 | |----|---------|-----------|-------------|---------| | 7 | 125 | ~5470 | -123 | ~2km | | 9 | 125 | ~1460 | -129 | ~3km | | **12** | **125** | **~290** | **-137** | **~5km** | | 12 | 250 | ~580 | -134 | ~3.5km | > **SF12 + BW125** 是最常见的高灵敏度配置,适合需要最远距离但数据量很少的场景。 ### CAD(Channel Activity Detection) CAD 是 LoRa 的一个特殊功能——在极低功耗下**周期性监听空中是否有 LoRa 信号**。如果检测到信号,唤醒 MCU 接收;无信号则快速返回休眠。 CAD 的意义:普通 LoRa 接收模式下,接收机须持续打开(功耗 ~10mA),而 CAD 的监听功耗极低(~5μA)。 --- ## LoRa 星型组网 LoRa 网络采用**星型拓扑**: ``` ┌─→ 节点1 (SF9, 上报温度) │ ┌──────┐ ├─→ 节点2 (SF9, 上报湿度) ┌──────────┐ │ 网关 │←┼─→ 节点3 (SF7, 上报位移) ───→│ 云服务器 │ │(接收) │ └─→ ... │(数据存储)│ └──────┘ └──────────┘ ↓ STM32(处理数据) ``` - **节点**:传感器,定时发送数据 - **网关**:集中接收所有节点数据,转发到 STM32 或云端 - LoRa 本身只定义了**物理层**(调制方式),MAC 层协议(如 LoRaWAN)定义了网络架构 --- ## 实验:LoRa 节点与网关通信 **项目路径**:`stm32/13_lora_node_hal`(节点)、`stm32/14_lora_gateway_hal`(网关) 实际代码使用 LLCC68 芯片 SPI 驱动 API(`lora.c`),通过 SPI 接口配置 LoRa 射频参数并收发数据。两项目共享相同的 `lora.h/.c` 驱动,默认配置为 SF9 / BW125 / CR4/5 / 480MHz。 ### 实验 1:LoRa 节点(按键触发发送) **文件:`stm32/13_lora_node_hal/Core/Src/main.c`** ```c #include "main.h" #include "spi.h" #include "usart.h" #include "gpio.h" #include "lora.h" uint8_t isKeyed; uint32_t count; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); printf("尚硅谷LoRa通讯实验:普通节点...\n"); LoRa_Init(); LoRa_EnterRxMode(); uint8_t rxBuff[256]; uint16_t rxLen; while (1) { LoRa_RecvData(rxBuff, &rxLen); if (rxLen > 0) { printf("收到数据:data: %.*s\n", rxLen, rxBuff); rxLen = 0; } if (isKeyed) { printf("按键按下!\n"); uint8_t msg[100] = {0}; sprintf((char *)msg, "一个普通LoRa节点,准备开始发送数据... %d", ++count); LoRa_SendData(msg, strlen((char *)msg)); isKeyed = 0; LoRa_EnterRxMode(); } } } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == KEY_Pin) { HAL_Delay(100); if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin)) isKeyed = 1; } } ``` ### 实验 2:LoRa 网关(自动回复) **文件:`stm32/14_lora_gateway_hal/Core/Src/main.c`** ```c #include "main.h" #include "spi.h" #include "usart.h" #include "gpio.h" #include "lora.h" int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); printf("尚硅谷LoRa通讯实验:网关节点...\n"); LoRa_Init(); LoRa_EnterRxMode(); uint8_t rxBuff[256]; uint16_t rxLen; while (1) { LoRa_RecvData(rxBuff, &rxLen); if (rxLen > 0) { printf("收到数据:data: %.*s\n", rxLen, rxBuff); rxLen = 0; uint8_t *msg = "网关已经收到你的数据,收到请回复..."; LoRa_SendData(msg, strlen((char *)msg)); LoRa_EnterRxMode(); } } } ``` ### LoRa 驱动(节点与网关共享) **文件:`stm32/13_lora_node_hal/Interface/LoRa/lora.h`**(默认射频参数:SF9/BW125/CR4/5/480MHz/+17dBm) ```c #ifndef __LORA_H #define __LORA_H #include "driver_llcc68_interface.h" #define LLCC68_LORA_DEFAULT_STOP_TIMER_ON_PREAMBLE LLCC68_BOOL_FALSE #define LLCC68_LORA_DEFAULT_REGULATOR_MODE LLCC68_REGULATOR_MODE_DC_DC_LDO #define LLCC68_LORA_DEFAULT_PA_CONFIG_DUTY_CYCLE 0x02 #define LLCC68_LORA_DEFAULT_PA_CONFIG_HP_MAX 0x03 #define LLCC68_LORA_DEFAULT_TX_DBM 17 #define LLCC68_LORA_DEFAULT_RAMP_TIME LLCC68_RAMP_TIME_10US #define LLCC68_LORA_DEFAULT_SF LLCC68_LORA_SF_9 #define LLCC68_LORA_DEFAULT_BANDWIDTH LLCC68_LORA_BANDWIDTH_125_KHZ #define LLCC68_LORA_DEFAULT_CR LLCC68_LORA_CR_4_5 #define LLCC68_LORA_DEFAULT_LOW_DATA_RATE_OPTIMIZE LLCC68_BOOL_FALSE #define LLCC68_LORA_DEFAULT_RF_FREQUENCY 480000000U #define LLCC68_LORA_DEFAULT_SYMB_NUM_TIMEOUT 0 #define LLCC68_LORA_DEFAULT_SYNC_WORD 0x3444U #define LLCC68_LORA_DEFAULT_RX_GAIN 0x94 #define LLCC68_LORA_DEFAULT_OCP 0x38 #define LLCC68_LORA_DEFAULT_PREAMBLE_LENGTH 12 #define LLCC68_LORA_DEFAULT_HEADER LLCC68_LORA_HEADER_EXPLICIT #define LLCC68_LORA_DEFAULT_BUFFER_SIZE 255 #define LLCC68_LORA_DEFAULT_CRC_TYPE LLCC68_LORA_CRC_TYPE_ON #define LLCC68_LORA_DEFAULT_INVERT_IQ LLCC68_BOOL_FALSE #define LLCC68_LORA_DEFAULT_CAD_SYMBOL_NUM LLCC68_LORA_CAD_SYMBOL_NUM_2 #define LLCC68_LORA_DEFAULT_CAD_DET_PEAK 24 #define LLCC68_LORA_DEFAULT_CAD_DET_MIN 10 #define LLCC68_LORA_DEFAULT_START_MODE LLCC68_START_MODE_WARM #define LLCC68_LORA_DEFAULT_RTC_WAKE_UP LLCC68_BOOL_TRUE uint8_t LoRa_Init(void); uint8_t LoRa_SendData(uint8_t data[], uint16_t len); uint8_t LoRa_EnterRxMode(void); void LoRa_RecvData(uint8_t rxBuff[], uint16_t *rxLen); #endif ``` **文件:`stm32/13_lora_node_hal/Interface/LoRa/lora.c`**(LLCC68 SPI 驱动封装) ```c #include "lora.h" static llcc68_handle_t gs_handle; uint8_t LoRa_Init(void) { printf("LoRa开始初始化...\n"); uint8_t res; uint32_t reg; uint8_t modulation, config; DRIVER_LLCC68_LINK_INIT(&gs_handle, llcc68_handle_t); DRIVER_LLCC68_LINK_SPI_INIT(&gs_handle, llcc68_interface_spi_init); DRIVER_LLCC68_LINK_SPI_DEINIT(&gs_handle, llcc68_interface_spi_deinit); DRIVER_LLCC68_LINK_SPI_WRITE_READ(&gs_handle, llcc68_interface_spi_write_read); DRIVER_LLCC68_LINK_RESET_GPIO_INIT(&gs_handle, llcc68_interface_reset_gpio_init); DRIVER_LLCC68_LINK_RESET_GPIO_DEINIT(&gs_handle, llcc68_interface_reset_gpio_deinit); DRIVER_LLCC68_LINK_RESET_GPIO_WRITE(&gs_handle, llcc68_interface_reset_gpio_write); DRIVER_LLCC68_LINK_BUSY_GPIO_INIT(&gs_handle, llcc68_interface_busy_gpio_init); DRIVER_LLCC68_LINK_BUSY_GPIO_DEINIT(&gs_handle, llcc68_interface_busy_gpio_deinit); DRIVER_LLCC68_LINK_BUSY_GPIO_READ(&gs_handle, llcc68_interface_busy_gpio_read); DRIVER_LLCC68_LINK_DELAY_MS(&gs_handle, llcc68_interface_delay_ms); DRIVER_LLCC68_LINK_DEBUG_PRINT(&gs_handle, llcc68_interface_debug_print); DRIVER_LLCC68_LINK_RECEIVE_CALLBACK(&gs_handle, llcc68_interface_receive_callback); res = llcc68_init(&gs_handle); if (res != 0) { return 1; } // 进入待机模式 → 配置寄存器链 llcc68_set_standby(&gs_handle, LLCC68_CLOCK_SOURCE_XTAL_32MHZ); llcc68_set_stop_timer_on_preamble(&gs_handle, LLCC68_LORA_DEFAULT_STOP_TIMER_ON_PREAMBLE); llcc68_set_regulator_mode(&gs_handle, LLCC68_LORA_DEFAULT_REGULATOR_MODE); llcc68_set_pa_config(&gs_handle, LLCC68_LORA_DEFAULT_PA_CONFIG_DUTY_CYCLE, LLCC68_LORA_DEFAULT_PA_CONFIG_HP_MAX); llcc68_set_rx_tx_fallback_mode(&gs_handle, LLCC68_RX_TX_FALLBACK_MODE_STDBY_XOSC); llcc68_set_dio_irq_params(&gs_handle, 0x03FF, 0x03FF, 0x0000, 0x0000); llcc68_clear_irq_status(&gs_handle, 0x03FF); llcc68_set_packet_type(&gs_handle, LLCC68_PACKET_TYPE_LORA); llcc68_set_tx_params(&gs_handle, LLCC68_LORA_DEFAULT_TX_DBM, LLCC68_LORA_DEFAULT_RAMP_TIME); // 设置 LoRa 调制参数:SF9 / BW125 / CR4/5 llcc68_set_lora_modulation_params(&gs_handle, LLCC68_LORA_DEFAULT_SF, LLCC68_LORA_DEFAULT_BANDWIDTH, LLCC68_LORA_DEFAULT_CR, LLCC68_LORA_DEFAULT_LOW_DATA_RATE_OPTIMIZE); // 设置射频频率:480MHz llcc68_frequency_convert_to_register(&gs_handle, LLCC68_LORA_DEFAULT_RF_FREQUENCY, ®); llcc68_set_rf_frequency(&gs_handle, reg); llcc68_set_buffer_base_address(&gs_handle, 0x00, 0x00); llcc68_set_lora_symb_num_timeout(&gs_handle, LLCC68_LORA_DEFAULT_SYMB_NUM_TIMEOUT); llcc68_reset_stats(&gs_handle, 0x0000, 0x0000, 0x0000); llcc68_clear_device_errors(&gs_handle); llcc68_set_lora_sync_word(&gs_handle, LLCC68_LORA_DEFAULT_SYNC_WORD); // 配置发射调制 + 接收增益 + 过流保护 llcc68_get_tx_modulation(&gs_handle, &modulation); modulation |= 0x04; llcc68_set_tx_modulation(&gs_handle, modulation); llcc68_set_rx_gain(&gs_handle, LLCC68_LORA_DEFAULT_RX_GAIN); llcc68_set_ocp(&gs_handle, LLCC68_LORA_DEFAULT_OCP); llcc68_get_tx_clamp_config(&gs_handle, &config); config |= 0x1E; llcc68_set_tx_clamp_config(&gs_handle, config); printf("LoRa初始化完成!\n"); return 0; } uint8_t LoRa_SendData(uint8_t data[], uint16_t len) { TXEN_HIGH; RXEN_LOW; llcc68_set_dio_irq_params(&gs_handle, LLCC68_IRQ_TX_DONE | LLCC68_IRQ_TIMEOUT | LLCC68_IRQ_CAD_DONE | LLCC68_IRQ_CAD_DETECTED, LLCC68_IRQ_TX_DONE | LLCC68_IRQ_TIMEOUT | LLCC68_IRQ_CAD_DONE | LLCC68_IRQ_CAD_DETECTED, 0x0000, 0x0000); llcc68_clear_irq_status(&gs_handle, 0x03FFU); if (llcc68_lora_transmit(&gs_handle, LLCC68_CLOCK_SOURCE_XTAL_32MHZ, LLCC68_LORA_DEFAULT_PREAMBLE_LENGTH, LLCC68_LORA_DEFAULT_HEADER, LLCC68_LORA_DEFAULT_CRC_TYPE, LLCC68_LORA_DEFAULT_INVERT_IQ, data, len, 0) != 0) return 1; return 0; } uint8_t LoRa_EnterRxMode(void) { TXEN_LOW; RXEN_HIGH; llcc68_set_dio_irq_params(&gs_handle, LLCC68_IRQ_RX_DONE | LLCC68_IRQ_TIMEOUT | LLCC68_IRQ_CRC_ERR | LLCC68_IRQ_CAD_DONE | LLCC68_IRQ_CAD_DETECTED, LLCC68_IRQ_RX_DONE | LLCC68_IRQ_TIMEOUT | LLCC68_IRQ_CRC_ERR | LLCC68_IRQ_CAD_DONE | LLCC68_IRQ_CAD_DETECTED, 0x0000, 0x0000); llcc68_clear_irq_status(&gs_handle, 0x03FFU); llcc68_set_lora_packet_params(&gs_handle, LLCC68_LORA_DEFAULT_PREAMBLE_LENGTH, LLCC68_LORA_DEFAULT_HEADER, LLCC68_LORA_DEFAULT_BUFFER_SIZE, LLCC68_LORA_DEFAULT_CRC_TYPE, LLCC68_LORA_DEFAULT_INVERT_IQ); uint8_t setup; llcc68_get_iq_polarity(&gs_handle, &setup); #if LLCC68_LORA_DEFAULT_INVERT_IQ == LLCC68_BOOL_FALSE setup |= 1 << 2; #else setup &= ~(1 << 2); #endif llcc68_set_iq_polarity(&gs_handle, setup); llcc68_continuous_receive(&gs_handle); return 0; } void LoRa_RecvData(uint8_t rxBuff[], uint16_t *rxLen) { llcc68_irq_handler(&gs_handle); if (gs_handle.receive_len > 0) { *rxLen = gs_handle.receive_len; memcpy(rxBuff, gs_handle.receive_buf, *rxLen); gs_handle.receive_len = 0; } } ``` --- ## 核心参数速查表 | 参数 | lora.h 宏定义 | 说明 | |------|--------------|------| | 扩频因子 | `LLCC68_LORA_DEFAULT_SF` | SF9(默认),可改为 SF7~SF12 | | 带宽 | `LLCC68_LORA_DEFAULT_BANDWIDTH` | 125 KHz(默认) | | 编码率 | `LLCC68_LORA_DEFAULT_CR` | CR4/5(默认) | | 发射功率 | `LLCC68_LORA_DEFAULT_TX_DBM` | +17dBm(默认) | | 中心频率 | `LLCC68_LORA_DEFAULT_RF_FREQUENCY` | 480000000 Hz | | 同步字 | `LLCC68_LORA_DEFAULT_SYNC_WORD` | 0x3444(公开网络) | | 前导码长度 | `LLCC68_LORA_DEFAULT_PREAMBLE_LENGTH` | 12 | | CRC | `LLCC68_LORA_DEFAULT_CRC_TYPE` | CRC_ON | ## 常见问题与避坑 1. **通信距离短** → 增大 SF(用 12)、降低 BW(用 125)、检查天线匹配(阻抗 50Ω) 2. **丢包率高** → 降低数据速率(高 SF + 低 BW)、增加重传机制、避免信噪比过低的链路 3. **节点功耗高** → 开启 CAD(低功耗监听模式)、降低发射功率(10dBm 比 20dBm 省一半)、延长发送间隔 4. **多节点冲突** → 不同节点错开发送时间(TDMA)、使用不同的扩频因子(CDMA 效果,互不干扰) 5. **LoRa 模块不响应** → 检查 SPI 接线(NSS/SCK/MISO/MOSI)、检查模块供电(峰值 > 120mA)、天线是否接好 6. **SF/BW/CR 参数必须一致** → 节点和网关的射频参数完全一致才能通信