quarta-feira, maio 06, 2009

Spoke-o-dometer - Parte 7

Continuando a nossa série, vamos ver se conseguimos mostrar alguns caracteres com o circuito. Neste ponto vou tentar mostrar apenas números.

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:

No meu circuito, os LEDs correspondem (de cima para baixo) a P2.7, P2.6, P1.5, P1.4, P1.3, P1.2 e P1.1. Isto facilita guardar cada coluna em um byte, seguindo a mesma ordem de bits:
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
};
Timing

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:

Raphz disse...

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.

Daniel Quadros disse...

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!

[]