GD32H737 1Mbps 数字通信链路实现

一、项目概述

本项目基于 GD32H737VMT6 单片机,利用片上定时器、DMA、GPIO 外设,实现一套完整的数字信号收发与同步解调链路。无需专用通信芯片,仅通过软件算法完成高速率、高可靠性的通信验证,可直接应用于自定义串口、无线基带、高速数据传输等场景。

核心功能

  • 1Mbps 码率数字波形发送
  • 5MHz 硬件过采样接收
  • DMA 乒乓缓冲,采样与处理并行执行
  • 位同步(DTTL)自动校正时钟偏差
  • 帧同步(前导码检测)实现帧数据对齐
  • 误码率自动统计,实测 0 误码稳定传输

二、整体工作逻辑

1. 信号发送逻辑

  1. 定义待发送字节数据,将其按位展开为比特流
  2. 构建 DMA 发送缓冲区,每一位对应 GPIO 置 1 或清 0 操作
  3. 定时器 1 以 1Mbps 频率触发 DMA 请求
  4. DMA 直接将波形数据搬运至 GPIO 输出,全程不占用 CPU

2. 信号接收与乒乓缓冲逻辑

  1. 定时器 2 以 5MHz 频率对输入信号进行采样
  2. DMA 持续将采样值搬运至接收缓冲区
  3. 接收缓冲区分为前半段、后半段,形成乒乓结构
  4. 半满中断触发:处理前半段采样数据
  5. 全满中断触发:处理后半段采样数据
  6. 采样与解调并行执行,无数据丢失、无覆盖

3. 解调与同步逻辑

  1. 将采样值归一化为判决电平,便于比特识别
  2. 位同步:动态调整采样点,保证每一位采样位置准确
  3. 帧同步:匹配预设前导码,定位有效数据起始位置
  4. 按字节重组数据,完成帧解包
  5. 与原始发送数据对比,统计总比特数、错误比特数,计算误码率

三、核心模块实现逻辑

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 控制器动态调整采样步长,锁定最佳采样时刻。

  1. 对采样数据分块取平均,完成比特判决

  2. 检测比特边沿跳变,计算采样位置误差

  3. 动态修正采样时钟,保证每一位采样稳定可靠

    复制代码
     	// ===================== 位同步 + 比特判决(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. 帧同步逻辑

位同步保证单比特正确,帧同步用于确定一帧数据的起始位置,避免帧错位。

  1. 预设 16bit 固定前导码

  2. 在解调比特流中逐位匹配前导码

  3. 匹配成功后,从当前位置开始解包有效数据

    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. 误码率统计逻辑

通过逐位对比发送数据与接收数据,统计通信可靠性:

  1. 按字节异或,判断比特是否错误

  2. 累计总传输比特数与错误比特数

  3. 误码率 = 错误比特数 / 总比特数

    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 误码通信。


五、适用场景

  • 自定义高速数字通信接口
  • 软件无线电基带信号处理
  • 无线模块数字解调
  • 工业高速数据传输
  • 通信原理教学与实验
相关推荐
LCG元3 小时前
STM32实战:基于STM32F103的MQTT协议通信(EMQ X Broker)
stm32·单片机·嵌入式硬件
zmj3203243 小时前
51单片机
单片机·嵌入式硬件·51单片机
zmj3203243 小时前
MCS-51单片机
单片机·嵌入式硬件·51单片机
小柯博客3 小时前
从零开始打造 OpenSTLinux 6.6 Yocto 系统 - STM32MP2(基于STM32CubeMX)(八)
c语言·git·stm32·单片机·嵌入式硬件·嵌入式·yocto
421!12 小时前
GPIO工作原理以及核心
开发语言·单片机·嵌入式硬件·学习
cmpxr_17 小时前
【单片机】STM32的启动流程(Keil)
stm32·单片机·嵌入式硬件
广药门徒17 小时前
嵌入式常用通信协议速率对比及布线要点全解析
单片机·嵌入式硬件
cmpxr_18 小时前
【单片机】RAM和ROM
单片机·嵌入式硬件
yong999021 小时前
可自动调整的24V步进电机设计方案
单片机·嵌入式硬件