segunda-feira, junho 15, 2009

Spoke-o-dometer - Parte 10

Neste post vou explicar a parte do código do Spoke-o-dometer (SOD) relacionada à programação dos periféricos do microcontrolador. A documentação da Texas a respeito está disponível aqui em formato PDF. Sigo abaixo a mesma ordem que a documentação.

A maior dificuldade nesta programação está na grande quantidade de opções disponíveis. Para cada módulo do microcontrolador é necessário escolher a opção mais apropriada, ver quais as opções default após o reset e alterar os bits necessários nos registradores que controlam as opções.

Clock

O MSP430F2013 que estou usando permite usar até três fontes para o clock:
  • LFXT1CLK: clock de baixa frequência gerado por um cristal externo. Não usado no SOD.
  • VCLOCK: clock interno de 12KHz. Usei no início para gerar as interrupções de tempo; na versão final não é usado.
  • DCOCLK: clock interno de alta frequência. Programado para 8MHz no SOD.
Estas fontes podem ser conectadas a três sinais de clock que alimentarão o processador e os periféricos. Esta conexão pode ser direta ou através de um divisor. Os sinais de clock são:
  • MCLK: master clock, alimenta o processador. No nosso caso, é o SCOCLK (8MHz).
  • SMCLK: sub-main clock, é uma das alternativas de clock para os periféricos. No SOD o SMCLK é configurado para DCOCLK/8 (1MHz) e será usado para o timer e para o conversor analógico digital (ADC).
  • ACLK: clock auxiliar (normalmente de baixa frequência). No SOD é configurado para VCLOCK e era usado inicialmente com o timer. Na versão final não é usado.
Os clocks estão diretamente ligados ao consumo de energia. Quando o processador está rodando, todos os clocks estão ativos e o consumo é máximo. O processador pode ser colocado para dormir aguardando interrupções; o MCLK é desligado e os demais clocks podem ser desligados se não estiverem ligados aos periféricos que irão acordar o processador.

Nos testes iniciais eu estava usando um gerenciamento de energia mais sofisticado. O SMCLK só era mantido ligado com o processador dormindo quando o ADC estava ligado. Quando o ADC estava inativo somente ACLK ficava ligado, deixando o consumo muito próximo de zero.

Na versão final o timer está sendo alimentado pelo SMCLK portanto estou deixando SMCLK e ACLK sempre ligados.

A programação dos clocks é feita nas linhas abaixo:
  1.    // Altera a configuração de clock para ativar o VLOCLK  
  2. BCSCTL3 |= LFXT1S1;  
  3.   
  4. // SMCLK = MCLK / 8  
  5. BCSCTL2 = DIVS_3;  
  6.   
  7. // Alimentação já deve estar estável, vamos ligar o DCO  
  8. BCSCTL1 = CALBC1_8MHZ;  
  9. DCOCTL  = CALDCO_8MHZ;  
A macro abaixo coloca o processador no modo 0 de economia de energia, no qual o processador fica parado mas os clocks SMCLK e ACLK ficam ligados:
  1. LPM0;             // Dorme tratando interrupção  
O controle da economia de energia fica no registrador de status. Quando uma interrupção ocorre, este registrador é salvo na pilha e todos os clocks são ligados. Ao encerrar o tratamento de uma interrupção o registrador de status é restaurado da pilha; se nada especial for feito o processador volta a dormir no mesmo modo que estava antes. Isto é suficiente para a versão final do código.

Nas versões de teste, onde eu mudava dinamicamente de modo de economia de energia, era preciso usar as macros _BIC_SR_IRQ e _BIS_SR_IRQ para mudar a imagem do registrador na pilha.

E/S Digital

Esta parte é simples. o MSP430F2013 possui 10 pinos que podem ser usados para entrada e saída digital. Oito deles compõem o chamado P1 e dois deles o P2 (que usa somente os dois bits mais significativos).

Em P1 os bits 0 a 5 são programados para saída e correspondem a LEDs (o bit 0 é o LED verde na placa do processador; os demais são LEDs vermelhos do SOD). Os bits 6 e 7 são as entradas do ADC.

Em P2 os bits 6 e 7 são programados para saída e correspondem a LEDs do SOD.

A programação fica assim:
  1. // Programa entradas e saídas  
  2. P1SEL = 0xC0;               // P1.0 a P1.5 -> LEDs  
  3.                             // P1.6 e P1.7 são A3+ e A3-  
  4. P1DIR = 0x3F;  
  5. P1OUT = 0;                  // Todos as saídas em zero  
  6.   
  7. P2SEL = 0;                  // Todos os pinos como I/O  
  8. P2DIR = 0xFF;               // Todos os pinos como saída  
  9. P2OUT = 0;                  // Todos as saídas em zero  

PxSEL indica quais pinos serão de E/S digital (bit em 1) e quais serão usados para funções especiais (bit em 0). PxDIR indica se o pino é de entrada (bit em 0) ou saída (bit em 1). PxOUT controla os pinos programados para saída digital.

Watchdog Timer

O WDT é uma segurança contra programas perdidos: quando ativo o software precisa periodicamente re-armá-lo para o processador não ser reiniciado. Após um reset o WDT é ligado; no SOD estamos simplesmente desligando-o:
  1. // Desliga Watchdog  
  2. TCTL = WDTPW + WDTSSEL + WDTHOLD;  
Timer

O MSP430F2013 possui um timer bastante versátil. No SOD vamos usá-lo somente para gerar uma interrupção periódica.

Isto é feito colocando-o no up mode: o timer conta de zero até um valor final (inclusive), gera a interrupção e volta a contar a partir de zero.

Uso como clock para o timer o SMCLK (1MHz) e programo o valor final em 99. Desta forma, uma interrupção é gerada a cada 100 pulsos do clock, o que resulta em um intervalo de 100 micro-segundos:
  1. // Valor para contar 100us c/ clock de 1MHz  
  2. #define TEMPO_100uS 99   // (100us * 1MHz) - 1  
  3.   
  4.   // Programar a interrupção de tempo real p/ cada 100us  
  5.   TACCR0 = TEMPO_100uS;  
  6.   TACTL = TASSEL_2 + MC_1 + TAIE; // SCLK, up mode, interrupt  

A rotina de tratamento da interrupção deve ser marcada através do pragma vector=TIMERA1_VECTOR. O timer pode gerar interrupções por diversos motivos, o registrador TAIV informa qual. O que nos interessa é o overflow:
  1. // Tratamento da interrupção do Timer A  
  2. #pragma vector=TIMERA1_VECTOR  
  3. __interrupt void Timer_A_TO(void)  
  4. {  
  5.   byte valor;  
  6.   
  7.   switch (TAIV)  
  8.   {  
  9.       case 10:        // overflow  
  10.           // nosso tratamento  
  11.           break;  
  12.       
  13.       case 2:         // CCR1, não utilizado  
  14.           break;  
  15.   }  
  16. }  
Conversor Analógico Digital SD-16

Expliquei em detalhes o SD-16 na parte 5. A única alteração é que na versão final o DCO está a 8MHz mas o SMCLK continua sendo 1MHz. Em algumas versões intermediárias usei o SMCLK a 8MHz e programaei o SD-16 para dividí-lo por 8.

O código permanece assim:
  1.   // Programa o ADC  
  2. // MSP430F2013 -> SD16  
  3. SD16CTL = SD16VMIDON + SD16REFON + SD16DIV_0 + SD16SSEL_1;  // 1.2V ref, SMCLK  
  4. SD16INCTL0 = SD16INCH_3;                        // PGA = 1x, Diff inputs A3- & A3+  
  5. SD16CCTL0 =  SD16SNGL + SD16UNI + SD16IE;       // Single conversion, Unipolar, 256 SR, Int enable  
  6. SD16CTL &= ~SD16VMIDON;                     // VMID off: used to settle ref cap  
  7. SD16AE = SD16AE6 + SD16AE7;                     // P1.6 & P1.7: A3+/- SD16_A inputs  
O SD16 é disparado de dentro da interrupção de tempo:
  1. if (!bAmostrando)  
  2.   
  3.   SD16CTL |= SD16REFON;       // Liga a referência do SD16  
  4.   SD16CCTL0 |= SD16SC;        // Inicia uma conversão  
  5.   bAmostrando = TRUE;  

Quando a conversão é concluída, uma interrupção é gerada e o resultado está em SD16MEM0. Para economia de energia mantemos a referência ligada somente durante a conversão.
  1. // Tratamento da interrupção do SD16  
  2. #pragma vector = SD16_VECTOR  
  3. __interrupt void SD16ISR(void)  
  4. {  
  5.   unsigned int medida;  
  6.   
  7.   SD16CTL &= ~SD16REFON;      // Desliga referência do SD16_A  
  8.   medida = SD16MEM0 >> 8;   // Pega medida (limpa IFG)  
  9.   bAmostrando = FALSE;            // Fim da amostragem  
  10.   
  11.  // trata a medida obtida  
  12. }  
Embora o conversor forneça um resultado de 16 bits, usamos apenas os 8 mais significativos.


Com isto espero ter explicado toda a parte do código relacionada à programação dos periféricos do microcontrolador. No último post da série vou explicar a lógica do velocímetro.

Nenhum comentário: