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:
// Esta tabela define a animação
// Cade frame determina o estado dos LEDs nos três planos
uint16_t const frameTab[][3] PROGMEM = 
{
  { 0b100000000,  0b000000000, 0b000000000 },
  { 0b010000000,  0b000000000, 0b000000000 },
  { 0b001000000,  0b000000000, 0b000000000 },
  { 0b000100000,  0b000000000, 0b000000000 },
  { 0b000010000,  0b000000000, 0b000000000 },
  { 0b000001000,  0b000000000, 0b000000000 },
  { 0b000000100,  0b000000000, 0b000000000 },
  { 0b000000010,  0b000000000, 0b000000000 },
  { 0b000000001,  0b000000000, 0b000000000 },
  { 0b000000000,  0b000000000, 0b000000000 },

  { 0b111111111,  0b000000000, 0b000000000 },
  { 0b000000000,  0b111111111, 0b000000000 },
  { 0b000000000,  0b000000000, 0b111111111 },
  { 0b000000000,  0b000000000, 0b000000000 },

  { 0b111000000,  0b111000000, 0b111000000 },
  { 0b000111000,  0b000111000, 0b000111000 },
  { 0b000000111,  0b000000111, 0b000000111 },
  { 0b000000000,  0b000000000, 0b000000000 },

  { 0b100100100,  0b100100100, 0b100100100 },
  { 0b010010010,  0b010010010, 0b010010010 },
  { 0b001001001,  0b001001001, 0b001001001 },
  { 0b000000000,  0b000000000, 0b000000000 }
};
#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:
// Rotinas locais
static void portInit(void);
static void startTimer1(void);

// Iniciação do sketch
void setup () 
{
  portInit();
  startTimer1();
};

// Laço principal do sketch
void loop () 
{
  // Nada a fazer - toda a ação ocorre na interrupção
};
As rotinas de iniciação das portas e do Timer1 são as mesmas da biblioteca padrão:
/*
 * Copyright (c) 2011 - Rolf van Widenfelt, Mitch Altman.  Some rights reserved.
 *
 * Note: This source code is licensed under a Creative Commons License, CC-by-nc-sa.
 *  (attribution, non-commercial, share-alike)
 *   see http://creativecommons.org/licenses/by-nc-sa/3.0/ for details.
*/
//  Inicia os ports de E/S
static void portInit(void)
{
 // note: these MUST be in sync with actual hardware!
 // note: DDR pins are set to "1" to be an output, "0" for input.

 //          76543210
 //PORTB = 0b00000001;  // initial: set to enable pullups on inputs: BUT1 pullup
 //DDRB  = 0b00100000;  // inputs: BUT1 (PB0); outputs: SCK (PB5); reserved (PB6, PB7)
 PORTB = 0x01;   // (see above)
 DDRB  = 0x30;   // (see above)
 
 //          76543210
 //PORTC = 0b00000000;  // initial: no pullups on inputs
 //DDRC  = 0b00111111;  // outputs: CC33-CC12 (PC0-PC5)
 PORTC = 0x00;  // (see above)
 DDRC  = 0x3F;  // (see above)
 
 //          76543210
 //PORTD = 0b00000000;  // initial: no pullups on inputs
 //DDRD  = 0b11111100;  // outputs: PP3-PP1 (PD2-PD4), CC31-CC11 (PD5-PD7); reserved (PD0/RxD,PD1/TxD)
 PORTD = 0x00;  // (see above)
 DDRD  = 0xFC;  // (see above)
}

//
// set up timer1 in "fast PWM" mode 14 (see waveform generation, pg 137 of atmega168/328 doc - doc8271).
//
// note: this causes TIMER1 to interrupt every 1ms! (TIMER1_OVF)
// note: it's super important to call this AFTER arduino environment does it's good (and bad)
//  things to various TIMER settings.
//
static void startTimer1(void)
{
 // make sure interrupts are off
 cli();

 // initialize ICR1, which sets the "TOP" value for the counter to interrupt and start over
 // note: value of 125-1 ==> 1khz (assumes 8mhz clock, prescaled by 1/64)
 ICR1 = 125-1;
 OCR1A = 0;  // needed?? (no)
 
 //
 // timer1 config:
 // fast pwm (mode 14) - set WGM13,WGM12,WGM11 bits
 // prescaler clock/64 - set CS11,CS10 bits
 //
 TCCR1A = _BV(WGM11);
 TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11) | _BV(CS10);
 TCCR1C = 0; // not needed?
 TIMSK1 = _BV(TOIE1);  // enable timer1 overflow interrupt.. now were running!

 sei();
}
A lógica toda está na rotina de interrupção do timer:
// Rotina de interrupção do timer1
// Executada a cada milisegundo
ISR(TIMER1_OVF_vect)
{
  static int iRep = 0;    // repetições do frame
  static int iPlane = 0;  // plano atual no frame
  static int iFrame = 0;  // frame atual
  uint16_t plane;
  uint8_t dbits, cbits;
  
  // Pega na Flash o padrão do plano corrente do frame atual
  plane = pgm_read_word (&frameTab[iFrame][iPlane]);
  
  // Acende os LEDs

  //
  // pixels 0,1,2 go to IO pins D7,D6,D5
  // also, add in 3 bits to turn off all the planes (PP1-PP3) by pulling them high
  //
  dbits = ((plane & 0x001) << 7) |
   ((plane & 0x002) << 5) |
   ((plane & 0x004) << 3) |
   (0x7 << 2);
  PORTD = dbits;

  //
  // pixels 3-8 go to IO pins C5-C0
  //
  cbits = ((plane & 0x008) << 2) |
   ((plane & 0x010))      |
   ((plane & 0x020) >> 2) |
   ((plane & 0x040) >> 4) |
   ((plane & 0x080) >> 6) |
   ((plane & 0x100) >> 8);
  PORTC = cbits;
  
  // Seleciona o Plano
  if (iPlane == 0) {
    PORTD &= ~0x10;
  } else if (iPlane == 1) {
    PORTD &= ~0x08;
  } else if (iPlane == 2) {
    PORTD &= ~0x04;
  }  
  
  // Vai para o próximo plano
  if (++iPlane == 3)
  {
    // Repete o frame
    iPlane = 0;
    if (++iRep == (TIME_FRAME/3))
    {
      // Próximo frame
      iRep = 0;
      if (++iFrame == NUM_FRAMES)
      {
        // De volta ao primeiro frame
        iFrame = 0;
      }
    }
  }
}
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