Playstation 2 Controller on Atmega168

This entry describes using a PlayStation 2 Dual Shock controller on an Atmega168 microcontroller. This will enable me to use it for robot control in the future.

Getting Started

Everything to know about connecting the controller and reading the packet standards can be found here: Curious Inventor Tutorial

However, that guide does not show the specifics for using an Atmel microcontroller, which is what I did and will show in this build log. There are other such projects on the internet, but I could not find any using the ready made SPI of the AVR, they were all bit-banged.

I found that jumper wires just about fit inside the connector.


Following the pin-out diagram of the link above, I made the following schematic:

Two pull-up resistors are necessary, the controller can only pull signals to ground. This is to keep it from interfering with other controllers using the same bus on the PlayStation.


The initialization of the SPI driver involves writing to the SPI Control Register (SPCR) and changing the Data Direction Register (DDR) of the required pins. The microcontroller will work as the master, to simulate being a PlayStation. In order to support both ps1 and ps2 controllers, the SPI will be set to run at 250kHz. With a microcontroller running at 16MHz, I got the following equation:

16000000 / 250000 = 64

Ergo, I’ll need to divide the fosc by 64, setting SPR1=1, SPR0=0 and SPI2X=0 (see datasheet for atmega168, pg. 168 rev2545r).

The ps2 controller is high-when-idle, which means that the MISO and MOSI pins will be giving 5v when no communication is done. This is set by CPOL=1. DORD=1 is needed to make the SPI send the LSB first. CPHA is sample on trail mode, the same mode as the ps2 controller uses.

SPE is SPI Enable and MSTR is for master mode. In order to read from MISO and write to SCK, MOSI and Attention, DDRB must be changed:

Now the SPI will be initialized. Writing a byte is done as described in the datasheet:

At the same time as a byte is sent using SPI, the SPI data register (SPDR) is filled with a received byte from the slave. Here comes a very important point regarding the PlayStation2 controller. When sending several bytes to the controller, it expects a delaytime of approximately 30 microseconds. Without this delaytime, the controller won’t respond at all to any requests.

These commands were wrapped into a separate spi library I called spi.h and spi.c, and will be included in the downloads section.

Talking to the controller

Sending a command
The attention line is pulled to ground whenever a data transmission is about to start. It is held there until all bytes of a specific command have been sent.

For each byte sent with the SPI_MTx function, one byte will be received. These bytes normally contains status information and other snacks that I’ve chosen to ignore. A for-loop sends the data packets described in the curious inventor tutorial. The functions ps2_configmode, ps2_analogmode, ps2_setupmotor, ps2_returnpres and ps2_exitconfig are simple wrappers to send these commands to the controller:

ps2_configmode puts the controller in configuration mode. Then all of the other functions described above is called. As a result, the controller will be set to analog mode, enable the motors and send pressure values for each button.

The following packet is sent to poll the controller:
byte nr – value – answer – function
1 – 0x01 – 0xff – Header
2 – 0x42 – 0x79 – Polling command
3 – 0x00 – 0x5a – Header
4 – 0x01 – 0xff – Small motor, on=0x01 off=0x00. smallmotor parameter
5 – 0xff – 0xff – Big motor, speed 0 to 255. speed parameter
6 – 0x00 – 0x7f – Right joystick x-axis. Put in ps2.rx
7 – 0x00 – 0x7f – Right joystick y-axis. Put in ps2.ry
8 – 0x00 – 0x7f – Left joystick x-axis. Put in ps2.lx
9 – 0x00 – 0x7f – Left joystick y-axis. Put in ps2.ly
10 – 0x00 – 0x00 – Right button pressure. Put in ps2.pressure[0]
11 – 0x00 – 0x00 – Left button pressure. Put in ps2.pressure[1]
12 – 0x00 – 0x00 – Up button pressure. Put in ps2.pressure[2]
13 – 0x00 – 0x00 – Down button pressure. Put in ps2.pressure[3]
14 – 0x00 – 0x00 – Triangle button pressure. Put in ps2.pressure[4]
15 – 0x00 – 0x00 – Circle button pressure. Put in ps2.pressure[5]
16 – 0x00 – 0x00 – Cross button pressure. Put in ps2.pressure[6]
17 – 0x00 – 0x00 – Square button pressure. Put in ps2.pressure[7]
18 – 0x00 – 0x00 – L1 button pressure. Put in ps2.pressure[8]
19 – 0x00 – 0x00 – R1 button pressure. Put in ps2.pressure[9]
20 – 0x00 – 0x00 – L2 button pressure. Put in ps2.pressure[10]
21 – 0x00 – 0x00 – R2 button pressure. Put in ps2.pressure[11]

The pressure values are 0 for nothing and 255 for max pressure.

Future work

The program uses 5.9% of the program memory and 7.6% of the data memory, also including the uart support used for debugging.The SPI transmission is very fast, but no interrupts were used, so uses extra cpu time for waiting for the SPI. This could be improved with the SPI interrupt vector.

So far, I haven’t used the PlayStation controller for anything fun. All I’ve done is to poll the data and control the motors inside. It would be cool to hook this up to a robot or at least use it to control a servo.




  1. tobias


    is it possible to get your code? (You can sent it via mail)
    The download dosn’t worke!

    Thank’s a lot!

    Kind regards

  2. Benjamin

    i dont understand on the two 10kOm resistors, do you have to connect them to the controler voltage and ground?

    1. larsivar

      Hi Benjamin! One of the resistors is connected from your voltage supply to the green wire and the pd7 on the microcontroller. The other one is connected from the voltage supply to the brown wire and the pb4 on the microcontroller.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">