Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
main.cpp
- Committer:
- thetazzbot
- Date:
- 2016-12-14
- Revision:
- 6:29a04fe27b5e
- Parent:
- 4:05f4ace9508a
File content as of revision 6:29a04fe27b5e:
/* Modified for RetroPie MW 2016 */
/* Copyright 2014 M J Roberts, MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//
// - Button input wiring. 24 of the KL25Z's GPIO ports are mapped as digital inputs
// for buttons and switches. The software reports these as joystick buttons when
// it sends reports to the PC. These can be used to wire physical pinball-style
// buttons in the cabinet (e.g., flipper buttons, the Start button) and miscellaneous
// switches (such as a tilt bob) to the PC. Visual Pinball can use joystick buttons
// for input - you just have to assign a VP function to each button using VP's
// keyboard options dialog. To wire a button physically, connect one terminal of
// the button switch to the KL25Z ground, and connect the other terminal to the
// the GPIO port you wish to assign to the button. See the buttonMap[] array
// below for the available GPIO ports and their assigned joystick button numbers.
// If you're not using a GPIO port, you can just leave it unconnected - the digital
// inputs have built-in pull-up resistors, so an unconnected port is the same as
// an open switch (an "off" state for the button).
//
//
// STATUS LIGHTS: The on-board LED on the KL25Z flashes to indicate the current
// device status. The flash patterns are:
//
// two short red flashes = the device is powered but hasn't successfully
// connected to the host via USB (either it's not physically connected
// to the USB port, or there was a problem with the software handshake
// with the USB device driver on the computer)
//
// short red flash = the host computer is in sleep/suspend mode
//
// long red/green = the LedWiz unti number has been changed, so a reset
// is needed. You can simply unplug the device and plug it back in,
// or presss and hold the reset button on the device for a few seconds.
//
// long yellow/green = everything's working, but the plunger hasn't
// been calibrated; follow the calibration procedure described above.
// This flash mode won't appear if the CCD has been disabled. Note
// that the device can't tell whether a CCD is physically attached;
// if you don't have a CCD attached, you can set the appropriate option
// in config.h or use the Windows config tool to disable the CCD
// software features.
//
// alternating blue/green = everything's working
//
#include "mbed.h"
#include "math.h"
#include "USBGamepad.h"
#define DECL_EXTERNS
#include "config.h"
// ---------------------------------------------------------------------------
// utilities
// number of elements in an array
#define countof(x) (sizeof(x)/sizeof((x)[0]))
// --------------------------------------------------------------------------
//
//
// Build the full USB product ID. If we're using the LedWiz compatible
// vendor ID, the full product ID is the combination of the LedWiz base
// product ID (0x00F0) and the 0-based unit number (0-15). If we're not
// trying to be LedWiz compatible, we just use the exact product ID
// specified in config.h.
#define MAKE_USB_PRODUCT_ID(vid, pidbase, unit) \
((vid) == 0xFAFA && (pidbase) == 0x00F0 ? (pidbase) | (unit) : (pidbase))
// --------------------------------------------------------------------------
//
// Joystick axis report range - we report from -JOYMAX to +JOYMAX
//
#define JOYMAX 4096
// ---------------------------------------------------------------------------
//
// On-board RGB LED elements - we use these for diagnostic displays.
//
//TODO: FIXME for Nucleo
DigitalOut ledR(LED1), ledG(LED2), ledB(LED3);
// ---------------------------------------------------------------------------
//
// Button input
//
// button input map array
DigitalIn *buttonDigIn[NUM_OF_BUTTONS]; // config.h
// button state
struct ButtonState
{
// current on/off state
int pressed;
// Sticky time remaining for current state. When a
// state transition occurs, we set this to a debounce
// period. Future state transitions will be ignored
// until the debounce time elapses.
int t;
} buttonState[NUM_OF_BUTTONS];
// timer for button reports
static Timer buttonTimer;
// initialize the button inputs
void initButtons()
{
// create the digital inputs
for (int i = 0 ; i < countof(buttonDigIn) ; ++i)
{
if (i < countof(buttonMap) && buttonMap[i] != NC)
buttonDigIn[i] = new DigitalIn(buttonMap[i]);
else
buttonDigIn[i] = 0;
}
// start the button timer
buttonTimer.start();
}
// read the button input state
uint32_t readButtons()
{
// start with all buttons off
uint32_t buttons = 0;
// figure the time elapsed since the last scan
int dt = buttonTimer.read_ms();
// reset the timef for the next scan
buttonTimer.reset();
// scan the button list
uint32_t bit = 1;
DigitalIn **di = buttonDigIn;
ButtonState *bs = buttonState;
for (int i = 0 ; i < countof(buttonDigIn) ; ++i, ++di, ++bs, bit <<= 1)
{
// read this button
if (*di != 0)
{
// deduct the elapsed time since the last update
// from the button's remaining sticky time
bs->t -= dt;
if (bs->t < 0)
bs->t = 0;
// If the sticky time has elapsed, note the new physical
// state of the button. If we still have sticky time
// remaining, ignore the physical state; the last state
// change persists until the sticky time elapses so that
// we smooth out any "bounce" (electrical transients that
// occur when the switch contact is opened or closed).
if (bs->t == 0)
{
// get the new physical state
int pressed = !(*di)->read();
// update the button's logical state if this is a change
if (pressed != bs->pressed)
{
// store the new state
bs->pressed = pressed;
// start a new sticky period for debouncing this
// state change
bs->t = 25;
}
}
// if it's pressed, OR its bit into the state
if (bs->pressed)
buttons |= bit;
}
}
// return the new button list
return buttons;
}
// ---------------------------------------------------------------------------
//
// Customization joystick subbclass
//
class MyUSBGamepad: public USBGamepad
{
public:
MyUSBGamepad(uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBGamepad(vendor_id, product_id, product_release, true)
{
suspended_ = false;
}
// are we connected?
int isConnected() { return configured(); }
// Are we in suspend mode?
int isSuspended() const { return suspended_; }
protected:
virtual void suspendStateChanged(unsigned int suspended)
{ suspended_ = suspended; }
// are we suspended?
int suspended_;
};
// ---------------------------------------------------------------------------
//
// Main program loop. This is invoked on startup and runs forever. Our
// main work is to read our devices, process
// the readings into nudge and plunger position data, and send the results
// to the host computer via the USB joystick interface.
//
int main(void)
{
// turn off our on-board indicator LED
ledR = 1;
ledG = 1;
ledB = 1;
// we're not connected/awake yet
bool connected = false;
time_t connectChangeTime = time(0);
// initialize the button input ports
initButtons();
// we don't need a reset yet
bool needReset = false;
// Create the joystick USB client.
MyUSBGamepad js(USB_VENDOR_ID,USB_PRODUCT_ID,USB_PRODUCT_VER); // vendor, product, product release
// last report timer - we use this to throttle reports, since VP
// doesn't want to hear from us more than about every 10ms
Timer reportTimer;
reportTimer.start();
// set up a timer for our heartbeat indicator
Timer hbTimer;
hbTimer.start();
int hb = 0;
uint16_t hbcnt = 0;
// we're all set up - now just loop, processing sensor reports and
// host requests
for (;;)
{
// update the buttons
uint32_t buttons = readButtons();
// don't poll too fast, this can outrun the host
if (reportTimer.read_ms() > 15)
{
js.update(buttons);
// we've just started a new report interval, so reset the timer
reportTimer.reset();
}
#ifdef DEBUG_PRINTF
if (x != 0 || y != 0)
printf("%d,%d\r\n", x, y);
#endif
// check for connection status changes
int newConnected = js.isConnected() && !js.isSuspended();
if (newConnected != connected)
{
// give it a few seconds to stabilize
time_t tc = time(0);
if (tc - connectChangeTime > 3)
{
// note the new status
connected = newConnected;
connectChangeTime = tc;
}
}
// provide a visual status indication on the on-board LED
if (hbTimer.read_ms() > 1000)
{
if (!newConnected)
{
// suspended - turn off the LED
ledR = 1;
ledG = 1;
ledB = 1;
// show a status flash every so often
if (hbcnt % 3 == 0)
{
// disconnected = red/red flash; suspended = red
for (int n = js.isConnected() ? 1 : 2 ; n > 0 ; --n)
{
ledR = 0;
wait(0.05);
ledR = 1;
wait(0.25);
}
}
}
else if (needReset)
{
// connected, need to reset due to changes in config parameters -
// flash red/green
hb = !hb;
ledR = (hb ? 0 : 1);
ledG = (hb ? 1 : 0);
ledB = 0;
}
else
{
// connected - flash blue/green
hb = !hb;
ledR = 1;
ledG = (hb ? 0 : 1);
ledB = (hb ? 1 : 0);
}
// reset the heartbeat timer
hbTimer.reset();
++hbcnt;
}
}
}