acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

ButtonConfig.h (13303B)

      1 /*
      2 MIT License
      3 
      4 Copyright (c) 2018 Brian T. Park
      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 */
     24 
     25 #ifndef ACE_BUTTON_BUTTON_CONFIG_H
     26 #define ACE_BUTTON_BUTTON_CONFIG_H
     27 
     28 #if !defined(RASPBERRY_PI)
     29 #include <Arduino.h>
     30 #else
     31 #include <raspi/raspi.h>
     32 #endif /* RASPBERRY_PI */
     33 
     34 // TODO: Verify if this is actually needed. The program size seems to be
     35 // identical with or without it on the Arduino IDE (which uses gcc).
     36 #define ACE_BUTTON_INLINE __attribute__((always_inline))
     37 
     38 namespace ace_button {
     39 
     40 class AceButton;
     41 class TimingStats;
     42 
     43 /**
     44  * Class that defines the timing parameters and event handler of an AceButton or
     45  * a group of AceButton instances. It is assumed that in many cases, a group of
     46  * multiple buttons will need to be assigned the same configuration parameters.
     47  * For example, various timing delays and the EventHandler. Instead of storing
     48  * these parameters in each instance of AceButton (which consumes static
     49  * memory), we save space by collecting them into a separate ButtonConfig class.
     50  * Each AceButton instance contains a pointer to an instance of ButtonConfig,
     51  * and an instance of ButtonConfig will be shared among multiple AceButtons.
     52  *
     53  * Various timing parameters are given default values. They can be
     54  * overridden by the user.
     55  *
     56  * A single default "System" ButtonConfig instance is created automatically and
     57  * can be accessed using the ButtConfig::getSystemButtonConfig() static method.
     58  * For convenience and ease of use, every instance of AceButton is attached to
     59  * this "System" ButtonConfig by default. The client code can override this
     60  * association by attaching another ButtonConfig instance using the
     61  * AceButton(ButtonConfig*) constuctor (the recommended solution) or the
     62  * AceButton::setButtonConfig() method.
     63  */
     64 class ButtonConfig {
     65   public:
     66     // Various timing constants, in milliseconds.
     67     //
     68     // Note that the timing constants are stored as uint16_t (2
     69     // bytes) instead of unsigned long (4 bytes) which is the type returned by
     70     // the millis() system method. It turns out that we can store and perform
     71     // all timing calculations using uint16_t without ill effect, as long as the
     72     // polling of AceButton::check() happens more frequently than the rollover
     73     // time of a uint16_t (i.e. 65.536 seconds) and certain precautions (e.g.
     74     // AceButton::checkOrphanedClick()) are taken before a uint16_t rollover
     75     // happens. In theory, these additional precautions would be needed even if
     76     // an 'unsigned long' is used but almost no one does them because they
     77     // assume that their code won't be running continuously for the rollover
     78     // time of an 'unsigned long' (i.e. 49.7 days).
     79 
     80     /** Default value returned by getDebounceDelay(). */
     81     static const uint16_t kDebounceDelay = 20;
     82 
     83     /** Default value returned by getClickDelay(). */
     84     static const uint16_t kClickDelay = 200;
     85 
     86     /** Default value returned by getDoubleClickDelay(). */
     87     static const uint16_t kDoubleClickDelay = 400;
     88 
     89     /** Default value returned by getLongPressDelay(). */
     90     static const uint16_t kLongPressDelay = 1000;
     91 
     92     /** Default value returned by getRepeatPressDelay(). */
     93     static const uint16_t kRepeatPressDelay = 1000;
     94 
     95     /** Default value returned by getRepeatPressInterval(). */
     96     static const uint16_t kRepeatPressInterval = 200;
     97 
     98     // Various features controlled by feature flags.
     99 
    100     /**
    101      * Type of the feature flag. It used to be a uint8_t but got changed to a
    102      * uint16_t when more than 8 flags were needed. Let's define a typedef to
    103      * make it easier to change this in the future.
    104      */
    105     typedef uint16_t FeatureFlagType;
    106 
    107     /** Flag to activate the AceButton::kEventClicked event. */
    108     static const FeatureFlagType kFeatureClick = 0x01;
    109 
    110     /**
    111      * Flag to activate the AceButton::kEventDoubleClicked event.
    112      * Activating this automatically activates kEventClicked since there is
    113      * no double-click without a click.
    114      */
    115     static const FeatureFlagType kFeatureDoubleClick = 0x02;
    116 
    117     /** Flag to activate the AceButton::kEventLongPress event. */
    118     static const FeatureFlagType kFeatureLongPress = 0x04;
    119 
    120     /** Flag to activate the AceButton::kEventRepeatPressed event. */
    121     static const FeatureFlagType kFeatureRepeatPress = 0x08;
    122 
    123     /** Flag to suppress kEventReleased after a kEventClicked. */
    124     static const FeatureFlagType kFeatureSuppressAfterClick = 0x10;
    125 
    126     /**
    127      * Flag to suppress kEventReleased after a kEventDoubleClicked. A
    128      * kEventClicked is _always_ suppressed after a kEventDoubleClicked to
    129      * prevent generating 2 double-clicks if the user performed a triple-click.
    130      */
    131     static const FeatureFlagType kFeatureSuppressAfterDoubleClick = 0x20;
    132 
    133     /** Flag to suppress kEventReleased after a kEventLongPressed. */
    134     static const FeatureFlagType kFeatureSuppressAfterLongPress = 0x40;
    135 
    136     /** Flag to suppress kEventReleased after a kEventRepeatPressed. */
    137     static const FeatureFlagType kFeatureSuppressAfterRepeatPress = 0x80;
    138 
    139     /**
    140      * Flag to suppress kEventClicked before a kEventDoubleClicked. This causes
    141      * the notification of a kEventClicked to be delayed until the delay time of
    142      * getDoubleClickDelay() has passed so that we can determine if there was a
    143      * kEventDoubleClicked.
    144      */
    145     static const FeatureFlagType kFeatureSuppressClickBeforeDoubleClick = 0x100;
    146 
    147     /**
    148      * Convenience flag to suppress all suppressions. Calling
    149      * setFeature(kFeatureSuppressAll) suppresses all and
    150      * clearFeature(kFeatureSuppressAll) clears all suppression. Note however
    151      * that isFeature(kFeatureSuppressAll) currently means "is ANY feature
    152      * enabled?" not "are ALL features enabled?".
    153      */
    154     static const FeatureFlagType kFeatureSuppressAll =
    155         (kFeatureSuppressAfterClick |
    156         kFeatureSuppressAfterDoubleClick |
    157         kFeatureSuppressAfterLongPress |
    158         kFeatureSuppressAfterRepeatPress |
    159         kFeatureSuppressClickBeforeDoubleClick);
    160 
    161     /**
    162      * The event handler signature.
    163      *
    164      * @param button pointer to the AceButton that generated the event
    165      * @param eventType the event type which trigger the call
    166      * @param buttonState the state of the button that triggered the event
    167      */
    168     typedef void (*EventHandler)(AceButton* button, uint8_t eventType,
    169         uint8_t buttonState);
    170 
    171     /** Constructor. */
    172     ButtonConfig() {}
    173 
    174     // These configuration methods are virtual so that they can be overriddden.
    175     // Subclasses can override at the class-level by defining a new virtual
    176     // function in the subclass, or by defining an instance variable and storing
    177     // the parameter with each instance of this class.
    178 
    179     /** Milliseconds to wait for debouncing. */
    180     uint16_t getDebounceDelay() { return mDebounceDelay; }
    181 
    182     /** Milliseconds to wait for a possible click. */
    183     uint16_t getClickDelay() { return mClickDelay; }
    184 
    185     /**
    186      * Milliseconds between the first and second click to register as a
    187      * double-click.
    188      */
    189     uint16_t getDoubleClickDelay() {
    190       return mDoubleClickDelay;
    191     }
    192 
    193     /** Milliseconds for a long press event. */
    194     uint16_t getLongPressDelay() {
    195       return mLongPressDelay;
    196     }
    197 
    198     /**
    199      * Milliseconds that a button needs to be Pressed down before the start of
    200      * the sequence of RepeatPressed events. The first event will fire as soon
    201      * as this delay has passed. Subsequent events will fire after
    202      * getRepeatPressInterval() time.
    203      */
    204     uint16_t getRepeatPressDelay() {
    205       return mRepeatPressDelay;
    206     }
    207 
    208     /**
    209      * Milliseconds between two successive RepeatPressed events.
    210      */
    211     uint16_t getRepeatPressInterval() {
    212       return mRepeatPressInterval;
    213     }
    214 
    215     /** Set the debounceDelay. */
    216     void setDebounceDelay(uint16_t debounceDelay) {
    217       mDebounceDelay = debounceDelay;
    218     }
    219 
    220     /** Set the clickDelay. */
    221     void setClickDelay(uint16_t clickDelay) {
    222       mClickDelay = clickDelay;
    223     }
    224 
    225     /** Set the doubleClickDelay. */
    226     void setDoubleClickDelay(uint16_t doubleClickDelay) {
    227       mDoubleClickDelay = doubleClickDelay;
    228     }
    229 
    230     /** Set the longPressDelay. */
    231     void setLongPressDelay(uint16_t longPressDelay) {
    232       mLongPressDelay = longPressDelay;
    233     }
    234 
    235     /** Set the repeatPressDelay. */
    236     void setRepeatPressDelay(uint16_t repeatPressDelay) {
    237       mRepeatPressDelay = repeatPressDelay;
    238     }
    239 
    240     /** Set the repeatPressInterval. */
    241     void setRepeatPressInterval(uint16_t repeatPressInterval) {
    242       mRepeatPressInterval = repeatPressInterval;
    243     }
    244 
    245     // The getClock() and readButton() are external dependencies that normally
    246     // would be injected using separate classes, but in the interest of saving
    247     // RAM in an embedded environment, we expose them in this class instead.
    248 
    249     /**
    250      * Return the milliseconds of the internal clock. Override to use something
    251      * other than millis(). The return type is 'unsigned long' instead of
    252      * uint16_t because that's the return type of millis().
    253      */
    254     virtual unsigned long getClock() { return millis(); }
    255 
    256     /**
    257      * Return the microseconds of the internal clock. Can be overridden
    258      * for testing purposes.
    259      */
    260     virtual unsigned long getClockMicros() { return micros(); }
    261 
    262     /**
    263      * Return the HIGH or LOW state of the button. Override to use something
    264      * other than digitalRead(). The return type is 'int' instead of uint16_t
    265      * because that's the return type of digitalRead().
    266      */
    267     virtual int readButton(uint8_t pin) {
    268       return digitalRead(pin);
    269     }
    270 
    271     // These methods return the various feature flags that control the
    272     // functionality of the AceButton.
    273 
    274     /** Check if the given features are enabled. */
    275     bool isFeature(FeatureFlagType features) ACE_BUTTON_INLINE {
    276       return mFeatureFlags & features;
    277     }
    278 
    279     /** Enable the given features. */
    280     void setFeature(FeatureFlagType features) ACE_BUTTON_INLINE {
    281       mFeatureFlags |= features;
    282     }
    283 
    284     /** Disable the given features. */
    285     void clearFeature(FeatureFlagType features) ACE_BUTTON_INLINE {
    286       mFeatureFlags &= ~features;
    287     }
    288 
    289     // EventHandler
    290 
    291     /** Return the eventHandler. */
    292     EventHandler getEventHandler() ACE_BUTTON_INLINE {
    293       return mEventHandler;
    294     }
    295 
    296     /**
    297      * Install the event handler. The event handler must be defined for the
    298      * AceButton to be useful.
    299      */
    300     void setEventHandler(EventHandler eventHandler) ACE_BUTTON_INLINE {
    301       mEventHandler = eventHandler;
    302     }
    303 
    304     // TimingStats
    305 
    306     /** Set the timing stats object. The timingStats can be nullptr. */
    307     void setTimingStats(TimingStats* timingStats) {
    308       mTimingStats = timingStats;
    309     }
    310 
    311     /** Get the timing stats. Can return nullptr. */
    312     TimingStats* getTimingStats() { return mTimingStats; }
    313 
    314     /**
    315      * Return a pointer to the singleton instance of the ButtonConfig
    316      * which is attached to all AceButton instances by default.
    317      */
    318     static ButtonConfig* getSystemButtonConfig() ACE_BUTTON_INLINE {
    319       return &sSystemButtonConfig;
    320     }
    321 
    322   protected:
    323     /**
    324      * Initialize to its pristine state, except for the EventHandler which is
    325      * unchanged. This is intended mostly for testing purposes.
    326      */
    327     virtual void init() {
    328       mFeatureFlags = 0;
    329       mTimingStats = nullptr;
    330     }
    331 
    332   private:
    333     /**
    334      * A single static instance of ButtonConfig provided by default to all
    335      * AceButton instances.
    336      */
    337     static ButtonConfig sSystemButtonConfig;
    338 
    339     // Disable copy-constructor and assignment operator
    340     ButtonConfig(const ButtonConfig&) = delete;
    341     ButtonConfig& operator=(const ButtonConfig&) = delete;
    342 
    343     /** The event handler for all buttons associated with this ButtonConfig. */
    344     EventHandler mEventHandler = nullptr;
    345 
    346     /** A bit mask flag that activates certain features. */
    347     FeatureFlagType mFeatureFlags = 0;
    348 
    349     /** The timing stats object. */
    350     TimingStats* mTimingStats = nullptr;
    351 
    352     uint16_t mDebounceDelay = kDebounceDelay;
    353     uint16_t mClickDelay = kClickDelay;
    354     uint16_t mDoubleClickDelay = kDoubleClickDelay;
    355     uint16_t mLongPressDelay = kLongPressDelay;
    356     uint16_t mRepeatPressDelay = kRepeatPressDelay;
    357     uint16_t mRepeatPressInterval = kRepeatPressInterval;
    358 };
    359 
    360 }
    361 #endif