Os temporizadores são amplamente usados na eletrônica para controlar por quanto tempo um sistema pode funcionar. São usados em padarias, restaurantes, fornos de microondas e em diversos tipos de indústrias. Neste artigo, aprenderemos como construir um cronômetro decrescente com o Arduino Uno.
MIC051S
Os temporizadores são construídos com base em periféricos timer que vêm dentro dos microcontroladores. Consulte a Figura 1. Os periféricos timer são circuitos de contadores digitais que contam os pulsos de relógio (clock) do microcontrolador.
Esses contadores são programados para que, em uma determinada quantidade de pulsos de clock, executem uma interrupção. Assim, podemos implementar, por exemplo, um temporizador (timer) para que a cada milissegundo ele gere uma interrupção. Com isso, você pode construir uma base de tempo para contar milissegundos.
EXEMPLO BÁSICO PARA TEMPORIZADOR.
Um método amplamente utilizado no Arduino para gerar temporizações é a função delay(), que recebe como parâmetro o número de milissegundos que você deseja cronometrar. A seguir está um exemplo básico de tempo usando esta função:
int LED_OUTPUT = 13;
int TIME_TO_DEC = 22;
const int display_1_Pinout[7] = { 2, 3, 4, 5, 6, 7, 8 };
const int display_2_Pinout[7] = { 9, 10, 11, 12, A0, A1, A2 };
const byte anodeDisplay[10] =
{
0b1000000, //0
0b1111001, //1
0b0100100, //2
0b0110000, //3
0b0011001, //4
0b0010010, //5
0b0000010, //6
0b1111000, //7
0b0000000, //8
0b0010000, //9
};
/****************************************************************
****************************************************************/
void setup()
{
pinMode(LED_OUTPUT, OUTPUT);
for(int i = 0; i < 7; i++)
{
pinMode(display_1_Pinout[i], OUTPUT);
pinMode(display_2_Pinout[i], OUTPUT);
}
}
/****************************************************************
****************************************************************/
void Display_2_Write(int number)
{
byte numberBit = anodeDisplay[number];
for (int i = 0; i < 7; i++)
{
int bit = bitRead(numberBit, i);
digitalWrite(display_2_Pinout[i], bit);
}
}
/****************************************************************
****************************************************************/
void Display_1_Write(int number)
{
byte numberBit = anodeDisplay[number];
for (int i = 0; i < 7; i++)
{
int bit = bitRead(numberBit, i);
digitalWrite(display_1_Pinout[i], bit);
}
}
/****************************************************************
****************************************************************/
void Display_Show(int number)
{
int units = number % 10;
int tens = number / 10;
Display_1_Write(units);
Display_2_Write(tens);
}
/****************************************************************
****************************************************************/
void loop()
{
digitalWrite(LED_OUTPUT, HIGH);
for(int cnt=TIME_TO_DEC; cnt>=1; cnt--)
{
Display_Show(cnt);
delay(1000);
}
Display_1_Write(0);
digitalWrite(LED_OUTPUT, LOW);
while(1);
}
O circuito para testar o programa anterior pode ser visto na Figura 2.
O LED na placa Arduino é usado para verificar o tempo. Este LED está conectado à saída digital número 13 da placa Arduino Uno. Esse tempo é executado toda vez que a placa Arduino é conectada à fonte de tensão. Observe que no final do programa há um loop infinito formado pela instrução while (1). Quando a função delay () é executada, ela não permite que o microcontrolador execute qualquer outra instrução, portanto, em programas de temporização onde é necessária mais complexidade, a função é usada:
millis ();
O que essa função faz é retornar o número de milissegundos desde que a placa Arduino iniciou a execução do programa, ou seja, desde que o circuito estava conectado à fonte de alimentação. Um detalhe a ter em mente com a função millis () é que o valor retornado é um unsigned long. É um tipo de dado que é armazenado em 4 bytes, ou seja, 32 bits e sua faixa vai de 0 a 4.294.967.295. É necessário ter cuidado ao fazer cálculos matemáticos, uma vez que é necessário que as variáveis usadas nesses cálculos também sejam unsigned long para evitar erros. Um erro muito comum é usar variáveis do tipo int, unsigned int e long. Os tipos de variáveis int y unsigned int no Arduino Uno são de 16 bits. O tipo long é de 32 bits, mas são números que podem armazenar valores negativos, pois seu intervalo vai de -2.147.483.648 a 2.147.483.647.
Neste artigo iremos implementar um cronômetro com 2 dígitos. Também visualizaremos este projeto do ponto de vista da UML, que é uma linguagem de gerenciamento de projetos e cada vez mais utilizada no mundo dos microcontroladores.
UML
UML ou Unified Modeling Language, é uma forma de visualizar um projeto por meio de gráficos ou imagens. O logotipo que representa esse idioma pode ser visto na Figura 3.
Os gráficos mais usados na área do microcontrolador são:
O diagrama de casos de uso.
O diagrama de estado.
O diagrama de classes.
À medida que avançamos neste artigo, descreveremos a utilidade de cada diagrama para vê-lo de maneira prática.
O DIAGRAMA DE CASOS DE USO.
Descreve de forma genérica as funções que um usuário pode realizar no sistema. Para o caso do cronômetro, precisamos de um caso de uso o Start e outro caso de uso para oStop . Observe a Figura 4.
O DIAGRAMA DOS ESTADOS.
Os diagramas de estado representam os possíveis estados em que um sistema está em um determinado momento. No caso do temporizador, temos 3 estados:
enum STATES {
Começando,
Diminuindo,
Final
};
Os eventos são usados para passar de um estado para outro. Para o caso do cronômetro, temos 3 eventos:
enum EVENTS {
COMEÇAR,
STOP_SETPOINT,
CARRAÇA
};
A Figura 5 mostra o diagrama de estado do temporizador.
O ponto preto no estado Begining indica que quando o programa começa a ser executado, o cronômetro é inicializado nesse estado. Esse tipo de diagrama também é conhecido como: Máquina de estado.
DIAGRAMA DE CLASSE.
Os diagramas de classes nos permitem visualizar as variáveis e funções usadas em um projeto. Para o caso do cronômetro, a Figura 6 mostra o diagrama de classes. Como alguns projetos não são totalmente escritos na linguagem C ++, mas são uma mistura da linguagem C e da linguagem C ++, é necessário adaptar os conceitos do diagrama de classes para essas 2 linguagens. A Figura 6 é uma amostra dessa adaptação.
Los diagramas de clases nos permiten visualizar las variables y funciones usadas en un proyecto. Para el caso del temporizador la Figura 6 muestra el diagrama de clases. Como algunos proyectos no son totalmente escritos en lenguaje C++, sino que son una mezcla de lenguaje C y lenguaje C++, es necesario adaptar los conceptos del diagrama de clases para estos 2 lenguajes. La Figura 6 es una muestra de esta adaptación.
PROGRAMA PARA EL TEMPORIZADOR DESCENDENTE CON ARDUINO UNO.
El siguiente programa es un temporizador descendente en segundos. El valor que determina la temporización (set point), es declarado en la línea de programa:
int SET_POINT = 37;
Esta línea indica al programa para temporizar por 37 segundos. Cambiando este valor se puede temporizar hasta un rango de 99 segundos. La Figura 7 muestra el circuito electrónico para probar el temporizador.
El circuito pose dos pulsadores para controlar el temporizador. El código es el siguiente:
PROGRAMA para o temporizador descendente com ARDUINO UNO.
O programa a seguir é uma contagem regressiva em segundos. O valor que determina o tempo (set point) é declarado na linha do programa:
int SET_POINT = 37;
Esta linha informa ao programa para atingir o tempo limite de 37 segundos. Ao alterar este valor, é possível atrasar até um intervalo de 99 segundos. A Figura 8 mostra o circuito eletrônico para teste do temporizador.
O circuito possui dois botões para controlar o cronômetro. O código é o seguinte:
int LED_OUTPUT = A5;
int SET_POINT = 37;
enum EVENTS {
START,
STOP_SETPOINT,
TICK
};
enum STATES {
Begining,
Decreasing,
Ending
};
int stateTimer = Begining;
int miliSecond_Time = 0;
int timer = 0;
const int display_1_Pinout[7] = { 2, 3, 4, 5, 6, 7, 8 };
const int display_2_Pinout[7] = { 9, 10, 11, 12, A0, A1, A2 };
const byte anodeDisplay[10] =
{
0b1000000, //0
0b1111001, //1
0b0100100, //2
0b0110000, //3
0b0011001, //4
0b0010010, //5
0b0000010, //6
0b1111000, //7
0b0000000, //8
0b0010000, //9
};
class Button
{ private:
int pin;
int level;
int lastLevel;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
public:
Button( int _pin)
{
pin = _pin;
pinMode(pin, INPUT_PULLUP);
level = digitalRead(pin);
lastLevel = !level;
}
bool getLevel()
{
int currentLevel = digitalRead(pin);
if (currentLevel != lastLevel)
{
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay)
{
level = currentLevel;
}
lastLevel = currentLevel;
return level;
}
} ;
Button start_Button(A3);
Button stopSetpoint_Button(A4);
/****************************************************************
****************************************************************/
void setup()
{
pinMode(LED_OUTPUT, OUTPUT);
for(int i = 0; i < 7; i++)
{
pinMode(display_1_Pinout[i], OUTPUT);
pinMode(display_2_Pinout[i], OUTPUT);
}
miliSecond_Time = millis();
Stop();
}
/****************************************************************
****************************************************************/
void Display_2_Write(int number)
{
byte numberBit = anodeDisplay[number];
for (int i = 0; i < 7; i++)
{
int bit = bitRead(numberBit, i);
digitalWrite(display_2_Pinout[i], bit);
}
}
/****************************************************************
****************************************************************/
void Display_1_Write(int number)
{
byte numberBit = anodeDisplay[number];
for (int i = 0; i < 7; i++)
{
int bit = bitRead(numberBit, i);
digitalWrite(display_1_Pinout[i], bit);
}
}
/****************************************************************
****************************************************************/
void Display_Show(int number)
{
int units = number % 10;
int tens = number / 10;
Display_1_Write(units);
Display_2_Write(tens);
}
/****************************************************************
****************************************************************/
void Rele_Off()
{
digitalWrite(LED_OUTPUT, HIGH);
}
/****************************************************************
****************************************************************/
void Rele_On()
{
digitalWrite(LED_OUTPUT, LOW);
}
/****************************************************************
****************************************************************/
void Stop()
{
Rele_Off();
timer = SET_POINT;
Display_Show(timer);
stateTimer = Begining;
}
/****************************************************************
****************************************************************/
void Decrement()
{
timer--;
if( timer == 0 )
{
stateTimer = Ending;
Rele_Off();
}
Display_Show(timer);
}
/****************************************************************
****************************************************************/
void Start()
{
miliSecond_Time = millis();
Rele_On();
stateTimer = Decreasing;
}
/****************************************************************
****************************************************************/
void SM(EVENTS e)
{
switch( stateTimer )
{
case Begining:
switch( e )
{
case START:
Start();
break;
}
break;
case Decreasing :
switch( e )
{
case STOP_SETPOINT:
Stop();
break;
case TICK:
Decrement();
break;
}
break;
case Ending :
switch( e )
{
case STOP_SETPOINT:
Stop();
break;
}
break;
}
}
/****************************************************************
****************************************************************/
void loop()
{
int current_Time = millis();
if ((current_Time - miliSecond_Time) >= 1000)
{
miliSecond_Time = current_Time;
SM(TICK);
}
if ( start_Button.getLevel() == LOW)
{
SM(START);
}
if ( stopSetpoint_Button.getLevel() == LOW)
{
SM(STOP_SETPOINT);
}
}
Na função loop (), os botões são escaneados para ver se foram pressionados e o tempo do cronômetro também é verificado para ver se um segundo já passou e o evento TICK é enviado para a função SM (), que está encarregada de processar o Máquina de estados. A função SM () se encarrega de verificar em que estado se encontra o cronômetro e enviar o evento para esse estado. Cada estado processa o evento de acordo com a lógica de operação do temporizador.
Quando o cronômetro está no Begining, é verificado se o evento START foi enviado. Nesse caso, é feita uma transição para o estado Decreasing . Quando o temporizador está no estado Decreasing, 2 eventos ocorrem. O evento STOP para ou para o cronômetro e faz a transição para o estado Begining . O evento TICK diminui a variável Timer e verifica se ela ainda não atingiu zero. Nesse caso, o estado não muda e permanece no estado BeginingTimer . Mas se a variável chegar a zero, ela passará para o estado Ending e o LED será desligado. O estado Ending verifica se o evento STOP foi recebido, em caso afirmativo, uma transição é feita para o estado Begining . A saída utilizada para o LED pode também ser utilizada para controlar um relé e assim permitir o manuseio de componentes eletrônicos ou elétricos de maior consumo. Veja a Figura 9.
A CLASSE BUTTON
A classe Button é usada para representar e manipular botões ou botões neste projeto. Quando um botão é pressionado, a hora ou momento em que isso aconteceu é armazenado e se esse tempo for maior que 70 milissegundos, um evento é gerado. O código que implementa a classe Button é o seguinte:
class Button
{ private:
int pin;
int level;
int lastLevel;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
public:
Button( int _pin)
{
pin = _pin;
pinMode(pin, INPUT_PULLUP);
level = digitalRead(pin);
lastLevel = !level;
}
bool getLevel()
{
int currentLevel = digitalRead(pin);
if (currentLevel != lastLevel)
{
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay)
{
level = currentLevel;
}
lastLevel = currentLevel;
return level;
}
} ;
As classes implementadas em C ++ são compostas por variáveis ??e funções que são declaradas dentro do texto como a seguir:
classe Xxxxxx
{
};
O Xxxxxx representa o nome da classe e deve se referir ao utilitário fornecido por essa classe. Como implementamos uma classe para lidar com os botões, por esse motivo foi dado o nome de Button . A Figura 10 mostra com mais detalhes as variáveis e funções da classe Button .