├── LICENSE ├── README.md ├── examples ├── ControllerTest │ └── ControllerTest.ino ├── GameportToUSB │ ├── GameportToUSB.ino │ ├── LICENSE │ └── README.md └── GenesisToXBox360 │ └── GenesisToXBox360.ino ├── keywords.txt ├── library.properties └── src ├── GameControllers.h ├── SegaController.cpp ├── SegaController.h ├── debouncer.h ├── dwt.h ├── gamecube.cpp ├── gameport.cpp ├── genesis.cpp └── nunchuck.cpp /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-19 arpruss and (c) 2017 Jon Thysell 4 | 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## GameControllersSTM32 2 | GameControllersSTM32 is a library that allows reading some miscellaneous game controllers on STM32F1 boards. 3 | Supported controllers include: 4 | 5 | ## Purpose 6 | Read data from: 7 | 8 | * GameCube 9 | 10 | * Wii Nunchuck (accelerometer not currently supported) 11 | 12 | * PC analog game port 13 | 14 | ## Usage 15 | * `GameCubeController controller = GameCubeController(unsigned pin);` 16 | 17 | * `NunchuckController controller = NunchuckController(unsigned scl=PB6, unsigned sda=PB7);` (uses software I2C) 18 | 19 | * `GamePortController controller = GamePortController(unsigned xPin, unsigned yPin, unsigned sliderPin, unsigned rotatePin, 20 | unsigned button1Pin, unsigned button2Pin, unsigned button3Pin, unsigned button4Pin);` 21 | 22 | * `bool controller.begin();` (Returns false on fail) 23 | 24 | * `bool controller.read(GameControllerData_t* data);` (Returns false on fail) 25 | 26 | * pins on gameport connector: X=3, Y=6, slider=13, rotate=11, buttons=2,7,10,14; the code assumes a 10K pull-down from the analog axes to ground 27 | -------------------------------------------------------------------------------- /examples/ControllerTest/ControllerTest.ino: -------------------------------------------------------------------------------- 1 | #include "GameControllers.h" 2 | 3 | GamePortController controller(PA0,PA1,PA2,PA3,PA4,PA5,PA6,PA7); 4 | 5 | void setup() { 6 | Serial.begin(); 7 | controller.begin(); 8 | } 9 | 10 | void loop() { 11 | GameControllerData_t data; 12 | Serial.println(String(analogRead(PA3))); 13 | if (controller.read(&data)) { 14 | Serial.println(String(data.joystickX)+" "+String(data.joystickY)+ 15 | +" "+String(data.shoulderRight)+" "+String(data.cX)+" "+ 16 | String(data.buttons,HEX)); 17 | } 18 | delay(200); 19 | } 20 | -------------------------------------------------------------------------------- /examples/GameportToUSB/GameportToUSB.ino: -------------------------------------------------------------------------------- 1 | #include "GameControllers.h" // https://github.com/arpruss/GameControllersSTM32 2 | #include "USBComposite.h" // https://github.com/arpruss/USBHID_stm32f1 3 | 4 | #define LED_BUILTIN PB12 // change to match your board ; PB12 for some black pills ; PC13 for blue/red pill 5 | 6 | // This requires the libmaple-based stm32f1 core: https://github.com/rogerclarkmelbourne/Arduino_STM32 7 | // and these libraries: https://github.com/arpruss/GameControllersSTM32 8 | // and these libraries: https://github.com/arpruss/USBHID_stm32f1 9 | // 10 | // Pinout for reading gameport analog values: 11 | // Gameport 3 (X1) A0 --10K-- ground (X) 12 | // Gameport 6 (Y1) A1 --10K-- ground (Y) 13 | // Gameport 13 (X2) A2 --10K-- ground (slider) 14 | // Gameport 11 (Y2) A3 --10K-- ground (Z rotate) 15 | // Gameport 1, 8, 9, 15 -- 3.3V 16 | // Gameport 4, 5, 12 -- GND 17 | // Gameport 2 (B1) -- A4 (button 1) 18 | // Gameport 7 (B2) -- A5 (button 2) 19 | // Gameport 10 (B3) -- A6 (button 3) 20 | // Gameport 14 (B4) -- A7 (button 4) 21 | 22 | GamePortController controller(PA0,PA1,PA2,PA3,PA4,PA5,PA6,PA7); 23 | USBHID HID; 24 | HIDJoystick Joystick(HID); 25 | 26 | void setup() 27 | { 28 | pinMode(LED_BUILTIN, OUTPUT); 29 | USBComposite.setProductId(0xE004); 30 | USBComposite.setProductString("MapleGameportToUSB"); 31 | HID.begin(HID_JOYSTICK); 32 | // USBHID.registerComponent(); 33 | // USBComposite.begin(); 34 | digitalWrite(LED_BUILTIN, 1); 35 | controller.begin(); 36 | controller.setSamples(4); 37 | adc_set_sample_rate(ADC1, ADC_SMPR_13_5); // ADC_SMPR_13_5, ADC_SMPR_1_5 38 | Joystick.setManualReportMode(true); 39 | } 40 | 41 | void loop() 42 | { 43 | GameControllerData_t data; 44 | if (controller.read(&data)) { 45 | Joystick.X(data.joystickX); 46 | Joystick.Y(data.joystickY); 47 | Joystick.Xrotate(data.cX); 48 | Joystick.sliderRight(1023-data.shoulderRight); 49 | uint8_t mask = 1; 50 | for (int i=1; i<=8; i++, mask <<= 1) 51 | Joystick.button(i, (data.buttons & mask) != 0); 52 | Joystick.send(); 53 | digitalWrite(LED_BUILTIN, data.buttons == 0); 54 | } 55 | 56 | delay(10); 57 | } 58 | 59 | -------------------------------------------------------------------------------- /examples/GameportToUSB/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 arpruss 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/GameportToUSB/README.md: -------------------------------------------------------------------------------- 1 | # GameportToUSB 2 | STM32F1 Gamecube to USB adapter 3 | -------------------------------------------------------------------------------- /examples/GenesisToXBox360/GenesisToXBox360.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // NB: connect A10 to TX, A9 to RX 5 | 6 | GenesisController gen(PA0, PA1, PA2, PA3, PA4, PA5, PA6); 7 | USBXBox360 XBox360; 8 | GameControllerData_t data; 9 | 10 | #define XBOX_A 13 11 | #define XBOX_B 14 12 | #define XBOX_X 15 13 | #define XBOX_Y 16 14 | #define XBOX_DUP 1 15 | #define XBOX_DDOWN 2 16 | #define XBOX_DLEFT 3 17 | #define XBOX_DRIGHT 4 18 | #define XBOX_START 5 19 | #define XBOX_LSHOULDER 9 20 | #define XBOX_RSHOULDER 10 21 | #define XBOX_GUIDE 11 22 | 23 | const uint16_t remap_retroarch[16] = { 24 | XBOX_B, // A 25 | XBOX_A, // B 26 | XBOX_X, // C 27 | XBOX_LSHOULDER, // X 28 | 29 | XBOX_Y, // Y 30 | XBOX_RSHOULDER, // Z 31 | XBOX_START, // START 32 | XBOX_GUIDE, // MODE 33 | 34 | XBOX_DLEFT, // LEFT 35 | XBOX_DRIGHT, // RIGHT 36 | XBOX_DDOWN, // DOWN 37 | XBOX_DUP, // UP 38 | 39 | 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF 40 | }; 41 | 42 | const uint16_t* remap = remap_retroarch; 43 | 44 | inline int16_t range10u16s(uint16_t x) { 45 | return (((int32_t)(uint32_t)x - 512) * 32767 + 255) / 512; 46 | } 47 | 48 | void setup() { 49 | gen.begin(); 50 | USBComposite.setProductString("GenesisToXBox360"); 51 | XBox360.begin(); 52 | XBox360.setManualReportMode(true); 53 | } 54 | 55 | void loop() { 56 | if (gen.read(&data)) { 57 | XBox360.X(range10u16s(data.joystickX)); 58 | XBox360.Y(-range10u16s(data.joystickY)); 59 | uint16_t mask = 1; 60 | for (int i = 0; i < 16; i++, mask <<= 1) { 61 | uint16_t xb = remap[i]; 62 | if (xb != 0xFFFF) 63 | XBox360.button(xb, 0 != (data.buttons & mask)); 64 | } 65 | XBox360.send(); 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For GameCubeControllersSTM32 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | GameController KEYWORD1 10 | GameCubeController KEYWORD1 11 | NunchuckController KEYWORD1 12 | GamePortController KEYWORD1 13 | 14 | ####################################### 15 | # Methods and Functions (KEYWORD2) 16 | ####################################### 17 | 18 | read KEYWORD2 19 | readWithRumble KEYWORD2 20 | begin KEYWORD2 21 | 22 | ####################################### 23 | # Constants (LITERAL1) 24 | ####################################### 25 | 26 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=GameControllersSTM32 2 | version=0.0.8 3 | author=Alexander Pruss 4 | maintainer=arpruss 5 | sentence=Support Nunchuck, GameCube controller and Game Port joysticks on STM32F1 6 | paragraph=Support Nunchuck, GameCube controller and Game Port joysticks on STM32F1 7 | category=Sensors 8 | url=https://github.com/arpruss/GameControllersSTM32 9 | architectures=STM32F1,STM32 10 | -------------------------------------------------------------------------------- /src/GameControllers.h: -------------------------------------------------------------------------------- 1 | #ifndef _NINTENDO_CONTROLLER_H 2 | #define _NINTENDO_CONTROLLER_H 3 | 4 | #include 5 | #include "debouncer.h" 6 | #include "SegaController.h" 7 | #include 8 | #include 9 | 10 | #define CONTROLLER_NONE 0 11 | #define CONTROLLER_GAMECUBE 1 12 | #define CONTROLLER_NUNCHUCK 2 13 | #define CONTROLLER_GAMEPORT 3 14 | 15 | #define INPUT_NOT_IMPLEMENTED ((unsigned)-1) 16 | 17 | typedef struct { 18 | gpio_dev* device; 19 | uint32_t mask; 20 | uint32_t pinNumber; 21 | } PortData; 22 | 23 | const uint16_t gcmaskA = 0x01; 24 | const uint16_t gcmaskB = 0x02; 25 | const uint16_t gcmaskX = 0x04; 26 | const uint16_t gcmaskY = 0x08; 27 | const uint16_t gcmaskDLeft = 0x100; 28 | const uint16_t gcmaskDRight = 0x200; 29 | const uint16_t gcmaskDDown = 0x400; 30 | const uint16_t gcmaskDUp = 0x800; 31 | const uint16_t gcmaskZ = 0x1000; 32 | const uint16_t gcmaskR = 0x2000; 33 | const uint16_t gcmaskL = 0x4000; 34 | 35 | const uint16_t gcmaskDPad = 0xF00; 36 | const uint16_t gcmaskStart = 0x10; 37 | 38 | const uint16_t segaMaskA = 1; 39 | const uint16_t segaMaskB = 2; 40 | const uint16_t segaMaskC = 4; 41 | const uint16_t segaMaskX = 8; 42 | const uint16_t segaMaskY = 16; 43 | const uint16_t segaMaskZ = 32; 44 | const uint16_t segaMaskStart = 64; 45 | const uint16_t segaMaskMode = 128; 46 | const uint16_t segaMaskLeft = gcmaskDLeft; 47 | const uint16_t segaMaskRight = gcmaskDRight; 48 | const uint16_t segaMaskDown = gcmaskDDown; 49 | const uint16_t segaMaskUp = gcmaskDUp; 50 | 51 | typedef struct { 52 | uint16_t buttons; 53 | uint8_t joystickX; 54 | uint8_t joystickY; 55 | uint8_t cX; 56 | uint8_t cY; 57 | uint8_t shoulderLeft; 58 | uint8_t shoulderRight; 59 | } GameCubeData_t; 60 | 61 | typedef struct { 62 | uint8_t device; 63 | uint16_t buttons; 64 | uint16_t joystickX; // 10 bit range for analog values 65 | uint16_t joystickY; 66 | uint16_t cX; 67 | uint16_t cY; 68 | uint16_t shoulderLeft; 69 | uint16_t shoulderRight; 70 | } GameControllerData_t; 71 | 72 | class GameController { 73 | protected: 74 | bool dpadToJoystick = true; 75 | void setPortData(PortData *p, unsigned pin) { 76 | if (pin == INPUT_NOT_IMPLEMENTED) { 77 | p->device = NULL; 78 | } 79 | else { 80 | p->device = digitalPinToPort(pin); 81 | p->mask = digitalPinToBitMask(pin); 82 | p->pinNumber = PIN_MAP[pin].gpio_bit; 83 | } 84 | } 85 | public: 86 | bool read(GameControllerData_t* data) { 87 | return false; 88 | } 89 | bool begin(void) { 90 | return true; 91 | } 92 | void setDPadToJoystick(bool value) { 93 | dpadToJoystick = value; 94 | } 95 | bool getDPadToJoystick() { 96 | return dpadToJoystick; 97 | } 98 | }; 99 | 100 | class NunchuckControllerBase : public GameController { 101 | private: 102 | void (*usDelay)(uint32_t us); 103 | static uint16_t rescale(uint8_t value); // 8 to 10 bit 104 | uint8_t sendBytes(uint8_t location, uint8_t value); 105 | const uint8_t i2cAddress = 0x52; 106 | uint8_t buffer[6]; 107 | WireBase* wirebase; 108 | 109 | public: 110 | bool initNunchuck(void); // wire must be initialized first 111 | bool read(GameControllerData_t* data); 112 | NunchuckControllerBase(WireBase* _wirebase) { 113 | wirebase = _wirebase; 114 | } 115 | }; 116 | 117 | class NunchuckController : public NunchuckControllerBase { 118 | private: 119 | TwoWire wire; 120 | 121 | public: 122 | bool begin(void) { 123 | wire.begin(); 124 | return initNunchuck(); 125 | } 126 | 127 | NunchuckController(unsigned port=1, unsigned mode=0) : 128 | wire(port, mode), NunchuckControllerBase(&wire) {}; 129 | }; 130 | 131 | class NunchuckController_SoftWire : public NunchuckControllerBase { 132 | private: 133 | SoftWire wire; 134 | 135 | public: 136 | bool begin(void) { 137 | wire.begin(); 138 | return initNunchuck(); 139 | } 140 | 141 | NunchuckController_SoftWire(unsigned scl=PB6, unsigned sda=PB7, unsigned speed=SOFT_STANDARD) : 142 | wire(scl, sda, speed), NunchuckControllerBase(&wire) {}; 143 | }; 144 | 145 | class GamePortController : public GameController { 146 | private: 147 | Debouncer* debouncers[4]; 148 | unsigned buttonPins[4]; 149 | uint32_t axisResistor = 10000; 150 | int16_t getValue(unsigned pin); 151 | unsigned xPin; 152 | unsigned yPin; 153 | unsigned sliderPin; 154 | unsigned rzPin; 155 | bool haveAxes; 156 | unsigned samples=4; 157 | public: 158 | bool begin(void); 159 | bool read(GameControllerData_t* data); 160 | GamePortController(unsigned axis1, unsigned axis2, unsigned axis3, unsigned axis4, 161 | unsigned button1, unsigned button2, unsigned button3, unsigned button4); 162 | void setSamples(unsigned n) { 163 | samples = n; 164 | } 165 | void setAxisResistors(uint32_t ohms) { 166 | axisResistor = ohms; 167 | } 168 | }; 169 | 170 | class GameCubeController : public GameController { 171 | private: 172 | PortData port; 173 | unsigned fails; 174 | GameCubeData_t gcData; 175 | static inline uint16_t rescale(uint8_t value) { 176 | int32_t v = 512+((int32)value-128)*(1023*9)/(8*255); 177 | if (v<0) 178 | return 0; 179 | else if (v>1023) 180 | return 1023; 181 | else 182 | return v; 183 | } 184 | static inline uint16_t rescaleReversed(uint8_t value) { 185 | int32_t v = 512-((int32)value-128)*(1023*9)/(8*255); 186 | if (v<0) 187 | return 0; 188 | else if (v>1023) 189 | return 1023; 190 | else 191 | return v; 192 | } 193 | public: 194 | void sendBits(uint32_t data, uint8_t bits); 195 | bool receiveBits(void* data0, uint32_t bits); 196 | bool begin(void); 197 | bool read(GameControllerData_t* data); 198 | bool readWithRumble(GameControllerData_t* data, bool rumble); 199 | bool readWithRumble(GameCubeData_t* data, bool rumble); 200 | GameCubeController(unsigned pin); 201 | }; 202 | 203 | #define NUM_GENESIS_PINS 7 204 | enum GenesisPins { GENESIS_PIN_1 = 0, GENESIS_PIN_2, GENESIS_PIN_3, GENESIS_PIN_4, GENESIS_PIN_5, GENESIS_PIN_6, GENESIS_PIN_7, GENESIS_PIN_9 }; 205 | #define GENESIS_PIN_SELECT = GENESIS_PIN_7; 206 | 207 | class GenesisController : public GameController { 208 | private: 209 | SegaController* sega; 210 | public: 211 | bool begin(void); 212 | bool read(GameControllerData_t* data); 213 | GenesisController(unsigned pin1, unsigned pin2, unsigned pin3, unsigned pin4, unsigned pin6, unsigned pin7, unsigned pin9); 214 | }; 215 | 216 | #endif // _NINTENDO_CONTROLLER_H 217 | -------------------------------------------------------------------------------- /src/SegaController.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SegaController.cpp 3 | // 4 | // Author: 5 | // Jon Thysell 6 | // 7 | // Copyright (c) 2017 Jon Thysell 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #include "Arduino.h" 28 | #include "SegaController.h" 29 | 30 | SegaController::SegaController(unsigned db9_pin_7, unsigned db9_pin_1, unsigned db9_pin_2, unsigned db9_pin_3, unsigned db9_pin_4, unsigned db9_pin_6, unsigned db9_pin_9) 31 | { 32 | // Set pins 33 | _selectPin = db9_pin_7; 34 | 35 | _inputPins[0] = db9_pin_1; 36 | _inputPins[1] = db9_pin_2; 37 | _inputPins[2] = db9_pin_3; 38 | _inputPins[3] = db9_pin_4; 39 | _inputPins[4] = db9_pin_6; 40 | _inputPins[5] = db9_pin_9; 41 | 42 | // Setup output pin 43 | pinMode(_selectPin, OUTPUT); 44 | digitalWrite(_selectPin, HIGH); 45 | 46 | // Setup input pins 47 | for (byte i = 0; i < SC_INPUT_PINS; i++) 48 | { 49 | pinMode(_inputPins[i], INPUT_PULLUP); 50 | } 51 | 52 | _currentState = 0; 53 | _sixButtonMode = false; 54 | _lastReadTime = millis(); 55 | } 56 | 57 | word SegaController::getState() 58 | { 59 | if (millis()-_lastReadTime < SC_READ_DELAY_MS) 60 | { 61 | // Not enough time has elapsed, return previously read state 62 | return _currentState; 63 | } 64 | 65 | noInterrupts(); 66 | 67 | // Clear current state 68 | _currentState = 0; 69 | 70 | for (byte cycle = 0; cycle < SC_CYCLES; cycle++) 71 | { 72 | readCycle(cycle); 73 | } 74 | 75 | // When a controller disconnects, revert to three-button polling 76 | if (!(_currentState & SC_CTL_ON)) 77 | { 78 | _sixButtonMode = false; 79 | } 80 | 81 | interrupts(); 82 | 83 | _lastReadTime = millis(); 84 | 85 | return _currentState; 86 | } 87 | 88 | void SegaController::readCycle(byte cycle) 89 | { 90 | // Set the select pin low/high 91 | digitalWrite(_selectPin, cycle % 2); 92 | 93 | delayMicroseconds(50); 94 | 95 | // Read flags 96 | switch (cycle) 97 | { 98 | case 2: 99 | // Check that a controller is connected 100 | _currentState |= (digitalRead(_inputPins[2]) == LOW && digitalRead(_inputPins[3]) == LOW) * SC_CTL_ON; 101 | 102 | // Check controller is connected before reading A/Start to prevent bad reads when inserting/removing cable 103 | if (_currentState & SC_CTL_ON) 104 | { 105 | // Read input pins for A, Start 106 | if (digitalRead(_inputPins[4]) == LOW) { _currentState |= SC_BTN_A; } 107 | if (digitalRead(_inputPins[5]) == LOW) { _currentState |= SC_BTN_START; } 108 | } 109 | break; 110 | case 3: 111 | // Read input pins for Up, Down, Left, Right, B, C 112 | if (digitalRead(_inputPins[0]) == LOW) { _currentState |= SC_BTN_UP; } 113 | if (digitalRead(_inputPins[1]) == LOW) { _currentState |= SC_BTN_DOWN; } 114 | if (digitalRead(_inputPins[2]) == LOW) { _currentState |= SC_BTN_LEFT; } 115 | if (digitalRead(_inputPins[3]) == LOW) { _currentState |= SC_BTN_RIGHT; } 116 | if (digitalRead(_inputPins[4]) == LOW) { _currentState |= SC_BTN_B; } 117 | if (digitalRead(_inputPins[5]) == LOW) { _currentState |= SC_BTN_C; } 118 | break; 119 | case 4: 120 | _sixButtonMode = (digitalRead(_inputPins[0]) == LOW && digitalRead(_inputPins[1]) == LOW); 121 | break; 122 | case 5: 123 | if (_sixButtonMode) 124 | { 125 | // Read input pins for X, Y, Z, Mode 126 | if (digitalRead(_inputPins[0]) == LOW) { _currentState |= SC_BTN_Z; } 127 | if (digitalRead(_inputPins[1]) == LOW) { _currentState |= SC_BTN_Y; } 128 | if (digitalRead(_inputPins[2]) == LOW) { _currentState |= SC_BTN_X; } 129 | if (digitalRead(_inputPins[3]) == LOW) { _currentState |= SC_BTN_MODE; } 130 | } 131 | break; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/SegaController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SegaController.h 3 | // 4 | // Author: 5 | // Jon Thysell 6 | // 7 | // Copyright (c) 2017 Jon Thysell 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #ifndef SegaController_h 28 | #define SegaController_h 29 | 30 | enum 31 | { 32 | SC_CTL_ON = 1, // The controller is connected 33 | SC_BTN_UP = 2, 34 | SC_BTN_DOWN = 4, 35 | SC_BTN_LEFT = 8, 36 | SC_BTN_RIGHT = 16, 37 | SC_BTN_START = 32, 38 | SC_BTN_A = 64, 39 | SC_BTN_B = 128, 40 | SC_BTN_C = 256, 41 | SC_BTN_X = 512, 42 | SC_BTN_Y = 1024, 43 | SC_BTN_Z = 2048, 44 | SC_BTN_MODE = 4096, 45 | SC_BTN_1 = 128, // Master System compatibility 46 | SC_BTN_2 = 256 // Master System compatibility 47 | }; 48 | 49 | const byte SC_INPUT_PINS = 6; 50 | 51 | const byte SC_CYCLES = 8; 52 | 53 | const unsigned long SC_READ_DELAY_MS = 5; // Must be >= 3 to give 6-button controller time to reset 54 | 55 | class SegaController { 56 | public: 57 | SegaController(unsigned db9_pin_7, unsigned db9_pin_1, unsigned db9_pin_2, unsigned db9_pin_3, unsigned db9_pin_4, unsigned db9_pin_6, unsigned db9_pin_9); 58 | 59 | word getState(); 60 | 61 | private: 62 | void readCycle(byte cycle); 63 | 64 | word _currentState; 65 | 66 | unsigned long _lastReadTime; 67 | 68 | boolean _sixButtonMode; 69 | 70 | unsigned _selectPin; // output select pin 71 | unsigned _inputPins[SC_INPUT_PINS]; 72 | }; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/debouncer.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEBOUNCER_H 2 | #define _DEBOUNCER_H 3 | 4 | #define STM32_SPECIFIC 5 | 6 | class Debouncer { 7 | protected: 8 | uint32_t lastToggleTime; 9 | uint8_t activeValue; 10 | bool curState; 11 | bool highState; 12 | bool lowState; 13 | uint32_t debounceTime; 14 | bool releaseCanceled = false; 15 | #ifdef STM32_SPECIFIC 16 | volatile uint32_t* port; 17 | uint32_t mask; 18 | #endif 19 | int pin; 20 | 21 | Debouncer() {} 22 | 23 | public: 24 | virtual inline bool getRawState(void) { 25 | #ifdef STM32_SPECIFIC 26 | if (*port & mask) 27 | return highState; 28 | else 29 | return lowState; 30 | #else 31 | return activeValue==digitalRead(pin); 32 | #endif 33 | } 34 | 35 | private: 36 | bool update(void) { 37 | bool v = getRawState(); 38 | uint32_t t = millis(); 39 | if (v != curState && t - lastToggleTime >= debounceTime) { 40 | lastToggleTime = t; 41 | curState = v; 42 | return true; 43 | } 44 | return false; 45 | } 46 | 47 | public: 48 | Debouncer(int p, uint8_t active=HIGH, uint32_t time=20) { 49 | activeValue = active; 50 | debounceTime = time; 51 | highState = active==HIGH; 52 | lowState = active==LOW; 53 | pin = p; 54 | #ifdef STM32_SPECIFIC 55 | port = &(PIN_MAP[p].gpio_device->regs->IDR); 56 | mask = 1u << PIN_MAP[p].gpio_bit; 57 | #endif 58 | } 59 | 60 | void begin(void) { 61 | curState = getRawState(); 62 | lastToggleTime = millis(); 63 | } 64 | 65 | // Code using Debounce should choose exactly one of three polling methods: 66 | // wasToggled(), getState() and wasPressed() 67 | // and should not switch between them, with the exception that a single initial call to 68 | // getState() is acceptable. 69 | 70 | // for toggle monitoring 71 | inline bool wasToggled(void) { 72 | return update(); 73 | } 74 | 75 | // for state monitoring 76 | bool getState(void) { 77 | update(); 78 | return curState; 79 | } 80 | 81 | // for press monitoring 82 | bool wasPressed(void) { 83 | return wasToggled() && curState; // curState value is a side-effect of wasToggled() 84 | } 85 | 86 | void cancelRelease(void) { 87 | releaseCanceled = true; 88 | } 89 | 90 | // for release monitoring 91 | bool wasReleased(void) { 92 | if (wasToggled() && ! curState) { // curState value is a side-effect of wasToggled() 93 | if (releaseCanceled) { 94 | releaseCanceled = false; 95 | return false; 96 | } 97 | else { 98 | return true; 99 | } 100 | } 101 | return false; 102 | } 103 | }; 104 | 105 | class DebouncerAnalog : public Debouncer { 106 | private: 107 | uint16_t threshold; 108 | 109 | public: 110 | bool getRawState(void) { 111 | uint16_t value; 112 | // TODO: go analog 113 | if (analogRead(pin) >= threshold) 114 | return highState; 115 | else 116 | return lowState; 117 | } 118 | 119 | DebouncerAnalog(int p, uint8_t active=HIGH, uint16_t threshold=512, uint32_t time=20) : Debouncer() { 120 | activeValue = active; 121 | debounceTime = time; 122 | highState = active==HIGH; 123 | lowState = active==LOW; 124 | pin = p; 125 | } 126 | }; 127 | #endif 128 | 129 | -------------------------------------------------------------------------------- /src/dwt.h: -------------------------------------------------------------------------------- 1 | // ...header taken from stuff floating around the net... 2 | 3 | #ifndef DWT_BASE 4 | 5 | #define SystemCoreClock F_CPU 6 | 7 | #include "Arduino.h" 8 | 9 | #define __I volatile const /* read only */ 10 | #define __IO volatile /* read write */ 11 | #define __O volatile /* write */ 12 | 13 | typedef struct 14 | { 15 | __IO uint32_t CTRL; /* Offset: 0x000 (R/W) Control Register */ 16 | __IO uint32_t CYCCNT; /* Offset: 0x004 (R/W) Cycle Count Register */ 17 | __IO uint32_t CPICNT; /* Offset: 0x008 (R/W) CPI Count Register */ 18 | __IO uint32_t EXCCNT; /* Offset: 0x00C (R/W) Exception Overhead Count Register */ 19 | __IO uint32_t SLEEPCNT; /* Offset: 0x010 (R/W) Sleep Count Register */ 20 | __IO uint32_t LSUCNT; /* Offset: 0x014 (R/W) LSU Count Register */ 21 | __IO uint32_t FOLDCNT; /* Offset: 0x018 (R/W) Folded-instruction Count Register */ 22 | __I uint32_t PCSR; /* Offset: 0x01C (R/ ) Program Counter Sample Register */ 23 | __IO uint32_t COMP0; /* Offset: 0x020 (R/W) Comparator Register 0 */ 24 | __IO uint32_t MASK0; /* Offset: 0x024 (R/W) Mask Register 0 */ 25 | __IO uint32_t FUNCTION0; /* Offset: 0x028 (R/W) Function Register 0 */ 26 | uint32_t RESERVED0[1]; 27 | __IO uint32_t COMP1; /* Offset: 0x030 (R/W) Comparator Register 1 */ 28 | __IO uint32_t MASK1; /* Offset: 0x034 (R/W) Mask Register 1 */ 29 | __IO uint32_t FUNCTION1; /* Offset: 0x038 (R/W) Function Register 1 */ 30 | uint32_t RESERVED1[1]; 31 | __IO uint32_t COMP2; /* Offset: 0x040 (R/W) Comparator Register 2 */ 32 | __IO uint32_t MASK2; /* Offset: 0x044 (R/W) Mask Register 2 */ 33 | __IO uint32_t FUNCTION2; /* Offset: 0x048 (R/W) Function Register 2 */ 34 | uint32_t RESERVED2[1]; 35 | __IO uint32_t COMP3; /* Offset: 0x050 (R/W) Comparator Register 3 */ 36 | __IO uint32_t MASK3; /* Offset: 0x054 (R/W) Mask Register 3 */ 37 | __IO uint32_t FUNCTION3; /* Offset: 0x058 (R/W) Function Register 3 */ 38 | } DWT_Type; 39 | 40 | typedef struct 41 | { 42 | __IO uint32_t DHCSR; /* Offset: 0x000 (R/W) Debug Halting Control and Status Register */ 43 | __O uint32_t DCRSR; /* Offset: 0x004 ( /W) Debug Core Register Selector Register */ 44 | __IO uint32_t DCRDR; /* Offset: 0x008 (R/W) Debug Core Register Data Register */ 45 | __IO uint32_t DEMCR; /* Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ 46 | } CoreDebug_Type; 47 | 48 | #define CoreDebug_DEMCR_TRCENA_Pos 24 /* CoreDebug DEMCR: TRCENA Position */ 49 | #define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /* CoreDebug DEMCR: TRCENA Mask */ 50 | 51 | #define DWT_BASE (0xE0001000UL) 52 | #define CoreDebug_BASE (0xE000EDF0UL) 53 | 54 | #define DWT ((volatile DWT_Type * const ) DWT_BASE ) 55 | #define CoreDebug ((volatile CoreDebug_Type * const) CoreDebug_BASE) 56 | #endif 57 | -------------------------------------------------------------------------------- /src/gamecube.cpp: -------------------------------------------------------------------------------- 1 | #include "GameControllers.h" 2 | #include "dwt.h" 3 | 4 | #define FLEXIBLE_TIMING // work with controllers that don't have 4us bit timing 5 | 6 | static const uint8_t maxFails = 4; 7 | static const uint32_t cyclesPerUS = (SystemCoreClock / 1000000ul); 8 | static const uint32_t quarterBitSendingCycles = cyclesPerUS * 5 / 4; 9 | #ifndef FLEXIBLE_TIMING 10 | //static const uint32_t bitReceiveCycles = cyclesPerUS * 4; 11 | static const uint32_t halfBitReceiveCycles = cyclesPerUS * 2; 12 | #endif 13 | static const uint32_t halfBitTimeoutCycles = cyclesPerUS * 6; 14 | 15 | // Timeout for controller response: 16 | // http://www.int03.co.uk/crema/hardware/gamecube/gc-control.html measured no delay or 15 us 17 | // 8us works for a knockoff controller I have 18 | // 16us works for Konami DDR pad; 15us works half the time 19 | static const uint32_t responseTimeoutCycles = 32*cyclesPerUS; // cyclesPerUS * 100; 20 | 21 | GameCubeController::GameCubeController(unsigned p) { 22 | setPortData(&port, p); 23 | } 24 | 25 | bool GameCubeController::begin() { 26 | gpio_set_mode(port.device, port.pinNumber, GPIO_OUTPUT_OD); // set open drain output 27 | CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; 28 | DWT->CTRL |= 1; 29 | fails = maxFails; // force update 30 | return true; 31 | } 32 | 33 | // at most 32 bits can be sent 34 | void GameCubeController::sendBits(uint32_t data, uint8_t bits) { 35 | data <<= 32 - bits; 36 | DWT->CYCCNT = 0; 37 | uint32_t timerEnd = DWT->CYCCNT; 38 | do { 39 | gpio_write_bit(port.device, port.pinNumber, 0); 40 | if (0x80000000ul & data) 41 | timerEnd += quarterBitSendingCycles; 42 | else 43 | timerEnd += 3 * quarterBitSendingCycles; 44 | while (DWT->CYCCNT < timerEnd) ; 45 | gpio_write_bit(port.device, port.pinNumber, 1); 46 | if (0x80000000ul & data) 47 | timerEnd += 3 * quarterBitSendingCycles; 48 | else 49 | timerEnd += quarterBitSendingCycles; 50 | data <<= 1; 51 | bits--; 52 | while (DWT->CYCCNT < timerEnd) ; 53 | } while (bits); 54 | DWT->CYCCNT = 0; 55 | gpio_write_bit(port.device, port.pinNumber, 0); 56 | while (DWT->CYCCNT < quarterBitSendingCycles) ; 57 | DWT->CYCCNT = 0; 58 | gpio_write_bit(port.device, port.pinNumber, 1); 59 | while (DWT->CYCCNT < 2*quarterBitSendingCycles) ; 60 | } 61 | 62 | 63 | #ifdef FLEXIBLE_TIMING 64 | // This version should work with controllers whose timing is off, like some Hori pads ( https://www.raphnet.net/electronique/gc_n64_usb/index_en.php ) 65 | 66 | // bits must be greater than 0 67 | bool GameCubeController::receiveBits(void* data0, uint32_t bits) { 68 | uint8_t* data = (uint8_t*)data0; 69 | uint8_t bitmap = 0x80; 70 | uint32_t lowTime; 71 | uint32_t highTime; 72 | 73 | // wait for start of transmission (low) 74 | DWT->CYCCNT = 0; 75 | while (gpio_read_bit(port.device, port.pinNumber)) { 76 | if (DWT->CYCCNT >= responseTimeoutCycles) 77 | return false; 78 | } 79 | 80 | DWT->CYCCNT = 0; // for measuring low time 81 | 82 | *data = 0; 83 | do { 84 | while (!gpio_read_bit(port.device, port.pinNumber)) { 85 | if (DWT->CYCCNT >= halfBitTimeoutCycles) 86 | return false; 87 | } 88 | lowTime = DWT->CYCCNT; 89 | 90 | DWT->CYCCNT = 0; 91 | while (gpio_read_bit(port.device, port.pinNumber)) { 92 | if (DWT->CYCCNT >= halfBitTimeoutCycles) 93 | return false; 94 | } 95 | highTime = DWT->CYCCNT; 96 | DWT->CYCCNT = 0; // for measuring low time of next bit 97 | 98 | if (highTime > lowTime) { 99 | *data |= bitmap; 100 | } 101 | 102 | bitmap >>= 1; 103 | bits--; 104 | if (bitmap == 0) { 105 | data++; 106 | bitmap = 0x80; 107 | if (bits) 108 | *data = 0; 109 | } 110 | } while (bits); 111 | 112 | // wait for end of stop bit, just in case 113 | while (!gpio_read_bit(port.device, port.pinNumber)) { 114 | if (DWT->CYCCNT >= halfBitTimeoutCycles) 115 | return false; 116 | } 117 | 118 | return true; 119 | } 120 | #else 121 | // bits must be greater than 0 122 | bool GameCubeController::receiveBits(void* data0, uint32_t bits) { 123 | uint8_t* data = (uint8_t*)data0; 124 | uint8_t bitmap = 0x80; 125 | 126 | // wait for start of transmission (low) 127 | DWT->CYCCNT = 0; 128 | while (gpio_read_bit(port.device, port.pinNumber)) { 129 | if (DWT->CYCCNT >= responseTimeoutCycles) 130 | return false; 131 | } 132 | 133 | *data = 0; 134 | do { 135 | 136 | DWT->CYCCNT = 0; 137 | while (DWT->CYCCNT < halfBitReceiveCycles - 2) ; 138 | 139 | if (gpio_read_bit(port.device, port.pinNumber)) { 140 | *data |= bitmap; 141 | } 142 | bitmap >>= 1; 143 | bits--; 144 | if (bitmap == 0) { 145 | data++; 146 | bitmap = 0x80; 147 | if (bits) 148 | *data = 0; 149 | } 150 | 151 | // wait for high part of bit if necessary 152 | DWT->CYCCNT = 0; 153 | while (!gpio_read_bit(port.device, port.pinNumber)) { 154 | if (DWT->CYCCNT >= halfBitTimeoutCycles) 155 | return false; 156 | } 157 | // wait for start of next bit (or the stop bit at the end) 158 | DWT->CYCCNT = 0; 159 | while (gpio_read_bit(port.device, port.pinNumber)) { 160 | if (DWT->CYCCNT >= halfBitTimeoutCycles) 161 | return false; 162 | } 163 | } while (bits); 164 | 165 | // wait for end of stop bit, just in case 166 | DWT->CYCCNT = 0; 167 | while (!gpio_read_bit(port.device, port.pinNumber)) { 168 | if (DWT->CYCCNT >= halfBitTimeoutCycles) 169 | return false; 170 | } 171 | 172 | return true; 173 | } 174 | #endif 175 | 176 | bool GameCubeController::readWithRumble(GameCubeData_t* data, bool rumble) { 177 | if (fails >= maxFails) { 178 | nvic_globalirq_disable(); 179 | sendBits(0b00000000l, 8); 180 | nvic_globalirq_enable(); 181 | delayMicroseconds(410); 182 | fails = 0; 183 | } 184 | nvic_globalirq_disable(); 185 | sendBits(rumble ? 0b010000000000001100000001l : 0b010000000000001100000000l, 24); 186 | bool success = receiveBits(data, 64); 187 | nvic_globalirq_enable(); 188 | return success; 189 | } 190 | 191 | bool GameCubeController::readWithRumble(GameControllerData_t* data, bool rumble) { 192 | bool success = readWithRumble(&gcData, rumble); 193 | if (success && 0 == (gcData.buttons & 0x80) /*&& (gcData.buttons & 0x8000)*/ ) { 194 | data->device = CONTROLLER_GAMECUBE; 195 | data->buttons = gcData.buttons; 196 | data->joystickX = rescale(gcData.joystickX); 197 | data->joystickY = rescaleReversed(gcData.joystickY); 198 | data->cX = rescale(gcData.cX); 199 | data->cY = rescaleReversed(gcData.cY); 200 | data->shoulderLeft = rescale(gcData.shoulderLeft); 201 | data->shoulderRight = rescale(gcData.shoulderRight); 202 | /* support Konami pads */ 203 | if (0 == (gcData.buttons & 0x20) && gcData.joystickX == 1 && gcData.joystickY == 1 && gcData.cX == 1 && gcData.cY == 1 && 204 | gcData.shoulderLeft == 1 && gcData.shoulderRight == 1 /*&& gcData.buttons*/ ) { 205 | data->joystickX = 512; 206 | data->joystickY = 512; 207 | 208 | if (dpadToJoystick) { 209 | if (gcData.buttons & gcmaskDLeft) { 210 | data->joystickX = 0; 211 | } 212 | else if (gcData.buttons & gcmaskDRight) { 213 | data->joystickX = 1023; 214 | } 215 | 216 | if (gcData.buttons & gcmaskDUp) { 217 | data->joystickY = 0; 218 | } 219 | else if (gcData.buttons & gcmaskDDown) { 220 | data->joystickY = 1023; 221 | } 222 | } 223 | } 224 | return true; 225 | } 226 | else { 227 | fails++; 228 | return false; 229 | } 230 | } 231 | 232 | bool GameCubeController::read(GameControllerData_t* data) { 233 | return readWithRumble(data, false); 234 | } 235 | -------------------------------------------------------------------------------- /src/gameport.cpp: -------------------------------------------------------------------------------- 1 | #include "GameControllers.h" 2 | 3 | const uint32_t R1max = 100000; 4 | const uint32_t maximumAnalogValue = 4095; 5 | const uint32_t minimumAnalogValue = 4095 * 10 / 110; 6 | 7 | GamePortController::GamePortController(unsigned axis1, unsigned axis2, unsigned axis3, unsigned axis4, 8 | unsigned button1, unsigned button2, unsigned button3, unsigned button4) { 9 | xPin = axis1; 10 | yPin = axis2; 11 | sliderPin = axis3; 12 | rzPin = axis4; 13 | haveAxes = axis1 != INPUT_NOT_IMPLEMENTED || axis2 != INPUT_NOT_IMPLEMENTED || axis3 != INPUT_NOT_IMPLEMENTED || axis4 != INPUT_NOT_IMPLEMENTED; 14 | 15 | buttonPins[0]=button1; 16 | buttonPins[1]=button2; 17 | buttonPins[2]=button3; 18 | buttonPins[3]=button4; 19 | } 20 | 21 | int16_t GamePortController::getValue(unsigned pin) { 22 | if (pin == INPUT_NOT_IMPLEMENTED) 23 | return -1; 24 | uint32_t v = 0; 25 | for (unsigned i=0;i R1max) 37 | return 1023; 38 | else 39 | return R*1023/R1max; 40 | } 41 | } 42 | 43 | bool GamePortController::begin() { 44 | for (unsigned i=0; i<4; i++) 45 | if (INPUT_NOT_IMPLEMENTED != buttonPins[i]) { 46 | pinMode(buttonPins[i], INPUT_PULLUP); 47 | debouncers[i] = new Debouncer(buttonPins[i], LOW); 48 | } 49 | else { 50 | debouncers[i] = NULL; 51 | } 52 | 53 | if (xPin != INPUT_NOT_IMPLEMENTED) 54 | pinMode(xPin, INPUT_ANALOG); 55 | if (yPin != INPUT_NOT_IMPLEMENTED) 56 | pinMode(yPin, INPUT_ANALOG); 57 | if (sliderPin != INPUT_NOT_IMPLEMENTED) 58 | pinMode(sliderPin, INPUT_ANALOG); 59 | if (sliderPin != INPUT_NOT_IMPLEMENTED) 60 | pinMode(rzPin, INPUT_ANALOG); 61 | 62 | return true; 63 | } 64 | 65 | bool GamePortController::read(GameControllerData_t* data) { 66 | if (haveAxes) { 67 | int16_t x = getValue(xPin); 68 | int16_t y = getValue(yPin); 69 | int16_t s = getValue(sliderPin); 70 | int16_t rz = getValue(rzPin); 71 | if (x < 0 && y < 0 && s < 0 && rz < 0) 72 | return false; 73 | memset(data, 0, sizeof(GameControllerData_t)); 74 | data->device = CONTROLLER_GAMEPORT; 75 | data->joystickX = x >= 0 ? x : 512; 76 | data->joystickY = y >= 0 ? y : 512; 77 | data->cX = rz >= 0 ? rz : 512; 78 | data->cY = 512; 79 | data->shoulderRight = s >= 0 ? 1023-s : 0; 80 | } 81 | else { 82 | memset(data, 0, sizeof(GameControllerData_t)); 83 | } 84 | uint8_t mask = 1; 85 | for (unsigned i = 0; i < 4 ; i++, mask <<= 1) 86 | if (debouncers[i] != NULL && debouncers[i]->getState()) 87 | data->buttons |= mask; 88 | return true; 89 | } 90 | -------------------------------------------------------------------------------- /src/genesis.cpp: -------------------------------------------------------------------------------- 1 | #include "GameControllers.h" 2 | #include "dwt.h" 3 | 4 | static const uint32_t genesisReadDelayMS = 5+1; 5 | 6 | static const uint8_t maxFails = 4; 7 | static const uint32_t cyclesPerUS = (SystemCoreClock / 1000000ul); 8 | static const uint32_t quarterBitSendingCycles = cyclesPerUS * 5 / 4; 9 | static const uint32_t bitReceiveCycles = cyclesPerUS * 4; 10 | static const uint32_t halfBitReceiveCycles = cyclesPerUS * 2; 11 | 12 | GenesisController::GenesisController(unsigned pin1, unsigned pin2, unsigned pin3, unsigned pin4, unsigned pin6, unsigned pin7, unsigned pin9) { 13 | sega = new SegaController(pin7, pin1, pin2, pin3, pin4, pin6, pin9); 14 | } 15 | 16 | bool GenesisController::begin() { 17 | return true; 18 | } 19 | 20 | bool GenesisController::read(GameControllerData_t* data) { 21 | word state = sega->getState(); 22 | memset(data, 0, sizeof(*data)); 23 | data->joystickX = 512; 24 | data->joystickY = 512; 25 | data->cX = 512; 26 | data->cY = 512; 27 | if (state & SC_CTL_ON) { 28 | data->buttons = ( (state & SC_BTN_A) ? segaMaskA : 0 ) | 29 | ( (state & SC_BTN_B) ? segaMaskB : 0 ) | 30 | ( (state & SC_BTN_C) ? segaMaskC : 0 ) | 31 | ( (state & SC_BTN_X) ? segaMaskX : 0 ) | 32 | ( (state & SC_BTN_Y) ? segaMaskY : 0 ) | 33 | ( (state & SC_BTN_Z) ? segaMaskZ : 0 ) | 34 | ( (state & SC_BTN_MODE) ? segaMaskMode : 0 ) | 35 | ( (state & SC_BTN_START) ? segaMaskStart : 0); 36 | if (dpadToJoystick) { 37 | if (state & SC_BTN_LEFT) 38 | data->joystickX = 0; 39 | else if (state & SC_BTN_RIGHT) 40 | data->joystickX = 1023; 41 | 42 | if (state & SC_BTN_UP) 43 | data->joystickY = 0; 44 | else if (state & SC_BTN_DOWN) 45 | data->joystickY = 1023; 46 | } 47 | else { 48 | data->buttons |= ( (state & SC_BTN_UP) ? segaMaskUp : 0 ) | 49 | ( (state & SC_BTN_DOWN) ? segaMaskDown : 0 ) | 50 | ( (state & SC_BTN_LEFT) ? segaMaskLeft : 0 ) | 51 | ( (state & SC_BTN_RIGHT) ? segaMaskRight : 0 ); 52 | } 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/nunchuck.cpp: -------------------------------------------------------------------------------- 1 | #include "GameControllers.h" 2 | #include 3 | 4 | //#define MANUAL_DECRYPT 5 | 6 | static void delayMicrosecondsDouble(uint32_t us) { 7 | // some stm32f103c8t6 clones need doubling the delay! 8 | delayMicroseconds(2*us); 9 | } 10 | 11 | bool NunchuckControllerBase::initNunchuck() { 12 | uint32_t us = micros(); 13 | delayMicroseconds(10000); 14 | if (micros()-us < 7500) { 15 | usDelay = delayMicrosecondsDouble; 16 | } 17 | else { 18 | usDelay = delayMicroseconds; 19 | } 20 | usDelay(250); 21 | 22 | #ifdef MANUAL_DECRYPT 23 | if (!sendBytes(0x40,0x00)) { 24 | return false; 25 | } 26 | usDelay(250); 27 | #else 28 | if (! sendBytes(0xF0, 0x55)) { 29 | return false; 30 | } 31 | usDelay(250); 32 | if (! sendBytes(0xFB, 0x00)) 33 | return false; 34 | #endif 35 | usDelay(250); 36 | return true; 37 | } 38 | 39 | uint8_t NunchuckControllerBase::sendBytes(uint8_t location, uint8_t value) { 40 | wirebase->beginTransmission(i2cAddress); 41 | wirebase->write(location); 42 | wirebase->write(value); 43 | return 0 == wirebase->endTransmission(); 44 | } 45 | 46 | uint16_t NunchuckControllerBase::rescale(uint8_t x) { 47 | int32_t x1 = 512+((int32_t)x-128)*48/10; 48 | if (x1 < 0) 49 | return 0; 50 | else if (x1 > 1023) 51 | return 1023; 52 | else 53 | return x1; 54 | } 55 | 56 | bool NunchuckControllerBase::read(GameControllerData_t* data) { 57 | unsigned retries = 2; 58 | while (1) { 59 | wirebase->beginTransmission(i2cAddress); 60 | wirebase->write(0x00); 61 | if (0 == wirebase->endTransmission()) 62 | break; 63 | retries--; 64 | if (!retries) 65 | return false; 66 | usDelay(500); 67 | } 68 | 69 | usDelay(500); 70 | #ifdef SERIAL_DEBUG 71 | // Serial.println("Requested"); 72 | #endif 73 | 74 | wirebase->requestFrom(i2cAddress, 6); 75 | int count = 0; 76 | while (wirebase->available() && count<6) { 77 | #ifdef MANUAL_DECRYPT 78 | buffer[count++] = ((uint8_t)0x17^(uint8_t)wirebase->read()) + (uint8_t)0x17; 79 | #else 80 | buffer[count++] = wirebase->read(); 81 | #endif 82 | } 83 | if (count < 6) 84 | return 0; 85 | memset(data, 0, sizeof(GameControllerData_t)); 86 | data->joystickX = rescale(buffer[0]); 87 | data->joystickY = 1023-rescale(buffer[1]); 88 | data->buttons = 0; 89 | if (! (buffer[5] & 1) ) // Z 90 | data->buttons |= 1; 91 | if (! (buffer[5] & 2) ) // C 92 | data->buttons |= 2; 93 | data->cX = 512; 94 | data->cY = 512; 95 | data->shoulderLeft = 0; 96 | data->shoulderRight = 0; 97 | data->device = CONTROLLER_NUNCHUCK; 98 | return true; 99 | } 100 | --------------------------------------------------------------------------------