terça-feira, julho 31, 2012

Convertendo o Display JY-MCU3208 em um Timer / Cronômetro

Vamos dar um uso prático para o display JY-MCU3208, transformando-o em um timer e cronômetro. A inspiração é um programa de PC que eu fiz algum tempo atrás.


Operação

O tempo no display esta no formato MM:SS (minutos : segundos). A operação é bastante simples e utiliza os três botões do display:
  • O primeiro botão é usado para parar e inciar a contagem. Se o valor no display for zero, será ativada a função de cronômetro, com o valor sendo incrementado a cada segundo (ao atingir 99:59 voltará a zero e continuará contando). Se o valor for diferente de zero ele será decrementado a cada segundo, parando quando atingir zero (modo timer).
  • O segundo botão incrementa os segundos, quando o timer estiver parado. A contagem vai de 0 a 59.
  • O terceiro botão incrementaros minutos, quando o timer estiver parado. A contragem vai de 0 a 99.
A lógica utilizada trata os botões quando eles são soltos.

Código

O código aproveita o gerador de caracteres e a rotina de escrita anteriores, acrescentando o caracter "dois pontos":
// Matriz de Caracteres 5x7
// Contem os dígitos de 0 a 9 e dois pontos
uint8_t digitos [11][7] PROGMEM =
{
    { 0x0E, 0x11, 0x15, 0x15, 0x15, 0x11, 0x0E },
    { 0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x1F },
    { 0x0E, 0x11, 0x02, 0x04, 0x08, 0x10, 0x1F },
    { 0x0E, 0x11, 0x01, 0x06, 0x01, 0x11, 0x0E },
    { 0x11, 0x11, 0x11, 0x1F, 0x01, 0x01, 0x01 },
    { 0x1F, 0x10, 0x10, 0x1E, 0x01, 0x11, 0x0E },
    { 0x06, 0x08, 0x10, 0x1E, 0x11, 0x11, 0x0E },
    { 0x1F, 0x11, 0x01, 0x02, 0x04, 0x04, 0x04 },
    { 0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E },
    { 0x0E, 0x11, 0x11, 0x0F, 0x01, 0x02, 0x0C },
    { 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00 }
};

// Mostra o digito d a partir da posição x
static void display (uint8_t x, uint8_t d)
{
    uint8_t i, y, mask, gc;
    
    for (y = 0; y < 7; y++)
    {
        gc = pgm_read_byte(&(digitos[d][y]));
        mask = 0x10;
        for (i = 0; i < 5; i++)
        {
            ht1632c_setLED (x+i, y, gc & mask);
            mask = mask >> 1;
        }
    }
}
Um vetor guarda o valor atual do display e uma variável o modo atual de operação.
// Variaveis
static volatile uint8_t tempo[5];
static volatile enum { PARADO, SUBINDO, DESCENDO } modo;
O programa principal inicia as variáveis e o hardware.
// Programa principal
int main(void)
{
    uint8_t i;
    
    // Zera o relogio
    tempo[0] = 0;
    tempo[1] = 0;
    tempo[2] = 10;
    tempo[3] = 0;
    tempo[4] = 0;
    modo = PARADO;
    for (i = 0; i < 5; i++)
        display (6*i, tempo[i]);

    // Liga pullup das teclas
    TEC_DDR &= ~(TEC_KEY1|TEC_KEY2|TEC_KEY3);
    TEC_PORT |= TEC_KEY1|TEC_KEY2|TEC_KEY3;
    
    ht1632c_init();             // inicia o controlador do display
    ht1632c_send_screen ();     // limpa o display
    tempo_init();               // inicia a contagem de tempo

    // Loop perpétuo - somente tratando a interrupção de timer
    for (;;)
    {
    }
}

// Inicia a contagem de tempo
static void tempo_init (void)
{
  ASSR |= (1<<AS2);     // timer2 async from external quartz
  TCCR2 = 0b00000010;   // normal,off,/8; 32768Hz/256/8 = 16 Hz
  TIMSK |= (1<<TOIE2);  // enable timer2 overflow int
  sei();                // enable interrupts
}
A ação toda está na rotina de interrupção do timer.
// Interrupção do Timer2
ISR(TIMER2_OVF_vect) 
{
    static uint8_t cnt_seg = 16;
    static uint8_t teclas = TEC_KEY1|TEC_KEY2|TEC_KEY3;
    uint8_t mudou = FALSE;
    uint8_t i;

    if (--cnt_seg == 0)
    {
        cnt_seg = 16;
        
        // Atualiza a contagem
        if (modo == SUBINDO)
        {
            mudou = TRUE;
            if (++tempo[4] == 10)
            {
                tempo[4] = 0;
                if (++tempo[3] == 6)
                {
                    tempo[3] = 0;
                    if (++tempo[1] == 10)
                    {
                        tempo[1] = 0;
                        if (++tempo[0] == 10)
                            tempo[0] = 0;
                    }
                }
            }
        }
        else if (modo == DESCENDO)
        {
            mudou = TRUE;
            if (tempo[4] == 0)
            {
                tempo[4] = 9;
                if (tempo[3] == 0)
                {
                    tempo[3] = 5;
                    if (tempo[1] == 0)
                    {
                        tempo[1] = 9;
                        tempo[0]--;
                    }
                    else
                        tempo[1]--;
                }
                else
                    tempo[3]--;
            }
            else
                tempo[4]--;
            if ((tempo[0] + tempo[1] + tempo[3] + tempo[4]) == 0)
                modo = PARADO;
        }
    }
    
    // Trata as teclas
    if (((teclas & TEC_KEY1) == 0) && ((TEC_PIN & TEC_KEY1) != 0))
    {
        // soltou tecla de modo
        if (modo == PARADO)
        {
            if ((tempo[0] + tempo[1] + tempo[3] + tempo[4]) == 0)
                modo = SUBINDO;
            else
                modo = DESCENDO;
        }
        else
            modo = PARADO;
    }
    if (modo == PARADO)
    {
        if (((teclas & TEC_KEY2) == 0) && ((TEC_PIN & TEC_KEY2) != 0))
        {
            mudou = TRUE;
            if (++tempo[4] == 10)
            {
                tempo[4] = 0;
                if (++tempo[3] == 6)
                    tempo[3] = 0;
            }
        }
        if (((teclas & TEC_KEY3) == 0) && ((TEC_PIN & TEC_KEY3) != 0))
        {
            mudou = TRUE;
            if (++tempo[1] == 10)
            {
                tempo[1] = 0;
                if (++tempo[0] == 10)
                    tempo[0] = 0;
            }
        }
    }
    teclas = TEC_PIN;
    
    if (mudou)
    {
        // Atualiza o display
        for (i = 0; i < 5; i++)
            display (6*i, tempo[i]);
    }
   
}
Uma sugestão de aperfeiçoamento é  detectar o acionamento de dois botões e zerar o display nesta condição.

Outra melhoria possível é fazer o display piscar quando estiver no modo timer e a contagem chegar a zero. Pode-se também conectar um alto-falante ou buzzer para gerar um alarme sonoro.

O Timer em Funcionamento

O vídeo abaixo mostra o resultado obtido.


Nenhum comentário: