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/
Escreva um comentário