# DSP-课设 **Repository Path**: li_lue/dsp-course-design ## Basic Information - **Project Name**: DSP-课设 - **Description**: dsp课设 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2023-10-24 - **Last Updated**: 2024-12-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # DSP课设调试记录——音频降噪设计与实现 ## ----2023.12.07 扩展程序,搭建多个滤波器 扩展部分对比多个滤波器的效果,我的解决方法是在矩阵键盘上切换滤波器,1-8键对应八个不同的滤波器,9键为关闭滤波器,在数码管上显示当前选用的滤波器和滤波器的开关状态。 #### 修改滤波器的方法: 1.按照**----2023.11.23 IIR滤波器算法移植**这一章节描述的滤波器导出方法导出系数矩阵到MATLAB工作区之后,打开工程目录中 MATLAB文件夹内的**creat_filter_for_dsp.m**脚本,修改第一行的filter_index参数,这个参数代表要修改的滤波器序号,范围是 1-8,运行脚本,可以生成iir_coeffsn.h文件,例如: ```MATLAB %将通过filterDesigner生成的SOS、G矩阵处理,保存到本地的iir_coeffs.h头文件 filter_index=4; %滤波器序号 currentDateTime = datetime('now', 'Format', 'yyyy-MM-dd HH:mm:ss'); userName = getenv('USERNAME'); ······ ``` ![](https://gitee.com/li_lue/drawing-bed/raw/master/images/20231209132326.png) 2.在app/IIR/bsp_iir.c文件中Include上刚刚在matlab中生成的头文件,例如导出一个滤波8,头文件名字是iir_coeffs8.h,那就在bsp_iir.c文件中添加#include "iir_coeffs8.h",剩下的创建滤波器跟滤波器绑定按键的操作程序都已经写好,只要加入头文件就能全部自动化完成。 3.开发板上的橙色(靠外面)的插线孔为开发板的输入,接电脑的耳机孔,蓝色(中间)的插线孔为输出口,接耳机,然后下载运行。 4.我在工程目录的MATLAB文件夹里放了滤波器仿真(怎么仿真看**----2023.11.20 IIR滤波器MATLAB仿真测试**这一章)和播放指定频率噪音的程序,可以尝试用电脑放音乐或者电视剧,然后用matlab里面的脚本(Nosiy.m)播放噪音,看滤波效果。 ## ----2023.12.05添加数码管显示滤波器开关状态,串口波形打印 纯娱乐向,没啥必要,觉得好玩才做的 效果: 源码:app/smg/* ```c if (bMS1Chg) { // 1ms软中断,每1ms运行一次 bMS1Chg = 0; //标志位清零 if (MS1 % 2 == 0) { UARTa_printf("%d\r\n", outputvalue); } if (iir_mode == 0) { SMG_DisplayBuff(0xFF, 0xC1, 0xC0, 0xFF); // on } else { SMG_DisplayBuff(0xB1, 0xB1, 0xC0, 0xFF); // off } } ``` ## ----2023.11.27 IIR库完美实现实时滤波器,测试效果与MATLAB仿真效果完全一致,课设到此几乎完成 ## ----2023.11.27 调用IIR滤波器库函数,实现实施滤波 初始化: ```c void IIR_init() { // Configure the object IIR_f32_setCoefficientsBPtr(hnd_iir, iir1_coeffs_B); IIR_f32_setCoefficientsAPtr(hnd_iir, iir1_coeffs_A); IIR_f32_setDelayLinePtr(hnd_iir, iir1_delayLine); IIR_f32_setInputPtr(hnd_iir, (float *) &in); IIR_f32_setOutputPtr(hnd_iir, (float *) &out); IIR_f32_setScalePtr(hnd_iir, iir1_scaleFactors); IIR_f32_setOrder(hnd_iir, FILTER_ORDER); IIR_f32_setInitFunction(hnd_iir, (v_pfn_v) IIR_f32_init); IIR_f32_setCalcFunction(hnd_iir, (v_pfn_v) IIR_f32_calc); hnd_iir->init(hnd_iir); } ``` 滤波: ```c float IIR_filter(float x) { float y; out = FLT_MAX; in = x; hnd_iir->calc(hnd_iir); y = out; return y; } ``` 使用: ```c interrupt void ISRMcbspRece(void) { int value = AIC23_INTPUT_AVG(); if (index % 2 == 0) { if (iir_mode == 0) { value = (int) IIR_filter((float) value); } if (index_rec < DATA_SIZE) { aic23_recdata[index_rec++] = value; } else { index_rec = 0; aic23_recdata[index_rec++] = value; } AIC23_OUTPUT(value); } LED9_TOGGLE; index++; PieCtrlRegs.PIEACK.all = 0x0020; } ``` ## ----2023.11.27 差分方程滤波测试失败 测试翻车,采用差分方程的直接计算在滤波器阶数超过4阶时莫名奇妙的出现正反馈震荡,用不了。 ## ----2023.11.23 更换滤波方案,采用差分方程滤波 当使用IIR滤波器时,可以使用直接二阶级联结(Direct Form II)的结构,这是一种常见的实现方式。 主要源代码如下: 1. **滤波器结构:** 该程序采用直接二阶级联结结构,这是IIR滤波器的一种标准实现方式。每个二阶级联结(Second Order Section,SOS)由两个分母系数(a1和a2)和三个分子系数(b0、b1和b2)定义。 2. **缓冲区:** 为了保存输入和输出的历史数据,程序使用了两个缓冲区`xBuffer`和`yBuffer`。`xBuffer`保存输入信号的历史值,`yBuffer`保存输出信号的历史值。 3. **滤波器计算:** 滤波器的计算过程在`iir_filter`函数中完成。对于每个输入`x`,程序首先将其放入输入缓冲区中,并且通过滤波器的差分方程计算对应的输出`y`。然后,将新的输出值存储在输出缓冲区中。 4. **多个SOS的叠加:** 由于存在多个SOS,程序通过循环对它们进行叠加。每个SOS都有自己的系数和缩放因子。循环中的计算将多个SOS的效果叠加,产生最终的滤波效果。 ```c volatile float xBuffer[SOS1_NUM] = {0}; volatile float yBuffer[SOS1_NUM] = {0}; float IIR_filter(float x) { int i; for ( i = SOS1_NUM - 1; i > 0; --i) { xBuffer[i] = xBuffer[i - 1]; } xBuffer[0] = x; float y = 0; for ( i = 0; i < SOS1_NUM; ++i) { float b0 = iir1_coeffs_B[i * 3]; float b1 = iir1_coeffs_B[i * 3 + 1]; float b2 = iir1_coeffs_B[i * 3 + 2]; float a1 = iir1_coeffs_A[i * 3 + 1]; float a2 = iir1_coeffs_A[i * 3 + 2]; y += b0 * xBuffer[0] + b1 * xBuffer[1] + b2 * xBuffer[2] - a1 * yBuffer[1] - a2 * yBuffer[2]; } for ( i = SOS1_NUM - 1; i > 0; --i) { yBuffer[i] = yBuffer[i - 1]; } yBuffer[0] = y; return y; } ``` 理论性能比组合滤波法快百倍以上,效果未测试,今天累了,不玩了 QAQ ## ----2023.11.23 组合滤波法测试,效率太低,得换方案 本次尝试方法如下: - 将采集数据按照一定储存深度存储 - 将新数据加入储存组并丢弃组内最早采集的数据 - 进行统一IIR滤波 - 将滤波后序列最后一项输出 测试结果: - 32k及以上采样率,128个float缓冲,计算超时,单位采样周期内无法实现数据的完全运算 - 8k采样率,128个float缓冲,计算几乎满足要求,但轻微超时,滤波效果OK - 8k采样率,64个数据缓冲,计算时间小于单个采样周期,滤波效果完美 综合考虑,组合滤波法效率过低,无法满足实时滤波功能要求,源代码未删除,仍放在app/IIR中,可以作为后期屏幕打印波形需求。 ## ----2023.11.23 IIR滤波器算法移植 将matlab生成的滤波器成功移植到板子上,并测试滤波效果,经过调试,确定滤波器为50阶椭圆IIR滤波器 ### 滤波器参数更新方法: - 文件/导出 - 设置如下,导出数据到工作区 - 运行c语言文件生成脚本**creat_filter_for_dsp.m** ### 算法移植 源文件:APP/IIR/bsp_iir.c_h 滤波器实现主要依托于ti官方fpu库 **滤波器函数**: ```c void IIR_filter_run(void) { // Locals uint16_t i; float in, out; // Configure the object IIR_f32_setCoefficientsBPtr(hnd_iir, iir1_coeffs_B); IIR_f32_setCoefficientsAPtr(hnd_iir, iir1_coeffs_A); IIR_f32_setDelayLinePtr(hnd_iir, iir1_delayLine); IIR_f32_setInputPtr(hnd_iir, (float *)&in); IIR_f32_setOutputPtr(hnd_iir, (float *)&out); IIR_f32_setScalePtr(hnd_iir, iir1_scaleFactors); IIR_f32_setOrder(hnd_iir, FILTER_ORDER); IIR_f32_setInitFunction(hnd_iir, (v_pfn_v)IIR_f32_init); IIR_f32_setCalcFunction(hnd_iir, (v_pfn_v)IIR_f32_calc); // Run the initialization function hnd_iir->init(hnd_iir); for(i = 0U; i < IIR_SIZE; i++) { out = FLT_MAX; in = IIR_input[i]; // Call the calculation routine hnd_iir->calc(hnd_iir); IIR_output[i] = out; } } ``` **滤波器测试**: ```c float amp=1.0,fre=94.0,fs=2000,PI=3.1415; for(i=0;i<512;i++) { IIR_input[i] = amp*sin(2.0*PI*fre/fs*i) + amp*sin(2.0*PI*3.0*fre/fs*i); } IIR_filter_run(); ``` **测试结果**: ## ----2023.11.20 IIR滤波器MATLAB仿真测试 源文件:./Matlab/* 主要有以下几个部分: **录音脚本**:recoding.m - 作用:录取一个5s的wav录音文件,采样率为8000hz,输出文件名为recoding.wav - 运行效果: **滤波器设计文件**:iir.fda - **设计滤波器**:用于仿真时,生成matlab函数可以用于仿真 **滤波测试**:filters.m 读取录制的wav录音文件,播放滤波前后录音以及波形和频谱,效果如下: ## ---- 2023.11.15 音频采样测试完成 SRAM共可存储262,144个16位数据,以44.1k采样率为例,共可采集262144/44100 = 5秒音频,这里采集了1秒数据进行测试 **采集程序:** 麦克风以左右双通道进行采集,实际中断触发次数为采样率的两倍,进行一次软件分频后采集左右通道的平均值作为采样值 ```c volatile unsigned int index = 0; volatile Uint32 index_rec = 0; interrupt void ISRMcbspRece(void) { int temp = AIC23_INTPUT_AVG(); if (index % 2 == 0) { AIC23_OUTPUT(temp); if(index_rec ## ---- 2023.11.15 外扩SRAM测试,链接文件修改 该核心板外扩SRAM为**IS61LV25616AL**其拥有**4,194,304-bit**=512KB容量,可以存放16bit数据共262,144位。 **非常重要:这SB开发板把XINTF管脚接到LED的D10-D14上去了,所以要使用外扩RAM不能点这几个灯,不然数据会乱飞起** **数据定义方法:** ```c #define DATA_SIZE 44100 #pragma DATA_SECTION(<数组名称>, "ZONE7DATA"); volatile <数据类型> <数组名称>[DATA_SIZE]; ``` **XINTF初始化:** 先初始化DMA,再初始化XINTF即可,调用方法: ``` DMAInitialize(); init_zone7(); ``` **链接文件修改**: 修改重点:在MEMORY分页定义中已对外扩SRAM做分页处理,并通过地址映射表得知SRAM映射在ZONE7(0x200000)上,修改块起始地址为0x200000,大小为0x040000,并将ZONE7DATA指向该分块 ``` MEMORY { ··· PAGE 1: ZONE7B : origin = 0x200000, length = 0x040000 /* TODO: XINTF zone 7 - data space 262144*16bit 512kbyte */ } SECTIONS { ··· ZONE7DATA : > ZONE7B, PAGE = 1 ··· } ``` ## ---- 2023.11.15 AIC23存在BUG,已修复,并添加采样率控制 文件:APP/AIC23.c 需要哪个采样率将define改成1即可,采样率高的优先 ```c #define SIMPLERATE_8K 0 #define SIMPLERATE_8K021 0 #define SIMPLERATE_32K 0 #define SIMPLERATE_44k1 1 #define SIMPLERATE_48K 0 ``` ## ----2023.11.13 AIC23编写,并通过收音/放音测试 添加源码:APP/AIC23/* 通过IICa控制AIC23芯片,并通过McBSP(多通道缓冲串口)传输音频数据 提供方法: ```c void AIC23_OUTPUT_LEFT(int16 value); // 左声道输出 void AIC23_OUTPUT_RIGHT(int16 value); // 右声道输出 void AIC23_OUTPUT(int16 value); // 左右声道同时输出 int16 AIC23_INTPUT_LEFT(); // 左声道输入 int16 AIC23_INTPUT_RIGHT(); // 右声道输入 int16 AIC23_INTPUT_AVG(); // 左右声道输入的平均值 ``` 初始化过程如下: ```c //Gpio复用设置 InitI2CGpio(); InitMcbspaGpio(); //初始化I2Ca和Mcbsp I2CA_Init(); InitMcbspa(); //设置参数AIC23参数 AIC23Write(0x00, 0x00); DELAY_US(100); AIC23Write(0x02, 0x00); DELAY_US(100); AIC23Write(0x04, 0x7f); DELAY_US(100); AIC23Write(0x06, 0x7f); DELAY_US(100); AIC23Write(0x08, 0x14); DELAY_US(100); AIC23Write(0x0A, 0x00); DELAY_US(100); AIC23Write(0x0C, 0x00); DELAY_US(100); AIC23Write(0x0E, 0x43); DELAY_US(100); AIC23Write(0x10, 0x23); DELAY_US(100); AIC23Write(0x12, 0x01); DELAY_US(100); //AIC23Init //使能中断 EALLOW; // This is needed to write to EALLOW protected registers PieVectTable.MRINTA = &ISRMcbspRece; EDIS; // This is needed to disable write to EALLOW protected registers PieCtrlRegs.PIECTRL.bit.ENPIE = 1; // Enable the PIE block PieCtrlRegs.PIEIER6.bit.INTx5 = 1; // Enable PIE Group 6, INT 5 IER |= M_INT6; // Enable CPU INT6 ``` ## ----2023.11.13 按键蜂鸣器反馈(娱乐向) ## ----2023.10.25 矩阵键盘驱动 ADC采样 ### 矩阵键盘: 源码路径:app/key 采用程序分离的方式,将按键的处理封装后放在主函数中,按键处理函数为 ```c /** * TODO:按键处理程序 */ void Key_Task() { uint8_t ensure = KEY_Scan(0); switch (ensure) { case KEY1_PRESS: break; case KEY2_PRESS: ······ default: } if (ensure != KEY_UNPRESS) { UARTa_printf("KEY Press:%d\n", ensure); } } ``` 将需要完成的操作放入对应的case中即可 函数: ```c /** * 矩阵键盘初始化函数 * 在main.c中PeripheralInit()被调用 */ void KEY_Init(void); /** * 按键扫描 * @param mode 模式:0:点动 1:长按 * @return */ char KEY_Scan(char mode); ``` ### ADC采样 源码路径:user/simple.c 主体由以下函数和变量构成: ```c //采样数组,用于存放采样数据,其中adc_sample_size为采样数,在simples.h中定义 volatile uint16_t adc_buff[adc_sample_size]; //索引,在采样中起辅助作用 volatile uint32_t adc_index; //采样完成标志,当一组数据采样完成之后adc_simple_done==1 volatile uint8_t adc_simple_done; /** * 初始化采样参数 * @param rate 采样率 */ void sample_init(uint32_t rate); /** * 单次采样,放在TIM2中断中 */ void sample_run(); /** * 开始采样,采样数据将覆盖原有数据 */ void sample_start(); ``` 用法: ​ 1.调用sample_start()并等待采样完成之后,标志adc_simple_done置1,在while大循环中调用软中断回调 ```c while (1) { if(adc_simple_done) { //采样完成软中断,用于处理群采样值 adc_simple_done=0; adc_simple_done_callback(); } ······ } ``` ## ----2023.10.24 新建工程,并完成代码框架搭建 主要工作: ### 1.调通串口打印 UARTa_printf: ```c void UARTa_printf(const char *format,...); ``` 用法与传统printf无异 ### 2.定时器软中断实现 通过软件中断代替Delay节省CPU资源和终端资源,注意主要有四个时间间隔的软中断,通过TIM0实现,不要改TIM0的任何配置,否则容易出错 ```c if (bMS1Chg) { // 1ms软中断,每1ms运行一次 bMS1Chg = 0; } if (bMS10Chg) { // 10ms软中断,每10ms运行一次 bMS10Chg = 0; } if (bMS100Chg) { // 100ms软中断,每100ms运行一次 LED14_TOGGLE; bMS100Chg = 0; } if (bSecChg) { // 1s软中断,每1s运行一次 LED13_TOGGLE; bSecChg = 0; } ``` ### 3.初始化过程封装 封装为两大块 ```c /** * 系统初始化 */ void SystemInit() { InitSysCtrl(); InitPieCtrl(); IER = 0x0000; IFR = 0x0000; InitPieVectTable(); } /** * 外设初始化 */ void PeripheralInit() { LED_Init(); //TIM0设置分频为150倍,计数器频率为1Mhz,中断每1ms触发,用于实现软中断标志位 TIM0_Init(150, 1000); //SCIA(UARTA) 设置波特率为115200 UARTa_Init(115200); //打印初始化完成信息 UARTa_printf("初始化完成\n"); UARTa_printf("开始运行\n"); } ```