forums.ps2dev.org Forum Index forums.ps2dev.org
Homebrew PS2, PSP & PS3 Development Discussions
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Charging the PS3 controller

 
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PS3 Development
View previous topic :: View next topic  
Author Message
jimparis



Joined: 10 Jun 2005
Posts: 1179
Location: Boston

PostPosted: Thu Jan 21, 2010 7:46 am    Post subject: Charging the PS3 controller Reply with quote

USB is only supposed to provide power to devices that have correctly enumerated. In particular, devices aren't allowed to draw the maximum 500mA unless the host has specifically assigned a device configuration that requested this much power. Many devices either ignore this rule, or follow the USB Battery Charging Specification, which provides a means for configuring higher-current draw using a particular resistor configuration on the D+ and D- lines. This is used by most USB-charged cell phones etc, and so it's easy to get them to charge by just supplying 5v and a resistor. Most USB wall chargers have this resistor, too.

However, the Sixaxis and Dualshock 3 controllers don't follow this convention and require the full enumeration before they charge. This means that they can't charge with most USB chargers, and need to talk to a real USB host and go through the enumeration process before they'll start drawing power.

My friend wanted to build his own controller charging dock, so we came up with a circuit and some code that will perform just enough of the USB enumeration process to get the controller to charge. The PS3 controller is a full-speed USB device, which means you need to talk to it at 12MHz for this to work. We used an ATtiny24A, which is one of the smallest and cheapest MCUs you can get that can drive output pins that fast. The circuit is straightforward:



JP1 is just for in-circuit programming and isn't necessary if you program the chip some other way. U2 is any 3.3v regulator. JP2 is where you supply 5v.

Currently it only supports one controller. It needs to be directly connected to the controller; you can't use a hub. The MCU should be able to handle charging up to 4 controllers at once using the other free pins, but the code needs a bit of love before that will work.

The code is available here: ps3-charger.zip
Code:
/* ATTINY24 code to emulate enough of a USB host to get a PS3
   controller to charge.
 
   Copyright (c) 2010 Jim Paris <jim@jtan.com>
   BSD license
*/

#include <avr/io.h>

FUSES =
{
   .low = 0xff,
   .high = 0xff & FUSE_RSTDISBL & FUSE_SPIEN & FUSE_BODLEVEL0,
   .extended = 0xff & EFUSE_DEFAULT,
};

/* Connections:
   D+  PA3
   D-  PA7
*/

#define MASK_DP (1 << 3)
#define MASK_DM (1 << 7)
#define MASK_BOTH (MASK_DP | MASK_DM)
#define MASK_NONE 0
#define MASK_OUT MASK_BOTH
#define MASK_IN MASK_NONE
#define FS_J MASK_DP
#define FS_K MASK_DM
#define SE0 0

#define J "out %[_porta], %[_j]\n"
#define K "out %[_porta], %[_k]\n"
#define X "out %[_porta], %[_x]\n"
#define IN "out %[_ddra], %[_in]\n"
#define OUT "out %[_ddra], %[_out]\n"
#define D "nop\n" // delay 1 bit time

#define asmblock(str) asm volatile (str \
   : \
   : [_porta] "I" (_SFR_IO_ADDR(PORTA)), \
     [_ddra] "I" (_SFR_IO_ADDR(DDRA)), \
     [_j] "r" (FS_J), \
     [_k] "r" (FS_K), \
     [_x] "r" (SE0), \
     [_in] "r" (MASK_IN), \
     [_out] "r" (MASK_OUT) \
   : "memory")

/* wait for the next "ms" timer ticks, which occur at 1ms intervals */
void mwait(int ms)
{
   static int initialized = 0;

   if (!initialized) {
      TIMSK1 = 0x00; /* no interrupts */
      OCR1A = 12000 - 1; /* 12000 cycles = 1 ms */
      TCCR1B = _BV(WGM12) | _BV(CS10); /* no prescale, CTC mode */
      initialized = 1;
   }

        while (ms-- > 0) {
      TIFR1 &= ~_BV(TOV1);
                while((TIFR1 & _BV(TOV1)) == 0)
                        continue;
        }
}

/* Send a bus reset */
static void send_reset(void)
{
   /* SE0 for >= 10 ms */
   PORTA = SE0;
   DDRA = MASK_OUT;
   mwait(10);
   DDRA = MASK_IN;
}

/* Wait for next frame and send SOF.
   Returns 0 if the device is not present. */
static int send_SOF(int count)
{
   while (count--) {
      mwait(1);
      if ((PINA & MASK_BOTH) != FS_J)
         return 0;
      asmblock(
         OUT
         // send SOF 1337
         K J K J K J K K K J J K J J K K K J K K K K J K K J J J J J J K X X J
         IN
         X
      );
   }
   return 1;
}

/* Send SET_ADDRESS */
static void send_SET_ADDRESS(void)
{
   asmblock(
      OUT
      // send SETUP
      K J K J K J K K K J J J K K J K J K J K J K J K J K J K K J K J X X J
      // delay a little
      D D D D D D D D
      // send DATA0
      K J K J K J K K K K J K J K K K J K J K J K J K K J J K J K J K K J K
      J K J K J K J K J K J K J K J K J K J K J K J K J K J K J K J K J K J
      K J K J K J K J K J J J K K J J J J J K K J K K J K X X J
      IN
      X
      // device will send ACK now (within 16 bit times)
   );
   // wait until new frame; an immediate IN will just get NAKed
   send_SOF(1);
   asmblock(
      OUT
      // send IN
      K J K J K J K K K J K K J J J K J K J K J K J K J K J K K J K J X X J
      // device will send DATA1 now.
      IN
      // delay for the expected 35 bits of data
      X D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D
      // wait 12 more bit times (16 is max turnaround)
      D D D D D D D D D D D D
      OUT
      // send ACK
      K J K J K J K K J J K J J K K K X X J
      IN
      X
   );
}

/* Send SET_CONFIGURATION */
static void send_SET_CONFIGURATION(void)
{
   asmblock(
      OUT
      // send SETUP
      K J K J K J K K K J J J K K J K K J K J K J K J K J K K J J J J X X J
      // delay a little
      D D D D D D D D
      // send DATA0
      K J K J K J K K K K J K J K K K J K J K J K J K K J K K J K J K K J K
      J K J K J K J K J K J K J K J K J K J K J K J K J K J K J K J K J K J
      K J K J K J K J K J J J J K J J K J J K K J K K J K X X J
      IN
      X
      // device will send ACK now (within 16 bit times)
   );
   // wait until new frame; an immediate IN will just get NAKed
   send_SOF(1);
   asmblock(
      OUT
      // send IN
      K J K J K J K K K J K K J J J K K J K J K J K J K J K K J J J J X X J
      // device will send DATA1 now.
      IN
      // delay for the expected 35 bits of data
      X D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D D
      // wait 12 more bit times (16 is max turnaround)
      D D D D D D D D D D D D
      OUT
      // send ACK
      K J K J K J K K J J K J J K K K X X J
      IN
      X
   );
}

int main(void)
{   
   /* Tristate D+ and D- */
   DDRA = MASK_IN;
   PORTA = SE0;

   mwait(100);

   for (;;) {
      /* if the device doesn't appear present, do nothing */
      while ((PINA & MASK_BOTH) != FS_J) {
         mwait(1);
      }

      // perform reset
      mwait(100);
      send_reset();

      // perform basic enumeration
      send_SOF(10);
      send_SET_ADDRESS();
      send_SOF(3);
      send_SET_CONFIGURATION();

      // now send empty frames as long as the device is present.
      while (send_SOF(1))
         continue;
   }
}
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PS3 Development All times are GMT + 10 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group