Introduction: Interfacing PS2 Controller With AVR -Bit Bang

Hey friends in this instructable I will show you how to interface sony PS2 controller with AVR microcontroller .This will be your handy code which you can  be used in future to control robots .You can get analogue value from joystick  which can be used to control the speed of motor .
I have explained the code with help of 3 section
    1)what is SPI ?
    2)PS2 protocol
    3)Finally the code
 I have written the code for  atmega640 in BIT BANGbut anyone who as worked with AVR can easily write a code for any series atmega microcontroller.You can also check 'Interfacing PS2 with AVR -SPI ' in which i have used internal SPI of AVR. 

Let's get started!

Step 1: Material and Software Required

Materials required
1)sony PS2 controller 
2)Atmega640
3)AVRISP programmer(or any other programmer depending upon your microcontroller  ) 
4)one to one connector 

software
1)Winavr(or any AVR compiler)
2)progISP

and of course you should  have basic knowledge of AVR programming and embedded C. 

Step 2: Understanding SPI

what is SPI ?
     (you can skip this step if u know SPI communication )
     Serial Peripheral Interface Bus or SPI bus is a synchronous serial data link de facto standard, that operates in full duplex mode. Devices communicate in master/slave mode where the master device initiates the data frame. Multiple slave devices are allowed with individual slave select (chip select) lines. Sometimes SPI is called a four-wire serial bus, contrasting with three-, two-, and one-wire serial buses. SPI is often referred to as SSI (Synchronous Serial Interface).
     In SPI there are 6 connection
      MOSI-master out slave in
      MISO-master in slave out
      SCK-clock is provided by master to slave by this pin
      SS-slave select ,master selects a slave by this pin
      VCC-voltage pin
      GND -ground
Both master and slave have shift register  when master provides 8 clocks the content of each register is interchanged and data is transferred  from master to slave and vice versa thus a full duplex communication . 

so what  is BIT BANG ?
      Bit banging is a technique for serial communications using software instead of dedicated hardware. Software directly sets and samples the state of pins on the microcontroller, and is responsible for all parameters of the signal: timing, levels, synchronization, etc. In contrast to bit banging, dedicated hardware (such as a modem, UART, or SPI) handles these parameters and provides a (buffered) data interface in other systems, so software is not required to perform signal demodulation. Bit banging can be implemented at very low cost, and is used in, for example, embedded systems.
     In this instructable I am using bit bang technique instead of using  AVR's SPI hardware .This code will work fine for many of our task .
 
you can refer this
1)http://avrbeginners.net/architecture/spi/spi.html 
 2)http://www.embedded.com/electronics-blogs/beginner-s-corner/4023908/Introduction-to-Serial-Peripheral-Interface      
site to understand SPI further

Step 3: PS2 Protocol

This http://store.curiousinventor.com/guides/PS2/ best site  to understand PS2 protocol.


Byte Sequence to Configure Controller for Analog Mode

-- 0x43 Go into configuration mode
byte #                      1     2     3     4         5
Command (hex)  01   43   00  0x01   00
Data (hex)              FF  41   5A  FF       FF
section header digital

-- 0x44 Turn on analog mode
byte #                     1     2     3   4         5         6   7   8    9
Command (hex) 01   44   00  0x01 0x03   00 00 00 00
Data (hex)             FF  F3   5A 00      00       00 00 00 00
section header config parameters

-- 0x43 Exit config mode
byte #                         1      2    3     4         5   6   7     8    9
Command (hex)     01   43   00   0x00   5A 5A 5A 5A 5A
Data (hex)                 FF  F3   5A   00       00 00 00 00 00
section header config parameters

Step 4: Hardware Setup


Setting a hardware is very easy you have to connect only 6 pins given below.you can use one to one connectors to connect PS2 side with board.Remove the plastic cover of  one to one wire and make the gap bigger to fit in PS2 connector and then insulate the cnnector
 

     -------------LOOKING AT THE PLUG-------------------
                        -------------------------------
PIN 1->          | o  o  o | o  o  o | o  o  o |
                      \___________________/
   
PIN # USAGE
1    DATA                             -PORTB3
2    COMMAND                   -PORTB2   
3    N/C (9 Volts unused)
4    GND                              -GND pin
5    VCC                               -VCC pin
6    ATT                                -PORTB1
7    CLOCK                         -PORTB0
8    N/C
9    ACK

you can connect wire as shown in picture above.I have made adapter to connect PS2 controller  with  the board 

Step 5: Explaning the Code

finally  the code
if u have understood the flowchart  this will be quite easy 

  The code has only 2 function
 1)  int gameByte(short int command)
 2)  void  int_PS2inanalougemode()
  


int gameByte(short int command)
{
        short int i ;                                                 // variable used as counter
        _delay_us(1);                                         
        short int data = 0x00;                             // clear data variable to save setting low bits later.
       
  for(i=0;i<8;i++)                                          // as 8 bytes are transferred i<8
        {
   if(command & _BV(i))                          //each bit of command is ANDED with 1 one by one, thus value of that  cmnd is if in                                                                               condition  
     {
    sbi(PORTB, PScommand);       // if command is one command pin is set
     }
   else
     {
    cbi(PORTB, PScommand);     // else command pin is  made zero 
     }
           
  
   cbi(PORTB, PSclock);                        // CLOCK LOW
   _delay_us(1);                                    // wait for output to stabilise
           
  
   if((PINB & _BV(PSdata)))
    {
    sbi(data, i);                               // read PSdata pin and store
    }
   else
    {
       cbi(data, i);
    }
   
  sbi(PORTB, PSclock);                             // CLOCK HIGH
        }
 
        sbi(PORTB, PScommand);       
        _delay_us(20);                                                   // wait for ACK to pass.
        return(data);


}

void  int_PS2inanalougemode()-
this function puts the controller in analogue mode until it returns value 0x73 in 2nd byte which indicates that PS2 controller  is in analogue mode if it doesn't return its increments the counter  and continues to put PS2 controller is in analogue mode     
 
int main(void)
in main loop we  simply poll the input of PS2 controller .
video

complete code is here

Comments

author
PranavH5 (author)2016-09-28

does this work with the wireless ps2 controller also?

author
arunimac (author)2015-08-14

Hi this is my code...
Can you please review it and just let me know what I am doing wrong
In this code initially what I have done is seen if pressing the buttons are giving me high logic on the specified pins
What's happening is that I am not able to come out of the loop in which he is checking for configuration and analog mode..

author
sunil93 (author)arunimac2015-08-14

does the red LED on the PS2 controller goes on?

if yes it means PS2 is going in analog mode but it is unable to tell the microcontroller

if no then PS2 contoller is not going in analog mode and you need to check the connection

author
arunimac (author)sunil932015-08-17

hi

the red light stays on for the whole duration but clicking any other does not give me any output in output port

that means ps2 is not able to send 0x73 to microcontroller

what should i do?

author
arunimac (author)sunil932015-08-14

red led goes on when just given vcc and ground
when given 5v with code it switch on's and when again clicked its goes off
but when i connect all the pins the pressing of buttons is not giving me high pins in my output port(portA)
is anything wrong with my code ??

author
sunil93 (author)arunimac2015-08-14

try out this code its for atmega640 the value from joystick is displayed on portK you can use any port just declare it output first

AS this code is working it should work

if not probably then there will be some connection problem ,solve it and then you could build your code upon this

/*

BIT BANG PS2 for atmega640 -connection according to SPI

-------------LOOKING AT THE PLUG-------------------

-------------------------------

PIN 1->| o o o | o o o | o o o |

\_____________________________/

PIN # USAGE

1 DATA

2 COMMAND

3 N/C (9 Volts unused)

4 GND

5 VCC

6 ATT

7 CLOCK

8 N/C

9 ACK

----NOTES--

*> 0x5A(by controller) to say "here comes the data".

*> bite 1. header. (should possibly put test on this byte to detect unplugging of controller.)

ie if (temp==0x73)

*/

#ifndef F_CPU

#define F_CPU 14745600UL // or whatever may be your frequency

#endif

#include<avr/io.h>

#include<avr/interrupt.h>

#include<util/delay.h>

#define PSdata 3 // PB3

#define PScommand 2 // PB5

#define PSclock 1 // PB2

#define PSattention 0 // PB6

#define sbi(x,y) x|=(1<<y)

#define cbi(x,y) x&=~(1<<y)

//#define _BV(y) (1<<y)

// PSx controller communication function.

// send a byte on the command line and receive one on the data line.

// needs Attention pin to have gone low before called to activate controller.

int sendTo_slave(short int command)

{

short int i ;

_delay_us(1);

short int data = 0x00; // clear data variable to save setting low bits later.

for(i=0;i<8;i++)

{

if(command & _BV(i))

{

sbi(PORTB, PScommand); // bit bang "command" out on PScommand wire.

}

else

{

cbi(PORTB, PScommand);

}

cbi(PORTB, PSclock); // CLOCK LOW

_delay_us(1); // wait for output to stabilise

if((PINB & _BV(PSdata)))

{

sbi(data, i); // read PSdata pin and store

}

else

{

cbi(data, i);

}

sbi(PORTB, PSclock); // CLOCK HIGH

}

sbi(PORTB, PScommand);

_delay_us(20); // wait for ACK to pass.

return(data);

}

void int_PS2inanalougemode()

{

// this loop continues to put PSx controller into analouge mode untill the

// controller responds with 0x73 in the 2nd byte.

// (PS2 controller responds with 0x73 when in analouge mode.)

// the status LEDs will continue to count upwards untill a controller is found.

// if everything is working correctly this should happen on the first pass of

// this loop but occasionally errors occur and a 2nd or 3rd itteration happen.

unsigned char chk_ana = 0, cnt = 0;

while(chk_ana != 0x73)

{

// put controller in config mode

sbi(PORTB, PScommand);

sbi(PORTB, PSclock);

cbi(PORTB, PSattention);

sendTo_slave(0x01);

sendTo_slave(0x43);

sendTo_slave(0x00);

sendTo_slave(0x01);

sendTo_slave(0x00);

sbi(PORTB, PScommand);

_delay_ms(1);

sbi(PORTB, PSattention);

_delay_ms(10);

// put controller in analouge mode

sbi(PORTB, PScommand);

sbi(PORTB, PSclock);

cbi(PORTB, PSattention);

sendTo_slave(0x01);

sendTo_slave(0x44);

sendTo_slave(0x00);

sendTo_slave(0x01);

sendTo_slave(0x03);

sendTo_slave(0x00);

sendTo_slave(0x00);

sendTo_slave(0x00);

sendTo_slave(0x00);

sbi(PORTB, PScommand);

_delay_ms(1);

sbi(PORTB, PSattention);

_delay_ms(10);

// exit config mode

sbi(PORTB, PScommand);

sbi(PORTB, PSclock);

cbi(PORTB, PSattention);

sendTo_slave(0x01);

sendTo_slave(0x43);

sendTo_slave(0x00);

sendTo_slave(0x00);

sendTo_slave(0x5A);

sendTo_slave(0x5A);

sendTo_slave(0x5A);

sendTo_slave(0x5A);

sendTo_slave(0x5A);

sbi(PORTB, PScommand);

_delay_ms(1);

sbi(PORTB, PSattention);

_delay_ms(10);

// poll controller and check in analouge mode.

sbi(PORTB, PScommand);

sbi(PORTB, PSclock);

cbi(PORTB, PSattention);

sendTo_slave(0x01);

chk_ana = sendTo_slave(0x42); // the 2nd byte to be returned from the controller should = 0x73 for "red" analouge controller.

sendTo_slave(0x00);

sendTo_slave(0x00);

sendTo_slave(0x00);

sendTo_slave(0x00);

sendTo_slave(0x00);

sendTo_slave(0x00);

sendTo_slave(0x00);

sbi(PORTB, PScommand);

_delay_ms(1);

sbi(PORTB, PSattention);

_delay_ms(10);

// keep increasing counter to be dispalyed untill PSx controller confirms it's in analouge mode.

PORTK=cnt++;

if (cnt > 254){ cnt=0;}

}

}

int main(void)

{

DDRK=0XFF;

DDRH=0xFF;

// PSx controller I/O pin setup:

sbi(DDRB, PB1); // clock. output. (blue)

cbi(DDRB, PB3); // data. input. (brown)

sbi(PORTB, PB3); // enable pullup resistor

cbi(DDRB, PB4); // acknolage. input. (green)

sbi(PORTB, PB4); // enable pullup resistor

sbi(DDRB, PB2); // command. output. (orange)

sbi(DDRB, PB0); // attention. output. (yellow)

int_PS2inanalougemode();

unsigned char data0, data1, data2, data3, data4, data5,RV,RH,LV,LH,temp;

while(1)

{

sbi(PORTB, PScommand); // start communication with PSx controller

sbi(PORTB, PSclock);

cbi(PORTB, PSattention);

sendTo_slave(0x01); // bite 0. header.

temp = sendTo_slave(0x42); // bite 1. header. (should possibly put test on this byte to detect unplugging of controller.)

sendTo_slave(0x00); // bite 2. header.

data0 = sendTo_slave(0x00); // bite 3. first data bite.

data1 = sendTo_slave(0x00); // bite 4.

RH = sendTo_slave(0x00); // RH

RV = sendTo_slave(0x00); // RV UP-00 DOWN-255

LH = sendTo_slave(0x00); // LH lEFT-00 RIGHT-255

LV = sendTo_slave(0x00);

_delay_us(1);

sbi(PORTB, PScommand); // close communication with PSx controller

_delay_us(1);

sbi(PORTB, PSattention); // all done.

PORTK=LH;

}

}

ALL the best!

author
arunimac (author)2015-08-04

hi i am trying to interface avr with ps2 controller and i am using atmega8535 for it
this might sound silly but since command line is used by ps2 to send to controller so why are we using those bytes in the code, if we are going to burn the code in microcontroller
and can we actually check those commands in code whether we have received them from ps2 and then act accordingly (as per defined under those conditions) like when i press R2 i want to writs the code to control motor inside that block . can it be used??
please do reply or please mail me at arunimachaurasia@gmail

author
sunil93 (author)arunimac2015-08-14

Hey ,Yeah you can program microcontroller to control motor on button press

can you please specify where are you facing problem.

so that i can help you out

author
arunimac (author)sunil932015-08-14



#include
#include
#define sbi(port,bit) (port) |= (1<<(bit))
#define cbi(port,bit) (port) &= ~(1<<(bit))
#ifdef F_CPU
#define F_CPU 16000000UL
#endif F_CPU
#define sei()
#include
#define wdt_enable(timeout)
#define wdt_reset()

#define PSclock 7 // PB7
#define PSdata 6 // PB6
#define PSacknolage 3 // PB3
#define PScommand 5 // PB5
#define PSattention 4 // PB4
#define VCC 1 //PB1
#define GND 0 //PB0

unsigned char gameByte(unsigned char command);


unsigned char gameByte(unsigned char command)
{
short int i ;
_delay_us(1);
short int data = 0x00; // clear data variable to save setting low bits later.
for(i=0;i<8;i++)
{
if(command & _BV(i)) sbi(PORTD, PScommand); // bit bang "command" out on PScommand wire.
else cbi(PORTD, PScommand);
cbi(PORTD, PSclock); // CLOCK LOW
_delay_us(1); // wait for output to stabilise
if((PIND & _BV(PSdata))) sbi(data, i); // read PSdata pin and store
else cbi(data, i);
sbi(PORTD, PSclock); // CLOCK HIGH
}
sbi(PORTD, PScommand);

_delay_us(20); // wait for ACK to pass.

return(data);
}


// put 1 byte on the 8 LEDs. obviously you need to change the output pins to
// match your board.


int main(void)
{
sbi(DDRB,PB1);
sbi(PORTB, PB1);
sbi(DDRB,PB0);
cbi(PORTB,PB0);

//_delay_ms(10);
// set the baud rate of the UART
// (needed for transmitting over radio module).
//uartSetUp(2400);

// PSx controller I/O pin setup:
sbi(DDRB, PB7); // clock. output. (blue)

cbi(DDRB, PB6); // data. input. (brown)
sbi(PORTB, PB6); // enable pullup resistor

cbi(DDRB, PB3); // acknolage. input. (green)
sbi(PORTB, PB3); // enable pullup resistor

sbi(DDRB, PB5); // command. output. (orange)

sbi(DDRB, PB4); // attention. output. (yellow)

// enable interupts
sei();

// watchdog timer reset and enable
//wdt_reset();
//wdt_enable(0x02);

//timerInit();


// this loop continues to put PSx controller into analouge mode untill the
// controller responds with 0x73 in the 2nd byte.
// (PS2 controller responds with 0x73 when in analouge mode.)
// the status LEDs will continue to count upwards untill a controller is found.
// if everything is working correctly this should happen on the first pass of
// this loop but occasionally errors occur and a 2nd or 3rd itteration happen.
unsigned char chk_ana = 0;
while(chk_ana != 0x73){
// put controller in config mode
sbi(PORTB, PScommand);
sbi(PORTB, PSclock);
cbi(PORTB, PSattention);


gameByte(0x01); //POLL ONCE JUST FOR ONCE
gameByte(0x42);
gameByte(0x00);
gameByte(0xFF);
gameByte(0xFF);


gameByte(0x01);
gameByte(0x43);
gameByte(0x00);
gameByte(0x01);
gameByte(0x00);

sbi(PORTB, PScommand);
_delay_ms(1);
sbi(PORTB, PSattention);

_delay_ms(1);

// put controller in analouge mode
sbi(PORTB, PScommand);
sbi(PORTB, PSclock);
cbi(PORTB, PSattention);

gameByte(0x01);
gameByte(0x44);
gameByte(0x00);
gameByte(0x01);
gameByte(0x03);
gameByte(0x00);
gameByte(0x00);
gameByte(0x00);
gameByte(0x00);

sbi(PORTB, PScommand);
_delay_ms(1);
sbi(PORTB, PSattention);

_delay_ms(1);
//TO CONFIGURE TO RETURN PRESSURE VALUES
sbi(PORTB, PScommand);
sbi(PORTB, PSclock);
cbi(PORTB, PSattention);
gameByte(0x01);
gameByte(0x4F);
gameByte(0x00);
gameByte(0xFF);
gameByte(0xFF);
gameByte(0x03);
gameByte(0x00);
gameByte(0x00);
gameByte(0x00);

sbi(PORTB, PScommand);
_delay_ms(1);
sbi(PORTB, PSattention);

// exit config mode
sbi(PORTB, PScommand);
sbi(PORTB, PSclock);
cbi(PORTB, PSattention);


gameByte(0x01);
gameByte(0x43);
gameByte(0x00);
gameByte(0x00);
gameByte(0x5A);
gameByte(0x5A);
gameByte(0x5A);
gameByte(0x5A);
gameByte(0x5A);

sbi(PORTB, PScommand);
_delay_ms(1);
sbi(PORTB, PSattention);

//_delay_ms(10);

// poll controller and check in analouge mode.
sbi(PORTB, PScommand);
sbi(PORTB, PSclock);
cbi(PORTB, PSattention);

gameByte(0x01);
chk_ana = gameByte(0x42); // the 2nd byte to be returned from the controller should = 0x73 for "red" analouge controller.
gameByte(0x00);
gameByte(0x00);
gameByte(0x00);
gameByte(0x00);
gameByte(0x00);
gameByte(0x00);
gameByte(0x00);

sbi(PORTB, PScommand);
_delay_ms(1);
sbi(PORTB, PSattention);
//_delay_ms(10);


sbi(DDRC ,PC1);
cbi(PORTC ,PC1);
_delay_ms(10);

}
sbi(DDRC ,PC1);
sbi(PORTC ,PC1);
_delay_ms(10);


while(chk_ana==0x73)
{
short int temp, data0, data1, data2=0,data3=0; // data4, data5,data6,data7,data8,data9,data10,
short int i=0 ;
while (1){

sbi(PORTB, PScommand); // start communication with PSx controller
sbi(PORTB, PSclock);
cbi(PORTB, PSattention);

gameByte(0x01); // bite 0. header.
temp = gameByte(0x42); // bite 1. header. (should possibly put test on this byte to detect unplugging of controller.)
gameByte(0x00); // bite 2. header.

data0 = gameByte(0x00); // bite 3. first data bite.
data1 = gameByte(0x00); // bite 4.
data2 = gameByte(0x00); // bite 5.
data3 = gameByte(0x00); // bite 6.
//data4 = gameByte(0x00); // bite 7.
//data5 = gameByte(0x00); // bite 8.
//data6 = gameByte(0x00);
//data7 = gameByte(0x00);
//data8 = gameByte(0x00);
//data9 = gameByte(0x00);
//data10 = gameByte(0x00);

if(data0==0xFE) //SELECT
{
sbi(DDRA, PA1);
sbi(PORTA, PA1);
_delay_ms(1);

}
else if(data0==0x7F) //LEFT ARROW
{
sbi(DDRA, PA2);
sbi(PORTA, PA2);
_delay_ms(1);
}
else if(data0==0xBF) //DOWNWARDS
{
sbi(DDRA, PA3);
sbi(PORTA, PA3);
_delay_ms(1);
}
else if(data0==0xDF) //RIGHT ARROW
{
sbi(DDRA, PA3);
sbi(PORTA, PA3);
_delay_ms(1);
}
else if(data0==0xEF) //UPWARDS
{
sbi(DDRA, PA1);
sbi(PORTA, PA4);
_delay_ms(1);
}
else if(data2==0x7F)
{
//RIGHT X-AXIS JOYSTICK
sbi(DDRA ,PA5);
sbi(PORTA, PA5);
_delay_ms(1);
}
else //NOTHING IS PRESSED
{
sbi(DDRA, PA6);
sbi(PORTA, PA6);
_delay_ms(1);
}




_delay_us(10);
sbi(PORTB, PScommand); // close communication with PSx controller
_delay_ms(1);
sbi(PORTB, PSattention); // all done.

};

};

}


author
marek.sobotka.1 (author)2015-03-17

Hi

My controller can't make a connection. Controller is wireless and leds only blink Maybe you know why? How to make proper delays? Or maybe it is something else?

author
girish7gujar (author)2014-03-03

Hi, please update your pin connection as
per following pin connection, your code works for digital pad but for
analog mode my receiver's link led blinks.please help in analog mode.

PIN # USAGE
1 DATA -PORTB3
2 COMMAND -PORTB2
3 N/C (9 Volts unused)
4 GND -GND pin
5 VCC -VCC pin
6 ATT -PORTB0
7 CLOCK -PORTB1
8 N/C
9 ACK-PORTB4

Red_Gear_PS2_Wireless_Controller_m_1_2x-07bec.jpg
author
sunil93 (author)girish7gujar2014-03-09

friend I tested the code and it works for both analog and digital mode.give it a try and let me know

all the best