一、项目概述
本项目基于 GD32H737VMT6 单片机,利用片上定时器、DMA、GPIO 外设,实现一套完整的数字信号收发与同步解调链路。无需专用通信芯片,仅通过软件算法完成高速率、高可靠性的通信验证,可直接应用于自定义串口、无线基带、高速数据传输等场景。
核心功能
- 1Mbps 码率数字波形发送
- 5MHz 硬件过采样接收
- DMA 乒乓缓冲,采样与处理并行执行
- 位同步(DTTL)自动校正时钟偏差
- 帧同步(前导码检测)实现帧数据对齐
- 误码率自动统计,实测 0 误码稳定传输
二、整体工作逻辑
1. 信号发送逻辑
- 定义待发送字节数据,将其按位展开为比特流
- 构建 DMA 发送缓冲区,每一位对应 GPIO 置 1 或清 0 操作
- 定时器 1 以 1Mbps 频率触发 DMA 请求
- DMA 直接将波形数据搬运至 GPIO 输出,全程不占用 CPU
2. 信号接收与乒乓缓冲逻辑
- 定时器 2 以 5MHz 频率对输入信号进行采样
- DMA 持续将采样值搬运至接收缓冲区
- 接收缓冲区分为前半段、后半段,形成乒乓结构
- 半满中断触发:处理前半段采样数据
- 全满中断触发:处理后半段采样数据
- 采样与解调并行执行,无数据丢失、无覆盖
3. 解调与同步逻辑
- 将采样值归一化为判决电平,便于比特识别
- 位同步:动态调整采样点,保证每一位采样位置准确
- 帧同步:匹配预设前导码,定位有效数据起始位置
- 按字节重组数据,完成帧解包
- 与原始发送数据对比,统计总比特数、错误比特数,计算误码率
三、核心模块实现逻辑
1. DMA 乒乓操作
乒乓缓冲是保证 1Mbps 速率下不丢采样点的关键机制。DMA 填充缓冲区时,CPU 可处理另一半区域数据,实现采样与处理并行。
-
半传输完成中断:标记前半段缓冲区就绪
-
全传输完成中断:标记后半段缓冲区就绪
-
中断服务函数仅做标志位处理,无耗时运算
void DMA1_Channel2_IRQHandler(void)
{
// 半传输完成
if(dma_interrupt_flag_get(DMA1, DMA_CH2, DMA_INT_FLAG_HTF))
{
dma_interrupt_flag_clear(DMA1, DMA_CH2, DMA_INT_FLAG_HTF);
rx_half_collision = processing_flag;
rx_half_flag = 1;
}
// 全传输完成
if(dma_interrupt_flag_get(DMA1, DMA_CH2, DMA_INT_FLAG_FTF))
{
dma_interrupt_flag_clear(DMA1, DMA_CH2, DMA_INT_FLAG_FTF);
rx_full_collision = processing_flag;
rx_full_flag = 1;
}
}
主循环根据中断标志处理对应缓冲区:
if(rx_half_flag)
{
processing_flag = 1;
rx_half_flag = 0;
process_rx_data(dma_rx_buf, SAMPLE_COUNT/2, 1);
processing_flag = 0;
}
else if(rx_full_flag)
{
processing_flag = 1;
rx_full_flag = 0;
process_rx_data(&dma_rx_buf[SAMPLE_COUNT/2], SAMPLE_COUNT/2, 2);
processing_flag = 0;
}
2. 位同步逻辑
位同步用于消除收发时钟偏差带来的采样错位,是实现 0 误码的核心。通过 PID 控制器动态调整采样步长,锁定最佳采样时刻。
-
对采样数据分块取平均,完成比特判决
-
检测比特边沿跳变,计算采样位置误差
-
动态修正采样时钟,保证每一位采样稳定可靠
// ===================== 位同步 + 比特判决(ARM_MATH加速) ===================== for (int sym = 0; sym < total_symbols; sym++) { float step = sps * sync_feed; if ((int32_t)start_idx >= input_len) { new_bit = 0; bits[sym] = new_bit; continue; } int32_t block_start = (int32_t)roundf(start_idx); int32_t block_size = (int32_t)step; if (block_start + block_size > input_len) { block_size = input_len - block_start; } if (block_size <= 0) { new_bit = 0; bits[sym] = new_bit; continue; } // ===== 核心加速:arm_mean_q7(这个函数一定存在)===== q7_t mean_val; arm_mean_q7(&input[block_start], block_size, &mean_val); new_bit = (mean_val < 0) ? 0 : 1; bits[sym] = new_bit; // ===== 边沿检测 + PID ===== if (new_bit != old_bit && start_idx > 2 * sps) { float half_sps = step / 2.0f; int32_t sum_start = (int32_t)roundf(start_idx - half_sps); int32_t sum_len = (int32_t)roundf(sps); if (sum_start < 0) sum_start = 0; if (sum_start + sum_len > input_len) { sum_len = input_len - sum_start; } // ===== 修复:替换为安全求和 ===== q7_t sum_val = 0; for(int k=0; k<sum_len; k++){ sum_val += input[sum_start + k]; } float err = (new_bit == 1) ? -(float)sum_val : (float)sum_val; float p = kp * err; integral += err; float i = ki * integral; sync_feed = 1.0f + p + i; if (sync_feed < 0.7f || sync_feed > 1.3f) { sync_feed = 1.0f; integral = 0.0f; }// printf("%f,", step);
}old_bit = new_bit; start_idx += step; }
3. 帧同步逻辑
位同步保证单比特正确,帧同步用于确定一帧数据的起始位置,避免帧错位。
-
预设 16bit 固定前导码
-
在解调比特流中逐位匹配前导码
-
匹配成功后,从当前位置开始解包有效数据
int preamble_pos = -1;
for (int i = 0; i <= total_symbols - PREAMBLE_LENGTH; i++)
{
int match = 1;
for (int j = 0; j < PREAMBLE_LENGTH; j++)
{
if (bits[i + j] != FSK_PREAMBLE[j])
{
match = 0;
break;
}
}
if (match)
{
preamble_pos = i;
break;
}
}
4. 1Mbps 速率配置逻辑
系统主频 300MHz,通过定时器周期精准控制发送与采样频率:
-
发送码率:1Mbps
-
接收采样率:5MHz(5 倍过采样)
-
定时器无分频,直接由主频驱动,时序精度高
#define SYSTEM_CLOCK 300000000U
#define TX_BAUD_RATE 1000000U
#define TIMER_TX_PERIOD (SYSTEM_CLOCK / TX_BAUD_RATE - 1U)#define RX_SAMPLE_RATE 5000000U
#define TIMER_RX_PERIOD (SYSTEM_CLOCK / RX_SAMPLE_RATE - 1U)
5. 误码率统计逻辑
通过逐位对比发送数据与接收数据,统计通信可靠性:
-
按字节异或,判断比特是否错误
-
累计总传输比特数与错误比特数
-
误码率 = 错误比特数 / 总比特数
void calculate_ber(const uint8_t *tx_bytes, uint32_t tx_len,
const uint8_t *rx_bytes, uint32_t rx_len)
{
uint32_t compare_len = rx_len;
if (compare_len == 0) return;for (uint32_t i = 0; i < compare_len - 1; i++) { uint8_t tx = tx_bytes[i % TX_DATA_LEN]; uint8_t rx = rx_bytes[i]; uint8_t xor_val = tx ^ rx; ber_stat.total_bits += 8; for (int j = 0; j < 8; j++) { if (xor_val & (1 << j)) ber_stat.error_bits++; } }}
四、测试结果
程序完成 100 次收发测试后,串口输出误码率统计:
plaintext
[BER统计] 总比特:102400 错误:0 误码率:0.000000
在 1Mbps 码率下,实现连续稳定的 0 误码通信。
五、适用场景
- 自定义高速数字通信接口
- 软件无线电基带信号处理
- 无线模块数字解调
- 工业高速数据传输
- 通信原理教学与实验