Mirror with some correction
Dependencies: mbed FastIO FastPWM USBDevice
Diff: main.cpp
- Revision:
- 65:739875521aae
- Parent:
- 64:ef7ca92dff36
- Child:
- 66:2e3583fbd2f4
--- a/main.cpp Tue Nov 22 20:46:36 2016 +0000 +++ b/main.cpp Wed Nov 23 19:49:20 2016 +0000 @@ -1522,15 +1522,24 @@ // DigitalIn for the button, if connected to a physical input TinyDigitalIn *di; - // current PHYSICAL on/off state, after debouncing - uint8_t physState : 1; + // Time of last pulse state transition. + // + // Each state change sticks for a minimum period; when the timer expires, + // if the underlying physical switch is in a different state, we switch + // to the next state and restart the timer. pulseTime is the time remaining + // remaining before we can make another state transition, in microseconds. + // The state transitions require a complete cycle, 1 -> 2 -> 3 -> 4 -> 1...; + // this guarantees that the parity of the pulse count always matches the + // current physical switch state when the latter is stable, which makes + // it impossible to "trick" the host by rapidly toggling the switch state. + // (On my original Pinscape cabinet, I had a hardware pulse generator + // for coin door, and that *was* possible to trick by rapid toggling. + // This software system can't be fooled that way.) + uint32_t pulseTime; - // current LOGICAL on/off state as reported to the host. - uint8_t logState : 1; - - // previous logical on/off state, when keys were last processed for USB - // reports and local effects - uint8_t prevLogState : 1; + // Config key index. This points to the ButtonCfg structure in the + // configuration that contains the PC key mapping for the button. + uint8_t cfgIndex; // Virtual press state. This is used to simulate pressing the button via // software inputs rather than physical inputs. To allow one button to be @@ -1549,15 +1558,31 @@ // a parameter that determines how long we wait for transients to settle). uint8_t dbState; - // Pulse mode: a button in pulse mode transmits a brief logical button press and - // release each time the attached physical switch changes state. This is useful - // for cases where the host expects a key press for each change in the state of - // the physical switch. The canonical example is the Coin Door switch in VPinMAME, - // which requires pressing the END key to toggle the open/closed state. This - // software design isn't easily implemented in a physical coin door, though - - // the easiest way to sense a physical coin door's state is with a simple on/off - // switch. Pulse mode bridges that divide by converting a physical switch state - // to on/off toggle key reports to the host. + // current PHYSICAL on/off state, after debouncing + uint8_t physState : 1; + + // current LOGICAL on/off state as reported to the host. + uint8_t logState : 1; + + // previous logical on/off state, when keys were last processed for USB + // reports and local effects + uint8_t prevLogState : 1; + + // Pulse state + // + // A button in pulse mode (selected via the config flags for the button) + // transmits a brief logical button press and release each time the attached + // physical switch changes state. This is useful for cases where the host + // expects a key press for each change in the state of the physical switch. + // The canonical example is the Coin Door switch in VPinMAME, which requires + // pressing the END key to toggle the open/closed state. This software design + // isn't easily implemented in a physical coin door, though; the simplest + // physical sensor for the coin door state is a switch that's on when the + // door is open and off when the door is closed (or vice versa, but in either + // case, the switch state corresponds to the current state of the door at any + // given time, rather than pulsing on state changes). The "pulse mode" + // option brdiges this gap by generating a toggle key event each time + // there's a change to the physical switch's state. // // Pulse state: // 0 -> not a pulse switch - logical key state equals physical switch state @@ -1565,22 +1590,13 @@ // 2 -> transitioning off-on // 3 -> on // 4 -> transitioning on-off - // - // Each state change sticks for a minimum period; when the timer expires, - // if the underlying physical switch is in a different state, we switch - // to the next state and restart the timer. pulseTime is the time remaining - // remaining before we can make another state transition, in microseconds. - // The state transitions require a complete cycle, 1 -> 2 -> 3 -> 4 -> 1...; - // this guarantees that the parity of the pulse count always matches the - // current physical switch state when the latter is stable, which makes - // it impossible to "trick" the host by rapidly toggling the switch state. - // (On my original Pinscape cabinet, I had a hardware pulse generator - // for coin door, and that *was* possible to trick by rapid toggling. - // This software system can't be fooled that way.) - uint8_t pulseState; - uint32_t pulseTime; - -} __attribute__((packed)) buttonState[MAX_BUTTONS]; + uint8_t pulseState : 3; // 5 states -> we need 3 bits + +} __attribute__((packed)); + +ButtonState *buttonState; // live button slots, allocated on startup +int8_t nButtons; // number of live button slots allocated +int8_t zblButtonIndex = -1; // index of ZB Launch button slot; -1 if unused // Button data @@ -1613,7 +1629,7 @@ { // scan all button input pins ButtonState *bs = buttonState; - for (int i = 0 ; i < MAX_BUTTONS ; ++i, ++bs) + for (int i = 0 ; i < nButtons ; ++i, ++bs) { // if this logical button is connected to a physical input, check // the GPIO pin state @@ -1645,60 +1661,90 @@ // in the physical button state. Timer buttonTimer; +// Count a button during the initial setup scan +void countButton(uint8_t typ, bool &kbKeys) +{ + // count it + ++nButtons; + + // if it's a keyboard key, note that we need a USB keyboard interface + if (typ == BtnTypeKey) + kbKeys = true; +} + // initialize the button inputs void initButtons(Config &cfg, bool &kbKeys) { // presume we'll find no keyboard keys kbKeys = false; + // Count up how many button slots we'll need to allocate. Start + // with assigned buttons from the configuration, noting that we + // only need to create slots for buttons that are actually wired. + nButtons = 0; + for (int i = 0 ; i < MAX_BUTTONS ; ++i) + { + // it's valid if it's wired to a real input pin + if (wirePinName(cfg.button[i].pin) != NC) + countButton(cfg.button[i].typ, kbKeys); + } + + // Count virtual buttons + + // ZB Launch + if (cfg.plunger.zbLaunchBall.port != 0) + { + // valid - remember the live button index + zblButtonIndex = nButtons; + + // count it + countButton(cfg.plunger.zbLaunchBall.keytype, kbKeys); + } + + // Allocate the live button slots + ButtonState *bs = buttonState = new ButtonState[nButtons]; + + // Configure the physical inputs + for (int i = 0 ; i < MAX_BUTTONS ; ++i) + { + PinName pin = wirePinName(cfg.button[i].pin); + if (pin != NC) + { + // point back to the config slot for the keyboard data + bs->cfgIndex = i; + + // set up the GPIO input pin for this button + bs->di = new TinyDigitalIn(pin); + + // if it's a pulse mode button, set the initial pulse state to Off + if (cfg.button[i].flags & BtnFlagPulse) + bs->pulseState = 1; + + // advance to the next button + ++bs; + } + } + // Configure the virtual buttons. These are buttons controlled via // software triggers rather than physical GPIO inputs. The virtual // buttons have the same control structures as regular buttons, but // they get their configuration data from other config variables. // ZB Launch Ball button - cfg.button[ZBL_BUTTON].set( - PINNAME_TO_WIRE(NC), - cfg.plunger.zbLaunchBall.keytype, - cfg.plunger.zbLaunchBall.keycode); - - // create the digital inputs - ButtonState *bs = buttonState; - for (int i = 0 ; i < MAX_BUTTONS ; ++i, ++bs) + if (cfg.plunger.zbLaunchBall.port != 0) { - PinName pin = wirePinName(cfg.button[i].pin); - if (pin != NC) - { - // set up the GPIO input pin for this button - bs->di = new TinyDigitalIn(pin); - - // if it's a pulse mode button, set the initial pulse state to Off - if (cfg.button[i].flags & BtnFlagPulse) - bs->pulseState = 1; - - // Note if it's a keyboard key of some kind. If we find any keyboard - // mappings, we'll declare a keyboard interface when we send our HID - // configuration to the host during USB connection setup. - switch (cfg.button[i].typ) - { - case BtnTypeKey: - // note that we have at least one keyboard key - kbKeys = true; - break; - - default: - // not a keyboard key - break; - } - } + // Point back to the config slot for the keyboard data. + // We use a special extra slot for virtual buttons, so + // we also need to set up the slot data. + bs->cfgIndex = ZBL_BUTTON_CFG; + cfg.button[ZBL_BUTTON_CFG].pin = PINNAME_TO_WIRE(NC); + cfg.button[ZBL_BUTTON_CFG].typ = cfg.plunger.zbLaunchBall.keytype; + cfg.button[ZBL_BUTTON_CFG].val = cfg.plunger.zbLaunchBall.keycode; + + // advnace to the next button + ++bs; } - // If the ZB Launch Ball feature is enabled, and it uses a keyboard - // key, this requires setting up a USB keyboard interface. - if (cfg.plunger.zbLaunchBall.port != 0 - && cfg.plunger.zbLaunchBall.keytype == BtnTypeKey) - kbKeys = true; - // start the button scan thread buttonTicker.attach_us(scanButtons, 1000); @@ -1729,8 +1775,7 @@ // scan the button list ButtonState *bs = buttonState; - ButtonCfg *bc = cfg.button; - for (int i = 0 ; i < MAX_BUTTONS ; ++i, ++bs, ++bc) + for (int i = 0 ; i < nButtons ; ++i, ++bs) { // if it's a pulse-mode switch, get the virtual pressed state if (bs->pulseState != 0) @@ -1827,6 +1872,7 @@ if (bs->logState || bs->virtState) { // OR in the joystick button bit, mod key bits, and media key bits + ButtonCfg *bc = &cfg.button[bs->cfgIndex]; uint8_t val = bc->val; switch (bc->typ) { @@ -3743,7 +3789,7 @@ btnState = on; // update the virtual button state - buttonState[ZBL_BUTTON].virtPress(on); + buttonState[zblButtonIndex].virtPress(on); } }