Mostrando postagens com marcador filtro iir. Mostrar todas as postagens
Mostrando postagens com marcador filtro iir. Mostrar todas as postagens

sábado, 14 de fevereiro de 2015

Programando um Filtro Digital IIR com a DSP Library do dsPIC30F


Este artigo demonstra os passos necessários para a elaboração de um Filtro Digital IIR Passa-Faixa  utilizando o Controlador Digital de Sinais  dsPIC30F4013 com a DSP Library da Microchip.


Filtros Digitais do tipo FIR (Resposta ao Impulso Finita) e IIR (Resposta ao Impulso Infinita) são muito utilizados por DSPs (Processadores Digitais de Sinais) para a filtragem de sinais.

Semelhantes aos filtros analógicos, os filtros digitais podem ser classificados em 4 tipos quanto à frequência:
- Passa Baixa;
- Passa Alta;
- Passa Faixa;
- Rejeita Faixa.

Funções de Janela são utilizadas em Filtros Digitais FIR para reduzir oscilações. Podem ser:
- janela Retangular;
- janela de Hamming;
- janela de Blackman;
- janela de Kaiser.

Para resolver problemas de aproximação em Filtros Digitais IIR podemos utilizar:
- filtro Butterworth
- filtro Tschebyscheff
- filtro Tschebyscheff inverso
- filtro Elliptic
- filtro Bessel

A Figura 1 mostra os 3 passos para você criar seu Filtro Digital.


 Figura 1 - Passos para criar um Filtro Digital

Para este artigo, foi escolhido a construção de um Filtro Digital IIR Passa-Faixa do tipo Blutterworth.

Vamos a algumas considerações do nosso projeto.

Objetivo do circuito: 
Filtrar um sinal entre 0 e 500 Hz e deixar "passar a faixa" de 60 à 180 Hz com 100 % da amplitude.

Especificações: 

- de acordo com o Teorema de Nyquist, a frequência de amostragem do ADC será portanto 1000 Hz (1 ksps).

- o microcontrolador escolhido é o dsPIC30F4013, um Controlador Digital de Sinais (DSC) da Microchip, que possui as funções para o Filtro Digital IIR disponíveis na DSP Library. O DSC possui as funções de um microcontrolador e de um DSP. Possui capacidade de processamento de 30 MIPS @117 MHz.
- será utilizado um programa de Digital Filter Design para definir os parâmetros do filtro digital passa-faixa a fim de obter o sinal filtrado especificado. O software fornece os coeficientes necessários para o cálculo do filtro.

- os coeficientes são utilizados no código fonte C do DSC (dsPIC). O programa consiste basicamente em declarar as variáveis (estruturadas), inicializar os ponteiros e variáveis, iniciar a estrutura do filtro com IIRLatticeInit ( ) e executar o cálculo do filtro digital com IIRLattice ( ).

- os dados resultantes do cálculo do filtro digital IIR executado em 117 pontos amostrados periodicamente são mostrados em um display gráfico GLCD para se ter uma ideia do que é "visto" pelo DSC.

- para teste do nosso circuito um gerador de sinais (ou software no computador) aplica um sinal senoidal com amplitude constante e frequência de 0 à 500 Hz à entrada analógica  do DSC que aplica o cálculo do filtro passa-faixa. O resultado é mostrado no display.


Definindo os parâmetros do Filtro Digital e gerando os coeficientes

No primeiro passo temos que definir alguns parâmetros para poder utilizar um software para gerar os coeficientes necessários para o cálculo do filtro digital pelo PIC.


 A Figura 2 traz graficamente a representação de cada parâmetro do filtro:
- frequência de amostragem;
- passband;
- stopband;
- máximo ripple no passband;
- mínima atenuação do stopband.


 
Figura 2 - Especificação do FIltro Digital passa-faixa

No segundo passo utilizaremos o QEDesign, um Digital Filter Design Software,  para modelar o nosso filtro digital com estes parâmetros e gerar os coeficientes para o programa do DSC.

No menu Design / IIR Design... iniciaremos o design para um Filtro Digital IIR. 

A Figura 3 mostra a primeira tela de configuração. Selecionaremos:
- método para o design do filtro: transformação bilinear;
- método de realização do filtro: estrutura Lattice;
- tipo de filtro: bandpass.

Figura 3 - Design para o Filtro Digital IIR

Na segunda tela, Figura 4, preencheremos os parâmetros do filtro passa-faixa conforme ilustrado anteriormente na Figura 2.

Figura 4 - Configurando os parâmetros para o filtro passa-faixa.

Na terceira tela selecionaremos a 8ª ordem para o filtro Butterworth, conforme Figura 5.


Figura 5 - Seleção da ordem do filtro.

A Figura 6 mostra o resultado para a modelagem do nosso filtro digital IIR passa faixa de acordo com os parâmetros fornecidos.


Figura 6 - Resultado da Modelagem do Filtro Digital IIR.

O gráfico da Magnitude vs Frequência da resposta do filtro é visto na Figura 7. Note que de 60 à 180 Hz teremos 100 % do sinal de entrada na saída. Na medida que distanciamos da faixa do filtro o sinal de entrada será atenuado.


Figura 7 - Gráfico Magnitude vs Frequência da resposta do filtro.

Clicando no botão "Save C File"  o software irá gerar um código fonte padrão na linguagem C para o filtro digital IIR construído, que pode ser visto na Figura 8. Com ele você pode construir seu filtro DIgital IIR em outro compilador C para computador ou outro modelo de microcontrolador.

O que nos interessa neste código são os valores dos coeficientes kappa e gamma, que iremos utilizar no programa do dsPIC.


/***************************************************************************
****************************************************************************
*   File: C:\Arquivos de programas\MDS\Filter Design\codigo C.c
*   Created by QEDesign 
*   C Code Generator  
****************************************************************************
*  Code Fragment to implement filter
*
*  The functions defined in 'qed_filt.c' must be compiled and linked in.
*    This can be accomplished by either #include "qed_filt.c"
*    or by separately compiling and linking 'qed_filt.c'
*
*** following is actual code fragment
*  extern LAT_filter LAT_codigo C;
*
*  init_lat_dbl (&LAT_codigo C);  // initialize filter structure 
*
*  LAT_codigo C.filter ( x, y, n, &LAT_codigo C);  // x is an array of input samples
*                                            // y is an array of output samples
*                                            // n is number of samples to process
*                                            // &LAT_codigo C is a pointer to the
*                                            //    filter structure
*****************************************************************************
*  This is a complete program which can be compiled and run to test the filter.
*  To change this to a subroutine only, just add in this program or add globally
*     in "qed_cgen.h" the line with the definition of DEFINE_SUBROUTINE as follows
*  #define DEFINE_SUBROUTINE
*****************************************************************************
****************************************************************************/

/* qed_cgen.h contains definitions of filter structures and function prototypes */
#include "qed_cgen.h"

/* filter functions are in files 'qed_filt.c'  */

double codigo C_kappa[9] = {
  -9.7948455815389446e-001,  /*    0 */
   9.7849500351160967e-001,  /*    1 */
  -8.6112175002729441e-001,  /*    2 */
   5.3530472709660060e-001,  /*    3 */
  -5.0440878979478099e-001,  /*    4 */
   3.9935338248048824e-001,  /*    5 */
  -1.2523348830583417e-001,  /*    6 */
   3.0071693969749614e-002,  /*    7 */
   0.0000000000000000e+000}; /*    8 */

double codigo C_gamma[9] = {
   5.1559577576911972e-004,  /*    0 */
   1.4085406081475862e-002,  /*    1 */
   3.6377697302576006e-002,  /*    2 */
  -1.1820731695711806e-001,  /*    3 */
  -3.7374705510515338e-001,  /*    4 */
  -1.4384357653258961e-001,  /*    5 */
   1.7693240143750988e-001,  /*    6 */
   1.7571299176925376e-001,  /*    7 */
   4.6649062145300228e-002}; /*    8 */

double codigo C_f[9];

double codigo C_b[9];

double codigo C_gain =  1.0000000000000000e+000; /* initial gain  */

LAT_filter LAT_codigo C = {

     1,     /* quantization: 0 off, 1 on   */
     1,     /* quantization type */
            /*   0  Floating point         */
            /*   1  Fixed point fractional */
   8,           /* lattice order */ 
   &codigo C_gain,   /* ptr to gain */
   codigo C_kappa,   /* ptr to kappa coefficients   */
   codigo C_gamma,   /* ptr to gamma coefficients */
   codigo C_f,       /* ptr to forward  function values */
   codigo C_b,       /* ptr to backward function values */
   lat_dbl};   /* ptr to filter routine */

/* call the following function first and normally only once */
/* init_lat_dbl (&LAT_codigo C)  */
/*   where &LAT_codigo C is a pointer to the LAT_filter */
/*   structure defining the filter */


/* call the following function to filter n samples */
/* LAT_codigo C.filter (pIn, pOut, int n, &LAT_codigo C); */

/*   where pIn  is a pointer to an array or buffer of samples to be filtered */
/*         pOut is a pointer to the array of filtered samples output by the filter */
/*         n    is the number of samples to filter */
/*         &LAT_codigo C is a pointer to the structure defining the filter */


#ifndef DEFINE_SUBROUTINE

/* The following main program can be used to test the filter.         */
/*   input is in file 'in' and the filtered samples are in file 'out' */
/*   The input and output files are ascii floating point values       */
/*    e.g 1.0342 with 1 sample per line                               */
/* The input files can be created in DSPworks and exported as         */
/*   ascii floating point or any other system capable of creating     */
/*   ascii files with floating point values.                          */
/* The filtered output file can be imported into DSPworks as an ascii */
/*    floating point file and an FFT can be run to validate           */
/*    the frequency response.                                         */

#include "qed_filt.c"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>


#define INSZ1  1000
#define OUTSZ1 1000

static    double x[INSZ1], y[OUTSZ1];


int main(int argc, char *argv[])

{
  int i, in_count, file_status, error;

  FILE *fin;              /* input file of samples */
  FILE *fout;             /* output file of filtered samples */ 

  fprintf (stderr," ***** start filter test *****\n");

  fprintf (stderr," this program accepts 0,1 or 2 command line arguments\n");
  fprintf (stderr," the first  argument is the filename of the input file\n");
  fprintf (stderr," the second argument is the filename of the output file\n");
  fprintf (stderr," if there are 0 arguments, input and output is respectively\n");
  fprintf (stderr,"     stdin and stdout\n");
  fprintf (stderr," if only one argument is specified, then output is stdout\n");
  fprintf (stderr," if input is stdin rather than a file, then fscanf expects input\n");
  fprintf (stderr,"     from the console which may be piped in or entered directly\n");

  fin  = stdin;
  fout = stdout;
  error = 0;

  if (argc == 1) {
        fprintf(stderr," ***** waiting for input *****\n");
  }
  if (argc >= 2) {
      fin = fopen (argv[1], "r");
      if (fin == NULL) {
          fprintf(stderr,"\n error - Cannot open file %s for input\n", argv[1]);
          error = 1;
      }
  }
  if (argc >= 3) {
      fout = fopen (argv[2], "w");
      if (fout == NULL) {
          fprintf(stderr,"\n error - Cannot open file %s for output\n", argv[2]);
          error = 1;
      }
  }
  if (error) {
      fprintf(stderr," ***** end filter test *****\n");
      return(0);
  }
  
  

  init_lat_dbl (&LAT_codigo C);


  do {

      /* get input samples */ 
      for (in_count = 0; in_count < INSZ1; in_count++) { 
          file_status = fscanf(fin,"%lf",&x[in_count]); 
          if (file_status != 1) 
              break; 
      }

      /* filter samples */ 

      if (in_count == 0) break;

      LAT_codigo C.filter( x, y, in_count, &LAT_codigo C);

      for (i = 0; i < in_count; i++)
          fprintf (fout,"%f\n",y[i]);

  } while (file_status == 1);

  fclose (fin); 
  fclose (fout);

  fprintf(stderr," ***** end filter test *****\n");
  return(1);

}
 
#endif 

Figura 8 - Código gerado pelo Digital Filter Design.


O Hardware

Utilizaremos o mínimo de componentes necessários para criar e testar o Filtro Digital IIR.

A Figura 9 mostra o diagrama simplificado do circuito.


Figura 9 - Diagrama simplificado do circuito.


A seguir a explicação para cada etapa:

SINAL ANALÓGICO: Deve estar compreendido dentro da faixa de medição do conversor analógico-digital (ADC) do microcontrolador: amplitude e frequência.

BIAS: Offset no sinal de entrada para metade da tensão de referência do ADC: os cálculos do filtro digital da biblioteca DSP necessitam que o sinal de entrada esteja na faixa de -1,0 à 1,0 (escala fracionária). Assim o zero será 2,5 V para uma tensão de referência do ADC de 0 Vcc (min) e 5 Vcc (max).

AMPLIFICADOR: para aumentar a amplitude do sinal de entrada.

FILTRO ANTI-ALIASING: é um filtro passa-baixa para evitar erros na medição. Assegura o Teorema de Nyquist: o sinal amostrado terá no máximo a metade da frequência de amostragem. Ex.: 500 Hz no máximo quando fs é 1000 sps.

dsPIC: Controlador Digital de Sinal (microcontrolador & DSP). Executa os cálculos matemáticos no sinal discreto amostrado através da biblioteca DSP da Microchip. Neste projeto utiliza os cálculos de um filtro digital IIR Lattice passa-faixa Butterworth de 8ª ordem.



O código fonte do PIC 

 No terceiro passo elaboraremos o programa do PIC no compilador MPLAB C30 da Microchip. Podem ser utilizadas as linguagens C ou Assembly. Optamos pela linguagem C.

Incluímos em nosso projeto a biblioteca "dsp.h" que possui as funções para o cálculo dos filtros digitais IIR, FIR e outras utilizadas em cálculos de processamentos digitais, como a FFT.

Na Figura 10 é visto parte do código em dsp.h relacionado à declaração da estrutura do Filtro Digital IIR Lattice.


 /*       Estrutura do Filtro Digital IIR Lattice                            */
typedef struct {
   int order;                           // filter order (M) 
                                        // M <= N (see IIRLattice for N) 
   fractional* kappaVals;               // ptr to lattice coefficients 
                                        // (k[m], 0 <= m <= M) 
                                        // either in X-Data or P-MEM 
   fractional* gammaVals;               // ptr to ladder coeficients 
                                        // (g[m], 0 <= m <= M) 
                                        // either in X-Data or P-MEM 
                                        // NULL for all pole implementation 
   int coeffsPage;                      // page number of program memory if 
                                        // coefficients are in program memory 
                                        // COEFFS_IN_DATA if not
   fractional* delay;                   // ptr to delay 
                                        // (d[m], 0 <= m <= M) 
                                        // only in Y-Data 
} IIRLatticeStruct;                     // IIR Lattice filter structure 

extern fractional* IIRLattice (         // IIR Lattice filtering 
   int numSamps,                        // number of input samples (N) 
   fractional* dstSamps,                // ptr to output samples 
                                        // (y[n], 0 <= n < N) 
   fractional* srcSamps,                // ptr to input samples 
                                        // (x[n], 0 <= n < N) 
   IIRLatticeStruct* filter             // filter structure 

                                        // returns dstSamps 
);

extern void IIRLatticeInit (            // Zero out dealy in filter structure 
   IIRLatticeStruct* filter             // Lattice filter structure 
);

Figura 10 - Estrutura do filtro IIR Lattice na biblioteca dsp.h.

No cógigo main.c do dsPIC iremos declarar:
-  a variável fooiir do tipo IIRLatticeStruct que irá conter a estrutura do nosso Filtro IIR Lattice;
- os vetores que irão conter os coeficientes do Filtro Digital IIR: kappa [ ], gamma [ ] e delayiir [ ];
- o vetor para os dados de entrada do filtro: adc_data [ ]. Os dados podem ser coletados pelo conversor analógico-digital do dsPIC, por exemplo;
- o vetor para os dados de saída do filtro: out_iir [ ].

O código contendo as declarações está contido na Figura 11.

/* declara estrutura Lattice filter structure com nome*/
IIRLatticeStruct fooiir;

fractional kappa[9] __attribute__ ((section (".xbss, bss, xmemory")));
fractional gamma[9] __attribute__ ((section (".xbss, bss, xmemory")));
fractional delayiir[9] __attribute__ ((section (".ybss, bss, ymemory")));

//fractional iniir [117];  ==> adc_data
fractional outiir [117];
int numero_amostra;


fractional adc_data [117]= {0,0,0,0,0,0,0,0,0,0,  //vetor de dados para o grafico de tendencia no GLCD
         0,0,0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0}; 

Figura 11 - Declaração das variáveis do filtro em main.c.

Após o início do programa, na função main ( ) temos que iniciar as variáveis e atribuir os coeficientes do filtro. Os valores para kappa e gamma foram retirados do código C gerado pelo Digital Filter Design Software.

A inicialização das variáveis é vista no código da Figura 12.


//------------- inicializa variaveis da estrutura filtro iir ---------------------------
//------------- filtro passa Faixa 40 A 180 Hz, amostragem 1 khz -----------------------
    fooiir.kappaVals = &kappa [0];        //inicia ponteiros 
    fooiir.gammaVals = &gamma [0];
    fooiir.delay = &delayiir [0];
    fooiir.coeffsPage = COEFFS_IN_DATA;     //coeficientes do filtro estao na memoria RAM     
    fooiir.order = 8;                       //seta a ordem do filtro
    numero_amostra =117;
// Coeficientes do filtro iir lattice
kappa[0] = Q15(-9.7948455815389446e-001); //passa faixa 40 a 180 hz 8 polos
kappa[1] = Q15( 9.7849500351160967e-001); 
kappa[2] = Q15(-8.6112175002729441e-001); 
kappa[3] = Q15( 5.3530472709660060e-001); 
kappa[4] = Q15(-5.0440878979478099e-001); 
kappa[5] = Q15( 3.9935338248048824e-001); 
kappa[6] = Q15(-1.2523348830583417e-001); 
kappa[7] = Q15( 3.0071693969749614e-002);
kappa[8] = Q15( 0.0000000000000000e+000);

gamma[0] = Q15( 5.1559577576911972e-004);
gamma[1] = Q15( 1.4085406081475862e-002);
gamma[2] = Q15(3.6377697302576006e-002);
gamma[3] = Q15(-1.1820731695711806e-001);
gamma[4] = Q15( -3.7374705510515338e-001);
gamma[5] = Q15( -1.4384357653258961e-001);
gamma[6] = Q15( 1.7693240143750988e-001);
gamma[7] = Q15(1.7571299176925376e-001);
gamma[8] = Q15( 4.6649062145300228e-002);  

Figura 12 -  Inicialização das variáveis e coeficientes do filtro.

Após o início do hardware do PIC e inicializadas as variáveis e coeficientes o programa pode passar por um loop infinito while (true), lendo dados do conversor analógico-digital e executando os cálculos do filtro.

A Figura 13, mostra o código necessário para executar o cálculo do filtro.

Considera-se que os dados de entrada (vindos do ADC) já estejam no vetor adc_data [ ].

2 funções são necessárias:

- IIRLatticeInit: inicializa a estrutura do Filtro Digital IIR Lattice;

- IIRLattice: executa o cálculo do Filtro Digital IIR Lattice;


    //inicializa estrutura do filtro digital IIR Lattice
    IIRLatticeInit (&fooiir); 


    //executa calculos do filtro iir lattice
    IIRLattice (numero_amostra, &outiir[0], &adc_data[0], &fooiir); //(numeroamostragem, *y (n), *x (n), *estrutura do filtro)

Figura 13 - Inicializando estrutura e executando o cálculo do Filtro Digital IIR Lattice.

O resultado final do nosso circuito é visto na Figura 14. Interligando um display gráfico GLCD 128x64 vemos o sinal de 60 Hz após ser aplicado ao Filtro Digital IIR.



Figura 14 - Sinal de 60 Hz aplicado ao circuito do Filtro DIgital IIR passa-faixa.

Observação: se no hardware não for considerado o filtro anti-aliasing (passa-baixa) pode-se ter um resultado desagradável na saída do filtro. Na Figura 15 fizemos este teste, retirando o filtro anti-aliasing do circuito e aplicando um sinal de entrada maior do que 500 Hz.


Figura 15 - Efeito de aliasing na amostragem digital.

O vídeo a seguir mostra o nosso circuito com o Filtro Digital IIR sendo elaborado e testado com um sinal de entrada variando de 0 à 500 Hz. O erro de aliasing também pode ser observado.



Video do circuito do Filtro Digital IIR com dsPIC


Referência Bibliográfica

SCANDELARI, L. Filtros Digitais. Cap. 5. Disponível em: http://paginapessoal.utfpr.edu.br/scandelari/laboratorio-de-pds/filtros.pdf/at_download/file

MICROCHIP. Filter Design for dsPIC. Digital Filter Design and Analysis System. Disponível em: http://ww1.microchip.com/downloads/en/DeviceDoc/SW300001%20manual.pdf

MICROCHIP. CE005 - Using FIR Filters from dsPIC Filter Design and DSP library. Disponível em: http://ww1.microchip.com/downloads/en/DeviceDoc/CE005_FIR_DSP_lib_Filter.zip