quinta-feira, 9 de dezembro de 2010

Controle do Super Nintendo SNES no Arduino

Fui convidado para palestrar sobre Arduino (pra variar... ;-) na Semana de Computação da UFF (de 8 a 11 de novembro de 2010) e, sempre que apresento a plataforma de hardware e software livres, gosto de demonstrar exemplos e um projeto - por exemplo: no FISL11 demonstrei o Turiquinhas, no Dev in Rio 2010 demonstrei o Semáforo DojoRio. Como o Turiquinhas ainda está na oficina (com o eixo quebrado), resolvi fazer um pequeno projeto prático para apresentar na Semana de Computação da UFF: como controlar servomotores utilizando um controle de SNES.

Controle do SNES (Super NIntendo Entertainment System)
Foto por armless

Apresentei esse projeto também na Latinoware 2010, em Foz do Iguaçu/PR (dentro da Itaipu Binacional) - que foi um evento sensacional! Para quem não teve a oportunidade de assistir, seguem os slides de minha palestra, que não mudaram muito de um evento para o outro:

Chega de enrolação - vamos agora então falar de como pegar os dados do controle de SNES com o Arduino! Inicialmente, para fazer a aquisição dos dados (saber quais botões estão pressionados no momento), tentei utilizar a biblioteca NESpad/SNESpad, porém não obtive sucesso - já havia tentado utilizá-la no FISL11, junto com o Fávio Amieiro, mas ela não funcionou, porém na época não pesquisei a fundo sobre o problema. Dessa vez resolvi pesquisar mais sobre, aprendi como um controle de SNES funciona internamente e verifiquei que a temporização da biblioteca não estavam corretos, daí postei uma issue no issue tracker do NESpad para que eles corrijam o problema, porém o autor disse que nos controles de SNES dele a temporização funcionava. Então decidi criar uma função (no melhor estido DIY :D) para fazer aquisição dos dados de acordo com a descrição que havia lido. Em breve darei mais uma olhada na biblioteca para verificar qual problema pode ter acontecido e tentar fazê-la funcionar para qualquer controle (pode ser que o controle que o autor da biblioteca tenha utilizado funcione de forma diferente do meu).

Antes de mostrar o código, vamos ver como o controle de SNES se comunica com o mundo externo: o conector possui 7 pinos, onde apenas 5 são realmente utilizados. Veja o esquema:

Pinagem do controle de SNES
Basicamente, internamente o controle do SNES tem um registrador de 16 bits cuja saída é serial (entrada possivelmente paralela). O que temos que fazer é dar um pulso no pino strobe e então pegar bit a bit (no total são 16) no pino data, dando um pulso entre cada bit no pino clock a cada 6 microsegundos (período = 12 microsegundos). Os pinos +5V e GND fornecem energia para o circuito interno do controle e os pinos N/C não são utilizados.

O que fiz então foi uma função que respeita a temporização e joga os 16 bits em uma variável do tipo unsigned int; a função demora em torno de 200 microsegundos para retornar esse inteiro (de 16 bits) - o detalhe é que o controle tem apenas 12 botões (cima, baixo, esquerda, direita, A, B, X, Y, select, start, L e R), então os 4 bits mais significativos do registrador de 16 bits não são utilizados (vêm sempre com o mesmo valor). Outro ponto importante é que o registrador é ativado em baixa (1 = botão não pressionado, 0 = botão pressionado), então no final da minha função eu inverto bit a bit dos valores lidos, para facilitar o uso dos dados. Além disso, defini algumas constantes que relaciona a posição do bit com cada botão do controle (exemplo: o botão B corresponde ao bit menos significativo). O código, que está disponível no meu BitBucket, ficou assim:

O código acima apenas lê os valores e controla dois servomotores e um alto falante, de acordo com os botões que foram pressionados. A cada loop, pego o estado dos botões com o comando:

 buttonsPressed = getSNESbuttons(CLOCK, STROBE, DATA); 
(os parâmetros da função são os números dos pinos onde o clock, strobe e data estão ligados) e para verificar, por exemplo, se o botão para cima foi apertado, faço um AND bit-a-bit com a constante relativa a esse botão:
if (buttonsPressed & SNES_UP) {
    //apertou botão para cima...
}
Segue a foto do projeto que apresentei nas palestras:

Arduino + controle de SNES + servomotores + alto falante - Foto por Tatiana Al-Chueyr na Latinoware 2010
Arduino + controle de SNES + servomotores + alto falante (foto por Tatiana Al-Chueyr, Latinoware 2010)

Sobre esse pequeno projeto e a palestra que ministrei na UFF: parece que a galera gostou - bastante gente da UFF entrou na lista ArduInRio. :-)

Ficou interessado? Gostou do artigo? Quer saber mais sobre o Arduino? Que tal fazer um Curso de Arduino no Rio de Janeiro? ;-) Caso tenha utilizado a função que criei (ela é software livre!) ou tenha qualquer dúvida, comente!

4 comentários:

  1. Show, Turicas!!!
    Muito legal esse post. Vou até usar esse código para brincar com o controle velho que eu tenho lá em casa.
    Parabéns.

    ResponderExcluir
  2. Muito bom Turicas!! Vou arrumar um controle de snes pra tentar brincar com isso.
    Seria interessante utilizar isso pra fazer um controle remoto (usando wifi ou bluetooth).

    ResponderExcluir
  3. Felipe,
    existe a possibilidade de ler os dados de um controle sem fio também usando o shield Bluetooth - o controle de Wii (Wiimode) ou o do Play Station (sem fio). :)

    ResponderExcluir
  4. Nintendo is one of the most advanced consoles of all the Nine that existed. Where to such perfection Playstation and Xbox? They do not stand close in this regard. And this is a confirmation.

    ResponderExcluir