Interrupções e E/S no ATMEL AVR ATmega88 |
A família Atmel AVR possui interrupções vetoradas e com prioridades. 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 interruoçã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: quando o contador atinge um deternminado valor (especificado num dos registradores de E/S OCR0A ou OCR0B) ou quando passa de ff para 00 ("overflow"). Um pedido e interrupção é registrado no bit 1 do registrador TIFR0 (Timer/Counter 0 Flag Register). Caso a interrupção esteja habilitada pelo bit 1 do registrador TIMSK0 (Timer/Counter 0 Interrupt Mask register), e pelo bit I do SREG, a interrupção ocorre (na pasagem de ff para 0 ela é denominada de "Timer/Counter0 Overflow") e a instrução no vetor 17 (posição $10) é executada (o mecanismo de interrupção também desliga o flag correspondente, bit 1 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), o registrador de controle TCCR0B (que permite selecionar o valor do fator de divisão do relógio -prescaler), 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).
(**)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
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