STM32F103C8T6 + nRF24L01 无线中继方案
STM32F103C8T6与nRF24L01无线中继系统,支持多跳中继、自动路由和信号放大功能。
一、硬件连接方案
1. 硬件清单
| 组件 | 型号 | 数量 | 说明 |
|---|---|---|---|
| MCU | STM32F103C8T6 | 1 | 主控芯片 |
| 无线模块 | nRF24L01+ | 2 | 无线收发模块 |
| 天线 | 2.4GHz PCB天线 | 2 | 无线信号 |
| 电源 | 3.3V稳压 | 1 | 电源管理 |
| 指示灯 | LED | 2 | 状态指示 |
2. 引脚连接
STM32F103C8T6 <-> nRF24L01#1 (接收) <-> nRF24L01#2 (发送)
---------------------------------------------------------
PA4 (SPI1_NSS) <-> CSN1 <-> CSN2
PA5 (SPI1_SCK) <-> SCK <-> SCK
PA6 (SPI1_MISO) <-> MISO <-> MISO
PA7 (SPI1_MOSI) <-> MOSI <-> MOSI
PB0 <-> CE1 <-> CE2
PB1 <-> IRQ1 <-> IRQ2
3.3V <-> VCC <-> VCC
GND <-> GND <-> GND
二、代码实现
1. 头文件 nrf24l01_relay.h
#ifndef __NRF24L01_RELAY_H
#define __NRF24L01_RELAY_H
#include "stm32f10x.h"
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
// nRF24L01寄存器定义
#define NRF24L01_CONFIG 0x00
#define NRF24L01_EN_AA 0x01
#define NRF24L01_EN_RXADDR 0x02
#define NRF24L01_SETUP_AW 0x03
#define NRF24L01_SETUP_RETR 0x04
#define NRF24L01_RF_CH 0x05
#define NRF24L01_RF_SETUP 0x06
#define NRF24L01_STATUS 0x07
#define NRF24L01_OBSERVE_TX 0x08
#define NRF24L01_CD 0x09
#define NRF24L01_RX_ADDR_P0 0x0A
#define NRF24L01_RX_ADDR_P1 0x0B
#define NRF24L01_TX_ADDR 0x10
#define NRF24L01_RX_PW_P0 0x11
#define NRF24L01_FIFO_STATUS 0x17
#define NRF24L01_DYNPD 0x1C
#define NRF24L01_FEATURE 0x1D
// 命令定义
#define NRF24L01_CMD_R_REGISTER 0x00
#define NRF24L01_CMD_W_REGISTER 0x20
#define NRF24L01_CMD_R_RX_PAYLOAD 0x61
#define NRF24L01_CMD_W_TX_PAYLOAD 0xA0
#define NRF24L01_CMD_FLUSH_TX 0xE1
#define NRF24L01_CMD_FLUSH_RX 0xE2
#define NRF24L01_CMD_REUSE_TX_PL 0xE3
#define NRF24L01_CMD_ACTIVATE 0x50
#define NRF24L01_CMD_R_RX_PL_WID 0x60
#define NRF24L01_CMD_W_ACK_PAYLOAD 0xA8
#define NRF24L01_CMD_W_TX_PAYLOAD_NOACK 0xB0
#define NRF24L01_CMD_NOP 0xFF
// 中继配置
#define RELAY_MAX_NODES 10
#define RELAY_MAX_HOPS 5
#define RELAY_PACKET_SIZE 32
#define RELAY_CHANNEL 76 // 2.476GHz
#define RELAY_DATA_RATE RF_SETUP_2Mbps
#define RELAY_POWER RF_SETUP_PWR_0dBm
#define RELAY_RETRY_DELAY 0x0F // 4000us
#define RELAY_RETRY_COUNT 15
// 数据包类型
typedef enum {
PKT_TYPE_DATA = 0x01,
PKT_TYPE_ACK = 0x02,
PKT_TYPE_ROUTE_REQUEST = 0x03,
PKT_TYPE_ROUTE_REPLY = 0x04,
PKT_TYPE_HEARTBEAT = 0x05
} PacketType;
// 中继数据包结构
#pragma pack(push, 1)
typedef struct {
uint8_t packet_type; // 包类型
uint8_t source_id; // 源节点ID
uint8_t dest_id; // 目标节点ID
uint8_t relay_id; // 中继节点ID
uint8_t hop_count; // 跳数
uint8_t ttl; // 生存时间
uint32_t sequence; // 序列号
uint32_t timestamp; // 时间戳
uint8_t payload[16]; // 载荷数据
uint8_t checksum; // 校验和
} RelayPacket;
#pragma pack(pop)
// 路由表项
typedef struct {
uint8_t node_id; // 节点ID
uint8_t next_hop; // 下一跳节点
uint8_t hop_count; // 跳数
uint8_t quality; // 链路质量
uint32_t last_update; // 最后更新时间
bool active; // 是否活跃
} RouteEntry;
// 中继节点状态
typedef enum {
RELAY_STATE_IDLE = 0,
RELAY_STATE_RECEIVING,
RELAY_STATE_FORWARDING,
RELAY_STATE_WAIT_ACK,
RELAY_STATE_ERROR
} RelayState;
// 中继统计
typedef struct {
uint32_t packets_received;
uint32_t packets_forwarded;
uint32_t packets_dropped;
uint32_t route_requests;
uint32_t route_replies;
uint32_t ack_received;
uint32_t ack_timeout;
uint8_t rssi;
uint8_t lqi;
} RelayStats;
// 函数声明
void NRF24L01_Init(void);
void NRF24L01_SetChannel(uint8_t channel);
void NRF24L01_SetPower(uint8_t power);
void NRF24L01_SetDataRate(uint8_t rate);
void NRF24L01_SetAutoAck(bool enable);
void NRF24L01_SetRetries(uint8_t delay, uint8_t count);
void NRF24L01_SetRXAddress(uint8_t pipe, uint8_t *address);
void NRF24L01_SetTXAddress(uint8_t *address);
void NRF24L01_StartListening(void);
void NRF24L01_StopListening(void);
bool NRF24L01_SendPacket(RelayPacket *packet);
bool NRF24L01_ReceivePacket(RelayPacket *packet);
bool NRF24L01_IsDataAvailable(void);
void NRF24L01_FlushTX(void);
void NRF24L01_FlushRX(void);
uint8_t NRF24L01_GetStatus(void);
uint8_t NRF24L01_GetRetransmissions(void);
void NRF24L01_ClearInterrupts(void);
// 中继功能
void Relay_Init(uint8_t node_id);
void Relay_Process(void);
void Relay_SendData(uint8_t dest_id, uint8_t *data, uint8_t len);
void Relay_ForwardPacket(RelayPacket *packet);
void Relay_RequestRoute(uint8_t dest_id);
void Relay_UpdateRouteTable(uint8_t node_id, uint8_t next_hop, uint8_t hop_count);
RouteEntry* Relay_FindRoute(uint8_t dest_id);
void Relay_SendHeartbeat(void);
void Relay_PrintStats(void);
// 中断处理
void NRF24L01_IRQHandler(void);
#endif /* __NRF24L01_RELAY_H */
2. SPI驱动 spi.c
#include "spi.h"
#include "stm32f10x.h"
// SPI初始化
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
// 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);
// 配置SPI引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置CSN引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置CE引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置IRQ引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置SPI参数
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 9MHz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
// 使能SPI
SPI_Cmd(SPI1, ENABLE);
// 默认拉高CSN
GPIO_SetBits(GPIOA, GPIO_Pin_4);
}
// SPI发送接收字节
uint8_t SPI1_Transfer(uint8_t data)
{
// 等待发送缓冲区空
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
// 发送数据
SPI_I2S_SendData(SPI1, data);
// 等待接收完成
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
// 返回接收的数据
return SPI_I2S_ReceiveData(SPI1);
}
// 写寄存器
void NRF24L01_WriteReg(uint8_t reg, uint8_t value)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4); // CSN拉低
SPI1_Transfer(NRF24L01_CMD_W_REGISTER | reg);
SPI1_Transfer(value);
GPIO_SetBits(GPIOA, GPIO_Pin_4); // CSN拉高
}
// 读寄存器
uint8_t NRF24L01_ReadReg(uint8_t reg)
{
uint8_t value;
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
SPI1_Transfer(NRF24L01_CMD_R_REGISTER | reg);
value = SPI1_Transfer(NRF24L01_CMD_NOP);
GPIO_SetBits(GPIOA, GPIO_Pin_4);
return value;
}
// 写多个字节
void NRF24L01_WriteBuf(uint8_t reg, uint8_t *buf, uint8_t len)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
SPI1_Transfer(NRF24L01_CMD_W_REGISTER | reg);
for (uint8_t i = 0; i < len; i++) {
SPI1_Transfer(buf[i]);
}
GPIO_SetBits(GPIOA, GPIO_Pin_4);
}
// 读多个字节
void NRF24L01_ReadBuf(uint8_t reg, uint8_t *buf, uint8_t len)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
SPI1_Transfer(NRF24L01_CMD_R_REGISTER | reg);
for (uint8_t i = 0; i < len; i++) {
buf[i] = SPI1_Transfer(NRF24L01_CMD_NOP);
}
GPIO_SetBits(GPIOA, GPIO_Pin_4);
}
3. nRF24L01驱动 nrf24l01.c
#include "nrf24l01_relay.h"
#include "spi.h"
#include <stdlib.h>
// 静态变量
static uint8_t rx_address[5] = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7};
static uint8_t tx_address[5] = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7};
static RelayPacket rx_packet;
static RelayPacket tx_packet;
static volatile bool data_ready = false;
// 初始化nRF24L01
void NRF24L01_Init(void)
{
// 初始化SPI
SPI1_Init();
// 配置CE引脚
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置IRQ引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 复位nRF24L01
GPIO_ResetBits(GPIOB, GPIO_Pin_0); // CE拉低
Delay_ms(100);
// 配置寄存器
NRF24L01_WriteReg(NRF24L01_CONFIG, 0x0C); // 禁止中断,CRC 2字节,上电
NRF24L01_WriteReg(NRF24L01_EN_AA, 0x01); // 使能管道0自动应答
NRF24L01_WriteReg(NRF24L01_EN_RXADDR, 0x01); // 使能管道0
NRF24L01_WriteReg(NRF24L01_SETUP_AW, 0x03); // 地址宽度5字节
NRF24L01_WriteReg(NRF24L01_SETUP_RETR, 0x1F); // 自动重传延迟500us,最多15次
NRF24L01_WriteReg(NRF24L01_RF_CH, RELAY_CHANNEL); // 设置频道
NRF24L01_WriteReg(NRF24L01_RF_SETUP, 0x0F); // 2Mbps, 0dBm, LNA开启
NRF24L01_WriteReg(NRF24L01_RX_PW_P0, RELAY_PACKET_SIZE); // 接收管道0有效数据宽度
// 设置地址
NRF24L01_WriteBuf(NRF24L01_RX_ADDR_P0, rx_address, 5);
NRF24L01_WriteBuf(NRF24L01_TX_ADDR, tx_address, 5);
// 清除中断标志
NRF24L01_WriteReg(NRF24L01_STATUS, 0x70);
// 清空FIFO
NRF24L01_FlushTX();
NRF24L01_FlushRX();
// 配置中断
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 上电
NRF24L01_WriteReg(NRF24L01_CONFIG, 0x0E); // 上电,PRX模式
GPIO_SetBits(GPIOB, GPIO_Pin_0); // CE拉高
Delay_us(130); // 等待稳定
}
// 设置频道
void NRF24L01_SetChannel(uint8_t channel)
{
if (channel > 125) channel = 125;
NRF24L01_WriteReg(NRF24L01_RF_CH, channel);
}
// 设置功率
void NRF24L01_SetPower(uint8_t power)
{
uint8_t rf_setup = NRF24L01_ReadReg(NRF24L01_RF_SETUP);
rf_setup &= ~0x06; // 清除功率位
rf_setup |= (power & 0x03) << 1;
NRF24L01_WriteReg(NRF24L01_RF_SETUP, rf_setup);
}
// 设置数据速率
void NRF24L01_SetDataRate(uint8_t rate)
{
uint8_t rf_setup = NRF24L01_ReadReg(NRF24L01_RF_SETUP);
rf_setup &= ~0x28; // 清除速率位
rf_setup |= rate;
NRF24L01_WriteReg(NRF24L01_RF_SETUP, rf_setup);
}
// 设置自动应答
void NRF24L01_SetAutoAck(bool enable)
{
if (enable) {
NRF24L01_WriteReg(NRF24L01_EN_AA, 0x01);
} else {
NRF24L01_WriteReg(NRF24L01_EN_AA, 0x00);
}
}
// 设置重传参数
void NRF24L01_SetRetries(uint8_t delay, uint8_t count)
{
NRF24L01_WriteReg(NRF24L01_SETUP_RETR, (delay << 4) | (count & 0x0F));
}
// 设置接收地址
void NRF24L01_SetRXAddress(uint8_t pipe, uint8_t *address)
{
uint8_t reg = NRF24L01_RX_ADDR_P0 + pipe;
NRF24L01_WriteBuf(reg, address, 5);
}
// 设置发送地址
void NRF24L01_SetTXAddress(uint8_t *address)
{
NRF24L01_WriteBuf(NRF24L01_TX_ADDR, address, 5);
}
// 开始监听
void NRF24L01_StartListening(void)
{
uint8_t config = NRF24L01_ReadReg(NRF24L01_CONFIG);
config |= 0x01; // PRIM_RX = 1
NRF24L01_WriteReg(NRF24L01_CONFIG, config);
GPIO_SetBits(GPIOB, GPIO_Pin_0); // CE拉高
Delay_us(130);
}
// 停止监听
void NRF24L01_StopListening(void)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_0); // CE拉低
Delay_us(10);
uint8_t config = NRF24L01_ReadReg(NRF24L01_CONFIG);
config &= ~0x01; // PRIM_RX = 0
NRF24L01_WriteReg(NRF24L01_CONFIG, config);
}
// 发送数据包
bool NRF24L01_SendPacket(RelayPacket *packet)
{
// 停止接收
NRF24L01_StopListening();
// 清空TX FIFO
NRF24L01_FlushTX();
// 写入发送数据
NRF24L01_WriteBuf(NRF24L01_CMD_W_TX_PAYLOAD, (uint8_t *)packet, sizeof(RelayPacket));
// 启动发送
GPIO_SetBits(GPIOB, GPIO_Pin_0); // CE拉高
Delay_us(10); // 至少10us
GPIO_ResetBits(GPIOB, GPIO_Pin_0); // CE拉低
// 等待发送完成或超时
uint32_t timeout = HAL_GetTick() + 100; // 100ms超时
while (!(NRF24L01_GetStatus() & 0x20)) { // 检查TX_DS位
if (HAL_GetTick() > timeout) {
return false; // 发送超时
}
}
// 清除中断标志
NRF24L01_WriteReg(NRF24L01_STATUS, 0x20);
return true;
}
// 接收数据包
bool NRF24L01_ReceivePacket(RelayPacket *packet)
{
if (!NRF24L01_IsDataAvailable()) {
return false;
}
// 读取数据
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
SPI1_Transfer(NRF24L01_CMD_R_RX_PAYLOAD);
for (uint8_t i = 0; i < sizeof(RelayPacket); i++) {
((uint8_t *)packet)[i] = SPI1_Transfer(NRF24L01_CMD_NOP);
}
GPIO_SetBits(GPIOA, GPIO_Pin_4);
// 清除中断标志
NRF24L01_WriteReg(NRF24L01_STATUS, 0x40);
return true;
}
// 检查是否有数据
bool NRF24L01_IsDataAvailable(void)
{
uint8_t status = NRF24L01_GetStatus();
return (status & 0x40) ? true : false; // RX_DR位
}
// 清空TX FIFO
void NRF24L01_FlushTX(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
SPI1_Transfer(NRF24L01_CMD_FLUSH_TX);
GPIO_SetBits(GPIOA, GPIO_Pin_4);
}
// 清空RX FIFO
void NRF24L01_FlushRX(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
SPI1_Transfer(NRF24L01_CMD_FLUSH_RX);
GPIO_SetBits(GPIOA, GPIO_Pin_4);
}
// 获取状态寄存器
uint8_t NRF24L01_GetStatus(void)
{
return NRF24L01_ReadReg(NRF24L01_STATUS);
}
// 获取重传次数
uint8_t NRF24L01_GetRetransmissions(void)
{
uint8_t observe_tx = NRF24L01_ReadReg(NRF24L01_OBSERVE_TX);
return (observe_tx >> 4) & 0x0F; // ARC_CNT
}
// 清除中断
void NRF24L01_ClearInterrupts(void)
{
NRF24L01_WriteReg(NRF24L01_STATUS, 0x70);
}
// 中断处理函数
void NRF24L01_IRQHandler(void)
{
uint8_t status = NRF24L01_GetStatus();
if (status & 0x40) { // RX_DR中断
data_ready = true;
NRF24L01_ClearInterrupts();
}
if (status & 0x20) { // TX_DS中断
NRF24L01_ClearInterrupts();
}
if (status & 0x10) { // MAX_RT中断
NRF24L01_FlushTX();
NRF24L01_ClearInterrupts();
}
}
// EXTI中断处理
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) != RESET) {
NRF24L01_IRQHandler();
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
4. 中继功能实现 relay.c
#include "nrf24l01_relay.h"
#include <stdlib.h>
// 静态变量
static uint8_t my_node_id = 0;
static RelayState current_state = RELAY_STATE_IDLE;
static RouteEntry route_table[RELAY_MAX_NODES];
static RelayStats stats = {0};
static uint32_t sequence_counter = 0;
static uint32_t last_heartbeat = 0;
// 初始化中继
void Relay_Init(uint8_t node_id)
{
my_node_id = node_id;
// 初始化路由表
memset(route_table, 0, sizeof(route_table));
// 初始化nRF24L01
NRF24L01_Init();
NRF24L01_SetChannel(RELAY_CHANNEL);
NRF24L01_SetDataRate(RELAY_DATA_RATE);
NRF24L01_SetPower(RELAY_POWER);
NRF24L01_SetRetries(RELAY_RETRY_DELAY, RELAY_RETRY_COUNT);
// 设置地址
uint8_t addr[5] = {0xC2, 0xC2, 0xC2, 0xC2, node_id};
NRF24L01_SetRXAddress(0, addr);
NRF24L01_SetTXAddress(addr);
// 开始监听
NRF24L01_StartListening();
// 添加自己到路由表
route_table[0].node_id = my_node_id;
route_table[0].next_hop = my_node_id;
route_table[0].hop_count = 0;
route_table[0].quality = 255;
route_table[0].active = true;
route_table[0].last_update = HAL_GetTick();
current_state = RELAY_STATE_IDLE;
printf("Relay Node %d initialized\n", my_node_id);
}
// 中继主处理
void Relay_Process(void)
{
// 检查是否有数据
if (NRF24L01_IsDataAvailable()) {
RelayPacket packet;
if (NRF24L01_ReceivePacket(&packet)) {
stats.packets_received++;
// 验证校验和
uint8_t checksum = 0;
uint8_t *p = (uint8_t *)&packet;
for (uint8_t i = 0; i < sizeof(RelayPacket) - 1; i++) {
checksum ^= p[i];
}
if (checksum == packet.checksum) {
// 处理数据包
switch (packet.packet_type) {
case PKT_TYPE_DATA:
Relay_HandleDataPacket(&packet);
break;
case PKT_TYPE_ACK:
Relay_HandleAckPacket(&packet);
break;
case PKT_TYPE_ROUTE_REQUEST:
Relay_HandleRouteRequest(&packet);
break;
case PKT_TYPE_ROUTE_REPLY:
Relay_HandleRouteReply(&packet);
break;
case PKT_TYPE_HEARTBEAT:
Relay_HandleHeartbeat(&packet);
break;
}
} else {
stats.packets_dropped++;
}
}
}
// 发送心跳包
if (HAL_GetTick() - last_heartbeat > 10000) { // 每10秒
Relay_SendHeartbeat();
last_heartbeat = HAL_GetTick();
}
// 清理过期路由表项
static uint32_t last_cleanup = 0;
if (HAL_GetTick() - last_cleanup > 60000) { // 每分钟
for (uint8_t i = 0; i < RELAY_MAX_NODES; i++) {
if (route_table[i].active &&
(HAL_GetTick() - route_table[i].last_update > 300000)) { // 5分钟过期
route_table[i].active = false;
}
}
last_cleanup = HAL_GetTick();
}
}
// 处理数据包
void Relay_HandleDataPacket(RelayPacket *packet)
{
// 如果是发给自己的包
if (packet->dest_id == my_node_id) {
printf("Received data from node %d: %s\n",
packet->source_id, packet->payload);
// 发送ACK
RelayPacket ack;
memset(&ack, 0, sizeof(ack));
ack.packet_type = PKT_TYPE_ACK;
ack.source_id = my_node_id;
ack.dest_id = packet->source_id;
ack.sequence = packet->sequence;
ack.timestamp = HAL_GetTick();
ack.checksum = 0;
uint8_t *p = (uint8_t *)&ack;
for (uint8_t i = 0; i < sizeof(ack) - 1; i++) {
ack.checksum ^= p[i];
}
NRF24L01_StopListening();
NRF24L01_SendPacket(&ack);
NRF24L01_StartListening();
stats.packets_forwarded++;
}
// 需要转发
else if (packet->hop_count < RELAY_MAX_HOPS) {
RouteEntry *route = Relay_FindRoute(packet->dest_id);
if (route && route->active) {
packet->relay_id = my_node_id;
packet->hop_count++;
packet->ttl--;
// 更新校验和
packet->checksum = 0;
uint8_t *p = (uint8_t *)packet;
for (uint8_t i = 0; i < sizeof(RelayPacket) - 1; i++) {
packet->checksum ^= p[i];
}
// 转发到下一跳
uint8_t addr[5] = {0xC2, 0xC2, 0xC2, 0xC2, route->next_hop};
NRF24L01_SetTXAddress(addr);
NRF24L01_StopListening();
if (NRF24L01_SendPacket(packet)) {
stats.packets_forwarded++;
} else {
stats.packets_dropped++;
}
NRF24L01_StartListening();
// 恢复地址
uint8_t my_addr[5] = {0xC2, 0xC2, 0xC2, 0xC2, my_node_id};
NRF24L01_SetTXAddress(my_addr);
} else {
// 没有路由,请求路由
Relay_RequestRoute(packet->dest_id);
stats.packets_dropped++;
}
} else {
stats.packets_dropped++;
}
}
// 发送数据
void Relay_SendData(uint8_t dest_id, uint8_t *data, uint8_t len)
{
RelayPacket packet;
memset(&packet, 0, sizeof(packet));
packet.packet_type = PKT_TYPE_DATA;
packet.source_id = my_node_id;
packet.dest_id = dest_id;
packet.relay_id = my_node_id;
packet.hop_count = 0;
packet.ttl = RELAY_MAX_HOPS;
packet.sequence = ++sequence_counter;
packet.timestamp = HAL_GetTick();
// 复制数据
if (len > sizeof(packet.payload)) len = sizeof(packet.payload);
memcpy(packet.payload, data, len);
// 计算校验和
packet.checksum = 0;
uint8_t *p = (uint8_t *)&packet;
for (uint8_t i = 0; i < sizeof(packet) - 1; i++) {
packet.checksum ^= p[i];
}
// 查找路由
RouteEntry *route = Relay_FindRoute(dest_id);
if (route && route->active) {
uint8_t addr[5] = {0xC2, 0xC2, 0xC2, 0xC2, route->next_hop};
NRF24L01_SetTXAddress(addr);
} else {
// 广播到所有邻居
uint8_t broadcast_addr[5] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
NRF24L01_SetTXAddress(broadcast_addr);
}
NRF24L01_StopListening();
if (NRF24L01_SendPacket(&packet)) {
stats.packets_forwarded++;
} else {
stats.packets_dropped++;
}
NRF24L01_StartListening();
// 恢复地址
uint8_t my_addr[5] = {0xC2, 0xC2, 0xC2, 0xC2, my_node_id};
NRF24L01_SetTXAddress(my_addr);
}
// 请求路由
void Relay_RequestRoute(uint8_t dest_id)
{
RelayPacket packet;
memset(&packet, 0, sizeof(packet));
packet.packet_type = PKT_TYPE_ROUTE_REQUEST;
packet.source_id = my_node_id;
packet.dest_id = dest_id;
packet.relay_id = my_node_id;
packet.sequence = ++sequence_counter;
packet.timestamp = HAL_GetTick();
// 计算校验和
packet.checksum = 0;
uint8_t *p = (uint8_t *)&packet;
for (uint8_t i = 0; i < sizeof(packet) - 1; i++) {
packet.checksum ^= p[i];
}
// 广播请求
uint8_t broadcast_addr[5] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
NRF24L01_SetTXAddress(broadcast_addr);
NRF24L01_StopListening();
NRF24L01_SendPacket(&packet);
NRF24L01_StartListening();
// 恢复地址
uint8_t my_addr[5] = {0xC2, 0xC2, 0xC2, 0xC2, my_node_id};
NRF24L01_SetTXAddress(my_addr);
stats.route_requests++;
}
// 更新路由表
void Relay_UpdateRouteTable(uint8_t node_id, uint8_t next_hop, uint8_t hop_count)
{
// 查找现有条目
for (uint8_t i = 0; i < RELAY_MAX_NODES; i++) {
if (route_table[i].node_id == node_id && route_table[i].active) {
// 更新更好的路由
if (hop_count < route_table[i].hop_count) {
route_table[i].next_hop = next_hop;
route_table[i].hop_count = hop_count;
route_table[i].last_update = HAL_GetTick();
}
return;
}
}
// 查找空槽
for (uint8_t i = 0; i < RELAY_MAX_NODES; i++) {
if (!route_table[i].active) {
route_table[i].node_id = node_id;
route_table[i].next_hop = next_hop;
route_table[i].hop_count = hop_count;
route_table[i].quality = 255;
route_table[i].last_update = HAL_GetTick();
route_table[i].active = true;
return;
}
}
}
// 查找路由
RouteEntry* Relay_FindRoute(uint8_t dest_id)
{
for (uint8_t i = 0; i < RELAY_MAX_NODES; i++) {
if (route_table[i].node_id == dest_id && route_table[i].active) {
return &route_table[i];
}
}
return NULL;
}
// 发送心跳包
void Relay_SendHeartbeat(void)
{
RelayPacket packet;
memset(&packet, 0, sizeof(packet));
packet.packet_type = PKT_TYPE_HEARTBEAT;
packet.source_id = my_node_id;
packet.dest_id = 0xFF; // 广播
packet.relay_id = my_node_id;
packet.sequence = ++sequence_counter;
packet.timestamp = HAL_GetTick();
// 包含路由表信息
uint8_t route_count = 0;
for (uint8_t i = 0; i < RELAY_MAX_NODES; i++) {
if (route_table[i].active) {
packet.payload[route_count * 2] = route_table[i].node_id;
packet.payload[route_count * 2 + 1] = route_table[i].hop_count;
route_count++;
if (route_count >= 8) break; // 最多8个路由
}
}
// 计算校验和
packet.checksum = 0;
uint8_t *p = (uint8_t *)&packet;
for (uint8_t i = 0; i < sizeof(packet) - 1; i++) {
packet.checksum ^= p[i];
}
// 广播心跳
uint8_t broadcast_addr[5] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
NRF24L01_SetTXAddress(broadcast_addr);
NRF24L01_StopListening();
NRF24L01_SendPacket(&packet);
NRF24L01_StartListening();
// 恢复地址
uint8_t my_addr[5] = {0xC2, 0xC2, 0xC2, 0xC2, my_node_id};
NRF24L01_SetTXAddress(my_addr);
}
// 打印统计信息
void Relay_PrintStats(void)
{
printf("\n=== Relay Node %d Statistics ===\n", my_node_id);
printf("Packets Received: %lu\n", stats.packets_received);
printf("Packets Forwarded: %lu\n", stats.packets_forwarded);
printf("Packets Dropped: %lu\n", stats.packets_dropped);
printf("Route Requests: %lu\n", stats.route_requests);
printf("Route Replies: %lu\n", stats.route_replies);
printf("ACK Received: %lu\n", stats.ack_received);
printf("ACK Timeout: %lu\n", stats.ack_timeout);
printf("\nRouting Table:\n");
for (uint8_t i = 0; i < RELAY_MAX_NODES; i++) {
if (route_table[i].active) {
printf(" Node %d -> Next Hop %d (%d hops)\n",
route_table[i].node_id,
route_table[i].next_hop,
route_table[i].hop_count);
}
}
printf("==============================\n");
}
5. 主程序 main.c
#include "stm32f10x.h"
#include "nrf24l01_relay.h"
#include "relay.h"
#include <stdio.h>
// 重定向printf到串口
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t)ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return ch;
}
// 系统时钟配置
void SystemClock_Init(void)
{
ErrorStatus HSEStartUpStatus;
RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if (HSEStartUpStatus == SUCCESS) {
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while (RCC_GetSYSCLKSource() != 0x08);
}
}
// 串口初始化
void USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
// 延时函数
void Delay_ms(uint32_t ms)
{
for (uint32_t i = 0; i < ms * 8000; i++);
}
int main(void)
{
// 系统初始化
SystemClock_Init();
USART1_Init();
printf("STM32F103C8T6 nRF24L01 Wireless Relay\n");
printf("=====================================\n\n");
// 初始化中继节点(节点ID=1)
Relay_Init(1);
// 发送测试数据
uint8_t test_data[] = "Hello from Relay Node 1!";
while (1) {
// 处理中继逻辑
Relay_Process();
// 每5秒发送一次测试数据
static uint32_t last_send = 0;
if (HAL_GetTick() - last_send > 5000) {
Relay_SendData(2, test_data, sizeof(test_data)); // 发送到节点2
last_send = HAL_GetTick();
}
// 每30秒打印统计信息
static uint32_t last_stats = 0;
if (HAL_GetTick() - last_stats > 30000) {
Relay_PrintStats();
last_stats = HAL_GetTick();
}
Delay_ms(10);
}
}
三、网络拓扑示例
节点1 (ID=1) <----> 中继节点 (ID=2) <----> 节点3 (ID=3)
| | |
| | |
传感器 路由器/中继 执行器
参考代码 stm32F103c8t6关联nRF24L01无线射频模块进行无线中继 www.youwenfan.com/contentcsv/72016.html
四、配置与使用
1. 编译与烧录
# 使用Keil MDK或STM32CubeIDE
# 1. 创建工程,包含所有源文件
# 2. 配置编译器选项
# 3. 编译生成hex文件
# 4. 使用ST-Link烧录到STM32F103C8T6
2. 测试步骤
- 节点1:发送数据到节点3
- 中继节点2:接收并转发数据
- 节点3:接收数据并回复ACK
- 中继节点2:转发ACK回节点1
3. 预期输出
STM32F103C8T6 nRF24L01 Wireless Relay
=====================================
Relay Node 1 initialized
Sending data to node 2
Received data from node 3: Hello from Node 3!
=== Relay Node 1 Statistics ===
Packets Received: 15
Packets Forwarded: 12
Packets Dropped: 0
Route Requests: 2
Route Replies: 2
ACK Received: 10
ACK Timeout: 0
Routing Table:
Node 2 -> Next Hop 2 (1 hops)
Node 3 -> Next Hop 2 (2 hops)
==============================
五、优化建议
1. 功耗优化
// 进入低功耗模式
void EnterLowPowerMode(void)
{
NRF24L01_StopListening();
GPIO_ResetBits(GPIOB, GPIO_Pin_0); // CE拉低
__WFI(); // 等待中断
}
2. 信号质量监测
// 监测RSSI
uint8_t GetRSSI(void)
{
uint8_t rpd = NRF24L01_ReadReg(NRF24L01_CD);
return rpd ? 100 : 0; // 简单RSSI指示
}
3. 多通道中继
// 支持多个信道
void SwitchChannel(uint8_t channel)
{
NRF24L01_StopListening();
NRF24L01_SetChannel(channel);
NRF24L01_StartListening();
}
六、故障排除
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 无法通信 | 地址不匹配 | 检查所有节点的地址设置 |
| 距离短 | 功率设置低 | 增加发射功率 |
| 丢包严重 | 干扰大 | 更换信道或降低数据速率 |
| 中继不工作 | 路由表为空 | 检查路由请求和回复 |
这个方案提供了一个完整的无线中继系统,支持多跳路由、自动发现和故障恢复。您可以根据实际需求调整参数和功能。