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).
#include <TM1638.h> static TM1638 disp(8, 9, 7); static const int grav = 5; static long altitude = 9990000; static int comb = 16000; static int veloc = 5000; static int burn = 0; 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 ); void setup() { } void loop() { AtlDisplay (); LeTeclas (); Simula (); delay (100); } 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; } } static void LeTeclas ( void ) { byte teclas = disp.getButtons(); int i; for (i = 0; i < 8; i++) if (teclas & (1 << i)) break ; burn = queima [i]; } static void AtlDisplay ( void ) { int valor; int idot; 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); 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 ); } 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); } //
// 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.
VIDEO
Nenhum comentário:
Postar um comentário