Interrupções e E/S no ATMEL AVR ATmega88


Atualizado em: 13/10/2009  -   Prof. Célio Guimarães

A família Atmel AVR possui interrupções vetoradas e com prioridades (p.12-14 do Datasheet). São 26 vetores de interrupção no modelo ATmega 88 (p. 56 do Datasheet) localizados nos endereços $0 a $19 da memória de programa (flash). Cada vetor de interrupção está associado a um tipo específico de interrupção: o vetor 1 (posição $0) está associado à interrupção de RESET, os vetores 2 e 3 (posições $1 e $2) às interrupções externas INT0 e INT1 geradas nos pinos 4 e 5 da CPU, respectivamente; os vetores 15 a 17 estão associados ao "Timer 0", o vetor 23 (posição $16) à interrupção de fim de escrita da EEPROM, etc. Se solicitadas concorrentemente, as interrupções de número menor têm prioridade sobre as de número maior. Normalmente no vetor de interrupção existe uma instrução de salto para a rotina de interrupção propriamente dita.

O mecanismo de interrupção(*) empilha o endereço da próxima instrução onde ocorreu a interrupção e desabilita globalmente interrupções desligando o bit 7 do registrador de estado (desliga também o bit de "pedido de interrupção -flag- no registrador apropriado conforme veremos). Cabe à rotina de interrupção salvar o registrador de estado (SREG) e eventuais registradores que venha utilizar (**). Uma rotina de interrupção volta ao programa interrompido através da instrução RETI que desempilha e coloca no PC o endereço da próxima instrução a ser executada e liga o bit 7 no SREG (habilitando globalmente interrupções). Nenhuma interrupção é aceita antes que esta instrução (a próxima) seja executada.

As 64 posições de memória RAM logo após os 32 registradores de propósito geral são os registradores de E/S (p. 334 a 337 do Datasheet): eles constituem o espaço de endereçamento de E/S, acessado através das instruções in e out (cada registrador de E/S possui um endereço nesse espaço ("porta") e um mnemônico para o mesmo definido no arquivo de configuração .inc). Bits reservados nesses registradores também possuem mnemônicos e podem ser individualmente acessados através das instruções sbi e cbi (que tomam como operando apenas uma das 1ªs 32 portas).

Interrupções são habilitadas(desabilitadas) no AVR em dois níveis: globalmente (isto é, todas as interrupções) ligando(desligando) o bit 7 (bit I) do SREG (via instruções SEI e CLI) e para cada tipo de interrupção através de um bit específico num dos registradores de E/S chamados de "máscara de interrupções". Exemplo: as interrupções externas INT0 e INT1, geradas, respectivamente, por um sinal nos pinos 4 e 5 da CPU, só serão habilitadas se os bits 0 ou 1 (respectivamente) do registrador EIMSK (External Interrupt Mask Register) estiverem em 1 e além disso se interrupções estiverem globalmente habilitadas pelo bit 7 em 1 do SREG. Além do "bit de máscara" cada tipo de interrupção possui um bit para registrar o "pedido da interrupção" gerado pelo evento causador. Tais bits são denominados de "flags" e eles estão localizados em registradores de E/S com nomes do tipo "... Flag Register". Por exemplo, os bits 0 e 1 do EIFR (External Interrupts Flag Register) registram os pedidos de interrupção INT0 e INT1, respectivamente (se o pedido for do tipo subida, descida ou mudança de sinal, veja a seguir).

Pode ser necessário qualificar o tipo do sinal gerador de um pedido de interrupção: por exemplo, um (pedido de) interrupção externa no pino 4 (INT0) poderia ser gerado pela subida de um sinal nesse pino, pela descida do sinal, pela mudança do sinal ou por um sinal estável (nível baixo) nesse pino, de acordo com as características do sistema sendo controlado. Neste caso, isto será configurado através dos bits 0 a 3 do registrador de E/S EICRA External Interrupt Control Register A (Table 11-1 p. 84 do datasheet).

Qualquer um dos 23 pinos de E/S do Atmega88 também pode ser configurado para gerar uma interrupção quando há uma mudança de sinal ("toggle") no pino. Isto está denotado pela notação PCINT0..23 no diagrama da p. 2. Eles são organizados em 3 grupos pois apenas 3 vetores de interrupção (4 a 6) são reservados para este tipo de interrupção (p. 83 do datasheet). Os registradores PCMSK0, PCMSK1 e PCMSK2 (p. 336) são usados para configurar quais pinos usarão este tipo de interrupção e os bits 0 a 2 do registrador de controle PCICR são usados para habilitar as interrupções correspondentes PCIE0, PCIE1 e PCIE2; os bits 0 a 2 do registrador de flag PCIFR são usados para registrar os pedidos de interrupção de cada um dos grupos acima. Observe que se dois pinos do mesmo grupo forem configurados para este tipo de interrupção não será possivel distinguir qual interrupção foi gerada. Modelos mais simples do AVR como o ATiny2313 não possuem esse recurso.

Alguns periféricos mais complexos como os temporizadores e USART podem ter a eles associados vários registradores de E/S para registrar o estado ou controlar/configurar o seu funcionamento e inclusive gerar vários tipos de interrupções (pg 56 do Datasheet). Vamos ilustrar isso com o temporizador de 8 bits denominado "Timer/Counter 0". Esse temporizador usa um contador de 8 bits (registrador TCNT0) que é incrementado através de uma taxa derivada de uma divisão selecionável do relógio do sistema, denominada de "prescaler" (um fator de divisão dentre 1, 8, 64, 256 e 1024 é selecionado através dos bits 0, 1 e 2 do registrador TCCR0B Timer/Counter 0 Control Register B - table 12-9 p. 103). O Timer/Counter 0 pode gerar vários tipos de interrupção, selecionáveis através dos registradores de controle TCCR0A e TCCR0B: quando o contador atinge um deternminado valor (especificado num dos registradores de E/S OCR0A ou OCR0B) ou quando o contador TCNT0 passa de ff para 00 ("Timer/Counter0 Overflow"). Neste último caso o pedido de interrupção é registrado no bit 0 do registrador TIFR0 (Timer/Counter 0 Flag Register). Caso esta interrupção esteja habilitada pelo bit 0 do registrador TIMSK0 (Timer/Counter 0 Interrupt Mask register), e pelo bit I do SREG, a interrupção ocorre no final da execução da instrução corrente e a instrução no vetor 17 (posição $10) é executada (o mecanismo de interrupção também desliga o flag correspondente, bit 0 do TIFR0). Citamos acima vários registradores de controle/estado associados ao "Timer/Counter0" (p. 102-105 do datasheet): o contador TCNT0 que pode ser lido ou escrito via instrução in ou out, (o que permite dinamicamente controlar o tempo entre sucessivas interrupções), os registradores de controle TCCR0A e TTCR0B, o registrador de controle TIMSK0 ("máscara") e o registrador de estado TIFR0 (flag).

Assim como no 8086, ao retornar de uma rotina de interrupção (via instrução RETI) o hardware garante que a próxima instrução do programa interrompido será executada (antes que uma outra possível interrupção seja aceita).


(*)Mecanismo de interrupção é o conjunto de ações indivisíveis (isto é não interrompíveis) tomadas pelo hardware desde o instante em que a interrupção é aceita até o momento em que a 1ª instrução do vetor de interrupção é iniciada. No caso do AVR ele desliga o bit I do SREG, o bit do pedido - flag - correspondente, e empilha dois bytes contendo o endereço da próxima instrução onde ocorreu a interrupção; ele dura 4 ciclos no ATmega 88 (8 ciclos se a CPU estiver no modo sleep)

(**)Um esqueleto de uma rotina de interrupção que não altera SREG e registradores poderia ser assim:

    push r16        ; vamos usar r16 para salvar SREG na pilha
	in r16, SREG
	push r16        ; SREG salvo: nenhuma das instruções acima alterou SREG
	push rxx        ; salvo rxx para usar na rotina
	. . .           ; salva outros registradores, se preciso
	. . .           ; código da rotina de interrupção
	pop rxx         ; preparando para sair, pops devem ser dados na ordem inversa
	pop r16         ; r16 tem agora o valor original do SREG
	out SREG,r16    ; restauramos o SREG original
	pop r16         ; e o r16; agora o topo da pilha tem o endereço de retorno,
	reti            ; volta ao programa interrompido ligando o bit I em SREG

E/S Digitais

O modelo ATmega88 possui 23 pinos para E/S. São pinos multi-funcionais, podendo individualmente serem selecionados (programados) para entrada digital, saída digital, ou funções específicas relacionadas aos diversos recursos de E/S do microcontrolador (como as interrupções externas INT0 e INT1, já mencionadas, USART, conversor analógico-digital, etc). Vamos focar aqui no seu uso para E/S digital. Existem 3 conjuntos de pinos que podem ser usados independentemente para entrada ou saída digital denominados PORT B (8 pinos), PORT C (7 pinos) e PORT D (8 pinos). Para acada um desses conjuntos existem 3 registradores de E/S associados denominados PORTB (Port B Data Register), DDRB (Port B Data Direction Registe) e PINB (Port B Input Register) e os correspondentes PORTC, DDRC, PINC e PORTD, DDRD e PIND. O registrador DDRB permite configurar individualmente cada pino da "Porta B" para entrada (valor 0) ou saída (valor 1); no registrador de dados PORTB se pode ler a entrada ou especificar a saída no(s) pino(s) desejados(s); caso o pino(s) tenha(m) sido configurado(s) para entrada também permite ativar/desativar os resistores internos de pull-up (nos pinos configurados como entrada), escrevendo 1 nos bits correspondentes de PORTB. O valor inicial (após o RESET) dos registradores PORTX e DDRX (X= B, C ou D) é 0. O registrador PINX (X=B,C ou D) é usado apenas para entrada (leitura) mas caso se faça uma saída de valor 1 em um bit de PINX ela tem como efeito trocar o valor (toggle) do bit correspondente em PORTX (p. 66, 10.2.2 do datasheet). Quando configurado para saída um pino do ATmega88 pode emitir até 20 mA de corrente, suficiente para ligar um led comum.

Modos de sleep

A fim de economizar energia o AVR ATmega88 pode ficar "dormindo" em um de cinco possíveis modos quando a instrução sleep (previamente habilitada ligando o bit de controle SE, bit 0, do registador SMCR) é executada. Os modos de sleep permitem desativar seletivamente "módulos" da CPU que utilizam o relógio e por isto consomem mais energia. Isto pode ser feito ligando bits apropriados no registrador de E/S PRR (Power Reduction Register, p. 39-40 do datasheet). Vamos apresentar a seguir apenas os dois modos mais comuns, presentes também nos modelos mais simples do AVR (detalhes dos diversos modos de sleeep podem ser vistos no datasheet, p. 37-42): Em ambos os casos para que a instrução sleep tenha efeito o bit SE (sleep enable) do registrador SMCR deve ser ligado. Exemplo: para permitir a CPU entrar no modo sleep power down a seguinte seqüencia de instruções poderia ser executada:
 	  ldi r16, $5		 
	  out SMCR, r16      ; liga SE, habilita modo power down
	  sei                ; habilita globalmente interrupções
	  sleep              ; pára a CPU aguardando uma interrupção permitida pelo modo
	  ....               ; executa a partir daqui após retornar  da rotina de interrupção