|
| 1 | +/* |
| 2 | + * Sega Controller Reader |
| 3 | + * Author: Jon Thysell <thysell@gmail.com> |
| 4 | + * Version: 1.0 |
| 5 | + * Date: 7/26/2014 |
| 6 | + * |
| 7 | + * Reads buttons presses from Sega Genesis 3/6 button controllers |
| 8 | + * and reports their state via the Serial connection. Handles hot |
| 9 | + * swapping of controllers and auto-switches between 3 and 6 button |
| 10 | + * polling patterns. |
| 11 | + * |
| 12 | + */ |
| 13 | + |
| 14 | +// Controller Button Flags |
| 15 | +const int ON = 1; |
| 16 | +const int UP = 2; |
| 17 | +const int DOWN = 4; |
| 18 | +const int LEFT = 8; |
| 19 | +const int RIGHT = 16; |
| 20 | +const int START = 32; |
| 21 | +const int A = 64; |
| 22 | +const int B = 128; |
| 23 | +const int C = 256; |
| 24 | +const int X = 512; |
| 25 | +const int Y = 1024; |
| 26 | +const int Z = 2048; |
| 27 | + |
| 28 | +// Controller DB9 Pin 7 Mappings |
| 29 | +const int SELECT[] = { 8, 9 }; |
| 30 | + |
| 31 | +typedef struct |
| 32 | +{ |
| 33 | + int player; |
| 34 | + int pin; |
| 35 | + int lowFlag; |
| 36 | + int highFlag; |
| 37 | + int pulse3Flag; |
| 38 | +} input; |
| 39 | + |
| 40 | +// Controller DB9 Pin to Button Flag Mappings |
| 41 | +// First column is the controller index, second column |
| 42 | +// is the Arduino pin that the controller's DB9 pin is |
| 43 | +// attached to |
| 44 | +input inputMap[] = { |
| 45 | + { 0, 2, UP, UP, Z}, // P0 DB9 Pin 1 |
| 46 | + { 0, 3, DOWN, DOWN, Y}, // P0 DB9 Pin 2 |
| 47 | + { 0, 4, ON, LEFT, X}, // P0 DB9 Pin 3 |
| 48 | + { 0, 5, ON, RIGHT, 0}, // P0 DB9 Pin 4 |
| 49 | + { 0, 6, A, B, 0}, // P0 DB9 Pin 6 |
| 50 | + { 0, 7, START, C, 0}, // P0 DB9 Pin 9 |
| 51 | + { 1, A0, UP, UP, Z}, // P1 DB9 Pin 1 |
| 52 | + { 1, A1, DOWN, DOWN, Y}, // P1 DB9 Pin 2 |
| 53 | + { 1, A2, ON, LEFT, X}, // P1 DB9 Pin 3 |
| 54 | + { 1, A3, ON, RIGHT, 0}, // P1 DB9 Pin 4 |
| 55 | + { 1, A4, A, B, 0}, // P1 DB9 Pin 6 |
| 56 | + { 1, A5, START, C, 0} // P1 DB9 Pin 9 |
| 57 | +}; |
| 58 | + |
| 59 | +// Controller State |
| 60 | +int currentState[] = { 0, 0 }; |
| 61 | +int lastState[] = { -1, -1 }; |
| 62 | + |
| 63 | +// Default to three-button mode until six-button connects |
| 64 | +boolean sixButtonMode[] = { false, false }; |
| 65 | + |
| 66 | +void setup() |
| 67 | +{ |
| 68 | + // Setup input pins |
| 69 | + for (int i = 0; i < sizeof(inputMap) / sizeof(input); i++) |
| 70 | + { |
| 71 | + pinMode(inputMap[i].pin, INPUT); |
| 72 | + digitalWrite(inputMap[i].pin, HIGH); |
| 73 | + } |
| 74 | + |
| 75 | + // Setup select pins |
| 76 | + for (int i = 0; i < 2; i++) |
| 77 | + { |
| 78 | + pinMode(SELECT[i], OUTPUT); |
| 79 | + digitalWrite(SELECT[i], HIGH); |
| 80 | + } |
| 81 | + |
| 82 | + Serial.begin(9600); |
| 83 | +} |
| 84 | + |
| 85 | +void loop() |
| 86 | +{ |
| 87 | + readButtons(); |
| 88 | + sendStates(); |
| 89 | +} |
| 90 | + |
| 91 | +void readButtons() |
| 92 | +{ |
| 93 | + for (int i = 0; i < 2; i++) |
| 94 | + { |
| 95 | + resetState(i); |
| 96 | + if (sixButtonMode[i]) |
| 97 | + { |
| 98 | + read6buttons(i); |
| 99 | + } |
| 100 | + else |
| 101 | + { |
| 102 | + read3buttons(i); |
| 103 | + } |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +void resetState(int player) |
| 108 | +{ |
| 109 | + currentState[player] = 0; |
| 110 | +} |
| 111 | + |
| 112 | +void read3buttons(int player) |
| 113 | +{ |
| 114 | + // Set SELECT LOW and read lowFlag |
| 115 | + digitalWrite(SELECT[player], LOW); |
| 116 | + |
| 117 | + delayMicroseconds(20); |
| 118 | + |
| 119 | + for (int i = 0; i < sizeof(inputMap) / sizeof(input); i++) |
| 120 | + { |
| 121 | + if (inputMap[i].player == player && digitalRead(inputMap[i].pin) == LOW) |
| 122 | + { |
| 123 | + currentState[player] |= inputMap[i].lowFlag; |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + // Set SELECT HIGH and read highFlag |
| 128 | + digitalWrite(SELECT[player], HIGH); |
| 129 | + |
| 130 | + delayMicroseconds(20); |
| 131 | + |
| 132 | + for (int i = 0; i < sizeof(inputMap) / sizeof(input); i++) |
| 133 | + { |
| 134 | + if (inputMap[i].player == player && digitalRead(inputMap[i].pin) == LOW) |
| 135 | + { |
| 136 | + currentState[player] |= inputMap[i].highFlag; |
| 137 | + } |
| 138 | + } |
| 139 | + |
| 140 | + // When a six-button first connects, it'll spam UP and DOWN, |
| 141 | + // which signals the game to switch to 6-button polling |
| 142 | + if (currentState[player] == (ON | UP | DOWN)) |
| 143 | + { |
| 144 | + sixButtonMode[player] = true; |
| 145 | + } |
| 146 | + // When a controller disconnects, revert to three-button polling |
| 147 | + else if ((currentState[player] & ON) == 0) |
| 148 | + { |
| 149 | + sixButtonMode[player] = false; |
| 150 | + } |
| 151 | + |
| 152 | + delayMicroseconds(20); |
| 153 | +} |
| 154 | + |
| 155 | +void read6buttons(int player) |
| 156 | +{ |
| 157 | + // Poll for three-button states twice |
| 158 | + read3buttons(player); |
| 159 | + read3buttons(player); |
| 160 | + |
| 161 | + // After two three-button polls, pulse the SELECT line |
| 162 | + // so the six-button reports the higher button states |
| 163 | + digitalWrite(SELECT[player], LOW); |
| 164 | + delayMicroseconds(20); |
| 165 | + digitalWrite(SELECT[player], HIGH); |
| 166 | + |
| 167 | + for(int i = 0; i < sizeof(inputMap) / sizeof(input); i++) |
| 168 | + { |
| 169 | + if (inputMap[i].player == player && digitalRead(inputMap[i].pin) == LOW) |
| 170 | + { |
| 171 | + currentState[player] |= inputMap[i].pulse3Flag; |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + delayMicroseconds(1000); |
| 176 | +} |
| 177 | + |
| 178 | +void sendStates() |
| 179 | +{ |
| 180 | + // Only report controller states if at least one has changed |
| 181 | + boolean hasChanged = false; |
| 182 | + |
| 183 | + for (int i = 0; i < 2; i++) |
| 184 | + { |
| 185 | + if (currentState[i] != lastState[i]) |
| 186 | + { |
| 187 | + hasChanged = true; |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + if (hasChanged) |
| 192 | + { |
| 193 | + for (int i = 0; i < 2; i++) |
| 194 | + { |
| 195 | + Serial.print((currentState[i] & ON) == ON ? "+" : "-"); |
| 196 | + Serial.print((currentState[i] & UP) == UP ? "U" : "0"); |
| 197 | + Serial.print((currentState[i] & DOWN) == DOWN ? "D" : "0"); |
| 198 | + Serial.print((currentState[i] & LEFT) == LEFT ? "L" : "0"); |
| 199 | + Serial.print((currentState[i] & RIGHT) == RIGHT ? "R" : "0"); |
| 200 | + Serial.print((currentState[i] & START) == START ? "S" : "0"); |
| 201 | + Serial.print((currentState[i] & A) == A ? "A" : "0"); |
| 202 | + Serial.print((currentState[i] & B) == B ? "B" : "0"); |
| 203 | + Serial.print((currentState[i] & C) == C ? "C" : "0"); |
| 204 | + Serial.print((currentState[i] & X) == X ? "X" : "0"); |
| 205 | + Serial.print((currentState[i] & Y) == Y ? "Y" : "0"); |
| 206 | + Serial.print((currentState[i] & Z) == Z ? "Z" : "0"); |
| 207 | + |
| 208 | + Serial.print((i == 0) ? "," : "\n"); |
| 209 | + lastState[i] = currentState[i]; |
| 210 | + } |
| 211 | + } |
| 212 | +} |
| 213 | + |
0 commit comments