17-LoRa远距离通信.md 12 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(高级篇)V2.0.1 — LoRa章节 + 配套代码13~14" author: "尚硅谷研究院" date: 2026-07-15

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 (SF12, 上报温度)
         │
┌──────┐ ├─→ 节点2 (SF9, 上报湿度)     ┌──────────┐
│ 网关  │←┼─→ 节点3 (SF7, 上报位移) ───→│ 云服务器 │
│(接收) │ └─→ ...                       │(数据存储)│
└──────┘                                └──────────┘
         ↓
    STM32(处理数据)
  • 节点:传感器,定时发送数据
  • 网关:集中接收所有节点数据,转发到 STM32 或云端
  • LoRa 本身只定义了物理层(调制方式),MAC 层协议(如 LoRaWAN)定义了网络架构

实验:LoRa 节点与网关通信

软件设计(SPI 驱动 API 模式 — LLCC68 库)

项目路径stm32/13_lora_node_hal(节点)、stm32/14_lora_gateway_hal(网关)

实际代码使用 LLCC68 芯片 SPI 驱动 API,通过 SPI 配置 LoRa 射频参数并收发数据,而非 AT 指令。

文件:stm32/13_lora_node_hal/Core/Src/main.c(节点端)

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lora.h"
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

// 定义全局变量,按键标志位,以及计数值
uint8_t isKeyed;
uint32_t count;

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  // MX_SPI1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  printf("尚硅谷LoRa通讯实验:普通节点...\n");

  LoRa_Init();

  // 进入接收模式
  LoRa_EnterRxMode();

  // 定义接收缓冲区和长度
  uint8_t rxBuff[256];
  uint16_t rxLen;

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  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();
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
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;
    }
    
  }
  
}
/* USER CODE END 4 */

void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

文件:stm32/14_lora_gateway_hal/Core/Src/main.c(网关端)

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lora.h"
/* USER CODE END Includes */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  // MX_SPI1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  printf("尚硅谷LoRa通讯实验:网关节点...\n");

  LoRa_Init();

  // 进入接收模式
  LoRa_EnterRxMode();

  // 定义接收缓冲区和长度
  uint8_t rxBuff[256];
  uint16_t rxLen;

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  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();
    }
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

核心参数速查表

参数 AT 指令 说明
工作模式 AT+MODE=0 0=透传, 1=定点传
本机地址 AT+ADDR=1 1~65535
目标地址 AT+SEND=addr,len,data 定点模式时指定
中心频率 AT+BAND=470000000 单位 Hz(470~510MHz)
扩频因子 AT+SF=12 6~12
带宽 AT+BW=125 125/250/500 KHz
编码率 AT+CR=1 1=4/5, 2=4/6, 3=4/7, 4=4/8
发射功率 AT+POWER=20 0~20dBm
CAD 使能 AT+CAD=1 0=关, 1=开(低功耗监听)

常见问题与避坑

  1. 通信距离短 → 增大 SF(用 12)、降低 BW(用 125)、检查天线匹配(阻抗 50Ω)
  2. 丢包率高 → 降低数据速率(高 SF + 低 BW)、增加重传机制、避免信噪比过低的链路
  3. 节点功耗高 → 开启 CAD(低功耗监听模式)、降低发射功率(10dBm 比 20dBm 省一半)、延长发送间隔
  4. 多节点冲突 → 不同节点错开发送时间(TDMA)、使用不同的扩频因子(CDMA 效果,互不干扰)
  5. LoRa 模块不响应 → 检查波特率匹配、模块供电(峰值 > 120mA)、天线是否接好(长时间无天线可能烧 PA)
  6. SF/BW/CR 参数必须一致 → 节点和网关的射频参数完全一致才能通信