编程技术分享

分享编程知识,探讨技术创新

0%

简介:ExpressLRS双频二合一高频头,开关切换

好的,作为一名高级嵌入式软件开发工程师,很高兴能为您分析和设计 ExpressLRS 双频二合一高频头的嵌入式软件系统。
关注微信公众号,提前获取相关推文

项目背景理解:

ExpressLRS (ELRS) 是一种开源的远程无线电控制系统,以其低延迟、高刷新率和长距离通信能力而闻名,特别适用于无人机和遥控模型。双频二合一高频头意味着它支持两个不同的射频频段(通常是 2.4GHz 和 900MHz/868MHz),并且可以在这两个频段之间切换,以适应不同的应用场景和环境条件。开关切换功能允许用户根据实际需求选择最佳频段。

需求分析:

  1. 双频段支持: 系统必须能够支持 2.4GHz 和 900MHz/868MHz 两个频段的 ExpressLRS 协议。
  2. 频段切换: 需要实现硬件开关或软件方式进行频段切换,并确保切换的可靠性和实时性。
  3. ExpressLRS 协议实现: 完整实现 ExpressLRS 协议栈,包括数据包编码、解码、加密、频率跳频、链路管理、遥测数据处理等关键功能。
  4. 低延迟和高刷新率: 继承 ExpressLRS 的核心优势,确保低延迟和高刷新率的数据传输,满足实时控制需求。
  5. 可靠性: 系统必须稳定可靠,能够长时间运行,并具备完善的错误处理和恢复机制。
  6. 高效性: 代码设计和算法选择应注重效率,充分利用嵌入式系统的资源,降低功耗。
  7. 可扩展性: 系统架构应具备良好的可扩展性,方便后续功能扩展和协议升级。
  8. 测试验证: 提供完善的测试方案和工具,确保系统功能的正确性和性能指标的达成。
  9. 维护升级: 预留固件升级接口,方便用户进行固件更新和维护。

系统架构设计:

为了满足上述需求,并构建可靠、高效、可扩展的系统平台,我推荐采用分层架构的设计模式。分层架构可以将复杂系统分解成多个相对独立的模块,降低模块之间的耦合度,提高代码的可维护性和可重用性。

推荐的分层架构:

  1. 硬件抽象层 (HAL - Hardware Abstraction Layer):

    • 目的: 隔离硬件差异,为上层软件提供统一的硬件接口。
    • 模块:
      • GPIO 驱动: 控制 GPIO 引脚的输入输出,用于开关切换、LED 指示、外部中断等。
      • SPI 驱动: 控制 SPI 接口,用于与射频收发芯片通信。
      • UART 驱动: 控制 UART 接口,用于调试信息输出、配置参数设置、固件升级等。
      • 定时器驱动: 提供定时器功能,用于协议时序控制、频率跳频、任务调度等。
      • 中断控制器驱动: 管理中断,响应外部中断和定时器中断。
      • 电源管理驱动 (可选): 控制电源模式,优化功耗。
    • 接口: 提供统一的函数接口,例如 HAL_GPIO_SetPinState(), HAL_SPI_TransmitReceive(), HAL_UART_SendByte() 等。
  2. 板级支持包 (BSP - Board Support Package):

    • 目的: 针对具体的硬件平台进行初始化配置,连接 HAL 层和系统层。
    • 模块:
      • 系统时钟配置: 初始化系统时钟,配置 MCU 工作频率。
      • 外设初始化: 调用 HAL 层接口,初始化 GPIO, SPI, UART, 定时器等外设。
      • 中断向量表配置: 配置中断向量表,将中断服务函数与中断向量关联。
      • 内存管理 (可选): 如果需要动态内存分配,实现简单的内存管理功能。
    • 功能: BSP_Init(), SystemClock_Config(), Peripheral_Init(), Interrupt_Config() 等初始化函数。
  3. 射频驱动层 (RF Driver Layer):

    • 目的: 控制射频收发芯片,实现射频信号的发送和接收。
    • 模块:
      • 射频芯片初始化: 配置射频芯片的工作模式、频率、功率等参数。
      • 射频发送 (TX): 将数据包通过射频芯片发送出去。
      • 射频接收 (RX): 从射频芯片接收数据包。
      • 频率管理: 实现频率切换和频率跳频逻辑。
      • 功率控制: 控制射频发射功率。
      • 频段切换控制: 处理频段切换逻辑,配置射频芯片工作在 2.4GHz 或 900MHz/868MHz 频段。
    • 接口: RF_Init(), RF_TransmitPacket(), RF_ReceivePacket(), RF_SetFrequency(), RF_SetPowerLevel(), RF_SwitchBand() 等函数。
  4. ExpressLRS 协议层 (ELRS Protocol Layer):

    • 目的: 实现 ExpressLRS 协议栈的核心功能。
    • 模块:
      • 数据包编码/解码: 实现 ExpressLRS 数据包的编码和解码,包括数据封装、CRC 校验、加密解密等。
      • 频率跳频管理: 实现频率跳频算法,控制射频频率的切换。
      • 链路管理: 维护链路状态,处理连接建立、心跳包、链路断开等事件。
      • 遥测数据处理: 处理遥测数据,例如 RSSI, LQI, 电压, 电流等,并将遥测数据打包发送。
      • 配置参数管理: 存储和管理 ExpressLRS 协议的配置参数,例如发射功率、频率信道、遥测周期等。
      • 数据包队列管理: 管理待发送和已接收的数据包队列。
    • 接口: ELRS_Init(), ELRS_EncodePacket(), ELRS_DecodePacket(), ELRS_HandlePacket(), ELRS_ProcessTelemetry(), ELRS_SetConfiguration() 等函数。
  5. 应用层 (Application Layer):

    • 目的: 实现系统的高级功能和用户交互逻辑。
    • 模块:
      • 系统初始化: 调用 BSP 层、RF 驱动层、ELRS 协议层初始化函数,完成系统启动前的准备工作。
      • 主循环: 系统主循环,负责处理接收到的数据包、发送数据包、处理遥测数据、检测开关状态、控制 LED 指示等。
      • 频段切换逻辑: 检测硬件开关状态或接收软件指令,调用 RF 驱动层函数进行频段切换。
      • 配置管理: 加载和保存系统配置参数,例如频段选择、发射功率、遥测周期等。
      • 错误处理: 处理系统运行时发生的错误,例如射频通信错误、协议解析错误等,并进行错误日志记录或恢复操作。
      • 用户界面 (可选): 如果需要,可以通过 UART 或其他接口提供简单的命令行界面或配置界面。
      • 固件升级 (可选): 实现固件升级功能,可以通过 UART 或其他接口接收新的固件并进行更新。
    • 功能: main(), System_Init(), Main_Loop(), Handle_BandSwitch(), Load_Configuration(), Save_Configuration(), Error_Handler(), Firmware_Upgrade() 等函数。

代码实现 (C 语言):

由于代码量庞大 (3000 行以上),这里无法提供完整的代码。但我会提供每个关键模块的详细代码框架和核心代码片段,以帮助您理解架构和实现思路。 以下代码示例将基于一个常见的嵌入式开发平台,例如 STM32 微控制器,并假设使用的射频芯片是常见的 2.4GHz 和 900MHz/868MHz 芯片 (例如 Semtech SX1280 和 SX1276/SX1278)。 实际项目中需要根据具体的硬件平台和射频芯片进行适配和修改。

1. 硬件抽象层 (HAL):

hal_gpio.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

#include <stdint.h>
#include <stdbool.h>

typedef enum {
GPIO_PIN_RESET = 0,
GPIO_PIN_SET = 1
} GPIO_PinState;

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_AF_PP, // Alternate Function Push-Pull
GPIO_MODE_AF_OD // Alternate Function Open-Drain
} GPIO_ModeTypeDef;

typedef struct {
uint32_t Pin; // GPIO Pin number
GPIO_ModeTypeDef Mode; // GPIO Mode
// ... 其他 GPIO 配置参数 (例如 Pull-up/Pull-down, Speed 等)
} GPIO_InitTypeDef;

void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct);
void HAL_GPIO_SetPinState(uint32_t Pin, GPIO_PinState State);
GPIO_PinState HAL_GPIO_ReadPin(uint32_t Pin);
void HAL_GPIO_TogglePin(uint32_t Pin);

#endif // HAL_GPIO_H

hal_gpio.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "hal_gpio.h"
// ... 包含 STM32 平台相关的头文件 (例如 stm32fxxx_hal.h)

void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct) {
// ... 根据 GPIO_InitStruct 配置 STM32 GPIO 寄存器
// 例如使能 GPIO 时钟, 配置 GPIO 模式, 输出类型, 上下拉, 速度等
}

void HAL_GPIO_SetPinState(uint32_t Pin, GPIO_PinState State) {
// ... 根据 State 设置或复位 GPIO 引脚
}

GPIO_PinState HAL_GPIO_ReadPin(uint32_t Pin) {
// ... 读取 GPIO 引脚状态并返回
return GPIO_PIN_RESET; // 示例,实际需要读取寄存器
}

void HAL_GPIO_TogglePin(uint32_t Pin) {
// ... 翻转 GPIO 引脚状态
}

hal_spi.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef HAL_SPI_H
#define HAL_SPI_H

#include <stdint.h>
#include <stdbool.h>

typedef struct {
uint32_t Instance; // SPI 实例 (例如 SPI1, SPI2)
uint32_t Mode; // SPI 模式 (主/从)
uint32_t Direction; // 数据方向 (全双工/半双工)
uint32_t DataSize; // 数据位宽度 (8-bit, 16-bit)
uint32_t ClockPolarity; // 时钟极性
uint32_t ClockPhase; // 时钟相位
uint32_t BaudRatePrescaler; // 波特率预分频
uint32_t FirstBit; // MSB/LSB first
// ... 其他 SPI 配置参数 (例如 CRC, NSS 管理等)
} SPI_InitTypeDef;

void HAL_SPI_Init(SPI_InitTypeDef *SPI_InitStruct);
uint8_t HAL_SPI_TransmitReceiveByte(uint32_t Instance, uint8_t data);
void HAL_SPI_Transmit(uint32_t Instance, uint8_t *pData, uint16_t Size, uint32_t Timeout);
void HAL_SPI_Receive(uint32_t Instance, uint8_t *pData, uint16_t Size, uint32_t Timeout);
// ... 其他 SPI 函数 (例如 DMA 传输, 中断处理等)

#endif // HAL_SPI_H

hal_spi.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "hal_spi.h"
// ... 包含 STM32 平台相关的头文件

void HAL_SPI_Init(SPI_InitTypeDef *SPI_InitStruct) {
// ... 根据 SPI_InitStruct 配置 STM32 SPI 寄存器
// 例如使能 SPI 时钟, 配置 SPI 模式, 时钟极性, 相位, 波特率等
}

uint8_t HAL_SPI_TransmitReceiveByte(uint32_t Instance, uint8_t data) {
// ... 发送一个字节并通过 SPI 接收一个字节
return 0; // 示例,实际需要读写 SPI 数据寄存器
}

void HAL_SPI_Transmit(uint32_t Instance, uint8_t *pData, uint16_t Size, uint32_t Timeout) {
// ... 通过 SPI 发送 Size 个字节的数据
}

void HAL_SPI_Receive(uint32_t Instance, uint8_t *pData, uint16_t Size, uint32_t Timeout) {
// ... 通过 SPI 接收 Size 个字节的数据
}

(HAL 层 UART 和 定时器驱动类似,这里省略)

2. 板级支持包 (BSP):

bsp.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#ifndef BSP_H
#define BSP_H

#include "hal_gpio.h"
#include "hal_spi.h"
#include "hal_uart.h"
#include "hal_timer.h"

void BSP_Init(void);
void SystemClock_Config(void);
void Peripheral_Init(void);
void Interrupt_Config(void);

// ... 定义板级相关的常量,例如 GPIO 引脚定义, SPI 实例定义, UART 实例定义等

#define LED_PIN GPIO_PIN_5 // 假设 LED 连接到 GPIO Pin 5
#define LED_GPIO_PORT GPIOA

#define RF_CS_PIN GPIO_PIN_0 // 假设 RF CS 连接到 GPIO Pin 0
#define RF_CS_GPIO_PORT GPIOB
#define RF_SPI_INSTANCE SPI1

#define DEBUG_UART_INSTANCE UART1

#define BAND_SWITCH_PIN GPIO_PIN_1 // 假设频段切换开关连接到 GPIO Pin 1
#define BAND_SWITCH_GPIO_PORT GPIOC

#endif // BSP_H

bsp.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include "bsp.h"
// ... 包含 STM32 平台相关的头文件

void BSP_Init(void) {
SystemClock_Config();
Peripheral_Init();
Interrupt_Config();
}

void SystemClock_Config(void) {
// ... 配置 STM32 系统时钟,例如 HSE 晶振, PLL 倍频等
}

void Peripheral_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
SPI_InitTypeDef SPI_InitStruct = {0};
UART_InitTypeDef UART_InitStruct = {0};
// ... 初始化其他外设

// LED GPIO 初始化
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(&GPIO_InitStruct);

// RF CS GPIO 初始化
GPIO_InitStruct.Pin = RF_CS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(&GPIO_InitStruct);
HAL_GPIO_SetPinState(RF_CS_PIN, GPIO_PIN_SET); // 初始状态 CS 拉高

// RF SPI 初始化
SPI_InitStruct.Instance = RF_SPI_INSTANCE;
SPI_InitStruct.Mode = SPI_MODE_MASTER;
SPI_InitStruct.Direction = SPI_DIRECTION_2LINES;
SPI_InitStruct.DataSize = SPI_DATASIZE_8BIT;
SPI_InitStruct.ClockPolarity = SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = SPI_NSS_SOFT; // 软件 NSS 管理
SPI_InitStruct.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; // 根据射频芯片要求设置
SPI_InitStruct.FirstBit = SPI_FIRSTBIT_MSB;
HAL_SPI_Init(&SPI_InitStruct);

// Debug UART 初始化
UART_InitStruct.Instance = DEBUG_UART_INSTANCE;
UART_InitStruct.BaudRate = 115200; // 常用波特率
UART_InitStruct.WordLength = UART_WORDLENGTH_8B;
UART_InitStruct.StopBits = UART_STOPBITS_1;
UART_InitStruct.Parity = UART_PARITY_NONE;
UART_InitStruct.HwFlowCtl = UART_HWCONTROL_NONE;
UART_InitStruct.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&UART_InitStruct);

// Band Switch Pin 初始化 (输入模式)
GPIO_InitStruct.Pin = BAND_SWITCH_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP; // 假设开关接地有效
HAL_GPIO_Init(&GPIO_InitStruct);

// ... 初始化其他外设 (例如定时器)
}

void Interrupt_Config(void) {
// ... 配置 NVIC 中断控制器,使能外设中断,设置中断优先级等
}

3. 射频驱动层 (RF Driver Layer):

rf_driver.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef RF_DRIVER_H
#define RF_DRIVER_H

#include <stdint.h>
#include <stdbool.h>

typedef enum {
RF_BAND_2_4GHZ,
RF_BAND_SUB_GHZ // 900MHz/868MHz
} RF_BandTypeDef;

typedef enum {
RF_STATE_IDLE,
RF_STATE_TX,
RF_STATE_RX
} RF_StateTypeDef;

typedef struct {
RF_BandTypeDef Band; // 当前频段
RF_StateTypeDef State; // 当前状态
uint32_t Frequency; // 当前频率 (Hz)
uint8_t PowerLevel; // 发射功率等级 (例如 0-15)
// ... 其他射频配置参数
} RF_ConfigTypeDef;

void RF_Init(RF_BandTypeDef band);
void RF_TransmitPacket(uint8_t *packet, uint16_t packet_len);
bool RF_ReceivePacket(uint8_t *packet, uint16_t *packet_len, uint32_t timeout_ms);
void RF_SetFrequency(uint32_t frequency_hz);
void RF_SetPowerLevel(uint8_t level);
void RF_SwitchBand(RF_BandTypeDef band);
RF_BandTypeDef RF_GetCurrentBand(void);
RF_StateTypeDef RF_GetCurrentState(void);
RF_ConfigTypeDef RF_GetCurrentConfig(void);

#endif // RF_DRIVER_H

rf_driver.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include "rf_driver.h"
#include "bsp.h" // 使用 BSP 定义的 SPI 实例和 GPIO 引脚
#include "hal_gpio.h"
#include "hal_spi.h"
#include "hal_delay.h" // 需要一个简单的延时函数

#define RF_CS_LOW() HAL_GPIO_SetPinState(RF_CS_PIN, GPIO_PIN_RESET)
#define RF_CS_HIGH() HAL_GPIO_SetPinState(RF_CS_PIN, GPIO_PIN_SET)

static RF_ConfigTypeDef current_rf_config;

// 射频芯片寄存器操作函数 (示例,需要根据具体芯片手册实现)
static uint8_t RF_ReadRegister(uint8_t address);
static void RF_WriteRegister(uint8_t address, uint8_t value);
static void RF_WriteBuffer(uint8_t address, uint8_t *buffer, uint16_t length);
static void RF_ReadBuffer(uint8_t address, uint8_t *buffer, uint16_t length);

void RF_Init(RF_BandTypeDef band) {
current_rf_config.Band = band;
current_rf_config.State = RF_STATE_IDLE;
current_rf_config.Frequency = (band == RF_BAND_2_4GHZ) ? 2400000000 : 915000000; // 默认频率
current_rf_config.PowerLevel = 10; // 默认功率等级

// 初始化射频芯片 (示例,需要根据具体芯片手册配置)
RF_CS_LOW();
RF_WriteRegister(0x01, 0x80); // 复位芯片
HAL_Delay_ms(10);
RF_CS_HIGH();
HAL_Delay_ms(10);

RF_SwitchBand(band); // 设置初始频段
RF_SetFrequency(current_rf_config.Frequency);
RF_SetPowerLevel(current_rf_config.PowerLevel);

// ... 其他射频芯片初始化配置 (例如调制方式, 数据速率, 信道带宽等)
}

void RF_TransmitPacket(uint8_t *packet, uint16_t packet_len) {
current_rf_config.State = RF_STATE_TX;
// ... 进入 TX 模式,配置 TX 寄存器 (例如数据长度, 地址等)

RF_CS_LOW();
RF_WriteBuffer(0x00, packet, packet_len); // 假设地址 0x00 是 FIFO TX 缓冲区
RF_CS_HIGH();

// ... 启动 TX 发送,等待发送完成中断或轮询状态寄存器
HAL_Delay_ms(1); // 示例延时,实际需要根据数据速率和包长计算
current_rf_config.State = RF_STATE_IDLE;
}

bool RF_ReceivePacket(uint8_t *packet, uint16_t *packet_len, uint32_t timeout_ms) {
current_rf_config.State = RF_STATE_RX;
// ... 进入 RX 模式,配置 RX 寄存器 (例如数据长度, 地址等)

uint32_t start_time = HAL_GetTick();
while (HAL_GetTick() - start_time < timeout_ms) {
// ... 轮询状态寄存器,检查是否有数据包到达
if ( /* 接收到数据包标志 */ ) {
RF_CS_LOW();
*packet_len = /* 读取数据包长度 */;
RF_ReadBuffer(0x00, packet, *packet_len); // 假设地址 0x00 是 FIFO RX 缓冲区
RF_CS_HIGH();
current_rf_config.State = RF_STATE_IDLE;
return true;
}
}
current_rf_config.State = RF_STATE_IDLE;
return false; // 超时未接收到数据包
}

void RF_SetFrequency(uint32_t frequency_hz) {
current_rf_config.Frequency = frequency_hz;
// ... 根据 frequency_hz 配置射频芯片的频率寄存器
}

void RF_SetPowerLevel(uint8_t level) {
current_rf_config.PowerLevel = level;
// ... 根据 level 配置射频芯片的功率寄存器
}

void RF_SwitchBand(RF_BandTypeDef band) {
current_rf_config.Band = band;
if (band == RF_BAND_2_4GHZ) {
// 配置 2.4GHz 频段射频芯片 (例如 SX1280)
// ... 初始化 SX1280 寄存器
// ... 设置频率范围为 2.4GHz
} else { // RF_BAND_SUB_GHZ
// 配置 900MHz/868MHz 频段射频芯片 (例如 SX1276/SX1278)
// ... 初始化 SX1276/SX1278 寄存器
// ... 设置频率范围为 900MHz/868MHz
}
}

RF_BandTypeDef RF_GetCurrentBand(void) {
return current_rf_config.Band;
}

RF_StateTypeDef RF_GetCurrentState(void) {
return current_rf_config.State;
}

RF_ConfigTypeDef RF_GetCurrentConfig(void) {
return current_rf_config;
}

// 射频芯片寄存器操作函数实现 (示例,需要根据具体芯片手册实现)
static uint8_t RF_ReadRegister(uint8_t address) {
uint8_t value = 0;
RF_CS_LOW();
HAL_SPI_TransmitReceiveByte(RF_SPI_INSTANCE, address & 0x7F); // 读操作,最高位为 0
value = HAL_SPI_TransmitReceiveByte(RF_SPI_INSTANCE, 0x00);
RF_CS_HIGH();
return value;
}

static void RF_WriteRegister(uint8_t address, uint8_t value) {
RF_CS_LOW();
HAL_SPI_TransmitReceiveByte(RF_SPI_INSTANCE, address | 0x80); // 写操作,最高位为 1
HAL_SPI_TransmitReceiveByte(RF_SPI_INSTANCE, value);
RF_CS_HIGH();
}

static void RF_WriteBuffer(uint8_t address, uint8_t *buffer, uint16_t length) {
RF_CS_LOW();
HAL_SPI_TransmitReceiveByte(RF_SPI_INSTANCE, address | 0x80); // 写操作
for (uint16_t i = 0; i < length; i++) {
HAL_SPI_TransmitReceiveByte(RF_SPI_INSTANCE, buffer[i]);
}
RF_CS_HIGH();
}

static void RF_ReadBuffer(uint8_t address, uint8_t *buffer, uint16_t length) {
RF_CS_LOW();
HAL_SPI_TransmitReceiveByte(RF_SPI_INSTANCE, address & 0x7F); // 读操作
for (uint16_t i = 0; i < length; i++) {
buffer[i] = HAL_SPI_TransmitReceiveByte(RF_SPI_INSTANCE, 0x00);
}
RF_CS_HIGH();
}

4. ExpressLRS 协议层 (ELRS Protocol Layer):

(由于 ExpressLRS 协议非常复杂,这里只提供框架和关键函数示例,完整实现需要参考 ExpressLRS 官方文档和源代码)

elrs_protocol.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef ELRS_PROTOCOL_H
#define ELRS_PROTOCOL_H

#include <stdint.h>
#include <stdbool.h>
#include "rf_driver.h"

// ... 定义 ExpressLRS 数据包结构体, 配置参数结构体, 状态结构体等

void ELRS_Init(RF_BandTypeDef band);
bool ELRS_SendPacket(uint8_t *data, uint16_t data_len);
bool ELRS_ReceivePacket(uint8_t *data, uint16_t *data_len, uint32_t timeout_ms);
void ELRS_ProcessReceivedPacket(uint8_t *packet, uint16_t packet_len);
void ELRS_HandleLink(void); // 链路管理
void ELRS_ProcessTelemetry(void); // 遥测数据处理
void ELRS_SetConfiguration(void *config); // 设置配置参数
void ELRS_SwitchBand(RF_BandTypeDef band); // 切换频段

#endif // ELRS_PROTOCOL_H

elrs_protocol.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include "elrs_protocol.h"
#include "rf_driver.h"
#include "bsp.h"
#include "hal_timer.h" // 需要定时器用于频率跳频, 链路管理等

// ... 定义全局变量,例如 ELRS 配置参数, 链路状态, 频率跳频表等

static RF_BandTypeDef current_elrs_band;

void ELRS_Init(RF_BandTypeDef band) {
current_elrs_band = band;
RF_Init(band); // 初始化射频驱动

// ... 初始化 ELRS 协议相关参数,例如频率跳频表,链路状态等
}

bool ELRS_SendPacket(uint8_t *data, uint16_t data_len) {
uint8_t elrs_packet_buffer[256]; // 假设最大包长 256 字节
uint16_t elrs_packet_len = 0;

// ... 1. 数据包编码 (根据 ExpressLRS 协议格式封装数据)
// 例如添加包头, 序列号, 数据负载, CRC 校验等

// ... 2. 加密 (可选,根据配置决定是否加密)

// ... 3. 频率跳频 (根据跳频算法选择下一个频率)
// RF_SetFrequency(next_frequency);

// ... 4. 调用射频驱动发送数据包
RF_TransmitPacket(elrs_packet_buffer, elrs_packet_len);

return true; // 假设发送成功
}

bool ELRS_ReceivePacket(uint8_t *data, uint16_t *data_len, uint32_t timeout_ms) {
uint8_t rf_packet_buffer[256];
uint16_t rf_packet_len = 0;

// ... 1. 频率跳频 (接收端也需要同步跳频)
// RF_SetFrequency(current_frequency);

// ... 2. 调用射频驱动接收数据包
if (RF_ReceivePacket(rf_packet_buffer, &rf_packet_len, timeout_ms)) {
// ... 3. 解密 (可选,如果数据包加密)

// ... 4. 数据包解码 (根据 ExpressLRS 协议格式解析数据)
// 例如校验 CRC, 解封装数据负载

// ... 5. 将解码后的数据复制到 data 缓冲区,并设置 *data_len

return true; // 接收并解码成功
} else {
return false; // 接收超时或射频接收失败
}
}

void ELRS_ProcessReceivedPacket(uint8_t *packet, uint16_t packet_len) {
// ... 处理接收到的 ELRS 数据包,例如解析遥控指令, 更新链路状态, 处理遥测请求等
}

void ELRS_HandleLink(void) {
// ... 链路管理,例如发送心跳包, 检查链路状态, 处理链路断开重连等
// 可以使用定时器定期执行链路管理任务
}

void ELRS_ProcessTelemetry(void) {
// ... 遥测数据处理,例如收集传感器数据 (电压, 电流, RSSI, LQI 等), 格式化遥测数据包, 发送遥测数据包
}

void ELRS_SetConfiguration(void *config) {
// ... 设置 ELRS 协议的配置参数,例如发射功率, 频率信道, 遥测周期等
}

void ELRS_SwitchBand(RF_BandTypeDef band) {
current_elrs_band = band;
RF_SwitchBand(band); // 切换射频驱动频段
// ... 更新 ELRS 协议层频段相关配置 (例如频率跳频表)
}

5. 应用层 (Application Layer):

main.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include "bsp.h"
#include "rf_driver.h"
#include "elrs_protocol.h"
#include "hal_gpio.h"
#include "hal_delay.h"
#include "hal_uart.h" // 用于调试输出

int main(void) {
BSP_Init(); // 初始化 BSP

RF_BandTypeDef current_band = RF_BAND_2_4GHZ; // 默认频段 2.4GHz
ELRS_Init(current_band); // 初始化 ELRS 协议层

HAL_UART_SendString(DEBUG_UART_INSTANCE, "ExpressLRS Dual-Band RF Module Initialized!\r\n");

while (1) {
// 1. 检测频段切换开关状态
GPIO_PinState switch_state = HAL_GPIO_ReadPin(BAND_SWITCH_PIN);
RF_BandTypeDef new_band = (switch_state == GPIO_PIN_RESET) ? RF_BAND_SUB_GHZ : RF_BAND_2_4GHZ; // 假设开关接地切换到 Sub-GHz

if (new_band != current_band) {
current_band = new_band;
ELRS_SwitchBand(current_band); // 切换频段
HAL_UART_Printf(DEBUG_UART_INSTANCE, "Band switched to %s\r\n", (current_band == RF_BAND_2_4GHZ) ? "2.4GHz" : "Sub-GHz");
}

// 2. 处理接收到的数据包
uint8_t rx_packet_buffer[256];
uint16_t rx_packet_len = 0;
if (ELRS_ReceivePacket(rx_packet_buffer, &rx_packet_len, 10)) { // 10ms 超时
ELRS_ProcessReceivedPacket(rx_packet_buffer, rx_packet_len);
HAL_UART_Printf(DEBUG_UART_INSTANCE, "Received packet, length: %d\r\n", rx_packet_len);
}

// 3. 发送数据包 (示例,假设发送遥测数据或控制指令)
// ... 准备要发送的数据 data 和 data_len
// ELRS_SendPacket(data, data_len);

// 4. 处理链路管理
ELRS_HandleLink();

// 5. 处理遥测数据
ELRS_ProcessTelemetry();

// 6. LED 指示 (示例,根据系统状态控制 LED 闪烁)
HAL_GPIO_TogglePin(LED_PIN);
HAL_Delay_ms(500);
}
}

// ... 定义 HAL_Delay_ms, HAL_GetTick 等延时和时间函数 (如果 HAL 层没有提供)

代码说明:

  • 分层架构: 代码按照 HAL, BSP, RF Driver, ELRS Protocol, Application 分层组织,结构清晰,易于维护和扩展。
  • 模块化设计: 每个模块负责特定的功能,例如 GPIO 控制、SPI 通信、射频收发、协议处理等,模块之间通过接口进行交互,降低耦合度。
  • 抽象接口: HAL 层提供了硬件抽象接口,上层代码不直接依赖于具体的硬件细节,方便移植到不同的硬件平台。
  • 频段切换: 应用层代码检测硬件开关状态,并调用 ELRS_SwitchBand() 函数进行频段切换,底层驱动和协议层会自动处理频段切换的细节。
  • ExpressLRS 协议框架: elrs_protocol.celrs_protocol.h 提供了 ExpressLRS 协议栈的框架,包括数据包编码解码、频率跳频、链路管理、遥测处理等关键模块,但具体实现需要根据 ExpressLRS 协议规范进行填充。
  • 调试输出: 代码中使用了 UART 调试输出,方便开发者进行调试和监控系统运行状态。

技术和方法实践验证:

  • 分层架构: 分层架构是嵌入式系统开发中常用的成熟架构模式,经过了广泛的实践验证,可以有效提高代码的可维护性、可重用性和可扩展性。
  • 硬件抽象层 (HAL): HAL 层是嵌入式软件开发中的重要组成部分,可以隔离硬件差异,提高代码的移植性,被广泛应用于各种嵌入式系统中。
  • ExpressLRS 协议: ExpressLRS 协议本身是经过实践验证的成熟协议,具有低延迟、高刷新率、长距离通信等优点,在无人机和遥控模型领域得到了广泛应用。
  • C 语言编程: C 语言是嵌入式系统开发中最常用的编程语言,具有高效、灵活、可移植等优点,拥有丰富的库和工具支持。
  • SPI 通信: SPI 接口是射频芯片常用的通信接口,SPI 通信技术成熟可靠,被广泛应用于各种嵌入式系统中。
  • 中断处理: 中断是嵌入式系统中处理异步事件的重要机制,合理使用中断可以提高系统的实时性和响应速度。
  • 定时器: 定时器在嵌入式系统中用于实现各种定时功能,例如协议时序控制、频率跳频、任务调度等。

代码扩展和完善建议:

  1. 完善 ExpressLRS 协议实现: 根据 ExpressLRS 协议规范,详细实现数据包编码解码、频率跳频算法、链路管理、遥测处理等模块的代码。
  2. 添加配置管理模块: 实现配置参数的加载、保存和修改功能,例如可以通过 UART 命令行或配置文件进行配置。
  3. 实现固件升级功能: 添加固件升级模块,可以通过 UART 或其他接口接收新的固件并进行更新。
  4. 优化射频驱动: 根据具体的射频芯片手册,优化射频驱动代码,例如配置最佳的调制方式、数据速率、信道带宽、功率等级等。
  5. 添加错误处理机制: 完善错误处理代码,例如检测射频通信错误、协议解析错误等,并进行错误日志记录和恢复操作。
  6. 功耗优化: 如果对功耗有要求,可以添加电源管理模块,实现低功耗模式,例如睡眠模式、休眠模式等。
  7. 单元测试和集成测试: 编写单元测试用例和集成测试用例,对每个模块和整个系统进行全面的测试验证,确保系统功能的正确性和性能指标的达成。
  8. 代码注释和文档: 编写详细的代码注释和文档,方便代码维护和团队协作。

总结:

这个代码架构和代码框架提供了一个构建 ExpressLRS 双频二合一高频头嵌入式软件系统的良好起点。通过分层架构、模块化设计、抽象接口和实践验证的技术方法,可以构建一个可靠、高效、可扩展的系统平台。 要完成一个完整的项目,还需要根据具体的硬件平台、射频芯片和 ExpressLRS 协议规范,进行详细的代码实现、测试和优化。 希望这些信息对您有所帮助!

欢迎关注我的其它发布渠道