quarta-feira, dezembro 31, 2008

Controlando um LED com um MSP430 - Parte II

Nesta parte vamos ver o software do nosso projeto.

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:
  1. #include <msp430.h>  
  2.   
  3. typedef unsigned char  byte;  
  4.   
  5. // Bits correspondentes aos pinos de E/S  
  6. #define BOTAO   0x01  
  7. #define LED     0x40  
  8.   
  9. // Valor para contar 50ms c/ clock de 12KHz  
  10. #define TEMPO_50MS  600     // 50ms * 12KHz  
  11.   
  12. // Controle do LED  
  13. static enum { APAGADO, ACESO, PISCANDO } ModoLed;  
  14.   
  15. // Controles do estado do botão  
  16. #define BOTAO_ANTERIOR 0x01 // este bit indica o estado anterior  
  17. #define BOTAO_APERTADO 0x02 // este bit tem o valor c/ "debounce"  
  18. static byte ModoBotao;  
  19.   
  20. void main (void)  
  21. {  
  22.    // Desliga Watchdog  
  23.    WDTCTL = WDTPW + WDTSSEL + WDTHOLD;  
  24.   
  25.    // Altera a configuração de clock para ativar o VLOCLK  
  26.    BCSCTL3 |= LFXT1S1;  
  27.     
  28.    // Programa entradas e saídas  
  29.    P1SEL = 0;                  // Todos os pinos como I/O  
  30.    P1DIR = 0xFF & ~BOTAO;      // Somente o botão é entrada  
  31.    P1REN = BOTAO;              // Usar resistor no botão  
  32.    P1OUT = BOTAO;              // Resistor é pullup  
  33.       
  34.    P2SEL = 0;                  // Todos os pinos como I/O  
  35.    P2DIR = 0xFF;               // Todos os pinos como saída  
  36.    P2OUT = 0;                  // Todos as saídas em zero  
  37.     
  38.    // Inicia os nossos controles  
  39.    ModoBotao = 0;  
  40.    ModoLed = APAGADO;  
  41.     
  42.    // Alimentação já deve estar estável, vamos ligar o DCO  
  43.    BCSCTL1 = CALBC1_12MHZ;  
  44.    DCOCTL  = CALDCO_12MHZ;  
  45.     
  46.    // Programar a interrupção de tempo real p/ cada 50ms  
  47.    TACCR0 = TEMPO_50MS;  
  48.    TACTL = TASSEL_1 + MC_1 + TAIE; // ACLK, up mode, interrupt  
  49.   
  50.    // O nosso programa principal vai ficar dormindo,  
  51.    // todo o tratamento será feito na interrupção  
  52.    _BIS_SR(LPM3_bits + GIE);     // Dorme tratando interrupção  
  53.     
  54.    // Nuca vai chegar aqui  
  55.    while (1)  
  56.      ;  
  57. }  
  58.   
  59. // Tratamento da interrupção do Timer A  
  60. #pragma vector=TIMERA1_VECTOR  
  61. __interrupt void Timer_A_TO(void)  
  62. {  
  63.    switch (TAIV)  
  64.    {  
  65.        case 10:        // overflow  
  66.             
  67.            // Trata o LED  
  68.            if (ModoLed == PISCANDO)  
  69.                P2OUT ^= LED;  
  70.             
  71.            // Trata o botão  
  72.            if ((P1IN & BOTAO) == 0)  
  73.            {  
  74.                // Botao está apertado  
  75.                if (ModoBotao & BOTAO_ANTERIOR)  
  76.                {  
  77.                    if ((ModoBotao & BOTAO_APERTADO) == 0)  
  78.                    {  
  79.                        // Acabamos de detectar o aperto  
  80.                        ModoBotao |= BOTAO_APERTADO;  
  81.                        switch (ModoLed)  
  82.                        {  
  83.                            case APAGADO:  
  84.                                ModoLed = ACESO;  
  85.                                P2OUT |= LED;  
  86.                                break;  
  87.                            case ACESO:  
  88.                                ModoLed = PISCANDO;  
  89.                                break;  
  90.                            case PISCANDO:  
  91.                                ModoLed = APAGADO;  
  92.                                P2OUT &= ~LED;  
  93.                                break;  
  94.                        }  
  95.                    }  
  96.                }  
  97.                else  
  98.                {  
  99.                    // Vamos aguardar a confirmação  
  100.                    ModoBotao |= BOTAO_ANTERIOR;  
  101.                }  
  102.            }  
  103.            else  
  104.            {  
  105.                if (ModoBotao & BOTAO_ANTERIOR)  
  106.                    ModoBotao &= ~BOTAO_ANTERIOR;   // aguarda confirmar  
  107.                else  
  108.                    ModoBotao &= ~BOTAO_APERTADO;   // confirmado  
  109.            }  
  110.            break;  
  111.         
  112.        case 2:         // CCR1, não utilizado  
  113.            break;  
  114.    }  
  115. }  
  116.   
  117. </msp430.h>  
Alguns comentários sobre o código:
  • 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.
Na próxima parte vamos ver este mesmo código em Assembly; na quarta e última parte veremos como montar o circuito, compilar o código e gravá-lo no microcontrolador.

2 comentários:

Henrique disse...

Daniel , quando eu utilizo o cristal de 32k que vem com a placa do launchpad , o sistema fica muito sensível , não posso conectá-lo a protoboard nem mesmo tocar na malha do gnd ou acionar algo com as portas que não seja os leds do launchpad que o microcontrolador trava a execução , como se o cristal parasse de ressoar ... Você já passou por isso ?

Daniel Quadros disse...

Henrique, não usei ainda o cristal de 32K na launchpad. Normalmente é recomendado aterrar a carcaça do cristal.