The Nintendo 64 Controller Interface is an mbed library that allows one or more Nintendo 64 controllers to be used as input devices for the mbed. With this library, one will be able to control games created for an mbed using a Nintendo 64 controller. In addition, the library can easily be used to forward N64 inputs to a computer. Using the N64 Controller executable, one can communicate with multiple controllers.
Revision 0:95064759a964, committed 2016-04-28
- Comitter:
- fomartin
- Date:
- Thu Apr 28 00:10:38 2016 +0000
- Commit message:
- N64 Controller Interface;
Changed in this revision
diff -r 000000000000 -r 95064759a964 N64Controller/N64Controller.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/N64Controller/N64Controller.cpp Thu Apr 28 00:10:38 2016 +0000 @@ -0,0 +1,145 @@ +#include "N64Controller.h" + +extern "C" void my_wait_us_asm(int n); + +N64Controller::N64Controller(PinName pinNumber, bool passiveMode) + :pin(pinNumber), + debug(USBTX, USBRX) +{ + this->passiveMode = passiveMode; + inputData = 0; + + //open drain means that the pin never actually drives the signal high. + //the pull-up resistor drives it high, and the pin can "drain" the signal + //to drive it low. other internet sources say this is the best way to + //set up the n64 data pin, and that driving it high with the mbed + //could damage the mbed or the controller + pin.mode(OpenDrain); + + writeByte(RESET); //not sure if this is mandatory... but it resets the "neutral" stick position to the current position + + ticker.attach_us(this, &N64Controller::poll, POLLING_INTERVAL); +} + +void N64Controller::poll() +{ + writeByte(GET_DATA); + readBytes((char *)(&inputData), 4); + + //due to order we are reading bits in vs. order we are storing them, + //X and Y values need their bits reversed (all other values are single bit, + //so there is no need to reverse them) + + //reverse bits in X byte (bit-magic was taken from stack overflow... it simply reverses bits) + char *xByte = ((char *)(&inputData)) + 2; + *xByte = (*xByte & 0xF0) >> 4 | (*xByte & 0x0F) << 4; + *xByte = (*xByte & 0xCC) >> 2 | (*xByte & 0x33) << 2; + *xByte = (*xByte & 0xAA) >> 1 | (*xByte & 0x55) << 1; + + //reverse bits in Y byte + char *yByte = ((char *)(&inputData)) + 3; + *yByte = (*yByte & 0xF0) >> 4 | (*yByte & 0x0F) << 4; + *yByte = (*yByte & 0xCC) >> 2 | (*yByte & 0x33) << 2; + *yByte = (*yByte & 0xAA) >> 1 | (*yByte & 0x55) << 1; +} + + +bool N64Controller::a() { return ((inputData & (A_MASK)) > 0); } +bool N64Controller::b() { return (inputData & (B_MASK)) > 0; } +bool N64Controller::l() { return (inputData & (L_MASK)) > 0; } +bool N64Controller::r() { return (inputData & (R_MASK)) > 0; } +bool N64Controller::z() { return (inputData & (Z_MASK)) > 0; } +bool N64Controller::start() { return (inputData & (START_MASK)) > 0; } + +bool N64Controller::dUp() { return (inputData & (D_UP_MASK)) > 0; } +bool N64Controller::dDown() { return (inputData & (D_DOWN_MASK)) > 0; } +bool N64Controller::dLeft() { return (inputData & (D_LEFT_MASK)) > 0; } +bool N64Controller::dRight() { return (inputData & (D_RIGHT_MASK)) > 0; } + +bool N64Controller::cUp() { return (inputData & (C_UP_MASK)) > 0; } +bool N64Controller::cDown() { return (inputData & (C_DOWN_MASK)) > 0; } +bool N64Controller::cLeft() { return (inputData & (C_LEFT_MASK)) > 0; } +bool N64Controller::cRight() { return (inputData & (C_RIGHT_MASK)) > 0; } + +int8_t N64Controller::joyX() { return (inputData >> X_R_SHIFT) & 0xFF; } +int8_t N64Controller::joyY() { return (inputData >> Y_R_SHIFT) & 0xFF; } + +uint32_t N64Controller::getInputData() { return inputData; } + + +void N64Controller::writeByte(char byte) +{ + __disable_irq(); // Disable Interrupts + + for(int bitNumber = 7; bitNumber >= 0; bitNumber--) + { + int bit = 0; + bit = (byte >> bitNumber) & 0x1; + + if(bit == 0) + { + pin.write(0); + myWait_us(3); + pin.write(1); + myWait_us(1); + } + else + { + pin.write(0); + myWait_us(1); + pin.write(1); + myWait_us(3); + } + } + + //end with signal bit + pin.write(0); + myWait_us(1); + pin.write(1); + + __enable_irq(); // Enable Interrupts +} + +void N64Controller::readBytes(char *buffer, int bytesToRead) +{ + __disable_irq(); // Disable Interrupts + + for(int i = 0; i < bytesToRead; i++) + { + char byte = 0; + + //read 1 byte at a time + for(int bit = 0; bit < 8; bit++) + { + int prevReading = pin.read(); + + //read until falling edge + while(true) + { + int reading = pin.read(); + + if(prevReading == 1 && reading == 0) + { + //falling edge detected. Wait 2 us and read the value in + myWait_us(2); + byte |= (pin.read() << bit); + break; + } + + prevReading = reading; + } + } + + //Since mbed is little-endian, the MSB is stored last. + //Since we want the data laid out in the same order it arrived in + //(i.e., we want A in bit 0), we put the LSB first + buffer[i] = byte; + } + + __enable_irq(); // Enable Interrupts +} + +void N64Controller::myWait_us(int n) +{ + my_wait_us_asm(n); +}
diff -r 000000000000 -r 95064759a964 N64Controller/N64Controller.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/N64Controller/N64Controller.h Thu Apr 28 00:10:38 2016 +0000 @@ -0,0 +1,79 @@ +#ifndef __N64Controller__ +#define __N64Controller__ + +#include "mbed.h" + +class N64Controller +{ + public: + /* + * Passive Mode makes it so the mbed does not request data from the n64 controller. This allows the controller + * to be plugged into a real n64 and have the signal split to the mbed as well. The mbed will still read the + * incoming inputs in passive mode, but all of the data requests will come from the n64 itself. + */ + N64Controller(PinName pin, bool passiveMode); + + bool a(); + bool b(); + bool l(); + bool r(); + bool z(); + bool start(); + + bool dUp(); + bool dDown(); + bool dLeft(); + bool dRight(); + + bool cUp(); + bool cDown(); + bool cLeft(); + bool cRight(); + + int8_t joyX(); + int8_t joyY(); + + void setPassiveMode(bool); + + uint32_t getInputData(); + + private: + void poll(); + void writeByte(char data); + void readBytes(char* buffer, int numBytes); + void myWait_us(int numMicroseconds); + + DigitalInOut pin; + Ticker ticker; + uint32_t inputData; + bool passiveMode; + Serial debug; + + #define RESET 0xFF + #define GET_DATA 0x01 + + #define POLLING_INTERVAL 10000 //0.01 seconds + + #define A_MASK (0x1 << 0) + #define B_MASK (0x1 << 1) + #define Z_MASK (0x1 << 2) + #define START_MASK (0x1 << 3) + #define D_UP_MASK (0x1 << 4) + #define D_DOWN_MASK (0x1 << 5) + #define D_LEFT_MASK (0x1 << 6) + #define D_RIGHT_MASK (0x1 << 7) + + //bits 8, 9 unused + + #define L_MASK (0x1 << 10) + #define R_MASK (0x1 << 11) + #define C_UP_MASK (0x1 << 12) + #define C_DOWN_MASK (0x1 << 13) + #define C_LEFT_MASK (0x1 << 14) + #define C_RIGHT_MASK (0x1 << 15) + + #define X_R_SHIFT 16 + #define Y_R_SHIFT 24 +}; + +#endif \ No newline at end of file
diff -r 000000000000 -r 95064759a964 N64Controller/my_wait_us_asm.s --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/N64Controller/my_wait_us_asm.s Thu Apr 28 00:10:38 2016 +0000 @@ -0,0 +1,113 @@ + AREA asm_func, CODE, READONLY + EXPORT my_wait_us_asm + +my_wait_us_asm + +WAIT_1_US + ; According to ARM spec, NOPs may be removed by the assembler, so they + ; are not a reliable way to eat up time. Instead we simply do empty adds + ; to eat up clock cycles. + + ; The LPC1768 operates at 96 MHz. Assuming each instruction is 1 clock cycle, + ; 96 instructions should take 1 microsecond + + ADD R1, R2, #0 ; clock cycle 1 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 ; clock cycle 10 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 ; clock cycle 20 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 ; clock cycle 20 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 ; clock cycle 30 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 ; clock cycle 50 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 ; clock cycle 60 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 ; clock cycle 70 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 ; clock cycle 80 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 ; clock cycle 90 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 + ADD R1, R2, #0 ; clock cycle 94 + + SUBS R0, R0, #1 ; clock cycle 95 + BGT WAIT_1_US ; clock cycle 96 + + BX LR ; return + END \ No newline at end of file
diff -r 000000000000 -r 95064759a964 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Apr 28 00:10:38 2016 +0000 @@ -0,0 +1,189 @@ +#include "mbed.h" +#include "N64Controller.h" + +N64Controller controller(p25, false); + +DigitalOut led1(LED1); +DigitalOut led2(LED2); +DigitalOut led3(LED3); +DigitalOut led4(LED4); + +Serial pc (USBTX, USBRX); + +//Timer timer; + +int main() { + while(1) + { + if(pc.getc() == '!') + { + //we had issues sending the raw binary over serial (probably because + //the serial connection was interpreting them as characters) + //so instead we will just send the raw ASCII for the number + //e.g., if inputData is 5267 we will send the string '0000005267' + //(max uint32 value is 10 digits, so we pad with 0 to ensure we + //are always sending 10 bytes + uint32_t data = controller.getInputData(); + char payload[10]; + sprintf(payload, "%010u", data); + + for(int i = 0; i < 10; i++) + { + pc.putc(payload[i]); + } + } + else + { + led1 = 1; + led2 = 1; + led3 = 1; + led4 = 1; + } + } + + //timer.start(); +// +// while(1) +// { +// if(controller.a()) +// { +// led1 = 1; +// } +// else +// { +// led1 = 0; +// } +// +// if(controller.b()) +// { +// led2 = 1; +// } +// else +// { +// led2 = 0; +// } +// +// if(controller.l()) +// { +// led3 = 1; +// } +// else +// { +// led3 = 0; +// } +// +// if(controller.r()) +// { +// led4 = 1; +// } +// else +// { +// led4 = 0; +// } +// +// //prints inputs to serial once every two seconds +// if(timer.read_ms() > 2000) +// { +// char output [1000]; +// memset(output, 0x0, 1000); +// int outputIndex = 0; +// +// sprintf(&output[outputIndex], "\n\rButtons Pressed:\n\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// +// if(controller.a()) +// { +// sprintf(&output[outputIndex], "\tA\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.b()) +// { +// sprintf(&output[outputIndex], "\tB\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.z()) +// { +// sprintf(&output[outputIndex], "\tZ\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.l()) +// { +// sprintf(&output[outputIndex], "\tL\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.r()) +// { +// sprintf(&output[outputIndex], "\tR\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.cUp()) +// { +// sprintf(&output[outputIndex], "\tC-Up\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.cDown()) +// { +// sprintf(&output[outputIndex], "\tC-Down\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.cLeft()) +// { +// sprintf(&output[outputIndex], "\tC-Left\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.cRight()) +// { +// sprintf(&output[outputIndex], "\tC-Right\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.dUp()) +// { +// sprintf(&output[outputIndex], "\tD-Up\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.dDown()) +// { +// sprintf(&output[outputIndex], "\tD-Down\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.dLeft()) +// { +// sprintf(&output[outputIndex], "\tD-Left\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// if(controller.dRight()) +// { +// sprintf(&output[outputIndex], "\tD-Right\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// } +// +// sprintf(&output[outputIndex], "\nAnalog Position:\n\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// +// sprintf(&output[outputIndex], "\tX: %d\n\r", controller.joyX()); +// outputIndex += strlen(&output[outputIndex]); +// +// sprintf(&output[outputIndex], "\tY: %d\n\r", controller.joyY()); +// outputIndex += strlen(&output[outputIndex]); +// +// sprintf(&output[outputIndex], "\n\r-----------------\n\r"); +// outputIndex += strlen(&output[outputIndex]); +// +// pc.printf("%s", output); +// +// timer.reset(); +// } +// } +}
diff -r 000000000000 -r 95064759a964 mbed.bld --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Thu Apr 28 00:10:38 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/99a22ba036c9 \ No newline at end of file