1. 信号分离装置的核心挑战
2023年全国大学生电子设计竞赛H题"信号分离装置"看似简单,实则暗藏玄机。题目要求将混合信号A+B分离还原,并实现A与A'信号的同步显示。实测中发现,当信号源A和再生信号A'分别来自不同时钟源时,即使标称频率完全相同,示波器上也会出现明显的相位漂移现象。
这个问题困扰了我们团队整整三天。最初我们以为只是代码bug,后来用频谱仪测量才发现:信号发生器的20.000kHz和单片机生成的20.000kHz,实际频率差可能达到0.002Hz。这个微小差异在短时间内难以察觉,但经过10秒累积就会产生20ms的相位差——相当于半个周期!
2. 同频锁相的三种技术路线
2.1 硬件锁相环方案
传统PLL芯片如CD4046是经典解决方案。我们测试时发现,当输入信号质量较差时(如含噪声的三角波),PLL可能失锁。关键参数设置:
- 低通滤波器截止频率:建议设为信号频率的1/10
- VCO中心频率:设置为目标频率的1.2倍
// 硬件PLL配置示例 void PLL_Config(void) { RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; GPIOB->CRL &= ~(GPIO_CRL_CNF1 | GPIO_CRL_MODE1); // PB1输入 AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI1_PB; // EXTI1映射到PB1 }2.2 软件锁相实现
基于STM32的输入捕获功能,我们开发了数字锁相算法。核心思路:
- 用TIM2的输入捕获测量信号周期
- 动态调整DDS输出频率
- 加入PID控制改善稳定性
实测中发现,当信号频率突变时,软件方案需要3-5个周期才能重新锁定。改进方法是加入频率预测算法:
float predict_next_period(float history[], int len) { float trend = 0; for(int i=1; i<len; i++){ trend += (history[i]-history[i-1]); } return history[len-1] + trend/(len-1); }2.3 主从时钟级联方案
这是最稳定的解决方案,但实现复杂度最高。关键步骤:
- 将TIM1配置为主模式,触发TIM2
- TIM2配置为从模式,使用ITR1触发源
- 通过调整TIM1的ARR值微调频率
我们遇到的坑是:STM32F103的定时器级联存在1个时钟周期的触发延迟,需要在软件中补偿:
// 主从定时器配置 void Timer_Cascade_Config(void) { TIM1->CR2 |= TIM_CR2_MMS_1; // TRGO输出更新事件 TIM2->SMCR |= TIM_SMCR_SMS_2 | TIM_SMCR_TS_2; // 从模式,ITR1触发 TIM2->CNT = 1; // 补偿触发延迟 }3. 信号分离的关键技术细节
3.1 FFT参数优化
题目要求的5kHz分辨率决定了FFT窗口大小。对于72MHz时钟的STM32,我们推荐配置:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 采样频率 | 250kHz | 满足奈奎斯特准则 |
| 采样点数 | 1024 | 频率分辨率≈244Hz |
| 窗函数 | 汉宁窗 | 降低频谱泄漏 |
| 幅值检测阈值 | 0.3V | 需根据噪声水平调整 |
实际调试中发现,三角波的5次谐波幅值可能比基波还大。我们的解决方案是加权判断:
int is_triangle_wave(float* spectrum) { float fundamental = spectrum[target_bin]; float third_harmonic = spectrum[target_bin*3]; float fifth_harmonic = spectrum[target_bin*5]; return (fifth_harmonic > fundamental*0.3) && (third_harmonic > fundamental*0.5); }3.2 DDS波形合成技巧
使用STM32的DAC+DMA实现DDS时,要注意:
- 波形表长度建议取128点以上
- 开启DMA循环模式
- 定时器触发频率=目标频率×波形表长度
我们总结的波形生成公式:
void generate_wave_table(uint16_t* table, int len, float (*wave_func)(float)) { for(int i=0; i<len; i++){ float angle = 2*PI*i/len; table[i] = 2048 + 2047*wave_func(angle); } }4. 调试经验与性能优化
4.1 示波器使用技巧
同步显示调试的关键点:
- 始终使用同一时钟源触发所有通道
- 开启无限余辉模式观察长期稳定性
- 测量时间差时使用光标功能
我们发现的实用技巧:将触发源设为信号A,然后观察A'的相位漂移速度,可以快速判断频率差方向。
4.2 系统延迟测量
信号处理链路的总延迟会影响同步精度。我们设计的测量方法:
- 输入脉冲信号
- 记录输入到输出的时间差
- 多次测量取平均值
实测数据表明,我们的系统存在约8us的固定延迟,需要在软件中补偿:
#define SYSTEM_LATENCY_US 8 void compensate_latency(void) { TIM1->ARR = desired_period - SYSTEM_LATENCY_US; }4.3 抗干扰设计
比赛现场电磁环境复杂,我们采取了以下措施:
- 所有模拟信号线使用屏蔽线
- 电源入口加π型滤波
- 关键芯片的退耦电容并联0.1μF和10μF
- 数字地与模拟地单点连接
5. 开源代码深度解析
我们在GitHub开源的方案包含以下关键模块:
fft_analyzer.c:实时频谱分析dds_engine.c:可调频波形合成pll_controller.c:数字锁相环实现signal_detector.c:波形特征识别
重点说明PLL控制器的设计思路:
- 相位检测器采用异或门实现
- 环路滤波器使用IIR结构
- DDS频率控制字采用32位高精度
// 数字PLL核心算法 void PLL_Update(int edge_delta) { static int32_t phase_error = 0; phase_error += edge_delta; float freq_adjust = KP * edge_delta + KI * phase_error; dds_freq += freq_adjust; }6. 未解决问题的思考
虽然我们最终实现了基本功能,但仍有改进空间:
- 频率切换时的锁定时间可以优化
- 极端频率(如99kHz)时的稳定性不足
- 自动增益控制功能未实现
特别是当输入信号含有噪声时,我们的方案会出现误判。可能的改进方向是加入数字滤波算法:
float adaptive_filter(float new_sample) { static float history[4] = {0}; float avg = (history[0]+history[1]+history[2]+history[3])/4; float filtered = 0.8*avg + 0.2*new_sample; // 更新历史数据 for(int i=3; i>0; i--) history[i] = history[i-1]; history[0] = filtered; return filtered; }这次比赛让我们深刻认识到,理论方案和实际实现之间往往存在巨大鸿沟。特别是在时间紧迫的情况下,必须学会做合理的取舍。建议后续开发者提前准备常用算法模块,比如FFT、DDS等,把精力集中在最核心的创新点上。