Como usar Interrupções com Arduino

Interrupções com Arduino

Uma interrupção é um sinal enviado por um dispositivo de hardware que temporariamente interrompe a tarefa que a CPU está executando no momento, para que o dispositivo em si seja atendido. Logo após, o programa retoma seu processamento do ponto onde havia parado.

As interrupções são realizadas em nível de hardware, quando temos um evento externo sendo acionado, como por exemplo o pressionamento de um botão, e assim um sinal é enviado para o microcontrolador (ou microprocessador). Este processamento será executado imediatamente, interrompendo o que quer que a CPU esteja realizando no momento.

Neste artigo vamos apresentar e aprender a usar as interrupções externas em programação para Arduino

Rotinas de Interrupção em Arduino

Um programa normal para Arduino consiste em duas rotinas principais: setup() e loop().

Quando empregamos interrupções, criamos uma (ou mais) função adicional, tecnicamente chamada de ISR – Interrupt Service Routine (Rotina de Serviço de Interrupção). Esta função é invocada toda vez que uma interrupção é gerada no circuito.

Assim, o programa passaria a ter uma estrutura como esta:

void setup() {
}

void loop() {
}

void isr() {
}

Note que a rotina de interrupção retorna void, ou seja, não retorna nada, apenas executa uma tarefa. Seu nome deve ser escolhido de acordo com a função que irá desempenhar. É possível ter mais de uma rotina de interrupção, dependendo da placa usada.

Em que momento exatamente esta rotina será chamada? Isso ocorre quando determinados pinos mudam seu estado – ou seja, ocorre a interrupção. Se um pino estiver em um nível lógico baixo, por exemplo, e mudar para alto, a interrupção é disparada, e o programa chama a rotina isr().

Note que apenas alguns pinos especiais são capazes de disparar interrupções, e esses pinos variam dependendo da versão do Arduino utilizada. Por exemplo, nos Arduinos Uno, Nano e Mini apenas os pinos digitais 2 e 3 podem ser usados para trabalhar com interrupções.

A tabela a seguir mostra diversos modelos de Arduino e seus respectivos pinos de interrupção:

Placa Pinos para Interrupção
Uno / Nano / Mini 2, 3
Mega, Mega2560, MegaADK 2, 3, 18, 19, 20, 21
Micro, Leonardo, com chip 32u4 0, 1, 2, 3, 7
Zero Todos, exceto o 4
MKR1000 Rev.1 0, 1, 4, 5, 6, 7, 8, 9, A1, A2
Due Todos os pinos digitais
101 Todos os pinos digitais; CHANGE só funciona nos pinos 2, 5, 7, 8, 10, 11, 12, 13

Exemplo de Código com Interrupção

O exemplo a seguir ilustra o emprego de interrupções em Arduino:

const int LED = 9;
const int pinoInterrupcao = 2;
volatile int est = LOW;

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(pinoInterrupcao, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(pinoInterrupcao), piscar, CHANGE);
}

void loop() {
  digitalWrite(LED, est);
}

void piscar() {
  est = !est;
}

Neste código, devemos conectar um botão ao pino 2, de interrupção. Toda vez que o botão for pressionado, o estado do pino muda, o que gerará uma interrupção. Assim, o LED será aceso ou apagado, dependendo de seu estado anterior.

A interrupção gerada chama a rotina piscar(), que possui o código que será executado toda vez que o botão for pressionado.

Para configurar a interrupção, nós usamos a função interna attachInterrupt(). Vamos falar sobre ela na sequência.

Função attachInterrupt()

Permite usar interrupções em pinos digitais.

Sintaxe:

attachInterrupt(pinoInterrupção, funçãoISR, modo);

Parâmetros:

  • pinoInterrupção – Vetor de Interrupção. Qual pino irá gerar a interrupção (não é o número do pino!)
  • funçãoISR – Função a ser chamada quando uma interrupção ocorrer
  • modo – Modo de disparo da interrupção

Os modos de disparo são os seguintes:

  • CHANGE – Interrupção é disparada quando o pino muda de estado
  • FALLING – Interrupção é disparada quando o pino vai do estado HIGH para LOW
  • HIGH – Interrupção é disparada quando o pino está no nível HIGH. Este modo somente está disponível para Arduinos baseados em chip ARM, como o Due, Zero ou MKR1000.
  • LOW – Interrupção é disparada quando o pino está no nível LOW
  • RISING – Interrupção é disparada quando o pino vai do estado LOW para HIGH

Relembremos no exemplo mostrado anteriormente o momento em que esta função é executada:

attachInterrupt(digitalPinToInterrupt(pinoInterrupcao), piscar, CHANGE);

Neste exemplo passamos o pino de interrupção usando uma outra função, digitalPinToInterrupt(), que toma um número de pino passado como argumento e retorna o número da interrupção associada, se houver. No caso, o pino passado é o pino 2, que equivale à interrupção 0 do Arduino Uno. Alternativamente, poderíamos ter passado diretamente o número 0, sem usar esta função auxiliar:

attachInterrupt(0, piscar, CHANGE);

Note que para isso é necessário saber os números das interrupções, e não os números dos pinos das placas, que são distintos entre si.

A palavra piscar indica qual rotina será executada quando a interrupção ocorrer. Ela deve ser codificada fora da rotina loop(), e recomenda-se que seja uma função curta e simples, que execute somente o que é estritamente necessário durante a interrupção. Outro ponto importante é que esta função não deve retornar nenhum valor, e nem pode receber valores de entrada (parâmetros). Mas pode usar variáveis, desde que sejam globais.

Finalmente, usamos o modo CHANGE, que significa que sempre que houver mudança de estado no pino (H -> L ou L -> H), a interrupção será disparada.

Note que a função attachInterrupt() deve ser declarada na rotina de setup() do programa.

Variável volatile

Um ponto importante a notar é que empregamos a variável de nome est na interrupção, para armazenar o estado do LED, e esta variável foi declarada como volatile:

volatile int est = LOW;

A palavra volatile é uma palavra-chave da linguagem C que se aplica a variáveis. Significa que o valor da variável não está totalmente sob o controle do programa em si. em nosso caso, significa que o valor do estado pode mudar, devido a uma ocorrência que o programa não pode prever, que no caso é o pressionamento do botão por um usuário.

Além disso, ao usarmos a palavra volatile nos prevenimos de problemas de compilação que possam ocorrer, como por exemplo o risco que existe do compilador simplesmente eliminar esta variável da memória ao compilar o programa, pois ela não é usada diretamente nas rotinas de loop() e setup(). Compiladores costumam fazer isso, para eliminar variáveis não utilizadas e, assim, economizar espaço em memória.

É isso aí! Existem outras formas de trabalhar com interrupções no Arduino, como por exemplo o uso de registradores e máscaras de registrador. Como são formas mais avançadas, as estudaremos em uma outra lição.

Até mais!

Referências

https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

Sobre Fábio dos Reis (1195 Artigos)
Fábio dos Reis trabalha com tecnologias variadas há mais de 30 anos, tendo atuado nos campos de Eletrônica, Telecomunicações, Programação de Computadores e Redes de Dados. É um entusiasta de Ciência e Tecnologia em geral, adora Viagens e Música, e estuda idiomas, além de ministrar cursos e palestras sobre diversas tecnologias em São Paulo e outras cidades do Brasil.

Escreva um comentário

Seu e-mail não será divulgado


*