quarta-feira, setembro 02, 2015

Timer para Apresentações - Operação e Software

Concluindo a apresentação deste projeto, vou falar como é a operação (que pode ser vista no vídeo abaixo) e como eu estruturei o software.



Obs.: Reparar que refiz a fixação dos botões. Outra alteração foi o reposicionamento do buzzer para o lado de fora, para ficar mais audível.

A operação é bastante simples. Inicialmente, com o relógio parado, os LEDs na extremidade pulsam bem devagar. Através do botão da esquerda se seleciona o tempo dentre as opções programadas no firmware. O botão da direita inicia a contagem decrescente, com os LEDs nas extremidades pulsando mais rápido. O botão da direita pode ser usado para parar momentaneamente a contagem (apertando de novo retoma) e o botão da esquerda interrompe (voltando à situação inicial). Se a contagem chegar a zero, a "bomba" "explode": o buzzer é acionado e os LEDs azuis no interior piscam rapidamente. Neste ponto é preciso apertar o botão esquerdo para voltar ao estado inicial.

O programa principal do software está organizado como uma máquina de estados. Os estados possíveis são RESET, INICIAL, CONTANDO, PAUSA e EXPLOSAO. A mudança entre os estados (exceto de RESET para INICIAL) é controlada pelos botões (quando a tecla é solta) e o evento da contagem chegar a zero. A atualização do display e o acionamento do buzzer na explosão também são feitos pelo programa principal:

  1. // Programa Principal  
  2. int main (void)  
  3. {  
  4.     enum { RESET, INICIAL, CONTANDO, PAUSA, EXPLOSAO } estado;  
  5.     uint8_t iVal = 0;  
  6.       
  7.     // Inicia o hardware  
  8.     iniciaHw();  
  9.       
  10.     // Inicia o software  
  11.     estado = RESET;  
  12.       
  13.     // Permite interrupções  
  14.     sei ();  
  15.       
  16.     // Eterno equanto dure  
  17.     for (;;)  
  18.     {  
  19.         // Maquina de Estados  
  20.         switch (estado)  
  21.         {  
  22.             case RESET:  
  23.                 contador = valCont[iVal];  
  24.                 mostraContador ();  
  25.                 apagaLedMeio ();  
  26.                 acendeLedPonta (100, 200);  
  27.                 estado = INICIAL;  
  28.                 break;  
  29.             case INICIAL:  
  30.                 if (botaoEsq)  
  31.                 {  
  32.                     while (botaoEsq)  
  33.                         ;  
  34.                     click();  
  35.                     if (++iVal == N_VALORES)  
  36.                         iVal = 0;  
  37.                     contador = valCont[iVal];  
  38.                     mostraContador ();  
  39.                 }  
  40.                 if (botaoDir)  
  41.                 {  
  42.                     while (botaoDir)  
  43.                         ;  
  44.                     click();  
  45.                     acendeLedPonta (20, 20);  
  46.                     ligaContador (valCont[iVal]);  
  47.                     estado = CONTANDO;  
  48.                 }  
  49.                 break;  
  50.             case CONTANDO:  
  51.                 if (contAtl)  
  52.                 {  
  53.                     mostraContador();  
  54.                     contAtl = FALSE;  
  55.                 }  
  56.                 if (contZero)  
  57.                 {  
  58.                     mostraContador();  
  59.                     acendeLedMeio (20, 20);  
  60.                     PORTA |= BUZZER;  
  61.                     estado = EXPLOSAO;  
  62.                     break;  
  63.                 }  
  64.                 if (botaoEsq)  
  65.                 {  
  66.                     while (botaoEsq)  
  67.                         ;  
  68.                     click();  
  69.                     pausaContador();  
  70.                     estado = RESET;  
  71.                 }  
  72.                 if (botaoDir)  
  73.                 {  
  74.                     while (botaoDir)  
  75.                         ;  
  76.                     click();  
  77.                     pausaContador();  
  78.                     apagaLedPonta();  
  79.                     estado = PAUSA;  
  80.                 }  
  81.                 break;  
  82.             case PAUSA:  
  83.                 if (botaoEsq)  
  84.                 {  
  85.                     while (botaoEsq)  
  86.                         ;  
  87.                     click();  
  88.                     estado = RESET;  
  89.                 }  
  90.                 if (botaoDir)  
  91.                 {  
  92.                     while (botaoDir)  
  93.                         ;  
  94.                     click();  
  95.                     acendeLedPonta (20, 20);  
  96.                     contOn = TRUE;  
  97.                     estado = CONTANDO;  
  98.                 }  
  99.                 break;  
  100.             case EXPLOSAO:  
  101.                 if (botaoEsq)  
  102.                 {  
  103.                     while (botaoEsq)  
  104.                         ;  
  105.                     click();  
  106.                     PORTA &= ~BUZZER;  
  107.                     estado = RESET;  
  108.                 }  
  109.                 break;  
  110.         }  
  111.     }  
  112. }  
A rotina de interrupção do timer é usada para:
  • Verificar o estado das teclas e efetuar o "debounce"
  • Controlar a piscada dos LEDs
  • Decrementar o contador
  • Desligar o buzzer no click das teclas.
  1. // Tratamento da interrupção do timer 0  
  2. // ocorre a cada 10ms  
  3. ISR (TIM0_COMPA_vect)  
  4. {  
  5.     static uint8_t debDir = 0, debEsq = 0;  
  6.     static uint8_t contSeg = 100;  
  7.   
  8.     // Atualiza estado das teclas  
  9.     if ((PINB & TEC1) == 0)  
  10.     {  
  11.         if (botaoDir)  
  12.         {  
  13.             debDir = 0;  
  14.         }  
  15.         else  
  16.         {  
  17.             if (++debDir == DEBOUNCE)  
  18.             {  
  19.                 botaoDir = TRUE;  
  20.                 debDir = 0;  
  21.             }  
  22.         }  
  23.     }  
  24.     else  
  25.     {  
  26.         if (botaoDir)  
  27.         {  
  28.             if (++debDir == DEBOUNCE)  
  29.             {  
  30.                 botaoDir = FALSE;  
  31.                 debDir = 0;  
  32.             }  
  33.         }  
  34.         else  
  35.         {  
  36.             debDir = 0;  
  37.         }  
  38.     }  
  39.     if ((PINB & TEC2) == 0)  
  40.     {  
  41.         if (botaoEsq)  
  42.         {  
  43.             debEsq = 0;  
  44.         }  
  45.         else  
  46.         {  
  47.             if (++debEsq == DEBOUNCE)  
  48.             {  
  49.                 botaoEsq = TRUE;  
  50.                 debEsq = 0;  
  51.             }  
  52.         }  
  53.     }  
  54.     else  
  55.     {  
  56.         if (botaoEsq != 0)  
  57.         {  
  58.             if (++debEsq == DEBOUNCE)  
  59.             {  
  60.                 botaoEsq = 0;  
  61.                 debEsq = 0;  
  62.             }  
  63.         }  
  64.         else  
  65.         {  
  66.             debEsq = 0;  
  67.         }  
  68.     }  
  69.   
  70.     // Trata o contador  
  71.     if (contOn && (--contSeg == 0))  
  72.     {  
  73.         contSeg = 100;  
  74.         if (contador != 0)  
  75.         {  
  76.             contAtl = TRUE;  
  77.             if (--contador == 0)  
  78.                 contZero = TRUE;  
  79.         }  
  80.     }  
  81.       
  82.     // Trata os LEDs  
  83.     if (cntLedPonta != 0)  
  84.     {  
  85.         if (--cntLedPonta == 0)  
  86.         {  
  87.             if ((PORTA & LED1) != 0)  
  88.             {  
  89.                 PORTA &= ~(LED1 | LED2);  
  90.                 cntLedPonta = tmpOffPonta;  
  91.             }  
  92.             else  
  93.             {  
  94.                 PORTA |= (LED1 | LED2);  
  95.                 cntLedPonta = tmpOnPonta;  
  96.             }  
  97.         }  
  98.     }  
  99.     if (cntLedMeio != 0)  
  100.     {  
  101.         if (--cntLedMeio == 0)  
  102.         {  
  103.             if ((PORTA & LED3) != 0)  
  104.             {  
  105.                 PORTA &= ~(LED3 | LED4);  
  106.                 cntLedMeio = tmpOffMeio;  
  107.             }  
  108.             else  
  109.             {  
  110.                 PORTA |= (LED3 | LED4);  
  111.                 cntLedMeio = tmpOnMeio;  
  112.             }  
  113.         }  
  114.     }  
  115.       
  116.     // Click das teclas  
  117.     if (cntClick != 0)  
  118.     {  
  119.         if (--cntClick == 0)  
  120.         {  
  121.             PORTA &= ~BUZZER;  
  122.         }  
  123.     }  
  124. }  

O resto do código (inclusive as rotinas de manipulação do display) podem ser vistas baixando Timer.zip dos arquivos do blog.

O curioso com relação ao software foi que nos primeiros testes nada funcionada direito, o microcontrolador estava muito doido! Depois de muitos testes, descobri que o problema era que quando eu linkava os objetos não estava informando o modelo do microcontrolador. Corrigido isto, funcionou praticamente tudo.

Nenhum comentário: