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



3 comentários:

  1. Hermano me podrias ayudar con un codigo sencillo a modo de ejemplo para guiarme.Es un filtro IIR con el dsapic30f2011.

    ResponderExcluir
  2. hi Márcio nice work
    can u send me your code and circuit on borseyogesh1436@gmail.com
    thanks

    ResponderExcluir