quarta-feira, setembro 11, 2013

LEDCube Kit v2: Programação III

Encerrando esta série, vamos alterar o código do exemplo anterior para acrescentar um controle (limitado) da intensidade dos LEDs.

Recordando, no LEDcube nos controlamos os LEDs um plano por vez. No código anterior (e na biblioteca padrão) cada plano fica aceso por 1 ms e apagado por 2 (enquanto os outros planos são apresentados). A nossa visão faz a média e temos a impressão que os três planos estão acesos simultaneamente.

O controle dos LEDs é apenas aceso ou apagado, mas podemos simular um controle de intensidade, variando o tempo que ele permanece aceso. Vou implementar aqui algo bem simples, controlando junto a intensidade de todos os LEDs de um plano. Com mais trabalho seria possível controlar a intensidade de cada LED.

No código anterior cada plano ficava aceso 1ms em cada 3. Para reduzir o brilho, vou deixar de acender o plano em algumas ocasiões. Por exemplo, indicando por 1, 2 e 3 os planos e por - o display apagado, imagine a seguinte sequência:

1 2 3 1 - - 1 2 - 1 - -

Nestes 12 segundos o plano 1 ficou aceso por 4 ms (que era o que fazíamos no código anterior), o plano 2 ficou aceso por 2 ms e o plano 3 por apenas 1 ms. Repetindo isto várias vezes, a nossa vista vai considerar que os LEDs do plano estão mais intensos que os do plano 2, por sua vez mais intensos que os do plano 3.

Para implementar isto, codifiquei a intensidade dos planos em 2 bits na minha tabela de animação (11 sendo a intensidade mínima e 00 a máxima):
uint16_t const frameTab[][3] PROGMEM = 
{
  { 0b00000000000,  0b11000010000, 0b00000000000 },
  { 0b00000000000,  0b10000010000, 0b00000000000 },
  { 0b00000000000,  0b01000010000, 0b00000000000 },
  { 0b00000000000,  0b00000010000, 0b00000000000 },
  { 0b00000000000,  0b01000010000, 0b00000000000 },
  { 0b00000000000,  0b10000010000, 0b00000000000 },
  { 0b00000000000,  0b11000010000, 0b00000000000 },
  
  { 0b11101000101,  0b11101000101, 0b11101000101 },
  { 0b10101000101,  0b10101000101, 0b10101000101 },
  { 0b01101000101,  0b01101000101, 0b01101000101 },
  { 0b00101000101,  0b00101000101, 0b00101000101 },
  { 0b01101000101,  0b01101000101, 0b01101000101 },
  { 0b10101000101,  0b10101000101, 0b10101000101 },
  { 0b11101000101,  0b11101000101, 0b11101000101 },

};
A rotina de interrupção do timer passa a testar estes bits adicionais, deixando os LEDs apagados conforme estes bits e o número da repetição:
// 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;
  boolean bShow;
  
  // Pega na Flash o padrão do plano corrente do frame atual
  plane = pgm_read_word (&frameTab[iFrame][iPlane]);
  
  // Verifica se mostra o plano                    0 1 2 3
  // 00xxxxxxxxx mostra sempre (brilho máximo)     x x x x
  // 01xxxxxxxxx não mostra 1 vez em 4             x x x -
  // 10xxxxxxxxx não mostra metade das vezes       x - x -
  // 11xxxxxxxxx não mostra 3 vezes em 4           x - - -
  bShow = true;
  if (((iRep & 3) == 1) && ((plane & 0b10000000000) != 0)) {
    bShow = false;
  } else if (((iRep & 3) == 2) && ((plane & 0b11000000000) == 0b11000000000)) {
    bShow = false;
  } if (((iRep & 3) == 3) && ((plane & 0b11000000000) != 0)) {
    bShow = false;
  }  
  
  if (bShow) {
    //
    // 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;
    }  
  } else {
    PORTD = 0x7 << 2;
  }
  
  // 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 vídeo abaixo mostra o resultado:



Nenhum comentário: