目录
一、赛题亮相:H 题是什么
2023 年的电子设计大赛可谓是电子领域的一场年度盛宴,吸引了无数电子爱好者和专业学子投身其中。在众多富有挑战性的赛题里,H 题 “信号分离装置” 脱颖而出,成为了大家热议和钻研的焦点。这道题旨在考察参赛者对信号处理、电路设计以及编程控制等多方面知识的综合运用能力,在整个竞赛中占据着举足轻重的地位,其难度和创新性也让不少参赛队伍绞尽脑汁 。
H 题的任务是设计并制作一个信号分离装置。具体来说,一台双路输出信号源会输出 2 路周期信号 A 和 B,它们的频率范围在 20kHz - 100kHz 之间,并且满足\(f_A<f_B\) ,峰峰值均为 1V。这两路信号经过一个增益为 1 的加法器后,产生混合信号 C。而我们的核心任务就是让信号 C 通过设计的分离电路,成功分离出信号 A’和 B’,并且要保证信号 A’和 B’与原始信号 A 和 B 相比波形无失真,同时 A’和 A、B’和 B 的波形在示波器上能连续稳定同频显示。
从基本要求到发挥部分,H 题的难度层层递进。基本要求中,首先要制作一个增益为 1 的加法器实现 C = A + B,这一步相对基础,主要是考察大家对基本电路搭建的掌握。接着,当信号 A 和 B 均为正弦波,且\(f_A = 50kHz\) ,\(f_B = 100kHz\)时,要求装置能正确分离出信号 A’和 B’ ,且峰峰值均不小于 1V,这就需要大家开始思考信号分离的方法了。进一步,当信号 A 和 B 均为正弦波,频率分别为 10kHz 的整数倍时,同样要实现准确分离,此时频率的不确定性增加了难度。
而发挥部分则更具挑战性。信号 A 和 B 可以是正弦波或三角波,频率分别为 5kHz 的整数倍,要实现分离难度不小;在特定条件下,还要求装置能设置并控制信号 B’与 A’的初相位差,范围 0° - 180° ,设置分辨率 5° ,误差绝对值不大于 5° 。这些要求不仅考验硬件电路的设计,更对软件算法和代码实现提出了很高的要求,需要参赛者具备扎实的专业知识和灵活的创新思维 。
二、火眼金睛:H 题难点剖析
(一)信号分离困境
在解决 H 题时,最直观的想法便是采用滤波的方式将混合信号 C 中的 A 和 B 信号分离开来。但实际操作中,直接滤波的方法困难重重。以模拟滤波器为例,当遇到频率极为接近的信号,比如 95kHz 和 100kHz 的信号,其频率相差仅 5%,这就要求滤波器的过渡带极窄 。可常见电容的精度很难达到 5% 以上,这使得高阶模拟滤波器难以实现准确的截止频率和极窄的过渡带。如果提高滤波器阶数,虽能在一定程度上提高分离度,但又会引发新的问题。对于模拟滤波器,电容、电感等元件参数的误差会随着阶数增加而累计,导致滤波器性能不稳定;对于数字滤波器,高阶的 IIR 滤波器稳定性欠佳,而高阶的 FIR 滤波器计算量会大幅增加,难以满足实时性要求,在实际竞赛中,时间就是关键,无法满足实时性无疑是致命的 。
(二)重建信号挑战
当直接滤波难以实现时,信号重建成为了另一种可行思路。这种方案是让分离电路重新生成 C 信号中含有的 A、B 两个信号。但它也面临着严峻的挑战。由于信号发生器产生的 A、B 信号和重新产生的 A'、B' 信号频率不可能绝对准确,精度大约在 0.01% 左右,也就是万分之一。对于 100KHz 的信号,最糟糕的情况下,两者之间每秒将相差 ±10 个左右的波形。这就导致如果用示波器同时显示原始信号和重建信号,会出现无法稳定同频显示的现象,比如用 A 信号作为触发源,A' 信号每秒在屏幕上可能会向左或向右 “滚动” 最多 20 个波形 。要解决这个问题,就需要不断地实时测量 A 与 A'、B 与 B' 之间的相位差,并根据这个相位差不断调整重建的 A' 和 B' 的相位,这对算法的精度和实时性提出了极高的要求,如何在有限的时间内实现精准的相位测量和调整,成为了参赛者们需要攻克的难题 。
三、条条大路通罗马:解题思路探讨
面对 H 题的重重挑战,参赛者们各显神通,提出了多种解题思路和技术方案 ,每一种方案都蕴含着独特的智慧和巧妙的设计。
(一)全数字方案
全数字方案是一种较为纯粹的信号处理方式。在这个方案中,首先要对混合信号 C 进行采样,将其从模拟信号转换为数字信号,以便后续在数字域进行处理 。采样的精度和频率至关重要,它直接影响到后续计算的准确性和信号重建的质量。例如,若采样频率过低,可能会导致信号混叠,丢失重要的频率信息;而采样精度不足,则会引入量化误差,影响信号的准确性 。
采样完成后,便要在数字域计算 A、B 信号的频率。这通常需要运用一些数字信号处理算法,如快速傅里叶变换(FFT) 。FFT 能够将时域信号转换为频域信号,通过分析频域信号的峰值位置,我们可以准确地确定 A、B 信号的频率。确定频率后,就可以利用数模转换器(DAC)重建同频率的两路信号 A' 和 B' 。DAC 的作用是将数字信号转换回模拟信号,在这个过程中,需要根据之前计算得到的频率信息,生成相应的数字波形,并通过 DAC 输出模拟信号 。
然而,由于信号发生器产生的 A、B 信号和重新产生的 A'、B' 信号频率不可能绝对准确,会存在一定的相位差。为了使示波器上能稳定显示原信号和重建信号,就需要不停地重复计算输入的 A、B 与 A'、B' 的相位差,并调整 A'、B' 的相位 。这一步骤需要实时进行,对处理器的计算能力和算法的效率提出了很高的要求。通常可以采用锁相环(PLL)技术来实现相位的跟踪和调整,PLL 能够根据输入信号和输出信号的相位差,自动调整输出信号的相位,使其与输入信号保持同步 。
(二)模拟芯片辅助方案
模拟芯片辅助方案则巧妙地借助了模拟相位测量芯片的力量,其中 AD8302 就是一款常用的模拟相位测量芯片 。AD8302 具有高精度、宽频带的特点,能够精确地测量两个信号之间的相位差 。在这个方案中,首先将混合信号 C 和重建信号 A' 输入到 AD8302 中,AD8302 会对这两个信号进行相位比较,并输出一个与相位差成正比的直流电压信号 。这个直流电压信号就代表了 A 与 A' 之间的相位差信息 。
微控制器(MCU)通过 AD8302 的输出获取实时相位差数据 。MCU 作为整个系统的控制核心,负责数据的采集、处理和控制信号的输出 。它读取 AD8302 输出的直流电压信号,并将其转换为数字量,以便进行后续的计算和处理 。例如,MCU 可以通过内部的模数转换器(ADC)将模拟电压信号转换为数字信号,然后根据预先设定的算法,计算出相位差的具体数值 。
在获取相位差数据后,MCU 会根据这个数据对重建信号的相位进行调整 。调整的方式有多种,比如可以通过改变信号发生器的控制参数,如频率控制字或相位控制字,来实现对信号相位的微调 。也可以采用数字信号处理算法,对重建信号进行相位补偿,使其与原始信号的相位保持一致 。通过不断地测量相位差并调整重建信号的相位,就能够实现信号的稳定分离和显示 。
(三)DDS 芯片重建方案
DDS 芯片重建方案利用了直接数字频率合成(DDS)技术的优势 。DDS 芯片如 AD9833/AD9854 等,能够通过数字方式精确地合成各种频率和相位的信号 。其工作原理是基于相位累加器和查找表,通过不断地累加相位值,并根据相位值从查找表中读取相应的波形数据,最终经过数模转换输出模拟信号 。
在这个方案中,首先由 MCU 根据输入信号 C 的频率分析结果,计算出需要生成的 A' 和 B' 信号的频率和相位参数 。然后,MCU 通过相应的接口,如 SPI 接口,将这些参数传输给 DDS 芯片 。DDS 芯片根据接收到的参数,生成对应的信号 A' 和 B' 。在生成信号的过程中,DDS 芯片能够保证信号的频率和相位精度,并且具有快速的频率切换能力 。
为了满足题目中对信号相位差控制的要求,MCU 需要实时调整 DDS 芯片产生信号的相位 。这可以通过修改 DDS 芯片的相位控制寄存器来实现 。MCU 根据实时测量得到的相位差数据,计算出需要调整的相位值,并将其写入 DDS 芯片的相位控制寄存器中 。DDS 芯片在接收到新的相位控制字后,会立即调整输出信号的相位,从而实现对信号相位差的精确控制 。通过这种方式,能够有效地解决信号重建过程中的相位漂移问题,实现信号的稳定输出和显示 。
四、代码实操:软件实现核心代码展示
(一)核心算法代码片段
在信号分离装置的软件实现中,计算相位差和频率的算法是核心部分。以基于快速傅里叶变换(FFT)计算频率为例,在 C 语言中,借助 STM32 的 DSP 库,代码如下:
#include "arm_math.h"
#define SAMPLES 1024 // 采样点数,通常取2的幂次方,这里为1024点,可根据实际需求调整,点数越多频率分辨率越高,但计算量也越大
#define PI 3.1415926f // 圆周率定义,用于后续计算
arm_cfft_radix4_instance_f32 cfft_inst; // 定义一个CFFT实例,用于快速傅里叶变换计算
float32_t input_buff[SAMPLES]; // 输入采样数据缓冲区,存储从ADC采集到的时域信号数据
float32_t complex_buff[SAMPLES * 2]; // 复数缓冲区,用于存储经过预处理后的时域数据,为FFT计算做准备,长度是采样点数的2倍,因为要存储实部和虚部
float32_t magnitude_buff[SAMPLES]; // 幅度缓冲区,用于存储FFT计算后的频域信号幅度值
// 初始化FFT实例
void fft_init(void) {
arm_cfft_radix4_init_f32(&cfft_inst, SAMPLES, 0, 1); // 使用arm_cfft_radix4_init_f32函数初始化CFFT实例,传入采样点数、是否为逆变换(这里为0表示正变换)、数据存储格式(这里为1表示按复数格式存储)
}
// 计算频率
float32_t calculate_frequency(void) {
// 将输入的时域数据转换为复数格式,实部为输入数据,虚部为0
for (uint16_t i = 0; i < SAMPLES; i++) {
complex_buff[2 * i] = input_buff[i]; // 将时域数据存入复数缓冲区的偶数位,作为实部
complex_buff[2 * i + 1] = 0.0f; // 虚部初始化为0,因为输入的是实数信号
}
arm_cfft_radix4_f32(&cfft_inst, complex_buff); // 执行快速傅里叶变换,将时域信号转换为频域信号,结果存储在complex_buff中
// 计算幅度谱
for (uint16_t i = 0; i < SAMPLES; i++) {
magnitude_buff[i] = sqrtf(complex_buff[2 * i] * complex_buff[2 * i] + complex_buff[2 * i + 1] * complex_buff[2 * i + 1]); // 根据复数的模计算公式,计算每个频率点的幅度值,存储在magnitude_buff中
}
// 寻找幅度最大的频率点(峰值)
uint16_t max_index = 0;
for (uint16_t i = 1; i < SAMPLES / 2; i++) {
if (magnitude_buff[i] > magnitude_buff[max_index]) {
max_index = i; // 遍历幅度缓冲区,找到幅度最大的频率点的索引
}
}
// 根据采样频率和最大幅度索引计算频率
float32_t sample_frequency = 1000000.0f; // 假设采样频率为1MHz,实际应用中需根据ADC的采样设置准确获取,采样频率决定了可测量的最高频率
return (max_index * sample_frequency) / SAMPLES; // 根据奈奎斯特采样定理,计算出对应的频率值并返回
}
在计算相位差时,如果采用过零检测法,代码示例如下:
// 假设已经获取到两路信号的过零时刻数组zero_crossing_time_A和zero_crossing_time_B
// 假设采样周期为sample_period
float calculate_phase_difference(uint32_t *zero_crossing_time_A, uint32_t *zero_crossing_time_B, uint32_t num_samples, float sample_period) {
uint32_t min_num_samples = num_samples;
if (num_samples > sizeof(zero_crossing_time_A) / sizeof(zero_crossing_time_A[0]) || num_samples > sizeof(zero_crossing_time_B) / sizeof(zero_crossing_time_B[0])) {
min_num_samples = sizeof(zero_crossing_time_A) / sizeof(zero_crossing_time_A[0]) < sizeof(zero_crossing_time_B) / sizeof(zero_crossing_time_B[0])?
sizeof(zero_crossing_time_A) / sizeof(zero_crossing_time_A[0]) : sizeof(zero_crossing_time_B) / sizeof(zero_crossing_time_B[0]);
}
float total_phase_difference = 0;
for (uint32_t i = 0; i < min_num_samples; i++) {
float time_difference = (float)(zero_crossing_time_A[i] - zero_crossing_time_B[i]) * sample_period;
float period = (float)(zero_crossing_time_A[i + 1] - zero_crossing_time_A[i]) * sample_period;
float phase_difference = (time_difference / period) * 360.0f;
total_phase_difference += phase_difference;
}
return total_phase_difference / min_num_samples;
}
(二)整体代码架构说明
主程序流程主要包含系统初始化、信号采集、信号处理、信号输出以及相位调整等环节。系统初始化阶段,会对硬件设备如 ADC、DAC、DDS 芯片以及相关的定时器、中断等进行配置 。以基于 STM32 的开发为例,代码如下:
int main(void) {
// 初始化ADC
ADC_InitTypeDef ADC_InitStruct = {0};
ADC_InitStruct.ScanConvMode = DISABLE;
ADC_InitStruct.ContinuousConvMode = ENABLE;
ADC_InitStruct.DiscontinuousConvMode = DISABLE;
ADC_InitStruct.ExternalTrigConv = ADC_SOFTWARE_START;
ADC_InitStruct.DataAlign = ADC_DATAALIGN_RIGHT;
ADC_InitStruct.NbrOfChannel = 1;
HAL_ADC_Init(&hadc1, &ADC_InitStruct);
// 初始化DAC
DAC_InitTypeDef DAC_InitStruct = {0};
DAC_InitStruct.DAC_Trigger = DAC_TRIGGER_NONE;
DAC_InitStruct.DAC_WaveGeneration = DAC_WAVE_GENERATION_NONE;
DAC_InitStruct.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUNMASK_BIT0;
DAC_InitStruct.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
HAL_DAC_Init(&hdac1, &DAC_InitStruct);
// 初始化DDS芯片(假设通过SPI接口)
// 配置SPI相关寄存器,设置时钟、数据模式等
__HAL_RCC_SPI1_CLK_ENABLE();
SPI_InitTypeDef SPI_InitStruct = {0};
SPI_InitStruct.Mode = SPI_MODE_MASTER;
SPI_InitStruct.Direction = SPI_DIRECTION_2LINES;
SPI_InitStruct.DataSize = SPI_DATASIZE_8BIT;
SPI_InitStruct.CLKPolarity = SPI_POLARITY_LOW;
SPI_InitStruct.CLKPhase = SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = SPI_NSS_SOFT;
SPI_InitStruct.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
SPI_InitStruct.FirstBit = SPI_FIRSTBIT_MSB;
SPI_InitStruct.TIMode = SPI_TIMODE_DISABLE;
SPI_InitStruct.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPolynomial = 7;
HAL_SPI_Init(&hspi1, &SPI_InitStruct);
// 初始化定时器用于定时采样
TIM_HandleTypeDef TIM_InitStruct = {0};
TIM_InitStruct.Instance = TIM2;
TIM_InitStruct.Init.Prescaler = 999; // 分频系数,这里设置为999,结合系统时钟得到合适的采样周期
TIM_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
TIM_InitStruct.Init.Period = 999; // 自动重装载值,与分频系数共同决定采样周期
TIM_InitStruct.Init.ClockDivision = 0;
HAL_TIM_Base_Init(&htim2, &TIM_InitStruct);
// 使能定时器中断
HAL_TIM_Base_Start_IT(&htim2);
while (1) {
// 主循环中主要进行信号处理和输出控制
// 例如,根据计算得到的频率和相位差,调整DDS芯片输出信号
float frequency_A = calculate_frequency_A();
float frequency_B = calculate_frequency_B();
float phase_difference = calculate_phase_difference();
// 根据频率和相位差设置DDS芯片的控制参数
// 假设DDS芯片通过SPI接口通信,以下为设置频率和相位的示例代码
uint32_t frequency_word_A = calculate_frequency_word(frequency_A);
uint32_t frequency_word_B = calculate_frequency_word(frequency_B);
uint32_t phase_word = calculate_phase_word(phase_difference);
// 通过SPI发送控制字到DDS芯片
HAL_SPI_Transmit(&hspi1, (uint8_t *)&frequency_word_A, 4, 1000);
HAL_SPI_Transmit(&hspi1, (uint8_t *)&frequency_word_B, 4, 1000);
HAL_SPI_Transmit(&hspi1, (uint8_t *)&phase_word, 4, 1000);
}
}
在信号采集环节,通过 ADC 按照设定的采样频率对混合信号 C 进行采样,并将采集到的数据存储在预先定义好的缓冲区中 。信号处理部分则调用前面提到的频率计算和相位差计算函数,对采集到的数据进行分析处理 。根据处理结果,通过 DAC 或者 DDS 芯片输出重建的信号 A' 和 B' 。在整个过程中,为了实现信号的稳定显示和准确输出,会不断地实时测量相位差,并根据相位差调整信号的输出相位 。各个功能模块之间通过数据缓冲区和全局变量进行数据交互,数据流向从 ADC 采集开始,经过处理后流向 DAC 或 DDS 进行信号输出 。例如,ADC 采集的数据存储在 input_buff 中,经过 FFT 计算后在 magnitude_buff 中得到频域数据,进而计算出频率和相位差,这些数据再用于控制 DDS 芯片输出重建信号 。
五、避坑指南:常见问题与解决方法
(一)硬件电路问题
在硬件搭建过程中,信号干扰是一个常见且棘手的问题 。由于信号频率较高,在 20kHz - 100kHz 之间,电路板上的走线就如同一个个天线,很容易接收和发射干扰信号 。比如,当分离电路中的模拟信号走线与数字信号走线距离过近时,数字信号的快速跳变会产生高频噪声,这些噪声通过电磁耦合的方式进入模拟信号,导致信号失真,在示波器上观察到的波形会出现杂波和抖动 。为了解决这个问题,在 PCB 设计阶段,要遵循 “分层、分区、远离” 的原则 。将模拟信号层和数字信号层分开,避免信号相互干扰;对不同功能模块进行分区布局,比如将电源模块、信号采集模块和信号处理模块分别放置在不同区域;同时,保证模拟信号走线与数字信号走线之间有足够的距离,必要时可以使用地线进行隔离 。
电源不稳定也是硬件电路中经常出现的问题 。电源作为整个系统的能量来源,其稳定性直接影响到系统的性能 。当电源输出的电压存在波动时,会导致芯片工作异常,信号处理出现偏差 。例如,在使用开关电源时,由于其工作原理是通过高频开关来调节电压,会产生一定的纹波 。这些纹波如果不加以处理,会叠加到信号上,影响信号的质量 。解决电源不稳定的问题,可以采用多种措施 。在电源输入端添加滤波电容,如电解电容和陶瓷电容组合使用,电解电容用于滤除低频纹波,陶瓷电容用于滤除高频纹波 。同时,可以使用线性稳压芯片对开关电源输出的电压进行二次稳压,进一步提高电压的稳定性 。另外,在布线时,要注意电源走线的宽度和布局,保证电源能够稳定地为各个模块供电 。
(二)软件调试问题
软件调试时,代码逻辑错误是最容易出现的问题之一 。在复杂的信号处理算法中,一个小小的逻辑错误可能会导致整个系统的功能无法实现 。比如在计算相位差的算法中,如果判断条件写错,可能会导致计算出的相位差不准确,进而影响信号的重建和显示 。对于这种问题,首先要仔细检查代码逻辑,使用调试工具如断点调试、单步执行等,逐步跟踪代码的执行过程,查看变量的值是否符合预期 。在编写代码时,要养成良好的编程习惯,添加详细的注释,清晰地描述代码的功能和逻辑,这样不仅便于自己调试,也方便团队成员之间的交流和协作 。
中断冲突也是软件调试中可能遇到的问题 。在基于微控制器的系统中,常常会使用多个中断来处理不同的事件,如定时器中断用于定时采样、外部中断用于响应外部信号等 。当多个中断同时触发或者中断优先级设置不合理时,就会出现中断冲突,导致系统运行异常 。例如,假设定时器中断和外部中断同时触发,而微控制器在处理定时器中断时,外部中断又不断请求,就可能导致定时器中断无法正常完成,影响信号的采样频率 。解决中断冲突,需要合理设置中断优先级,根据事件的重要性和实时性要求,为不同的中断分配合适的优先级 。同时,在中断服务程序中,要尽量减少代码的执行时间,避免长时间占用 CPU 资源,影响其他中断的响应 。
六、写在最后
回顾 2023 年电子设计大赛 H 题,从对赛题的深度剖析,到各种解题思路的探讨,再到软件实现的核心代码展示以及常见问题的解决方法,我们一步步揭开了这道赛题的神秘面纱 。通过这次解析,我们不仅对信号分离这一复杂的电子设计任务有了更深入的理解,也领略到了电子设计领域的魅力与挑战 。
电子设计是一个充满创新和无限可能的领域,每一次的挑战都是成长的机遇 。希望大家通过这篇文章,能够有所收获,激发对电子设计的兴趣和热情 。如果你也是电子设计的爱好者,不妨亲自尝试一下,用代码和电路去实现自己的创意 。相信在实践的过程中,你会遇到问题,也会收获惊喜,而这些都是电子设计带给我们最宝贵的财富 。期待大家在电子设计的道路上不断探索,取得更多的好成绩 ,创造出更多精彩的作品 !