quarta-feira, abril 22, 2009

Spoke-o-dometer - Parte 5

Nesta parte vamos testar o sensor. Para isto vamos examinar um pouco como funcionam o sensor e o conversor analógico SD16_A do MSP430. E, é claro, ver o código.

O Efeito Hall

O efeito Hall é o surgimento de uma diferença de pontecial elétrico em um condutor elétrico, transversa a uma corrente elétrica no condutor e a campo magnético perpendicular à corrente (tradução minha para a introdução em inglês de http://en.wikipedia.org/wiki/Hall_effect).

Simplificando, se pegamos um condutor elétrico alimentado por uma fonte e o submetemos a um campo magnético, surge no condutor uma diferença de potencial entre as bordas perpendiculares à corrente e ao campo magnético.

Sensor de Efeito Hall

O sensor de efeito Hall utiliza o efeito Hall descrito acima em um semicondutor para detectar um campo magnético. Em alguns sensores, como o que estou usando, obtem-se uma tensão proporcional ao campo magnético. Em outros obtem-se apenas uma indicação de presença ou ausência do campo magnético (o que bastaria para o meu projeto).

Alguns detalhes adicionais podem ser vistos (em inglês) na wikipedia (em http://en.wikipedia.org/wiki/Hall_effect_sensor); tem uma animação bonitinha.

O ADC SD16_A

Como descrito (para variar) na Wikipedia inglesa (http://en.wikipedia.org/wiki/Analog-to-digital_converter#ADC_structures) existem várias formas de implementar um Conversor Anaógico Digital (ADC). O objetivo é sempre fornecer um número proporcional a uma tensão.

O funcionamento de um ADC usando modulação Sigma Delta pode ser visto (em inglês) em http://en.wikipedia.org/wiki/Sigma-delta_modulation#Analog_to_Digital_Conversion. Se eu entendi direito, a tensão de entrada (sinal analógico) é usada para gerar pulsos de largura constante espaçados por um tempo proporcional à tensão de entrada (esta é a parte delta). Estes pulsos são contados em um tempo fixo (esta é a parte sigma), fornecendo assim um valor digital proporcional à tensão de entrada. Isto permite construir ADCs com precisão muito boa (o que não deixa de ser um desperdício para o meu projeto).

A programação do SD16_A do MSP430F2113 pode ser encontrada no MSP430x2xx Family User's Guide. Eu me basei no Application Note SLAA283A (que você pode baixar daqui).

A programação é feita escrevendo em quatro registradores do SD16_A:
  • SD16CTL: aqui selecionamos o clock e areferência. No nosso caso vamos usar como clock o SMCLK que virá do DCO. Como o limite do SD16_A é 1.1MHz, segui o Application Note e usei o DCO a 1MHz sem divisão. A referência que vamos usar é a interna (1.2V). Seguindo a recomendação do fabricante, o VMID buffer é ligado e pouco depois desligado para estabilizar o capacitor da referência.
  • SD16INCTL0: controle da entrada. Mantemos o default de esperar quatro amostragens antes de interromper, usamos um ganho de 1 e escolhemos a entrada A3.
  • SD16CCTL0: outro registrador de controle. Selecionamos o modo Single onde se dispara uma conversão e uma interrupção é gerada quando a conversão foi completada (a interrupção também é habilitada neste registrador). Pedimos o resultado na forma Unipolar, onde o resultado é um valor de 0 a 0xFFFF. Por último, usamos uma taxa de oversampling de 256.
  • SD16AE: Habilita os pinos de entrada analógica (no nosso caos, P1.6 e P1.7).
Feita a programação, para fazer uma leitura do sinal analógico entre os pinos P1.6 e P1.7, basta iniciar uma conversão e aguardar a interrupção (não esquecendo de manter o DCO e SMCLK rodando). Quando a interrupção ocorrer, o resultado está em SD16MEM0.

O Programa de Teste

O programa de teste usa os LEDs para mostrar (em binário) a tensão na saída do sensor. Para testar o sensor basta aproximar ou afastar um imã e observar o valor nos LEDs. Aproveitando a idéia da Application Note, o programa economia energia deixando o clock e a referência ligados somente enquanto efetua uma conversão.
/*
Spoke-o-dometer
Teste1 - teste do sensor

Daniel Quadros - abril, 2009
*/

#include <msp430.h>

// Valor para contar 100ms c/ clock de 12KHz
#define TEMPO_100MS 1200 // 100ms * 12KHz

typedef unsigned char byte;

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

// Programa Principal
void main (void)
{
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 + 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++;

// 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 100ms
TACCR0 = TEMPO_100MS;
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; // Save result (clears IFG)

Led (result);

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

// Mostra resultado nos LEDs
static void Led (unsigned int valor)
{
// vamos mostrar somente os 7 bits mais significativos
valor >>= 8;
P1OUT = (P1OUT & 0xC0) | (valor & 0x3E);
P2OUT = (P2OUT & 0x3F) | (valor & 0xC0);
}

No próximo post vamos prender o nosso circuito à roda da bicicleta e ver o funcionamento do sensor em uma situação mais real.

Nenhum comentário: