API for interfacing with a game cube controller

Dependencies:   mbed

Gamecube Controller Interface

This is a library to interface the Gamecube Controller to the mbed. The goal of this project is to read input data from the controller.

Introduction

This library lets you read data from a Gamecube Controller. It allows you to use two pins to get 6+ buttons, a D-pad (up/down/left/right), two variable triggers, and two joysticks. The controller was stripped of its connector and this wiki shows the subsequent steps to wire the controller to the mbed and code needed to read data through a serial connection. Assembly was used because the timing for the controller needs to have microsecond accuracy. The shortest wait found using wait_us(...) was 1.5us.

Pinout

ColorFunction
Yellow5V power supply (rumble)
RedData: bi-directional / 3.3V
GreenGround
WhiteGround
Blue3.3V power supply

*Note this pinout is for the latest Gamecube controller /media/uploads/christopherjwang/6.jpg

Wiring

The Gamecube controller was cut to expose the wires. Seperate them and solder other wires on to them for stability and consistency.

/media/uploads/christopherjwang/2.jpg

Color (Gamecube)mbed Pin
YellowVOUT
Redp9 and p10
GreenGround
WhiteGround
BlueVU

/media/uploads/christopherjwang/bro.png

Gamecube Controller Protocol

The Gamecube controller uses 3.3V logic (bidirectional), 3.3V to power the controller, and 5V for the rumble motor.

The 5V power used by the rumble motor is always on, and the motor is controlled by a command sent to the controller.

The controller uses one bi-directional data line to communicate with the console. This is an active high 3.3V logic signal, using a 10K pull-up resistor to 3.3V to hold the line high. Communication is initiated by the console sending a 24-bit string to the controller, after which the controller responds with 64-bits of button state and joystick data.

The transfer speed is fast at around 4 microseconds per bit. .A low bit is signaled by a 3us low followed by 1us high, and a high bit is signaled by 1us low followed by 3us high.

Here is a 0 followed by a 1

/media/uploads/christopherjwang/bro2.png

The last bit of the command is the 'rumble' control. Setting this bit to one enables the rumble motor, and clearing it disables the motor.

Polling for Data

When the Gamecube or the controller sends a string of bits, it terminates it with a single (high) stop bit. Therefore, in order to send the string 00000000, the Gamecube would send 000000001.

There is a typical interval of about 6ms between successive updates. The real time update time would depend on the game being played or the video frame update rate. The sequence starts with a 24-bit command from the console:

0100 0000 0000 0011 0000 000 (0*) *rumble

After the 24-bit command, the controller responds with a string of bits that contain the state of all the buttons along with joystick position data. The sequence of the returned data is shown in the table below. The buttons are listed in transmission order, from left to right.

Byte 0000StartYXBA
Byte 11LRZD-UpD-DownD-RightD-Left
Byte 2Joystick X Value (8 bit)
Byte 3Joystick Y Value (8 bit)
Byte 4C-Stick X Value (8 bit)
Byte 5C-Stick Y Value (8 bit)
Byte 6Left Button Value (8 bit)
Byte 7Right Button Value (8 bit)

Example Code

Basic program to use with library

//
#include "mbed.h"
#include "Gamecube.h"
 
Serial pc1(USBTX, USBRX); // tx, rx
DigitalOut myled(LED1);
int main() {
    Gamecube g(p9); // gamecube controller connected to pin 9
    int device_id = g.get_device_id();
    if (device_id != NINTENDO_DEVICE_GC_WIRED) { // device id for controller is 0x0900
        pc1.printf("this device is not a nintendo gamecube wired controller!, it returned an ID of %d\n", device_id);    
    }
    while(1) {
        g.update();
        pc1.printf("A: %d \n\rB: %d \n\rX: %d \n\rY: %d \n\rL: %d \n\rR: %d \n\rZ: %d \n\rSTART: %d \n\rD_UP: %d \n\rD_LEFT: %d \n\rD_DOWN: %d \n\rD_RIGHT: %d \n\rJOYSTICK_X: %d \n\rJOYSTICK_Y: %d \n\rC_STICK_X: %d \n\rC_STICK_Y: %d \n\rLEFT_TRIGGER: %d \n\rRIGHT_TRIGGER: %d \n\r", g.A, g.B, g.X, g.Y, g.L, g.R, g.Z, g.START, g.D_UP, g.D_LEFT, g.D_DOWN, g.D_RIGHT, g.JOYSTICK_X, g.JOYSTICK_Y, g.C_STICK_X, g.C_STICK_Y, g.LEFT_TRIGGER, g.RIGHT_TRIGGER);
        wait(1);
        
        g.rumble(true); // rumble working
        myled = 1;
        wait(1);
        myled = 0;
        g.rumble(false);
        wait(.2);
    }
}

Functions

Functions

    // constructor
    Gamecube(PinName _data_line); 

    // gets the device id
    int get_device_id();

    // call this to update public variables in header file
    void update(void);

Library

Import programgamecube_controller

API for interfacing with a game cube controller

Demo

Committer:
christopherjwang
Date:
Sun Dec 06 19:16:05 2015 +0000
Revision:
1:a93f71ee6778
Parent:
0:7434770d9fc9
MVP, hard coded at p9 and p10

Who changed what in which revision?

UserRevisionLine numberNew contents of line
christopherjwang 0:7434770d9fc9 1 ; no attempt to reserve registers!
christopherjwang 0:7434770d9fc9 2 ; void gc_asm_write_read(uint32_t *buff, uint8_t len, uint32_t *read_buff, uint8_t read_buff_len)
christopherjwang 0:7434770d9fc9 3 ; R0-R3 inputs
christopherjwang 0:7434770d9fc9 4 ; R1 and R3 are decremented as the buffers are looped through
christopherjwang 0:7434770d9fc9 5 ; R4 #0x00000001 bit mask for p10
christopherjwang 0:7434770d9fc9 6 ; R5 my personal "temp" register for quick calculations, if not used in succession it should be considered clobbered
christopherjwang 0:7434770d9fc9 7 ; R6 0x2009C020 GPIO port 2 base address
christopherjwang 0:7434770d9fc9 8 ; R7 address offset to write and read from buff and read_buff_len, it increments whenever R0 or R3 decrements
christopherjwang 0:7434770d9fc9 9
christopherjwang 0:7434770d9fc9 10
christopherjwang 0:7434770d9fc9 11 AREA asm_func, CODE, READONLY
christopherjwang 0:7434770d9fc9 12 EXPORT gc_asm_write_read
christopherjwang 0:7434770d9fc9 13 gc_asm_write_read
christopherjwang 0:7434770d9fc9 14 PUSH {R4-R7, LR}
christopherjwang 0:7434770d9fc9 15 SUB R7, R7, R7
christopherjwang 0:7434770d9fc9 16
christopherjwang 0:7434770d9fc9 17 WRITE_LOOP
christopherjwang 0:7434770d9fc9 18
christopherjwang 0:7434770d9fc9 19 ; Load GPIO Port 1 base address in register R1
christopherjwang 0:7434770d9fc9 20 LDR R6, =0x2009C000 ; 0x2009C020 = GPIO port 1 base address
christopherjwang 0:7434770d9fc9 21 MOV.W R4, #0x00000001 ; 0x040000 = 1<<18 all "0"s with a "1" in bit 18
christopherjwang 0:7434770d9fc9 22
christopherjwang 0:7434770d9fc9 23 LDR R5, [R0, R7]
christopherjwang 0:7434770d9fc9 24
christopherjwang 0:7434770d9fc9 25 CMP R5, #0 ; value == 0 ?
christopherjwang 0:7434770d9fc9 26 BNE SEND_HIGH
christopherjwang 0:7434770d9fc9 27 STR R4, [R6,#0x1c] ; set low
christopherjwang 0:7434770d9fc9 28 BL WAIT_ONE_US_MINUS_OVERHEAD
christopherjwang 0:7434770d9fc9 29 BL WAIT_ONE_US
christopherjwang 0:7434770d9fc9 30 BL WAIT_ONE_US
christopherjwang 0:7434770d9fc9 31 B _END_SEND_HIGH
christopherjwang 0:7434770d9fc9 32 SEND_HIGH
christopherjwang 0:7434770d9fc9 33 STR R4, [R6,#0x1c] ; set low
christopherjwang 0:7434770d9fc9 34 BL WAIT_ONE_US
christopherjwang 0:7434770d9fc9 35 STR R4, [R6,#0x18] ; set high
christopherjwang 0:7434770d9fc9 36 BL WAIT_ONE_US_MINUS_OVERHEAD
christopherjwang 0:7434770d9fc9 37 BL WAIT_ONE_US
christopherjwang 0:7434770d9fc9 38
christopherjwang 0:7434770d9fc9 39 _END_SEND_HIGH
christopherjwang 0:7434770d9fc9 40 STR R4, [R6,#0x18]
christopherjwang 0:7434770d9fc9 41 BL WAIT_ONE_US
christopherjwang 0:7434770d9fc9 42
christopherjwang 0:7434770d9fc9 43 SUB R1, R1, #0x01 ; going through the array backwards (it was reversed in the c layer)
christopherjwang 0:7434770d9fc9 44 ADD R7, R7, #0x04 ; increment address that we access using R7
christopherjwang 0:7434770d9fc9 45
christopherjwang 0:7434770d9fc9 46 CMP R1, #0
christopherjwang 0:7434770d9fc9 47 BLT FINISH_WRITE
christopherjwang 0:7434770d9fc9 48 B WRITE_LOOP
christopherjwang 0:7434770d9fc9 49
christopherjwang 0:7434770d9fc9 50 FINISH_WRITE
christopherjwang 0:7434770d9fc9 51 ; send one high bit to terminate
christopherjwang 0:7434770d9fc9 52 STR R4, [R6,#0x1C] ; set low
christopherjwang 0:7434770d9fc9 53 BL WAIT_ONE_US
christopherjwang 0:7434770d9fc9 54 STR R4, [R6,#0x18] ; set high
christopherjwang 0:7434770d9fc9 55 BL WAIT_ONE_US_MINUS_OVERHEAD
christopherjwang 0:7434770d9fc9 56 ;end of send one high bit to terminate
christopherjwang 0:7434770d9fc9 57
christopherjwang 0:7434770d9fc9 58 ; begin read
christopherjwang 0:7434770d9fc9 59
christopherjwang 0:7434770d9fc9 60 SUB R7, R7, R7 ; offset to write into the array given
christopherjwang 0:7434770d9fc9 61 SUB R3, R3, #1
christopherjwang 0:7434770d9fc9 62 B FIRST_READ
christopherjwang 0:7434770d9fc9 63
christopherjwang 0:7434770d9fc9 64 READ_LOOP
christopherjwang 0:7434770d9fc9 65 SUB R3, R3, #1 ;R3 is the number of bits to read
christopherjwang 0:7434770d9fc9 66 CMP R3, #0
christopherjwang 0:7434770d9fc9 67 BLT FINISH
christopherjwang 0:7434770d9fc9 68
christopherjwang 0:7434770d9fc9 69 LDR R5, =0x999999
christopherjwang 0:7434770d9fc9 70 WAIT_FOR_HIGH
christopherjwang 0:7434770d9fc9 71 SUB R5, R5, #1 ; only check 0x100 times, 0x100 is arbitrarily chosen right now
christopherjwang 0:7434770d9fc9 72 CMP R5, #0
christopherjwang 0:7434770d9fc9 73 BEQ FINISH ; TODO: add in fail case here!
christopherjwang 0:7434770d9fc9 74
christopherjwang 0:7434770d9fc9 75 LDR R5, [R6, #0x14] ; load pin states
christopherjwang 0:7434770d9fc9 76 AND R5, R5, R4 ; get only p10
christopherjwang 0:7434770d9fc9 77 CMP R5, #0
christopherjwang 0:7434770d9fc9 78 BEQ WAIT_FOR_HIGH
christopherjwang 0:7434770d9fc9 79
christopherjwang 0:7434770d9fc9 80 ; wait for 2us to get the bit we want
christopherjwang 0:7434770d9fc9 81 BL WAIT_TWO_US_MINUS_OVERHEAD
christopherjwang 0:7434770d9fc9 82
christopherjwang 0:7434770d9fc9 83 FIRST_READ
christopherjwang 0:7434770d9fc9 84 LDR R5, [R6, #0x14] ; load pin states
christopherjwang 0:7434770d9fc9 85 AND R5, R5, R4 ; get only p10
christopherjwang 0:7434770d9fc9 86 CMP R5, #0
christopherjwang 0:7434770d9fc9 87 BEQ STORE_ZERO
christopherjwang 0:7434770d9fc9 88 LDR R5, =0x01
christopherjwang 0:7434770d9fc9 89 STR R5, [R2, R7]
christopherjwang 0:7434770d9fc9 90 BL WAIT_ONE_US_MINUS_OVERHEAD
christopherjwang 0:7434770d9fc9 91 B END_STORE_ZERO
christopherjwang 0:7434770d9fc9 92 STORE_ZERO
christopherjwang 0:7434770d9fc9 93 LDR R5, =0x00
christopherjwang 0:7434770d9fc9 94 STR R5, [R2, R7]
christopherjwang 0:7434770d9fc9 95 END_STORE_ZERO
christopherjwang 0:7434770d9fc9 96 ADD R7, R7, #0x04 ; increment address stored
christopherjwang 0:7434770d9fc9 97 B READ_LOOP
christopherjwang 0:7434770d9fc9 98
christopherjwang 0:7434770d9fc9 99
christopherjwang 0:7434770d9fc9 100 FINISH
christopherjwang 0:7434770d9fc9 101 POP {R4-R7, LR}
christopherjwang 0:7434770d9fc9 102 BX LR
christopherjwang 0:7434770d9fc9 103
christopherjwang 0:7434770d9fc9 104 WAIT_ONE_US_MINUS_OVERHEAD
christopherjwang 0:7434770d9fc9 105 LDR R5, =0x8
christopherjwang 0:7434770d9fc9 106 _WAIT_ONE_US_MINUS_OVERHEAD
christopherjwang 0:7434770d9fc9 107 SUB R5, R5, #1
christopherjwang 0:7434770d9fc9 108 NOP
christopherjwang 0:7434770d9fc9 109 NOP
christopherjwang 0:7434770d9fc9 110 NOP
christopherjwang 0:7434770d9fc9 111 NOP
christopherjwang 0:7434770d9fc9 112 NOP
christopherjwang 0:7434770d9fc9 113 NOP
christopherjwang 0:7434770d9fc9 114 CMP R5, #0
christopherjwang 0:7434770d9fc9 115 BNE _WAIT_ONE_US_MINUS_OVERHEAD
christopherjwang 0:7434770d9fc9 116 BX LR
christopherjwang 0:7434770d9fc9 117
christopherjwang 0:7434770d9fc9 118 WAIT_ONE_US
christopherjwang 0:7434770d9fc9 119 LDR R5, =0x9
christopherjwang 0:7434770d9fc9 120 _WAIT_ONE_US
christopherjwang 0:7434770d9fc9 121 SUB R5, R5, #1
christopherjwang 0:7434770d9fc9 122 NOP
christopherjwang 0:7434770d9fc9 123 NOP
christopherjwang 0:7434770d9fc9 124 NOP
christopherjwang 0:7434770d9fc9 125 NOP
christopherjwang 0:7434770d9fc9 126 NOP
christopherjwang 0:7434770d9fc9 127 CMP R5, #0
christopherjwang 0:7434770d9fc9 128 BNE _WAIT_ONE_US
christopherjwang 0:7434770d9fc9 129 BX LR
christopherjwang 0:7434770d9fc9 130
christopherjwang 0:7434770d9fc9 131 WAIT_TWO_US_MINUS_OVERHEAD
christopherjwang 0:7434770d9fc9 132 LDR R5, =0x17
christopherjwang 0:7434770d9fc9 133 _WAIT_TWO_US_MINUS_OVERHEAD
christopherjwang 0:7434770d9fc9 134 SUB R5, R5, #1
christopherjwang 0:7434770d9fc9 135 NOP
christopherjwang 0:7434770d9fc9 136 NOP
christopherjwang 0:7434770d9fc9 137 NOP
christopherjwang 0:7434770d9fc9 138 NOP
christopherjwang 0:7434770d9fc9 139 NOP
christopherjwang 0:7434770d9fc9 140 NOP
christopherjwang 0:7434770d9fc9 141 CMP R5, #0
christopherjwang 0:7434770d9fc9 142 BNE _WAIT_TWO_US_MINUS_OVERHEAD
christopherjwang 0:7434770d9fc9 143 BX LR
christopherjwang 0:7434770d9fc9 144
christopherjwang 0:7434770d9fc9 145
christopherjwang 0:7434770d9fc9 146 END