quinta-feira, novembro 17, 2016

Novo Timer para Apresentações - Software

Vamos dar uma olhada rápida no software que eu fiz para a palestra/oficina de montagem de timers.


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).
  1. // Estado do timer  
  2. static bool bParado = true;  
  3. static bool bAlarme = false;  
  4. 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:
  1. static uint8_t cntPonto = 50;  
  2. static uint8_t mskPonto = 0;  
  3.   
  4. if (bParado) {  
  5.   mskPonto = 0x80;  
  6. else if (--cntPonto == 0) {  
  7.     cntPonto = 50;  
  8.     mskPonto ^= 0x80;  
  9. }  
Quando o contador chega a zero (alarme), o buzzer é pulsado junto com os pontoes:
  1. if (bAlarme) {  
  2.   // Pulsar o buzzer junto com o ponto  
  3.   digitalWrite (pinBuzzer, (mskPonto == 0) ? LOW : HIGH);        
  4. }  
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.
  1. static uint8_t cntSeg = 200;  
  2.   
  3.   // Trata contagem do tempo  
  4.   if (--cntSeg == 0) {  
  5.     cntSeg = 200;  
  6.     if (valor[3] > 0) {  
  7.       valor[3]--;  
  8.     } else {  
  9.       valor[3] = 9;  
  10.       if (valor[2] > 0) {  
  11.         valor[2]--;  
  12.       } else {  
  13.         valor[2] = 5;  
  14.         if (valor[1] > 0) {  
  15.           valor[1]--;  
  16.         } else {  
  17.           valor[1] = 9;  
  18.           valor[0]--;  
  19.         }  
  20.       }  
  21.     }  
  22.     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.
  1. // Desenho dos digitos  
  2. static const uint8_t digito[10] = {  
  3.   0x3F, 0x06, 0x5B, 0x4F, 0x66,  
  4.   0x6D, 0x7D, 0x07, 0x7F, 0x6F  
  5. };  
  6.   
  7.   static uint8_t iDig = 0;  
  8.   
  9.   // Mostra o valor atual  
  10.   digitalWrite (pinDigito[iDig], LOW);  
  11.   iDig = (iDig+1) & 3;  
  12.   uint8_t segtos = digito[valor[iDig]] | mskPonto;  
  13.   for (uint8_t iSegto = 0; iSegto < 8; iSegto++) {  
  14.     digitalWrite (pinSegto[iSegto], (segtos & 1) ? LOW : HIGH);  
  15.     segtos = segtos >> 1;  
  16.   }  
  17.   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).
  1. // Opções de contagem  
  2. static char opcoes[][4] = {  
  3.   { 0, 0, 3, 0 },  
  4.   { 0, 0, 4, 5 },  
  5.   { 0, 1, 0, 0 },  
  6.   { 0, 2, 0, 0 },  
  7.   { 0, 5, 0, 0 },  
  8.   { 1, 0, 0, 0 },  
  9.   { 1, 5, 0, 0 },  
  10.   { 3, 0, 0, 0 }  
  11. };  
  12. #define N_OPC (sizeof(opcoes)/sizeof(opcoes[0]))  
  13. static uint8_t iOpc = 0;  
  14.   
  15. void loop() {  
  16.   if (bParado) {  
  17.     if (digitalRead (pinBotaoA) == 0) {  
  18.       // Passar para o próximo valor  
  19.       iOpc++;  
  20.       if (iOpc == N_OPC) {  
  21.         iOpc = 0;  
  22.       }  
  23.       valor[0] = opcoes[iOpc][0];  
  24.       valor[1] = opcoes[iOpc][1];  
  25.       valor[2] = opcoes[iOpc][2];  
  26.       valor[3] = opcoes[iOpc][3];  
  27.   
  28.       // Espera soltar  
  29.       while (digitalRead (pinBotaoA) == 0) {  
  30.       }  
  31.       delay (200);  // debounce  
  32.     }  
  33.     if (digitalRead (pinBotaoB) == 0) {  
  34.       // Espera soltar  
  35.       while (digitalRead (pinBotaoA) == 0) {  
  36.       }  
  37.       delay (200);  // debounce  
  38.       // Inicar contagem  
  39.       bParado = false;  
  40.     }  
  41.   } else {  
  42.     if (bAlarme) {  
  43.       if (digitalRead (pinBotaoA) == 0) {  
  44.         // Desligar o alarme  
  45.         bAlarme = false;  
  46.         bParado = true;  
  47.         digitalWrite (pinBuzzer, LOW);  
  48.           
  49.         // Voltar ao valor inicial  
  50.         valor[0] = opcoes[iOpc][0];  
  51.         valor[1] = opcoes[iOpc][1];  
  52.         valor[2] = opcoes[iOpc][2];  
  53.         valor[3] = opcoes[iOpc][3];  
  54.     
  55.         // Espera soltar  
  56.         while (digitalRead (pinBotaoA) == 0) {  
  57.         }  
  58.         delay (200);  // debounce  
  59.       }  
  60.     } else {  
  61.       if (digitalRead (pinBotaoB) == 0) {  
  62.         // Parar contagem  
  63.         bParado = true;  
  64.         // Espera soltar  
  65.         while (digitalRead (pinBotaoA) == 0) {  
  66.         }  
  67.         delay (200);  // debounce  
  68.       }  
  69.     }  
  70.   }  
  71. }  
O programa completo está nos arquivos do blog (veja no alto à direita), no arquivo TimerBSides.zip.

Nenhum comentário: