O princípio básico deste software foi fazer algo simples e aproveitar ideias que já usei antes.
Do ponto de vista global o timer possui um estado (andando ou parado) e um valor a apresentar no display. Para facilitar, vamos manter também um flag para indicar que a contagem chegou a zero (alarme). O valor será armazenado em quatro bytes (um por dígito, com valor de 0 a 9).
// Estado do timer static bool bParado = true; static bool bAlarme = false; static char valor[4];O coração de tudo é uma temporização para contagem de tempo e atualização do display. A precisão do cristal do Arduino é mais que suficiente para esta aplicação. Para ficar mais simples, vamos usar uma interrupção de timer a cada 5 milisegundos. A biblioteca Timer1 é perfeita para isto. Além de apresentar o valor atual no display, a rotina de interrupção irá decrementar o valor, piscar os pontos e pulsar o buzzer. Começando pelos pontos, vamos piscá-los a cada 50 interrupções quando o timer estiver andando e mantê-los acesos quando o timer estiver parado:
static uint8_t cntPonto = 50;
static uint8_t mskPonto = 0;
if (bParado) {
mskPonto = 0x80;
} else if (--cntPonto == 0) {
cntPonto = 50;
mskPonto ^= 0x80;
}
Quando o contador chega a zero (alarme), o buzzer é pulsado junto com os pontoes: if (bAlarme) {
// Pulsar o buzzer junto com o ponto
digitalWrite (pinBuzzer, (mskPonto == 0) ? LOW : HIGH);
}
Se o timer está andando e não atingiu zero, a contagem deve ser decrementada a cada 200 interrupções. O código abaixo faz na raça o tratamento dos quatro dígitos e a detecção de zero. static uint8_t cntSeg = 200;
// Trata contagem do tempo
if (--cntSeg == 0) {
cntSeg = 200;
if (valor[3] > 0) {
valor[3]--;
} else {
valor[3] = 9;
if (valor[2] > 0) {
valor[2]--;
} else {
valor[2] = 5;
if (valor[1] > 0) {
valor[1]--;
} else {
valor[1] = 9;
valor[0]--;
}
}
}
bAlarme = (valor[0] | valor[1] | valor[2] | valor[3]) == 0;
Completando a rotina de interrupção, temos a apresentação do valor. A cada interrupção um dígito é apresentado. Uma tabela é usada para determinar quais segmentos devem ser ativados para cada número.// Desenho dos digitos
static const uint8_t digito[10] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66,
0x6D, 0x7D, 0x07, 0x7F, 0x6F
};
static uint8_t iDig = 0;
// Mostra o valor atual
digitalWrite (pinDigito[iDig], LOW);
iDig = (iDig+1) & 3;
uint8_t segtos = digito[valor[iDig]] | mskPonto;
for (uint8_t iSegto = 0; iSegto < 8; iSegto++) {
digitalWrite (pinSegto[iSegto], (segtos & 1) ? LOW : HIGH);
segtos = segtos >> 1;
}
digitalWrite (pinDigito[iDig], HIGH);
O programa principal se resume a tratar os botões (que vou chamar de A e B). Com o timer parado, o botão A seleciona a contagem inicial e o botão B inicia (ou retoma) a contagem. Com o timer andando, o botão A interrompe o alarme (se a contagem chegou a zero) e o botão B para a contagem (se ainda não chegou a zero).// Opções de contagem
static char opcoes[][4] = {
{ 0, 0, 3, 0 },
{ 0, 0, 4, 5 },
{ 0, 1, 0, 0 },
{ 0, 2, 0, 0 },
{ 0, 5, 0, 0 },
{ 1, 0, 0, 0 },
{ 1, 5, 0, 0 },
{ 3, 0, 0, 0 }
};
#define N_OPC (sizeof(opcoes)/sizeof(opcoes[0]))
static uint8_t iOpc = 0;
void loop() {
if (bParado) {
if (digitalRead (pinBotaoA) == 0) {
// Passar para o próximo valor
iOpc++;
if (iOpc == N_OPC) {
iOpc = 0;
}
valor[0] = opcoes[iOpc][0];
valor[1] = opcoes[iOpc][1];
valor[2] = opcoes[iOpc][2];
valor[3] = opcoes[iOpc][3];
// Espera soltar
while (digitalRead (pinBotaoA) == 0) {
}
delay (200); // debounce
}
if (digitalRead (pinBotaoB) == 0) {
// Espera soltar
while (digitalRead (pinBotaoA) == 0) {
}
delay (200); // debounce
// Inicar contagem
bParado = false;
}
} else {
if (bAlarme) {
if (digitalRead (pinBotaoA) == 0) {
// Desligar o alarme
bAlarme = false;
bParado = true;
digitalWrite (pinBuzzer, LOW);
// Voltar ao valor inicial
valor[0] = opcoes[iOpc][0];
valor[1] = opcoes[iOpc][1];
valor[2] = opcoes[iOpc][2];
valor[3] = opcoes[iOpc][3];
// Espera soltar
while (digitalRead (pinBotaoA) == 0) {
}
delay (200); // debounce
}
} else {
if (digitalRead (pinBotaoB) == 0) {
// Parar contagem
bParado = true;
// Espera soltar
while (digitalRead (pinBotaoA) == 0) {
}
delay (200); // debounce
}
}
}
}
O programa completo está nos arquivos do blog (veja no alto à direita), no arquivo TimerBSides.zip.
Nenhum comentário:
Postar um comentário