Created
May 24, 2023 02:21
-
-
Save evinjaff/65406133b9823e21a53ba5cb11d8ec3c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// NES Controller FSM implementation - By Evin Jaff (2023) | |
// Wrote this for funsies during a DnD session with friends because I was bored | |
// Wanted to see if I could write some code to emulate the NES Controller signals - might use this driver code for another project | |
// https://tresi.github.io/nes/ | |
// https://www.nesdev.org/wiki/Standard_controller | |
//Preprocessed Pins | |
#define LATCH_PIN 4 | |
#define CLCK_PIN 5 | |
#define DATA_PIN 6 | |
#define check_latch digitalRead(LATCH_PIN) | |
#define check_pulse digitalRead(CLCK_PIN) | |
//global vars | |
bool pulse_fall; | |
bool latch_fall; | |
unsigned int previous_pulse_state = 0; | |
unsigned int previous_latch_state = 0; | |
enum states { | |
IDLE, | |
A, | |
B, | |
SELECT, | |
START, | |
UP, | |
DOWN, | |
LEFT, | |
RIGHT | |
}; | |
enum states curr_state; | |
void setup() { | |
// put your setup code here, to run once: | |
Serial.begin(9600); | |
curr_state=IDLE; | |
pulse_fall = false; | |
latch_fall = false; | |
} | |
void loop() { | |
// put your main code here, to run repeatedly: | |
unsigned int pulse = check_pulse; | |
unsigned int latch = check_latch; | |
//Main loop pseudocode - Start in Idle before a Latch | |
//Wait for a HIGH on the latch, and then poll the controller for inputs | |
switch (curr_state){ | |
case IDLE: | |
//check transition | |
if (latch){ | |
curr_state = A; | |
} | |
break; | |
case A: | |
if(!latch_fall){ | |
check_latch_fall(latch); | |
} | |
//check latch fall instead | |
if(pulse == 1){ | |
pulse_fall = false; | |
latch_fall = false; | |
curr_state = B; | |
} | |
break; | |
case B: | |
if(!pulse_fall){ | |
check_pulse_fall(pulse); | |
} | |
//signal states | |
//check latch fall instead | |
if(pulse == 1 && pulse_fall){ | |
pulse_fall = false; | |
latch_fall = false; | |
curr_state = SELECT; | |
} | |
break; | |
case SELECT: | |
if(!pulse_fall){ | |
check_pulse_fall(pulse); | |
} | |
//signal states | |
//check transition | |
if(pulse && pulse_fall){ | |
pulse_fall = false; | |
curr_state = START; | |
} | |
break; | |
case START: | |
if(!pulse_fall){ | |
check_pulse_fall(pulse); | |
} | |
//signal states | |
digitalWrite(DATA_PIN, 0); | |
//check transition | |
if(pulse && pulse_fall){ | |
pulse_fall = false; | |
curr_state = UP; | |
} | |
break; | |
case UP: | |
if(!pulse_fall){ | |
check_pulse_fall(pulse); | |
} | |
//signal states | |
digitalWrite(DATA_PIN, 0); | |
//check transition | |
if(pulse && pulse_fall){ | |
pulse_fall = false; | |
curr_state = DOWN; | |
} | |
break; | |
case DOWN: | |
if(!pulse_fall){ | |
check_pulse_fall(pulse); | |
} | |
//signal states | |
digitalWrite(DATA_PIN, 0); | |
//check transition | |
if(pulse && pulse_fall){ | |
pulse_fall = false; | |
curr_state = LEFT; | |
} | |
break; | |
case LEFT: | |
if(!pulse_fall){ | |
check_pulse_fall(pulse); | |
} | |
//signal states | |
digitalWrite(DATA_PIN, 0); | |
//check transition | |
if(pulse && pulse_fall){ | |
pulse_fall = false; | |
curr_state = RIGHT; | |
} | |
break; | |
case RIGHT: | |
if(!pulse_fall){ | |
check_pulse_fall(pulse); | |
} | |
//signal states | |
digitalWrite(DATA_PIN, 0); | |
//check transition | |
if(pulse && pulse_fall){ | |
pulse_fall = false; | |
curr_state = IDLE; | |
} | |
break; | |
} | |
previous_pulse_state = pulse; | |
previous_latch_state = latch; | |
// Delay for stepping through polling | |
// delay(200); | |
} | |
//Code to make sure that a lingering HIGH on the latch doesn't skip the FSM state. Does this by checking every cycle if the latch has fallen to 0 | |
void check_pulse_fall(unsigned int pulse){ | |
if (previous_pulse_state == 1 & pulse == 0){ | |
pulse_fall = true; | |
} | |
else if(pulse == 0){ | |
pulse_fall = true; | |
} | |
else{ | |
pulse_fall = false; | |
} | |
} | |
//Code to make sure that a lingering HIGH on the latch doesn't skip the FSM state. Does this by checking every cycle if the latch has fallen to 0 | |
void check_latch_fall(unsigned int latch){ | |
if (latch == 0){ | |
latch_fall = true; | |
} | |
} | |
//debugging function | |
void print_state(enum states state){ | |
switch (curr_state){ | |
case IDLE: | |
Serial.println("IDLE"); | |
break; | |
case A: | |
Serial.println("A"); | |
break; | |
case B: | |
Serial.println("B"); | |
break; | |
case SELECT: | |
Serial.println("SELECT"); | |
break; | |
case START: | |
Serial.println("START"); | |
break; | |
case UP: | |
Serial.println("UP"); | |
break; | |
case DOWN: | |
Serial.println("DOWN"); | |
break; | |
case LEFT: | |
Serial.println("LEFT"); | |
break; | |
case RIGHT: | |
Serial.println("RIGHT"); | |
break; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment