sexta-feira, abril 24, 2009

Spoke-o-dometer - Parte 6

Nesta parte vamos testar o circuito na roda de uma bicicleta. Mas antes vamos usar as informações do teste anterior para codificar a detecção de imã.

Detectando o Imã

Relembrando, o sensor usado fornece uma tensão proporcional ao campo magnético a que é submetido. Longe do imã, temos uma tensão de 4.5/2 V. À medida que o sensor se aproximar de um imã esta tensão irá baixar ou subir, dependendo do polo. Pelo teste anterior, a leitura do SD16 com o sensor longe do imã é de 88 (considerando somente o byte mais significativo do resultado).

Uma primeira ideia seria usar um limite único para detectar o imã. Por exemplo, considerar que quando o valor passar de 110 estamos próximos do imã. Entretanto, é provável que as leituras oscilem o que poderia causar uma sequência de detecções em uma única passagem pelo imã.

O mais seguro é colocar uma histerese no teste. Por exemplo, considerar que estamos próximos do imã quando o valor passar de 120 e só voltar a considerar que estamos longe quando o valor cair abaixo de 100. Desta forma, mesmo que o valor passe de 120, baixe para 110 e depois volte para 120 consideramos uma única detecção.

Para ficarmos independentes da polaridade do imã, usamos também dois valores abaixo do de repouso (neste teste, vamos detectar o imã quando o valor ficar abaixo de 56 e considerar que nos afastamos dele quando o valor ficar acima de 76).

Fixando o Circuito e o Imã

O circuito é fixado na válvula do pneu. A posição do imã é relativamente crítica, neste teste ele ficou preso ao freio.

O imã é o "botão" metálico na parte inferior central da foto. As pilhas (que não aprecem na foto), estão diametralmente opostas ao circuito porém mais próximas do centro.

O Software de Teste

O software de teste implementa a histerese mencionada acima. A cada detecção é incrementado um contador, que é apresentado nos LEDs.


/*
Spoke-o-dometer
Teste3 - Teste do sensor em movimento

Daniel Quadros - abril, 2009
*/

#include <msp430.h>

// Valor para contar 1ms c/ clock de 12KHz
#define TEMPO_1MS 12 // 1ms * 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;

static byte bDetectou = FALSE; // TRUE qdo detecta o imã
static unsigned int cont = 0;

// Rotinas Locais
static void Led (unsigned int valor);

// Programa Principal
void main (void)
{
// 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 + SD16SSEL_1; // 1.2V ref, SMCLK
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_1MHZ;
DCOCTL = CALDCO_1MHZ;

// Programar a interrupção de tempo real p/ cada 10ms
TACCR0 = TEMPO_1MS;
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)
{
switch (TAIV)
{
case 10: // overflow
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;
Led (++cont);
}
else if (bDetectou && (result < VPOS_2) && (result > VNEG_2))
bDetectou = FALSE;

_BIS_SR_IRQ (SCG1+SCG0); // voltar para LPM3
}

// Mostra resultado nos LEDs
static void Led (unsigned int valor)
{
// vamos mostrar somente os 7 bits menos significativos
valor &= 0x7F;
valor <<= 1;
P1OUT = (P1OUT & 0xC0) | (valor & 0x3E);
P2OUT = (P2OUT & 0x3F) | (valor & 0xC0);
}
Para finalizar, o vídeo abaixo mostra o teste em ação:

video

Na próxima parte (se tudo correr bem) vamos começar a mostrar mensagens.

Nenhum comentário: