目录

目录

目录

前言

G题方案分享

题目

实物图

方案图

分析

《亡赛回忆录》

低频信号类入门指南

信号类

理论知识

需求仪器

软件重点(这里只说32,因为没时间学FPGA)

硬件模块准备

赛题训练


前言

考虑到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,非常重要,若有则用。

硬件模块准备

由于我是软件员,硬件这一块也就记得需要用什么,大概写一下吧。

  1. ADC前端调理:(电压可加偏执、可放大和衰减,建议多路),这个淘宝上基本没有卖,建议自己搞,加法器、反相比例什么的。
  2. 程控:VGA、程控移相器(其实就是数字电位器)、压控移相器(这个淘宝没有,自己搞)、模拟开关、程控滤波器(MAX262、LTC1068)。
  3. 基本:放大器、加法器、乘法器、比较器、AGC、衰减器、跟随器(很重要,阻抗匹配利器,有时需要单路转多路)、有效值检测、高低带通带阻滤波器。
  4. 附加:贴片转直插板子(重要,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 将唐。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐