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 | 678 | ButtonDebounce::ButtonDebounce(const char *name) | ||
16 | 678 | : m_name(name) | ||
17 | { | |||
18 | 678 | } | ||
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 | 1386 | 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 584 times.
✓ Branch 1 taken 802 times.
|
2/2✓ Decision 'true' taken 584 times.
✓ Decision 'false' taken 802 times.
|
1386 | 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 | 584 | nextDebounce = s_firstDebounce; | ||
29 | 584 | s_firstDebounce = this; | ||
30 | } | |||
31 | 1386 | m_threshold = MS2NT(threshold); | ||
32 | 1386 | m_pin = &pin; | ||
33 | 1386 | m_mode = &mode; | ||
34 | 1386 | m_inverted = inverted; | ||
35 | 1386 | startConfiguration(); | ||
36 | 1386 | isInstanceRegisteredInGlobalList = true; | ||
37 | 1386 | } | ||
38 | ||||
39 | 221 | void ButtonDebounce::stopConfigurationList() { | ||
40 | 221 | ButtonDebounce *listItem = s_firstDebounce; | ||
41 | 221 | int loopLimitCounter = 0; | ||
42 |
2/2✓ Branch 0 taken 221 times.
✓ Branch 1 taken 221 times.
|
2/2✓ Decision 'true' taken 221 times.
✓ Decision 'false' taken 221 times.
|
442 | while (listItem != nullptr) { |
43 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 221 times.
|
221 | criticalAssertVoid(loopLimitCounter++ < 10000, "dead stopConfigurationList?"); | |
44 | 221 | listItem->stopConfiguration(); | ||
45 | 221 | listItem = listItem->nextDebounce; | ||
46 | } | |||
47 | } | |||
48 | ||||
49 | 221 | void ButtonDebounce::startConfigurationList() { | ||
50 | 221 | ButtonDebounce *listItem = s_firstDebounce; | ||
51 | 221 | int loopLimitCounter = 0; | ||
52 |
2/2✓ Branch 0 taken 221 times.
✓ Branch 1 taken 221 times.
|
2/2✓ Decision 'true' taken 221 times.
✓ Decision 'false' taken 221 times.
|
442 | while (listItem != nullptr) { |
53 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 221 times.
|
221 | criticalAssertVoid(loopLimitCounter++ < 10000, "dead startConfigurationList?"); | |
54 | 221 | listItem->startConfiguration(); | ||
55 | 221 | listItem = listItem->nextDebounce; | ||
56 | } | |||
57 | } | |||
58 | ||||
59 | 221 | void ButtonDebounce::stopConfiguration() { | ||
60 | // If the configuration has changed | |||
61 | #if ! EFI_ACTIVE_CONFIGURATION_IN_FLASH | |||
62 |
2/4✓ Branch 0 taken 221 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 221 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 221 times.
|
221 | 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 | 221 | } | ||
72 | ||||
73 | 1607 | 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 | 1607 | active_pin = *m_pin; | ||
81 | 1607 | active_mode = *m_mode; | ||
82 | 1607 | } | ||
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 | 584 | void initButtonDebounce() { | ||
147 | #if !EFI_UNIT_TEST | |||
148 | addConsoleAction("debounce", ButtonDebounce::debug); | |||
149 | #endif /* EFI_UNIT_TEST */ | |||
150 | 584 | } | ||
151 |