GCC Code Coverage Report


Directory: ./
File: firmware/hw_layer/debounce.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 83.6% 56 0 67
Functions: 91.7% 11 0 12
Branches: 70.8% 17 0 24
Decisions: 77.8% 14 - 18

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