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:
#includeAlguns comentários sobre o código:
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;
}
}
- 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.