Skip to content

Instantly share code, notes, and snippets.

@uXeBoy
Last active January 15, 2024 12:41
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save uXeBoy/5e9ec52823b7d73a187370573bdbda1b to your computer and use it in GitHub Desktop.
Save uXeBoy/5e9ec52823b7d73a187370573bdbda1b to your computer and use it in GitHub Desktop.
NES-controller.ino
/* Quick and dirty NES to DMG button driver (originally by Mr.Blinky)
*
* Using digital pins so it can be easily run on any Arduino
* Because DigitalRead and DigitalWrite are pretty slow, no delays are
* required when changing controller pin states and reading in data
*/
//NES button state masks
#define BS_A _BV(7)
#define BS_B _BV(6)
#define BS_SELECT _BV(5)
#define BS_START _BV(4)
#define BS_RIGHT _BV(3)
#define BS_LEFT _BV(2)
#define BS_UP _BV(1)
#define BS_DOWN _BV(0)
//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
volatile uint8_t buttons_state = 0xFF;
void setup()
{
//Serial.begin(9600);
//NES controller pins
pinMode(CONTROLLER_DATA, INPUT_PULLUP);
pinMode(CONTROLLER_LATCH, OUTPUT);
pinMode(CONTROLLER_CLOCK, OUTPUT);
//DMG button driver pins
pinMode(P10, OUTPUT);
pinMode(P11, OUTPUT);
pinMode(P12, OUTPUT);
pinMode(P13, OUTPUT);
pinMode(P14, INPUT_PULLUP);
pinMode(P15, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(P14), interruptP14, FALLING);
attachInterrupt(digitalPinToInterrupt(P15), interruptP15, FALLING);
}
void interruptP14()
{
PORTF = (buttons_state & 0x0F) << 4;
}
void interruptP15()
{
PORTF = (buttons_state & 0xF0);
}
void updateButtonState(uint8_t button)
{
if (digitalRead(CONTROLLER_DATA)) buttons_state |= button;
else buttons_state &= ~button;
digitalWrite(CONTROLLER_CLOCK, HIGH); //clock out next button state
digitalWrite(CONTROLLER_CLOCK, LOW);
}
void loop()
{ //read controller button states
digitalWrite(CONTROLLER_LATCH, HIGH); //parallel load controller button states
digitalWrite(CONTROLLER_CLOCK, LOW); //ensure clock is low when switching to serial mode
digitalWrite(CONTROLLER_LATCH, LOW); //switch to serial mode
updateButtonState(BS_A);
updateButtonState(BS_B);
updateButtonState(BS_SELECT);
updateButtonState(BS_START);
updateButtonState(BS_UP);
updateButtonState(BS_DOWN);
updateButtonState(BS_LEFT);
updateButtonState(BS_RIGHT);
if (PIND & B00000011) PORTF = 0xF0;
//Serial.println(buttons_state, BIN);
}
@HoZy88
Copy link

HoZy88 commented Nov 9, 2020

Odd question, I'm looking at using a SNES controller via an Arduino but I want to be able to split the outputs to separate pins on a pro micro.
So, up/down/left/right/a/b/x/y/l/r/start/select etc. All on separate pins for output. How would I go about modifying this? Thanks.

@uXeBoy
Copy link
Author

uXeBoy commented Nov 9, 2020

@HoZy88 the code by @MrBlinky that I adapted this from in the first place is much closer to what you are looking for - here it is, the original and the best: https://github.com/MrBlinky/Arduboy/blob/master/SNES-controller/SNES-controller.ino

@Tman2097
Copy link

Can we get a version of this for Atmega328?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment