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.
  1. /* 
  2.    Spoke-o-dometer 
  3.    Teste1 - teste do sensor 
  4.  
  5.    Daniel Quadros - abril, 2009 
  6. */  
  7.   
  8. #include <msp430.h>  
  9.   
  10. // Valor para contar 100ms c/ clock de 12KHz  
  11. #define TEMPO_100MS  1200   // 100ms * 12KHz  
  12.   
  13. typedef unsigned char  byte;  
  14.   
  15. // Rotinas Locais  
  16. static void Led (unsigned int valor);  
  17.   
  18. // Programa Principal  
  19. void main (void)  
  20. {  
  21.    int cont = 0;  
  22.    
  23.    // Desliga Watchdog  
  24.    WDTCTL = WDTPW + WDTSSEL + WDTHOLD;  
  25.   
  26.    // Altera a configuração de clock para ativar o VLOCLK  
  27.    BCSCTL3 |= LFXT1S1;  
  28.    
  29.    // Programa entradas e saídas  
  30.    P1SEL = 0xC0;               // P1.0 a P1.5 -> LEDs  
  31.                                // P1.6 e P1.7 são A3+ e A3-  
  32.    P1DIR = 0x3F;  
  33.    P1OUT = 0;                  // Todos as saídas em zero  
  34.      
  35.    P2SEL = 0;                  // Todos os pinos como I/O  
  36.    P2DIR = 0xFF;               // Todos os pinos como saída  
  37.    P2OUT = 0;                  // Todos as saídas em zero  
  38.    
  39.    // Programa o ADC  
  40.    // MSP430F2013 -> SD16  
  41.    SD16CTL = SD16VMIDON + SD16REFON + SD16SSEL_1;  // 1.2V ref, SMCLK  
  42.    SD16INCTL0 = SD16INCH_3;                        // PGA = 1x, Diff inputs A3- & A3+  
  43.    SD16CCTL0 =  SD16SNGL + SD16UNI + SD16IE;       // Single conversion, Unipolar, 256 SR, Int enable  
  44.    SD16CTL &= ~SD16VMIDON;                         // VMID off: used to settle ref cap  
  45.    SD16AE = SD16AE6 + SD16AE7;                     // P1.6 & P1.7: A3+/- SD16_A inputs  
  46.    
  47.    // Dá um tempinho para estabilizar a alimentação  
  48.    while (cont < 0xFF)  
  49.      cont++;  
  50.    
  51.    // Alimentação já deve estar estável, vamos ligar o DCO  
  52.    BCSCTL1 = CALBC1_1MHZ;  
  53.    DCOCTL  = CALDCO_1MHZ;  
  54.    
  55.    // Programar a interrupção de tempo real p/ cada 100ms  
  56.    TACCR0 = TEMPO_100MS;  
  57.    TACTL = TASSEL_1 + MC_1 + TAIE; // ACLK, up mode, interrupt  
  58.   
  59.    // O nosso programa principal vai ficar dormindo,  
  60.    // todo o tratamento será feito na interrupção  
  61.    _BIS_SR(LPM3_bits + GIE);        // Dorme tratando interrupção  
  62.    
  63.    // Nuca vai chegar aqui  
  64.    while (1)  
  65.      ;  
  66. }  
  67.   
  68. // Tratamento da interrupção do Timer A  
  69. #pragma vector=TIMERA1_VECTOR  
  70. __interrupt void Timer_A_TO(void)  
  71. {  
  72.    switch (TAIV)  
  73.    {  
  74.        case 10:        // overflow  
  75.            SD16CTL |= SD16REFON;       // Liga a referência do SD16  
  76.            SD16CCTL0 |= SD16SC;        // Inicia uma conversão  
  77.            _BIC_SR_IRQ (SCG1+SCG0);    // Manter osciladores ligados  
  78.            break;  
  79.        
  80.        case 2:         // CCR1, não utilizado  
  81.            break;  
  82.    }  
  83. }  
  84.   
  85. // Tratamento da interrupção do SD16  
  86. #pragma vector = SD16_VECTOR  
  87. __interrupt void SD16ISR(void)  
  88. {  
  89.    unsigned int result;  
  90.    
  91.    SD16CTL &= ~SD16REFON;        // Desliga referência do SD16_A  
  92.    result = SD16MEM0;            // Save result (clears IFG)  
  93.    
  94.    Led (result);  
  95.    
  96.    _BIS_SR_IRQ (SCG1+SCG0);      // voltar para LPM3  
  97. }  
  98.   
  99. // Mostra resultado nos LEDs  
  100. static void Led (unsigned int valor)  
  101. {  
  102.    // vamos mostrar somente os 7 bits mais significativos  
  103.    valor >>= 8;  
  104.    P1OUT = (P1OUT & 0xC0) | (valor & 0x3E);  
  105.    P2OUT = (P2OUT & 0x3F) | (valor & 0xC0);  
  106. }  

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: