Tutorial: interrupções de tempo (timer interruptions) no Arduino

Introdução

Hoje, no Parque, apareceu o ex-aluno Mackson, com o seu projeto de TCC, o qual foi desenvolvido com várias passagens pelo Automação no Parque. Já foi até objeto de post anterior, aqui. Agora, nos "finalmentes" ele trouxe a maquininha para a gente apreciar.



O trabalho dele consiste desenvolver um sistema para operação automática de um biodigestor. Ele montou uma caixa com o diagrama do equipamento no campo e colocou o circuito dentro da caixa, com LEDs, chaves e potenciômetros para ilustrar o funcionamento da bagaça.
Basicamente o digestor fornece gás metano, que é conduzido através de uma válvula (LED amarelo, aceso qdo a válvula está aberta, bombeado por um compressor (LED verde) e queimado num queimador por uma chama (LED azul). A luz verde indica que o sistema está operando em modo Normal.
Aí alguém deu o palpite: mas a chama não é azul, ela muda de cor. E se a gente colocasse um LED RGB e fizesse um efeito chama com ele mudando de cor? Imediatamente a galera se mobilizou para botar a coisa rodando.
A dificuldade residia em que, ao mesmo tempo em que a rotina da chama deveria ficar "rodando", o sistema teria que monitorar os outros indicadores (temperatura, pressão etc), ou seja, durante o tempo em que a chama estiver ligada, seria interessante que a rotina da chama rodasse "em paralelo" com o resto do sistema, de maneira a criar o efeito desejado.
É aí que entra o recurso de time interruption.

Teoria

Para quem programa há vinte anos ou mais: interrupção do Arduino é igual a interrupção do DOS. Pode pular a teoria.
Para os "menos experientes", interrupção é um mecanismo que permite a um processador parar uma rotina, executar uma outra tarefa e depois voltar à execução da tarefa principal. "Mas então é como uma sub-rotina, um sub-programa?", perguntaria alguém.
Sim, é. A diferença é que o desvio do processo de execução para essa sub-rotina se dá não por decisão do programa que chamou a sub-rotina, mas sim por um outro evento qualquer, por exemplo, um intervalo fixo de tempo entre as chamadas. Assim, a cada "n" microssegundos, o Arduino para o que estiver fazendo, roda a rotina e depois retoma o que estava fazendo antes.

Exempo

Vamos a um exemplo. Primeiro o resultado, um LED RGB com o tal "efeito chama" funcionando.



O LED RGB é uma espécie de 3 em 1. São três LED, um (R)vermelho, outro (G)verde e um (B)azul, tudo no mesmo encapsulamento, com 4 conectores: um para cada cor e um comum. Normalmente o comum é o terra, mas eu já vi muitos onde o comum é o +. Nesse caso, vc tem que ligar ele no 5V ou 3.3V e escrever 1 na porta correspondente para apagar e 0 para acender. Uma dica do Alex, frequentador do Automação no Parque e dono do projeto MariaMole: para "misturar" melhor as cores do LED RGB, lixe o bicho com BomBril, de maneira a que fique um pouco opaco.

O jeito óbvio de fazer uma aplicação dessa seria:

int rPin = 11;
int gPin = 10;

void setup()
{
  pinMode(rPin,OUTPUT);
  pinMode(gPin,OUTPUT);
}

void loop()
{
  for (int i=0; i<100; i++)
  {
    analogWrite(gPin,i);
    analogWrite(rPin,200);
    delay(5);
  }
  for (int i=100; i>0; i--)
  {
    analogWrite(gPin,i);
    analogWrite(rPin,200);
    delay(5);
  }
}


No código acima, os pinos 10 e 11 estão ligados aos terminais verde e vermelho do LED, respectivamente.

No loop(), temos dois laços: um varia a intensidade do verde entre 0 e 100 crescendo, o outro varia a velocidade do LED decrescendo. O vermelho é mantido num valor constante. Moleza.

Só que... imagine que o seu programa tem que fazer algum outro serviço além de ficar comandando o LED. Aí complica, porque se vc colocar, por exemplo, um comando delay(1000), o LED vai "congelar" ou apagar, dependendo de como seu código for escrito. Seria interessante que o código pudesse rodar e o efeito LED continuasse acontecendo, até que vc resolvesse desligá-lo. É a aí que entra a interrupção. Veja o código abaixo, agora usando interrupção de tempo, abaixo:

#include <TimerOne.h>
int rPin = 11;
int gPin = 10;

volatile byte _i = 0;
volatile byte _j = 1;

void chama()
{
    analogWrite(gPin,_i);
    analogWrite(rPin,200);
    _i+=_j;
    if (_i == 100)
      _j=-1;
    else
      if (_i == 0)
        _j=1;
}
void setup()
{
  Timer1.initialize(5000);
  Timer1.attachInterrupt(chama); 
  Serial.begin(9600);
  Serial.begin(9600);
  pinMode(rPin,OUTPUT);
  pinMode(gPin,OUTPUT);
}

void loop()
{
}


Agora temos:
O # include da biblioteca TimeOne.h.
Temos uma rotina chama(), que faz o efeito de chama. Ela é um pouquinho mais complicada:
Temos duas variáveis _j, _j, do tipo inteiro, que são usadas pela rotina mas não ficam definidas dentro dela. Isso porque o seu conteúdo deverá ser preservado a cada chamada da rotina, quer dizer, depois que a rotina terminar esses valores tem que permanecer para ser usados a cada chamada. A palavra "volatile", colocada antes da declaração dessas variáveis basicamente diz ao compilador para cuidar para que essas variáveis tenham seus conteúdos preservados da chamada de uma rotina para outra.
Dentro da rotina agora temos o seguinte: o valor da cor vermelha é sempre 200 (valor arbitrário que vc tem que descobrir para o seu LED qual o melhor para obter o efeito que deseja), e o da cor verde é o valor de _i.
A cada chamada da rotina, _j é somado a _i. Em seguida o valor de _i é testado, de maneira que, quando _i for 100, _j = -1, ou seja, _i passará a ser decrementado. Quando _i for 0, _j = 1, o que fará com que no próximo loop _i assuma o valor 1 e assim por diante.
Em seguida vem o setup(). Lá, a instrução   Timer1.initialize(5000); define que a cada 5000 microssegundos a rotina de timer será executada. Em seguida, a instrução Timer1.attachInterrupt(chama); define a rotina a ser chamada a cada interrupção A nossa chama() anteriormente descrita.

Observem que não existe nenhuma rotina na instrução loop(), ou seja, ela está livre para que vc escreva seu código sem se preocupar com a rotina chama(), que estará sendo executada e dando o efeito desejado normalmente.

Não é legal esse troço de interrupção? Agora vejam o efeito funcionando no TCC do Mackson.


E aí se foi mais um sábado de Automação no Parque, o último com a minha participação esse ano. Se o mundo estiver por aí daqui a alguns dias, estaremos de volta em 2013.

Comentários

  1. Nunca fui fã de Arduino, mas saber que recursos que tenho como básico (interrupções externas e por estouro de tempo) começam a ser discutidas, dão um outro panorama sobre esta plataforma. Muito clara e útil sua explicação.
    Abraço!

    ResponderExcluir
    Respostas
    1. Olá, Anônimo.
      Pq vc nunca foi fã do Arduino? Vc usa algum outro produto semelhante?

      Ótimo que o blog lhe foi útil.

      Um feliz 2014 para vc!

      Excluir
  2. valeu amigo pelo conhecimento compartilhado aí

    ResponderExcluir
  3. Olá Mauro, vc sabe como eu uso as interrupções do arduino com um timer diferente do timer principal?

    ResponderExcluir
    Respostas
    1. Cara, timer no Arduino com interrupção é complicado. É que durante a execução da interrupção o timer para, então medição de tempo em programa que usa interrupção é só com o uso de um relógio externo, tipo RTC. Aqui no blog tem um post que fala sobre esse tipo de circuito.

      Excluir
  4. Muito obrigada pelo post, muito interessante, mas gostaria de saber se você tem mais alguma coisa sobre interrupções, por exemplo, como escolher o timer do modelo de arduino que estou usando para determinada aplicação? Aguardo sua resposta

    ResponderExcluir
  5. Cara muito legal, e a melhor parte foi: "Imediatamente a galera se mobilizou para botar a coisa rodando.", parabéns a todos, Paulo.

    ResponderExcluir
    Respostas
    1. Paulo, que ótimo que vc gostou, apareça por aqui de vez em quando prá ver as novidades.

      Excluir
  6. primeiramente, parabéns pelo post, muito bem explicado; agora, gostaria de saber se tem como nesse estilo de codigo no lugar da rotina do led rgb rodar uma rotina que verificasse a serial e caso receba determinados comando execute funçoes, enquanto roda por tras um rotina com varios delays por exemplo?

    ResponderExcluir
    Respostas
    1. Jonas,

      Acho que nesse caso infelizmente não vai rolar. Tem que testar, mas acontece que esse recurso "multithread" do Arduino é muitíssimo limitado, serve para processos muito simples, como esse que vc viu aí.

      Já acessar a serial é um processo muito complicado, que exige muito do Arduino. Então, não sei se ele "guenta chupar esa manga" enquanto faz alguma outra coisa.

      Mas existem outros caminhos, uma máquina de estados, por exemplo, deve te ajudar.

      Abracadabraço,

      Mauro

      Excluir
  7. Boa Noite Mauro,

    Sou novo com arduino, e estou fazendo um projeto de sensor de tensao, corrente e frequencia, estão todos prontos e funcionando perfeitamente individualmente, mas quando junto os 3 códigos, a frequencia não mostra no display, só a tensao e corrente, estou achando que preciso fazer interrupção externa, mas não faço ideia de como arrumar isso, pode me ajudar?

    Agradeço pela atenção.

    Rodrigo Goering

    ResponderExcluir
  8. Tem uma galera que desce a lenha no arduino dizendo que é coisa de menino, brinquedinho e tal, quem "já cresceu" usa é raspiberry pi... kkkkk, OK!!! concordo que o pi é milhões de vezes mais avançado que um arduino mini pro 328. Mas tem que ser muito burro e teimoso pra ir comprar pão na padaria da esquina usando um helicóptero! kkkkkkk Não vejo o arduino e pi como concorrentes, um pi custa quase 200,00 contra 15,00 de um 328. Não existe uma versão do pi que seja minimalista. Cá entre nós... vai animar de colocar um pi de 200,00 sozinho no mato só pra testar a temperatura ou umidade?

    Quem é esperto usa arduino para funções da qual ele atende bem, e usa pi pra coisas que o arduino tem dificuldades. Afinal de contas, só porque uma coisa tem um monte de recursos e capacidades não quer dizer que é melhor em tudo!

    Eu uso arduino para automatizar tudo que preciso, só uso pi quando é realmente necessário.

    O post ficou show! Primeira vez que venho aqui e já tá no favoritos pra visitar semanalmente.

    ResponderExcluir
    Respostas
    1. Ótimo que vc gostou, apareça por aqui, sempre tem novidade. Acho que tem como vc seguir tb.

      Eu uso Arduino, RPi, Intel Edison, Arduino Yún... às vezes faço o mesmo projeto em mais de uma plataforma, ás vezes combino duas (Arduino e Pi, por exemplo, se complementam bem), enfim, sem preconceito.

      O negócio é usar o melhor de cada um.

      Ultimamente como tenho me interessado por visão computacional e outras coisas mais "pesadas", tenho usado mais as plaquinhas com sistema operacional.

      Mas prá controlar alguns servos, nada melhor que o Arduino véio de guerra...

      Abracadabraço,

      Mauro Assis

      Excluir

Postar um comentário

Postagens mais visitadas deste blog

Controle PID de Potência em Corrente Alternada - Arduino e TRIAC - Parte III

Controle PID de Potência em Corrente Alternada - Arduino e TRIAC - Parte I

Dividindo um programa (sketch) do Arduino em mais de um arquivo