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.
- 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.
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 VLOCLKA 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:
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;
LPM0; // Dorme tratando interrupçãoO 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 WatchdogTimer
WDTCTL = WDTPW + WDTSSEL + WDTHOLD;
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 AConversor Analógico Digital SD-16
#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;
}
}
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 ADCO SD16 é disparado de dentro da interrupção de tempo:
// 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
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 SD16Embora o conversor forneça um resultado de 16 bits, usamos apenas os 8 mais significativos.
#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
}
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:
Postar um comentário