Vamos ver o porquê usar interrupções no Arduino, incluindo um exemplo real pelo qual passei recentemente. Mas para iniciar, vamos falar da função delay(), adorada pelos iniciantes.
Quando a pessoa inicia nos microcontroladores, inclusive no Arduino, são apresentados exemplos básicos e simples. Um destes exemplos é o “blink”, que vem com a IDE do Arduino.
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
Observe o código acima, após cada função “digitalWrite” é chamada uma função delay(1000) de 1000 milissegundos. O código fica “parado”, esperando os 1000 milissegundos acabar. Nada é executado enquanto esse tempo não passa.
Uma solução é utilizar as funções millis() ou micros() como eu explico aqui. A ideia não é complicada: conta-se um tempo e entra-se em determinada função somente após este tempo passar. O pedaço de código abaixo foi retirado do meu blog em Inglês.
time = micros();
if (enterFunction == true){
previousTime= time;
Serial.println(previousTime); // for debugging
// Start your code below
//-----------------------
//-----------------------
// End of your code
}
// The DELAY time is adjusted in the constant below >>
if (time - previousTime < 999990){ // 1 million microsencods= 1 second delay
/* I have actually used 0.999990 seconds, in a trial to compensate the time that
this IF function takes to be executed. this is really a point that
need improvement in my code */
enterFunction= false;
}
else {
enterFunction= true;
}
Este método funciona com códigos que executam rápido e em baixa frequência. Vou contar um caso em que descobri que esta função não pode nem deve ser usada para tudo.
Na empresa onde trabalho precisei desenvolver um produto onde deveria-se gerar uma onda quadrada de 55Hz, além de ler entradas digitais, ler um teclado via i2c e controlar um display LCD 20×4.
Usei o código acima para executar as quatro funções (onda quadrada, entradas digitais, teclado i2c, display LCD). Minha surpresa foi na hora de colocar o osciloscópio para medir a frequência de 55Hz.
Chamou atenção o fato de a onda não ser exatamente quadrada, ser um pouco mais parecida com um PWM. A frequência se manteve.
Ao abrir um pouco mais o zoom, outra ingrata surpresa: a cada segundo a onda de 55Hz era interrompida por vários milissegundos.
Me dei conta de que a cada segundo o display LCD 20×4 é atualizado uma vez. Significa então que a atualização do display a cada segundo faz o programa ficar “parado” por vários milissegundos.
Esse é exatamente o problema de utilizar micros() ou millis() para escalonar o programa, caso alguma das suas funções demore mais que o tempo de entrada na função, o programa vai ficar parado ali.
Entram então as interrupções, que baseadas em algum temporizador timer (ou até mesmo em entradas digitais) param o programa e o levam até onde algum código precisa ser executado. Uma das bibliotecas disponíveis para Arduino que lidam com interrupção de timer é a timerinterrupt, neste link.
Outra forma é utilizar diretamente os registradores, conforme o exemplo abaixo. Basicamente no código abaixo são gerados 55Hz no pino chamado “frequency” (pino 21 do Arduino Mega), através de entrar na função “ISR(TIMER1_COMPA_vect)” e sempre mudar o estado da saída com “digitalWrite(frequency, !digitalRead(frequency))”.
cli();//stop interrupts
//set timer1 interrupt at 1Hz
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
// set compare match register for 1hz increments
// Frequency was calculated on this site https://www.arduinoslovakia.eu/application/timer-calculator
OCR1A = 18180; // 110.00495022276003 Hz (16000000/((18180+1)*8))
// Tive que colocar 110Hz porque cada vez que interrompe é contado um ciclo, porém
// minha onda quadrada tem nível alto e baixo, dois "momentos" porém em um só ciclo
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS12 and CS10 bits for 1024 prescaler
//TCCR1B |= (1 << CS12) | (1 << CS10);
TCCR1B |= (1 << CS11);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();//allow interrupts
}
ISR(TIMER1_COMPA_vect){ //timer1 interrupt 1Hz toggles pin 13 (LED)
//generates pulse wave of frequency 1Hz/2 = 0.5kHz (takes two cycles for full wave- toggle high then toggle low)
digitalWrite(frequency, !digitalRead(frequency));
}
No caso do exemplo acima os 55Hz são críticos, já demais funções (i2c, display LCD, teclado) “podem esperar”.
Pra resumir
Quando iniciamos no aprendizado de uma ferramenta como o Arduino, não temos ideia do que é possível fazer e nem do que não fazer. Abandonar o delay() é somente um passo, começamos então a utilizar funções com micros() e millis().
Porém dependendo do que queremos fazer e de quanto código vamos escrever, estas funções não nos ajudam mais, temos que partir para utilização “pesada” de interrupções. É aí que a brincadeira começa a ficar interessante.
Deixe um comentário