Interrupções

Vimos que na condição de reset o sistema é iniciado a partir do endereço zero da memória de programa. Isso ocorre devido à localização dos vetores de interrupção. Especificamente, os vetores de interrupção ocupam posições fixas na memória de programa, e o reset é considerado uma "interrupção" estabelecida no endereço zero.

Interrupções e Subrotinas no Intel 8051 - Localização dos vetores de interrupção [1].

Alguns periféricos podem gerar interrupções e para isso alguns endereços são reservados:

  • IE0: 0x0003, Interrupção externa 0;

  • TF0: 0x000B, Interrupção por overflow do timer 0;

  • IE1: 0x0013, Interrupção externa 1;

  • TF1: 0x001B, Interrupção por overflow do timer 1;

  • RI e TI: 0x0023, Interrupção do canal serial.

Se a interrupção não é utilizada, seu endereço é considerado de propósito geral. Além disso, os vetores são espaçados em 8 bytes. Portanto, se a rotina de interrupção é curta, essa região de 8 bytes pode ser utilizada, já as rotinas longas podem utilizar instruções de jump.

Registradores

As interrupções são controladas pelos registradores Interrupt Enable (IE) e Interrupt Priority (IP). O registrador IE, ilustrado na Figura abaixo, pode ser endereçado bit a bit e está localizado no endereço A8H. Esse registrador tem como função controlar as interrupções que o sistema deve atender.

Registrador IE [1].
  • 7 - EA: Interrupções globais

  • 4 - ES: Interrupção do canal serial

  • 3 - ET1: Interrupção do temporizador/contador 1

  • 2 - EX1: Interrupção externa 1

  • 1 - ET0: Interrupção do temporizador/contador 0

  • 0 - EX0: Interrupção externa 0

O registrador IP, ilustrado na Figura abaixo, também pode ser endereçado bit a bit e está localizado no endereço B8H. Esse registrador tem como função determinar a prioridade de uma fonte de interrupção, podendo ser alta ou baixa.

Registrador IP [1].
  • 4 – PS: Prioridade da interrupção do canal serial

  • 3 – PT1: Prioridade da interrupção do temporizador/contador 1

  • 2 – PX1: Prioridade da interrupção externa 1

  • 1 – PT0: Prioridade da interrupção do temporizador/contador 0

  • 0 – PX0: Prioridade da interrupção externa 0

Para utilizar uma interrupção deve-se:

  1. Escrever a rotina de interrupção com início no endereço do vetor correspondente. Isso é feito a partir da diretiva ORG <endereço da interrupção>;

  2. O bit correspondente a interrupção desejada deve ser habilitado no registrador IE;

  3. O bit EA (Enable All) deve ser ativado no registrador IE.

Além desses dois registradores, é importante citar a função do Program Status Word (PSW). Esse registrador, ilustrado na Figura abaixo, contém as flags de estado da CPU, pode ser endereçado bit a bit e está localizado no endereço D0H.

Registrador PSW [1].
  • 7 - CY: Flag de Carry out

  • 6 - AC: Flag auxiliar de Carry out - operações de adição

  • 5 - F0: Flag de propóstio geral

  • 4 e 3 - RS1 e RS0: Seleção do banco de registradores

  • 2 - OV: Overflow de operações aritméticas

  • 1 - x : Flag definida pelo usuário

  • 0 - P: Flag de paridade – É definida pelo hardware (é igual a 1 quando o acumulador possui um número ímpar de bits em 1)

Cabe ressaltar que esse registrador determina qual banco de registradores está sendo utilizado. Isso implica na referência dos nomes dos registradores R0~R7.

Banco de Registradores [1].

O Sistema de Interrupção

A Figura 6 mostra o sistema de interrupção. Nessa figura é possível observar a função dos registradores Interrupt Enable (IE) e Interrupt Priority (IP). Mas o que acontece quando interrupções simultâneas são capturadas? Bom, nesse caso é necessário avaliar a prioridade da interrupção, e no caso de interrupções com a mesma prioridade a seguinte ordem de avaliação é estabelecida: primeiro IE0, em seguida TF0, IE1, TF1, RI e por ultimo, TI.

Sistema de Interrupções [1].

Quando a interrupção é disparada, uma operação LCALL é realizada com destino ao vetor de interrupção correspondente. Durante a execução de LCALL, somente o contador de programas (PC) é armazenado na pilha. Portanto, os registradores PSW, Acumulado e B não são gravados na pilha durante a chamada de uma rotina de interrupção.

O Ponteiro de Pilha (Stack Pointer)

O Stack Pointer é um registrador que possui uma função muito importante na CPU. Esse registrador, que é inicializado com o valor 07H durante a operação de Reset, tem como função gerenciar a chamada e o retorno de eventos de interrupção e chamada de subrotinas. O valor 07H indica que o registrador aponta para o endereço 07H, isto é, para o registrador R0 do segundo banco de registradores. Nesse sentido, o endereço armazenado em SP é chamado de topo da pilha. Para isso acontecer, duas instruções operam diretamente sobre esse registrador, são elas: PUSH e POP:

  • A operação PUSH faz com que um dado seja armazenado no endereço seguinte ao indicado pelo SP, isto é, no topo da pilha. Após essa operação o registrador SP é incrementado;

  • A operação POP recupera o valor armazenado no topo da pilha. Após essa operação o registrador SP é decrementado.

Sabendo dessas duas operações, e tendo em vista um evento de interrupção, como será que a CPU gerencia o fluxo de execução do programa no momento de uma interrupção? Foi dito que durante a interrupção o programa é desviado por meio LCALL para o endereço do vetor de interrupção. Nesse momento, o contador de programa, que indica a próxima instrução que será buscada na memória, é armazenado na pilha.

Quando a instrução RETI é executada, o contador de programa é restabelecido com os valores que estavam armazenados na pilha, isto é, o endereço da próxima instrução que seria executada antes de ocorrer a interrupção! Esse mesmo procedimento ocorre com as outras instruções de chamada de subrotina, contudo a instrução de retorno deve ser RET, pois RETI é restrito ao contexto das interrupções. É importante lembrar que a pilha não fica limitada a essas funções, e pode ser utilizada normalmente pelo programador.