NES Controller / GameBoy Button Translation


#1

Yahtzee! Great work - looking forward to the video!

Yeah, saw your instagram post about the consolizer:

Let me know if you still need any help with the Arduino code - talking to a NES controller is a solved problem :slight_smile: @Mr.Blinky has some nice code for that:


VGA1306 (VGA-out for DIY Arduboys implemented on an FPGA!)
(Elliot Goofe) #2

Oh hell yeah! So this works for DMG and not just Arduboy? This was something I couldn’t fully get my head around. I really want to integrate a NES controller into the consolizer.


#3

Just the output side of the code would need to be adjusted - to present the output ‘four buttons at a time’ like the DMG wants:

…you have sending button presses to the DMG working though, right? Just need the part for getting button inputs from the joypad?


(Elliot Goofe) #4

Was able to get half the buttons working: start, select, a, b, sort of. So half of that circuit. My buddy Postman helped me with getting that bit of code. I’m not the best myself with programming.

We had issues converting the 8 bits of data to 4 so the Gameboy could read it. In that Instagram post I have a video demoing pressing the A button that basically presses random buttons at a time.

Any info to help move forward would be much appreciated!

I made a diagram to show the button traces that go to the DAN225 diodes that communicate with the cpu for button presses if that helps anyone.

I would really love to see controls working without the need for the front PCB. I’ve already tested the board outputs video without it, - audio and buttons.


#5

Post the code or a link to the code and it should be straightforward enough - heading to the beach, so might be a while before I can reply! :slight_smile:


(Elliot Goofe) #6
const int A_BUTTON         = 0;
const int B_BUTTON         = 1;
const int SELECT_BUTTON    = 2;
const int START_BUTTON     = 3;

const int DOWN_BUTTON      = 3;
const int UP_BUTTON        = 2;
const int LEFT_BUTTON      = 1;
const int RIGHT_BUTTON     = 0;

byte nesRegisterP14  = 0;    // We will use this to hold button states in first four bits pertaining to D-pad
byte nesRegisterP15  = 0;    // We will use this to hold button states in the second four bits of data pertaining to A, B, Select, and Start

//Inputs
int nesData = 4; //Data pin for NES Controler
int P14 = A4; //P14 pin on GameBoy's CPU, Set low to read d-pad input
int P15 = A5; //P15 pin on Gameboy's CPU, set low to read A, B, Start, Select

//Outputs
int nesClock = 2; //CLK pin for NES controller
int nesLatch = 3; //Latch pin for NES controller

/*P10-13 represent bits of controller info controlled by P14 and P15 states
P10 -> d-pad right or a
P11 -> d-pad left or b
P12 -> d-pad up or select
P13 -> d-pad down or start*/

int P10 = 6;
int P11 = 7;
int P12 = 8;
int P13 = 9;

byte readNesControllerP14()
{
  int tempData1 = 127;

  digitalWrite(nesLatch, HIGH);
  digitalWrite(nesLatch, LOW);
 
    // Up button
  digitalWrite(nesClock, HIGH);
  digitalWrite(nesClock, LOW);
  if (digitalRead(nesData) == LOW)
    bitClear(tempData1, UP_BUTTON);
    
  // Down button
  digitalWrite(nesClock, HIGH);
  digitalWrite(nesClock, LOW);
  if (digitalRead(nesData) == LOW)
    bitClear(tempData1, DOWN_BUTTON);

  // Left button
  digitalWrite(nesClock, HIGH);
  digitalWrite(nesClock, LOW);
  if (digitalRead(nesData) == LOW)
    bitClear(tempData1, LEFT_BUTTON);  
    
  // Right button
  digitalWrite(nesClock, HIGH);
  digitalWrite(nesClock, LOW);
  if (digitalRead(nesData) == LOW)
    bitClear(tempData1, RIGHT_BUTTON);

  return tempData1;
}


byte readNesControllerP15()
{
  int tempData2 = 127;

 
  digitalWrite(nesClock, HIGH);
  digitalWrite(nesClock, LOW);
  if (digitalRead(nesData) == LOW)
    bitClear(tempData2, A_BUTTON);
    
  digitalWrite(nesClock, HIGH);
  digitalWrite(nesClock, LOW);
  if (digitalRead(nesData) == LOW)
    bitClear(tempData2, B_BUTTON);
 
 
  // Select button
  digitalWrite(nesClock, HIGH);
  digitalWrite(nesClock, LOW);
  if (digitalRead(nesData) == LOW)
    bitClear(tempData2, SELECT_BUTTON);

  // Start button
  digitalWrite(nesClock, HIGH);
  digitalWrite(nesClock, LOW);
  if (digitalRead(nesData) == LOW)
    bitClear(tempData2, START_BUTTON);

    return tempData2;
}



void setup()
{
  //Setting input pins to inputs
  pinMode(nesData, INPUT_PULLUP);
  pinMode(P14, INPUT_PULLUP);
  pinMode(P15, INPUT_PULLUP);

  //Setting output pins to output
  pinMode(nesClock, OUTPUT);
  pinMode(nesLatch, OUTPUT);
  pinMode(P10, OUTPUT);
  pinMode(P11, OUTPUT);
  pinMode(P12, OUTPUT);
  pinMode(P13, OUTPUT);

  //setting initial states
  digitalWrite(nesClock, LOW);
  digitalWrite(nesLatch, LOW);


}

void loop()
{

  // Quickly pulse the nesLatch pin so that the register grab what it see on
  // its parallel data pins.

  if(analogRead(P14) > 127)
  {
    nesRegisterP14 = readNesControllerP14();

    digitalWrite(P10, bitRead(nesRegisterP14, RIGHT_BUTTON));
    digitalWrite(P11, bitRead(nesRegisterP14, LEFT_BUTTON));
    digitalWrite(P12, bitRead(nesRegisterP14, UP_BUTTON));
    digitalWrite(P13, bitRead(nesRegisterP14, DOWN_BUTTON));
    
  }



  if(analogRead(P15) <= 127)
  {
    nesRegisterP15 = readNesControllerP15();

    digitalWrite(P10, bitRead(nesRegisterP15, A_BUTTON));
    digitalWrite(P11, bitRead(nesRegisterP15, B_BUTTON));
    digitalWrite(P12, bitRead(nesRegisterP15, SELECT_BUTTON));
    digitalWrite(P13, bitRead(nesRegisterP15, START_BUTTON));
    
  }



Serial.println("P14 val");
Serial.println(nesRegisterP14);
Serial.println("P15 val");
Serial.println(nesRegisterP15);

}

(Pharap) #7

I’ve formatted your code for you. You might want to have a read of this:

Particularly this section. Knowing markdown comes in very handy.

(Don’t let the onebox fool you, it’s a page on the project’s wiki. Really it ought to be a separate repo or a gist or something.)


#8

OK, have adapted @Mr.Blinky’s code accordingly, and edited the pin numbers to match the ones used in your code - here you go:

…let me know how that goes?


(Elliot Goofe) #9

Is this supposed to be in the code? Should we be defining it as a byte? There’s no function to feed these values into.

Tried uploading the code. The buttons sort of work, other times it freezes or resets.


#10

Might work better using interrupts for P14 / P15, will set up something and play here - what is the specific Arduino board you are using?


(Elliot Goofe) #11

M0 with samd21. We are using “hard coded” interrupts. It’s sort of working. We have glitchy dpad control and combos of start/a start/b.

I’ll post the updated code with comments in my Google drive.

https://drive.google.com/file/d/1HeE-v2wXGz2z9sM-_3GcC0axVg93dXw8/view?usp=drivesdk


#12

SAMD21 is 3.3V though? (and a bit of overkill? :slight_smile:) I have it working and pretty stable now on a 5V Pro Micro (if you have a Leonardo / Micro / Pro Micro around you can use the same code and pin assignments, otherwise would need to adjust the PORT / PIN commands to be specific to your board):

…didn’t have a NES controller close-to-hand, so used this ASCII Stick instead! :smile:


(Elliot Goofe) #13

Hehe yes a bit overkill. I’ll use a Pro Micro. Testing currently.

Just wondering what _BV is reffering to? I’m just making sure I don’t need to add anything else to compile.

Getting errors about PortF when compiling.

Are you including arduboy2core.cpp?


#14

There is nothing else to add, should compile on its own. Errors about PORTF would mean you don’t have the right board selected? Pro Micro isn’t built-in to the Arduino IDE boards, but you can just select Leonardo.

https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_use_bv.html


(Elliot Goofe) #15

Thank you. I had to program a generic Pro Micro as a Leonardo. Trying to get it to work now. *Fingers crossed

Still having issues… :confused:


(Elliot Goofe) #16

I think the issue might stem from using a NES controller and you are using that ASCII controller. The NES uses a 4021 shift register IC. Which makes plugging the input to the Gameboy harder.


#17

No, the ASCII Stick is a NES / Famicom controller with a 4021 inside - just had a weird form-factor… could you post a photo of your wiring to the Pro Micro? Did you follow the pin assignments in the code:

//DMG button driver pins
#define P10  A0
#define P11  A1
#define P12  A2
#define P13  A3
#define P14   2
#define P15   3

//NES controller pins
#define CONTROLLER_DATA  4
#define CONTROLLER_LATCH 5
#define CONTROLLER_CLOCK 6

(Elliot Goofe) #18

Ah okay. My mistake. Here is a shot of the wiring. Sorry for the mess.


#19

Wow :slight_smile: Heading out, so I’ll try and take a closer look later on - assuming that you are inserting pins on the breadboard to simulate button presses?


(Elliot Goofe) #20

Yup. The chip was from a controller with a broken pcb, so I had to remove that to test. This should work with a SNES controller too right?