mirror of
https://github.com/pothos/arduino-n64-controller-library
synced 2024-11-24 17:52:21 -05:00
moved here from http://pothos.blogsport.eu/2012/03/26/arduino-n64-controller-library-und-tetris-port/
This commit is contained in:
commit
4458b8f80c
512
N64Controller/N64Controller.cpp
Normal file
512
N64Controller/N64Controller.cpp
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
|
||||||
|
#include "N64Controller.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "pins_arduino.h"
|
||||||
|
|
||||||
|
// these two macros set arduino pin 2 to input or output, which with an
|
||||||
|
// external 1K pull-up resistor to the 3.3V rail, is like pulling it high or
|
||||||
|
// low. These operations translate to 1 op code, which takes 2 cycles
|
||||||
|
#define N64_PIND_HIGH DDRD &= ~pincode
|
||||||
|
#define N64_PIND_LOW DDRD |= pincode
|
||||||
|
#define N64_PIND_QUERY (PIND & pincode)
|
||||||
|
|
||||||
|
#define N64_PINB_HIGH DDRB &= ~pincode
|
||||||
|
#define N64_PINB_LOW DDRB |= pincode
|
||||||
|
#define N64_PINB_QUERY (PINB & pincode)
|
||||||
|
|
||||||
|
void N64Controller::set_up() {
|
||||||
|
n64_PIN = 2; // might also be set/changed by constructor or begin() afterwards
|
||||||
|
n64_key_Dup = false;
|
||||||
|
n64_key_Ddown = false;
|
||||||
|
n64_key_Dleft = false;
|
||||||
|
n64_key_Dright = false;
|
||||||
|
n64_key_Start = false;
|
||||||
|
n64_key_Z = false;
|
||||||
|
n64_key_A = false;
|
||||||
|
n64_key_B = false;
|
||||||
|
n64_key_Cup = false;
|
||||||
|
n64_key_Cdown = false;
|
||||||
|
n64_key_Cleft = false;
|
||||||
|
n64_key_Cright = false;
|
||||||
|
n64_key_L = false;
|
||||||
|
n64_key_R = false;
|
||||||
|
n64_key_X = 0;
|
||||||
|
n64_key_Y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
N64Controller::N64Controller() {
|
||||||
|
set_up();
|
||||||
|
}
|
||||||
|
N64Controller::N64Controller(int serialPin) {
|
||||||
|
set_up();
|
||||||
|
n64_PIN = serialPin;
|
||||||
|
}
|
||||||
|
void N64Controller::begin(int serialPin) {
|
||||||
|
n64_PIN = serialPin;
|
||||||
|
begin();
|
||||||
|
}
|
||||||
|
void N64Controller::begin() {
|
||||||
|
// Communication with N64 controller controller on this pin
|
||||||
|
// Don't remove these lines, we don't want to push +5V to the controller
|
||||||
|
digitalWrite(n64_PIN, LOW);
|
||||||
|
pinMode(n64_PIN, INPUT);
|
||||||
|
n64_first_register = true;
|
||||||
|
switch (n64_PIN) {
|
||||||
|
case 0: n64_pincode = 0x01;
|
||||||
|
break;
|
||||||
|
case 1: n64_pincode = 0x02;
|
||||||
|
break;
|
||||||
|
case 2: n64_pincode = 0x04;
|
||||||
|
break;
|
||||||
|
case 3: n64_pincode = 0x08;
|
||||||
|
break;
|
||||||
|
case 4: n64_pincode = 0x10;
|
||||||
|
break;
|
||||||
|
case 5: n64_pincode = 0x20;
|
||||||
|
break;
|
||||||
|
case 6: n64_pincode = 0x40;
|
||||||
|
break;
|
||||||
|
case 7: n64_pincode = 0x80;
|
||||||
|
break;
|
||||||
|
case 8: n64_pincode = 0x01; n64_first_register = false;
|
||||||
|
break;
|
||||||
|
case 9: n64_pincode = 0x02; n64_first_register = false;
|
||||||
|
break;
|
||||||
|
case 10: n64_pincode = 0x04; n64_first_register = false;
|
||||||
|
break;
|
||||||
|
case 11: n64_pincode = 0x08; n64_first_register = false;
|
||||||
|
break;
|
||||||
|
case 12: n64_pincode = 0x10; n64_first_register = false;
|
||||||
|
break;
|
||||||
|
case 13: n64_pincode = 0x20; n64_first_register = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
n64_pincode = 0x04; n64_PIN = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (n64_first_register) {
|
||||||
|
N64_init_PIND(n64_pincode);
|
||||||
|
} else {
|
||||||
|
N64_init_PINB(n64_pincode);
|
||||||
|
}
|
||||||
|
translate_raw_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void N64Controller::N64_init_PIND(char pincode) {
|
||||||
|
// Initialize the gamecube controller by sending it a null byte.
|
||||||
|
// This is unnecessary for a standard controller, but is required for the
|
||||||
|
// Wavebird.
|
||||||
|
unsigned char initialize = 0x00;
|
||||||
|
noInterrupts();
|
||||||
|
N64_PIND_send(pincode, &initialize, 1);
|
||||||
|
|
||||||
|
// Stupid routine to wait for the gamecube controller to stop
|
||||||
|
// sending its response. We don't care what it is, but we
|
||||||
|
// can't start asking for status if it's still responding
|
||||||
|
int x;
|
||||||
|
for (x=0; x<64; x++) {
|
||||||
|
// make sure the line is idle for 64 iterations, should
|
||||||
|
// be plenty.
|
||||||
|
if (!N64_PIND_QUERY)
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query for the gamecube controller's status. We do this
|
||||||
|
// to get the 0 point for the control stick.
|
||||||
|
unsigned char command[] = {0x01};
|
||||||
|
N64_PIND_send(pincode, command, 1);
|
||||||
|
// read in data and dump it to N64_raw_dump
|
||||||
|
N64_PIND_get(pincode);
|
||||||
|
interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
void N64Controller::N64_init_PINB(char pincode) {
|
||||||
|
unsigned char initialize = 0x00;
|
||||||
|
noInterrupts();
|
||||||
|
N64_PINB_send(pincode, &initialize, 1);
|
||||||
|
int x;
|
||||||
|
for (x=0; x<64; x++) {
|
||||||
|
if (!N64_PINB_QUERY)
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
unsigned char command[] = {0x01};
|
||||||
|
N64_PINB_send(pincode, command, 1);
|
||||||
|
N64_PINB_get(pincode);
|
||||||
|
interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sends the given byte sequence to the controller
|
||||||
|
* length must be at least 1
|
||||||
|
* Oh, it destroys the buffer passed in as it writes it
|
||||||
|
*/
|
||||||
|
void N64Controller::N64_PIND_send(char pincode, unsigned char *buffer, char length) {
|
||||||
|
// Send these bytes
|
||||||
|
char bits;
|
||||||
|
|
||||||
|
bool bit;
|
||||||
|
|
||||||
|
// This routine is very carefully timed by examining the assembly output.
|
||||||
|
// Do not change any statements, it could throw the timings off
|
||||||
|
//
|
||||||
|
// We get 16 cycles per microsecond, which should be plenty, but we need to
|
||||||
|
// be conservative. Most assembly ops take 1 cycle, but a few take 2
|
||||||
|
//
|
||||||
|
// I use manually constructed for-loops out of gotos so I have more control
|
||||||
|
// over the outputted assembly. I can insert nops where it was impossible
|
||||||
|
// with a for loop
|
||||||
|
|
||||||
|
asm volatile (";Starting outer for loop");
|
||||||
|
outer_loop:
|
||||||
|
{
|
||||||
|
asm volatile (";Starting inner for loop");
|
||||||
|
bits=8;
|
||||||
|
inner_loop:
|
||||||
|
{
|
||||||
|
// Starting a bit, set the line low
|
||||||
|
asm volatile (";Setting line to low");
|
||||||
|
N64_PIND_LOW; // 1 op, 2 cycles
|
||||||
|
|
||||||
|
asm volatile (";branching");
|
||||||
|
if (*buffer >> 7) {
|
||||||
|
asm volatile (";Bit is a 1");
|
||||||
|
// 1 bit
|
||||||
|
// remain low for 1us, then go high for 3us
|
||||||
|
// nop block 1
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\nnop\n");
|
||||||
|
|
||||||
|
asm volatile (";Setting line to high");
|
||||||
|
N64_PIND_HIGH;
|
||||||
|
|
||||||
|
// nop block 2
|
||||||
|
// we'll wait only 2us to sync up with both conditions
|
||||||
|
// at the bottom of the if statement
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
asm volatile (";Bit is a 0");
|
||||||
|
// 0 bit
|
||||||
|
// remain low for 3us, then go high for 1us
|
||||||
|
// nop block 3
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\n");
|
||||||
|
|
||||||
|
asm volatile (";Setting line to high");
|
||||||
|
N64_PIND_HIGH;
|
||||||
|
|
||||||
|
// wait for 1us
|
||||||
|
asm volatile ("; end of conditional branch, need to wait 1us more before next bit");
|
||||||
|
|
||||||
|
}
|
||||||
|
// end of the if, the line is high and needs to remain
|
||||||
|
// high for exactly 16 more cycles, regardless of the previous
|
||||||
|
// branch path
|
||||||
|
|
||||||
|
asm volatile (";finishing inner loop body");
|
||||||
|
--bits;
|
||||||
|
if (bits != 0) {
|
||||||
|
// nop block 4
|
||||||
|
// this block is why a for loop was impossible
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\n");
|
||||||
|
// rotate bits
|
||||||
|
asm volatile (";rotating out bits");
|
||||||
|
*buffer <<= 1;
|
||||||
|
|
||||||
|
goto inner_loop;
|
||||||
|
} // fall out of inner loop
|
||||||
|
}
|
||||||
|
asm volatile (";continuing outer loop");
|
||||||
|
// In this case: the inner loop exits and the outer loop iterates,
|
||||||
|
// there are /exactly/ 16 cycles taken up by the necessary operations.
|
||||||
|
// So no nops are needed here (that was lucky!)
|
||||||
|
--length;
|
||||||
|
if (length != 0) {
|
||||||
|
++buffer;
|
||||||
|
goto outer_loop;
|
||||||
|
} // fall out of outer loop
|
||||||
|
}
|
||||||
|
|
||||||
|
// send a single stop (1) bit
|
||||||
|
// nop block 5
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\n");
|
||||||
|
N64_PIND_LOW;
|
||||||
|
// wait 1 us, 16 cycles, then raise the line
|
||||||
|
// 16-2=14
|
||||||
|
// nop block 6
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\n");
|
||||||
|
N64_PIND_HIGH;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void N64Controller::N64_PINB_send(char pincode, unsigned char *buffer, char length) {
|
||||||
|
char bits;
|
||||||
|
bool bit;
|
||||||
|
asm volatile (";Starting outer for loop");
|
||||||
|
outer_loop:
|
||||||
|
{
|
||||||
|
asm volatile (";Starting inner for loop");
|
||||||
|
bits=8;
|
||||||
|
inner_loop:
|
||||||
|
{
|
||||||
|
asm volatile (";Setting line to low");
|
||||||
|
N64_PINB_LOW;
|
||||||
|
asm volatile (";branching");
|
||||||
|
if (*buffer >> 7) {
|
||||||
|
asm volatile (";Bit is a 1");
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\nnop\n");
|
||||||
|
asm volatile (";Setting line to high");
|
||||||
|
N64_PINB_HIGH;
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
asm volatile (";Bit is a 0");
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\n");
|
||||||
|
|
||||||
|
asm volatile (";Setting line to high");
|
||||||
|
N64_PINB_HIGH;
|
||||||
|
asm volatile ("; end of conditional branch, need to wait 1us more before next bit");
|
||||||
|
}
|
||||||
|
asm volatile (";finishing inner loop body");
|
||||||
|
--bits;
|
||||||
|
if (bits != 0) {
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\n");
|
||||||
|
asm volatile (";rotating out bits");
|
||||||
|
*buffer <<= 1;
|
||||||
|
goto inner_loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asm volatile (";continuing outer loop");
|
||||||
|
--length;
|
||||||
|
if (length != 0) {
|
||||||
|
++buffer;
|
||||||
|
goto outer_loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\n");
|
||||||
|
N64_PINB_LOW;
|
||||||
|
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\n");
|
||||||
|
N64_PINB_HIGH;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void N64Controller::N64_PIND_get(char pincode)
|
||||||
|
{
|
||||||
|
// listen for the expected 8 bytes of data back from the controller and
|
||||||
|
// blast it out to the N64_raw_dump array, one bit per byte for extra speed.
|
||||||
|
// Afterwards, call translate_raw_data() to interpret the raw data and pack
|
||||||
|
// it into the N64_status struct.
|
||||||
|
asm volatile (";Starting to listen");
|
||||||
|
unsigned char timeout;
|
||||||
|
char bitcount = 32;
|
||||||
|
char *bitbin = N64_raw_dump;
|
||||||
|
|
||||||
|
// Again, using gotos here to make the assembly more predictable and
|
||||||
|
// optimization easier (please don't kill me)
|
||||||
|
read_loop:
|
||||||
|
timeout = 0x3f;
|
||||||
|
// wait for line to go low
|
||||||
|
while (N64_PIND_QUERY) {
|
||||||
|
if (!--timeout)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// wait approx 2us and poll the line
|
||||||
|
asm volatile (
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
);
|
||||||
|
*bitbin = N64_PIND_QUERY;
|
||||||
|
++bitbin;
|
||||||
|
--bitcount;
|
||||||
|
if (bitcount == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// wait for line to go high again
|
||||||
|
// it may already be high, so this should just drop through
|
||||||
|
timeout = 0x3f;
|
||||||
|
while (!N64_PIND_QUERY) {
|
||||||
|
if (!--timeout)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
goto read_loop;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void N64Controller::N64_PINB_get(char pincode)
|
||||||
|
{
|
||||||
|
asm volatile (";Starting to listen");
|
||||||
|
unsigned char timeout;
|
||||||
|
char bitcount = 32;
|
||||||
|
char *bitbin = N64_raw_dump;
|
||||||
|
read_loop:
|
||||||
|
timeout = 0x3f;
|
||||||
|
while (N64_PINB_QUERY) {
|
||||||
|
if (!--timeout)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
asm volatile (
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
"nop\nnop\nnop\nnop\nnop\n"
|
||||||
|
);
|
||||||
|
*bitbin = N64_PINB_QUERY;
|
||||||
|
++bitbin;
|
||||||
|
--bitcount;
|
||||||
|
if (bitcount == 0)
|
||||||
|
return;
|
||||||
|
timeout = 0x3f;
|
||||||
|
while (!N64_PINB_QUERY) {
|
||||||
|
if (!--timeout)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
goto read_loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void N64Controller::print_N64_status()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
// bits: A, B, Z, Start, Dup, Ddown, Dleft, Dright
|
||||||
|
// bits: 0, 0, L, R, Cup, Cdown, Cleft, Cright
|
||||||
|
Serial.println();
|
||||||
|
Serial.print("Start: ");
|
||||||
|
Serial.println(N64_status.data1 & 16 ? 1:0);
|
||||||
|
|
||||||
|
Serial.print("Z: ");
|
||||||
|
Serial.println(N64_status.data1 & 32 ? 1:0);
|
||||||
|
|
||||||
|
Serial.print("B: ");
|
||||||
|
Serial.println(N64_status.data1 & 64 ? 1:0);
|
||||||
|
|
||||||
|
Serial.print("A: ");
|
||||||
|
Serial.println(N64_status.data1 & 128 ? 1:0);
|
||||||
|
|
||||||
|
Serial.print("L: ");
|
||||||
|
Serial.println(N64_status.data2 & 32 ? 1:0);
|
||||||
|
Serial.print("R: ");
|
||||||
|
Serial.println(N64_status.data2 & 16 ? 1:0);
|
||||||
|
|
||||||
|
Serial.print("Cup: ");
|
||||||
|
Serial.println(N64_status.data2 & 0x08 ? 1:0);
|
||||||
|
Serial.print("Cdown: ");
|
||||||
|
Serial.println(N64_status.data2 & 0x04 ? 1:0);
|
||||||
|
Serial.print("Cright:");
|
||||||
|
Serial.println(N64_status.data2 & 0x01 ? 1:0);
|
||||||
|
Serial.print("Cleft: ");
|
||||||
|
Serial.println(N64_status.data2 & 0x02 ? 1:0);
|
||||||
|
|
||||||
|
Serial.print("Dup: ");
|
||||||
|
Serial.println(N64_status.data1 & 0x08 ? 1:0);
|
||||||
|
Serial.print("Ddown: ");
|
||||||
|
Serial.println(N64_status.data1 & 0x04 ? 1:0);
|
||||||
|
Serial.print("Dright:");
|
||||||
|
Serial.println(N64_status.data1 & 0x01 ? 1:0);
|
||||||
|
Serial.print("Dleft: ");
|
||||||
|
Serial.println(N64_status.data1 & 0x02 ? 1:0);
|
||||||
|
|
||||||
|
Serial.print("Stick X:");
|
||||||
|
Serial.println(N64_status.stick_x, DEC);
|
||||||
|
Serial.print("Stick Y:");
|
||||||
|
Serial.println(N64_status.stick_y, DEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
void N64Controller::translate_raw_data()
|
||||||
|
{
|
||||||
|
// The get_N64_status function sloppily dumps its data 1 bit per byte
|
||||||
|
// into the get_status_extended char array. It's our job to go through
|
||||||
|
// that and put each piece neatly into the struct N64_status
|
||||||
|
int i;
|
||||||
|
memset(&N64_status, 0, sizeof(N64_status));
|
||||||
|
// line 1
|
||||||
|
// bits: A, B, Z, Start, Dup, Ddown, Dleft, Dright
|
||||||
|
for (i=0; i<8; i++) {
|
||||||
|
N64_status.data1 |= N64_raw_dump[i] ? (0x80 >> i) : 0;
|
||||||
|
}
|
||||||
|
// line 2
|
||||||
|
// bits: 0, 0, L, R, Cup, Cdown, Cleft, Cright
|
||||||
|
for (i=0; i<8; i++) {
|
||||||
|
N64_status.data2 |= N64_raw_dump[8+i] ? (0x80 >> i) : 0;
|
||||||
|
}
|
||||||
|
// line 3
|
||||||
|
// bits: joystick x value
|
||||||
|
// These are 8 bit values centered at 0x80 (128)
|
||||||
|
for (i=0; i<8; i++) {
|
||||||
|
N64_status.stick_x |= N64_raw_dump[16+i] ? (0x80 >> i) : 0;
|
||||||
|
}
|
||||||
|
for (i=0; i<8; i++) {
|
||||||
|
N64_status.stick_y |= N64_raw_dump[24+i] ? (0x80 >> i) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
n64_key_A = (bool)N64_raw_dump[0];
|
||||||
|
n64_key_B = (bool)N64_raw_dump[1];
|
||||||
|
n64_key_Z = (bool)N64_raw_dump[2];
|
||||||
|
n64_key_Start = (bool)N64_raw_dump[3];
|
||||||
|
n64_key_Dup = (bool)N64_raw_dump[4];
|
||||||
|
n64_key_Ddown = (bool)N64_raw_dump[5];
|
||||||
|
n64_key_Dleft = (bool)N64_raw_dump[6];
|
||||||
|
n64_key_Dright = (bool)N64_raw_dump[7];
|
||||||
|
n64_key_L = (bool)N64_raw_dump[10];
|
||||||
|
n64_key_R = (bool)N64_raw_dump[11];
|
||||||
|
n64_key_Cup = (bool)N64_raw_dump[12];
|
||||||
|
n64_key_Cdown = (bool)N64_raw_dump[13];
|
||||||
|
n64_key_Cleft = (bool)N64_raw_dump[14];
|
||||||
|
n64_key_Cright = (bool)N64_raw_dump[15];
|
||||||
|
n64_key_X = (int) N64_status.stick_x;
|
||||||
|
n64_key_Y = (int) N64_status.stick_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void N64Controller::update() {
|
||||||
|
unsigned char data, addr;
|
||||||
|
unsigned char command[] = {0x01};
|
||||||
|
if (n64_first_register) {
|
||||||
|
noInterrupts();
|
||||||
|
N64_PIND_send(n64_pincode, command, 1);
|
||||||
|
N64_PIND_get(n64_pincode);
|
||||||
|
interrupts();
|
||||||
|
} else {
|
||||||
|
noInterrupts();
|
||||||
|
N64_PINB_send(n64_pincode, command, 1);
|
||||||
|
N64_PINB_get(n64_pincode);
|
||||||
|
interrupts();
|
||||||
|
}
|
||||||
|
translate_raw_data();
|
||||||
|
}
|
98
N64Controller/N64Controller.h
Normal file
98
N64Controller/N64Controller.h
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/**
|
||||||
|
* Gamecube controller to Nintendo 64 adapter
|
||||||
|
* by Andrew Brown
|
||||||
|
* Rewritten for N64 to HID by Peter Den Hartog
|
||||||
|
* Modified to be a library by Kai Lüke
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To use, hook up the following to the Arduino:
|
||||||
|
* Digital I/O 2: N64 serial line
|
||||||
|
* All appropriate grounding and power lines, i.e.
|
||||||
|
* GND to left N64 controller PIN, Dig.PIN2 to middle Serial/Signal,
|
||||||
|
* 3.3V to right N64 PIN
|
||||||
|
* /------------\
|
||||||
|
* / O O O \
|
||||||
|
* | GND Signl 3.3V |
|
||||||
|
* |________________|
|
||||||
|
* Maybe: connect PIN X with external 1K pull-up resistor to the 3.3V rail
|
||||||
|
* Default and fallback PIN is 2
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef N64Controller_h
|
||||||
|
#define N64Controller_h
|
||||||
|
|
||||||
|
|
||||||
|
class N64Controller {
|
||||||
|
public:
|
||||||
|
N64Controller();
|
||||||
|
N64Controller(int serialPin); // first thing to call
|
||||||
|
void begin(int serialPin);
|
||||||
|
void begin(); // second thing to call
|
||||||
|
void update(); // then update always and get button info
|
||||||
|
// consider to have a delay instead of
|
||||||
|
// calling update all the time in a loop
|
||||||
|
inline bool button_D_up() { return n64_key_Dup; };
|
||||||
|
inline bool button_D_down() { return n64_key_Ddown; };
|
||||||
|
inline bool button_D_left() { return n64_key_Dleft; };
|
||||||
|
inline bool button_D_right() { return n64_key_Dright; };
|
||||||
|
inline bool button_Start() { return n64_key_Start; };
|
||||||
|
inline bool button_A() { return n64_key_A; };
|
||||||
|
inline bool button_B() { return n64_key_B; };
|
||||||
|
inline bool button_Z() { return n64_key_Z; };
|
||||||
|
inline bool button_L() { return n64_key_L; };
|
||||||
|
inline bool button_R() { return n64_key_R; };
|
||||||
|
inline bool button_C_up() { return n64_key_Cup; };
|
||||||
|
inline bool button_C_down() { return n64_key_Cdown; };
|
||||||
|
inline bool button_C_left() { return n64_key_Cleft; };
|
||||||
|
inline bool button_C_right() { return n64_key_Cright; };
|
||||||
|
inline int axis_x() { return n64_key_X; };
|
||||||
|
inline int axis_y() { return n64_key_Y; };
|
||||||
|
|
||||||
|
void print_N64_status();
|
||||||
|
private:
|
||||||
|
void set_up();
|
||||||
|
int n64_PIN; // might also be set by constructor or begin()
|
||||||
|
char n64_pincode;
|
||||||
|
bool n64_first_register; // PIN0-7: DDRD PIN8-13: DDRB
|
||||||
|
bool n64_key_Dup;
|
||||||
|
bool n64_key_Ddown;
|
||||||
|
bool n64_key_Dleft;
|
||||||
|
bool n64_key_Dright;
|
||||||
|
bool n64_key_Start;
|
||||||
|
bool n64_key_Z;
|
||||||
|
bool n64_key_A;
|
||||||
|
bool n64_key_B;
|
||||||
|
bool n64_key_Cup;
|
||||||
|
bool n64_key_Cdown;
|
||||||
|
bool n64_key_Cleft;
|
||||||
|
bool n64_key_Cright;
|
||||||
|
bool n64_key_L;
|
||||||
|
bool n64_key_R;
|
||||||
|
int n64_key_X;
|
||||||
|
int n64_key_Y;
|
||||||
|
|
||||||
|
void N64_init_PIND(char pincode);
|
||||||
|
void N64_PIND_send(char pincode, unsigned char *buffer, char length);
|
||||||
|
void N64_PIND_get(char pincode);
|
||||||
|
|
||||||
|
void N64_init_PINB(char pincode);
|
||||||
|
void N64_PINB_send(char pincode, unsigned char *buffer, char length);
|
||||||
|
void N64_PINB_get(char pincode);
|
||||||
|
|
||||||
|
void translate_raw_data();
|
||||||
|
|
||||||
|
// 8 bytes of data that we get from the controller
|
||||||
|
struct {
|
||||||
|
// bits: 0, 0, 0, start, y, x, b, a
|
||||||
|
unsigned char data1;
|
||||||
|
// bits: 1, L, R, Z, Dup, Ddown, Dright, Dleft
|
||||||
|
unsigned char data2;
|
||||||
|
char stick_x;
|
||||||
|
char stick_y;
|
||||||
|
} N64_status;
|
||||||
|
|
||||||
|
char N64_raw_dump[33]; // 1 received bit per byte
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
49
README
Normal file
49
README
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
Arduino N64 Controller Library
|
||||||
|
|
||||||
|
Based on the work in http://www.instructables.com/id/Use-an-Arduino-with-an-N64-controller/ here comes a comfortable library for usage with e.g. Arduino Uno. For NES there is already http://code.google.com/p/nespad/ . This library uses inline assembly and controllers can be attached to PIN 0 up to 13. But be aware that it's not written in best way possible. Place the folder N64Controller into your folder 'libraries'.
|
||||||
|
|
||||||
|
I used it in combination with TVout ( http://code.google.com/p/arduino-tvout/ ) and EEPROM ( http://arduino.cc/playground/Code/EEPROMWriteAnything ) for highscore I modified an existing Tetris port which itself uses Simple Tetris Clone under MIT license to be useing this library here and the result is quite nice: http://pothos.blogsport.eu/files/2012/03/N64Tetris.zip
|
||||||
|
|
||||||
|
|
||||||
|
Example code for library usage:
|
||||||
|
|
||||||
|
|
||||||
|
#include <N64Controller.h>
|
||||||
|
|
||||||
|
N64Controller player1 (12); // this controler for player one is on PIN 12
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
player1.begin(); // Initialisation
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
delay(30);
|
||||||
|
player1.update(); // read key state
|
||||||
|
if (player1.button_A() && player1.button_D_down()
|
||||||
|
|| player1.button_Start()) { // has no deeper meaning ;)
|
||||||
|
int xachse = player1.axis_x(); // can be negative oder positive
|
||||||
|
// regarding to orientation of the analog stick
|
||||||
|
}
|
||||||
|
// …
|
||||||
|
}
|
||||||
|
|
||||||
|
Wireing:
|
||||||
|
|
||||||
|
To use, hook up the following to the Arduino:
|
||||||
|
Digital I/O 2: N64 serial line
|
||||||
|
All appropriate grounding and power lines, i.e.
|
||||||
|
GND to left N64 controller PIN, Dig.PIN2 to middle Serial/Signal,
|
||||||
|
3.3V to right N64 PIN
|
||||||
|
/------------\
|
||||||
|
/ O O O \
|
||||||
|
| GND Signl 3.3V |
|
||||||
|
|________________|
|
||||||
|
Maybe: connect PIN X with external 1K pull-up resistor to the 3.3V rail
|
||||||
|
Default and fallback PIN is 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Gamecube controller to Nintendo 64 adapter
|
||||||
|
by Andrew Brown
|
||||||
|
Rewritten for N64 to HID by Peter Den Hartog
|
||||||
|
Modified to be a library with selectable pins by Kai Lüke
|
Loading…
Reference in New Issue
Block a user