Desenvolvimento e criação do zero de uma máquina de arcade para quatro jogadores

imagem

Em novembro de 2019, abandonei o emprego e decidi dedicar vários meses ao aprendizado de uma nova habilidade que desejava aprender há muito tempo. Naquela época, eu trabalhava como desenvolvedor web. Antes disso, estudei desenvolvimento de software. E ainda mais cedo, quando criança, eu estava constantemente experimentando eletrônicos e programas. Portanto, eu me senti confiante o suficiente na criação de software.

No entanto, sempre houve essa coisa mágica chamada “ferro” , que uso diariamente, mas não tenho idéia de como ela realmente funciona. Um desenvolvedor de software (e especialmente um desenvolvedor web) não precisa entender o hardware. Como todo código de desenvolvimento da web é de nível muito alto, raramente acontece que o problema com o nosso software esteja relacionado ao hardware.

Portanto, nunca trabalhei em “ferro” e não tinha nenhum conhecimento geral sobre engenharia elétrica, exceto aqueles que nos foram dados no ensino médio. E eu queria mudar essa situação. Estabeleci uma meta absurda, que na época parecia muito distante do meu conjunto de conhecimentos. E então ... eu apenas comecei a experimentar. Planejei verificar até onde poderia ir antes de ficar sem motivação. Eu não esperava alcançar meu objetivo, mas de alguma forma consegui.

Aqui está o meu resultado:


Meu objetivo incrível era criar um console de videogame para quatro jogadores do zero. Ele usa um microcontrolador como um cérebro e uma faixa de LED como um monitor. Usando um joystick e quatro botões para cada jogador localizado nas bordas da mesa, os jogadores controlam um jogo que é carregado dinamicamente no sistema a partir de um chip de memória. Graças a isso, a jogabilidade no dispositivo se assemelha aos bons consoles antigos. Só que agora não precisamos soprar o cartucho para se livrar dos bugs.

Para minha surpresa ... eu realmente pude concluir o projeto. Levei cerca de três meses úteis para ciclos de experimentos, falhas, leitura de documentação, desespero e novas tentativas. Seria necessário fixar o número de horas de trabalho para saber quanto tempo gastei nisso. Vou assumir que cerca de 800-900 horas. (Supondo que eu trabalhasse 6/7 dias por semana, entre 8 e 10 horas por dia, o que é bastante próximo da verdade.)

Tentei documentar o processo de minhas experiências do começo ao fim, tirando fotos e gravando vídeos. Com este post, documentarei o projeto e minha experiência por mim mesmo. Espero que, para você, isso se torne uma fonte de inspiração se você também decidir assumir um projeto igualmente incrível.

Indo pro trabalho


Como eu disse no começo do artigo, quando iniciei o projeto, eu não tinha conhecimento de eletrônica, exceto pela fórmula incorretamente lembrada da lei de Ohm. Portanto, primeiro, eu precisava estudar como a eletrônica funciona na prática e só então tentar montar um console de jogos. Eu nem sabia como ligar o LED usando o microcontrolador. Então, meu primeiro passo: encomendar uma eletrônica simples e um microcontrolador para experimentos. Para começar, tentarei ligar o LED. Estou quase certo de que minha primeira tentativa de ligar o LED levou à sua queima. Mas isso não foi documentado na câmera, então vamos fingir que não era.


Experimentos com um microcontrolador, resistores, um transistor e um LED.

Este foi o primeiro marco importante. Agora sabemos como criar código que roda no microcontrolador. Também aprendemos a usar transistores como um simples interruptor elétrico para ligar um LED. Surpreendente!

Aventura com EEPROM



FIG. 1. Usando registradores de deslocamento para controlar 16 linhas de saída com apenas 3 pinos de saída.

Parte do projeto foi a capacidade de escrever lógica de jogo em chips de memória (EEPROM). Eles deveriam ter sido lidos quando um cartucho com um player é inserido no sistema.

A longo prazo, me machucou o fato de não ter aprendido informações suficientes sobre que tipo de EEPROM deveria ser usado neste projeto. Eu era tão apaixonado que comprei os velhos chips EEPROM paralelos (W27C512).

Para escrever ou ler a partir deles, eu precisava definir um sinal alto ou baixo nas 16 linhas. Se eu não quisesse ocupar todos os contatos de saída do microcontrolador, seria lógico aprender como usar os registradores de deslocamento.


FIG. 2. O circuito usado em uma tentativa malsucedida de ler / gravar dados em um chip EEPROM paralelo.

Com apenas alguns contatos, podemos controlar separadamente todas as 16 linhas.

Agora vamos aplicar esse conhecimento à EEPROM! Sim mas não. Não consegui gravar dados na EEPROM de forma estável . Este tipo de chip requer 12-14 V para remover ou gravar bytes. Tentei fornecê-los usando transistores como interruptores para fornecer tensão aumentada. E pelo que entendi, deve funcionar. Mas não deu certo. Ainda não entendi qual é exatamente o problema. Na fig. A Figura 2 mostra os circuitos que tentei usar para ler / gravar bytes de / para EEPROM.


FIG. 3. Um circuito que lê / grava com êxito bytes em um chip EEPROM serial.Finalmente

, tendo estudado mais informações sobre esse tópico, aprendi que hoje as EEPROMs paralelas não são usadas com tanta frequência. Geralmente, os desenvolvedores preferem o EEPROM I2C serial e, portanto, é muito mais fácil encontrar tutoriais sobre eles. (Por exemplo, este incrível tutorial do Sparkfun ).

Na fig. A Figura 3 mostra meu esquema para ler e gravar dados em EEPROMs seriais ... É MUITO mais simples e muito mais confiável. Isso me ensinou que você não deveria estar muito feliz em pedir peças e tentar desenvolver o projeto muito rapidamente. Passamos cerca de 1 a 2 semanas em EEPROMs paralelas e, por fim, não as usamos. No entanto, no processo, aprendemos muito em eletrônica e especificações de leitura (o que por si só é uma habilidade). Os esforços não foram completamente desperdiçados.

Então, como fazemos de qualquer maneira?


Agora podemos ler e gravar nos chips de memória. Qual será o próximo passo? Nesta fase, será lógico descrever meu plano de console com mais detalhes usando vários desenhos como exemplo.


Plano geral. Este será um console de jogo com controladores para quatro jogadores. Cada jogador possui um joystick e vários botões.

O círculo vermelho marca o plugue do cartucho de jogo. Esse conector terá quatro pinos de metal que o console usa para ler EEPROMs seriais e carregar jogos na memória.

Verde mostra como planejamos criar uma tela de tiras de LED. Queríamos que o pixel da tela fosse uma mancha quadrada e luminosa de cor, não um ponto que se misturasse com outros pontos.

Para alcançar o efeito desejado, usamos esse esquema para separar as fontes de luz uma da outra. Ao mesmo tempo, dispersa a luz que cai no topo da tela. (As faixas brancas abaixo da grade indicam a faixa de LED)

Eu decidi fazer um display LED de 42x42. Ou seja, no total são obtidos 1764 LEDs. Eu escolhi o número 42, porque, graças a isso, o tamanho da mesa será ideal para quatro pessoas.

Ligar tantos LEDs é uma tarefa assustadora. No brilho máximo, o LED RGB consome 60 mA de corrente. (que dá branco com brilho máximo). Se multiplicarmos pelo número total de LEDs, obteremos o consumo máximo de corrente de 105,84 A a 5 V! Para nos aproximarmos dessa corrente, adquirimos uma fonte de alimentação SE-600–5 MEAN WELL. Ele pode fornecer corrente de até 100 A. O que é menos do que teoricamente possível 105 A. Mas não pretendo exibir telas totalmente brancas com brilho máximo nos jogos. Além disso, podemos limitar o brilho programaticamente. Também adicionaremos fusíveis para evitar exceder acidentalmente esse limite de 100 A.


FIG. 4. Protótipo de papelão para um display LED 3x3.

Voltarei à montagem dessa tela mais tarde. Primeiro você precisa fazer a prototipagem. Vamos ver se conseguimos fazer com que uma pequena tela de LED funcione.

Esse protótipo 3x3 é composto de vários pedaços de papelão que separam pixels e um pedaço de papel comum para impressoras que dispersa a luz. Funcionou muito bem! Recebemos uma confirmação maravilhosa da capacidade de trabalho do conceito.

Para controlar os LEDs usando um microcontrolador, usamos a biblioteca FastLED. Entretanto, se os LEDs forem controlados por um barramento de dados, é impossível atualizar 1764 LEDs a uma velocidade suficiente para jogos. Dada uma frequência de 800 kHz, podemos atingir uma taxa de quadros de aproximadamente 17 quadros por segundo. Não é exatamente o que nos esforçamos. Pessoalmente, gosto de jogar com pelo menos 60FPS. Felizmente, isso é possível. É suficiente usar mais de um barramento de dados. A tela de 42x42 pode ser convenientemente dividida em 7 partes iguais. Cada uma dessas partes pode ser controlada separadamente, com seu próprio barramento de dados. Isso é possível graças ao uso da função de saída paralela da biblioteca FastLED no microcontrolador Teensy 4 . Usando 7 barramentos de dados, podemos obter uma taxa de atualização máxima de aproximadamente 120FPS!

Não planejei isso, mas por puro acaso, no início do projeto, escolhi esse microcontrolador específico. Pareceu-me que para o bom funcionamento dos jogos exigiria o rápido tempo de processamento fornecido por este MK. O protótipo na Fig. 4 já é controlado pela função de saída paralela. No entanto, neste protótipo, usamos apenas 2 barramentos de dados, apenas para testar se tudo realmente funcionará como indicado.

Como vamos executar código com EEPROM?


Portanto, nesta fase, podemos ler e escrever na EEPROM e também temos uma prova prática do conceito da tela LED. Qual é o próximo?

Restam apenas duas tarefas técnicas sérias: a organização do método de entrada para todos os jogadores (botões e joysticks). E também precisamos descobrir como executar a lógica do jogo da EEPROM e exibir os resultados dessas instruções no visor.

Os botões e joysticks solicitados ainda não chegaram. Portanto, primeiro estaremos envolvidos na execução da lógica do jogo da EEPROM.

Existem várias maneiras de executar instruções (código) da EEPROM. No entanto, primeiro precisamos definir certos requisitos:

1) O código deve ser fácil de escrever.

2) O código deve ser pequeno (tamanho do arquivo).

3) O código deve poder interagir com bibliotecas prontas, por exemplo, FastLED, a fim de reduzir a quantidade de meu trabalho.

4) O código deve ser capaz de carregar na RAM o mais rápido possível, a fim de reduzir o tempo de carregamento.

5) Você precisa emular o código em um PC comum para testar jogos.

6) Nossa solução deve ser de alto desempenho (fornecer pelo menos 60FPS).

7) Uma solução não deve exigir a compra de um grande número de equipamentos adicionais.

8) A solução deve funcionar no Teensy 4.0 MK.

9) A solução deve ser fácil de implementar.

Eu vim com quatro maneiras diferentes de executar código da EEPROM:

- Escrevemos na EEPROM o código Arduino compilado usual. Em seguida, fazemos o programador externo do Arduino ler o código compilado da EEPROM e reprogramar o Arduino em tempo real toda vez que um novo jogo é carregado.

- Carregamos o código do assembler na RAM e o executamos a partir daí.

- Utilizamos um interpretador de código pronto, por exemplo, ArduinoBASIC ou Bitlash .

- Nós escrevemos nosso próprio interpretador de código.

Aqui está uma breve comparação das vantagens e desvantagens das quatro soluções para o nosso projeto:


(1) O tamanho do arquivo para uma solução com um programador externo é terrível. Juntamente com a lógica do jogo, todas as bibliotecas de código que usamos neste projeto devem ser armazenadas na EEPROM. Para exibir o estado do jogo, cada EEPROM deve ter sua própria cópia da biblioteca FastLED. Isso não é nada bom. Provavelmente, esse problema pode ser contornado adicionando-se o código base a um programador externo, que é então combinado com o código na EEPROM antes de programar o Arduino. Isso seria uma tarefa de alto risco, porque não é tão fácil encontrar tutoriais na Internet. Provavelmente gastaríamos muito tempo nisso.

(2) Executar o assembler a partir da RAM é uma ótima opção. Por isso, decidi considerar. A complexidade da escrita de código pode ser reduzida usando uma linguagem de alto nível, por exemplo, C, que é compilada no código correto do assembler. No entanto, não estava claro como seria fácil fazê-lo interagir com outras bibliotecas no Arduino, então decidi abandoná-lo.

(3) Usar um intérprete pronto também é uma solução muito boa. No entanto, tanto quanto eu sei, todos esses intérpretes são executados com base em cadeias de caracteres. Essas linhas longas devem ser gravadas na EEPROM. Esse não é um problema tão grande, mas definitivamente não é a melhor maneira de minimizar o tamanho do arquivo. Além disso, não estava claro se esses intérpretes poderiam interagir com as bibliotecas do Arduino. Portanto, decidi que usar intérpretes prontos não é uma boa ideia.

(4) Finalmente, temos a solução 4: criar nosso próprio interpretador de código. Satisfaz todos os requisitos, porque a maneira como o intérprete é implementado depende inteiramente de mim. Ele vaiter alta velocidade se eu conseguir alta velocidade do próprio intérprete. Sim, o tamanho do arquivo será pequeno e o código será fácil de escrever ... se eu mesmo fornecer isso. Em outras palavras, teremos controle completo sobre tudo. E se eu fizer um esforço, tudo sairá perfeitamente. A única desvantagem séria de tal solução será um longo tempo de desenvolvimento. Você deve se lembrar que passei de 800 a 900 horas nesse projeto. Ou seja, é óbvio que decidi escolher a solução 4 - para criar meu próprio interpretador de código. O tempo não foi um problema para mim. Aproveitei esta oportunidade para aprender como as linguagens de programação são criadas. Vou descobrir por que uma ou outra decisão arquitetônica foi tomada nessas linguagens.

O nascimento do ArcadableScript


O princípio geral do intérprete é bastante simples. Não entrarei em detalhes de sua estrutura interna, porque no momento da redação, planejava reescrever completamente o intérprete para que se tornasse mais eficiente e fosse mais fácil para ele escrever código. No entanto, o básico permanece o mesmo. O intérprete executará um loop de código simples para cada medida do jogo:


Uma grande desvantagem desse esquema é que a entrada e o estado do jogo são verificados apenas uma vez por quadro. Para jogos que não exigem uma reação rápida, isso é normal. No entanto, em outros jogos, o jogador não poderá reagir entre os quadros. Vou resolver esse problema na próxima versão do intérprete.


FIG. 5

No diagrama com a fig. A Figura 5 mostra como planejei atualizar o intérprete em um futuro próximo, criando dois ciclos separados para o estado do jogo e os quadros do jogo. Isso nos permitirá atualizar o estado do jogo centenas de vezes por segundo e a exibição - apenas 60 vezes por segundo.

Infelizmente, no Teensy 4.0, não é possível executar o código multithread do hardware. Portanto, não seremos capazes de realizar esses dois ciclos em paralelo. Mas tenho certeza de que vou inventar alguma coisa.


Depois de algum tempo, consegui escrever dois programas simples usando minha própria linguagem de código de código inventada. Eles usam números brutos como entrada para minimizar o tamanho do arquivo. Ou seja, completei a escrita desses dois programas, literalmente anotando listas de números que o intérprete pode entender. Para dar uma idéia de quão legível é esse bytecode, demonstrarei as instruções reais usadas no programa de exemplo que move um ponto pela tela:

// TODO: Write/read all untyped... data to/from ROM
  int untypedGamestate[] = {
    0, // Previous time, to keep track of game time/framerate.
    0, // Player position x.
    0, // Player position y.
    0, // Player R color value.
    0, // Player G color value.
    255, // Player B color value.
  };
  int untypedValues[][3] = {
    // ID, Type, Value
    {0, 0, 0}, // Move up button value 
    {1, 0, 1}, // Move right button value
    {2, 0, 2},  // Move down button value
    {3, 0, 3},  // Move left button value
    {4, 3, 1},  // True/1
    {5, 3, 0},  // False/0
    {6, 4, 0},  // Current millis since game start
    {7, 2, 0},  // Gamestate previous time
    {8, 2, 1},  // Gamestate player x
    {9, 2, 2},  // Gamestate player y
    {10, 2, 3}, // Gamestate player r color
    {11, 2, 4}, // Gamestate player g color
    {12, 2, 5}, // Gamestate player b color
    {13, 3, 1}, // Move player up after button press boundary check
    {14, 1, 0}, // System config screen width
    {15, 1, 1}, // System config screen height
    {16, 3, 3}, // Move player down after button press boundary check
    {17, 3, 5}, // Move player left after button press boundary check
    {18, 3, 7}, // Move player right after button press boundary check
  }
  int untypedCalculations[][5] = {
    // ID, ending, valueLeftID, calculationRightID, calculationOperator
    {0, 0, 9, 1, 1}, // Current player y position - 1
    {1, 1, 4, 0, 0}, // True/1
    {2, 1, 0, 0, 0}, // Up button
    {3, 1, 2, 0, 0}, // Down button
    {4, 1, 5, 0, 0}, // False/0
    {5, 1, 9, 0, 0}, // Current player y position
    {6, 0, 15, 1, 1} // screenheight - 1
    {7, 0, 9, 1, 0}, // Current player y position + 1
    {8, 1, 3, 0, 0}, // Left button
    {9, 1, 1, 0, 0}, // Right button
    {10, 1, 8, 0, 0}, // Current player x position
    {11, 0, 8, 1, 0}, // Current player x position + 1
    {12, 0, 8, 1, 1}, // Current player x position - 1
    {13, 0, 14, 1, 1} // screenwidth - 1
  }
  int untypedInstructions[][10] = {
    // ID, rootInstruction, conditionCalculationLeftID, conditionCalculationRightID, conditionOperator, conditionSuccesValueLeftID,
    // conditionSuccessCalculationRightID, hasFailedCondition, conditionFailedValueLeftID, conditionFailedCalculationRightID
    {0, 1, 2, 1, 0, 13, 0, 0, 0, 0}, // move player up when up button is pressed.
    {1, 0, 5, 4, 1, 9, 0, 0, 0, 0}, // move the player up when the boundary is not reached.
    {2, 1, 3, 1, 0, 16, 0, 0, 0, 0}, // move player down when down button is pressed. 
    {3, 0, 5, 6, 1, 9, 7, 0, 0, 0} // move the player down when the boundary is not reached.
    {4, 1, 8, 1, 0, 17, 0, 0, 0, 0}, // move player left when left button is pressed.
    {5, 0, 10, 4, 1, 8, 12, 0, 0, 0}, // move the player left when the boundary is not reached.
    {6, 1, 9, 1, 0, 18, 0, 0, 0, 0}, // move player right when right button is pressed.
    {7, 0, 10, 13, 1, 8, 11, 0, 0, 0}, // move the player right when the boundary is not reached.
  };

Se você dedicar algum tempo para estudar cuidadosamente o código, poderá entender como a versão anterior desse intérprete funcionou. O princípio geral do uso de "valores", "cálculos" e "instruções" é preservado na versão atual. No entanto, muita coisa mudou. Não temos mais uma lista fixa de valores de estados de jogos, agora eles são apenas parte da lista regular de valores. Além disso, separamos as “condições” das instruções. Graças a isso, podemos reutilizar as instruções no código, o que reduz o tamanho do arquivo.

Não vamos entrar em detalhes do intérprete, porque na versão redesenhada, tudo isso mudará em breve.

Agora, estamos no estágio em que sabemos ler / gravar na EEPROM e podemos executar instruções com base em matrizes de números comuns. O próximo passo lógico será remover listas de instruções codificadas do código e gravá-las na EEPROM. A partir de agora, poderemos tentar ler e executar as instruções armazenadas na EEPROM.


Nesta fase, temos todas as provas da capacidade de trabalho dos conceitos que precisamos para começar a criar o resultado final!

Obviamente, cada parte individual do protótipo que criamos ainda exige muito trabalho. Mas, na minha opinião, as tarefas mais difíceis já foram resolvidas. Tudo o que eu precisava fazer era desenvolver o que já tinha. Tudo é simples!

Conjunto de exibição de LED



Demorou muito trabalho para montar a tela. Muito trabalho. Comecei por ter que soldar fios em 42 tiras separadas. Cada tira requer três fios de um lado e dois do outro. Ou seja, apenas cerca de 200 fios. Antes deste projeto, eu não tinha muita prática em solda, então você pode perceber claramente que quanto mais eu faço solda, mais forte é a qualidade. Obviamente, no final, tive que refazer muitas das primeiras faixas de LED, porque não gostei do resultado. Tudo isso faz parte do processo de aprendizado!


O próximo passo: conectamos todas as tiras de LED a um grande painel de madeira. Olhando para trás, acho que talvez por uma melhor condutividade térmica tenha valido a pena usar uma folha de metal, porque depois de uma hora de operação a tela agora fica quente (40-50 ° C). Mas isso ainda não acontece com tanta frequência, por isso não é um grande problema. No entanto, se eu decidisse repetir o projeto, corrigiria esse aspecto.


Em seguida, conectamos fios de diâmetro maior aos condutores positivos e negativos das tiras de LED. Devemos garantir que os fios de grande diâmetro suportem de forma confiável uma corrente máxima de 100 A. Para proteção adicional, também adicionamos fusíveis de 15 A a cada parte da tela.Neste

estágio, estamos prontos para fazer as primeiras tentativas de controlar a tela. Depois de muitas falhas e manipulações com parâmetros e fios de software, finalmente consegui exibir uma única cor no monitor sem distorção e interferência. Demorou muito tempo e o resultado ainda estava longe de ser perfeito. Por um longo tempo, eu ainda tive problemas com sinais distorcidos até postar uma pergunta sobre isso em electronics.stackexchange.com. Pessoas excelentes deste site me ajudaram a diagnosticar o problema. Ainda não entendi completamente do que se tratava, mas, conectando os condutores negativos diretamente ao longo dos barramentos de dados, desde o aterramento do MK ao terminal de aterramento próximo à entrada de dados, consegui resolvê-lo. Desde então, não tive problemas com distorções na tela.


Como vimos na ilustração acima, duas camadas de material são sobrepostas nas tiras de LED.

Como material para este revestimento, precisamos de algo forte, transparente, mas disperso. No entanto, eu não queria usar o vidro porque é caro e frágil. Por isso, decidi usar um poliestireno transparente padrão. Para que ele dispersasse a luz, eu a tratei com uma lixa fina a tal ponto que era impossível olhar através dela. Tudo é muito simples.

Muito mais tempo foi gasto na criação de uma grade que seria localizada diretamente em cima das tiras. Pensei em encomendar uma grade com os parâmetros necessários, mas eles me chamaram de um preço de cerca de 500 euros. Na minha opinião, ela não valia a pena. Portanto, eu tive que coletar nós mesmos. Para isso, precisamos de tiras de plástico brancas finas (com no máximo 2 mm de espessura), duráveis ​​e opacas. Muitos requisitos. Portanto, a seleção do material certo levou muito tempo. Como se viu, listras de persianas são ideais.


Primeiro, fizemos um suporte pequeno para fazer cortes uniformes e uniformes em faixas largas. Neste caso, foram obtidas tiras de plástico bastante pequenas. Então precisávamos fazer incisões no meio de cada faixa exatamente nos lugares certos. Isso é necessário para conectar os pedaços de plástico e montar a grade. É o que faço na terceira foto. Antes de fazer isso, verifique se a largura da serra é a mesma ou um pouco maior que a largura da tira que você cortou. Caso contrário, a grade não pode ser montada. No meu caso, descobriu-se que uma serra para metal tem uma espessura ideal.

Na quarta foto, o resultado da montagem de todas as tiras na grade é visível. Era um trabalho muito chato e monótono, mas a grade acabou sendo muito durável. Em geral, parece que todas as células têm o mesmo tamanho. Não há muita variação entre os tamanhos das células; caso contrário, a tela pareceria estranha, então tudo ficou muito bom. Estou satisfeito com esta grelha.


Depois coloquei a grade em uma moldura de madeira que executava várias tarefas. Primeiro, ela segura uma folha de poliestireno no topo da grelha. Ele garante que a grade não se mova. Além disso, uma moldura de madeira pressiona todos os fios para que prendam com mais segurança e não possam se soltar acidentalmente (o que aconteceu algumas vezes quando mudei a tela).


Esta fotografia mostra uma folha de poliestireno lixada colocada em uma gradinha. Observe que a grade agora está quase invisível, e é isso que queríamos.


Depois de passar longas horas experimentando a implementação correta do controle de exibição, finalmente conseguimos fazê-lo funcionar. Um enorme problema foi a preservação da integridade dos sinais. Mas como eu disse acima. A comunidade de electronics.stackexchange me ajudou com isso!

A tela é brilhante, muito mais brilhante do que eu esperava. Era necessário prever isso, solicitando uma unidade de 100 A para sua fonte de alimentação.


Como eu já disse várias vezes, usamos a biblioteca FastLED para controlar os LEDs. Mas não mencionei o fato de que também uso uma versão modificada da biblioteca FastLED-GFX de Jürgen Skrocki (que é uma porta da Adafruit-GFX-Library ) para renderização simples de figuras no visor . As alterações nesta biblioteca são pequenas, mas foram necessárias para que o intérprete pudesse se comunicar com a biblioteca de uma maneira conveniente.

Cérebros e ...


Uma das últimas tarefas técnicas que precisam ser resolvidas para concluir este projeto é a conclusão do cérebro do console. Você precisa transformá-los em um conjunto conveniente e compacto. Mas primeiro precisamos descobrir como lidar com todos os sinais de entrada dos players. Deixe-me lembrá-lo de que cada jogador possui 4 botões e um joystick. Isso é um total de 16 sinais de entrada digital e 8 analógicos. O MK nunca possui contatos suficientes para tal entrada; além disso, dois pinos do barramento de dados já são usados ​​para ler a EEPROM e 7 pinos do barramento de dados paralelos são necessários para controlar a exibição.

Para resolver esse problema, usamos um par de registradores de deslocamento para entrada digital. E para todas as entradas analógicas, usaremos um multiplexador analógico.


Esquema para leitura / gravação simultânea de EEOPROM, processamento de entrada do player e controle de exibição.

E então a confusão começa. Deixe-me explicar o que está acontecendo nesta foto, da esquerda para a direita. No topo da tábua de pão esquerda, há 7 barramentos de dados usados ​​para controlar a exibição. Abaixo está a área em que a EEPROM pode ser inserida e onde é lida.

No topo da tábua de pão do meio, há um multiplexador analógico. Este multiplexador pode receber até 16 sinais de entrada analógicos e, dependendo dos sinais de entrada no multiplexador, conecte um dos sinais analógicos ao MK. Portanto, precisamos apenas de um pino de entrada analógica e um par de pinos de entrada digital para processar 16 entradas analógicas. Parece que nenhuma entrada analógica está conectada ao multiplexador nesta foto.

Sob o multiplexador é MK Teensy.

Na placa de ensaio correta, processamos sinais de entrada digital. Nesta foto, 16 sinais digitais são simplesmente conectados ao terra. Eu ainda estava esperando os botões chegarem, então testei todos os 16 sinais digitais dessa maneira. Graças aos registros de deslocamento, foram necessários apenas 4 pinos para controlar todos os sinais digitais.

Todo o sistema é um enorme caos não confiável. É necessário torná-lo mais compacto para que não se afaste de uma expiração descuidada. Portanto, pedi várias placas de prototipagem e comecei a soldar novamente!



E isso acabou sendo uma tarefa difícil! No entanto, essa foi uma excelente ocasião para aprender a soldar com precisão.

Para criar esse circuito, usei como exemplo o caos dos fios na tábua de pão. Depois, tentei criar um circuito mais inteligente, para que os componentes que deveriam se comunicar fossem o mais próximo possível. Então, tentei me livrar da necessidade de puxar muitos fios. Agora acho que você poderia usar algumas ferramentas de modelagem para projetar o circuito. Mas ainda assim ficou muito bem.

Eu decidi projetar um circuito de duas camadas. Na placa protótipo inferior, todas as entradas são processadas. Em cada uma das 4 conexões de entrada, tenho 8 linhas de entrada (1 - terra, 1 - positiva, 4 - entrada digital, 2 - entrada analógica). O MK se comunica com a camada inferior usando quatro conexões próximas aos registradores de deslocamento e quatro conexões próximas ao multiplexador.

A fonte de alimentação está localizada na camada superior, à direita, existem 7 barramentos de dados de saída para a tela e, na parte inferior, há contatos para a leitura da EEPROM. E, finalmente, no centro, é claro, está o microcontrolador.


Entre as placas de controle principais e a entrada dos jogadores, coloquei placas intermediárias para reduzir o número de fios necessários em ambos os lados de 12 para 8, porque não é necessário usar todos os 5 fios terra que vão da placa principal à placa de entrada. Um aterramento é suficiente para todos os botões e o joystick. Portanto, existem quatro dessas placas intermediárias. Gosto que você perceba uma melhoria na qualidade de cada próximo painel que coletar.


Como nunca recebi os botões encomendados, encomendei outro conjunto de outro vendedor e depois de alguns dias recebi-o. Isso nos permitiu finalmente começar a testar todos os botões.


Nesta fase, também escrevi o primeiro jogo que pode ser jogado no console. Sim, agora eu chamo de console.

Consegui porque, no processo de trabalhar na tela, aprimorei simultaneamente o intérprete. Em particular, eu consegui melhorar o próprio processo de desenvolvimento ao escrever código. Esse processo não é tão interessante externamente, por isso decidi não o documentar particularmente.


Criei um ambiente de desenvolvimento simples para escrever código que pode ser executado no console.

Esse ambiente funciona em um navegador e permite escrever código para o console sem mexer nas listas caóticas de números que vimos acima. Além disso, criei um emulador de console que é executado no mesmo aplicativo da web.

A capacidade de emular jogos para o console em um aplicativo da Web economizou muito tempo, porque a depuração em um PC é muito mais fácil do que em um MK.

O emulador não é perfeito. Existem alguns erros que distinguem a emulação da reprodução em uma versão real do console. Tentarei eliminá-los no futuro quando escrever uma nova versão do intérprete. A necessidade de suportar duas versões do intérprete (uma escrita em C ++ e a outra em TS) foi um pouco irritante, mas valeu a pena.

Juntando tudo


Tudo está pronto. Podemos exibir imagens com base na lógica do jogo tirada da EEPROM e processar a entrada do jogador a partir de botões e joysticks. Agora precisamos combinar tudo isso em um dispositivo durável.


Comecei fazendo furos nas placas de alumínio que prendem os botões e os joysticks no lugar. Em seguida, foi possível inserir essas placas nas ranhuras da caixa de madeira em que estavam convenientemente recuadas. Graças a isso, os jogadores não poderão arranhar acidentalmente o metal.



Coletamos tudo ao redor da tela e adicionamos pernas dobráveis ​​confiáveis ​​(para fácil armazenamento). As placas de metal pintadas com tinta preta parecem muito bonitas. Há uma sensação de que o projeto está realmente avançando para a conclusão.


Mas é claro que me enganei ao acreditar que o projeto estava quase completo. Aqui está a aparência do console por cerca de um mês.

Coletamos tudo, entendemos que o botão não funciona. Desmontamos novamente, resolvemos o problema com o botão Tudo parece funcionar. Entendemos que o joystick não funciona. Desmonte novamente. Mais cedo ou mais tarde, todos os botões e joysticks começam a funcionar. Mas então os LEDs param de funcionar. Para corrigi-los, é necessário desmontar o console inteiro e remontar desde o início. Isso durou cerca de um mês, até que finalmente tudo funcionou.

Resultado




Tudo está pronto! Depois de iniciar o trabalho no projeto, já se passaram 3 a 4 meses. Portanto, é lógico que minha motivação para continuar a melhorar o intérprete, pintar a árvore e desenvolver novos jogos já tenha secado. No momento da redação deste texto, já havia um mês, quando eu estive envolvido pela última vez no projeto. Finalmente ganhei forças para escrever este post documental para mim e para você, meu leitor. Embora eu não espere que alguém em sã consciência leia realmente tudo o que escrevi, espero que você tenha gostado de ver fotos do desenvolvimento gradual do projeto.

Mas se você ler tudo isso, poderá estar interessado no repositório Github do projeto. Claro, todo o meu código é de código aberto! https://github.com/Arcadable

Outros planos de desenvolvimento de projetos:

- Primeiro, você precisa criar um cartucho mais bonito para jogar Pong for four, como mostra o último vídeo. Embora seja um pedaço de papelão com pedaços curvos de papel alumínio, que são usados ​​como superfícies de contato.

- Além disso, quero pintar melhor o console. Inicialmente, planejei pintá-lo com amigos muito experientes que gostam de pintar. No entanto, agora devido à pandemia, isso não é possível.

- Finalmente, como eu disse algumas vezes, vou reescrever o intérprete de uma maneira mais eficiente. Também melhorarei o ambiente de desenvolvimento para facilitar os jogos. Atualmente, o design de aplicativos da Web está prejudicando a produtividade.


Compensação pela gravação de vídeos verticais.

All Articles