segunda-feira, setembro 09, 2013

LEDCube Kit v2: Programação II

Já vimos a biblioteca padrão, onde controlamos separadamente cada LED. Veremos agora uma forma alternativa de programação, na qual definimos em uma tabela os padrões a serem apresentados.


Cada frame é definido por três palavras de 16 bits, cada uma controlando os 9 LEDs de um plano:
  1. // Esta tabela define a animação  
  2. // Cade frame determina o estado dos LEDs nos três planos  
  3. uint16_t const frameTab[][3] PROGMEM =   
  4. {  
  5.   { 0b100000000,  0b000000000, 0b000000000 },  
  6.   { 0b010000000,  0b000000000, 0b000000000 },  
  7.   { 0b001000000,  0b000000000, 0b000000000 },  
  8.   { 0b000100000,  0b000000000, 0b000000000 },  
  9.   { 0b000010000,  0b000000000, 0b000000000 },  
  10.   { 0b000001000,  0b000000000, 0b000000000 },  
  11.   { 0b000000100,  0b000000000, 0b000000000 },  
  12.   { 0b000000010,  0b000000000, 0b000000000 },  
  13.   { 0b000000001,  0b000000000, 0b000000000 },  
  14.   { 0b000000000,  0b000000000, 0b000000000 },  
  15.   
  16.   { 0b111111111,  0b000000000, 0b000000000 },  
  17.   { 0b000000000,  0b111111111, 0b000000000 },  
  18.   { 0b000000000,  0b000000000, 0b111111111 },  
  19.   { 0b000000000,  0b000000000, 0b000000000 },  
  20.   
  21.   { 0b111000000,  0b111000000, 0b111000000 },  
  22.   { 0b000111000,  0b000111000, 0b000111000 },  
  23.   { 0b000000111,  0b000000111, 0b000000111 },  
  24.   { 0b000000000,  0b000000000, 0b000000000 },  
  25.   
  26.   { 0b100100100,  0b100100100, 0b100100100 },  
  27.   { 0b010010010,  0b010010010, 0b010010010 },  
  28.   { 0b001001001,  0b001001001, 0b001001001 },  
  29.   { 0b000000000,  0b000000000, 0b000000000 }  
  30. };  
  31. #define NUM_FRAMES (sizeof(frameTab)/sizeof(frameTab[0]))  
A rotina setup() inicia as portas e o Timer1 do ATmega; a rotina loop() não precisa fazer nada:
  1. // Rotinas locais  
  2. static void portInit(void);  
  3. static void startTimer1(void);  
  4.   
  5. // Iniciação do sketch  
  6. void setup ()   
  7. {  
  8.   portInit();  
  9.   startTimer1();  
  10. };  
  11.   
  12. // Laço principal do sketch  
  13. void loop ()   
  14. {  
  15.   // Nada a fazer - toda a ação ocorre na interrupção  
  16. };  
As rotinas de iniciação das portas e do Timer1 são as mesmas da biblioteca padrão:
  1. /* 
  2.  * Copyright (c) 2011 - Rolf van Widenfelt, Mitch Altman.  Some rights reserved. 
  3.  * 
  4.  * Note: This source code is licensed under a Creative Commons License, CC-by-nc-sa. 
  5.  *  (attribution, non-commercial, share-alike) 
  6.  *   see http://creativecommons.org/licenses/by-nc-sa/3.0/ for details. 
  7. */  
  8. //  Inicia os ports de E/S  
  9. static void portInit(void)  
  10. {  
  11.  // note: these MUST be in sync with actual hardware!  
  12.  // note: DDR pins are set to "1" to be an output, "0" for input.  
  13.   
  14.  //          76543210  
  15.  //PORTB = 0b00000001;  // initial: set to enable pullups on inputs: BUT1 pullup  
  16.  //DDRB  = 0b00100000;  // inputs: BUT1 (PB0); outputs: SCK (PB5); reserved (PB6, PB7)  
  17.  PORTB = 0x01;   // (see above)  
  18.  DDRB  = 0x30;   // (see above)  
  19.    
  20.  //          76543210  
  21.  //PORTC = 0b00000000;  // initial: no pullups on inputs  
  22.  //DDRC  = 0b00111111;  // outputs: CC33-CC12 (PC0-PC5)  
  23.  PORTC = 0x00;  // (see above)  
  24.  DDRC  = 0x3F;  // (see above)  
  25.    
  26.  //          76543210  
  27.  //PORTD = 0b00000000;  // initial: no pullups on inputs  
  28.  //DDRD  = 0b11111100;  // outputs: PP3-PP1 (PD2-PD4), CC31-CC11 (PD5-PD7); reserved (PD0/RxD,PD1/TxD)  
  29.  PORTD = 0x00;  // (see above)  
  30.  DDRD  = 0xFC;  // (see above)  
  31. }  
  32.   
  33. //  
  34. // set up timer1 in "fast PWM" mode 14 (see waveform generation, pg 137 of atmega168/328 doc - doc8271).  
  35. //  
  36. // note: this causes TIMER1 to interrupt every 1ms! (TIMER1_OVF)  
  37. // note: it's super important to call this AFTER arduino environment does it's good (and bad)  
  38. //  things to various TIMER settings.  
  39. //  
  40. static void startTimer1(void)  
  41. {  
  42.  // make sure interrupts are off  
  43.  cli();  
  44.   
  45.  // initialize ICR1, which sets the "TOP" value for the counter to interrupt and start over  
  46.  // note: value of 125-1 ==> 1khz (assumes 8mhz clock, prescaled by 1/64)  
  47.  ICR1 = 125-1;  
  48.  OCR1A = 0;  // needed?? (no)  
  49.    
  50.  //  
  51.  // timer1 config:  
  52.  // fast pwm (mode 14) - set WGM13,WGM12,WGM11 bits  
  53.  // prescaler clock/64 - set CS11,CS10 bits  
  54.  //  
  55.  TCCR1A = _BV(WGM11);  
  56.  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11) | _BV(CS10);  
  57.  TCCR1C = 0; // not needed?  
  58.  TIMSK1 = _BV(TOIE1);  // enable timer1 overflow interrupt.. now were running!  
  59.   
  60.  sei();  
  61. }  
A lógica toda está na rotina de interrupção do timer:
  1. // Rotina de interrupção do timer1  
  2. // Executada a cada milisegundo  
  3. ISR(TIMER1_OVF_vect)  
  4. {  
  5.   static int iRep = 0;    // repetições do frame  
  6.   static int iPlane = 0;  // plano atual no frame  
  7.   static int iFrame = 0;  // frame atual  
  8.   uint16_t plane;  
  9.   uint8_t dbits, cbits;  
  10.     
  11.   // Pega na Flash o padrão do plano corrente do frame atual  
  12.   plane = pgm_read_word (&frameTab[iFrame][iPlane]);  
  13.     
  14.   // Acende os LEDs  
  15.   
  16.   //  
  17.   // pixels 0,1,2 go to IO pins D7,D6,D5  
  18.   // also, add in 3 bits to turn off all the planes (PP1-PP3) by pulling them high  
  19.   //  
  20.   dbits = ((plane & 0x001) << 7) |  
  21.    ((plane & 0x002) << 5) |  
  22.    ((plane & 0x004) << 3) |  
  23.    (0x7 << 2);  
  24.   PORTD = dbits;  
  25.   
  26.   //  
  27.   // pixels 3-8 go to IO pins C5-C0  
  28.   //  
  29.   cbits = ((plane & 0x008) << 2) |  
  30.    ((plane & 0x010))      |  
  31.    ((plane & 0x020) >> 2) |  
  32.    ((plane & 0x040) >> 4) |  
  33.    ((plane & 0x080) >> 6) |  
  34.    ((plane & 0x100) >> 8);  
  35.   PORTC = cbits;  
  36.     
  37.   // Seleciona o Plano  
  38.   if (iPlane == 0) {  
  39.     PORTD &= ~0x10;  
  40.   } else if (iPlane == 1) {  
  41.     PORTD &= ~0x08;  
  42.   } else if (iPlane == 2) {  
  43.     PORTD &= ~0x04;  
  44.   }    
  45.     
  46.   // Vai para o próximo plano  
  47.   if (++iPlane == 3)  
  48.   {  
  49.     // Repete o frame  
  50.     iPlane = 0;  
  51.     if (++iRep == (TIME_FRAME/3))  
  52.     {  
  53.       // Próximo frame  
  54.       iRep = 0;  
  55.       if (++iFrame == NUM_FRAMES)  
  56.       {  
  57.         // De volta ao primeiro frame  
  58.         iFrame = 0;  
  59.       }  
  60.     }  
  61.   }  
  62. }  
O primeiro passo é pegar na Flash a palavra de 16bits correspondente ao plano e frame atuais. A rotina pgm_read_word da biblioteca avr-libc faz o trabalho pesado. O passo seguinte é mover os bits desta palavra para os bits que controlam os LEDs nos ports C e D. No port D está também a seleção do plano a energizar. A rotina repete em sequência os planos 0, 1 e 2 até completar o tempo de apresentação do frame, quando passa para o frame seguinte. Ao chegar ao último frame, volta ao primeiro.

Com esta programação a animação é controlada pela estrutura de dados (frameTab), não é necessário alterar o código para mostrar uma animação diferente. Uma evolução (que fica como exercício ao leitor) é pegar os frames da E2Prom ao invés da Flash e acrescentar uma forma de receber a animação pela serial e gravá-la na E2Prom.

O vídeo abaixo mostra o programa em ação.


O sketch está nos arquivos do blog em LedCube2pt.zip.

Um comentário:

NOME disse...

Olá gosto muito desse Blog ,
poderia me ajudar, vou começa meu TCC 1.Porém não tenho ideias, pode me dar uma ideia? que esteja dentro de eletrônica e automação