tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(高级篇)V2.0.1 — LoRa章节 + 配套代码13~14" author: "尚硅谷研究院" date: 2026-07-16
用生活理解:LoRa 就像"对讲机里的长跑冠军"——说话速度很慢(50bps),但声音能传出几公里,而且电池用几年不换。适合田野里的温度传感器每隔一小时报一次数。LoRa 是在水下吹气泡——气泡(信号)虽然升得慢,但能飘很远。
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 使用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 是 LoRa 的一个特殊功能——在极低功耗下周期性监听空中是否有 LoRa 信号。如果检测到信号,唤醒 MCU 接收;无信号则快速返回休眠。
CAD 的意义:普通 LoRa 接收模式下,接收机须持续打开(功耗 ~10mA),而 CAD 的监听功耗极低(~5μA)。
LoRa 网络采用星型拓扑:
┌─→ 节点1 (SF9, 上报温度)
│
┌──────┐ ├─→ 节点2 (SF9, 上报湿度) ┌──────────┐
│ 网关 │←┼─→ 节点3 (SF7, 上报位移) ───→│ 云服务器 │
│(接收) │ └─→ ... │(数据存储)│
└──────┘ └──────────┘
↓
STM32(处理数据)
项目路径: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。
文件:stm32/13_lora_node_hal/Core/Src/main.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;
}
}
文件:stm32/14_lora_gateway_hal/Core/Src/main.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();
}
}
}
文件:stm32/13_lora_node_hal/Interface/LoRa/lora.h(默认射频参数:SF9/BW125/CR4/5/480MHz/+17dBm)
#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 驱动封装)
#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 |