quinta-feira, novembro 22, 2012

Display de LEDs JY-LKM1638 - Parte 3

Encerrando esta série sobre o display JY-LKM1638, vou usá-lo junto com um Arduino para implementar um jogo de pouso lunar.

O Lunar Lander Mark II
Em 2009 eu montei o Lunar Lander Mark I. Este novo projeto aproveita a parte do código que faz a simulação, calculando a altitude e velocidade.

O Display
 Para aproveitar bem o display, implementei a seguinte interface com o jogador:
  • Os quatro dígitos da esquerda apresentam a altitude. O jogo termina quando ela chega a zero.
  • Os quatro dígitos da direita apresentam a velocidade. Velocidade positiva indica que a nave está se aproximando da Lua, velocidade negativa indica que ela está se afastando.
  • O nível do combustível é mostrado nos LEDs. Inicialmente os LEDs são apresentados na cor verde. Quando sobra somente o último LED, o programa passa para um resolução maior com os LEDs em vermelho.
  • Os botões disparam a queima de combustível. Quanto mais à direita, maior a quantidade queimada.
Para controlar o display use a biblioteca TM168 do Ricardo Batista. Para apresentar os valores usei a rotina de escrever um dígito, pois a rotina de escrever números não suporta mostrar dois valores simultaneamente.

Abaixo está o código (que está também nos arquivos do blog, em  LunarLander_LKM1638.zip).
//
// Lunar Lander para o display JY-LKM1638
// Daniel Quadros - 19/11/2012
//
// Utiliza TM1638 Library de Ricardo Batista
//

#include <TM1638.h>

static TM1638 disp(8, 9, 7);

static const int grav = 5;         // acelaração da gravidade

static long altitude = 9990000;    // altitude atual
static int comb = 16000;           // combustível disponível
static int veloc = 5000;           // velocidade atual
static int burn = 0;               // taxa de queima do combustível

// Valores para queima conforme a tecla apertada
// com a tensão no potenciômetro
const int queima[9] =
{
   4, 8, 12, 16, 20, 24, 27, 30, 0
};

static void AtlDisplay (void);
static void LeTeclas (void);
static void Simula (void);

// Iniciação
void setup()
{
  //Serial.begin (9600);
}

// Loop principal
void loop()
{
  AtlDisplay ();
  LeTeclas ();
  //Serial.print (altitude);
  //Serial.print (" ");
  //Serial.print (veloc);
  //Serial.print (" ");
  //Serial.print (comb);
  //Serial.print (" ");
  //Serial.println (burn);
  Simula ();
  delay (100);
}

// Simula o movimento
static void Simula (void)
{
  signed int newv;
  
  if (altitude > 0)
  {
     if (burn)
     {
        if (burn > comb)
           burn = comb;
        comb -= burn;
        newv = veloc - burn + grav;
        burn = 0;
     }
     else
        newv = veloc + grav;
     
     altitude -= (veloc + newv) >> 1;
     if (altitude < 0)
        altitude = 0;
     veloc = newv;
  }
}

// Verifica se tem tecla apertada
// Se sim, queima combustível
static void LeTeclas (void)
{
  byte teclas = disp.getButtons();
  int i;
  for (i = 0; i < 8; i++)
    if (teclas & (1 << i))
      break;
  burn = queima [i];  
}

// Atualiza o display
static void AtlDisplay (void)
{
  int valor;
  int idot;
  
  // Altitude
  if (altitude > 999900L)
  {
    valor = (int) (altitude / 1000L);
    idot = 3;
  }
  else if (altitude > 99990L)
  {
    valor = (int) (altitude / 100L);
    idot = 2;
  }
  else if (altitude > 9999L)
  {
    valor = (int) (altitude / 10L);
    idot = 1;
  }
  else
  {
    valor = (int) altitude;
    idot = 0;
  }
  disp.setDisplayDigit (valor / 1000, 0, idot == 0);
  disp.setDisplayDigit ((valor / 100) % 10, 1, idot == 1);
  disp.setDisplayDigit ((valor / 10) % 10, 2, idot == 2);
  disp.setDisplayDigit (valor % 10, 3, idot == 3);
  
  // Velocidade
  if (veloc > 9999)
  {
    disp.setDisplayDigit (veloc / 10000, 4, false);
    disp.setDisplayDigit ((veloc / 1000) % 10, 5, false);
    disp.setDisplayDigit ((veloc / 100) % 10, 6, false);
    disp.setDisplayDigit ((veloc / 10) % 10, 7, true);
  }
  else if (veloc > 0)
  {
    disp.setDisplayDigit (veloc / 1000, 4, false);
    disp.setDisplayDigit ((veloc / 100) % 10, 5, false);
    disp.setDisplayDigit ((veloc / 10) % 10, 6, true);
    disp.setDisplayDigit (veloc % 10, 7, false);
  }
  else
  {
    disp.setDisplayToString ("-", 0, 4);
    valor = -veloc / 10;
    disp.setDisplayDigit (valor / 100, 5, false);
    disp.setDisplayDigit ((valor / 10) % 10, 6, false);
    disp.setDisplayDigit (valor % 10, 7, true);
  }
  
  // Combustível
  if (comb > 2000)
  {
    int i, pos;
    
    pos = 7 - (comb - 1) / 2000;
    for (i = 0; i < pos; i++)
      disp.setLED (0, i);
    for (; i < 8; i++)
      disp.setLED (TM1638_COLOR_GREEN, i);
  }
  else if (comb != 0)
  {
    int i, pos;
    pos = 7 - (comb - 1) / 250;
    for (i = 0; i < pos; i++)
      disp.setLED (0, i);
    for (; i < 8; i++)
      disp.setLED (TM1638_COLOR_RED, i);
  }
  else
    disp.setLEDs (0);
}
O vídeo a seguir mostra o jogo em andamento.


Nenhum comentário: