CONFIGURANDO CORRETAMENTE OS REGISTRADORES DOS MICROCONTROLADORES AVR - parte 3

USO DOS REGISTRADORES MCUCR, GICR e GIFR – CONTROLE DA INTERRUPÇÃO EXTERNA



ANALISE EM “LOOP” X INTERRUPÇÃO

Em muitos casos, quando o desenvolvedor iniciante se depara com a necessidade de analisar uma chave ou mesmo a saída de um sensor qualquer, através de um microcontrolador, a primeira idéia é fazer tal analise através de um simples laço (loop). Para deixar este tipo de processo mais simples, vou dar um pequeno exemplo. Veja a figura abaixo.


No exemplo demonstrado na figura acima uma chave precisa ser analisada para que o programa possa tomar alguma atitude. A seguir passo um pequeno segmento de um código fonte que poderia ser utilizado para o exemplo dado na figura.


/*-----------------------------------------------------------
Subrotina principal
-----------------------------------------------------------*/
void main(void){

    /* Pull-ups para PORTB – Apenas PB0 ligado */
    PORTB = 0x01; /* b00000001 */

    /* Direção dos pinos de I/O para PORTB – PB0 = entrada */
    /* o restante de PORTB – saída */
    DDRB = 0xFE; /* b11111110 */

    /* Insere um nop (não operando) para sincronizar*/
    _NOP();   
    PORTB = 0x00; /*desliga pinos do PORTB */

    /* inicia processamento principal */
    while (1){
        if (PINB & 0x01)
            /* o botão não está pressionado */
            /* então desligue o pino 2 de PORTB */
            PORTB = PORTB & 0xFD; /* b11111101 */   
        else
            /* o botão está pressionado */
            /* então ligue o pino 2 de PORTB */
            PORTB = PORTB | 0x02; /* b00000010 */
    }

}


Se você observar o exemplo dado, irá reparar que o mesmo funciona adequadamente. Porém há um pequeno problema. E se o número de chaves/sensores for maior que um? E se o microcontrolador também tiver de cuidar de outros processos como o controle de um display (LCD ou 7 segmentos) ou mesmo uma outra saída qualquer também dependente dos recursos do microcontrolador como, por exemplo, uma válvula de segurança que tem que ser aberta imediatamente sempre que um sensor assim determinar? Este tipo de solução em laço seria a melhor forma de resolver o problema? Vamos analisar juntos os prós e contras deste tipo de solução.

A analise de entradas através de laços é viável apenas em casos onde o microcontrolador não tem que cuidar de outros eventos que demandem tempo ou ocupem o mesmo. Isso porque caso o microcontrolador fique “ocupado” com um outro processamento qualquer o mesmo não poderá analisar a entrada naquele instante e caso esta sofra uma modificação momentânea, o microcontrolador/programa/usuário não terá como “saber” que isto ocorreu. Para muitos desenvolvedores bastaria então usar um microcontrolador com maior velocidade de processamento para que as coisas funcionassem adequadamente. Sim, isso tem algo de verdade. Porém ainda há um problema. Quanto maior a velocidade de processamento, maior o consumo de energia. Este tipo de solução seria inadequada para projetos do tipo “portáteis” onde o consumo de energia tem alta prioridade ou ainda para projetos onde o custo/benefício tenha que ser o melhor possível. 

Sendo assim, se o seu projeto precisa apenas analisar o estado de uma chave para somente então fazer um determinado processamento e o mesmo não possui qualquer problema de alimentação (consumo de energia) ou ainda qualquer problema com "custos", este tipo de solução resolve bem o caso. Mas se a analise de uma ou mais entradas são importantes além de outros processamentos, a solução de leitura de entradas através de laços não é adequada. 

Neste caso, o uso de “interrupções externas” são mais indicadas. Um interrupção externa nada mais é que a detecção de um “evento externo” que permite interromper o processamento "normal" do microcontrolador sempre que uma entrada específica sofrer uma alteração. Isto garante que em qualquer ponto do programa o evento possa ser identificado e atendido. Você percebe então que se uma válvula de segurança tivesse que ter sua saída alterada por um microcontrolador, e este tivesse que fazer isso através da leitura de um sensor qualquer o melhor seria utilizar uma interrupção externa para tal, já que sempre que o estado da mesma sofresse uma alteração isto seria detectado e o programa poderia então ser desviado para o devido tratamento do evento, para somente então prosseguir de onde parou.


A INTERRUPÇÃO INT0 NO AVR

Antes de detalhar o uso dos registradores para controle da interrupção externa dos microcontroladores AVR é necessário compreender que este tipo de “sinal” não está disponível em todos os pinos de I/O do microcontrolador. Dependendo do modelo, é possível encontrar mais de uma interrupção externa no microcontrolador (INT0, INT1, INT2, etc), cada uma ligada a um pino de I/O diferente. Isso quer dizer que o recurso da interrupção externa é disponibilizada aos desenvolvedores de forma “compartilhada” com algum pino de I/O. Uma outra forma de explicar isso seria usando o jargão “função especial do pino de I/O”. Ou seja, o desenvolvedor pode usar o pino como uma I/O normal (entrada ou saída) ou ainda a “função especial” ligada ao mesmo.

Alem disso, a posição onde este recurso está ligado no microcontrolador varia de modelo para modelo. No ATMEGA8535, por exemplo, a INT0 compartilha o pino de I/O PD2. Já no modelo Attiny11 a INT0 está ligada ao pino PB0 e no ATMEGA128 ao pino PD0. Sendo assim, cabe ao desenvolvedor estudar o datasheet do microcontrolador que deseja usar, certificando-se da posição exata de cada recurso (função especial) que será utilizado.


OS REGISTRADORES MCUCR, GICR E GIFR

Para utilizar a interrupção externa 0 (INT0) nos microcontroladores AVR é necessário compreender o funcionamento de alguns registradores importantes no seu controle. São eles:


- Registrador MCUCR (Registro de controle do microcontrolador)
- Registrador GICR (Registro de controle de interrupção)

-
Registro GIFR (Registro/Flag para interrupção)


REGISTRADOR MCUCR

Na tabela abaixo, é dada a descrição do registro MCUCR – MCU Control Register, a posição de cada um dos bits de controle, o valor inicial dos mesmos durante o RESET do microcontrolador e a descrição de suas funções. Os bits 1 e 0 controlam a INT0, e sendo assim, apenas estes bits serão analisados, pois são de total interesse aqui. Os outros bits são irrelevantes neste momento.


            Tabela  – Registro MCUCR

Bit

7

6

5

4

3

2

1

0

SM2

SE

SM1

SM0

ISC11

ISC10

ISC01

ISC00

Leitura/

Escrita

R

R

R

R

R/W

R/W

R/W

R/W

Valor

Inicial

0

0

0

0

0

0

0

0


ISC01:00 – Interrupt Sense Control 1 and 0

Bits de leitura e escrita que permitem configurar como o sinal, aplicado ao pino INT0, irá gerar a interrupção. Esta configuração pode ser vista na tabela a seguir.


                        Tabela – Configuração dos bits ISC01 e ISC00

ISC01

ISC00

Descrição

0

0

Nível “0” gera a INT

0

1

Qualquer mudança no pino, gera a INT

1

0

Borda de decida gera INT

1

1

Borda de subida gera INT



REGISTRADOR GICR

A seguir, na tabela, é dada a descrição do registro GICR – General Interrupt Control Register, a posição de cada um dos bits de controle, o valor inicial dos mesmos durante o RESET do microcontrolador e a descrição de suas funções. Para o controle da interrupção externa INT0, apenas o sexto bit é importante, e sendo assim, apenas este será descrito aqui, ficando os demais para um outro momento.


            Tabela – Registro GICR

Bit

7

6

5

4

3

2

1

0

INT1

INT0

INT2

-

-

-

IVSEL

IVSE

Leitura/

Escrita

R/W

R/W

R/W

R

R

R

R/W

R/W

Valor

Inicial

0

0

0

0

0

0

0

0


INT0 – External Interrrupt Request 0 Enable

Bit de escrita e leitura que permite configurar de acordo com seu valor lógico (0 ou 1):

0 – desabilita interrupção através do pino INT0;
                1 – habilita interrupção através do pino INT0
.



REGISTRADOR GIFR

Na tabela a seguir, é dada a descrição do registro GIFR – General Interrupt Flag Register, a posição de cada um dos bits de controle, o valor inicial dos mesmos durante o RESET do microcontrolador e a descrição de suas funções. Para o controle da interrupção externa INT0, apenas o sexto bit é importante, e sendo assim, apenas este será descrito.


            Tabela – Registro GIFR

Bit

7

6

5

4

3

2

1

0

INTF1

INTF0

INTF2

-

-

-

-

-

Leitura/

Escrita

R/W

R/W

R/W

R

R

R

R

R

Valor

Inicial

0

0

0

0

0

0

0

0


INTF0 – External Interrrupt Flag 0

Bit de escrita e leitura que permite configurar de acordo com seu valor lógico (0 ou 1):

0 – nenhuma interrupção detectada no pino INT0;
                1 – interrupção detectada no pino INT0
.


UM PEQUENO EXEMPLO

Para ajudar ainda mais a compreensão de tudo o que foi dito na teoria nada melhor que um pequeno exercício para fixar a teoria. Porém antes de se dedicar ao teste aqui sugerido, é interessante que você tenha compreendido o uso dos registradores para o controle da interrupção externa INT0. Caso algo não ainda pareça um tanto “obscuro”, leia novamente tudo o que foi exposto acima até que fique “claro”.

A figura abaixo demonstra o circuito de testes necessário para este pequeno exemplo. Temos uma chave ligada ao pino INT0 (PD2) que tem ligação direta com o circuito interno responsável pela geração da interrupção do microcontrolador para eventos externos e um LED ligado ao pino PB0.

O código fonte fornecido mais a frente neste artigo foi preparado para o microcontrolador em questão. O compilador utilizado foi o GCC-AVR (Linux), sendo que seu compatível para Windows® é o WinAVR. O uso de um outro compilador qualquer requer as devidas adaptações e isso fica por sua conta.


A idéia é bem simples. Vamos preparar um programa que permitirá trocar o estado do LED sempre que a chave for pressiona, usando para isso a interrupção INT0 para detectar a alteração do estado da chave. Ao pressionarmos a chave o LED troca de estado, permanecendo assim até o próximo toque.

O circuito proposto é bem simples e minha sugestão é a sua montagem em uma matriz de contatos ou então em uma placa padrão, pois este servirá apenas para alguns testes. Os mais "moderninhos" podem utilizar um simulador, ao invéz de se permitir ter o "prazer incomensurável" de uma montagem real. 

O código fonte a ser utilizado em conjunto com o circuito apresentado pode ser visto a seguir.

//----------------------------------------------------------------------------
// Arquivos incluídos no módulo
//----------------------------------------------------------------------------
#define __AVR_ATmega8535__
#include </usr/avr/include/stdio.h>
#include </usr/avr/include/avr/io.h>
#include </usr/avr/include/avr/pgmspace.h>
#include </usr/avr/include/avr/interrupt.h>

//------------------------------------------------------------------------------
// Interrupção Externa INT0

//

// Entradas - nenhuma

// Saídas - altera estado do LED no pino PB0

//------------------------------------------------------------------------------

ISR(SIG_INTERRUPT0){

    PORTB = PORTB ^ 0x01; //XOR do valor contido na porta com “1”

}

//----------------------------------------------------------------------------
// Função main (principal)

//
// Entradas - nenhuma
// Saídas - nenhuma

//----------------------------------------------------------------------------

main(void){

    cli();                         //Desliga interrupção GLOBAL
    PORTB = 0;                     //Pull-ups para PORTB desligados
    DDRB = (1<<DDB0);              //Direção dos pinos de I/O para PORTB - PB0 = saída
    PORTD = 1;                     //Pull-up para PD0 ligado
    DDRD = 0xFE;                   //PD0 configurado como entrada. O resto é saída.
    _NOP();                        //não operando, não faz nada, apenas perde um ciclo

    MCUCR = (1<<ISC01);            //borda de descida gera a int no pino INT0
    GICR = (1<<INT0);              //habilita a int através do pino INT0 (PD2)

    PORTB = 0;                     //todos pinos de PORTB são zerados
    sei();                         //liga interrupção GLOBAL

    while (1){
        ;                          //o programa fica parado aqui, aguardando uma int externa (INT0)

    }

}


Para você que já leu as duas primeiras partes será bem fácil compreender o pedaço inicial deste programa onde são configurados os pinos de I/O, tanto para o PORTB quanto para o PORTD. Para aquele que chega agora sugero a leitura das partes anteriores Configuração e uso dos pinos de I/OConfiguração e uso do TIMER0 para uma melhor compreensão do programa demonstrado aqui e também para acompanhar os passos já dados em nossa “viagem” pelo mundo dos registradores dos microcontroladores AVR. 

No programa existem duas linhas na subrotina principal que configuram os registradores de controle MCUCR e GICR. Através do primeiro registrador é configurado de que maneira a alteração no pino de I/O PD2 irá gerar a interrupção (no exemplo a INT será gerada na borda de descida) e no segundo registrador habilitamos a geração da interrupção INT0.

Você que leu atentamente a teoria deve estar se perguntando: “E o bit INTF0 do registrador GIFR, não foi usado? Porque?”. A resposta para está pergunta é bem simples. Ele foi usado, mas de maneira indireta. A função ISR recebe como parâmetro o vector SIG_INTERRUPT0. Este vector está ligado ao bit INTF0 do registrador GIFR. Ele está definido no arquivo iom8535.h inserido através da diretriz __AVR_ATmega8535__ e ligada através do arquivo io.h. Assim não se faz necessário ler o estado do bit INTF0 através do registrador GIFR para saber se a interrupção foi ou não gerada. Isso acontece para a maioria dos microcontroladores AVR.

Você deve ter notado que o botão foi ligado diretamente ao microcontrolador sem um resistor de pull-up externo, isso porque o programa habilitou o resistor de pull-up interno. Assim, no pino de entrada tem-se sempre nível lógico alto. Quando se pressiona a chave o GND é conectado ao pino e assim temos a geração da interrupção na transição do nível lógico alto para baixo (borda de descida). Assim uma interupção externa INT0 é gerada e a função ISR(SIG_INTERRUPT0) é então chamada, trocando o estado do LED conectado ao pino PB0. Tudo bem simples não é?!

CONCLUSÃO

A cada nova parte desta pequena viagem pelo mundo dos registradores dos microcontroladores AVR, novas informações sobre os mesmos são acrescentadas. É extremamente recomendável que você que está interessado em acompanhar esta "viagem" estude as partes anteriores, os datasheets recomendados e um pouco da Linguagem de Programação C. Apenas com uma boa base teórica é que você poderá ter bons resultados práticos. 


Datasheets recomendados

ATMEGA8535 - http://www.atmel.com/dyn/resources/prod_documents/doc2502.pdf

ATMEGA16 - http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf



Este artigo foi publicado, com minha autorização, na revista Eletrônica Total nrº 125 de Julho/Agosto de 2007.



Copyright deste conteúdo reservado para Márcio José Soares e protegido pela Lei de Direitos Autorais LEI N° 9.610, de 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou parcial do conteúdo desta página em outros pontos da internet, livros ou outros tipos de publicações comerciais ou não, sem a prévia autorização por escrito do autor.