terça-feira, 1 de setembro de 2015

PIC32 Tetris Game and Graphic Display 240x320 TouchScreen



This is my project of the Tetris Game with PIC32MX and Graphic Display 240x320 QVGA TouchScreen.





Resources:

- standard Tetris pieces: O, I, S, Z, L, J and T;
- standard Tetris board: 20 lines x 10 columns;
- command Pause;
- selection for Initial Level;
- color or monochrome block;
- game record storage in external memory Flash SST25;
- standard Tetris falling iteration delay: 0.5 sec - 0.05 sec;
- standard Tetris score;
- 10 difficulty Levels;
- randomizes pieces with Timer4 (not used random function of the C Language).


 Figure 1 - Tetris Game with PIC32MX.



Hardware:
 - PIC32 USB Starter Kit II;
- I/O Expansion Board;
- GFXv3 SSD1926 LCD Graphic Controller;
- Graphic Display Solomon Systeck 240x320 with TouchScreen.


Figure 2 - The PIC32MX USB Starter Kit II.

Figure 3 - Circuit block diagram.


Figure 4 - PIC32 Starter kit Boards.



Figure 5 - Graphic Display 240x320 TouchScreen.


Software:
- MPLAB IDE v8.83;
- MPLAB C32 v2.02.

Firmware:
- MLA v2013-06-15;
- Graphics Library 3.06.04.


Figure 6 - Init screen.


Figure 7 - The PIC32MX Tetris Game.



Figure 8 - Color screen.


Figure 9 - Monochrome.


Figure 10 - Game Over!



This is the video of my project of the Tetris Game with PIC32MX and Graphic Display 240x320 QVGA TouchScreen.

Video - PIC32MX Tetris Game and Graphic Display QVGA TouchScreen.


Alexey Pajitnov created the first electronic version of Tetris in 1985 in Russia!


Special thanks to Colin Fahey for providing information, settings and examples codes of standard Tetris game on virtual gallery - website "The History of Tetris"!




References:



FAHEY, Colin. Standard Tetris. History of Tetris. Tetris A.I. system. 2003-2012. Available in: http://colinfahey.com/tetris/tetris.html.

MORAES, Ivan. Tetris Borland Turbo C. 2003. Available in:  http://www.linhadecodigo.com.br/codigo.aspx?id=382

GUNEE, Rickard. PIC-Tetris. PIC16F84 microcontroller-based NTSC / PAL video Tetris game.  1998. Available in:  http://www.rickard.gunee.com/projects/video/pic/tetris.php

MITCHELL, Graham. PICtris. Tetris on a PIC (18LF4520) Microcontroller. 64 LED matrix. 2010. Available in: http://digitaldiy.io/home/swordfish/projects/213-tetris-on-a-pic.html#.VZXwhFIxrkc

HENDRICH. PIC16F628 Tetris for Terminals game. 2006. Available in: https://tams.informatik.uni-hamburg.de/applets/hades/webdemos/95-dpi/pic16f628-tetris/tetris.html

MICROCHIP. Software Libraries. Available in: http://www.microchip.com/SoftwareLib.aspx

MICROCHIP. Microchip Graphics Library. Available in: http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2680&dDocName=en543091&utm_source=MicroSolutions&utm_content=AIPD&utm_campaign=Graphics+Library

domingo, 12 de julho de 2015

Teste de Tiristores TRIAC e SCR

Precisa testar um Tiristor???
Este post apresenta 2 testes básicos para verificar se um Tiristor está bom.


 

O Tiristor é uma chave semi-condutora para Corrente Alternada. Utilizado muito para Controle ON/OFF em chaves estáticas e Controle Proporcional de potência através de controle do disparo por trem de impulso, para cargas resistivas, ou ângulo de fase, para cargas indutivas.

O SCR conduz somente nos semiciclos positivos e o TRIAC conduz nos dois semiciclos. A condução é controlada por pulsos aplicados no gatilho (gate). O corte é feito na passagem por zero volt da tensão de alimentação.

Testando um Tiristor TRIAC

São basicamente 2 testes para verificar se o Tiristor está funcionando corretamente.

No primeiro teste verificamos a resistência do gate do Tiristor: com o multímetro, testamos a resistência entre os terminais do gate G e o terminal A1: deve apresentar uma resistência baixa de dezenas a algumas centenas de ohms, dependendo da potência do TRIAC. Entre os outros terminais a resistência deve ser infinita. Neste teste se o multímetro apresentar uma resistência muito baixa (menor do que 1 ohm) em qualquer combinação de pinos, o Tiristor está em curto-circuito. Geralmente um semicondutor danificado está em curto-circuito (0 ohm) ou aberto (resistência infinita)

Na Figura 1 podemos identificar os terminais e os principais parâmetros de um Tiristor retirados do seu datasheet. Exemplo para o Tiristor TRIAC T410-600D. Existem diversos sites na internet que encontramos o datasheet de componentes eletrônicos, como o http://alldatasheet.com/.
 
Figura 1 - Identificação dos terminais e parâmetros de um TRIAC retirados do datasheet.

No segundo teste verificamos o disparo/chaveamento do Tiristor: deve ser feito com o auxílio de uma fonte de alimentação alternada, uma carga, por exemplo uma lâmpada, e um resistor de gate para poder disparar o semicondutor. Isso porque não tem como disparar os TRIACs de potência com a tensão de teste do multímetro! É importante sempre consultar o datasheet para conhecer as especificações de tensão, corrente e corrente de gate do componente.

A Figura 2 mostra o diagrama do nosso circuito para teste do Tiristor TRIAC.

Figura 2 - Diagrama do circuito para teste do Tiristor TRIAC.

O resistor e a carga (lâmpada) foram escolhidos de acordo com os dados do datasheet do Tiristor. A tensão alternada aplicada, a corrente da carga e a corrente de gate devem ser menores do que os valores nominais encontrados no datasheet do Tiristor que se deseja testar em bancada. Caso ultrapasse os valores nominais de tensão e corrente o Tiristor irá queimar.

Você pode acompanhar os testes apresentados neste post no vídeo abaixo.

 Vídeo 1 - Testando um Tiristor TRIAC.



Testando um Tiristor SCR

Os Tiristores do tipo Silicon Controlled Rectifier (SCR) também podem ser testados com este circuito. A diferença é que um SCR conduz apenas nos semiciclos positivos quando recebem um pulso de disparo no gate. Com metade dos ciclos a carga recebe no máximo a metade da potência nominal. Para se obter a potência total, ou o controle nos 2 semiciclos, deve-se utilizar no circuito 2 SCRs em anti-paralelo.

Demonstramos aqui um teste para o Tiristor SCR TIC106D. Na Figura 3 são apresentados seus principais dados e pinagem retirados do datasheet.
Figura 3 - Características e pinos do SCR TIC106D.

A Figura 4 mostra o diagrama para o circuito de teste de um Tiristor SCR TIC106D.
Figura 4 - Circuito para teste do SCR.

Resultado esperado:
- a lâmpada deve acender apenas quando o botão estiver pressionado.
- se a lâmpada acender quando o botão não estiver pressionado o Tiristor está em curto-circuito (ruim);
- se a lâmpada não acender quando o botão for acionado o Tiristor está aberto (ruim) [considerando que a corrente de disparo, que circula no resistor do gate, está de acordo com a do datasheet].

É sempre importante estar de posse dos dados do datasheet do Tiristor que deseja testar.

O resistor de 1,5 kohm foi dimensionado de acordo com a corrente do gate e serve para testar diversos modelos de Tiristores.

quinta-feira, 4 de junho de 2015

Relógio Digital com o 8051

Que tal montar seu próprio Relógio Digital com um 8051?


Este artigo mostra o projeto de um Relógio Digital simples com 4 displays do tipo 7 Segmentos para fins didáticos.



Para a montagem é utilizada a lista de componentes:

- 1 microcontrolador da família 8051 (Atmel AT89S52);
- 4 displays 7 segmentos catodo comum (-);
- 4 transistores NPN BC337 para drive dos displays.
- 8 transistores PNP BC327 para drive dos segmentos.
- 2 botões para ajustes de hora e minuto;
- 1 cristal de 11,0592 MHz
- 2 capacitores de 33 pF para o cristal.
- 8 resistores de 330 ohms para os segmentos do display;
- 2 resistores de 10 kohms para pullup dos botões;
- 12 resistores de 10 kohms para acionamento dos transistores.

O circuito é alimentado com 5 Vcc.

O diagrama de interligação é mostrado na Figura 1.
 
Figura 1 - Diagrama do Relógio Digital.

A montagem pode ser feita em Matriz de Contatos. Veja a Figura 2.
 
Figura 2 - Montagem do Circuito em Matriz de Contatos.


O Vídeo a seguir mostra o Relógio Digital funcionando e a Simulação do circuito no Proteus.

 Vídeo 1 - Nosso Relógio Digital com o 8051 funcionando.


O programa do 8051

O programa do Relógio Digital foi escrito em Linguagem Assembly e pode ser compilado com os Compiladores ASM51 ou Keil. No Anexo 1 você encontra o código fonte.

A interrupção do Timer1 é utilizada como base de tempo para o relógio (clock).

O programa inicia no endereço 100H.

Após a inicialização algumas variáveis são carregadas com seus valores iniciais. 


O temporizador Timer1 é configurado para operar no modo 2: 8-bit com recarga automática. 

Os registros TL1 e TH1, carga e recarga do Timer1, são setados com valor (255 - 225).

No final das configurações são habilitadas as interrupções do Timer1 e global.

O Timer1 é inicializado.

O programa passa a rodar em um loop infinito verificando se os botões de ajustes de hora e minuto foram pressionados e atualizando o display.

A rotina de interrupção do Timer1 é chamada a cada 244,140625 us.
 
Os cálculos para definir a carga do Timer1 com valor (255 - 225) estão disponíveis no Quadro 1:


; CLOCK DO RELOGIO BASEADO NA INTERRUPCAO DO TIMER 1 
; OPERANDO NO MODO 1 - 8 BITS COM RECARGA AUTOMATICA
;
; CALCULOS
; FREQUENCIA DO CRISTAL f = 11.059.200 MHz
; PERIODO DO CRISTAL T = 1/f  =  9,04224537037037...e-8     s
; PERIODO DE UMA INSTRUCAO     = T * 12 =     1,085069444...e-6  s
; NUMERO DE INSTRUCOES EM 1 SEGUNDO = 921.600 IPS
; CARREGAMENTO DO TIMER 1 = 921.000 / 4096 = 225
; 4096 DECIMAL = 1000 HEXADECIMAL = 1 0000 0000 0000 BINARIO

Quadro 1 - Cálculos para definir o tempo da interrupção do Timer1.

As variáveis CONTADOR_L e CONTADOR_H armazenam o número de interrupções.

A cada 4096 interrupções (1000H em hexadecimal) é completado 1 segundo. A variável SEGUNDO é incrementada em 1 unidade e quando necessário as demais variáveis são incrementadas: 

- UNIDADE, para a unidade dos minutos do relógio;
- DEZENA, para dezena dos minutos do relógio;
- CENTENA, para a unidade das horas do relógio;
- MILHAR, para dezena das horas do relógio.

Este relógio possui o formato de 24 horas.

O acionamento do display é feito por multiplexação. Cada dígito fica ligado por um tempo de 1 ms. Pela cintilação do olho humano enxergamos os 4 displays acesos, mas na verdade apenas um se mantém ligado!

O horário do relógio é atualizado.

A cada 500 ms o "ponto" do relógio é invertido de status, para "piscar" a cada segundo.

Os "2 pontos" do relógio são formados invertendo-se a montagem do terceiro display de 7 segmentos em 180°.

Após compilar o seu projeto com o ASM51 ou Keil você deve programar o microcontrolador 8051 com o arquivo .hex. O vídeo 8051 - O primeiro projeto - Pisca LED - Compilação e Gravação do AT89S52  mostra um exemplo de como programar o AT89S52.



Você agora pode implementar o código e criar um horário de alarme para adicionar a função de despertador para o seu Relógio Digital!



Obs: 

Este é um circuito didático para ensinar o princípio de funcionamento de um relógio digital.


Para melhor precisão no relógio deve-se utilzar um Relógio de Tempo Real (Real Time Clock - RTC), como por exemplo o DS1302  com cristal de 32,768 kHz ou um microcontrolador com o módulo RTC interno.

Exemplos de circuitos com RTCC:
https://www.youtube.com/watch?v=zR0KW0H_dD4
https://www.youtube.com/watch?v=k_SVMvWyYak


O circuito também não pode ser desligado da alimentação, senão perde a hora, pois não tem bateria de backup.


Referência:

NICOLOSI, D. E. C.; Laboratório de Microcontroladores: Treino de instruções, hardware e software. São Paulo: Érica, 2002.



Anexo 1 - Código fonte do Relógio Digital com o 8051 em Assembly 

; ############ RELOGIO DIGITAL COM 4 DISPLAYS DE 7 SEGMENTOS ########################

; RELOGIO DIGITAL COM HORA MINUTO E 2 PONTOS
; AJUSTE DE HORA E MINUTO ATRAVES DE 2 TECLAS

; MICROCONTROLADOR 8051 - AT89S52
; COMPILADOR ASM51 ou Keil
; JUNHO/2013  M.R.G.

; SEGMENTOS DO DISPLAY NO PORT 3
; DISPLAY CATODO COMUM (-) 
; SEGMENTO ATIVO COM LOGICA NIVEL BAIXO (TRANSISTOR PNP PARA CADA SEGMENTO)

; DISPLAY UNIDADE P2.0
; DISPLAY DEZENA  P2.1
; DISPLAY CENTENA P2.2
; DISLAY MILHAR   P2.3

; BOTAO 1 P2.4
; BOTAO 2 P2.5

; CLOCK DO RELOGIO BASEADO NA INTERRUPCAO DO TIMER 1 
; OPERANDO NO MODO 1 - 8 BITS COM RECARGA AUTOMATICA
; CALCULOS
; FREQUENCIA DO CRISTAL f = 11.059.200 MHz
; PERIODO DO CRISTAL T = 1/f  =  9,04224537037037...e-8     s
; PERIODO DE UMA INSTRUCAO     = T * 12 =     1,085069444...e-6  s
; NUMERO DE INSTRUCOES EM 1 SEGUNDO = 921.600 IPS
; CARREGAMENTO DO TIMER 1 = 921.000 / 4096 = 225
; 4096 DECIMAL = 1000 HEXADECIMAL = 1 0000 0000 0000 BINARIO


$MOD51

; *************** DEFINE CONSTANTES   ********************************** 
        TEMPO10MS      EQU      9210
        TEMPO1MS       EQU      921
        DISPU          EQU      P2.0
        DISPD          EQU      P2.1
        DISPC          EQU      P2.2
        DISPM          EQU      P2.3
        BOTAO1         EQU      P2.4
        BOTAO2         EQU      P2.5

; ******************** DECLARACAO DE VARIAVEIS DA RAM ******************

        UNIDADE        EQU 60H       ;NUMERO
        DEZENA         EQU 61H       ;NUMERO
        CENTENA        EQU 62H       ;NUMERO
        MILHAR         EQU 63H       ;NUMERO
        UNIDADE7SEG    EQU 64H       ;CARACTERE
        DEZENA7SEG     EQU 65H       ;CARACTERE
        CENTENA7SEG    EQU 66H       ;CARACTERE
        MILHAR7SEG     EQU 67H       ;CARACTERE
        CONTADOR_L     EQU 68H       ;LSB CONTADOR INTERRUPCOES T1
        CONTADOR_H     EQU 69H       ;MSB CONTADOR INTERRUPCOES T1
        SEGUNDO        EQU 6AH       ;ARMAZENA OS SEGUNDOS
        PONTO          EQU 00H       ;BIT UTILIZADO PARA PISCAR 2 PONTOS

;*************** VETOR DE RESET ****************************************

        ORG    000H

        NOP
        NOP
        LJMP INICIO

;************ VETOR DE INTERRUPCAO DO TIMER    1***************************

        ORG  01BH

        LJMP INT_TIMER1

;************************** MAIN ***************************************
        ORG    100H

INICIO:    
        MOV SP, #30H    ;ALTERA ENDERECO DA BASE DA PILHA

;DESLIGA DISPLAYS
        MOV P3, #0FFH    ;DESATIVA TODOS OS SEGMENTOS
        CLR DISPU         ;DESATIVA TODOS OS DISPLAY
        CLR DISPD
        CLR DISPC
        CLR DISPM


;INICIALIZA VARIAVEIS DA RAM
        MOV        UNIDADE, #0        
        MOV        DEZENA, #0        
        MOV        CENTENA, #0        
        MOV        MILHAR, #0        
        MOV        CONTADOR_L, #0    
        MOV        CONTADOR_H, #0    
        MOV        SEGUNDO, #0
        CLR        PONTO        


;CONFIGURA TIMERS

        MOV    TMOD,#00100001B   ;TIMER 0 MODO 1 - 16 BITS
                                 ;TIMER 1 MODO 2 - 8 BITS COM RECARGA AUTOMATICA
        MOV TL1, #(255 - 225)    ;CARGA DO TIMER 1
        MOV TH1, #(255 - 225)    ;RECARGA DO TIMER 1


        LCALL ESCREVE7SEG        ;TESTE DISPLAYS


        SETB EA                 ;HABILITA FLAG DE INTERRUPCAO GERAL
        SETB ET1                ;HABILITA FLAG DE INTERRUPCAO DO TIMER 1
        
        SETB TR1                ;INICIA TEMPORIZADOR TIMER1



LOOP:
        JB BOTAO1, TESTA_BT2        ;SE TECLA NAO PRESSIONADA(1) DESVIA
        LCALL ACERTA_MINUTO
TESTA_BT2:
        JB BOTAO2, ACIONA_DISPLAY   ;SE TECLA NAO PRESSIONADA(1) DESVIA
        LCALL ACERTA_HORA
ACIONA_DISPLAY:
        LCALL ATUALIZA_DISPLAY

        LJMP LOOP





;AJUSTE MANUAL DO MINUTO PELO BOTAO 1
ACERTA_MINUTO:
        INC UNIDADE
        MOV A, UNIDADE
        CJNE A, #10D, INCREMENTO_MIN_FIM
ACERTA_DEZ_MINUTO:
        MOV UNIDADE, #0
        INC DEZENA
        MOV A, DEZENA
        CJNE A, #06D, INCREMENTO_MIN_FIM
        MOV DEZENA, #0
INCREMENTO_MIN_FIM:
        RET


;AJUSTE MANUAL DA HORA PELO BOTAO 2
ACERTA_HORA:
        INC CENTENA
        MOV A, CENTENA
        CJNE A, #04D, ACERTA_DEZ_HORA        ;VERIFICA SE COMPLETOU 24 HORAS
        MOV A, MILHAR
        CJNE A, #02D, INCREMENTO_HORA_FIM
        MOV CENTENA, #0                      ;COMPLETOU 24 HORAS ZERA O RELOGIO
        MOV    MILHAR, #0
        AJMP INCREMENTO_HORA_FIM
ACERTA_DEZ_HORA:
        CJNE A, #10D, INCREMENTO_HORA_FIM
        MOV CENTENA, #0
        INC MILHAR
INCREMENTO_HORA_FIM:
        RET





;SUBROTINA PARA ESCRITA NOS DISPLAY
ESCREVE7SEG:
        MOV P3, #00000000B    ;ACENDE TODOS OS SEGMENTOS
        LCALL MUX7SEG

;SUBROTINA MULTIPLEXA NUMERO NO DISPLAY    - VISUALIZA 1 DISPLAY ACESO POR VEZ
MUX7SEG:
          CLR DISPU           ;DESATIVA TODOS OS DISPLAY
        CLR DISPD
        CLR DISPC
        CLR DISPM

        SETB DISPU            ;LIGA DISPLAY DA UNIDADE
        LCALL DELAY500MS      ;DELAY
        CLR DISPU             ;DESLIGA DISPLAY DA UNIDADE
        NOP

        SETB DISPD            ;LIGA DISPLAY DA DEZENA
        LCALL DELAY500MS      ;DELAY
        CLR DISPD             ;DESLIGA DISPLAY DA DEZENA
        NOP

        SETB DISPC            ;LIGA DISPLAY DA CENTENA
        LCALL DELAY500MS      ;DELAY
        CLR DISPC             ;DESLIGA DISPLAY DA CENTENA
        NOP

        SETB DISPM            ;LIGA DISPLAY DO MILHAR
        LCALL DELAY500MS      ;DELAY
        CLR DISPM             ;DESLIGA DISPLAY DO MILHAR
        NOP

          RET


; ############## ATUALIZA DISPLAY 7 SEGMENTOS ##########################
ATUALIZA_DISPLAY:

    
        LCALL CONVERTE            ;CONVERTE NUMERO NO CARACTER CORRESPONDENTE 
                                  ;PARA O DISPLAY 7 SEGMENTOS

           MOV R3, #50D           ;4 X 50 X 1ms  = 200 ms

;FAZ A MULTIPLEXACAO DOS 4 DISPLAYS DE 7 SEGMENTOS
LOOP_DISPLAY_CONT2:
        MOV P3, UNIDADE7SEG    ;MOVE CARACTER DA UNIDADE PARA DISPLAY
        NOP
        NOP
        SETB DISPU            ;LIGA DISPLAY DA UNIDADE
        LCALL DELAY1MS        ;DELAY
        CLR DISPU             ;DESLIGA DISPLAY DA UNIDADE
        NOP
        NOP

        MOV A, DEZENA7SEG    ;MOVE CARACTER DA DEZENA PARA ACC
        MOV C, PONTO         ;MOVE VALOR DO PONTO DECIMAL
        MOV ACC.7, C
        MOV P3, A            ;MOVE CARACTER DA DEZENA PARA DISPLAY COM PONTO
        NOP
        NOP
        SETB DISPD            ;LIGA DISPLAY DA DEZENA
        LCALL DELAY1MS        ;DELAY
        CLR DISPD             ;DESLIGA DISPLAY DA DEZENA
        NOP
        NOP

        MOV A, CENTENA7SEG    ;MOVE CARACTER DA CENTENA PARA ACC
        MOV C, PONTO          ;MOVE VALOR DO PONTO DECIMAL
        MOV ACC.7, C
        MOV P3, A             ;MOVE CARACTER DA CENTENA PARA DISPLAY COM PONTO
        NOP 
        NOP
        SETB DISPC            ;LIGA DISPLAY DA CENTENA
        LCALL DELAY1MS        ;DELAY
        CLR DISPC             ;DESLIGA DISPLAY DA CENTENA
        NOP
        NOP

        MOV P3, MILHAR7SEG    ;MOVE CARACTER DO MILHAR PARA DISPLAY
        NOP
        NOP
        SETB DISPM            ;LIGA DISPLAY DO MILHAR
        LCALL DELAY1MS        ;DELAY
        CLR DISPM             ;DESLIGA DISPLAY DO MILHAR
        NOP
        NOP

        DJNZ R3, LOOP_DISPLAY_CONT2    ;DECREMENTA E VERIFICA SE TERMINOU OS 50 LOOPS

        RET


;  ---------------------------------------------------------

;CONVERTE NUMERO NO CARACTER CORRESPONDENTE 
CONVERTE:
        MOV A, UNIDADE
        LCALL COMPARA
        MOV UNIDADE7SEG, A

        MOV A, DEZENA
        LCALL COMPARA
        MOV DEZENA7SEG, A

        MOV A, CENTENA
        LCALL COMPARA
        MOV CENTENA7SEG, A
        
        MOV A, MILHAR
        LCALL COMPARA
        MOV MILHAR7SEG, A

        RET



;COMPARA E CARREGA ACC COM VALOR DO CARACTER 7 SEGMENTO
;CORRESPONDENTE AO ALGARISMO DECIMAL 
COMPARA:
       CJNE A, #0D, COMPARA1
       MOV A, #11000000B    ;CARACTER 0
       RET
COMPARA1:
       CJNE A, #1D, COMPARA2
       MOV A, #11111001B    ;CARACTER 1
       RET
COMPARA2:
       CJNE A, #2D, COMPARA3
       MOV A, #10100100B    ;CARACTER 2
       RET
COMPARA3:
       CJNE A, #3D, COMPARA4
       MOV A, #10110000B    ;CARACTER 3
       RET
COMPARA4:
       CJNE A, #4D, COMPARA5
       MOV A, #10011001B    ;CARACTER 4
       RET
COMPARA5:
       CJNE A, #5D, COMPARA6
       MOV A, #10010010B    ;CARACTER 5
       RET
COMPARA6:
       CJNE A, #6D, COMPARA7
       MOV A, #10000010B    ;CARACTER 6
       RET
COMPARA7:
       CJNE A, #7D, COMPARA8
       MOV A, #11111000B    ;CARACTER 7
       RET
COMPARA8:
       CJNE A, #8D, COMPARA9
       MOV A, #10000000B    ;CARACTER 8
       RET
COMPARA9:
       CJNE A, #9D, COMPARA_ERRO
       MOV A, #10010000B    ;CARACTER 9
       RET
COMPARA_ERRO:                ;ERRO - NAO E ALGARISMO DECIMAL 0 A 9
       MOV A, #0000000B      ;ACENDE TODOS SEGMENTOS
       RET





;SUBROTINA DE DELAY
DELAY500MS:
        MOV R0, #50D                        ;50 X 10 MS = 500 MS
DELAY10MS:                                  
        MOV TL0, #LOW (65535-TEMPO10MS)     ;CARREGA VALOR PARA TIMER 0
        MOV TH0, #HIGH(65535-TEMPO10MS)
        SETB TR0                           ;LIGA TIMER 0
        JNB TF0, $                         ;AGUARDA FIM DA CONTAGEM
        CLR TR0                            ;LIMPA FLAG
        CLR TF0
        DJNZ R0, DELAY10MS                 ;DECREMENTA E VERIFICA SE TERMINOU OS 50 LOOPS
        RET



;DELAY UNICO 1 MS
DELAY1MS:                                  
        MOV TL0, #LOW (65535-TEMPO1MS)   ;CARREGA VALOR PARA TIMER 0
        MOV TH0, #HIGH(65535-TEMPO1MS)
        SETB TR0                         ;LIGA TIMER 0
        JNB TF0, $                       ;AGUARDA FIM DA CONTAGEM
        CLR TR0                          ;LIMPA FLAG
        CLR TF0
        RET


DELAY20CICLO:
        MOV R4, #20
        DJNZ R4, $                        ;DELAY 
        RET


; ############# SUBROTINA DE TRATAMENTO DE INTERRUPCAO DO TIMER 1 ###########
INT_TIMER1:
        PUSH PSW                 ;ARMAZENA VALORES DOS REGISTROS PRINCIPAIS
        PUSH ACC

        MOV A, CONTADOR_L        ;MOVE BYTE LSB DO CONTADOR PARA ACC
        ADD A, #1D               ;INCREMENTA UMA UNIDADE
        JNC    COMPARA_CONT_L    ;DESVIA SE CARRY = 0 - VERIFICA OVERFLOW
        CLR A                    ;ZERA BYTE LSB SE MAIOR QUE 255
        INC CONTADOR_H           ;INCREMENTA CONTADOR H

; COMPARA SE COMPLETOU MEIO SEGUNDO = 0800H OU 1 SEGUNDO = 1000H
COMPARA_CONT_L:
        MOV CONTADOR_L, A        ;RETORNA VALOR DE ACC PARA CONTADOR

        CJNE A, #00D , INT_FIM   ; SE NAO COMPLETOU TEMPO SAI DA INTERRUPCAO

COMPARA_CONT_H:        
        MOV A, CONTADOR_H         ; MOVE BYTE MSB DO CONTADOR PARA ACC
        CJNE A, #08H , COMPARA_1SEG  ; SE NAO COMPLETOU MEIO SEGUNDO DESVIA
        CPL    PONTO                ; COMPLETOU MEIO SEGUNDO INVERTE 2 PONTOS
COMPARA_1SEG:
         CJNE A, #10H , INT_FIM  ; SE NAO COMPLETOU 1 SEGUNDO SAI DA INTERRUPCAO

        MOV CONTADOR_H, #0       ;LIMPA CONTADOR
INCREMENTA_1SEG:
        INC SEGUNDO              ;INCREMENTA SEGUNDO
        CPL PONTO                ; COMPLETOU MEIO SEGUNDO INVERTE 2 PONTOS
        MOV A, SEGUNDO
        CJNE A, #60D, INCREMENTO_FIM  ;VERIFICA SE COMPLETOU 1 MINUTO

INC_UNI_MINUTO:
        MOV SEGUNDO, #0
        INC UNIDADE
        MOV A, UNIDADE
        CJNE A, #10D, INCREMENTO_FIM
INC_DEZ_MINUTO:
        MOV UNIDADE, #0
        INC DEZENA
        MOV A, DEZENA
        CJNE A, #06D, INCREMENTO_FIM
INC_UNI_HORA:
        MOV DEZENA, #0
        INC CENTENA
        MOV A, CENTENA
        CJNE A, #04D, INC_DEZ_HORA            ;VERIFICA SE COMPLETOU 24 HORAS
        MOV A, MILHAR
        CJNE A, #02D, INCREMENTO_FIM
        MOV CENTENA, #0                       ;COMPLETOU 24 HORAS ZERA O RELOGIO
        MOV    MILHAR, #0
        AJMP INCREMENTO_FIM
INC_DEZ_HORA:
        CJNE A, #10D, INCREMENTO_FIM
        MOV CENTENA, #0
        INC MILHAR



INCREMENTO_FIM:        


INT_FIM:
        CLR TF1            ;LIMPA FLAG DE INTERRUPCAO DO TIMER 1

        POP ACC            ;RESTAURA VALORES DOS REGISTROS PRINCIPAIS
        POP PSW

        RETI               ;RETORNA DA INTERRUPCAO
        
        END    

terça-feira, 12 de maio de 2015

Como utilizar IIR Filter com o PIC32MX - DSP Library

Este artigo demonstra como elaborar um Filtro Digital IIR utilizando o microcontrolador PIC32MX 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 (lowpass);
- Passa-alta (highpass);
- Passa-banda (bandpass);
- Rejeita-banda (bandstop).

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

Filtros Digitais IIR podem utilizar modelagem dos filtros analógicos:
- filtro Butterworth
- filtro Tschebyscheff
- filtro Tschebyscheff inverso
- filtro Elliptic
- filtro Bessel


De um modo geral os Filtros Digitais IIR são representados pela Equação 1.


Equação 1 - Equação básica para um Filtro Digital.


 A Função de Transferência H (z) para um Filtro Digital é vista na Equação 2.

 Equação 2 - Função de Transferência H (z) de um Filtro Digital. Fonte: ORFANIDS.



Considerando a0 =1  e M=N=2 obtemos a Equação 3 para um FIltro Digital IIR de Segunda Ordem.

Equação 3 - Função de Transferência para um Filtro Digital de Segunda Ordem. Fonte: ORFANIDS.


A Equação 4 mostra a equação diferencial para o FIltro Digital IIR de Segunda Ordem ou Biquadrado.

Equação 4 - Equação Diferencial para o Filtro Digital de Segunda Ordem. Fonte: ORFANIDS.


A equação diferencial do IIR FIlter Biquad é representada pelo Fluxograma 1.

Fluxograma 1 - Realização do Filtro IIR Segunda Ordem na Forma Direta. Fonte: ORFANIDS


Cada seção biquadrada do FIltro IIR da DSP Library da Microchip para o PIC32MX opera segundo o Fluxograma 2 (Second  Order Direct Form II Structure Transposed).
Fluxograma 2 - Operação do FIltro IIR da DSP Library da Microchip. Fonte: Microchip.



Criando um Filtro Digital IIR


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



 Figura 1 - Passos para criar um Filtro Digital.

Este artigo mostra um exemplo para a construção de 4 tipos de Filtros Digitais IIR:
a) filtro passa-baixa de 1000 Hz;
b) filtro passa-alta de 2000 Hz;
c) filtro passa-banda de 1500 Hz à 2000 Hz;
d) filtro rejeita-banda de 2000 Hz à 2500 Hz.


Vamos a algumas considerações do nosso projeto.

Objetivo do circuito: 

Filtrar um sinal entre 0 e 3840 Hz. Aplicar os 4 tipos de filtros IIR para verificar a resposta de cada um deles.

Especificações: 

- o sinal de entrada é uma onda senoidal gerada pelo próprio microcontrolador através da equação trigonométrica  cos(a+b) = 2 cos a cos b - cos(a-b).

- de acordo com o Teorema de Nyquist, a frequência de amostragem do sinal de entrada será 7680 Hz.

- o microcontrolador escolhido é o PIC32MX, um microcontrolador de 32-bit da Microchip, que possui as funções para o Filtro Digital IIR disponíveis na DSP Library. Possui 80 MHz, 512 kB de memória Flash e 128 KB High Speed SRAM.

- será utilizado o dsPIC Filter Design Lite, um programa de Digital Filter Design, para definir os parâmetros do filtro digital IIR 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 PIC32. O programa consiste basicamente em declarar as variáveis, inicializar os ponteiros e variáveis, iniciar a estrutura do filtro com mips_iir16_setup  ( ) e executar o cálculo do filtro digital com mips_iir16 ( ).

- é aplicada a transformada Rápida de Fourier (FFT) ao sinal filtrado para verificar a resposta do filtro.

- para teste do nosso circuito o PIC32 varia a frequência do sinal gerado de 0 à 3840 Hz.

- o sinal senoidal gerado e filtrado assim como a FFT são mostrados em um display gráfico.


Digital Filter Design: definindo os parâmetros do Filtro Digital

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

Utilizaremos o Filtro Digital IIR Biquadrado (cascata de segunda ordem). Cada IIR FIlter Biquad possui 2 pólos e 2 zeros.

O FIltro Analógico utilizado foi o Butterworth de 4ª ordem. 2 filtros de segunda ordem (biquadrado) em cascata. No total teremos 4 pólos e 4 zeros.
 
Veja a seguir o Design elaborado para os 4 tipos de Filtros utilizando o dsPIC Filter Design Lite: lowpass, highpass, bandpass e bandstop.

 Lowpass IIR FIlter Design


 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-baixa.

No segundo passo utilizaremos o dsPIC FD Lite, o Digital Filter Design Software,  para modelar o nosso filtro digital com estes parâmetros e gerar os coeficientes para ser utilizado no programa do PIC32.

O método de realização do filtro, quantização, neste caso é "cascated second order sections". Estamos projetando um IIR FIlter Biquad, isto é, com 2 pólos e 2 zeros para cada seção do fiiltro.

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

Escolheremos o tipo de filtro: lowpass.

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




Figura 3 - Configurando os parâmetros para o filtro passa-baixa.

A Figura 4 mostra o resultado matemático para a modelagem do nosso filtro digital IIR passa-baixa de acordo com os parâmetros fornecidos.

Figura 4 - Resultado da Modelagem do Filtro Digital IIR lowpass.


 Highpass IIR FIlter Design

A Figura 5 traz graficamente a representação de cada parâmetro do filtro highpass.


Figura 5 - Especificação do FIltro Digital passa-alta.

Os parâmetros do filtro passa-alta são configurados conforme a Figura 6.
 
Figura 6 - Configurando os parâmetros para o filtro passa-alta.

 A Figura 7 mostra o resultado matemático para a modelagem do filtro digital IIR passa-alta.

Figura 7 - Resultado da Modelagem do Filtro Digital IIR highpass.


 Bandpass IIR FIlter Design

 A Figura 8 traz graficamente a representação de cada parâmetro do filtro bandpass.


Figura 8 - Especificação do FIltro Digital passa-banda.

Os parâmetros do filtro passa-banda são configurados conforme a Figura 9.
 
Figura 9 - Configurando os parâmetros para o filtro passa-banda.

 A Figura 10 mostra o resultado matemático para a modelagem do filtro digital IIR passa-banda.

Figura 10 - Resultado da Modelagem do Filtro Digital IIR bandpass.


 Bandstop IIR FIlter Design

 A Figura 11 traz graficamente a representação de cada parâmetro do filtro bandstop.

Figura 11 - Especificação do FIltro Digital rejeita-banda.

Os parâmetros do filtro rejeita-banda são configurados conforme a Figura 12.


Figura 12 - Configurando os parâmetros para o filtro rejeita-banda.

 A Figura 13 mostra o resultado matemático para a modelagem do filtro digital IIR rejeita-banda.

Figura 13 - Resultado da Modelagem do Filtro Digital IIR bandstop.


Gerando os coeficientes para o Filtro IIR
 
Clicando no botão "Save C File"  o software (dsPIC FD Lite - Digital Filter Design) irá gerar um código fonte padrão na linguagem C para o filtro digital IIR construído, que pode ser visto no Código 1. Com ele você pode construir seu filtro DIgital IIR em outro compilador C para computador ou outro modelo de microcontrolador.


/***************************************************************************
****************************************************************************
*   File: E:\microchip_solutions_v2013-06-15\MyProjects\IIR FIlter\Digital Filter Design\Lowpass\lowpass1000Hz.c
*   Created by dsPIC FD Lite Version 1.0.0 Build 1 at 21:20:05 May 04 2015
*   C Code Generator - Version 4.0  
****************************************************************************
*  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 BiquadSections IIR_lowpass1000Hz;
*
*  init_biquad_float (&IIR_lowpass1000Hz);  // initialize filter structure 
*
*  IIR_lowpass1000Hz.filter ( x, y, n, &IIR_lowpass1000Hz);  // x is an array of input samples
*                                            // y is an array of output samples
*                                            // n is number of samples to process
*                                            // &IIR_lowpass1000Hz 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'  */

float lowpass1000Hz_num[  6] = {
   9.454345703125e-002F,  /* b[  1, 0] */
   1.891174316406e-001F,  /* b[  1, 1] */
   9.454345703125e-002F,  /* b[  1, 2] */
   6.185913085938e-002F,  /* b[  2, 0] */
   1.237487792969e-001F,  /* b[  2, 1] */
   6.185913085938e-002F}; /* b[  2, 2] */

float lowpass1000Hz_den[  6] = {
   1.000000000000e+000F,  /* a[  1, 0] */
  -8.160400390625e-001F,  /* a[  1, 1] */
   1.943054199219e-001F,  /* a[  1, 2] */
   5.000000000000e-001F,  /* a[  2, 0] */
  -5.340270996094e-001F,  /* a[  2, 1] */
   2.815856933594e-001F}; /* a[  2, 2] */

float lowpass1000Hz_m1[2];
float lowpass1000Hz_m2[2];

float lowpass1000Hz_gain = 1.000000000000e+000F; /* initial gain for cascade realization */
                          /* also applies to parallel realization */
float lowpass1000Hz_pars = 1.000000000000e+000F; /* scale value for parallel sections */

BiquadSections IIR_lowpass1000Hz = {

     2,     /* number of sections                     */
     0,     /* realization method                     */
            /*   0  Cascaded second order sections    */
            /*   1  Parallel second order sections    */
     1,     /* quantization: 0 off, 1 on              */
     1,     /* quantization type                      */
            /*   0  Floating point                    */
            /*   1  Fixed point fractional            */
     0,     /* realization type for cascaded sections only              */
            /*   0  Fixed point    - Transposed Canonic biquad sections */
            /*   1  Fixed point    - Canonic biquad sections            */
            /*   2  Floating point - 4 multiplies                       */
            /*   3  Floating point - 5 multiplies                       */
            /*   4  Floating point - recursive normal                   */
     0,     /* realization type for parallel sections only              */
            /*   0  Fixed point    - Transposed Canonic biquad sections */
            /*   1  Floating point - Transposed Canonic biquad sections */
   &lowpass1000Hz_gain,    /* pointer to gain for cascade/parallel realizations */
   &lowpass1000Hz_pars,    /* pointer to scale value for parallel sections      */
   lowpass1000Hz_num,      /* pointer to numerator coefficients                 */
   lowpass1000Hz_den,      /* pointer to denominator coefficients               */
   lowpass1000Hz_m1,       /* pointer to delay line 1                           */
   lowpass1000Hz_m2,       /* pointer to delay line 2                           */
   cas_blkfloat_fm1}; /* ptr to filter routine */

/* call the following function first and normally only once */
/* init_biquad_float (&IIR_lowpass1000Hz)  */
/*   where &IIR_lowpass1000Hz is a pointer to the BiquadSections */
/*   structure defining the filter */


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

/*   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 */
/*         &IIR_lowpass1000Hz 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    float 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_biquad_float (&IIR_lowpass1000Hz);


  do {

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

      /* filter samples */ 

      if (in_count == 0) break;

      IIR_lowpass1000Hz.filter( x, y, in_count, &IIR_lowpass1000Hz);

      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 

Código 1 - Código C gerado com coeficientes para o Filtro IIR Lowpass.

O que nos interessa neste código são os valores dos coeficientes A1, A2, B1 e B2, que iremos utilizar no programa do PIC32MX. "A" e "B" são os pólos e zeros do Filtro.

Os valores dos coeficientes estão identificados em formato de matriz:
Coeficiente [ número da seção Biquadrada , número do coeficiente ]

Exemplo:
A [1,1] = coeficiente A1 da primeira seção biquadrada.
A [1,2] = coeficiente A2 da primeira seção biquadrada.
A [2,1] = coeficiente A1 da segunda seção biquadrada.
A [2,2] = coeficiente A2 da segunda seção biquadrada.

B [1,1] = coeficiente B1 da primeira seção biquadrada.
B [1,2] = coeficiente B2 da primeira seção biquadrada.
B [2,1] = coeficiente B1 da segunda seção biquadrada.
B [2,2] = coeficiente B2 da segunda seção biquadrada.

O IIR FIlter Biquad possui 2 pólos e 2 zeros para cada seção (seções de segunda ordem).

Para o Filtro IIR, A0 não é utilizado na função mips_iir16 ( ) da biblioteca DSP Library com o PIC32MX.

B0 não é necessário ser informado na função mips_iir16 ( ) da biblioteca DSP Library para o PIC32, pois no IIR FIlter Biquad, B0 = B2.

Por isto, no programa do PIC32MX, iremos utilizar somente os coeficientes A1, A2, B1 e B2 de cada seção.

Estamos modelando um Filtro Analógico Butterworth de 4ª ordem. Temos então 4 pólos e 4 zeros.

No menu Codegen / Microchip / dsPIC30 iremos gerar o código em Assembly com os coeficientes. Este código com os coeficientes para o filtro IIR lowpass é visto no Código 2.


; ..............................................................................
;    File   lowpass1000Hz.s
; ..............................................................................

        .equ lowpass1000HzNumSections, 2

; ..............................................................................
;
; Allocate and initialize filter coefficients
;
; These coefficients have been designed for use in the Transpose filter only

        .section .xdata

lowpass1000HzCoefs:
.hword    0x060D    ; b( 1,0)/2
.hword    0x0C1A    ; b( 1,1)/2
.hword    0x343A    ; a( 1,1)/2
.hword    0x060D    ; b( 1,2)/2
.hword    0xF391    ; a( 1,2)/2
.hword    0x07EB    ; b( 2,0)/2
.hword    0x0FD7    ; b( 2,1)/2
.hword    0x445B    ; a( 2,1)/2
.hword    0x07EB    ; b( 2,2)/2
.hword    0xDBF5    ; a( 2,2)/2

; ..............................................................................
; Allocate states buffers in (uninitialized) Y data space

        .section .ybss,  "b"

lowpass1000HzStates1:
        .space lowpass1000HzNumSections*2

lowpass1000HzStates2:
        .space lowpass1000HzNumSections*2

; ..............................................................................
; Allocate and intialize filter structure

        .section .data
        .global _lowpass1000HzFilter

_lowpass1000HzFilter:
.hword lowpass1000HzNumSections-1
.hword lowpass1000HzCoefs
.hword 0xFF00
.hword lowpass1000HzStates1
.hword lowpass1000HzStates2
.hword 0x0000

; ..............................................................................
; Sample assembly language calling program
;  The following declarations can be cut and pasted as needed into a program
;        .extern    _IIRTransposeFilterInit
;        .extern    _BlockIIRTransposeFilter
;        .extern    _lowpass1000HzFilter
;
;        .section    .bss
;
;     The input and output buffers can be made any desired size
;       the value 40 is just an example - however, one must ensure
;       that the output buffer is at least as long as the number of samples
;       to be filtered (parameter 4)
;input:        .space    40
;output:    .space    40
;        .text
;
;
;  This code can be copied and pasted as needed into a program
;
;
; Set up pointers to access input samples, filter taps, delay line and
; output samples.
;        mov    #_lowpass1000HzFilter, W0    ; Initalize W0 to filter structure
;        call    _IIRTransposeFilterInit    ; call this function once
;
; The next 4 instructions are required prior to each subroutine call
; to _BlockIIRTransposeFilter
;        mov    #_lowpass1000HzFilter, W0    ; Initalize W0 to filter structure
;        mov    #input, W1    ; Initalize W1 to input buffer 
;        mov    #output, W2    ; Initalize W2 to output buffer
;        mov    #20, W3    ; Initialize W3 with number of required output samples
;        call    _BlockIIRTransposeFilter    ; call as many times as needed


                           Código 2 - Código Assembly gerado com coeficientes para o Filtro IIR Lowpass.

A vantagem de utilizar os coeficientes do código em Assembly é o fato de já estar no formato fracionário. O programa do PIC32 necessita dos coeficientes no formato int16, então basta somente copiá-los para o nosso projeto. O código em C gera os coeficientes em float.

Este procedimento deve ser repetido para os 4 tipos de filtro IIR: lowpass, highpass, bandpass e bandstop. Os coeficientes A1, A2, B1 e B2 são diferentes para cada design  elaborado.


O Hardware

Neste projeto utilizaremos:
a) PIC32MX USB Starter Kit II;
b) I/O Expansion Board;
c) GFXv3 Graphic Display.

O PIC32 irá gerar um sinal senoidal de frequência variável, aplicar o filtro digital IIR, calcular a Transformada Rápida de Fourier, a fim de verificar a resposta de cada tipo de filtro e comparar com os dados teóricos obtidos com o dsPIC FD Lite e mostrar no display os respectivos gráficos.



O código fonte do PIC32

 No terceiro passo elaboraremos o programa do PIC no compilador MPLAB C32 da Microchip com o ambiente de desenvolvimento MPLAB X. O Programa foi desenvolvido em linguagem C.

Incluímos em nosso projeto a biblioteca "dsplib_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.

O Código 3 mostra a inclusão da biblioteca e a declaração dos vetores do Filtro Digital IIR.




#include "dsplib_dsp.h"   // library to FFT and IIR FIlter functions


// vetores para iir filter
#define B  2    //2 Biquad Sections
biquad16 bq[B];
int16 coeffs[4*B];
int16 delayline[2*B];
int16 indata, outdata;
int16 sinal_Filtrado [N*2];


Código 3 - Incluindo a biblioteca DSP e declarando os vetores.

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 A1, A2, B1 e B2 foram retirados do código Assembly gerado pelo dsPIC Filter Design Lite.

A inicialização das variáveis e coeficientes são vistos no Código 4.

Os coeficientes são carregados para A1, A2, B1 e B2 de acordo com o tipo de filtro selecionado: lowpass, highpass, bandpass ou bandstop.




int main(void)
{
   ...


    // inicializa componentes de hardware e periféricos
    InitializeBoard();

    while(1)
    {

        // carrega coeficientes de acordo com o filtro selecionado
        iir_coefficients_setup ();

    ...
    ...
    ...
    }
}

void iir_coefficients_setup ()
{
    int i;
        //inicializa com zero delayline do iir filter
        for (i = 0; i < 2*B; i++)
            delayline[i] = 0;
   
    
        /* altera coeficientes do filtro IIR conforme a selecao */
        // load coefficients into bq here
        switch  (filterType) {
            case FILTER_OFF:
                
                break;
                
            case LOWPASS:
                bq [0].a1 = 0x343A; //a( 1,1)/2
                bq [0].a2 = 0xF391; //a( 1,2)/2
                bq [0].b1 = 0x0C1A; //b( 1,1)/2
                bq [0].b2 = 0x060D; //b( 1,2)/2

                bq [1].a1 = 0x445B; //a( 2,1)/2
                bq [1].a2 = 0xDBF5; //a( 2,2)/2
                bq [1].b1 = 0x0FD7; //b( 2,1)/2
                bq [1].b2 = 0x07EB; //b( 2,2)/2
                break;
                
            case HIGHPASS:
                bq [0].a1 = 0xFBAF; //a( 1,1)/2
                bq [0].a2 = 0xFD67; //a( 1,2)/2
                bq [0].b1 = 0xE0DC; //b( 1,1)/2
                bq [0].b2 = 0x0F92; //b( 1,2)/2

                bq [1].a1 = 0xFA00; //a( 2,1)/2
                bq [1].a2 = 0xE360; //a( 2,2)/2
                bq [1].b1 = 0xD4B0; //b( 2,1)/2
                bq [1].b2 = 0x15A7; //b( 2,2)/2
                break;
             case BANDPASS:
                bq [0].a1 = 0xFF3C; //a( 1,1)/2
                bq [0].a2 = 0xD061; //a( 1,2)/2
                bq [0].b1 = 0x0000; //b( 1,1)/2
                bq [0].b2 = 0xF7D0; //b( 1,2)/2

                bq [1].a1 = 0x1FDD; //a( 2,1)/2
                bq [1].a2 = 0xCFC9; //a( 2,2)/2
                bq [1].b1 = 0x0000; //b( 2,1)/2
                bq [1].b2 = 0xEFDC; //b( 2,2)/2
                break;
                
            case BANDSTOP:
                bq [0].a1 = 0x91BB; //a( 1,1)/2
                bq [0].a2 = 0xCF87; //a( 1,2)/2
                bq [0].b1 = 0xFFD1; //b( 1,1)/2
                bq [0].b2 = 0x0102; //b( 1,2)/2

                bq [1].a1 = 0x7135; //a( 2,1)/2
                bq [1].a2 = 0xCD3B; //a( 2,2)/2
                bq [1].b1 = 0xECE9; //b( 2,1)/2
                bq [1].b2 = 0x6872; //b( 2,2)/2
                break;  
                
            default :
                break;              
       }               


        mips_iir16_setup(coeffs, bq, B); 
    
}

Còdigo 4 - Carregando os coeficientes e inicializando a estrutura do Filtro IIR.

Primeiro deve-se iniciar com zero o vetor delayline [ ].

Depois carregam-se os valores dos coeficientesem bq [ ], de acordo com o tipo do filtro IIR selecionado.

Agora então podemos chamar a função mips_iir16_setup ( ) da biblioteca DSP do PIC32MX que se encarrega de inicializar a estrutura do filtro IIR.

O Código 5 mostra a aplicação do filtro propriamente dito em uma amostra do sinal de entrada.



/*  calculate iir filter biquad */
void iir_filter ()
{
    int i;

    for (i=0;i<N*2;i++)
    {
       // get input data value into indata
        indata = sine_wave_input[i] ;

        //calculate iir filter biquad
        outdata = mips_iir16(indata, coeffs, delayline, B, 1);

        sinal_Filtrado [i] = outdata;
    } //end for
}

Código 5 - Função que aplica o FIltro IIR ao sinal de entrada.

A função responsável pelo cálculo do Filtro IIR Biquad é a mips_iir16 ( ), e pertence à DSP Library da Microchip para o PIC32MX.

Nosso programa executa o Código 6 em um loop infinito: gera uma onda senoidal de frequência variável; aplica o filtro IIR ao sinal gerado; calcula a FFT ao sinal Filtrado e atualiza o display com os gráficos.


            sineWaveGeneration (signal_freq, 7680.0 );   //gera sinal senoidal
            iir_filter ();              //aplica o filtro iir ao sinal gerado
            moveVectorInputFFT ();      //move sinal gerado para entrada do calculo da FFT
            fftCalculate ();            //calcula a FFT do sinal gerado
            updateDisplay ();           //atualiza a tela


Código 6 - Loop do programa no código main.

O resultado final do nosso projeto é visto na Figura 14. Interligando um display gráfico QVGA 320x240 vemos o sinal gerado sendo aplicado ao Filtro Digital IIR.

Figura 14 - Sinais sendo mostrados no display gráfico.


A Figura 15 mostra a resposta na prática para o nosso filtro passa-baixa de 1000 Hz.

Figura 15 - Resposta do IIR FIlter lowpass.

A Figura 16 mostra a resposta na prática para o nosso filtro passa-alta de 2000 Hz.

Figura 16 - Resposta do IIR FIlter highpass.

A Figura 17 mostra a resposta na prática para o nosso filtro passa-banda de 1500 Hz à 2000 Hz.

Figura 17 - Resposta do IIR FIlter bandpass.

A Figura 18 mostra a resposta na prática para o nosso filtro rejeita-banda de 2000 Hz à 2500 Hz

Figura 18 - Resposta do IIR FIlter bandstop.

Estas curvas podem ser comparadas com aquelas do início deste post, geradas pelo dsPIC FD Lite, a fim de verificar a performance do Filtro Digital IIR da DSP Library com o PIC32MX!

Lembrando que a frequência de amostragem é 7680 Hz e o sinal gerado varia de 0 à 3840 Hz, respeitrando o Teorema de Nyquist.

O Vídeo 1 mostra o projeto do Filtro Digital IIR elaborado no MPLAB X  e rodando com o PIC32MX.

Vídeo 1 - IIR FIlter com PIC32MX.


Os cálculos da Fast Fourier Transform (FFT) para o PIC32MX você encontra aqui.




Referências:

ORFANIDS, Sophocles J. Introduction to Signal Processing. Pearson Education/Prentice Hall/Sophocles J. Orfanidis.2009. Disponível em: http://www.ece.rutgers.edu/~orfanidi/intro2sp

PALACHERLA, Amar. Implementing IIR Digital Filters. Application Note AN540. Microchip Technology Inc.1997.

RAMU, B. K. Anantha. Implementing FIR and IIR Digital Filters using PIC18 microcontrollers. Application Note AN852. Microchip Technology Designs. India. 2002.

SKANDA, Vinaya. Sine Wave Generator Using Numerically Controller PIC. Application Note AN1523A. Microchip Technology Inc. 2013.

HAYKIN, Simon S.; VAN VEEN, Barry. Sinais e sistemas. Porto Alegre: Bookman, 2001. 668p.

MANCHESTER. Z-transforms & IIR-type digital filters.  University of Manchester. School of Computer Science. Digital Media Processing. Section 5. 2009. 19p. Disponível em: www.cs.man.ac.uk/~barry/mydocs/COMP30291/Notes/DMP09-5.doc

MICROCHIP.  Using the Fast Fourier Transform (FFT) for Frequency Detection. Code Examples CE018. Microchip Technology Inc. Disponível em: http://ww1.microchip.com/downloads/en/DeviceDoc/CE018_FFT_DSPlib_101007.zip

MICROCHIP. Software Libraries. Disponível em: http://www.microchip.com/SoftwareLib.aspx

MICROCHIP. PIC32 DSP Library. Disponível em: http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2680&dDocName=en552827



Anexo A

Código básico para aplicar o cálculo de 1 tipo de Filtro Digital IIR.



#include "dsplib_dsp.h"   // library to FFT and IIR FIlter functions


// vetores para iir filter
#define B  2    //2 Biquad Sections
biquad16 bq[B];
int16 coeffs[4*B];
int16 delayline[2*B];
int16 indata, outdata;
int16 sinal_Filtrado [N*2];



int main(void)
{
   ...


    // inicializa componentes de hardware e periféricos
    InitializeBoard();

    
    // carrega coeficientes de acordo com o filtro selecionado
    iir_coefficients_setup ();


    while(1)
        {

        iir_filter ();    //aplica o filtro iir ao sinal gerado

        ...
        ...
        ...
        }
}

void iir_coefficients_setup ()
{
    int i;
        //inicializa com zero delayline do iir filter
        for (i = 0; i < 2*B; i++)
            delayline[i] = 0;
   
    
        // load coefficients into bq here
        
                bq [0].a1 = 0x343A; //a( 1,1)/2
                bq [0].a2 = 0xF391; //a( 1,2)/2
                bq [0].b1 = 0x0C1A; //b( 1,1)/2
                bq [0].b2 = 0x060D; //b( 1,2)/2

                bq [1].a1 = 0x445B; //a( 2,1)/2
                bq [1].a2 = 0xDBF5; //a( 2,2)/2
                bq [1].b1 = 0x0FD7; //b( 2,1)/2
                bq [1].b2 = 0x07EB; //b( 2,2)/2
 
        mips_iir16_setup(coeffs, bq, B); 
    
}



/*  calculate iir filter biquad */
void iir_filter ()
{
    int i;

    for (i=0;i<N*2;i++)
    {
       // get input data value into indata
        indata = sine_wave_input[i] ;

        //calculate iir filter biquad
        outdata = mips_iir16 (indata, coeffs, delayline, B, 1);

        sinal_Filtrado [i] = outdata;
    } //end for
}