segunda-feira, julho 20, 2009

Lunar Lander

Quando eu estava preparando o meu post sobre como controlar muitos LEDs, e pensando num exemplo de displays de sete segmentos, me veio uma vaga ideia de fazer uma versão de um antigo joguinho de pouso na lua (da infância dos micros pessoais). A ideia conceitual era a seguinte:


Há pouco mais de duas semanas foi que me dei conta que no dia 20 de julho era aniversário de 40 anos do pouso na Lua. Resolvi então fazer uma primeira versão para a data. O prazo curto me obrigou a adotar algumas simplificações e usar os componentes que tinha à mão, mesmo quando não eram os mais adequados.

O tosco resultado final foi o seguinte:


O Jogo

O jogo Lunar Lander consiste em pousar suavemente uma espaçonave na Lua, controlando a queima do pouco combustível disponível.

Procurando nos tubos da internet, achei uma versão extremamente legível, apesar de estar em uma linguagem que não conheço: http://eleven.sourceforge.net/demo/lander.html. Esta versão se baseia em programas escritos em BASIC por Dave Ahl (que eu conhecia dos anos 80, quando comprava a revista Creative Computing). As versões de DaveAhl estão disponíveis aqui.

Eu segui bem de perto a versão em Eleven, deixando de lado aperfeiçoamentos como variar a massa da nave à medida em que o combustível é queimado. Adotei também os mesmos valores; futuramente vou dar uma ajustada.

O Algorítimo

O algorítimo é trivial, com velocidade e posição sendo calculados incrementalmente:
  • a aceleração da nave é a gravidade menos a queima de combustível.
  • a nova velocidade é a velocidade atual mais a aceleração vezes o tempo.
  • a nova altitude é a média entre a velocidade atual e a nova vezes o tempo.
Sendo o tempo entre os cálculos fixos, basta acertar as escalas para fazer as contas usando somente soma e subtração.

O Display

Usei quatro displays duplos de sete segmentos que eu tinha guardado desde o final dos anos 70. Cada display possui 18 pinos, correspondentes aos anodos dos oitos LEDs (sete segmentos mais o ponto) e o catodo comum aos LEDs de cada dígito.

Os segmentos iguais dos dígitos foram interligados entre si e conectados, através de um resistor, às saídas de um shift register 74HC595. A seleção do dígito é feita por um decodificador 3-para-8 (inicialmente um 74LS138 e depois um 74HC138), que aciona um transistor conectado ao catodo. Eu aproveitei uns transistores antigos que eu tinha, mas esta parte não ficou boa; o resultado foi um brilho fraco do display. Faltou tempo e coragem para rever esta parte do circuito.

Montei o display em uma placa separada, para maior flexibilidade:


As conexões foram feitas com fio de wire-wrap soldado, o que deu bem mais trabalho que eu previa. O resultado é ligeiramente assustador, e o motivo pelo qual eu não tentei trocar os transistores:

O conector na parte de baixo recebe
  • a alimentação
  • os três sinais de seleção de dígito
  • os quatro sinais de controle do shift register (dado, clock de shift, clock de cópia do shift para a saída e o clear do shift)
Um primeiro teste, acionando os sinais por meio de botões, permitiu corrigir os pequenos erros de montagem.

O Microcontrolador

Devido ao uso do 74LS138 o display exigia 5V o que dificultava o uso do MSP430 (já troquei por um 74HC138 para algum dia operar com 3V). Por este motivo resolvi usar um PIC 16F628A (uma escolha mal pensada, como veremos adiante). Esta parte foi montada numa breadboard.


O PIC é o integrado no alto, próximo ao centro. A peça marrom claro à esquerda é um ressonador de 4MHz, que fornece o clock para o PIC. O LED foi usado inicialmente para testes. Na parte inferior estão o botão para acionar o retrofoguete e o potenciômetro que controla a quantidade de combustível a queimar por unidade de tempo. O conector de telefone, no alto à direita permite ligar o gravador para regravar o firmware sem retirar o PIC do circuito (ainda bem, pois foram algumas dezenas de gravações).

Tudo correu bem, até o momento de ligar o potenciômetro. Foi neste ponto que percebi que este modelo de PIC não tem ADC (conversor Analógico para Decimal). Por outro lado, ele tem um comparador analógico e uma fonte de referência programável, o jeito foi ficar variando a referência e detectar quando ela ela ultrapassa a tensão no potenciômetro.

O comparador deste PIC é muito pouco flexível. A única opção para usar a referência interna obriga a usar dois comparadores, consumindo dois pinos do PIC. Após realocar os pinos de E/S tudo parou de funcionar. Lendo com mais atenção a documentação do PIC, descobri que um dos pinos de E/S tem comportamento diferente dos demais. Era justo neste pino que eu tinha ligado o sinal de Clear do shift register, resultando em um display sempre apagado. Além disso, parece que o comparador ocupa não somente os dois pinos configurados como entrada mas também os outros dois que podem ser configurados como entrada (preciso investigar um pouco mais). Com o prazo e os pinos acabando, movi dois sinais para os pinos de programação. Felizmente as duas funções conviveram harmoniosamente.

O Firmware

Como está virando rotina nos meus projetos até agora, o coração do firmware é a interrupção do timer, que ocorre a cada milissegundo. Neste interrupção são feitos:
  • A atualização da altitude, velocidade e combustível. Seguindo os números do programa que tomei como base, a atualização deveria ser a cada 10 milissegundos. Para ficar mais fácil de acompanhar os números, dobrei este tempo (ou seja, o jogador trabalho com o sistema em "slow motion"). Todos os valores são inteiros; a altitude é um inteiro de 32 bits e os demais de 16 bits.
  • A leitura do comparador e reprogramação da tensão de referência para acompanhar o potenciômetro. Para minha surpresa, este código funcionou quase que de primeira. Por simplificação o potenciômetro seleciona apenas cinco níveis de queima (0, 25%, 50%, 75% e 100% da taxa de queima máxima).
  • A atualização dos segmentos a apresentar. Para isto é preciso converter os valores inteiros nos dígitos correspondentes, o que requer divisão. Entretanto as divisões são demoradas demais para ficar na interrupção. O jeito foi a cada 100 milissegundos a interrupção acionar um flag para que o programa principal faça as cálculos. Ao final dos cálculos o programa principal acionar um flag para a próxima interrupção usar o resultado. Os segmentos são armazenados em 8 bytes, com cada byte correspondendo a um dígito do display e cada bit a um segmento. Uma tabela auxiliar contém os valores correspondentes a cada dígito.
  • A varredura dos dígitos. A cada 3 milissegundos é selecionado o dígito seguinte e carregado o shift register com o valor correspondente aos seus segmentos.
Resultado Final

O Lunar Lander Mark I está funcionando! Falta vários acertos, tanto de hardware (como melhorar a luminosidade) como de software (como tratar o caso da velocidade ficar negativa), mas já dá para brincar.



Mais para frente, quando tiver um resultado um pouco melhor, eu publico o circuito e o código.

3 comentários:

Anônimo disse...

Muito legal,da trabalho levar em consideração todas as variáveis.
Sera que o modulo lunar da apolloXI tinha um computador mais fraco que um PIC16F87X? ou 8051 da vida?

Daniel Quadros disse...

É difícil comparar o computador da Apollo XI com microcontroladores como os PIC e 8051. Embora ele fosse bem mais lento, tinha uma boa quantidade de memória, multiplicação e divisão por hardware e trabalha com palavras de 16 bits. A wikipedia tem uma boa descrição dele em http://en.wikipedia.org/wiki/Apollo_Guidance_Computer.

Dr Oscar Bono disse...

Quer montar um?
http://klabs.org/history/build_agc/