Tutorial: Executando funções em intervalos de tempo fixos (timers) com Arduino

Esse tutorial eu postei originalmente no meu blog: blog.tiago.eti.br, e decidi reproduzi-lo aqui.

Embora o Ardiono seja uma plataforma muito fácil de utilizar e com bastante recursos, eu acho que ela ainda deixa a desejar em alguns apectos. Um deles é a falta do acesso direto aos timers pela biblioteca padrão do arduino. Nesse post eu vou mostrar como utilizar uma biblioteca para configurar de forma simples os timers do Arduino.

Mas o que são os Timers?

Um timer nada mais é do que um contador que é incrementado a cada intervalo de tempo (em alguns microcontroladores intervalo pode ser configurado, o Arduino é um deles). Os timers funcionam como um relógio que pode ser usado para contar o tempo, medir a duração de certos eventos, entre outras aplicações.

O Arduino vem equipado com um microcontrolador ATmega168 ou ATmega328 (que diferem apenas na quantidade de memória interna). Esses microcontroladores possuem e timrestimer0, timer1 and timer2, Timer0 e timer2 são contadores de 8bits, ou seja, contam de 0 a 255, e o timer1 é um contador de 16bits, conta de 0 a 65535.

O Arduino Mega vem equipado com o ATmega1280 ou ATmega2560 (que diferem apenas na quantidade de memória). Eles possuem 6 timerstimer0, timer1, timer2, timer3, timer4, timer5. Os timers 0, 1 e 2 são idênticos aos do ATmega168/328, e os timers 3, 4 e 5 são ambos de 16bits.

Timer0:

timer0 é utilizado pelo Arduino para funções como delay()millis() e micros(). Então não se deve utilizar esse timer para evitar comprometer essa funções.

Timer1:

No Arduino UNO esse é o timer utilizado pela biblioteca de controle de servos. Caso você não esteja utilizando essa biblioteca, esse timer está livre para ser utilizado para outros propósitos. No Arduino Mega esse timer só será utilizado para controlar os servos se você estiver usando mais de 12 servos.

Timer2:

Esse timer é utilizado pela função tone(). Então se você não precisar da função tone() esse timer está livre para outras aplicações.

Timer3 e Timer4:

Esses timers só estão presentes no Arduino Mega e eles só serão utilizados pela biblioteca do Arduino caso vocês esteja utilizando mais de 24 servos. De 25 a 36 servos o timer3 será utilizado e de 37 a 48 servos o timer4 será utilizado.

Timer5:

No Arduino Mega esse é o timer padrão para o controle de servos, ou seja, se você estiver utilizando de 1 a 12 servos, apenas esse timer será utilizado pela biblioteca.

Usando os timers

Os timers podem ser configurados para gerar uma interrupção quando o seu contador chegar ao valor máximo (timer overflow). Mas o que são interrupções?

Interrupções

No Arduino (e nos microcontroladores de forma geral) o programa é executado sequencialmente, linha após linha, instrução após instrução. Uma interrupção é um evento externo que interrompe a execução do programa e chama uma função para tratar a interrupção, após o fim dessa função a execução normal do programa é retomada de onde parou.

Os timers podem ser configurados para quando atingirem o valor máximo da contagem (overflow) gerarem uma interrupção e chamarem uma função específica.

Configurando os timers

A configuração "manual" dos timers é bem chata e trabalhosa pois exige a manipulação de vários registradores do microcontrolador, mas felizmente existem algumas bibliotecas que permitem fazer isso de forma bem simples. O meu objetivo é mostrar como usar a biblioteca Timer1 e Timer3 que está no Arduino Playground, e também em uma versão modificada delas que eu fiz para poder trabalhar com os timers 4 e 5. A minha versão modificada pode ser baixada aqui.

O primeiro passo é baixar as bibliotecas e coloca-las na pasta hardware/libraries/ que está dentro da pasta do Arduino ou em sketchbook/libraries/. O local exato destes diretórios depende do sistema operacional (Linux, Mac ou Windows) e também da forma como a IDE do Arduino foi instalada.

Usando a biblioteca

A descrição completa do funcionamento da biblioteca está neste link, mas eu irei fazer um breve resumo aqui. As principais funções da biblioteca são:

initialize(periodo)

Esse método deve ser chamado antes de qualquer outro método, pois ele é responsável pelas configurações iniciais. Você pode opcionalmente informar o intervalo (em microsegundos) no qual a interrupção deve ser gerada. O menor tempo aceito é 1 micro-segundo e o tempo máximo é de 8.388.480 micro-segundos, ou seja, aproximadamente 8,3 segundos. Caos não seja informado nenhum valor, será atribuído o valor padrão de 1.000.000 micro-segundos (1 segudo). Quando usado o timer1 o analogWrite() nos pinos 9 e 10 do Arduino param de funcionar.

setPeriod(periodo)

Modifica o período da interrupção.

attachInterrupt(funcao, periodo)

Atribui uma função para ser chamada a cada interrupção gerada pelo timer. Se não for especificado um período, será utilizado o período definido na inicialização ou por setPeriod.

Testando a biblioteca

Para testar essa biblioteca não é necessário nenhum hardware adicional, basta usarmos o LED conectado ao pino 13 da placa do Arduino e escrever um novo blink.

Aqui está a versão traduzida do exemplo que está na página da biblioteca:

/*
* Timer1 library example
* August 2012 Translated by Tiago de França Queiroz
* June 2008 | jesse dot tane at gmail dot com
*/

#include "TimerOne.h"

void setup()
{
pinMode(13, OUTPUT);
Timer1.initialize(500000); // Inicializa o Timer1 e configura para um período de 0,5 segundos
Timer1.attachInterrupt(callback); // Configura a função callback() como a função para ser chamada a cada interrupção do Timer1
}

void callback()
{
digitalWrite(13, digitalRead(13) ^ 1);
}

void loop()
{
// Seu código vai aqui...
}

 

Para usar os outros timers basta substituir o #include <TimerOne.h> por  #include <TimerTree.h>, #include <TimerFour.h> ou #include <TimerFive.h> e no resto do código substitua Timer1 por Timer3, Timer4, ou Timer5.

Essas bibliotecas são úteis em várias situações, comoquando o loop principal do seu programa está bloqueado por algum motivo (esperando um botão ser pressionado, por exemplo) e você precisa atualizar um display, ou ler um sensor independentemente do loop principal, entre outras.

É importante lembrar que o tempo necessário para executar as funções que são chamadas na interrupção dos timers deve ser MENOR do que o tempo entre as interrupções!

Lembre-se que a versão que eu modifiquei a função de pwm (que eu não falei nesse post) está desativado.

 

Referências:

http://letsmakerobots.com/node/28278

http://arduino.cc/playground/Code/Timer1

Exibições: 118073

Comentar

Você precisa ser um membro de Laboratorio de Garagem (arduino, eletrônica, robotica, hacking) para adicionar comentários!

Entrar em Laboratorio de Garagem (arduino, eletrônica, robotica, hacking)

Comentário de Tiago de França Queiroz em 18 setembro 2013 às 10:08

Então, internamente o delay usa o timer, eu nunca olhei a implementação, mas  ideia é essa:
1 - Seta o timer para o delay que vc quer, se o delay for maior do que o timer pode contar, coloca a callback pra contar um número X de interrupções.

2 - Coloca o timer pra dar
3 - Coloca uma espera ocupada onde vc quer o delay. Algo como "while(!flag);"
4 - Desativa o timer
5 - Continua o programa.


Eu não entendi direito o que vc fazer.... especialmente essa parte: "Que interrompa uma determinada função, porém permitir que outras ocorram".

O arduino só pode executar uma função por vez. O máximo que da pra conseguir é usar interrupções de software (como os timers) e hardware. O efeito disso vai ser interromper o que está sendo executado, executar a função relacionada com a interrupção e depois retomar a execução que foi interrompida.

Normalmente em aplicações com teclado e coisas do tipo vc tem que ficar lendo as teclas o tempo todo até detectar que uma foi pressionada.

Explica com mais detalhes o que vc quer fazer que eu tento te dar uma resposta melhor.

Comentário de Diogo Santos Silva em 13 setembro 2013 às 10:46

Certo, mas... então não existe "nada" que possa servir de alternativa para o delay()?

Estava olhando o millis()(quando vi o post sobre a biblioteca "timer"), que também possibilita o controle por ciclos de tempo, porém para o que pretendo(ações do componentes serem ativadas por teclado de acordo com o momento) sua aplicação não prática.

Existe algo que se aproxime desse tipo de ação?

Que interrompa uma determinada função, porém permitir que outras ocorram.

E valeu pela explicação Tiago!

Comentário de Tiago de França Queiroz em 12 setembro 2013 às 18:56

Diego, a função delay() e timer são bem diferentes. Para coisas bem simples como o blink elas parecem iguais, mas não são.

Basicamente o delay() para a execução do programa por dado tempo enquanto que os timers executam uma função a cada intervalo de tempo.

Quando uma interrupção do timer é gerado o loop() é interrompido e a função registrada com attachInterrupt é executada e depois o loop() continua sendo executado.

O objetivo do timer (no caso do exemplo desse post) é executar uma função em um intervalo regular de tempo independente do que está sendo executado no loop().


Eu consegui esclarecer sua dúvida?

Comentário de Diogo Santos Silva em 11 setembro 2013 às 11:29

Opa!

Não sei se nesse post posso abordar dessa forma o assunto, mas...... teriam como me dá uma força em como utilizar a biblioteca "timer"!?

Primeiro ao vou abordar o problema!

O delay() é um comando extremamente prático e simples de manusear, porém tem suas limitações. E está dando trabalho para achar um comando alternativo ou algo que possa desempenhar a mesma função, de forma prática.

Aí entra a utilização da biblioteca "timer"!

Eu ingressei recentemente na programação(é ainda sou nub nisso) logo não está sendo simples enxergar/construir um programa sem o delay(), para executar atividade de simples como manter componentes ativados e desativados!

Seria possível utilizar a biblioteca "timer", de forma a, desempenhar a mesma função que o delay() para a função manter ligado e/ou desligado por um tempo?

Usando o exemplo mais básico para o arduino (http://playground.arduino.cc/Portugues/LearningBlink) é possível fazer a mesma atividade utilizando a biblioteca "timer"?

Desculpe se eu estiver fugindo do tópico, mas ainda estou aprendendo na base de exemplos + tentativas.

Comentário de Wilkerson Willame em 16 abril 2013 às 11:42

Olá, estou tendo um problema com o "TimerFive" ! O programa (arduino) não está reconhecendo ele como biblioteca. Quando eu digo isso, digo que o nome <TimerFive.h> não está ficando laranjinha... rsrs'. Algum palpite de problema?

Comentário de Eduardo castellani em 9 janeiro 2013 às 12:33

Sim, Tiago, acho que um relé  seria necessario, mas postei um pedido de ajuda no forum e ate uma proposta, veja se tiver tempo. abraço

http://labdegaragem.com/forum/topics/pedido-de-ajuda-para-um-timer-...

Comentário de Tiago de França Queiroz em 9 janeiro 2013 às 10:39

Olá Eduardo, existem várias formas de fazer isso, algumas mais simples e outras mais complicadas.

Além de controlar essa chave o arduino vai fazer mais alguma coisa (receber dados, monitorar sensores)?

Essa chave de contato seria um relé (http://pt.wikipedia.org/wiki/Rel%C3%A9), certo?

Comentário de Eduardo castellani em 4 janeiro 2013 às 19:01

Oi Luiz, eu preciso controlar uma chave, que desligará uma resistencia eleltrica apos uns 10 min, mas não sei como fazer isso, tenho a chave de contato, o arduino, etc, mas não tenho o conhecimento.

Pode me ajudar?

Comentário de Rubens de Andrade Neto em 20 novembro 2012 às 0:05

Legal

Não sabia porque não conseguia fazer funcionar os motores (biblioteca servo) com o timer 1 para fazer o controle.

Tive que usar o Timer 2 no uno.

Segue link para utilizar o Timer 2:

http://arduino.cc/playground/Main/MsTimer2


abs

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço