This is a work in progress. Trying to make a working arcade button to USB interface with the Nucleo F411RE.
main.cpp
00001 /* Modified for RetroPie MW 2016 */ 00002 /* Copyright 2014 M J Roberts, MIT License 00003 * 00004 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 00005 * and associated documentation files (the "Software"), to deal in the Software without 00006 * restriction, including without limitation the rights to use, copy, modify, merge, publish, 00007 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 00008 * Software is furnished to do so, subject to the following conditions: 00009 * 00010 * The above copyright notice and this permission notice shall be included in all copies or 00011 * substantial portions of the Software. 00012 * 00013 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 00014 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00015 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 00016 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00017 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00018 */ 00019 00020 // 00021 // - Button input wiring. 24 of the KL25Z's GPIO ports are mapped as digital inputs 00022 // for buttons and switches. The software reports these as joystick buttons when 00023 // it sends reports to the PC. These can be used to wire physical pinball-style 00024 // buttons in the cabinet (e.g., flipper buttons, the Start button) and miscellaneous 00025 // switches (such as a tilt bob) to the PC. Visual Pinball can use joystick buttons 00026 // for input - you just have to assign a VP function to each button using VP's 00027 // keyboard options dialog. To wire a button physically, connect one terminal of 00028 // the button switch to the KL25Z ground, and connect the other terminal to the 00029 // the GPIO port you wish to assign to the button. See the buttonMap[] array 00030 // below for the available GPIO ports and their assigned joystick button numbers. 00031 // If you're not using a GPIO port, you can just leave it unconnected - the digital 00032 // inputs have built-in pull-up resistors, so an unconnected port is the same as 00033 // an open switch (an "off" state for the button). 00034 // 00035 // 00036 // STATUS LIGHTS: The on-board LED on the KL25Z flashes to indicate the current 00037 // device status. The flash patterns are: 00038 // 00039 // two short red flashes = the device is powered but hasn't successfully 00040 // connected to the host via USB (either it's not physically connected 00041 // to the USB port, or there was a problem with the software handshake 00042 // with the USB device driver on the computer) 00043 // 00044 // short red flash = the host computer is in sleep/suspend mode 00045 // 00046 // long red/green = the LedWiz unti number has been changed, so a reset 00047 // is needed. You can simply unplug the device and plug it back in, 00048 // or presss and hold the reset button on the device for a few seconds. 00049 // 00050 // long yellow/green = everything's working, but the plunger hasn't 00051 // been calibrated; follow the calibration procedure described above. 00052 // This flash mode won't appear if the CCD has been disabled. Note 00053 // that the device can't tell whether a CCD is physically attached; 00054 // if you don't have a CCD attached, you can set the appropriate option 00055 // in config.h or use the Windows config tool to disable the CCD 00056 // software features. 00057 // 00058 // alternating blue/green = everything's working 00059 // 00060 #include "mbed.h" 00061 #include "math.h" 00062 #include "USBGamepad.h" 00063 00064 00065 #define DECL_EXTERNS 00066 #include "config.h" 00067 00068 00069 // --------------------------------------------------------------------------- 00070 // utilities 00071 00072 // number of elements in an array 00073 #define countof(x) (sizeof(x)/sizeof((x)[0])) 00074 00075 // -------------------------------------------------------------------------- 00076 // 00077 // 00078 // Build the full USB product ID. If we're using the LedWiz compatible 00079 // vendor ID, the full product ID is the combination of the LedWiz base 00080 // product ID (0x00F0) and the 0-based unit number (0-15). If we're not 00081 // trying to be LedWiz compatible, we just use the exact product ID 00082 // specified in config.h. 00083 #define MAKE_USB_PRODUCT_ID(vid, pidbase, unit) \ 00084 ((vid) == 0xFAFA && (pidbase) == 0x00F0 ? (pidbase) | (unit) : (pidbase)) 00085 00086 00087 // -------------------------------------------------------------------------- 00088 // 00089 // Joystick axis report range - we report from -JOYMAX to +JOYMAX 00090 // 00091 #define JOYMAX 4096 00092 00093 00094 // --------------------------------------------------------------------------- 00095 // 00096 // On-board RGB LED elements - we use these for diagnostic displays. 00097 // 00098 //TODO: FIXME for Nucleo 00099 DigitalOut ledR(LED1), ledG(LED2), ledB(LED3); 00100 00101 // --------------------------------------------------------------------------- 00102 // 00103 // Button input 00104 // 00105 00106 // button input map array 00107 00108 DigitalIn *buttonDigIn[NUM_OF_BUTTONS]; // config.h 00109 00110 // button state 00111 struct ButtonState 00112 { 00113 // current on/off state 00114 int pressed; 00115 00116 // Sticky time remaining for current state. When a 00117 // state transition occurs, we set this to a debounce 00118 // period. Future state transitions will be ignored 00119 // until the debounce time elapses. 00120 int t; 00121 } buttonState[NUM_OF_BUTTONS]; 00122 00123 // timer for button reports 00124 static Timer buttonTimer; 00125 00126 // initialize the button inputs 00127 void initButtons() 00128 { 00129 // create the digital inputs 00130 for (int i = 0 ; i < countof(buttonDigIn) ; ++i) 00131 { 00132 if (i < countof(buttonMap) && buttonMap[i] != NC) 00133 buttonDigIn[i] = new DigitalIn(buttonMap[i]); 00134 else 00135 buttonDigIn[i] = 0; 00136 } 00137 00138 // start the button timer 00139 buttonTimer.start(); 00140 } 00141 00142 00143 // read the button input state 00144 uint32_t readButtons() 00145 { 00146 // start with all buttons off 00147 uint32_t buttons = 0; 00148 00149 // figure the time elapsed since the last scan 00150 int dt = buttonTimer.read_ms(); 00151 00152 // reset the timef for the next scan 00153 buttonTimer.reset(); 00154 00155 // scan the button list 00156 uint32_t bit = 1; 00157 DigitalIn **di = buttonDigIn; 00158 ButtonState *bs = buttonState; 00159 for (int i = 0 ; i < countof(buttonDigIn) ; ++i, ++di, ++bs, bit <<= 1) 00160 { 00161 // read this button 00162 if (*di != 0) 00163 { 00164 // deduct the elapsed time since the last update 00165 // from the button's remaining sticky time 00166 bs->t -= dt; 00167 if (bs->t < 0) 00168 bs->t = 0; 00169 00170 // If the sticky time has elapsed, note the new physical 00171 // state of the button. If we still have sticky time 00172 // remaining, ignore the physical state; the last state 00173 // change persists until the sticky time elapses so that 00174 // we smooth out any "bounce" (electrical transients that 00175 // occur when the switch contact is opened or closed). 00176 if (bs->t == 0) 00177 { 00178 // get the new physical state 00179 int pressed = !(*di)->read(); 00180 00181 // update the button's logical state if this is a change 00182 if (pressed != bs->pressed) 00183 { 00184 // store the new state 00185 bs->pressed = pressed; 00186 00187 // start a new sticky period for debouncing this 00188 // state change 00189 bs->t = 25; 00190 } 00191 } 00192 00193 // if it's pressed, OR its bit into the state 00194 if (bs->pressed) 00195 buttons |= bit; 00196 } 00197 } 00198 00199 // return the new button list 00200 return buttons; 00201 } 00202 00203 // --------------------------------------------------------------------------- 00204 // 00205 // Customization joystick subbclass 00206 // 00207 00208 class MyUSBGamepad: public USBGamepad 00209 { 00210 public: 00211 MyUSBGamepad(uint16_t vendor_id, uint16_t product_id, uint16_t product_release) 00212 : USBGamepad(vendor_id, product_id, product_release, true) 00213 { 00214 suspended_ = false; 00215 } 00216 00217 // are we connected? 00218 int isConnected() { return configured(); } 00219 00220 // Are we in suspend mode? 00221 int isSuspended() const { return suspended_; } 00222 00223 protected: 00224 virtual void suspendStateChanged(unsigned int suspended) 00225 { suspended_ = suspended; } 00226 00227 // are we suspended? 00228 int suspended_; 00229 }; 00230 00231 00232 00233 // --------------------------------------------------------------------------- 00234 // 00235 // Main program loop. This is invoked on startup and runs forever. Our 00236 // main work is to read our devices, process 00237 // the readings into nudge and plunger position data, and send the results 00238 // to the host computer via the USB joystick interface. 00239 // 00240 int main(void) 00241 { 00242 // turn off our on-board indicator LED 00243 ledR = 1; 00244 ledG = 1; 00245 ledB = 1; 00246 00247 // we're not connected/awake yet 00248 bool connected = false; 00249 time_t connectChangeTime = time(0); 00250 00251 // initialize the button input ports 00252 initButtons(); 00253 00254 // we don't need a reset yet 00255 bool needReset = false; 00256 // Create the joystick USB client. 00257 MyUSBGamepad js(USB_VENDOR_ID,USB_PRODUCT_ID,USB_PRODUCT_VER); // vendor, product, product release 00258 00259 // last report timer - we use this to throttle reports, since VP 00260 // doesn't want to hear from us more than about every 10ms 00261 Timer reportTimer; 00262 reportTimer.start(); 00263 00264 // set up a timer for our heartbeat indicator 00265 Timer hbTimer; 00266 hbTimer.start(); 00267 int hb = 0; 00268 uint16_t hbcnt = 0; 00269 00270 // we're all set up - now just loop, processing sensor reports and 00271 // host requests 00272 for (;;) 00273 { 00274 00275 // update the buttons 00276 uint32_t buttons = readButtons(); 00277 00278 00279 // don't poll too fast, this can outrun the host 00280 if (reportTimer.read_ms() > 15) 00281 { 00282 js.update(buttons); 00283 // we've just started a new report interval, so reset the timer 00284 reportTimer.reset(); 00285 } 00286 00287 00288 00289 #ifdef DEBUG_PRINTF 00290 if (x != 0 || y != 0) 00291 printf("%d,%d\r\n", x, y); 00292 #endif 00293 00294 // check for connection status changes 00295 int newConnected = js.isConnected() && !js.isSuspended(); 00296 if (newConnected != connected) 00297 { 00298 // give it a few seconds to stabilize 00299 time_t tc = time(0); 00300 if (tc - connectChangeTime > 3) 00301 { 00302 // note the new status 00303 connected = newConnected; 00304 connectChangeTime = tc; 00305 } 00306 } 00307 00308 // provide a visual status indication on the on-board LED 00309 if (hbTimer.read_ms() > 1000) 00310 { 00311 if (!newConnected) 00312 { 00313 // suspended - turn off the LED 00314 ledR = 1; 00315 ledG = 1; 00316 ledB = 1; 00317 00318 // show a status flash every so often 00319 if (hbcnt % 3 == 0) 00320 { 00321 // disconnected = red/red flash; suspended = red 00322 for (int n = js.isConnected() ? 1 : 2 ; n > 0 ; --n) 00323 { 00324 ledR = 0; 00325 wait(0.05); 00326 ledR = 1; 00327 wait(0.25); 00328 } 00329 } 00330 } 00331 else if (needReset) 00332 { 00333 // connected, need to reset due to changes in config parameters - 00334 // flash red/green 00335 hb = !hb; 00336 ledR = (hb ? 0 : 1); 00337 ledG = (hb ? 1 : 0); 00338 ledB = 0; 00339 } 00340 else 00341 { 00342 // connected - flash blue/green 00343 hb = !hb; 00344 ledR = 1; 00345 ledG = (hb ? 0 : 1); 00346 ledB = (hb ? 1 : 0); 00347 } 00348 00349 // reset the heartbeat timer 00350 hbTimer.reset(); 00351 ++hbcnt; 00352 } 00353 00354 } 00355 }
Generated on Fri Jul 15 2022 06:19:56 by 1.7.2