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 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;
- LPM0; // Dorme tratando interrupção
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
- TCTL = 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 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;
- }
- }
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
- 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
- }
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