| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 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 | 679 | ButtonDebounce::ButtonDebounce(const char *name) | ||
| 16 | 679 | : m_name(name) | ||
| 17 | { | |||
| 18 | 679 | } | ||
| 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 | 1390 | 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 |
2/2✓ Branch 0 taken 585 times.
✓ Branch 1 taken 805 times.
|
2/2✓ Decision 'true' taken 585 times.
✓ Decision 'false' taken 805 times.
|
1390 | 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 | 585 | nextDebounce = s_firstDebounce; | ||
| 29 | 585 | s_firstDebounce = this; | ||
| 30 | } | |||
| 31 | 1390 | m_threshold = MS2NT(threshold); | ||
| 32 | 1390 | m_pin = &pin; | ||
| 33 | 1390 | m_mode = &mode; | ||
| 34 | 1390 | m_inverted = inverted; | ||
| 35 | 1390 | startConfiguration(); | ||
| 36 | 1390 | isInstanceRegisteredInGlobalList = true; | ||
| 37 | 1390 | } | ||
| 38 | ||||
| 39 | 223 | void ButtonDebounce::stopConfigurationList() { | ||
| 40 | 223 | ButtonDebounce *listItem = s_firstDebounce; | ||
| 41 | 223 | int loopLimitCounter = 0; | ||
| 42 |
2/2✓ Branch 0 taken 223 times.
✓ Branch 1 taken 223 times.
|
2/2✓ Decision 'true' taken 223 times.
✓ Decision 'false' taken 223 times.
|
446 | while (listItem != nullptr) { |
| 43 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 223 times.
|
223 | criticalAssertVoid(loopLimitCounter++ < 10000, "dead stopConfigurationList?"); | |
| 44 | 223 | listItem->stopConfiguration(); | ||
| 45 | 223 | listItem = listItem->nextDebounce; | ||
| 46 | } | |||
| 47 | } | |||
| 48 | ||||
| 49 | 223 | void ButtonDebounce::startConfigurationList() { | ||
| 50 | 223 | ButtonDebounce *listItem = s_firstDebounce; | ||
| 51 | 223 | int loopLimitCounter = 0; | ||
| 52 |
2/2✓ Branch 0 taken 223 times.
✓ Branch 1 taken 223 times.
|
2/2✓ Decision 'true' taken 223 times.
✓ Decision 'false' taken 223 times.
|
446 | while (listItem != nullptr) { |
| 53 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 223 times.
|
223 | criticalAssertVoid(loopLimitCounter++ < 10000, "dead startConfigurationList?"); | |
| 54 | 223 | listItem->startConfiguration(); | ||
| 55 | 223 | listItem = listItem->nextDebounce; | ||
| 56 | } | |||
| 57 | } | |||
| 58 | ||||
| 59 | 223 | void ButtonDebounce::stopConfiguration() { | ||
| 60 | // If the configuration has changed | |||
| 61 | #if ! EFI_ACTIVE_CONFIGURATION_IN_FLASH | |||
| 62 |
2/4✓ Branch 0 taken 223 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 223 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 223 times.
|
223 | 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 | ✗ | needsPinInitialization = true; | ||
| 70 | } | |||
| 71 | 223 | } | ||
| 72 | ||||
| 73 | 1613 | 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 | 1613 | active_pin = *m_pin; | ||
| 81 | 1613 | active_mode = *m_mode; | ||
| 82 | 1613 | } | ||
| 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 | 104 | bool ButtonDebounce::getPhysicalState() { | ||
| 93 | #if EFI_PROD_CODE || EFI_UNIT_TEST | |||
| 94 | 104 | return efiReadPin(active_pin) ^ m_inverted; | ||
| 95 | #else | |||
| 96 | return false; | |||
| 97 | #endif | |||
| 98 | } | |||
| 99 | ||||
| 100 | 99 | bool ButtonDebounce::readPinState2(bool valueWithinThreshold) { | ||
| 101 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 99 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 99 times.
|
99 | if (!isBrainPinValid(*m_pin)) { |
| 102 | ✗ | return false; | ||
| 103 | } | |||
| 104 | 99 | efitick_t timeNowNt = getTimeNowNt(); | ||
| 105 | // If it's been less than the threshold since we were last called | |||
| 106 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 98 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 98 times.
|
99 | if (timeLast.getElapsedNt(timeNowNt) < m_threshold) { |
| 107 | 1 | return valueWithinThreshold; | ||
| 108 | } | |||
| 109 | 98 | bool value = getPhysicalState(); | ||
| 110 | // efiPrintf("[debounce] %s value %d", m_name, value); | |||
| 111 | // Invert | |||
| 112 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 90 times.
|
2/2✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 90 times.
|
98 | if (active_mode == PI_PULLUP) { |
| 113 | 8 | value = !value; | ||
| 114 | // efiPrintf("[debounce] %s inverted %d", m_name, value); | |||
| 115 | } | |||
| 116 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 93 times.
|
2/2✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 93 times.
|
98 | if (value) { |
| 117 | 5 | timeLast.reset(); | ||
| 118 | } | |||
| 119 | 98 | return value; | ||
| 120 | } | |||
| 121 | ||||
| 122 | 93 | 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 | 93 | storedValue = readPinState2(storedValue); | ||
| 129 | 93 | return storedValue; | ||
| 130 | } | |||
| 131 | ||||
| 132 | ✗ | void ButtonDebounce::debug() { | ||
| 133 | ✗ | ButtonDebounce *listItem = s_firstDebounce; | ||
| 134 | ✗ | while (listItem != nullptr) { | ||
| 135 | #if EFI_PROD_CODE || EFI_UNIT_TEST | |||
| 136 | ✗ | efiPrintf("%s timeLast %f", listItem->m_name, listItem->timeLast.getElapsedSeconds()); | ||
| 137 | ✗ | efiPrintf("physical pin state %d", listItem->getPhysicalState()); | ||
| 138 | ✗ | efiPrintf("state %d", listItem->storedValue); | ||
| 139 | ✗ | efiPrintf("mode %d", listItem->active_mode); | ||
| 140 | #endif | |||
| 141 | ||||
| 142 | ✗ | listItem = listItem->nextDebounce; | ||
| 143 | } | |||
| 144 | ✗ | } | ||
| 145 | ||||
| 146 | 585 | void initButtonDebounce() { | ||
| 147 | #if !EFI_UNIT_TEST | |||
| 148 | addConsoleAction("debounce", ButtonDebounce::debug); | |||
| 149 | #endif /* EFI_UNIT_TEST */ | |||
| 150 | 585 | } | ||
| 151 |