O Gerador de Caracteres
Lembrando, o nosso circuito possui 7 LEDs que correspondem a uma coluna no nosso gerador de caracteres. Optei pelo formato 5x7 (5 colunas x 7 linhas) para a matriz de caracteres, que era bastante tradicional nos primeiros terminais de vídeos e assemelhados.
A figura abaixo mostra os desenhos que fiz para os dígitos:

- static const byte gc[10][5] =
- {
- 0x7C, 0x82, 0x82, 0x82, 0x7C, // 0
- 0x02, 0x42, 0xFE, 0x02, 0x02, // 1
- 0x46, 0x8A, 0x92, 0xA2, 0x42, // 2
- 0x44, 0x82, 0x92, 0x92, 0x6C, // 3
- 0xF0, 0x10, 0x10, 0x10, 0xFE, // 4
- 0xF2, 0x92, 0x92, 0x92, 0x8C, // 5
- 0x6C, 0x92, 0x92, 0x92, 0x0C, // 6
- 0x80, 0x86, 0x98, 0xA0, 0xC0, // 7
- 0x6C, 0x92, 0x92, 0x92, 0x6C, // 8
- 0x60, 0x92, 0x92, 0x92, 0x6C // 9
- };
Neste teste, resolvi aumentar o clock via DCO para 8MHz e usar uma interrupção periódica a cada 0,5 ms (o meu tick). A rotina de interrupção conta os ticks entre as detecções do imã (ou seja, mede o tempo de uma volta). Esta contagem é dividida em 360 frames (360 para ficar fácil pensar em graus). Cada coluna de caracter é apresentada por um frame; após cada caracter os LEDs ficam apagados por 3 frames, para dar uma separação entre os caracteres.
Os LEDs ficam também apagados nos 45 frames após o imã (para colocar a imagem numa posição fora do quadro) e ao final da mensagem.
Uma última complicação é que na posição em que eu montei os caracteres precisam ser escritos de "trás para frente" (da última coluna do último caracter para a primeira coluna do primeiro caracter). Por simplificação a "mensagem" é fixa e deverá já estar com os caracteres em ordem invertida (mais exatamente, a mensagem é "9876543210" o que aparece no programa como "0123456789".
O Código
O código nesta etapa ficou assim:
- /*
- Spoke-o-dometer
- Teste3 - Teste de Display
- Daniel Quadros - abril, 2009
- */
- #include <msp430.h>
- // Valor para contar 500us c/ clock de 12KHz
- #define TEMPO_500uS 6 // 0,5ms * 12KHz
- // Limites para detectar passagem pelo imã
- #define VREPOUSO 88 // 01011000
- #define VPOS_1 120 // 01111000
- #define VPOS_2 100 // 01111000
- #define VNEG_1 56 // 00111000
- #define VNEG_2 76 // 00111000
- #define FALSE 0
- #define TRUE 1
- typedef unsigned char byte;
- // Gerador de Caracter (versão numérica)
- // Cada caracter corresponde a 5 bytes
- // Cada byte corresponde a uma coluna
- static const byte gc[10][5] =
- {
- 0x7C, 0x82, 0x82, 0x82, 0x7C, // 0
- 0x02, 0x42, 0xFE, 0x02, 0x02, // 1
- 0x46, 0x8A, 0x92, 0xA2, 0x42, // 2
- 0x44, 0x82, 0x92, 0x92, 0x6C, // 3
- 0xF0, 0x10, 0x10, 0x10, 0xFE, // 4
- 0xF2, 0x92, 0x92, 0x92, 0x8C, // 5
- 0x6C, 0x92, 0x92, 0x92, 0x0C, // 6
- 0x80, 0x86, 0x98, 0xA0, 0xC0, // 7
- 0x6C, 0x92, 0x92, 0x92, 0x6C, // 8
- 0x60, 0x92, 0x92, 0x92, 0x6C // 9
- };
- static byte bDetectou = FALSE; // TRUE qdo detecta o imã
- static unsigned long tickVolta;
- static unsigned long tickFrame = 0;
- static unsigned long contFrame;
- static byte col;
- char *pMsg;
- #define NFRAMES 360
- #define POS 45
- #define SPACE 3
- // Programa Principal
- void main (void)
- {
- unsigned int cont = 0;
- // Desliga Watchdog
- WDTCTL = WDTPW + WDTSSEL + WDTHOLD;
- // Altera a configuração de clock para ativar o VLOCLK
- BCSCTL3 |= LFXT1S1;
- // Programa entradas e saídas
- P1SEL = 0xC0; // P1.0 a P1.5 -> LEDs
- // P1.6 e P1.7 são A3+ e A3-
- P1DIR = 0x3F;
- P1OUT = 0; // Todos as saídas em zero
- P2SEL = 0; // Todos os pinos como I/O
- P2DIR = 0xFF; // Todos os pinos como saída
- P2OUT = 0; // Todos as saídas em zero
- // Programa o ADC
- // MSP430F2013 -> SD16
- SD16CTL = SD16VMIDON + SD16REFON + SD16DIV_3 + SD16SSEL_1; // 1.2V ref, SMCLK / 8
- SD16INCTL0 = SD16INCH_3; // PGA = 1x, Diff inputs A3- & A3+
- SD16CCTL0 = SD16SNGL + SD16UNI + SD16IE; // Single conversion, Unipolar, 256 SR, Int enable
- SD16CTL &= ~SD16VMIDON; // VMID off: used to settle ref cap
- SD16AE = SD16AE6 + SD16AE7; // P1.6 & P1.7: A3+/- SD16_A inputs
- // Dá um tempinho para estabilizar a alimentação
- while (cont < 0xFF)
- cont++;
- cont = 0;
- // Alimentação já deve estar estável, vamos ligar o DCO
- BCSCTL1 = CALBC1_8MHZ;
- DCOCTL = CALDCO_8MHZ;
- // Programar a interrupção de tempo real p/ cada 0,5ms
- TACCR0 = TEMPO_500uS;
- TACTL = TASSEL_1 + MC_1 + TAIE; // ACLK, up mode, interrupt
- // O nosso programa principal vai ficar dormindo,
- // todo o tratamento será feito na interrupção
- _BIS_SR(LPM3_bits + GIE); // Dorme tratando interrupção
- // Nuca vai chegar aqui
- while (1)
- ;
- }
- // Tratamento da interrupção do Timer A
- #pragma vector=TIMERA1_VECTOR
- __interrupt void Timer_A_TO(void)
- {
- byte valor;
- switch (TAIV)
- {
- case 10: // overflow
- tickVolta++;
- if (tickFrame != 0)
- {
- if (contFrame == 0)
- {
- if (col > 0)
- {
- col--;
- valor = gc [*pMsg - '0'][col];
- P1OUT = (P1OUT & 0xC1) | (valor & 0x3E);
- P2OUT = (P2OUT & 0x3F) | (valor & 0xC0);
- contFrame = tickFrame;
- }
- else
- {
- P1OUT = P1OUT & 0xC1;
- P2OUT = P2OUT & 0x3F;
- col = 5;
- pMsg++;
- if (*pMsg == 0)
- tickFrame = 0;
- else
- contFrame = tickFrame * SPACE;
- }
- }
- else
- contFrame--;
- }
- SD16CTL |= SD16REFON; // Liga a referência do SD16
- SD16CCTL0 |= SD16SC; // Inicia uma conversão
- _BIC_SR_IRQ (SCG1+SCG0); // Manter osciladores ligados
- break;
- case 2: // CCR1, não utilizado
- break;
- }
- }
- // Tratamento da interrupção do SD16
- #pragma vector = SD16_VECTOR
- __interrupt void SD16ISR(void)
- {
- unsigned int result;
- SD16CTL &= ~SD16REFON; // Desliga referência do SD16_A
- result = SD16MEM0 >> 8; // Pega resultado (limpa IFG)
- if (!bDetectou && ((result > VPOS_1) || (result < VNEG_1)))
- {
- bDetectou = TRUE;
- tickFrame = tickVolta / NFRAMES;
- pMsg = "0123456789";
- contFrame = tickFrame*(NFRAMES - POS - 10*(5+SPACE));
- tickVolta = 0;
- col = 5;
- P1OUT |= 1;
- }
- else
- {
- if (bDetectou && (result < VPOS_2) && (result > VNEG_2))
- {
- bDetectou = FALSE;
- P1OUT &= 0xFE;
- }
- }
- _BIS_SR_IRQ (SCG1+SCG0); // voltar para LPM3
- }
O Resultado
Ainda está longe do que eu espero chegar, mas o vídeo abaixo é uma bela "prova de conceito".
2 comentários:
Sou um voyeur do seu blog, Daniel. Mas esse post ficou demais. São posts que me deixam com raiva, pois não entendi lhufas da lógica do código :(
Formação em analise de sistemas é uma droga ahsehasehas.
Grande abraço e parabéns.
Rapho,
Acho que a culpa é minha, que não coloquei uma boa explicação do código. Aliás, percebi isto também quando o meu filho quis dar uma mexidas nele. Fica a promessa de colocar uma explicação decente em um post futuro!
[]
Postar um comentário