编程技术分享

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

0%

简介:mmdvm_hs_dual_hat双工板,可实现mmdvm通信。已验证,可直接打板。

我将为您详细阐述 MMDVM_HS_Dual_Hat 双工板嵌入式系统的代码设计架构,并提供相应的 C 代码示例。我们将从需求分析出发,逐步深入到系统实现、测试验证和维护升级,力求构建一个可靠、高效、可扩展的嵌入式平台。
关注微信公众号,提前获取相关推文

项目背景与需求分析

项目背景:

MMDVM (Multi-Mode Digital Voice Modem) 是一个开源项目,旨在提供一个多模式数字语音调制解调器,用于业余无线电通信。MMDVM_HS_Dual_Hat 双工板是在此基础上开发的一款硬件产品,它具备以下关键特性:

  • 双工通信: 支持同时发射和接收,实现全双工或半双工模式的通信。
  • 多模式支持: 能够处理多种数字语音模式,如 D-STAR, DMR, Fusion (C4FM), P25 等。
  • 高性能: 采用高速处理器和优化的硬件设计,确保实时、低延迟的通信性能。
  • 可扩展性: 硬件和软件设计应具备良好的扩展性,方便添加新的功能和模式。
  • 易用性: 提供友好的配置和管理界面,方便用户使用和维护。

需求分析:

基于项目背景和 MMDVM_HS_Dual_Hat 双工板的特性,我们可以提炼出以下核心需求:

  1. 实时通信: 系统必须能够实时处理音频数据,进行调制解调,保证语音通信的流畅性。
  2. 多模式支持: 需要支持多种数字语音模式,并能够方便地扩展新的模式。
  3. 双工操作: 实现同时发射和接收功能,支持双工通信模式。
  4. 硬件抽象: 代码需要与底层硬件解耦,方便移植和维护。
  5. 资源管理: 有效管理系统资源,包括 CPU、内存、外设等,确保系统稳定运行。
  6. 可配置性: 提供灵活的配置选项,允许用户自定义系统参数,如通信模式、频率、信道等。
  7. 错误处理: 具备完善的错误处理机制,能够检测并处理各种异常情况,保证系统可靠性。
  8. 可维护性和可升级性: 代码结构清晰,模块化设计,方便维护和升级。

代码设计架构

为了满足以上需求,并构建一个可靠、高效、可扩展的系统平台,我建议采用分层架构模块化设计相结合的代码架构。这种架构将系统划分为不同的层次和模块,每个层次和模块负责特定的功能,层与层之间通过清晰的接口进行通信,模块之间保持低耦合高内聚。

分层架构:

我将系统架构分为以下几个层次,从底层硬件到上层应用,逐层抽象和封装:

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

    • 功能: 封装底层硬件操作,提供统一的硬件访问接口,隐藏硬件差异。
    • 模块:
      • GPIO 驱动: 控制 GPIO 引脚的输入输出,用于控制外围设备,如 LED、按键、射频芯片控制等。
      • SPI 驱动: 实现 SPI 通信协议,用于与射频芯片、Codec 芯片等进行数据交换。
      • I2C 驱动: 实现 I2C 通信协议,用于与传感器、EEPROM 等进行数据交换。
      • UART 驱动: 实现 UART 通信协议,用于调试输出、串口控制等。
      • ADC/DAC 驱动: 控制模数转换器 (ADC) 和数模转换器 (DAC),用于音频信号的采集和输出。
      • 定时器驱动: 提供定时器功能,用于任务调度、时间管理等。
      • 中断控制器驱动: 管理中断请求,处理硬件中断事件。
  2. 设备驱动层 (Device Driver Layer):

    • 功能: 基于 HAL 层提供的接口,实现对具体硬件设备的驱动,提供设备的功能性接口。
    • 模块:
      • 射频收发器驱动: 控制射频收发芯片,实现信号的调制、解调、发射和接收。例如,常见的射频芯片如 ADF7021, STM32WB 系列集成射频等。
      • 音频 Codec 驱动: 控制音频编解码芯片,实现音频信号的 AD/DA 转换、音频处理等。例如,常见的 Codec 芯片如 CS4270, TLV320AIC3104 等。
      • 电源管理驱动: 控制电源管理芯片,实现电源的开关、电压调节、功耗管理等。
      • LED 驱动: 控制 LED 灯的亮灭、闪烁等,用于状态指示。
      • 按键驱动: 检测按键的按下和释放,用于用户输入。
  3. MMDVM 协议层 (MMDVM Protocol Layer):

    • 功能: 实现 MMDVM 协议栈,处理数字语音模式的编码、解码、调制、解调、同步、帧处理等核心功能。
    • 模块:
      • 模式识别模块: 识别不同的数字语音模式 (D-STAR, DMR, Fusion, P25 等)。
      • 调制解调模块: 实现各种数字调制解调算法 (FSK, GMSK, 4FSK, etc.)。
      • 编码解码模块: 实现语音编码和解码算法 (AMBE++, Codec2, etc.)。
      • 同步模块: 实现位同步、帧同步、载波同步等,确保可靠的通信。
      • 帧处理模块: 处理 MMDVM 协议帧的封装、解析、校验等。
      • 协议状态机: 管理 MMDVM 通信协议的状态流程。
  4. 应用逻辑层 (Application Logic Layer):

    • 功能: 实现系统的核心应用逻辑,包括通信控制、模式切换、参数配置、用户界面等。
    • 模块:
      • 通信控制模块: 负责管理通信流程,包括发射、接收、信道切换、功率控制等。
      • 模式管理模块: 负责切换不同的数字语音模式,加载和配置相应的协议模块。
      • 配置管理模块: 负责加载、保存和管理系统配置参数,例如频率、信道、模式、呼叫组等。
      • 用户界面模块 (可选): 如果需要用户界面 (例如,通过串口、Web 界面等),则负责处理用户输入,显示系统状态。
      • 状态监控模块: 监控系统运行状态,例如 CPU 负载、内存使用率、射频状态等。
  5. 操作系统层 (OS Layer - Real-Time Operating System, RTOS):

    • 功能: 提供任务调度、资源管理、同步机制等,支持多任务并发执行,提高系统实时性和响应性。
    • 选择: 可以选择轻量级的 RTOS,例如 FreeRTOS, RT-Thread, uC/OS 等。RTOS 不是必需的,对于简单的应用也可以采用裸机系统,但 RTOS 能显著提高系统的复杂度和可维护性,尤其是在处理实时通信和多任务并发的场景下。

模块化设计:

在每个层次内部,进一步采用模块化设计,将功能划分为独立的模块,模块之间通过接口进行交互。例如:

  • MMDVM 协议层: 可以针对每种数字语音模式 (D-STAR, DMR, Fusion, P25) 设计独立的模块,每个模块实现该模式的调制解调、编码解码等功能。这样可以方便地添加新的模式,只需要开发新的模式模块即可。
  • 设备驱动层: 可以针对不同的射频芯片、Codec 芯片设计独立的驱动模块,方便更换硬件平台。

数据流:

系统的数据流大致如下:

  1. 接收路径: 射频收发器接收射频信号 -> 射频收发器驱动 -> HAL SPI 接口 -> 微处理器 -> MMDVM 协议层 (解调、解码、帧处理) -> 应用逻辑层 (处理语音数据) -> 音频 Codec 驱动 -> HAL DAC 接口 -> 音频 Codec -> 音频输出 (扬声器/耳机)。
  2. 发射路径: 音频输入 (麦克风) -> 音频 Codec -> 音频 Codec 驱动 -> HAL ADC 接口 -> 微处理器 -> 应用逻辑层 (获取语音数据) -> MMDVM 协议层 (编码、调制、帧封装) -> 射频收发器驱动 -> HAL SPI 接口 -> 微处理器 -> 射频收发器 -> 射频信号发射。

C 代码实现框架

以下是一个基于上述架构的 C 代码实现框架示例,这只是一个框架,并非完整的、可直接编译运行的代码,目的是展示代码结构和关键接口。我将尽量详细展开,并包含一些关键模块的示例代码。

(1) 硬件抽象层 (HAL)

hal.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
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
#ifndef HAL_H
#define HAL_H

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

// GPIO 定义
typedef enum {
GPIO_PIN_0,
GPIO_PIN_1,
GPIO_PIN_2,
// ... 更多 GPIO 引脚定义
GPIO_PIN_MAX
} GPIO_PinTypeDef;

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_AF, // Alternate Function
GPIO_MODE_ANALOG
} GPIO_ModeTypeDef;

typedef enum {
GPIO_PULL_NONE,
GPIO_PULLUP,
GPIO_PULLDOWN
} GPIO_PullTypeDef;

typedef enum {
GPIO_SPEED_LOW,
GPIO_SPEED_MEDIUM,
GPIO_SPEED_HIGH
} GPIO_SpeedTypeDef;

typedef struct {
GPIO_PinTypeDef Pin;
GPIO_ModeTypeDef Mode;
GPIO_PullTypeDef Pull;
GPIO_SpeedTypeDef Speed;
// ... 其他 GPIO 配置参数
} GPIO_InitTypeDef;

// SPI 定义
typedef enum {
SPI_MODE_MASTER,
SPI_MODE_SLAVE
} SPI_ModeTypeDef;

typedef enum {
SPI_POLARITY_LOW, // CPOL = 0
SPI_POLARITY_HIGH // CPOL = 1
} SPI_PolarityTypeDef;

typedef enum {
SPI_PHASE_1EDGE, // CPHA = 0
SPI_PHASE_2EDGE // CPHA = 1
} SPI_PhaseTypeDef;

typedef enum {
SPI_DATASIZE_8BIT,
SPI_DATASIZE_16BIT
} SPI_DataSizeTypeDef;

typedef struct {
SPI_ModeTypeDef Mode;
SPI_PolarityTypeDef Polarity;
SPI_PhaseTypeDef Phase;
SPI_DataSizeTypeDef DataSize;
uint32_t BaudRatePrescaler; // 波特率预分频
// ... 其他 SPI 配置参数
} SPI_InitTypeDef;

// UART 定义 (类似 SPI)
// ...

// ADC 定义 (类似 SPI)
// ...

// DAC 定义 (类似 SPI)
// ...

// HAL 函数声明

// GPIO
void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct);
void HAL_GPIO_WritePin(GPIO_PinTypeDef Pin, bool PinState);
bool HAL_GPIO_ReadPin(GPIO_PinTypeDef Pin);

// SPI
void HAL_SPI_Init(SPI_InitTypeDef *SPI_InitStruct);
void HAL_SPI_Transmit(uint8_t *pData, uint16_t Size, uint32_t Timeout);
void HAL_SPI_Receive(uint8_t *pData, uint16_t Size, uint32_t Timeout);
uint8_t HAL_SPI_TransmitReceive(uint8_t TxData, uint32_t Timeout);

// UART
// ...

// ADC
// ...

// DAC
// ...

// 定时器
void HAL_TIM_DelayMs(uint32_t Delay);
// ...

// 中断控制器
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);
// ...

#endif // HAL_H

hal_stm32f4xx.c (示例,针对 STM32F4 系列)

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
#include "hal.h"
#include "stm32f4xx_hal.h" // 假设使用 STM32 HAL 库

// GPIO
void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct) {
GPIO_InitTypeDefTypeDef hal_gpio_init;
hal_gpio_init.Pin = GPIO_InitStruct->Pin;
// ... 将 GPIO_InitStruct 转换为 STM32 HAL 库的 GPIO_InitTypeDefTypeDef
HAL_GPIO_Init(GPIOx, &hal_gpio_init); // GPIOx 根据具体硬件确定
}

void HAL_GPIO_WritePin(GPIO_PinTypeDef Pin, bool PinState) {
HAL_GPIO_WritePin(GPIOx, Pin, PinState ? GPIO_PIN_SET : GPIO_PIN_RESET);
}

bool HAL_GPIO_ReadPin(GPIO_PinTypeDef Pin) {
return HAL_GPIO_ReadPin(GPIOx, Pin) == GPIO_PIN_SET;
}

// SPI
void HAL_SPI_Init(SPI_InitTypeDef *SPI_InitStruct) {
SPI_InitTypeDefTypeDef hal_spi_init;
hal_spi_init.Mode = SPI_InitStruct->Mode == SPI_MODE_MASTER ? SPI_MODE_MASTER : SPI_MODE_SLAVE;
// ... 将 SPI_InitStruct 转换为 STM32 HAL 库的 SPI_InitTypeDefTypeDef
HAL_SPI_Init(SPIx, &hal_spi_init); // SPIx 根据具体硬件确定
}

void HAL_SPI_Transmit(uint8_t *pData, uint16_t Size, uint32_t Timeout) {
HAL_SPI_Transmit(SPIx, pData, Size, Timeout);
}

void HAL_SPI_Receive(uint8_t *pData, uint16_t Size, uint32_t Timeout) {
HAL_SPI_Receive(SPIx, pData, Size, Timeout);
}

uint8_t HAL_SPI_TransmitReceive(uint8_t TxData, uint32_t Timeout) {
uint8_t RxData;
HAL_SPI_TransmitReceive(SPIx, &TxData, &RxData, 1, Timeout);
return RxData;
}

// UART, ADC, DAC, 定时器, 中断控制器 的 HAL 层实现 (类似 SPI)
// ...

void HAL_TIM_DelayMs(uint32_t Delay) {
HAL_Delay(Delay); // 使用 STM32 HAL 库的 HAL_Delay
}

void HAL_NVIC_EnableIRQ(IRQn_Type IRQn) {
HAL_NVIC_EnableIRQ(IRQn);
}

void HAL_NVIC_DisableIRQ(IRQn_Type IRQn) {
HAL_NVIC_DisableIRQ(IRQn);
}

(2) 设备驱动层 (Device Driver)

rf_transceiver_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_TRANSCEIVER_DRIVER_H
#define RF_TRANSCEIVER_DRIVER_H

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

typedef enum {
RF_TRANSCEIVER_STATE_IDLE,
RF_TRANSCEIVER_STATE_RX,
RF_TRANSCEIVER_STATE_TX
} RF_TransceiverStateTypeDef;

typedef struct {
// ... 射频收发器配置参数,例如频率、功率、模式等
uint32_t frequency;
uint8_t power_level;
// ...
} RF_TransceiverConfigTypeDef;

typedef struct {
RF_TransceiverStateTypeDef state;
RF_TransceiverConfigTypeDef config;
// ... 其他状态信息
} RF_TransceiverHandleTypeDef;

// 射频收发器驱动函数声明
bool RF_Transceiver_Init(RF_TransceiverHandleTypeDef *hTransceiver);
bool RF_Transceiver_SetFrequency(RF_TransceiverHandleTypeDef *hTransceiver, uint32_t frequency);
bool RF_Transceiver_SetPowerLevel(RF_TransceiverHandleTypeDef *hTransceiver, uint8_t power_level);
bool RF_Transceiver_SetMode(RF_TransceiverHandleTypeDef *hTransceiver, RF_TransceiverStateTypeDef mode);
bool RF_Transceiver_TransmitData(RF_TransceiverHandleTypeDef *hTransceiver, uint8_t *pData, uint16_t Size);
bool RF_Transceiver_ReceiveData(RF_TransceiverHandleTypeDef *hTransceiver, uint8_t *pData, uint16_t Size, uint32_t Timeout);
RF_TransceiverStateTypeDef RF_Transceiver_GetState(RF_TransceiverHandleTypeDef *hTransceiver);
// ...

#endif // RF_TRANSCEIVER_DRIVER_H

rf_transceiver_adf7021.c (示例,针对 ADF7021 射频芯片)

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
#include "rf_transceiver_driver.h"
#include "hal.h"

// ADF7021 寄存器地址定义
#define ADF7021_REG_CTRL1 0x01
#define ADF7021_REG_FREQ_MSB 0x02
// ...

// 内部函数:读写 ADF7021 寄存器 (使用 HAL SPI)
static uint8_t ADF7021_ReadReg(uint8_t reg_addr);
static void ADF7021_WriteReg(uint8_t reg_addr, uint8_t reg_value);

bool RF_Transceiver_Init(RF_TransceiverHandleTypeDef *hTransceiver) {
// 初始化 SPI 外设 (使用 HAL SPI)
SPI_InitTypeDef spi_init;
spi_init.Mode = SPI_MODE_MASTER;
spi_init.Polarity = SPI_POLARITY_LOW;
spi_init.Phase = SPI_PHASE_1EDGE;
spi_init.DataSize = SPI_DATASIZE_8BIT;
spi_init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; // 调整波特率
HAL_SPI_Init(&spi_init);

// 初始化 ADF7021 芯片 (根据 ADF7021 数据手册配置寄存器)
ADF7021_WriteReg(ADF7021_REG_CTRL1, 0x12); // 示例配置
// ... 其他寄存器配置

hTransceiver->state = RF_TRANSCEIVER_STATE_IDLE;
return true;
}

bool RF_Transceiver_SetFrequency(RF_TransceiverHandleTypeDef *hTransceiver, uint32_t frequency) {
// 根据频率计算 ADF7021 寄存器值,并写入寄存器
// ...
uint32_t freq_reg_value = /* 计算寄存器值 */;
ADF7021_WriteReg(ADF7021_REG_FREQ_MSB, (freq_reg_value >> 16) & 0xFF);
// ... 写入其他频率寄存器

hTransceiver->config.frequency = frequency;
return true;
}

bool RF_Transceiver_SetPowerLevel(RF_TransceiverHandleTypeDef *hTransceiver, uint8_t power_level) {
// 根据功率等级设置 ADF7021 功率寄存器
// ...
ADF7021_WriteReg(/* 功率寄存器地址 */, power_level);
hTransceiver->config.power_level = power_level;
return true;
}

bool RF_Transceiver_SetMode(RF_TransceiverHandleTypeDef *hTransceiver, RF_TransceiverStateTypeDef mode) {
if (mode == RF_TRANSCEIVER_STATE_RX) {
// 设置 ADF7021 进入接收模式
// ...
ADF7021_WriteReg(/* 模式控制寄存器 */, /* 接收模式值 */);
hTransceiver->state = RF_TRANSCEIVER_STATE_RX;
} else if (mode == RF_TRANSCEIVER_STATE_TX) {
// 设置 ADF7021 进入发射模式
// ...
ADF7021_WriteReg(/* 模式控制寄存器 */, /* 发射模式值 */);
hTransceiver->state = RF_TRANSCEIVER_STATE_TX;
} else if (mode == RF_TRANSCEIVER_STATE_IDLE) {
// 设置 ADF7021 进入空闲模式
// ...
ADF7021_WriteReg(/* 模式控制寄存器 */, /* 空闲模式值 */);
hTransceiver->state = RF_TRANSCEIVER_STATE_IDLE;
}
return true;
}

bool RF_Transceiver_TransmitData(RF_TransceiverHandleTypeDef *hTransceiver, uint8_t *pData, uint16_t Size) {
// 将数据写入 ADF7021 发射 FIFO
// ... 使用 HAL_SPI_Transmit 或循环写入寄存器
for (uint16_t i = 0; i < Size; i++) {
ADF7021_WriteReg(/* 数据 FIFO 寄存器 */, pData[i]);
}
return true;
}

bool RF_Transceiver_ReceiveData(RF_TransceiverHandleTypeDef *hTransceiver, uint8_t *pData, uint16_t Size, uint32_t Timeout) {
// 从 ADF7021 接收 FIFO 读取数据
// ... 使用 HAL_SPI_Receive 或循环读取寄存器,并处理超时
for (uint16_t i = 0; i < Size; i++) {
pData[i] = ADF7021_ReadReg(/* 数据 FIFO 寄存器 */);
}
return true;
}

RF_TransceiverStateTypeDef RF_Transceiver_GetState(RF_TransceiverHandleTypeDef *hTransceiver) {
return hTransceiver->state;
}

// 内部函数实现:读写 ADF7021 寄存器
static uint8_t ADF7021_ReadReg(uint8_t reg_addr) {
uint8_t tx_buf[2] = {reg_addr | 0x80, 0x00}; // 读命令,最高位置 1
uint8_t rx_buf[2];
HAL_SPI_TransmitReceive(tx_buf, rx_buf, 2, HAL_MAX_DELAY);
return rx_buf[1]; // 返回读取的数据
}

static void ADF7021_WriteReg(uint8_t reg_addr, uint8_t reg_value) {
uint8_t tx_buf[2] = {reg_addr & 0x7F, reg_value}; // 写命令,最高位置 0
HAL_SPI_Transmit(tx_buf, 2, HAL_MAX_DELAY);
}

audio_codec_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
37
38
39
40
41
#ifndef AUDIO_CODEC_DRIVER_H
#define AUDIO_CODEC_DRIVER_H

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

typedef enum {
AUDIO_CODEC_SAMPLE_RATE_8K,
AUDIO_CODEC_SAMPLE_RATE_16K,
// ...
} AudioCodecSampleRateTypeDef;

typedef enum {
AUDIO_CODEC_BIT_DEPTH_16BIT,
AUDIO_CODEC_BIT_DEPTH_24BIT,
// ...
} AudioCodecBitDepthTypeDef;

typedef struct {
AudioCodecSampleRateTypeDef sample_rate;
AudioCodecBitDepthTypeDef bit_depth;
// ... 其他音频 Codec 配置参数
} AudioCodecConfigTypeDef;

typedef struct {
AudioCodecConfigTypeDef config;
// ... 其他状态信息
} AudioCodecHandleTypeDef;

// 音频 Codec 驱动函数声明
bool AudioCodec_Init(AudioCodecHandleTypeDef *hCodec);
bool AudioCodec_SetConfig(AudioCodecHandleTypeDef *hCodec, AudioCodecConfigTypeDef *config);
bool AudioCodec_StartRecord(AudioCodecHandleTypeDef *hCodec);
bool AudioCodec_StopRecord(AudioCodecHandleTypeDef *hCodec);
bool AudioCodec_StartPlayback(AudioCodecHandleTypeDef *hCodec);
bool AudioCodec_StopPlayback(AudioCodecHandleTypeDef *hCodec);
bool AudioCodec_ReadData(AudioCodecHandleTypeDef *hCodec, uint8_t *pData, uint16_t Size, uint32_t Timeout);
bool AudioCodec_WriteData(AudioCodecHandleTypeDef *hCodec, uint8_t *pData, uint16_t Size, uint32_t Timeout);
// ...

#endif // AUDIO_CODEC_DRIVER_H

audio_codec_cs4270.c (示例,针对 CS4270 音频 Codec 芯片)

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
#include "audio_codec_driver.h"
#include "hal.h"

// CS4270 寄存器地址定义
#define CS4270_REG_POWER_CTRL 0x02
#define CS4270_REG_DAC_CTRL 0x04
// ...

// 内部函数:读写 CS4270 寄存器 (使用 HAL I2C,假设 CS4270 使用 I2C 控制)
static uint8_t CS4270_ReadReg(uint8_t reg_addr);
static void CS4270_WriteReg(uint8_t reg_addr, uint8_t reg_value);

bool AudioCodec_Init(AudioCodecHandleTypeDef *hCodec) {
// 初始化 I2C 外设 (使用 HAL I2C)
// ...

// 初始化 CS4270 芯片 (根据 CS4270 数据手册配置寄存器)
CS4270_WriteReg(CS4270_REG_POWER_CTRL, 0x01); // 示例配置,使能 DAC 和 ADC
CS4270_WriteReg(CS4270_REG_DAC_CTRL, 0x03); // 示例配置,DAC 输出使能,静音关闭
// ... 其他寄存器配置

return true;
}

bool AudioCodec_SetConfig(AudioCodecHandleTypeDef *hCodec, AudioCodecConfigTypeDef *config) {
// 根据配置参数设置 CS4270 寄存器,例如采样率、位深度等
// ...
hCodec->config = *config;
return true;
}

bool AudioCodec_StartRecord(AudioCodecHandleTypeDef *hCodec) {
// 启动 CS4270 ADC 录音
// ... 设置 CS4270 控制寄存器
return true;
}

bool AudioCodec_StopRecord(AudioCodecHandleTypeDef *hCodec) {
// 停止 CS4270 ADC 录音
// ... 设置 CS4270 控制寄存器
return true;
}

bool AudioCodec_StartPlayback(AudioCodecHandleTypeDef *hCodec) {
// 启动 CS4270 DAC 播放
// ... 设置 CS4270 控制寄存器
return true;
}

bool AudioCodec_StopPlayback(AudioCodecHandleTypeDef *hCodec) {
// 停止 CS4270 DAC 播放
// ... 设置 CS4270 控制寄存器
return true;
}

bool AudioCodec_ReadData(AudioCodecHandleTypeDef *hCodec, uint8_t *pData, uint16_t Size, uint32_t Timeout) {
// 从 CS4270 ADC 读取数据
// ... 使用 HAL ADC 接口 (假设 CS4270 音频数据通过 ADC 输出)
// 或者使用 I2S 接口 (如果 CS4270 使用 I2S 音频接口)
// ... 具体实现取决于 CS4270 的接口和 HAL 层提供的接口
return true;
}

bool AudioCodec_WriteData(AudioCodecHandleTypeDef *hCodec, uint8_t *pData, uint16_t Size, uint32_t Timeout) {
// 将数据写入 CS4270 DAC 进行播放
// ... 使用 HAL DAC 接口 (假设 CS4270 音频数据通过 DAC 输入)
// 或者使用 I2S 接口 (如果 CS4270 使用 I2S 音频接口)
// ... 具体实现取决于 CS4270 的接口和 HAL 层提供的接口
return true;
}

// 内部函数实现:读写 CS4270 寄存器 (使用 HAL I2C)
static uint8_t CS4270_ReadReg(uint8_t reg_addr) {
// ... 使用 HAL I2C 发送读命令,并读取数据
return 0; // 示例
}

static void CS4270_WriteReg(uint8_t reg_addr, uint8_t reg_value) {
// ... 使用 HAL I2C 发送写命令和数据
}

(3) MMDVM 协议层 (MMDVM Protocol)

mmdvm_protocol.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
#ifndef MMDVM_PROTOCOL_H
#define MMDVM_PROTOCOL_H

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

typedef enum {
MMDVM_MODE_DSTAR,
MMDVM_MODE_DMR,
MMDVM_MODE_FUSION,
MMDVM_MODE_P25,
// ... 更多模式
MMDVM_MODE_MAX
} MMDVM_ModeTypeDef;

typedef struct {
MMDVM_ModeTypeDef mode;
// ... 其他 MMDVM 协议配置参数
} MMDVM_ConfigTypeDef;

typedef struct {
MMDVM_ConfigTypeDef config;
// ... 其他状态信息
} MMDVM_HandleTypeDef;

// MMDVM 协议层函数声明
bool MMDVM_Init(MMDVM_HandleTypeDef *hMmdvm, MMDVM_ConfigTypeDef *config);
bool MMDVM_SetMode(MMDVM_HandleTypeDef *hMmdvm, MMDVM_ModeTypeDef mode);
bool MMDVM_ProcessRxData(MMDVM_HandleTypeDef *hMmdvm, uint8_t *pData, uint16_t Size);
bool MMDVM_GetTxData(MMDVM_HandleTypeDef *hMmdvm, uint8_t *pData, uint16_t Size);
// ...

#endif // MMDVM_PROTOCOL_H

mmdvm_protocol_core.c (MMDVM 协议核心逻辑,模式无关部分)

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 "mmdvm_protocol.h"
#include "modulation.h" // 假设有调制解调模块
#include "encoding.h" // 假设有编码解码模块

bool MMDVM_Init(MMDVM_HandleTypeDef *hMmdvm, MMDVM_ConfigTypeDef *config) {
hMmdvm->config = *config;
return true;
}

bool MMDVM_SetMode(MMDVM_HandleTypeDef *hMmdvm, MMDVM_ModeTypeDef mode) {
hMmdvm->config.mode = mode;
// 加载并初始化对应模式的协议模块 (例如,D-STAR 模块、DMR 模块等)
// ...
return true;
}

bool MMDVM_ProcessRxData(MMDVM_HandleTypeDef *hMmdvm, uint8_t *pData, uint16_t Size) {
// 1. 解调射频数据
uint8_t demodulated_data[Size];
Modulation_Demodulate(hMmdvm->config.mode, pData, Size, demodulated_data);

// 2. 解码数据 (例如,AMBE++, Codec2 等)
uint8_t decoded_audio_data[Size]; // 音频数据大小可能不同
Encoding_Decode(hMmdvm->config.mode, demodulated_data, Size, decoded_audio_data);

// 3. 帧处理、协议解析、错误校验等
// ... 根据 MMDVM 协议规范进行处理

// 4. 将解码后的音频数据传递给应用逻辑层
// ... 可以使用回调函数或者消息队列

return true;
}

bool MMDVM_GetTxData(MMDVM_HandleTypeDef *hMmdvm, uint8_t *pData, uint16_t Size) {
// 1. 从应用逻辑层获取要发射的音频数据
// ...

// 2. 编码音频数据 (例如,AMBE++, Codec2 等)
uint8_t encoded_data[Size]; // 编码后数据大小可能不同
Encoding_Encode(hMmdvm->config.mode, pData, Size, encoded_data);

// 3. 帧封装、协议封装等
// ... 根据 MMDVM 协议规范进行封装

// 4. 调制数据
Modulation_Modulate(hMmdvm->config.mode, encoded_data, Size, pData);

// 5. 将调制后的数据返回给调用者,用于射频发射
return true;
}

(4) 应用逻辑层 (Application Logic)

app_logic.h:

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

#include <stdint.h>
#include <stdbool.h>
#include "mmdvm_protocol.h"
#include "audio_codec_driver.h"
#include "rf_transceiver_driver.h"

// 应用逻辑层函数声明
bool AppLogic_Init(void);
void AppLogic_Run(void); // 主循环
void AppLogic_HandleRxAudioData(uint8_t *pData, uint16_t Size);
void AppLogic_GetTxAudioData(uint8_t *pData, uint16_t Size);
// ...

#endif // APP_LOGIC_H

app_logic.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
#include "app_logic.h"
#include "mmdvm_protocol.h"
#include "audio_codec_driver.h"
#include "rf_transceiver_driver.h"
#include "hal.h"

// 设备句柄
MMDVM_HandleTypeDef hMmdvm;
AudioCodecHandleTypeDef hCodec;
RF_TransceiverHandleTypeDef hTxTransceiver; // 发射收发器
RF_TransceiverHandleTypeDef hRxTransceiver; // 接收收发器 (双工板需要两个)

bool AppLogic_Init(void) {
// 初始化 HAL 层
// ... 系统时钟、外设时钟初始化

// 初始化设备驱动
AudioCodec_Init(&hCodec);
RF_Transceiver_Init(&hTxTransceiver);
RF_Transceiver_Init(&hRxTransceiver);

// 初始化 MMDVM 协议层
MMDVM_ConfigTypeDef mmdvm_config;
mmdvm_config.mode = MMDVM_MODE_DMR; // 默认模式
MMDVM_Init(&hMmdvm, &mmdvm_config);

// 初始化其他应用逻辑模块
// ...

return true;
}

void AppLogic_Run(void) {
uint8_t rx_rf_buffer[128]; // 接收射频数据缓冲区
uint8_t tx_audio_buffer[128]; // 发射音频数据缓冲区
uint8_t rx_audio_buffer[128]; // 接收音频数据缓冲区
uint8_t tx_rf_buffer[128]; // 发射射频数据缓冲区

// 启动音频 Codec 录音和播放
AudioCodec_StartRecord(&hCodec);
AudioCodec_StartPlayback(&hCodec);

// 设置射频收发器进入接收模式 (接收收发器)
RF_Transceiver_SetMode(&hRxTransceiver, RF_TRANSCEIVER_STATE_RX);

while (1) {
// 1. 接收射频数据 (接收收发器)
if (RF_Transceiver_ReceiveData(&hRxTransceiver, rx_rf_buffer, sizeof(rx_rf_buffer), 10)) { // 10ms 超时
// 2. MMDVM 协议处理接收到的射频数据
MMDVM_ProcessRxData(&hMmdvm, rx_rf_buffer, sizeof(rx_rf_buffer));
}

// 3. 从音频 Codec 读取音频数据 (用于发射)
if (AudioCodec_ReadData(&hCodec, tx_audio_buffer, sizeof(tx_audio_buffer), 10)) { // 10ms 超时
// 4. MMDVM 协议处理发射音频数据,获取发射射频数据
MMDVM_GetTxData(&hMmdvm, tx_rf_buffer, sizeof(tx_rf_buffer));

// 5. 发射射频数据 (发射收发器)
RF_Transceiver_SetMode(&hTxTransceiver, RF_TRANSCEIVER_STATE_TX); // 切换到发射模式
RF_Transceiver_TransmitData(&hTxTransceiver, tx_rf_buffer, sizeof(tx_rf_buffer));
RF_Transceiver_SetMode(&hTxTransceiver, RF_TRANSCEIVER_STATE_IDLE); // 发射完成后切换回空闲或接收模式
}

// 6. 处理接收到的音频数据 (例如,播放到扬声器)
// ... 在 MMDVM_ProcessRxData 中,解码后的音频数据应该传递到这里
// ... 可以使用回调函数或者消息队列,这里假设有 AppLogic_HandleRxAudioData 函数处理
// AppLogic_HandleRxAudioData(rx_audio_buffer, sizeof(rx_audio_buffer));

// 7. 其他应用逻辑处理 (例如,按键检测、状态指示、配置管理等)
// ...

HAL_TIM_DelayMs(1); // 1ms 任务周期
}
}

// 接收音频数据处理回调函数 (示例)
void AppLogic_HandleRxAudioData(uint8_t *pData, uint16_t Size) {
// 将接收到的音频数据写入音频 Codec 进行播放
AudioCodec_WriteData(&hCodec, pData, Size, HAL_MAX_DELAY);
}

// 获取发射音频数据函数 (示例)
void AppLogic_GetTxAudioData(uint8_t *pData, uint16_t Size) {
// 从音频输入设备 (例如,麦克风) 读取音频数据
// 这里简化为直接从音频 Codec 读取,实际应用中可能需要更复杂的处理
AudioCodec_ReadData(&hCodec, pData, Size, HAL_MAX_DELAY);
}

(5) main.c (主程序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "app_logic.h"

int main(void) {
// 系统初始化 (硬件初始化、HAL 初始化、RTOS 初始化 - 如果使用 RTOS)
// ...

// 应用逻辑初始化
AppLogic_Init();

// 启动应用逻辑主循环
AppLogic_Run();

return 0;
}

项目中采用的各种技术和方法

  1. 分层架构: 将系统划分为 HAL, 设备驱动层, MMDVM 协议层, 应用逻辑层, OS 层,提高代码的可维护性、可移植性和可扩展性。
  2. 模块化设计: 在每个层次内部,进一步划分为独立的模块,例如 MMDVM 协议层可以分为 D-STAR 模块、DMR 模块等,设备驱动层可以分为 ADF7021 驱动、CS4270 驱动等,提高代码的复用性和灵活性。
  3. 硬件抽象层 (HAL): 隔离硬件差异,方便代码移植到不同的硬件平台,降低硬件更换带来的软件修改成本。
  4. 设备驱动: 封装硬件设备的具体操作细节,向上层提供统一的功能性接口,简化上层应用开发。
  5. 实时操作系统 (RTOS - 可选): 使用 RTOS (例如 FreeRTOS) 可以更好地管理系统资源,实现多任务并发执行,提高系统的实时性和响应性,尤其适用于需要处理实时通信和多任务并发的嵌入式系统。
  6. 状态机: 在 MMDVM 协议层,可以使用状态机来管理复杂的协议流程,例如连接建立、数据传输、断开连接等状态的切换。
  7. 中断驱动: 充分利用硬件中断,提高系统的实时响应能力,例如射频收发器接收到数据、音频 Codec 数据就绪等事件可以通过中断通知 CPU 进行处理。
  8. 环形缓冲区 (Ring Buffer): 在音频数据处理、射频数据处理等场景中,可以使用环形缓冲区来提高数据处理效率,避免数据丢失。
  9. 异步通信: 在 HAL 层和设备驱动层,可以考虑使用异步通信方式 (例如,DMA - Direct Memory Access) 来提高数据传输效率,降低 CPU 负载。
  10. 错误处理机制: 在代码中加入完善的错误处理机制,例如参数校验、异常检测、错误日志记录、错误恢复等,提高系统的健壮性和可靠性。
  11. 代码注释和文档: 编写清晰的代码注释,并编写相应的系统设计文档、API 文档等,方便代码维护和团队协作。
  12. 版本控制 (Git): 使用 Git 进行代码版本控制,方便代码管理、协作开发、版本回溯等。
  13. 单元测试和集成测试: 进行充分的单元测试和集成测试,验证代码的正确性和稳定性。
  14. 代码审查: 进行代码审查,提高代码质量,发现潜在的 Bug 和设计缺陷。

总结

以上代码框架和技术方法是构建 MMDVM_HS_Dual_Hat 双工板嵌入式系统的一个良好起点。实际项目开发中,还需要根据具体的硬件平台、MMDVM 模式需求、性能要求等进行详细设计和优化。例如,针对不同的数字语音模式,需要实现相应的调制解调算法、编码解码算法、协议处理逻辑等。对于双工板,需要仔细设计双工通信的流程和资源管理,避免冲突和干扰。此外,还需要进行大量的实践验证和测试,才能最终构建一个可靠、高效、可扩展的 MMDVM 嵌入式系统平台。

希望这份详细的架构说明和代码框架能够帮助您理解 MMDVM_HS_Dual_Hat 双工板嵌入式系统的开发思路。如果您有任何疑问或需要更深入的探讨,请随时提出。

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