IIR滤波器在C语言中的实现

IIR滤波器在C语言中的实现,c,embedded,digital-filter,C,Embedded,Digital Filter,我正试图用C语言为电路板实现一个IIR过滤器。我的当前代码如下所示: #include "Cpu.h" #include "Events.h" #include "ADC_1.h" #include "AdcLdd1.h" #include "DAC_1.h" #include "PE_Types.h" #include "PE_Error.h" #include "PE_Const.h" #include "IO_Map.h" #define NSP 16 static uint16_t

我正试图用C语言为电路板实现一个IIR过滤器。我的当前代码如下所示:

#include "Cpu.h"
#include "Events.h"
#include "ADC_1.h"
#include "AdcLdd1.h"
#include "DAC_1.h"
#include "PE_Types.h"
#include "PE_Error.h"
#include "PE_Const.h"
#include "IO_Map.h"


#define NSP 16
static uint16_t DACvalue, ADCvalue;
static LDD_TError Error;
static LDD_TDeviceData *MyDacPtr;
int N=10; // Filter order
double NumCoeff[11]={0.8017, -8.0174, 36.0785, -96.2094, 168.3664, -202.0397, 
                    168.3664, -96.2094, 36.0785, -8.0174, 0.8017};

double DenomCoeff[11]={1.0000, -9.5582, 41.1210, -104.8588, 175.5143, -201.4924,
                    160.6706 , -87.8720, 31.5447, -6.7119, 0.6428};

double Signal[NSP], FilteredSignal[NSP];


int main(void)
{
  /* Write your local variable definition here */
  /*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
    int j, k;
    double y, Reg[NSP];
    PE_low_level_init();
    MyDacPtr = DAC_1_Init(NULL);

    for(j=0; j<NSP; j++) Reg[j] = 0.0; // Init the delay registers
    for(;;){
      for(j=0;j<NSP;j++)
     {  
      for(k=N; k>0; k--) Reg[k] = Reg[k-1];  // Shift the delay register values.

      (void)ADC_1_Measure(TRUE);                /* do conversion and wait for the result */
      (void)ADC_1_GetValue16(&ADCvalue);            /* get the result into value variable */
      Signal[j]=(ADCvalue/65535)*3.5;               /*Convert to volts*/
      Reg[0] = Signal[j];    // The denominator

      for(k=1; k<=N; k++)  Reg[0] -= DenomCoeff[k] * Reg[k];

      y = 0;            // The numerator
      for(k=0; k<=N; k++)y += NumCoeff[k] * Reg[k];
      FilteredSignal[j] = y;

      DACvalue=(FilteredSignal[j]*65535)*3.5; //Convert back to 16 bit 
      Error = DAC_1_SetValue(MyDacPtr, DACvalue);               /* Set DA converter output */
     }
  }
#包括“Cpu.h”
#包括“Events.h”
#包括“ADC_1.h”
#包括“AdcLdd1.h”
#包括“DAC_1.h”
#包括“PE_类型.h”
#包括“PE_Error.h”
#包括“PE_Const.h”
#包括“IO_Map.h”
#定义NSP 16
静态uint16_t数据值、ADC值;
静态LDU错误;
静态LDD_TDeviceData*MyDacPtr;
int N=10;//过滤顺序
双NumCoeff[11]={0.8017,-8.0174,36.0785,-96.2094168.3664,-202.0397,
168.3664, -96.2094, 36.0785, -8.0174, 0.8017};
双DenomCoeff[11]={1.0000,-9.5582,41.1210,-104.8588,175.5143,-201.4924,
160.6706 , -87.8720, 31.5447, -6.7119, 0.6428};
双信号[NSP],滤波信号[NSP];
内部主(空)
{
/*在此处编写局部变量定义*/
/***处理器专家内部初始化。不要删除此代码***/
int j,k;
双y,Reg[NSP];
PE_low_level_init();
MyDacPtr=DAC_1_Init(NULL);

对于(j=0;j这里是你做错的地方

  • 您还需要跟踪以前的输出
    • 也许你在尝试,但有趣的嵌套循环不起作用
  • 分母应用于输出,而不是输入
    • 应用系数时,分母不是真正的分母;之所以称之为分母,是因为频率响应像分母一样工作。分母系数乘以输出的历史,然后从输出中减去它们的总和。(考虑移动所有
      y(n-1)…y(n-k)
      术语位于等号的另一侧,仅在一侧保留过滤器输出)
    • 有趣的分母系数是第一个,它乘以你实际要计算的新输出!你将把你的整个结果除以这个系数,找出新的输出是什么。(在大多数IIR过滤器中,这是1.0,所以你可以跳过这个)
  • 16位整数除以65535等于零,无需转换为伏特(如其他人所述)。
    • 只有在最后才做工程,而且只有在你需要的时候才做;你不需要
我的建议是,在保持代码结构不变的同时

unsigned int ADCvalue, 
int Reg[NSP]; // use signed values instead of unsigned, and no need for double for history of ADCvalue

for(j=0; j<NSP; j++) Reg[j] = 0; // Init the delayed input registers
for(j=0; j<NSP; j++) FilteredSignal[j] = 0.0; // Init the delayed output registers

for(;;)
{  
     for(k=N; k>0; k--) Reg[k] = Reg[k-1];  // Shift the delay register values.
     for(k=N; k>0; k--) FilteredSignal[k] = FilteredSignal[k-1];

     (void)ADC_1_Measure(TRUE);                /* do conversion and wait for the result */
     (void)ADC_1_GetValue16(&ADCvalue);            /* get the result into value variable */

     Reg[0] = ADCvalue - 0x8000;    // Save the previous inputs samples (and shift the zero value to 0)

     y = 0;
     for(k=0; k<=N; k++) y += NumCoeff[k] * Reg[k];               // The numerator
     for(k=1; k<=N; k++) y -= DenomCoeff[k] * FilteredSignal[k];  // The denominator

     FilteredSignal[0] = y/DenomCoeff[0];

     DACvalue= FilteredSignal[0] + 0x8000;   // shift the zero value back to unsigned, centered at 0x8000
     Error = DAC_1_SetValue(MyDacPtr, DACvalue);               /* Set DA converter output */
}
unsigned int ADCvalue,
int Reg[NSP];//使用有符号值而不是无符号值,并且ADCvalue的历史记录不需要双精度
对于(j=0;j0;k--)FilteredSignal[k]=FilteredSignal[k-1];
(void)ADC_1_Measure(TRUE);/*执行转换并等待结果*/
(void)ADC_1_GetValue16(&ADCvalue);/*将结果放入值变量*/
Reg[0]=ADCvalue-0x8000;//保存以前的输入样本(并将零值移到0)
y=0;

对于C中的(k=0;k二阶IIR滤波器实现:

 static double b[] = {1,  -1.4, 1};
 static double a[] = {1, -1.3, 0.5};
 static double v1m1 = 0, v2m1 = 0, v1m, v2m;

 static double iirfilter(double x1) {
    double y1 = 0;
    y1 = (b[0] * x1 + v1m1) / a[0];
    v1m = (b[1] * x1 + v2m1) - a[1] * y1;
    v2m = b[2] * x1 - a[2] * y1;
    v1m1 = v1m;
    v2m1 = v2m;
    return y1;
 }

(ADCvalue/65535)实际上为什么要这样做呢?根本不需要在电压之间做线性缩放,只需保持原样它看起来也像你的滤波器不通过直流电(直流电不只是0dB,而是-无穷大[实际上它就像-40])。但是你输入了一个无符号值,这意味着你正在处理的信号可能有一个很大的直流分量。更糟糕的是,你正在输出另一个无符号的信号,即使过滤后的信号几乎肯定会有负值。那里出了问题,但我没有足够的信息来找出正确的结果要做的是将一个16位的值除以65536。正如Steve所注意到的,这只会得到零。在48MHz Cortex-M0上使用双精度浮点运算会占用大量处理器。您应该使用定点运算,最好是16位或32位。即使使用单精度也会获得更好的性能,但可能没有什么意义t保真度损失。您还可以通过使用DMA捕获样本块并处理整个块而不是每个样本处理来提高性能。我没有对此进行测试。如果它不起作用并且需要符号更改或索引调整,请告诉我。(例如,N对于分子和分母不总是相同的值…)嘿,迈克尔,谢谢你的回答;我尝试了你的代码,但不幸的是它仍然不起作用-我从DAC得到的是静态的,而不是我正在寻找的正弦波。我一直在按照这个代码来制作我自己的代码,如上图所示,也许你想看看:我会继续尝试新的东西,我会让你知道什么是有效的。再次感谢,——尼克·泰斯E2行似乎给出了问题:FilteredSignal[0]=y/DenomCoeff[0];DACvalue=FilteredSignal[0];如果我删除第一行并用DACvalue=y替换第二行,我会得到一系列方波。-尼克。刚醒来,我会看一看。顺便问一下,adc输出是有符号的还是无符号的?方波可能是由于整数包装不正确,我将adc更改为有符号值(让我知道adc上的零输入是否给出0x0000或0x8000)。输出历史记录也未初始化;已对其进行编辑。