【2025年电赛G题《电路模型探究装置》方案分享】亡赛回忆录 / 低频信号系统类入门指南
目录
目录
前言
考虑到CSDN新人较多,这份低频信号系统类入门指南就写在这里,但以后只会在博客园更新了。
这篇文章写给失意的自己,也是给整年的备赛画上句号,同时也希望这份入门指南能帮到打算做信号题的学弟学妹们。
G题方案分享
题目




实物图
大部分模块都是我们硬件员自己做的,买的模块倒是没用上多少。@将唐


方案图
总体硬件方案

软件方案

分析
基础要求
基础要求都是小儿科,看的都是基础一电路的稳定性,稳定性好的话,第一天测试一次就可以过了。
基础要求(1)
对于基础一,给出了一个传递函数,硬件员也很快算出了其R、C的值,我这里就算了其截止频率,是六百赫兹左右,因为其中有一个为5的增益,电路的截止频率算得三千多赫兹。虽然过程有点小争议,不过最后还是和硬件员确定了最终电路的截止频率为三千多赫兹。
基础要求(2)
可以看到我们方案图中,VGA放大后的电压经过一个RMS有效值检测模块,即避免了电压过大烧毁ADC,又可以ADC采样后的运算量,经过一个反馈,就可调控电压值到达我们的设定值:3.5Vpp。由于AD9833在小于1kHz和大于1.5MHz的正弦信号会有明显的衰减,所以需要对这两个特性的频率范围要加上额外的放大。
程序源码:
//--------------------------------------------------------- 基础部分2 ---------------------------------------------------------//
// 装置输出电压 > 3 Vpp
#define BASE_2_TH_VOL 2.00f
__attribute__((section (".RAM_SDRAM"))) float32_t base2_adc3_float_data[ADC_SAMPLE_LENGTH]; // 基础2浮点数据
float32_t base_2_DC_RMS; // 有效值
float32_t base_2_DC_RMS_sum; // 有效值总值
volatile uint8_t base_2_count; // 平均滤波计数
float32_t base_2_DC_amp;
void base2_adc3_opration(void)
{
if(adc_ch[2].conv_end_flag == 1){
adc_ch[2].conv_end_flag = 0;
// 浮点数据存储
for (uint32_t i = 0; i < ADC_SAMPLE_LENGTH; i ++) {
base2_adc3_float_data[i] = (float32_t)adc_ch[2].adc_buf[i] * ZOOM;
}
// 去直流(获取直流,此时获取的是有效值RMS = amp / 根号2)
remove_dc_part(base2_adc3_float_data, &base_2_DC_RMS, ADC_SAMPLE_LENGTH);
// 累加滤波
base_2_count ++;
base_2_DC_RMS_sum += base_2_DC_RMS;
if (base_2_count == 2) {
G_Start = false;
base_2_DC_RMS = base_2_DC_RMS_sum / base_2_count; // 有效值
base_2_DC_amp = base_2_DC_RMS * sqrtf(2.0f);
if (HMI_Fre >= 1000 && HMI_Fre <= 1100000) {
if (base_2_DC_amp < BASE_2_TH_VOL) {
set_vca_linear(BASE_2_TH_VOL / base_2_DC_amp);
}
} else if (HMI_Fre < 1000) {
set_vca_linear(8.0f);
} else if (HMI_Fre >= 1100000) {
set_vca_linear(10.0f);
}
Set_Current_USART(USART1_IDX);
printf("频率: %d \r\n", HMI_Fre);
printf("有效值: %f V\r\n", base_2_DC_RMS);
printf("幅值: %f V\r\n", base_2_DC_amp);
base_2_DC_RMS_sum = 0;
base_2_count = 0;
}
}
}
基础要求(3)
写死,跳过。
程序源码:
//--------------------------------------------------------- 基础部分3 ---------------------------------------------------------//
// 装置输出电压使得已知模型电路输出电压 ≈ 2Vpp
// 已知模型电路在1kHz下,增益为2.5192,要得到2Vpp电压则,要输入810mVpp (2Vpp / 2.5192) 左右电压
// 输入模型电路的电压原为632mVpp,则要调控VGA的增益为1.281645569620253(810mVpp / 632mVpp)
void base3_opration(void)
{
G_flag = RESET;
G_Start = false;
set_vca_linear(1.381645569620253f);
}
基础要求(4)
这里就稍稍麻烦一点,需要跟硬件队友配合一下。需要硬件队友使用其电路仿真软件导出此电路幅频特性曲线,列成一个数组表 = {频率,增益(线性),频率,增益...},通过串口屏上设定的频率,调控AD9833输出的频率,以及根据这个表来调控VGA的增益,就可达到理想值。
程序源码:
// [i*2]f [i*2+1]线性增益,与VGA增益成反比
const float32_t RC_ACF[RC_ACF_NUM] = {
100.0 ,4.809999999,
102.9029029 ,4.809999999,
105.8058058 ,4.923654857,
108.7087087 ,4.919545099,
111.6116116 ,4.915334507,
114.5145145 ,4.911023867,
...
}
//--------------------------------------------------------- 基础部分4 ---------------------------------------------------------//
// 装置输出电压使得已知模型电路输出信号频率+电压 ≈ 设定频率+电压
__attribute__((section (".RAM_SDRAM"))) float32_t base4_adc3_float_data[ADC_SAMPLE_LENGTH]; // 基础3浮点数据
float32_t base_4_DC_RMS_sum; // 有效值总值
volatile uint8_t base_4_count; // 平均滤波计数
float32_t base_4_DC_RMS; // 有效值
float32_t base4_freq;
float32_t base4_gain;
float32_t base4_set_sys_output_y; // 已知电路系统输出 Vpp
float32_t base4_target_sys_in_x; // 希望电路系统输入 Vpp
float32_t base4_curr_sys_in_x; // 当前电路系统输入 Vpp
float32_t base4_set_gain; // 希望设置增益值
void base4_adc3_opration(void)
{
if(adc_ch[2].conv_end_flag == 1){
adc_ch[2].conv_end_flag = 0;
// 浮点数据存储
for (uint32_t i = 0; i < ADC_SAMPLE_LENGTH; i ++) {
base4_adc3_float_data[i] = (float32_t)adc_ch[2].adc_buf[i] * ZOOM;
}
// 去直流(获取直流,此时获取的是有效值RMS = amp / 根号2)
remove_dc_part(base4_adc3_float_data, &base_4_DC_RMS, ADC_SAMPLE_LENGTH);
base_4_count ++;
base_4_DC_RMS_sum += base_4_DC_RMS;
if (base_4_count == 2) {
G_Start = false;
base_4_DC_RMS = base_4_DC_RMS_sum / base_4_count;
base4_curr_sys_in_x = base_4_DC_RMS * sqrtf(2.0f) * 2.0f; // Vpp
AD9833_A_SetFrequencyQuick(HMI_Fre, AD9833_A_SINE); // 调整AD9833频率,达到设定频率
for (uint16_t i = 0; i < RC_ACF_NUM / 2; i ++) {
if (fabs(RC_ACF[i * 2] - HMI_Fre) <= 2.0f) {
base4_gain = RC_ACF[i * 2 + 1];
Set_Current_USART(USART1_IDX);
printf("查表频率 = %f H在\r\n", RC_ACF[i * 2]);
printf("对应增益 = %f\r\n", base4_gain);
break;
}
}
base4_set_sys_output_y = (float32_t)HMI_Vol / 1000.0f; // 已知电路系统输出 Vpp
base4_target_sys_in_x = (float32_t)base4_set_sys_output_y / (float32_t)base4_gain; // 希望电路系统输入 Vpp
base4_set_gain = (float32_t)base4_target_sys_in_x / (float32_t)base4_curr_sys_in_x; // 希望设置增益值
set_vca_linear(base4_set_gain);
Set_Current_USART(USART1_IDX);
printf("已知电路系统输出 = %f Vpp\r\n", base4_set_sys_output_y);
printf("希望电路系统输入 = %f Vpp\r\n", base4_target_sys_in_x);
printf("当前电路系统输入 = %f Vpp\r\n", base4_curr_sys_in_x);
printf("希望设置增益值 = %f\r\n", base4_set_gain);
base_4_DC_RMS_sum = 0;
base_4_count = 0;
}
}
}
VGA
使用DA控制VGA是G题基础部分的核心,根据其 -20~60dB 对应的线性增益范围与 0.134 ~ 2.134V 的电压输入范围对应起来,写一些转换程序即可:
// 放大倍数范围:1000 ~ 0.1
// 对应电压输入范围:0.134 ~ 2.134
// dB
void set_vca_dB(float32_t target_gain)
{
// 检查输入增益是否在有效范围内
if (target_gain < -20.0f) target_gain = -20.0f;
else if (target_gain > 60.0f) target_gain = 60.0f;
// 修正后的线性公式:针对0dB(增益1)的误差进行截距调整
// 斜率保持-0.0242(兼顾其他点),截距调整为1.631以补偿0dB误差
float32_t target_voltage = -0.0242f * target_gain + 1.6108f;
set_DAC2_DC_amp(target_voltage);
}
// 线性(增益为倍数)
void set_vca_linear(float32_t target_gain)
{
if (target_gain <= 0.0f) {
target_gain = 0.001f; // 最小增益限制
}
set_vca_dB(20.0f * log10f(target_gain));
}
void set_DAC2_DC_init(float32_t amp)
{
HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2, DAC_ALIGN_12B_R, amp * (4095.0f / 3.246f));
HAL_DAC_Start(&hdac1,DAC_CHANNEL_2);
}
void set_DAC2_DC_amp(float32_t amp)
{
HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2, DAC_ALIGN_12B_R, amp * (4095.0f / 3.246f));
HAL_DAC_Start(&hdac1,DAC_CHANNEL_2);
}
发挥部分
这一题的重头戏来了,依我看,进阶部分结合了17年《自适应滤波器》、19年《简易电路特性测试仪》、21年《失真度测量》、23年《信号分离装置》这些前些年题目的重难点部分。
实现框图

方案描述
进阶最主要的就是“学习”一个未知电路的传递函数H(s),进阶一说是使扫频判断滤波类型都是次要的,扫频给的两分钟而是给你完全用来学习的。当时在这里,根据大佬的提示,我选择了使用lms来进行学习未知电路的传递函数,到最后其实也是一个查表法。

发挥部分(1)

判断滤波器类型
我这里就使用了两个频点去判断:100Hz和1MHz,取100Hz频点的装置输出和输入的比值1,取1MHz频点的装置输出和输入的比值2,可以从图中看到,不同滤波器的通带和阻带不同,这两个比值的范围也是不同的。这个方法很简单,不过要求滤波器在通带的增益必须为1上下,不得有明显衰减,我们实物测试得这个方法是没有问题的。下面是程序源码:
if (judge_filter == true) {
G_Start = false; // 按键启动标注位置零
judge_filter = false; // 判断滤波器标志位置零
_100Hz_fft_flag = true; // 100Hzfft计算标志位置一
sweep_start = false; // 扫频启动标志位置零
sweep_start_1 = true; // 扫频第一范围启动标志位置一
sweep_start_2 = false; // 扫频第二范围启动标志位置零
up1_sweep_freq = FREQ_START; // 初始化范围一扫频频率
up1_sweep_step = FREQ_STEP_1;
fir_index = 0;
AD9833_A_SetFrequencyQuick(100.0f, AD9833_A_SINE); // 一定要100Hz开始
set_vca_linear(3.33f);
// 计算第一/二个输入输出数据的幅值比值
for (uint32_t i = 0; i < INDEX_NUM; i++) {
in_out_amp_ration[i] = up1_ADC1_base_amp[i] / up1_ADC3_base_amp[i];
Set_Current_USART(USART1_IDX);
printf("in_out_amp_ration[%d] = %f\r\n", i, in_out_amp_ration[i]);
}
if (fabs(in_out_amp_ration[0] - 1.0f) < 0.3 && fabs(in_out_amp_ration[1] - 1.0f) > 0.3f) {
// 低通
HMI_Fliter = LOW_PASS;
Set_Current_USART(USART2_IDX);
printf("rec.txt=\"%d\"\xff\xff\xff", HMI_Fliter);
HMI_Fliter = 0;
}
else if (fabs(in_out_amp_ration[0] - 1.0f) > 0.3 && fabs(in_out_amp_ration[1] - 1.0f) < 0.3f) {
// 高通
HMI_Fliter = HIGH_PASS;
Set_Current_USART(USART2_IDX);
printf("rec.txt=\"%d\"\xff\xff\xff", HMI_Fliter);
HMI_Fliter = 0;
}
else if (fabs(in_out_amp_ration[0] - 1.0f) > 0.3 && fabs(in_out_amp_ration[1] - 1.0f) > 0.3f) {
// 带通
HMI_Fliter = BAND_PASS;
Set_Current_USART(USART2_IDX);
printf("rec.txt=\"%d\"\xff\xff\xff", HMI_Fliter);
HMI_Fliter = 0;
}
else if (fabs(in_out_amp_ration[0] - 1.0f) < 0.3 && fabs(in_out_amp_ration[1] - 1.0f) < 0.3f) {
// 带阻
HMI_Fliter = RESIST_PASS;
Set_Current_USART(USART2_IDX);
printf("rec.txt=\"%d\"\xff\xff\xff", HMI_Fliter);
HMI_Fliter = 0;
} else {
HMI_Fliter = 0;
Set_Current_USART(USART2_IDX);
printf("rec.txt=\"%d\"\xff\xff\xff", HMI_Fliter);
HMI_Fliter = 0;
}
Set_Current_USART(USART2_IDX);
printf("t.txt=\"学习完成\"\xff\xff\xff");
Set_Current_USART(USART1_IDX);
printf("比例数组第一个:%f\r\n", in_out_amp_ration[0]);
printf("比例数组最后一个:%f\r\n\r\n", in_out_amp_ration[1]);
return;
}
LMS传递函数学习
从1kHz扫频到60kHz,步进为200Hz,正好是要求中的200Hz,在每一个频率点进行一次LMS学习,得到一个频率点上的FIR系数(这里我用的是32阶的FIR滤波),我们定义一个二阶数组,最后可以获得一个两百九十多组的包含32个系数的各个频率点的FIR数组。相比于FFT,LMS学习的时间很快就结束了,大概就只有5s-10s,下面是源码:
//--------------------------------------------------------- 进阶部分1 ---------------------------------------------------------//
#define LOW_PASS 1
#define HIGH_PASS 2
#define BAND_PASS 3
#define RESIST_PASS 4
#define NO_BAND_PASS 5
#define NO_RESIST_PASS 6
#define AMP_INDEX_NUM ((AD9833_SWEEP_FREQ_STOP - AD9833_SWEEP_FREQ_START) / AD9833_SWEEP_FREQ_STEP)
#define FREQ_START 1000.0f // 起始频率 1000
#define FREQ_STOP 1000000.0f // 终止频率 1M
#define FREQ_STEP_1 200 // 1000Hz - 50khz 步进 200 Hz
#define FREQ_STEP_2 10000 // 50khz - 1MHz 步进 10k Hz
#define INDEX_NUM 2
// ADC1 测电路输出电压
__attribute__((section (".RAM_SDRAM"))) float32_t up1_adc1_float_data[ADC_SAMPLE_LENGTH]; // 浮点数据
__attribute__((section (".RAM_SDRAM"))) float32_t up1_adc1_window[ADC_SAMPLE_LENGTH]; // 窗函数
__attribute__((section (".RAM_SDRAM"))) float32_t up1_adc1_fft_input[MAX_FFT_N * 2]; // fft 输入数组(原始输出)
__attribute__((section (".RAM_SDRAM"))) float32_t up1_adc1_fft_output[MAX_FFT_N]; // fft输出数组
float32_t up1_ADC1_DC_part; // 直流量
float32_t up1_ADC1_base_amp[INDEX_NUM]; // 测得基波电压数组1、5、10、15、20、25...50
// ADC3 测电路输入电压
__attribute__((section (".RAM_SDRAM"))) float32_t up1_adc3_float_data[ADC_SAMPLE_LENGTH]; // 浮点数据
__attribute__((section (".RAM_SDRAM"))) float32_t up1_adc3_window[ADC_SAMPLE_LENGTH]; // 窗函数
__attribute__((section (".RAM_SDRAM"))) float32_t up1_adc3_fft_input[MAX_FFT_N * 2]; // fft 输入数组(原始输出)
__attribute__((section (".RAM_SDRAM"))) float32_t up1_adc3_fft_output[MAX_FFT_N]; // fft输出数组
float32_t up1_ADC3_DC_part; // 直流量
float32_t up1_ADC3_base_amp[INDEX_NUM]; // 测得基波电压数组1、5、10、15、20、25...50
float32_t up1_ADC3_base_freq[INDEX_NUM];
// 扫频
volatile bool sweep_start, sweep_start_1, sweep_start_2;
uint32_t up1_sweep_freq = FREQ_START; // 扫频起始频率
uint32_t up1_sweep_step = FREQ_STEP_1; // 扫频步进初始化
float32_t in_out_amp_ration[INDEX_NUM]; // 装置中输入信号幅值(ADC1)和输出信号幅值(ADC3)的比值
volatile bool _100Hz_fft_flag = true;
float32_t adc3_100Hz_freq, adc3_1MHz_freq;
// 判断滤波器类型
volatile bool judge_filter;
// LMS
__attribute__((section (".RAM_SDRAM"))) float32_t up1_lms_output[ADC_SAMPLE_LENGTH];
__attribute__((section (".RAM_SDRAM"))) float32_t up1_lms_err[ADC_SAMPLE_LENGTH];
float32_t up1_lmsCoeffs32[LMS_NUM_TAPS]; // 不可存SDRAM
// up1_fir
#define FIR_PARAMETER_NUM ((60100 - 1000) / 200 + 1) // 246
float32_t up1_save_fir_Coeffs32[FIR_PARAMETER_NUM][LMS_NUM_TAPS];
uint32_t fir_index = 0;
void up1_adc13_opration(void)
{
if (adc_ch[0].conv_end_flag == 1 && adc_ch[2].conv_end_flag == 1) {
adc_ch[0].conv_end_flag = 0;
adc_ch[2].conv_end_flag = 0;
//调试
// printf("up1_sweep_freq = %d\r\n", up1_sweep_freq);
// 浮点数据存储
for (uint32_t i = 0; i < ADC_SAMPLE_LENGTH; i++) {
up1_adc1_float_data[i] = (float32_t)adc_ch[0].adc_buf[i];
up1_adc3_float_data[i] = (float32_t)adc_ch[2].adc_buf[i];
}
// 去除直流分量
remove_dc_part(up1_adc1_float_data, &up1_ADC1_DC_part, ADC_SAMPLE_LENGTH);
remove_dc_part(up1_adc3_float_data, &up1_ADC3_DC_part, ADC_SAMPLE_LENGTH);
// 初始化为100Hz,先采样100Hz的信号幅值
if (_100Hz_fft_flag == true) { // 100Hz标志位为1时
_100Hz_fft_flag = false; // 100Hz标志位归零
sweep_start = true; // 扫频开始标志位置一
sweep_start_1 = true; // 扫频第一标志位置一
// fft
flat_top_window(up1_adc1_float_data, up1_adc1_window, ADC_SAMPLE_LENGTH);
flat_top_window(up1_adc3_float_data, up1_adc3_window, ADC_SAMPLE_LENGTH);
inf_fft_with_mag_norm_f32(up1_adc1_float_data, up1_adc1_fft_input, up1_adc1_fft_output, MAX_FFT_N);
inf_fft_with_mag_norm_f32(up1_adc3_float_data, up1_adc3_fft_input, up1_adc3_fft_output, MAX_FFT_N);
Set_Current_USART(USART1_IDX);
// 测幅值
up1_ADC1_base_amp[0] = find_fft_freq_amp(up1_adc1_fft_output, AMP, ADC_SAMPLE_RATE, MAX_FFT_N);
up1_ADC3_base_amp[0] = find_fft_freq_amp(up1_adc3_fft_output, AMP, ADC_SAMPLE_RATE, MAX_FFT_N);
adc3_100Hz_freq = find_fft_freq_amp(up1_adc3_fft_output, FREQ, ADC_SAMPLE_RATE, MAX_FFT_N);
printf ("up1_ADC1_base_amp[0] = %f mV\r\n", up1_ADC1_base_amp[0] * 1000.0f);
printf ("up1_ADC3_base_amp[0] = %f mV\r\n", up1_ADC3_base_amp[0] * 1000.0f);
printf ("adc3_100Hz_freq = %f Hz\r\n\r\n", adc3_100Hz_freq);
// 设置扫频开始频率
AD9833_A_SetFrequencyQuick(FREQ_START, AD9833_A_SINE);
}
if (sweep_start == true) { // 扫频开始标志位为1时
// 1k - 50k扫频,步进为 10kHz
if (up1_sweep_freq <= 60100 && sweep_start_1 == true) { // 扫频第一范围开始标志位
up1_sweep_freq += up1_sweep_step;
// LMS 计算fir参数
lms_filter_f32(up1_adc3_float_data, up1_adc1_float_data, up1_lms_output, up1_lms_err, up1_lmsCoeffs32);
// 保存该频点对应的FIR系数
memcpy(up1_save_fir_Coeffs32[fir_index++], up1_lmsCoeffs32, sizeof(up1_lmsCoeffs32));
if (up1_sweep_freq == 60200) {
sweep_start_1 = false; // 扫频第一范围开始标志位清零
sweep_start_2 = true; // 扫频第二范围开始标志位置一
up1_sweep_freq = 60000; // 下一扫频范围从50k开始
Set_Current_USART(USART1_IDX);
printf("学习完成\r\n\r\n");
up1_sweep_step = FREQ_STEP_2;
}
AD9833_A_SetFrequencyQuick(up1_sweep_freq, AD9833_A_SINE);
}
// 50k - 1M扫频 步进
else if (up1_sweep_freq >= 60000 && up1_sweep_freq <= 1010000 && sweep_start_2 == true) {
up1_sweep_freq += up1_sweep_step;
if (up1_sweep_freq == 1020000) {
sweep_start_2 = false; // 扫频第一范围开始标志位清零
judge_filter = true; // 滤波器判断标志位置一
up1_sweep_freq = 1000000; // 扫频起始频率
up1_sweep_step = FREQ_STEP_1; // 扫频步进初始化
// fft
flat_top_window(up1_adc1_float_data, up1_adc1_window, MAX_FFT_N);
flat_top_window(up1_adc3_float_data, up1_adc3_window, MAX_FFT_N);
inf_fft_with_mag_norm_f32(up1_adc1_float_data, up1_adc1_fft_input, up1_adc1_fft_output, MAX_FFT_N);
inf_fft_with_mag_norm_f32(up1_adc3_float_data, up1_adc3_fft_input, up1_adc3_fft_output, MAX_FFT_N);
// 测幅值
up1_ADC1_base_amp[1] = find_fft_freq_amp(up1_adc1_fft_output, AMP, ADC_SAMPLE_RATE, MAX_FFT_N);
up1_ADC3_base_amp[1] = find_fft_freq_amp(up1_adc3_fft_output, AMP, ADC_SAMPLE_RATE, MAX_FFT_N);
adc3_1MHz_freq = find_fft_freq_amp(up1_adc3_fft_output, FREQ, ADC_SAMPLE_RATE, MAX_FFT_N);
Set_Current_USART(USART1_IDX);
printf("up1_ADC1_base_amp[0] = %f mV\r\n", up1_ADC1_base_amp[0] * 1000.0f);
printf("up1_ADC3_base_amp[0] = %f mV\r\n", up1_ADC3_base_amp[0] * 1000.0f);
printf("up1_ADC1_base_amp[1] = %f mV\r\n", up1_ADC1_base_amp[1] * 1000.0f);
printf("up1_ADC3_base_amp[1] = %f mV\r\n\r\n", up1_ADC3_base_amp[1] * 1000.0f);
printf ("adc3_1MHz_freq = %f Hz\r\n\r\n", adc3_1MHz_freq);
}
AD9833_A_SetFrequencyQuick(up1_sweep_freq, AD9833_A_SINE);
HAL_Delay(1);
// printf("up1_sweep_freq = %d Hz\r\n", up1_sweep_freq);
}
}
if (judge_filter == true) {
G_Start = false; // 按键启动标注位置零
judge_filter = false; // 判断滤波器标志位置零
_100Hz_fft_flag = true; // 100Hzfft计算标志位置一
sweep_start = false; // 扫频启动标志位置零
sweep_start_1 = true; // 扫频第一范围启动标志位置一
sweep_start_2 = false; // 扫频第二范围启动标志位置零
up1_sweep_freq = FREQ_START; // 初始化范围一扫频频率
up1_sweep_step = FREQ_STEP_1;
fir_index = 0;
AD9833_A_SetFrequencyQuick(100.0f, AD9833_A_SINE); // 一定要100Hz开始
set_vca_linear(3.33f);
// 计算第一/二个输入输出数据的幅值比值
for (uint32_t i = 0; i < INDEX_NUM; i++) {
in_out_amp_ration[i] = up1_ADC1_base_amp[i] / up1_ADC3_base_amp[i];
Set_Current_USART(USART1_IDX);
printf("in_out_amp_ration[%d] = %f\r\n", i, in_out_amp_ration[i]);
}
if (fabs(in_out_amp_ration[0] - 1.0f) < 0.3 && fabs(in_out_amp_ration[1] - 1.0f) > 0.3f) {
// 低通
HMI_Fliter = LOW_PASS;
Set_Current_USART(USART2_IDX);
printf("rec.txt=\"%d\"\xff\xff\xff", HMI_Fliter);
HMI_Fliter = 0;
}
else if (fabs(in_out_amp_ration[0] - 1.0f) > 0.3 && fabs(in_out_amp_ration[1] - 1.0f) < 0.3f) {
// 高通
HMI_Fliter = HIGH_PASS;
Set_Current_USART(USART2_IDX);
printf("rec.txt=\"%d\"\xff\xff\xff", HMI_Fliter);
HMI_Fliter = 0;
}
else if (fabs(in_out_amp_ration[0] - 1.0f) > 0.3 && fabs(in_out_amp_ration[1] - 1.0f) > 0.3f) {
// 带通
HMI_Fliter = BAND_PASS;
Set_Current_USART(USART2_IDX);
printf("rec.txt=\"%d\"\xff\xff\xff", HMI_Fliter);
HMI_Fliter = 0;
}
else if (fabs(in_out_amp_ration[0] - 1.0f) < 0.3 && fabs(in_out_amp_ration[1] - 1.0f) < 0.3f) {
// 带阻
HMI_Fliter = RESIST_PASS;
Set_Current_USART(USART2_IDX);
printf("rec.txt=\"%d\"\xff\xff\xff", HMI_Fliter);
HMI_Fliter = 0;
} else {
HMI_Fliter = 0;
Set_Current_USART(USART2_IDX);
printf("rec.txt=\"%d\"\xff\xff\xff", HMI_Fliter);
HMI_Fliter = 0;
}
Set_Current_USART(USART2_IDX);
printf("t.txt=\"学习完成\"\xff\xff\xff");
Set_Current_USART(USART1_IDX);
printf("比例数组第一个:%f\r\n", in_out_amp_ration[0]);
printf("比例数组最后一个:%f\r\n\r\n", in_out_amp_ration[1]);
return;
}
}
}
发挥部分(2)

对于发挥部分2,将装置的输入接入信号发生器后,使用一路ADC将信号发生器发生的信号采样进来,进行FFT后测频,将测得的对应频率使用查表法来获取对应的FIR的系数,然后使用FIR数字滤波器将学习到的未知电路模型滤出来,从而就可以获取与未知电路模型相同的输出。
但是这里有一个很严重的问题,想让DAC完整的输出这个信号并不容易,因为DAC只能输出整周期的信号。在这里博主也是卡了比较,最后才想到用FFT将这个学习后输出的信号分解为幅频和相频后,再使用傅里叶级数将这个信号重新合成为一个周期的信号,再用DAC输出出来。
下面是源码,写的并不是很完全,就正弦波和三角波不错,幅值也没问题,但是方波和漂移并没有解决,因为确实没时间了:
//--------------------------------------------------------- 进阶部分2 ---------------------------------------------------------//
// ADC1 用来接收外部信号,获得频率等信息后,进行DAC输出
__attribute__((section (".RAM_SDRAM"))) float32_t up2_adc1_float_data[ADC_SAMPLE_LENGTH]; // 浮点数据
__attribute__((section (".RAM_SDRAM"))) float32_t up2_adc1_fft_input[MAX_FFT_N * 2]; // fft 输入数组(原始输出)
__attribute__((section (".RAM_SDRAM"))) float32_t up2_adc1_fft_output[MAX_FFT_N]; // fft输出数组
float32_t up2_adc1_DC_part;
float32_t up2_adc1_base_freq;
float32_t up2_adc1_base_amp;
float32_t up2_adc1_base_phase;
// ADC2 用来接收DAC反馈信号,进行锁相
__attribute__((section (".RAM_SDRAM"))) float32_t up2_adc2_float_data[ADC_SAMPLE_LENGTH]; // 浮点数据
__attribute__((section (".RAM_SDRAM"))) float32_t up2_adc2_fft_input[MAX_FFT_N * 2]; // fft 输入数组(原始输出)
__attribute__((section (".RAM_SDRAM"))) float32_t up2_adc2_fft_output[MAX_FFT_N]; // fft输出数组
float32_t up2_adc2_DC_part;
float32_t up2_adc2_base_freq;
float32_t up2_adc2_base_phase;
// fir
uint32_t up2_adc1_fir_index;
float32_t up2_fir_output[ADC_SAMPLE_LENGTH];
float32_t up2_fit_dc_part;
float32_t up2_fir_window[ADC_SAMPLE_LENGTH];
__attribute__((section (".RAM_SDRAM"))) float32_t up2_fir_fft_intput[MAX_FFT_N * 2];
__attribute__((section (".RAM_SDRAM"))) float32_t up2_fir_fft_output[MAX_FFT_N];
float32_t up2_fir_output_base_amp;
float32_t up2_fir_output_base_freq;
float32_t up2_fir_output_last_base_freq;
//波形标志
volatile uint8_t wave_falg;
volatile uint8_t lase_wave_falg;
uint16_t up2_dac_wave[DAC_WAVE_LENGTH];
// 三角波
uint32_t sanjiaobo_index;
float32_t sanjiaobo_amp[10];
float32_t sanjiaobo_phase[10];
// 方波
uint32_t fangbo_index;
float32_t fangbo_amp[10];
float32_t fangbo_phase[10];
// 占空比波
uint32_t zhankong_index;
float32_t zhankong_amp[20];
float32_t zhankong_phase[20];
// DAC信号频率 = 触发时钟频率 / DAC周期点数
static uint16_t dac_buffer_output[DAC_WAVE_LENGTH];
void up2_adc12_opration(void)
{
if (adc_ch[0].conv_end_flag == 1 && adc_ch[1].conv_end_flag == 1) {
adc_ch[0].conv_end_flag = 0;
adc_ch[1].conv_end_flag = 0;
// 浮点数据转换
for (uint32_t i = 0; i < ADC_SAMPLE_LENGTH; i++) {
up2_adc1_float_data[i] = (float32_t)adc_ch[0].adc_buf[i];
up2_adc2_float_data[i] = (float32_t)adc_ch[1].adc_buf[i];
}
// 去直流
remove_dc_part(up2_adc1_float_data, &up2_adc1_DC_part, ADC_SAMPLE_LENGTH);
remove_dc_part(up2_adc2_float_data, &up2_adc2_DC_part, ADC_SAMPLE_LENGTH);
// FFT处理
inf_fft_with_mag_norm_f32(up2_adc1_float_data, up2_adc1_fft_input, up2_adc1_fft_output, MAX_FFT_N);
inf_fft_with_mag_norm_f32(up2_adc2_float_data, up2_adc2_fft_input, up2_adc2_fft_output, MAX_FFT_N);
// 频率测量
up2_adc1_base_freq = find_fft_freq_amp(up2_adc1_fft_output, FREQ, ADC_SAMPLE_RATE, MAX_FFT_N);
up2_adc2_base_freq = find_fft_freq_amp(up2_adc2_fft_output, FREQ, ADC_SAMPLE_RATE, MAX_FFT_N);
// 幅值测量
up2_adc1_base_amp = find_fft_freq_amp(up2_adc1_fft_output, AMP, ADC_SAMPLE_RATE, MAX_FFT_N);
// 相位测量
up2_adc1_base_phase = find_phase_f32(up2_adc1_fft_input, up2_adc1_fft_output, MAX_FFT_N);
up2_adc2_base_phase = find_phase_f32(up2_adc2_fft_input, up2_adc2_fft_output, MAX_FFT_N);
printf ("up2_adc1_base_amp = %f V\r\n", up2_adc1_base_amp);
// 正弦波
if (up2_adc1_base_amp > 0.99f && up2_adc1_base_amp < 1.013f) {
wave_falg = 1;
up2_adc1_fir_index = (uint32_t)((up2_adc1_base_freq - 1000.0f) / 200.0f);
fir_fliter_function(up2_adc1_float_data, up2_fir_output, ADC_SAMPLE_LENGTH, LMS_NUM_TAPS, &up1_save_fir_Coeffs32[up2_adc1_fir_index][LMS_NUM_TAPS]);
flat_top_window(up2_fir_output, up2_fir_window, ADC_SAMPLE_LENGTH);
// fir fft运算
inf_fft_with_mag_norm_f32(up2_fir_output, up2_fir_fft_intput, up2_fir_fft_output, MAX_FFT_N);
up2_fir_output_base_amp = find_fft_freq_amp(up2_fir_fft_output, AMP, ADC_SAMPLE_RATE, MAX_FFT_N);
up2_fir_output_base_freq = find_fft_freq_amp(up2_fir_fft_output, FREQ, ADC_SAMPLE_RATE, MAX_FFT_N);
if (up2_fir_output_last_base_freq != up2_fir_output_base_freq || lase_wave_falg != wave_falg) {
memset(up2_dac_wave,0,sizeof(up2_dac_wave));
for (int i = 0; i < MAX_FFT_N / 2;i++) {
printf ("%f, %f\r\n", up2_fir_output[i] * ZOOM, up2_fir_fft_output[i]);
}
Set_Current_USART(USART1_IDX);
printf ("正弦波基波幅值 = %f V\r\n", up2_fir_output_base_amp);
printf ("正弦波基波频率 = %f Hz\r\n", up2_fir_output_base_freq);
printf ("\r\n");
set_dac_freq(up2_adc1_base_freq);
for (uint16_t i = 0; i < DAC_WAVE_LENGTH; i++) {
up2_dac_wave[i] = (uint32_t)(0.961538f * (up2_fir_output_base_amp * 2.0f) * 2047.0f / 3.246f * (sin(2.0f * PI * i / DAC_WAVE_LENGTH) + 1.5f));
}
// dac输出
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*)up2_dac_wave, DAC_WAVE_LENGTH, DAC_ALIGN_12B_R);
}
up2_fir_output_last_base_freq = up2_fir_output_base_freq;
}
// 三角波
else if (up2_adc1_base_amp < 0.85f && up2_adc1_base_amp > 0.800f) {
wave_falg = 2;
up2_adc1_fir_index = (uint32_t)((up2_adc1_base_freq - 1000.0f) / 200.0f);
fir_fliter_function(up2_adc1_float_data, up2_fir_output, ADC_SAMPLE_LENGTH, LMS_NUM_TAPS, &up1_save_fir_Coeffs32[up2_adc1_fir_index][LMS_NUM_TAPS]);
flat_top_window(up2_fir_output, up2_fir_window, ADC_SAMPLE_LENGTH);
// fft
inf_fft_with_mag_norm_f32(up2_fir_output, up2_fir_fft_intput, up2_fir_fft_output, MAX_FFT_N);
up2_fir_output_base_amp = find_fft_freq_amp(up2_fir_fft_output, AMP, ADC_SAMPLE_RATE, MAX_FFT_N);
sanjiaobo_index = find_fft_freq_amp(up2_fir_fft_output, INDEX, ADC_SAMPLE_RATE, MAX_FFT_N);
up2_fir_output_base_freq = find_fft_freq_amp(up2_fir_fft_output, FREQ, ADC_SAMPLE_RATE, MAX_FFT_N);
if (up2_fir_output_last_base_freq != up2_fir_output_base_freq || lase_wave_falg != wave_falg) {
memset(up2_dac_wave, 0, sizeof(up2_dac_wave));
Set_Current_USART(USART1_IDX);
for (int i = 0; i < MAX_FFT_N / 2;i++) {
printf ("%f, %f\r\n", up2_fir_output[i] * ZOOM, up2_fir_fft_output[i]);
}
printf ("三角波基波幅值 = %f V\r\n", up2_fir_output_base_amp);
printf ("三角波基波频率 = %f Hz\r\n", up2_fir_output_base_freq);
printf ("\r\n");
// 谐波幅值
sanjiaobo_amp[0] = up2_fir_output_base_amp;
for (int i = 1; i < 10; i ++) {
// 幅值、相位
sanjiaobo_phase[i] = atan2f(up2_fir_fft_intput[2 * sanjiaobo_index * (i * 2 + 1)+1], up2_fir_fft_intput[2 * sanjiaobo_index * (i * 2 + 1)]);
sanjiaobo_amp[i] = up2_fir_fft_output[sanjiaobo_index * (i * 2 + 1)];
Set_Current_USART(USART1_IDX);
printf("三角波%d次谐波 = %f mV\r\n", (i * 2 + 1),sanjiaobo_amp[i]);
}
for (int i = 0; i < DAC_WAVE_LENGTH; i ++) {
float32_t theta = 2.0f * PI * i / DAC_WAVE_LENGTH; //相位角度
float32_t fixed_coeff = 1.33f * (4150.0f / 3.246f) * (8.0f / (PI * PI));
float32_t signal = 1.5f // 直流
+ sanjiaobo_amp[0] * arm_sin_f32(theta * 1.0f + sanjiaobo_amp[0] )
- sanjiaobo_amp[1] * arm_sin_f32(theta * 3.0f + sanjiaobo_amp[1] )
+ sanjiaobo_amp[2] * arm_sin_f32(theta * 5.0f + sanjiaobo_amp[2] )
- sanjiaobo_amp[3] * arm_sin_f32(theta * 7.0f + sanjiaobo_amp[3] )
+ sanjiaobo_amp[4] * arm_sin_f32(theta * 9.0f + sanjiaobo_amp[4] )
- sanjiaobo_amp[5] * arm_sin_f32(theta * 11.0f + sanjiaobo_amp[5] )
+ sanjiaobo_amp[6] * arm_sin_f32(theta * 13.0f + sanjiaobo_amp[6] )
- sanjiaobo_amp[7] * arm_sin_f32(theta * 15.0f + sanjiaobo_amp[7] )
+ sanjiaobo_amp[8] * arm_sin_f32(theta * 17.0f + sanjiaobo_amp[8] )
- sanjiaobo_amp[9] * arm_sin_f32(theta * 19.0f + sanjiaobo_amp[9] );
up2_dac_wave[i] = (uint16_t)(fixed_coeff * signal);
}
set_dac_freq(up2_adc1_base_freq);
// dac输出
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*)up2_dac_wave, DAC_WAVE_LENGTH, DAC_ALIGN_12B_R);
}
up2_fir_output_last_base_freq = up2_fir_output_base_freq;
}
// 方波
else if (up2_adc1_base_amp > 1.27f) {
wave_falg = 3;
up2_adc1_fir_index = (uint32_t)((up2_adc1_base_freq - 1000.0f) / 200.0f);
fir_fliter_function(up2_adc1_float_data, up2_fir_output, ADC_SAMPLE_LENGTH, LMS_NUM_TAPS, &up1_save_fir_Coeffs32[up2_adc1_fir_index][LMS_NUM_TAPS]);
flat_top_window(up2_fir_output, up2_fir_window, ADC_SAMPLE_LENGTH);
// fft
inf_fft_with_mag_norm_f32(up2_fir_output, up2_fir_fft_intput, up2_fir_fft_output, MAX_FFT_N);
// 求幅值
up2_fir_output_base_amp = find_fft_freq_amp(up2_fir_fft_output, AMP, ADC_SAMPLE_RATE, MAX_FFT_N);
// 求频率
up2_fir_output_base_freq = find_fft_freq_amp(up2_fir_fft_output, FREQ, ADC_SAMPLE_RATE, MAX_FFT_N);
// 求索引
fangbo_index = find_fft_freq_amp(up2_fir_fft_output, INDEX, ADC_SAMPLE_RATE, MAX_FFT_N);
if (up2_fir_output_last_base_freq != up2_fir_output_base_freq || lase_wave_falg != wave_falg) {
memset(up2_dac_wave,0,sizeof(up2_dac_wave));
Set_Current_USART(USART1_IDX);
for (int i = 0; i < MAX_FFT_N / 2;i++) {
printf ("%f, %f\r\n", up2_fir_output[i] * ZOOM, up2_fir_fft_output[i]);
}
printf ("方波基波幅值 = %f V\r\n", up2_fir_output_base_amp);
printf ("方波基波频率 = %f Hz\r\n", up2_fir_output_base_freq);
printf ("\r\n");
// 谐波幅值
fangbo_amp[0] = up2_fir_output_base_amp;
for (int i = 1; i < 10; i ++) {
// 幅值、相位
fangbo_phase[i] = atan2f(up2_fir_fft_intput[2 * fangbo_index * (i * 2 + 1)+1], up2_fir_fft_intput[2 * fangbo_index * (i * 2 + 1)]);
fangbo_amp[i] = up2_fir_fft_output[fangbo_index * (i * 2 + 1)];
Set_Current_USART(USART1_IDX);
printf("方波%d次谐波 = %f mV\r\n", (i * 2 + 1),fangbo_amp[i]);
}
// 滤波后方波合成 - 修正部分
for (int i = 0; i < DAC_WAVE_LENGTH; i++) {
float32_t theta = 2.0f * PI * i / DAC_WAVE_LENGTH;
float32_t signal = 1.5f; // DC偏移
signal += (4.0f / PI) * (
fangbo_amp[0] * arm_sin_f32(theta * 1 + fangbo_phase[0]) +
fangbo_amp[1] * arm_sin_f32(theta * 3 + fangbo_phase[1]) +
fangbo_amp[2] * arm_sin_f32(theta * 5 + fangbo_phase[2]) +
fangbo_amp[3] * arm_sin_f32(theta * 7 + fangbo_phase[3]) +
fangbo_amp[4] * arm_sin_f32(theta * 9 + fangbo_phase[4]) +
fangbo_amp[5] * arm_sin_f32(theta * 11 + fangbo_phase[5]) +
fangbo_amp[6] * arm_sin_f32(theta * 13 + fangbo_phase[6]) +
fangbo_amp[7] * arm_sin_f32(theta * 15 + fangbo_phase[7]) +
fangbo_amp[8] * arm_sin_f32(theta * 17 + fangbo_phase[8]) +
fangbo_amp[9] * arm_sin_f32(theta * 19 + fangbo_phase[9])
);
up2_dac_wave[i] = (uint16_t)((4095.0f / 3.246f) * signal);
}
set_dac_freq(up2_adc1_base_freq);
// dac输出
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*)up2_dac_wave, DAC_WAVE_LENGTH, DAC_ALIGN_12B_R);
}
up2_fir_output_last_base_freq = up2_fir_output_base_freq;
}
lase_wave_falg = wave_falg;
// 以下都是可以用的,现在测试保底注释而已
// G_Start = false;
// // ADC1频率 —— fir系数表索引换算
// up2_adc1_fir_index = (uint32_t)((up2_adc1_base_freq - 1000.0f) / 200.0f);
// fir_fliter_function(up2_adc1_float_data, up2_fir_output, ADC_SAMPLE_LENGTH, LMS_NUM_TAPS, &up1_save_fir_Coeffs32[up2_adc1_fir_index][LMS_NUM_TAPS]);
// uint32_t fir_start_index = LMS_NUM_TAPS / 2;
// for (uint32_t i = 0; i < DAC_WAVE_LENGTH; i ++) {
// dac_buffer_output[i] = (uint16_t)((up2_fir_output[fir_start_index + i] * ZOOM + up2_adc1_DC_part * ZOOM) * (4095 / 3.3));
// Set_Current_USART(USART1_IDX);
// printf("%f, %f\r\n", up2_fir_output[fir_start_index + i] * ZOOM, dac_buffer_output[i] * DAC_ZOOM);
// }
// Set_Current_USART(USART1_IDX);
// printf("up2_adc1_base_freq = %f Hz\r\n", up2_adc1_base_freq);
// set_dac_freq((uint32_t)up2_adc1_base_freq);
// HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*)dac_buffer_output, DAC_WAVE_LENGTH, DAC_ALIGN_12B_R);
}
}
LMS和FIR源码
LMS
lms.c
void lms_filter_f32(float32_t* adc_value, float32_t* adc_ref, float32_t* lms_output, float32_t* lms_err, float32_t* lmsCoeffs32)
{
uint32_t blockSize = LMS_BLOCK_SIZE;
uint32_t numBlocks = LMS_BLOCK_SIZE / LMS_BLOCK_SIZE; /* 需要调用arm_lms_norm_f32的次数 */
float32_t lmsStateF32[LMS_BLOCK_SIZE + LMS_NUM_TAPS - 1]; /* 状态缓存,大小numTaps + blockSize - 1 */
// float32_t lmsCoeffs32[LMS_NUM_TAPS]; /* 滤波器系数 */
float32_t *inputF32, *outputF32, *inputREF, *outputERR;
arm_lms_norm_instance_f32 lmsS = {0}; /* 注意:arm_lms_norm_instance_f32内部成员可能仍使用float32_t,但属于库定义,无需修改 */
/* 如果是实时性的滤波,仅需清零一次 */
memset(lmsCoeffs32, 0, sizeof(*lmsCoeffs32));
memset(lmsStateF32, 0, sizeof(lmsStateF32));
/* 初始化输入输出缓存指针 */
inputF32 = (float32_t *)&adc_value[0]; /* 原始信号 */
inputREF = (float32_t *)&adc_ref[0]; /* 参考信号 */
outputF32 = (float32_t *)&lms_output[0]; /* 输出信号 */
outputERR = (float32_t *)&lms_err[0]; /* 误差信号 */
/* 归一化LMS初始化 */
arm_lms_norm_init_f32(&lmsS, /* LMS结构体 */
LMS_NUM_TAPS, /* 滤波器系数个数 */
(float32_t *)&lmsCoeffs32[0], /* 滤波器系数 */
&lmsStateF32[0], /* 状态缓存 */
0.001f, /* 步长(添加f后缀明确为单精度) */
blockSize); /* 处理的数据个数 */
/* 实现LMS自适应滤波,这里每次处理1个点 */
for (uint16_t i = 0; i < numBlocks; i++)
{
arm_lms_norm_f32(&lmsS, /* LMS结构体 */
inputF32 + (i * blockSize), /* 输入数据 */
inputREF + (i * blockSize), /* 参考数据 */
outputF32 + (i * blockSize), /* 输出数据 */
outputERR + (i * blockSize), /* 误差数据 */
blockSize); /* 处理的数据个数 */
}
}
lms.h
/****************************************************** LMS ******************************************************/
#define LMS_NUM_TAPS 32 /* 滤波器系数个数 */
#define LMS_BLOCK_SIZE 4096 /* 调用一次arm_lms_norm_f32处理的采样点个数 */
extern void lms_filter_f32(float32_t* adc_value, float32_t* adc_ref, float32_t* lms_output, float32_t* lms_err, float32_t* lmsCoeffs32);
FIR
fir.c
uint8_t fir_fliter_function(float32_t *adc_value, float32_t* fir_output, uint32_t fir_length, uint32_t FIR_NUMTAPS_LENGTH, float32_t* fir_pCoeffs)
{
arm_fir_instance_f32 * fir_S; /* FIR实例化结构体 */
uint16_t fir_numTaps = FIR_NUMTAPS_LENGTH; /* FIR滤波器系数个数 */
uint32_t fir_blockSize = fir_length; /* 块处理大小 */
uint32_t FIR_PSTATE_LENGTH = fir_length + FIR_NUMTAPS_LENGTH - 1; /* FIR滤波器状态变量的长度 */
float32_t fir_pState[FIR_PSTATE_LENGTH]; /* FIR滤波器状态变量暂存数组:状态数组的大小为 fir_numTaps + fir_blockSize - 1 */
float32_t fliter_InputBufer[fir_length]; /* FIR滤波输入数组 */
for (uint16_t i = 0; i < fir_length; i++) {
fliter_InputBufer[i] = adc_value[i];
}
/* 为FIR实例分配内存 */
fir_S = (arm_fir_instance_f32 *)malloc(sizeof(arm_fir_instance_f32));
if (fir_S == NULL) {
return 0; /*return true; 内存分配失败,处理错误 */
}
/* FIR滤波器函数是arm_fir_f32。使用此函数可以设计FIR低通,高通,带通和带阻 */
arm_fir_init_f32(fir_S,fir_numTaps,fir_pCoeffs,fir_pState,fir_blockSize);
arm_fir_f32(fir_S,fliter_InputBufer,fir_output,fir_blockSize);
free(fir_S); /* 释放内存 */
fir_S = NULL; /* 将指针设置为 NULL,以避免悬挂指针 */
return 1;
}
fir.h
extern uint8_t fir_fliter_function(float32_t *adc_value, float32_t* fir_output, uint32_t fir_length, uint32_t FIR_NUMTAPS_LENGTH, float32_t* fir_pCoeffs);
《亡赛回忆录》
记8月5日。
最后一次学习测试结束后,就立马封箱了。保险起见,我们给备用核心板烧录了同一份程序。
坐了六个小时大巴来到中山大学,等了五个小时,轮到我们审评了。
基础一,有惊无险;基础二,示波器信号是平的,感觉心跳停了一拍。。。
评委老师很好,不过直到我们排查到最后是核心板的问题后,换上备用的核心板,测完基础二,审评时间已经结束了。
别的就不多说了,赶紧进入下一环节吧。

低频信号类入门指南
信号类
信号这一方面挺杂的,即包含高频通信,又包含低频系统、仪表测量,以及各种超声波、声源、光源等都属于信号,可谓范围是非常的广,由于我们只涉及到了低频系统及仪表测量,这里就只讲这两部分。
开始时啥都不会,不知道学啥,什么都缺,软件也不懂,硬件模块也不知道需要啥。所有的东西都得从头开始做。。。
理论知识
信号类作为理论指导实践的一类赛题,所需的前置理论知识还是较多的,当然,不要忘了与实践结合起来。
- 《电路》(几个定理是基础)
- 《数电》(数电用的比较少)
- 《模电》(由于运放实在是太集成了,导致三极管平常用的并不多,但还是要学完)
- 《信号与系统》(FT的理论,以及传递函数的计算)
- 《数字信号处理》(还没学,不过会用就行)
- 《高频电子线路》(只需要了解最后的调制解调)
- 《运算放大器权威指南》
- 《新概念模拟电路》
- 等等等等。。。。。。
需求仪器
- 信号发生器
- 示波器
- 正负线性电源(低纹波)
软件重点(这里只说32,因为没时间学FPGA)
选型
STM32H7系列、F4系列,其他不推荐。
程序准备
- ADC 采样:DMA搬运、顺序采样、多ADC同步采样、欠采样、定时器触发采样、外部时钟触发采样、搞清楚采样频率、采样点数的关系、采样会遇到的各种问题、基本的滤波算法。
- 信号处理:fft、幅频、相频、ifft、window、fir、iir、lms、gcc-phat。
- 模块移植:AD9910(听说是DA神器,最好整一个,可惜我们没弄吃亏了),AD9833、AD9854、AD9959、AD7606、AD5933、SI5351、MAX262、MCP41010、VGA(DAC程控)、LTC1068(时钟控制滤波器)。
- 频率测量:fft测频、定时器时钟输入测频(好用)。
- 相位测量:目前就用过fft测量相位,就是相频特性曲线对应索引上的相位。
- 信号输出:DAC(一定要好好搞,分清楚它的使用环境、优劣)、DDS(这个不用多说,有个9910是最好的,不行就平民神器9833)。
- 信号显示:最基本的采样的时域波形、运算后的频谱显示、串口屏显示、TFTLCD显示(这里还是建议使用串口屏)。
- 信号仿真:MATLAB,硬件员一般用的multisum和各种spice。
- 信号存储:外部RAM,非常重要,若有则用。
硬件模块准备
由于我是软件员,硬件这一块也就记得需要用什么,大概写一下吧。
- ADC前端调理:(电压可加偏执、可放大和衰减,建议多路),这个淘宝上基本没有卖,建议自己搞,加法器、反相比例什么的。
- 程控:VGA、程控移相器(其实就是数字电位器)、压控移相器(这个淘宝没有,自己搞)、模拟开关、程控滤波器(MAX262、LTC1068)。
- 基本:放大器、加法器、乘法器、比较器、AGC、衰减器、跟随器(很重要,阻抗匹配利器,有时需要单路转多路)、有效值检测、高低带通带阻滤波器。
- 附加:贴片转直插板子(重要,jlc多白嫖点)、ADC采样板(ADC、DAC、时钟、信号输入和输出的端口最好都画成SMA接口)、洞洞板。(还有以上软件需要移植的模块,也是包含在这部分内)
目前就想到那么多,程序是我从零开始写的,模块是硬件员从零开始准备的,其中也有学弟的功劳。文字太过单调,只有真正经历过后才会懂得这种感觉💀💀💀
赛题训练
这里简单讲一下我们做过的电赛赛题吧。
第一道题 2020年电赛E题《放大器非线性失真研究装置》
开始时间:大二上学期刚开学
花费时间:两周左右
过程:由于我们在暑假已经简单学习了电路并学习完了模电,我们决定从这一道最符合理论的题目入手。于是我们查阅资料,开始在multisim进行一级一级电路的仿真和调试,直到功能全部完成,我们决定直接在洞洞板上焊接出来。我们也是第一次从理论->仿真->实践,心里还是比较忐忑的,不知实物是否会成功。好在我们过硬的焊接能力(bushi,也是完美实现了功能。下面就是我们的实物图,正负电源都是用俩电源拼出来的,太简陋了 XD。
当时还不会写信号处理的软件部分(当时确实是完全未知的领域),所以就只做了基础要求。
我们第一次的尝试,还有很多问题、细节这里是说不完的,可以去看看我以前写的文章,记录了我们遇到的一些问题和注意事项:2020年电赛E题-非线性放大器失真装置——最适合用于熟悉三极管的电赛真题(硬件部分)

第二道题 2021年电赛A题《信号失真度测量装置》
开始时间:大二上学期中期
结束时间:三天?(不太记得,从开始了解到再开始做好像过了一段时间)
过程:由于对软件方面一无所知,于是听了朋友的建议,在网上找了个指导(有关指导的利弊后面再说),这是他让我做的,看起来很简单,但也是我入门信号处理程序的开始。具体有什么细节也忘了,反正就是对于网上的程序,看懂,然后修改错误的地方,当时对采样和adc并没有很深的了解,也是我第一次接触到信号处理中的FFT。

第三道题 2023年电赛H题《信号分离装置》
开始时间:大二寒假
花费时间:一周
过程:这题就是简单地将两个信号相加后,通过各种方法,将两个信号重新分离/重建出来。
这题的重点就在四个字:同频显示,不过我那时没做出来,就做了除了同频显示之外的。
通过搜索这题的开源,我们也是大开眼界:单FPGA解法,单H7DAC解法、纯硬件滤波器解法、硬件锁相环CD4046、软件锁相环,当然,你甚至可以更换DDS的晶振来降低信号漂移的速度。

第四道题 2019年电赛D题《简易电路特性测试仪》
开始时间:大二下刚开始几周
花费时间:一周左右
过程:这题刚开始,就和队友在焊板子上就有了争执,可能都太累了,于是就找了个学弟帮忙硬件那边的活。这一题是我们真正开始了软硬联调,硬件组那边准备板子,而软件组这边根据硬件和实际情况去调整代码及参数。
硬件组在那时候并没准备多少可以用的板子,故很多都是当场焊出来的,稳定性嘛也就差了点,所以最后一个进阶要求其中有两个要求我们是做不出来的,因为根本看不见变化。
从这题开始我们对整个信号系统更有了理解和概念。

第五道题 校赛B题《声源定位装置》
开始时间:校赛开始
花费时间:一个月
过程:这次的校赛题整体看上去都比较奇怪,超哥虽然给我们留了信号题,但这不是我们做的那种啊喂,但也只能硬着头皮做。听说这题是电子科大的校赛题,当时非常想知道他们做的怎么样ಠ_ಠ。
声源定位的核心算法就是gcc-phat(广义互相关时延估计算法)和TDOA(基于时间差的定位算法),虽然最后我们也只用了gcc-phat算法,因为对于模拟麦克风接收到的声音信号传达到h7的ADC上后会有微小的误差,而这个误差在声源为2m外之后被放的极大,故使用误差极大的时延去给到TDOA计算的话,也只会获取到极大的坐标偏差。
由于题目限制了五个麦克风数量和放置位置,故麦克风的摆放位置也非常重要,我们选择了一个麦克风作为语音识别,三个作为模拟麦克风(为什么不用四个,因为我h7只有三个ADC :( ),总共四个麦克风。前前后后调整了差不多五六次拜访位置,终于确定最后是斜对角摆放。
效果:除了最后一题的双声源定位我们没有设备所以没做,其他要求都做完了,而且效果还挺好,可惜没有拍个视频(虽然其中也包含了些玄学运气成分)。要问为什么可以定位那么准,因为万物皆可查表法 XD,25年电赛G题更是让我感受到了这一点。
为什么这一题写那么多🤔?因为整整写了一个月程序,测试就测试了半个月🤣

第六道题 2017年电赛E题《自适应滤波器》
开始时间:大二下期末到暑假开始
花费时间:也是从开始到结束花了比较长一段时间,不过做的话就花了一周
过程:这题基本硬件占多,软件要写的比较少,不过最后缺了一个合适的程控移相器,这题也就不了了之了,不过根据硬件员调出来的效果,看得出来要是完整完成了,又是一个佳作,不过我这里没留有照片,硬件员那里应该存有。应该就从这题不久之后,我们的硬件员就开始了疯狂画板打板模式🤣(由于之前准备的板子还是缺了一些常用的),我们三个人一个月打了三十多块板子。
第七道题 重练2021年电赛A题《信号失真度测量装置》
开始时间:电赛前一两周吧
花费时间:三天
过程:由于硬件员最后的努力,我们的板子已经非常齐全了,硬件这方面不缺什么,软件这边写程序也就很顺畅,用了三天就完成了全部功能精度还高,虽然说这题非常简单。另外,这里用的主控是MSPM0G3519,也就是根据要求,使用了TI的芯片作为主控芯片,其实熟悉了最新版的CCS界面后,TI的芯片开发还是很简单易上手的。

第八道题 重练2023年电赛H题《信号分离装置》
开始时间:电赛前一周
花费时间:三天
过程:由于锁相我们到那时其实也没弄好,刚好又在B站看到了有大佬分享了软件锁相的程序,我就决定再重新挑战一下这题。花了一天基本就把除了同频显示之外的做完了,用两天对着开源的软件锁相程序进行研究,好在在第三天的时候发现了规律,也是成功锁相了。也算是完成了我们整了半年还没解决的问题。

最后一题 2025年电赛G题《电路模型探究装置》
过程:第一天其实就把基础部分的程序写完了,不过由于某些设计失误,基础部分一的“已知电路模型”的参数竟会随着时间的变化而变化,导致一些时间也在基础部分的程序微调中流失。
后面主要的时间还是在进阶部分,是“学习”未知电路模型。这里在网上就出现了各种各样的方案,这里做个汇总吧,可能到时会在博客园单独发一篇文章:
1、时域传统派,也是最难的一个方案,根据发挥要求(1)得到“未知模型电路”的传递函数,推算出“未知模型电路”的响应函数。将此响应函数从复频域转换到时域,再将此时域响应的波形经DAC输出。
2、傅立叶级数,这里指的不是信号输出重建,而是处理时的重建,通过测量电路输入和输出端的数据并进行fft,得到幅频和相频特性曲线。由于已经知道了输入信号的各频率的幅值和相位信息,经过这个幅频特性曲线后,即可得到输出信号各频率的幅值和相位信息,此时就可以着手重建了,使用傅立叶级数将各频率的幅值和相位累加即可,给到DAC输出。
3、ifft,流程则是ADC->FFT->频域乘法->IFFT->DAC,这个就有点小复杂。滤波本质上就是 输出信号=输入信号 * 冲激响应,由于在时域中计算卷积会相当复杂,于是转到频域变为相乘,已知输入信号,要求冲激响应,就要测量未知电路电路的输入和输出端的复数增益。频域相乘后,就得到了一个复数的输出信号的频谱,这时只需用ifft即可还原波形并输出。
4、fir滤波,由于lms的介绍字眼太过诱人:专门用来学习未知传递函数,于是我选择了此方案。我们只需简单知道lms函数的输出包含了一个关键参数:fir参数,即可直接对各频率的信号都进行一次lms,得到各频率信号的fir参数,最后模拟未知滤波器输出信号的时候,用此参数在各个频率信号进行构建对应频率参数的fir滤波器即可获得正确的输出信号。但是我dac这有点问题,上文有提到,这就不说了。
5、程控滤波器,直接使用MAX262进行模拟未知滤波器参数进行信号滤波,输出的信号不会频率漂移,不过要测量得出各种滤波器参数比较麻烦。
6、查表法,逆天的来了,最简单粗暴的方法,挨个往未知电路怼各种波形,记录未知元件输出,不过非常吃内存,估计要几个M至几十个M,听一位大佬说效果非常的nice。
7、iir,暂时不太了解此方案,略。
过程是非常有意思的,与各路大神交流让我收益无穷。


总结
练的题目不算多,而且低频信号系统类的题目,都是软件员要干的会多一些,不过硬件员要准备的板子也不少,在做软硬联调的时候,最好硬件员也要懂一些软件,软件员也要懂一些硬件,不然不好交流。
队友选择
- 志同道合、能坚持
- 分软件、硬件、杂活,每个人对于不同方向的知识都要懂一些方便配合。
- 有争执很正常,好好处理。
- 信号题一般都是两核心,毕竟分两个方向,软件和硬件稍微深入一些,另一个方向的人都会比较难跟上,不过每个人每个方面都需要了解一下。
对于指导
假如实在不知道从哪入手,或者遇到了瓶颈,在咸鱼上找一个指导未必是一个不合适的方法,兴庆的是我遇到了很多算是引路人吧,给了我很多帮助。不过很多好坏利弊都要自己判别一下了。
感受
准备了那么多,最后却全丢在三分运气上。不过也感受到,要获得电赛国奖在技术上并不困难,不过只是需要天时地利罢了。
我并非佼佼者,剩下的只有遗憾,终于也是明白失意是什么感受了,引用陶渊明的一句:”悟已往之不谏,知来者之可追。“无论是我们最后运气不好,还是调试有问题,都已经结束了,对于自己,过程才是最重要的吧!
加油把自己的努力变成一个合适的结果吧,祝看到这篇文章的你们好运!
ps.原谅我可能没有写全,不过相信到时以你们的经验,已经可以自己补充了。
By ———— 膝盖中箭卫兵 and 将唐。
更多推荐

所有评论(0)