Como ambiente de desenvolvimento vou usar o IAR Embedded Workbench Kickstart, que é fornecido junto com o programador EZ430-F2013 e pode também ser baixado gratuitamente do site da Texas. Esta versão grátis é limitada para gerar programas de até 4KBytes; como o modelo que estamos usando possui somente 2K de Flash, é mais que suficiente.
Existem, é claro, várias maneiras de estruturar o nossa programa. Optei por programar o timer para gerar interrupções a cada 50 milisegundos e colocar toda a lógica dentro do tratamento desta interrupção. Desta forma, na maior parte do tempo o processador estará parado em modo de economia de energia.
Para controlar o botão usei uma variável de tipo byte, onde 1 bit indica o estado do botão na leitura anterior e outro o estado após o debounce. Este segundo bit só é alterado após duas leituras consecutivas iguais.
Uma segunda variável do tipo byte armazena o estado do LED: apagado, aceso continuamente ou piscando. A cada pressionamento do botão o estado muda ciclicamente.
Sem mais delongas, eis o código em C:
- #include <msp430.h>
- typedef unsigned char byte;
- // Bits correspondentes aos pinos de E/S
- #define BOTAO 0x01
- #define LED 0x40
- // Valor para contar 50ms c/ clock de 12KHz
- #define TEMPO_50MS 600 // 50ms * 12KHz
- // Controle do LED
- static enum { APAGADO, ACESO, PISCANDO } ModoLed;
- // Controles do estado do botão
- #define BOTAO_ANTERIOR 0x01 // este bit indica o estado anterior
- #define BOTAO_APERTADO 0x02 // este bit tem o valor c/ "debounce"
- static byte ModoBotao;
- void main (void)
- {
- // Desliga Watchdog
- WDTCTL = WDTPW + WDTSSEL + WDTHOLD;
- // Altera a configuração de clock para ativar o VLOCLK
- BCSCTL3 |= LFXT1S1;
- // Programa entradas e saídas
- P1SEL = 0; // Todos os pinos como I/O
- P1DIR = 0xFF & ~BOTAO; // Somente o botão é entrada
- P1REN = BOTAO; // Usar resistor no botão
- P1OUT = BOTAO; // Resistor é pullup
- P2SEL = 0; // Todos os pinos como I/O
- P2DIR = 0xFF; // Todos os pinos como saída
- P2OUT = 0; // Todos as saídas em zero
- // Inicia os nossos controles
- ModoBotao = 0;
- ModoLed = APAGADO;
- // Alimentação já deve estar estável, vamos ligar o DCO
- BCSCTL1 = CALBC1_12MHZ;
- DCOCTL = CALDCO_12MHZ;
- // Programar a interrupção de tempo real p/ cada 50ms
- TACCR0 = TEMPO_50MS;
- TACTL = TASSEL_1 + MC_1 + TAIE; // ACLK, up mode, interrupt
- // O nosso programa principal vai ficar dormindo,
- // todo o tratamento será feito na interrupção
- _BIS_SR(LPM3_bits + GIE); // Dorme tratando interrupção
- // Nuca vai chegar aqui
- while (1)
- ;
- }
- // Tratamento da interrupção do Timer A
- #pragma vector=TIMERA1_VECTOR
- __interrupt void Timer_A_TO(void)
- {
- switch (TAIV)
- {
- case 10: // overflow
- // Trata o LED
- if (ModoLed == PISCANDO)
- P2OUT ^= LED;
- // Trata o botão
- if ((P1IN & BOTAO) == 0)
- {
- // Botao está apertado
- if (ModoBotao & BOTAO_ANTERIOR)
- {
- if ((ModoBotao & BOTAO_APERTADO) == 0)
- {
- // Acabamos de detectar o aperto
- ModoBotao |= BOTAO_APERTADO;
- switch (ModoLed)
- {
- case APAGADO:
- ModoLed = ACESO;
- P2OUT |= LED;
- break;
- case ACESO:
- ModoLed = PISCANDO;
- break;
- case PISCANDO:
- ModoLed = APAGADO;
- P2OUT &= ~LED;
- break;
- }
- }
- }
- else
- {
- // Vamos aguardar a confirmação
- ModoBotao |= BOTAO_ANTERIOR;
- }
- }
- else
- {
- if (ModoBotao & BOTAO_ANTERIOR)
- ModoBotao &= ~BOTAO_ANTERIOR; // aguarda confirmar
- else
- ModoBotao &= ~BOTAO_APERTADO; // confirmado
- }
- break;
- case 2: // CCR1, não utilizado
- break;
- }
- }
- </msp430.h>
- O include msp430.h define as constantes relativas ao microcontrolador. O modelo específico de microcontrolador é definido nas opções do projeto.
- O MSP430 inicia a execução com o watchdog ativo, portanto é necessário desativá-lo ou desarmá-lo periodicamente.
- Para o timer estou utilizando o VCLOCK, que é um clock interno lento (12KHz) e de baixo consumo. É somente este clock que vai estar ativo enquanto o processador aguarda uma interrupção. O VCLOCK não é muito preciso; para ter um clock preciso de baixo consumo é necessário colocar um cristal externo de 32KHz.
- Quando o processador está rodando é usado o DCO, que é um clock interno rápido (12MHz). Como parte do processo de fabricação são gravados na Flash parâmetros de calibração do DCO, desta forma esta clock é razoavelmente preciso.
- O MSP430 possui uma grande versatilidade nos pinos de entrada e saída digital. A recomendação da Texas é que os pinos não usados sejam programados como saída.
- O timer no MSP420F2011 é também muito flexível o que, neste caso, pode deixar a programação um pouco complicada. No caso é usado o up mode, no qual o timer conta de 0 até o valor em TACCR0. Ao atingir esta valor, o contador volta a zero e uma interrupção é gerada.
- A função _BIS_SR permite ligar bits do registrador de status. No caso são ligados os bits que colocam o processador para dormir com economia de energia (Low Power Mode 3) e o bit que permite interrupções. O resultado é que o processador fica parado na instrução seguinte, tratando interrupções. Quando uma interrupção ocorre, o registrador de status é salvo na pilha e o processador entra no modo normal de execução (LPM0). Ao final da interrupção o registrador de status é restaurado da pilha. Para o processamento continuar no programa principal uma interrupção precisaria alterar o registrador de status na pilha antes de retornar, o que não é feito neste programa.
- A pragma_vetor define que a rotina seguinte é uma rotina de tratamento de interrupção. O compilador cuida de colocar o endereço da rotina no vetor de interrupções e de colocar os preâmbulos e postâmbulos adequados no código.
- A interrupção do timer pode ser gerada por vários motivos, o registrador TAIV informa qual o motivo. No caso só estamos interessados na interrupção gerada quando o contador dá a volta.