Bem-vindos a uma nova entrada do curso de programação para Makers com Arduino e Protocoder! Nesta entrada vamos aprender a utilizar interrupções ou interrupts… mas, o que são? Uma interrupção é um sinal que interrompe o que quer que o processador esteja a calcular naquele momento, para executar outra função distinta. A interrupção pode acontecer através de um sinal externo (por exemplo, o pressionar de um botão) ou de um sinal interno (por exemplo, um temporizador ou um sinal de software). Uma vez chamada, a interrupção pausa o que o processador estiver a fazer naquele momento, e faz com que se execute uma outra função conhecida como Rotina de Serviço de Interrupção (ou pela sigla em inglês, ISR). Uma vez que a ISR tenha finalizado, o programa volta à função, que estava a correr anteriormente. Se quiseres conhecer mais sobre interrupções, podes visitar a página de referência aqui.
Lista de materiais
- ZUM BT-328, placa Arduino ou compatível.
- 1 Interruptor ou Botão.
Conexões Eléctricas
O número de pinos que podem ser utilizados como interrupts depende da placa que estivermos a utilizar. Para a placa ZUM BT-328, Arduino Uno e derivados, são 2, e encontram-se nos pinos 2 e 3, correspondendo ao interrupt 0 e interrupt 1 respectivamente.
Usando as interrupções
Para utilizar as interrupções, em primeiro lugar temos que declarar que interrupção queremos utilizar. No nosso caso utilizaremos a interrupção 0, que está no pino número 2. Para isso declaramos:
1 |
int interrupcao = 0; |
Agora, no setup, temos que especificar que função irá ser chamada no caso de ocorrer a interrupção. Para isso, utilizamos a função attachInterrupt, que tem a seguinte sintaxe:
1 |
attachInterrupt(interrupt, ISR, mode); |
Os argumentos da função attachInterrupt são:
- Interrupt: número da interrupção, ou seja, em que pino devemos “escutar ” para esperar pela interrupção.
- ISR: função que irá ser chamada, no caso de ocorrer a interrupção.
- Mode: define como a ISR será lançada, ou seja, o que tem que acontecer para que se execute a interrupção. Os diferentes modos são:
- LOW: executa-se sempre que o valor no pino seja 0.
- CHANGE: executa-se sempre que se produza uma alteração.
- RISING: executa-se enquanto o valor vá de 0 até 1 (ou de 0 até 5 Volts).
- FALLING: executa-se enquanto o valor vá de 1 a 0 (ou de 5 até 0 Volts).
Há que ter em conta que é necessário escrever os modos, com letras maiúsculas, se não vai produzir-se um erro. No nosso caso, utilizamos…
1 |
attachInterrupt(interrupcao, funcionInterrupcao, FALLING); |
…que detectará quando deixamos de pressionar o botão. Por outro lado, temos que definir a função que se irá chamar quando for detectada a interrupção. Neste caso, chamámos a função funcionInterrupcao, e vamos defini-la com o mesmo nome que colocámos em attachInterrupt. A função é:
1 2 3 4 5 |
void funcionInterrupcao(){ Serial.print("Interrupção número "); Serial.println(numInterrupt); numInterrupt++; } |
Aqui imprimimos através do monitor série, o número de vezes que ocorreu a interrupção. Uma das particularidades das interrupções é que não podem ter nenhum argumento na sua definição nem devolver nenhum valor, e por isso, para assegurar que a ISR tenha um bom acesso às variáveis, temos que utilizar uma variável de tipo volatile, e neste caso, de tipo int. Se quiseres conhecer mais sobre variáveis do tipo volatile, podes visitar esta página de referência. Outra das restrições das interrupçõess é que, enquanto estivermos dentro de uma ISR, o resto das outras interrupções não serão detectadas, não funcionará o delay e não se actualizará o valor de millis, e por isso é importante que as ISR sejam o mais curtas possível.
O código
Aqui podes ver o código completo. Lembra-te que também podes descarregar o código com as imagens utilizadas, no final desta lição.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
int interrupcao = 0; // Interrupt.0 encontra-se no pino digital 2 // Interrupt.1 encontra-se no pino digital 3 /* Criamos a variável do tipo <em>volatile</em> para assegurar que a variável estará acessível a partir do fluxo normal do programa e a partir da interrupção. */ volatile int numInterrupt = 0; void setup() { Serial.begin(9600); // Sintaxe das interrupções: // attachInterrupt(numero_interrupt,funcao_a_executar,modo); // Modos LOW,CHANGE,RISING,FALLING attachInterrupt(interrupcao, funcionInterrupcao, FALLING); } void loop() { /*Simulamos uma tarefa que necessite de algum tempo. Utilizamos <em>while(true)</em> porque assim vai-se cumprir sempre a condição e nunca deixará de executar o código incluído. */ while(true){ Serial.print("."); delay(250); } } void funcionInterrupcao() { Serial.print("Interrupção número "); Serial.println(numInterrupt); numInterrupt++; // Aumentamos o número armazenado em <em>numInterrupt</em> em uma unidade. } |
Em primeiro lugar, declaramos as variáveis que vamos utilizar: interrupcao, com a qual indicaremos ao microprocessador que interrupção queremos utilizar, e numInterrupt, que mostrará o número de vezes que ocorreu a interrupção. Na função setup, iniciamos a porta série e declaramos a interrupção que vamos utilizar com attachInterrupt(). Na função loop, simula-se uma tarefa que necessite de um certo tempo para ser executada. Neste caso, para não sobrecarregar a visualização de saída do programa através da porta série, imprime-se apenas um ponto “.” na porta série. Quando acedemos à interrupção, ao soltar o botão (neste exemplo), a execução do programa é interrompida e é impresso através da porta série “Interrupção número “, seguido do número de vezes que ocorreram as interrupções. Como exercício, propomos-te que modifiques o modo de aceder à interrupção, para entenderes e aprenderes qual é o efeito que tem sobre o mesmo circuito.