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:
   // Altera a configuração de clock para ativar o VLOCLK
BCSCTL3 |= LFXT1S1;

// SMCLK = MCLK / 8
BCSCTL2 = DIVS_3;

// Alimentação já deve estar estável, vamos ligar o DCO
BCSCTL1 = CALBC1_8MHZ;
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:
    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:

// 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

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:
    // Desliga Watchdog
WDTCTL = 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:

// Valor para contar 100us c/ clock de 1MHz
#define TEMPO_100uS 99 // (100us * 1MHz) - 1

// Programar a interrupção de tempo real p/ cada 100us
TACCR0 = TEMPO_100uS;
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:
// Tratamento da interrupção do Timer A
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A_TO(void)
{
byte valor;

switch (TAIV)
{
case 10: // overflow
// nosso tratamento
break;

case 2: // CCR1, não utilizado
break;
}
}
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:
    // Programa o ADC
// MSP430F2013 -> SD16
SD16CTL = SD16VMIDON + SD16REFON + SD16DIV_0 + 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
O SD16 é disparado de dentro da interrupção de tempo:
    if (!bAmostrando)
{
SD16CTL |= SD16REFON; // Liga a referência do SD16
SD16CCTL0 |= SD16SC; // Inicia uma conversão
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.
// Tratamento da interrupção do SD16
#pragma vector = SD16_VECTOR
__interrupt void SD16ISR(void)
{
unsigned int medida;

SD16CTL &= ~SD16REFON; // Desliga referência do SD16_A
medida = SD16MEM0 >> 8; // Pega medida (limpa IFG)
bAmostrando = FALSE; // Fim da amostragem

// trata a medida obtida
}
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: