Line data Source code
1 : /** 2 : * @file debounce.cpp 3 : * @brief Generic button debounce class 4 : * 5 : * @date Aug 31, 2020 6 : * @author David Holdeman, (c) 2020 7 : */ 8 : #include "pch.h" 9 : 10 : #include "debounce.h" 11 : #include "hardware.h" 12 : 13 : ButtonDebounce* ButtonDebounce::s_firstDebounce = nullptr; 14 : 15 584 : ButtonDebounce::ButtonDebounce(const char *name) 16 584 : : m_name(name) 17 : { 18 584 : } 19 : 20 : /** 21 : We need to have a separate init function because we do not have the pin or mode in the context in which the class is originally created 22 : */ 23 1169 : void ButtonDebounce::init (efitimems_t threshold, brain_pin_e &pin, pin_input_mode_e &mode, bool inverted) { 24 : // we need to keep track of whether we have already been initialized due to the way unit tests run. 25 1169 : if (!isInstanceRegisteredInGlobalList) { 26 : // Link us to the list that is used to track ButtonDebounce instances, so that when the configuration changes, 27 : // they can be looped through and updated. 28 498 : nextDebounce = s_firstDebounce; 29 498 : s_firstDebounce = this; 30 : } 31 1169 : m_threshold = MS2NT(threshold); 32 1169 : m_pin = &pin; 33 1169 : m_mode = &mode; 34 1169 : m_inverted = inverted; 35 1169 : startConfiguration(); 36 1169 : isInstanceRegisteredInGlobalList = true; 37 1169 : } 38 : 39 177 : void ButtonDebounce::stopConfigurationList() { 40 177 : ButtonDebounce *listItem = s_firstDebounce; 41 177 : int loopLimitCounter = 0; 42 354 : while (listItem != nullptr) { 43 177 : criticalAssertVoid(loopLimitCounter++ < 10000, "dead stopConfigurationList?"); 44 177 : listItem->stopConfiguration(); 45 177 : listItem = listItem->nextDebounce; 46 : } 47 : } 48 : 49 177 : void ButtonDebounce::startConfigurationList() { 50 177 : ButtonDebounce *listItem = s_firstDebounce; 51 177 : int loopLimitCounter = 0; 52 354 : while (listItem != nullptr) { 53 177 : criticalAssertVoid(loopLimitCounter++ < 10000, "dead startConfigurationList?"); 54 177 : listItem->startConfiguration(); 55 177 : listItem = listItem->nextDebounce; 56 : } 57 : } 58 : 59 177 : void ButtonDebounce::stopConfiguration() { 60 : // If the configuration has changed 61 : #if ! EFI_ACTIVE_CONFIGURATION_IN_FLASH 62 177 : if (*m_pin != active_pin || *m_mode != active_mode) { 63 : #else 64 : if (*m_pin != active_pin || *m_mode != active_mode || (isActiveConfigurationVoid && ((int)(*m_pin) != 0 || (int)(*m_mode) != 0))) { 65 : #endif /* EFI_ACTIVE_CONFIGURATION_IN_FLASH */ 66 : #if EFI_PROD_CODE 67 : efiSetPadUnused(active_pin); 68 : #endif /* EFI_UNIT_TEST */ 69 0 : needsPinInitialization = true; 70 : } 71 177 : } 72 : 73 1346 : void ButtonDebounce::startConfiguration() { 74 : #if EFI_PROD_CODE 75 : if (needsPinInitialization) { 76 : efiSetPadMode(m_name, *m_pin, getInputMode(*m_mode)); 77 : needsPinInitialization = false; 78 : } 79 : #endif 80 1346 : active_pin = *m_pin; 81 1346 : active_mode = *m_mode; 82 1346 : } 83 : 84 : /** 85 : @returns true if the button is pressed, and will not return true again within the set timeout 86 : */ 87 6 : bool ButtonDebounce::readPinEvent() { 88 6 : storedValue = readPinState2(false); 89 6 : return storedValue; 90 : } 91 : 92 20 : bool ButtonDebounce::getPhysicalState() { 93 : #if EFI_PROD_CODE || EFI_UNIT_TEST 94 20 : return efiReadPin(active_pin) ^ m_inverted; 95 : #else 96 : return false; 97 : #endif 98 : } 99 : 100 15 : bool ButtonDebounce::readPinState2(bool valueWithinThreshold) { 101 15 : if (!isBrainPinValid(*m_pin)) { 102 0 : return false; 103 : } 104 15 : efitick_t timeNowNt = getTimeNowNt(); 105 : // If it's been less than the threshold since we were last called 106 15 : if (timeLast.getElapsedNt(timeNowNt) < m_threshold) { 107 1 : return valueWithinThreshold; 108 : } 109 14 : bool value = getPhysicalState(); 110 : // efiPrintf("[debounce] %s value %d", m_name, value); 111 : // Invert 112 14 : if (active_mode == PI_PULLUP) { 113 8 : value = !value; 114 : // efiPrintf("[debounce] %s inverted %d", m_name, value); 115 : } 116 14 : if (value) { 117 5 : timeLast.reset(); 118 : } 119 14 : return value; 120 : } 121 : 122 9 : bool ButtonDebounce::readPinState() { 123 : // code comment could be out of date: 124 : // storedValue is a class variable, so it needs to be reset. 125 : // We don't actually need it to be a class variable in this method, 126 : // but when a method is implemented to actually get the pin's state, 127 : // for example to implement long button presses, it will be needed. 128 9 : storedValue = readPinState2(storedValue); 129 9 : return storedValue; 130 : } 131 : 132 0 : void ButtonDebounce::debug() { 133 0 : ButtonDebounce *listItem = s_firstDebounce; 134 0 : while (listItem != nullptr) { 135 : #if EFI_PROD_CODE || EFI_UNIT_TEST 136 0 : efiPrintf("%s timeLast %f", listItem->m_name, listItem->timeLast.getElapsedSeconds()); 137 0 : efiPrintf("physical pin state %d", listItem->getPhysicalState()); 138 0 : efiPrintf("state %d", listItem->storedValue); 139 0 : efiPrintf("mode %d", listItem->active_mode); 140 : #endif 141 : 142 0 : listItem = listItem->nextDebounce; 143 : } 144 0 : } 145 : 146 497 : void initButtonDebounce() { 147 : #if !EFI_UNIT_TEST 148 : addConsoleAction("debounce", ButtonDebounce::debug); 149 : #endif /* EFI_UNIT_TEST */ 150 497 : }