terça-feira, agosto 21, 2012

Piscando um LED com o ATtiny85

Vamos cumprir o ritual de todo microcontrolador novo: piscar um LED. Este é um exercício simples mas que requer uma boa leitura do datasheet, a montagem de um circuito básico e a conexão de um gravador.


Hardware

O hardware é uma adaptação do montado para o ATtiny2313 (afinal, ainda estava na protoboard). Foram usados todos os oito pinos do ATtiny85:
  • Dois pinos para a alimentação
  • Um pino para reset
  • Três pinos para a gravação da flash
  • Um pino para a tecla e outro para o LED
Tipicamente se abdica da programação no circuito para liberar mais três pinos de E/S. O pino de reset também pode ser usado para E/S, porém aí não dá mais para fazer a programação low voltage.

Como no projeto anterior a alimentação será com uma bateria de 3V e o clock será o interno de 8MHz dividido por 8. Os fuses do ATtiny serão mantidos na programação de fábrica (que você pode conferir no manual ou neste site).

Software

O software é praticamente o mesmo do ATtiny2313,  apenas trocando as portas onde estão ligados o LED e a tecla.
  1. #include <inttypes.h>  
  2. #include <avr/io.h>  
  3. #include <avr/interrupt.h>  
  4. #include <avr/sleep.h>  
  5.   
  6. // Bits correspondentes aos pinos de E/S  
  7. #define BOTAO   _BV(PB3)  
  8. #define LED     _BV(PB4)  
  9.   
  10. // Valor para contar (aproximadamente) 50ms  
  11. #define TEMPO_50MS  3  
  12.   
  13. // Controle do LED  
  14. static enum { APAGADO, ACESO, PISCANDO } ModoLed;  
  15.   
  16. // Controles do estado do botão  
  17. #define BOTAO_ANTERIOR 0x01 // este bit indica o estado anterior  
  18. #define BOTAO_APERTADO 0x02 // este bit tem o valor c/ "debounce"  
  19. static unsigned char ModoBotao;  
  20.   
  21. // Programa Principal  
  22. int main (void)  
  23. {  
  24.     // Pino do LED é saída  
  25.     DDRB |= LED;  
  26.     PORTB = 0;  
  27.       
  28.     // Pino do Botão é entrada com pullup  
  29.     DDRB &= ~BOTAO;  
  30.     PORTB |= BOTAO;  
  31.   
  32.     // Configura o timer 0  
  33.     TCCR0A = 0;                         // Modo normal: overflow a cada 256 contagens  
  34.     TCCR0B = _BV(CS01) | _BV(CS00);     // Usar clkIO/64: int a cada 64*256/1000 ms  
  35.                                         //   = 16,384 ms  
  36.     TIMSK = _BV(TOIE0);                // Interromper no overflow  
  37.   
  38.     // Inicia os nossos controles  
  39.     ModoBotao = 0;  
  40.     ModoLed = PISCANDO;  
  41.       
  42.     // Permite interrupções  
  43.     sei ();  
  44.       
  45.     // Loop infinito  
  46.     for (;;)  
  47.     {  
  48.         set_sleep_mode(SLEEP_MODE_IDLE);  
  49.         sleep_mode();  
  50.     }  
  51. }  
  52.   
  53. // Tratamento da interrupção do timer 0  
  54. ISR (TIMER0_OVF_vect)  
  55. {  
  56.     static unsigned char cnt = TEMPO_50MS;  
  57.       
  58.     // Aguarda 50 ms  
  59.     if (--cnt != 0)  
  60.         return;  
  61.     cnt = TEMPO_50MS;  
  62.       
  63.     // Trata o LED  
  64.     if (ModoLed == PISCANDO)  
  65.         PORTB ^= LED;  
  66.       
  67.     // Trata o botão  
  68.     if ((PINB & BOTAO) == 0)  
  69.     {  
  70.         // Botao está apertado  
  71.         if (ModoBotao & BOTAO_ANTERIOR)  
  72.         {  
  73.             if ((ModoBotao & BOTAO_APERTADO) == 0)  
  74.             {  
  75.                 // Acabamos de detectar o aperto  
  76.                 ModoBotao |= BOTAO_APERTADO;  
  77.                 switch (ModoLed)  
  78.                 {  
  79.                     case APAGADO:  
  80.                         ModoLed = ACESO;  
  81.                         PORTB |= LED;  
  82.                         break;  
  83.                     case ACESO:  
  84.                         ModoLed = PISCANDO;  
  85.                         break;  
  86.                     case PISCANDO:  
  87.                         ModoLed = APAGADO;  
  88.                         PORTB &= ~LED;  
  89.                         break;  
  90.                 }  
  91.             }  
  92.         }  
  93.         else  
  94.         {  
  95.             // Vamos aguardar a confirmação  
  96.             ModoBotao |= BOTAO_ANTERIOR;  
  97.         }  
  98.     }  
  99.     else  
  100.     {  
  101.         if (ModoBotao & BOTAO_ANTERIOR)  
  102.             ModoBotao &= ~BOTAO_ANTERIOR;   // aguarda confirmar  
  103.         else  
  104.             ModoBotao &= ~BOTAO_APERTADO;   // confirmado  
  105.     }  
  106. }  
Um makefile automatiza a geração e gravação do HEX. No caso estou usando o gravador USBtinyISP,

PROGNAME=Led85
AVRDUDEFLAGS=-c usbtiny -p attiny85

all: ${PROGNAME}.hex

program: ${PROGNAME}-upload

${PROGNAME}.hex: ${PROGNAME}.obj
    avr-objcopy -R .eeprom -O ihex ${PROGNAME}.obj ${PROGNAME}.hex

${PROGNAME}.obj: ${PROGNAME}.c
    avr-gcc -g -Os -Wall -mcall-prologues -mmcu=attiny85 \
    -Wl,-Map,${PROGNAME}.map -o ${PROGNAME}.obj ${PROGNAME}.c

${PROGNAME}-upload: ${PROGNAME}.hex
    avrdude ${AVRDUDEFLAGS} -U flash:w:${PROGNAME}.hex:i

Testes

A montagem foi muito simples: retirei o ATtiny2313, espetei o ATtiny85, movi três conexões (terra, tecla e LED). Aí foi só gravar a Flash e ligar a alimentação para ver o LED piscando.

Esta é a vantagem de famílias bem planejadas de microcontroladores: você adapta facilmente o projeto de um modelo para outro.

Nenhum comentário: