Controle de Playstation no PIC – (Parte 2) – Desenvolvimento

Controle playstation no PIC

Depois  do primeiro post mostrando a comunicação entre um controle sem fio de Playstation 2 e um microcontrolador PIC e de vários pedidos dos visitantes do blog, estou retomando o assunto. Neste artigo estou utilizando o PIC modelo PIC16F877a para a decodificação e comunicação, mas o código pode facilmente ser migrado para outro microcontrolador da família PIC de acordo com as necessidades do seu projeto.

No primeiro post, mostrei um pequeno vídeo com o funcionamento do circuito proposto. Agora vamos mostrar como fazê-lo.

Antes de mais nada vamos conhecer os detalhes de um joystick utilizado pelo PlayStation 2.

O Joystick do PS2:

Bom, este controle fantástico dispensa maiores apresentações a respeito de sua jogabilidade. Com seus 16 botões digitais e seus dois joysticks analógicos, pode ser a solução perfeita e de baixo custo para muitos projetos nas áreas da mecatrônica.

 controle playstation 2

Existem ainda no mercado diversos controles “compatíveis” “Made in China” que barateiam ainda mais nosso projeto e funcionam perfeitamente. Não posso afirmar que todos os modelos disponíveis funcionem com este método, entretanto, todos as que eu testei funcionaram e partindo do pressuposto de que eles funcionam no console, devem também funcionar aqui.

controle playstation 2 paralelo

Como citei a pouco, este circuito funciona tanto com o controle com fio quanto com o sem fio. Já encontrei na internet artigos feitos para controles com fio q não funcionam com controles sem fio. Este artigo foi baseados nestes e modificado para operar também com os controles sem fio. O muda entre eles é o tempo entre as duas leituras do controle. Se o tempo for grande, o controle sem fio perde a comunicação e não volta.

Como o controle sem fio dá muito mais liberdade ao projeto, além de ser muito mais legal, vou basear todo o artigo neles. Caso queira utilizar um controle com fio, basta fazê-lo.

 A Pinagem do conector

O conector do Joystick do PS2 possui nove pinos conforme a figura abaixo. Nela observamos o padrão de cores utilizado pelos controles com fio originais da Sony, caso seu controle possua cores diferentes, guie-se pelo conector, claro, antes de cortar os fios! rs

pinagem conector playstation

 

Descrição dos Pinos do conector macho do Joystick do PlayStation

1 – Data:

-Sinal enviado do controle para o Playstation.

 -Este-Este sinal é uma transmissão serial de 8 bits sincronizado com o sinal do pino de clock por banda de descida. Este sinal contém a resposta ao sinal Command.

-Este pino é uma saída em coletor aberto e necessita de um resistor de pull-up (1 à 110k). Esta resistência é necessária porque o controle só pode conectar este pino à terra, então ela se faz necessária para garantir o nível lógico 1.

 2 – Command:

-Sinal enviado do Playstation para o controle.

 -É uma transmissão serial de 8 bits, também sincronizado com o pino de clock. Ele será utilizado para estabelecer a comunicação entre o controle e o nosso pseudo console, solicitando as informações necessárias conforme veremos logo.

3 – Vibration motor power:

-Sinal enviado do Playstation para o controle.

 -Esta função não está implementada neste artigo, mas através deste pino podemos fazer o controle vibrar. Particularmente ainda não testei esta função.

4 -GND:

-Terminal de referência para os demais sinais além da alimentação do circuito.

5 – Power 3,3V à 5 V:

 -Sinal enviado do Playstation para o controle.

 -Tensão de alimentação do controle ou, no nosso caso, do transmissor sem fio do controle. Algumas literaturas informam que os controles genéricos sem fio só funcionam com 3,3V, nos meus testes funcionaram com 5 V normalmente. As mesmas literaturas informam que os controles originais funcionam com qualquer tensão entre 3,3V e 5V.

6 – Att:

-Sinal enviado do console para chamar a atenção do controle.

 -Este sinal permanece em nível lógico baixo durante a transmissão.

7 – Clock (250kHz à 500kHz):

-Sinal enviado do Playstation para o controle.

 -Utilizado para manter sincronia entre o Playstation e o controle.

8 – Não utilizado.

9 – ACK (Acknoledge):

-Sinal enviado do controle para o Playstation.

 -Este sinal deve ir para nível lógico zero, por pelo menos um pulso de clock depois de cada um dos 8 bits enviado e ATT mantida em nível lógico zero. Se o Sinal de ACK não for para nível lógico zero dentro de cerca de 60uS, o Playstation começa a interagir com outros dispositivos.

 -Para o nosso projeto não precisamos nos preocupar com este sinal, vamos apenas ignorá-lo.

Revisando novamente nosso conector, verificamos que serão utilizados em nosso projeto os seguintes sinais:

 pinagem conector playstation 2

Comunicação

Durante o estudo dos sinais encontrei no site Curious inventor uma leitura em tempo real dos sinais de transmissão realizada com um osciloscópio. Nela podemos observar exatamente como se combinam todos os sinais. O texto informa que está é uma leitura real entre um Playstation e uma Guitarra do Guitar Hero.

 sinais controle playstation
 

Simplistamente, a comunicação entre o Playstation e o controle ocorre da seguinte maneira:

  1. -O console envia um sinal para identificar se o controle está conectado;
  2. -O controle responde a este sinal e a comunicação é estabelecida;
  3. -Em seguida o console solicita os dados do controle;
  4. -O controle, então, envia o estado de todos os botões naquele momento e aguarda nova solicitação.

 Agora, com mais detalhes, vamos ver como funciona esta comunicação:

  • Quando o Playstation quer ler informações de um controle, primeiramente seta o sinal ATT para zero e envia o comando de inicio (0x01);
  • O Controle então responde com sua identificação: ( 0x41 = Digital; 0x73=Analógico) Em nosso projeto este valor não será utilizado;
  • Ao mesmo tempo em que o controle envia este byte de identificação, o Playstation está enviando o byte (0x42) para solicitar dados ao controle;
  • Em seguida o Playstation envia um byte nulo (0x00) enquanto o controle responde com o byte (0x5A) indicando que irá iniciar o envio dos dados dos botões;
  • Na sequência,são enviados 6 bytes com o status de todos os botões;
  • Depois de receber estes 6 bytes, o Playstation coloca o pino ATT em 1 para que o controle para de enviar dados;
  • A comunicação entre em repouso até que seja solicitado novo pedido de dados ao controle.

Com isso, a comunicação acontecerá da seguinte maneira:

 

Controle em modo digital:

Controle em modo digital

Controle em modo analógico

 Controle em modo analógico

Hardware:

O hardware para este projeto é muito simples, sendo composta, além do próprio controle, de:

  • Um microcontrolador PIC16F877a. A escolha deste uC se deu apenas por ele estar disponível em meu laboratório, mas o projeto pode ser facilmente adaptado para o modelo preferido de cada um;
PIC 16F877a
  • Um adaptador ou extensor para o conector do controle. Isso para o caso de não se cortar o cabo original do controle e utilizar os fios diretamente. No caso do controle sem fio, é possível soldar um novo cabo diretamente dos pinos do transmissor sem fio, mas eu prefiro utilizar o adaptador e poder substituir o controle facilmente caso ele apresente algum defeito, além do acabamento ficar muito melhor. Eu comprei meu adaptador no site Chines Deal Extreme, eles não cobram frete para o Brasil e os preços são muito baixos;
Adaptador Conector Playstation
 
  • Para este projeto, utilizei alguns LEDs para demonstrar o funcionamento do circuito, cada vez que um botão é pressionado, um led correspondente se acende. Em uma aplicação real, fica livre a imaginação do projetista.

 A seguir o esquema eletrônico do circuito Controle Playstation no PIC:

 projeto Playstation no pic

Software:

O software foi baseado no programa apresentado no site Robotizando. O original foi escrito para apresentar os dados em uma serial a ser utilizado por um outro dispositivo. Este site é muito bom e apresenta diversos outros artigos muito interessantes.

Fiz a modificação no código original para se comunicar com um controle sem fio (o original não permite este uso). Além disso, em aplicações não tão robustas como em robôs pouco complexos, podemos utilizar um único uC para comunicação com o controle de Playstation ao mesmo tempo que com os demais periféricos do robô (motores,atuadores, etc).

//************************************************************************//
//                                                                  
// PROJETO COMUNICAÇÃO JOYSTICK PS2 WIRELESS COM PIC 16F877A
//                                                                 
//************************************************************************//
//Configuração PIC 
//************************************************************************//
#include <16F877A.h>

#FUSES NOWDT                                //Sem watch dog
#FUSES HS                                   //Cristal alta velocidade
#FUSES PUT                                  //Timer de alimentação
#FUSES NOPROTECT                            //Código desprotegido
#FUSES NOBROWNOUT                           //Sem reset de brown out
#FUSES NOLVP                                //Programação em alta voltagem
#FUSES NOCPD                                //Sem proteção de memória eeprom
#FUSES NOWRT 

#use delay(clock=20000000)                        // cristal de 20 Mhz

//Definição dos ports
#byte porta = 0x05
#byte portb = 0x06
#byte portc = 0x07
#byte portd = 0x08
#byte porte = 0x09

//************************************************************************//
//CONTROLE PLAYSTATION                                                       
//************************************************************************//
//Definição de I/Os                                                             
//************************************************************************//
#define cmd           PIN_D6             
#define att           PIN_D4         
#define clk           PIN_D5      
#define dado          PIN_D7      

#define LED_STATUS    PIN_E0             //Led indicador de status

#define start         PIN_A1
#define select        PIN_A0
#define servo_x_l     PIN_D0
#define servo_y_l     PIN_D1
#define servo_x_r     PIN_D2
#define servo_y_r     PIN_D3
#define sobe          PIN_B0
#define desce         PIN_B1
#define direita       PIN_B2
#define esquerda      PIN_B3
#define triangulo     PIN_B4
#define bolinha       PIN_B5
#define quadrado      PIN_B6
#define xis           PIN_B7
#define l1            PIN_C0
#define l2            PIN_C1
#define l3            PIN_C2
#define r1            PIN_C3
#define r2            PIN_C4
#define r3            PIN_C5

//************************************************************************//
//Definição de parâmetros de comunicação com o controle PS2 
//************************************************************************//

//Tempos em micro-segundos (us)
#define tempoInicio     20
#define tempoClk        20
#define tempoEntreByte  20
#define tempoEntreCiclo 40 //ms  

//************************************************************************//
//VARIAVEIS GERAIS                                                           
//************************************************************************//
int psxDado[6];      //Buffer do controle
int psxDadoAnt[6];   //Buffer do controle
int1 psxMudou;

//Botões do Controle
int1 bt_select = 0;
int1 bt_l3  = 0;
int1 bt_r3  = 0;
int1 bt_start = 0;
int1 bt_sobe  = 0;
int1 bt_desce  = 0;
int1 bt_esquerda  = 0;
int1 bt_direita = 0;

int1 bt_l1 = 0;
int1 bt_l2 = 0;
int1 bt_r1 = 0;
int1 bt_r2 = 0;
int1 bt_triangulo = 0;
int1 bt_bolinha   = 0;
int1 bt_quadrado  = 0;
int1 bt_xis = 0;

int X_Left=0;
int Y_Left=0;
int X_Right=0;
int Y_Right=0;

int psxCont;             //contador genérico

int1 estado;             //estado do led

//************************************************************************//
//CONSTRUÇÕES DAS FUNÇÕES
//************************************************************************//

//************************************************************************//
//psxLeByte - Captura um byte, enviado pelo controle através do pino DADO
//************************************************************************//

int psxLeByte()
{
   int aux=0;
   int c;

      for(c=0;c<=7;c++)           //Passa por todos os bits da variável psxByte
   {
      output_bit(clk,false);      //baixa o clock, para que o controle
                                  //disponibilize o dado no pino "dado"
      delay_us(tempoClk);   

      if(INPUT(dado)==1)
         bit_clear(aux, c);
      else
         bit_set(aux, c);

      output_bit(clk,true); //Sobe o clock
   }

   delay_us(tempoEntreByte);
   return (aux);   
}

//************************************************************************//
//psxGravaByte - Escreve um byte, enviado pelo controle através do pino CMD
//************************************************************************//

void psxGravaByte(int byteDado)
{
   int c;
   for(c=0;c<=7;c++) //Passa por todos os bits da variável psxByte
   {
       //Sobe ou desce o pino de acordo com cada bit da variável psByte
       //significa que estamos enviando o comando 0x01h para o controle
      if(bit_test(byteDado,c))
         output_bit(cmd,true); 
      else
         output_bit(cmd,false);         

      output_bit(clk,false);       //depois de setar o pino, manda clock  
                                   //       delay_us(tempoClk);   
                                   //aguarda um tempinho...
      output_bit(clk,true);        //up clock... NOTA: clock  
 }

   output_bit(cmd,true);           //up comando para sinalizar
   delay_us(tempoEntreByte);       //espera um tempinho entre um byte e outro
}

//************************************************************************//
//psxCopiaDado - copia buffer atual para anterior
//************************************************************************//

void psxCopiaDado()
{
   int i;
   for(i=0;i<6;i++)
      psxDadoAnt[i] = psxDado[i];
}

//*************************************************************************//
//psxComparaDado - Compara leitura anterior com a atual
//************************************************************************//

boolean psxComparaDado()
{
   int j;
   for(j=0;j<6;j++)
   {
      if(psxDadoAnt[j]!=psxDado[j])
         return (1);
   }
   return (0);
}

//************************************************************************//
//psxAtualizaVariaveis - converte buffer de leitura para variaveis discretas  
//************************************************************************//

void psxAtualizaVariaveis()
{
   if((psxDado[0] & 1) == 1)
      bt_select = 1;
   else
      bt_select = 0;

   if((psxDado[0] & 2) == 2)
      bt_l3 = 1;
   else
      bt_l3 = 0;

   if((psxDado[0] & 4) == 4)
      bt_r3 = 1;
   else
      bt_r3 = 0;

   if((psxDado[0] & 8) == 8)
      bt_start = 1;
   else
      bt_start = 0;

   if((psxDado[0] & 16) == 16)
      bt_sobe    = 1;
   else
      bt_sobe    = 0;

   if((psxDado[0] & 32) == 32)
      bt_direita = 1;
   else
      bt_direita = 0;

   if((psxDado[0] & 64) == 64)
      bt_desce  = 1;
   else
      bt_desce  = 0;

   if((psxDado[0] & 128) == 128)
      bt_esquerda  = 1;
   else
      bt_esquerda  = 0;

   if((psxDado[1] & 1) == 1)
      bt_l2 = 1;
   else
      bt_l2 = 0;

   if((psxDado[1] & 2) == 2)
      bt_r2 = 1;
   else
      bt_r2 = 0;

   if((psxDado[1] & 4) == 4)
      bt_l1 = 1;
   else
      bt_l1 = 0;

   if((psxDado[1] & 8) == 8)
      bt_r1 = 1;
   else
      bt_r1 = 0;

   if((psxDado[1] & 16) == 16)
      bt_triangulo = 1;
   else
      bt_triangulo = 0;

   if((psxDado[1] & 32) == 32)
      bt_quadrado  = 1;
   else
      bt_quadrado  = 0;

   if((psxDado[1] & 64) == 64)
      bt_xis= 1; 
   else
      bt_xis= 0;

   if((psxDado[1] & 128) == 128)
      bt_bolinha = 1;
   else
      bt_bolinha = 0;

   x_right = psxDado[2];
   y_right = psxDado[3];
   x_Left = psxDado[4];
   y_Left = psxDado[5];
}

//************************************************************************//
//psxLeControle - Executa a leitura do controle de playstation    
//************************************************************************//

void psxLeControle()
{
   int psxByte = 0;

   output_bit(cmd,true);    //up comando
   output_bit(clk,true);    //up clock
   output_bit(att,false);   //att em low - habilita o joystick
   delay_us(tempoInicio);   //aguarda o controle entender o att em low

   //Envia o primeiro byte para o controle - 0x01
   psxByte = 1;         
   psxGravaByte(psxByte);

   //Envia o segundo byte para o controle - 0x42h
   psxByte = 66;
   psxGravaByte(psxByte);

   //Envia o terceiro byte para o controle - 0x00
   psxByte = 0;
   psxGravaByte(psxByte);

   //Recupera os 6 próximo bytes
      for(psxCont=0; psxCont<6; psxCont++)
         psxDado[psxCont] = psxLeByte();

   //Volta o att para alto... libera o controle indicando que ele pode 
   //parar de enviar dados

   output_bit(att,true);   

   //Compara com a variável anterior e verifica se será 
   //necessário atualizar as variaveis discretas
   if(psxComparaDado()==1)
   {
      psxAtualizaVariaveis();
      psxMudou = true;
   }
   else
      psxMudou = false;

   psxCopiaDado();

}

//************************************************************************//
//atualizaSaidas - Atualiza o status de todas as saídas do PIC
//************************************************************************//

void atualizaSaidas()
{
   output_bit(start,bt_start);
   output_bit(select,bt_select);
   output_bit(sobe,bt_sobe);
   output_bit(desce,bt_desce);
   output_bit(direita,bt_direita);
   output_bit(esquerda,bt_esquerda);
   output_bit(triangulo,bt_triangulo);
   output_bit(bolinha,bt_bolinha);
   output_bit(quadrado,bt_quadrado);
   output_bit(xis,bt_xis);
   output_bit(l1,bt_l1);
   output_bit(l2,bt_l2);
   output_bit(l3,bt_l3);
   output_bit(r1,bt_r1);
   output_bit(r2,bt_r2);
   output_bit(r3,bt_r3);
}

//************************************************************************//
//PROGRAMA PRINCIPAL
//************************************************************************//

void main()
{

set_tris_a(0b00000100);
set_tris_b(0b00000000);
set_tris_c(0b00000000);
set_tris_d(0b00000001);
set_tris_e(0b00000000);
porta=0x00;
portb=0x00;
portc=0x00;
portd=0x00;
porte=0x00;

   while(1)
   {

         psxLeControle();

        delay_ms(20);

        estado=!estado;
        output_bit(LED_STATUS,estado); 

        atualizaSaidas();

   } //Fim do laço principal
}//Fim do programa

//************************************************************************//

Referências:

 Caso utilize este artigo como referência, por favor indique nosso endereço. Abaixo estão os links dos artigos que eu utilizei como minhas referências.

Marcelo Maciel

facebooktwittergoogle pluslinkedin

Engenheiro de Controle e Automação e Técnico Eletrônico com mais de 10 anos de experiência no desenvolvimento de dispositivos microcontrolados para pequenas e médias empresas em diversos ramos. Além disso, possui vivência na área de Automação em grandes empresas desde 1999.


  • Marcelo não estou conseguindo compilar o programa no ccs compiler, está apontando 4 erros tem como você me dizer o que pode ser por favor!
    Desde já agradeço! Espero o retorno.

    • Olá Bruno, obrigado pela visita ao Blog. Me diga quais são os erros que estão aprecendo pra vc. Testei o programa antes de postá-lo e está ok.
      Verifique se na hora de copiar algum comentário não ficou sem as barras “//” em alguma linha.

  • Aparece os seguintes erros:
    Erro 117 “Line 170(4,5):Improper use of a function identifier”
    Erro 12 “Line 170 (12,13):Undefined identifier i ”
    Erro 12 “Line 170 (16,17):Undefined identifier i “
    Erro 12 “Line 324 (17,18):Undefined identifier – psxCopiaDado”
    4 Erros , 0 Warning.

    • Olá Bruno, Copiei o código do blog e refiz o teste do código e não apareceram estes erros. Na verdade encontrei um colchete “}” comentado erroneamente ( com //) na função “void psxGravaByte(int byteDado)”, mas que não poderiam gerar os erros q vc mencionou. Peço que copie novamente o código e teste mais uma vez.
      Qual compilador vc está utilizando? Este Código foi escrito para utilização com o MPlab e CCS. Por acaso está utilizando apenas o CCS sem o Mplab?

  • Marcelo analisei melhor fiz o o projeto com calma e compilei novamente, deu certinho, obrigado pelas dicas seu programa não tinham erros o erro era a pressa.Pretendo contruir o circuito para usa-lo como um controle básico de projetos.
    Valeu Marcelo!

  • Beleza!

  • ola eu estou tentando fazer um carrinho através da sua ideia, da qual eu gostei muito, mas eu nao entendi muito bem, vc poderia fazer um tutorial no youtube mostrando como que faz? agradeço

  • eu nao entendi direito a parte da programação do microcontrolador PIC16F877a vc poderia fazer de uma forma mais facil ou colocar como fazer no tutorial? agradeço novamente

  • como fazer p/ daxar comando analogico sempre ON

  • como fazer p/ daxar comando analogico sempre ligado (sem precisar apertar botao analogico)

  • como faço para controlar varios servo?

    • Olá Thiago,
      Para controlar vários servos vc deve criar uma função PWM para cada um deles e controlar estas funções de acordo o valor lido pelo controle do Playstation. Estou preparando um artigo que fala sobre isso. Se cadastre no Blog para receber a um aviso quando este artigo for publicado.

Your Turn To Talk

Leave a Reply to Marcelo Maciel Cancel Reply

Your email address will not be published.