| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file boards/hellen/hellen_board_id.cpp | |||
| 3 | * @brief Board-Id detector for Hellen boards | |||
| 4 | * | |||
| 5 | * @author andreika <prometheus.pcb@gmail.com> | |||
| 6 | * @author Andrey Belomutskiy, (c) 2012-2022 | |||
| 7 | * | |||
| 8 | * The main idea is to measure the capacitor charge/discharge time | |||
| 9 | * through a series resistors using standard digital I/O pins. | |||
| 10 | * One pin is used to provide a Vcc(3.3) or Vdd(0) voltage to the capacitor | |||
| 11 | * through a resistor, and another pin is used as a digital input. Then vice versa. | |||
| 12 | * | |||
| 13 | * The algo: | |||
| 14 | * 1) Completely discharge the capacitor (all pins are low) | |||
| 15 | * 2) Charge the capacitor until the voltage crosses the 0->1 voltage threshold (Vt) and measure the charging time #1 (Tc1). | |||
| 16 | * 3) Immediately discharge the capacitor to some unknown low voltage (Vl) - it should be well below the Vt threshold, | |||
| 17 | * using the same period of time used for charging as the discharge period (Td = Tc1). | |||
| 18 | * 4) Immediately charge the capacitor again and measure the time crossing the same 0->1 voltage threshold again (Tc2). | |||
| 19 | * 5) Repeat the procedure several times to get more precise timings. | |||
| 20 | * 6) Do some math and find the R and C values. | |||
| 21 | * 7) Board_Id = the unique combination of indices of the "measured" R1 and R2. | |||
| 22 | * | |||
| 23 | * The math proof: | |||
| 24 | * - Charging formula #1: | |||
| 25 | * Vt = V�� * (1 - exp(-Tc1 / RC)) | |||
| 26 | * - Discharging formula: | |||
| 27 | * Vl = Vt * exp(-Td / RC) | |||
| 28 | * - Charging formula #2: | |||
| 29 | * Vl = V�� * (1 - exp(-Tl / (RC))) | |||
| 30 | * - Where Tl is a charging time from 0 to Vl: | |||
| 31 | * Tl = Tc1 - Tc2 | |||
| 32 | * - Solve the equations: | |||
| 33 | * Vl = V�� * (1 - exp(-Tl / RC)) = Vt * exp(-Td / RC) | |||
| 34 | * V�� * (1 - exp(-Tl / RC)) = V�� * (1 - exp(-Tc1 / RC)) * exp(-Td / RC) | |||
| 35 | * (1 - exp(-Tl / RC)) = (1 - exp(-Tc1 / RC)) * exp(-Td / RC) | |||
| 36 | * - Simplify the equation: | |||
| 37 | * X = exp(-1/(RC)) | |||
| 38 | * (1 - X^Tc1) * X^Td + X^Tl - 1 = 0 | |||
| 39 | * | |||
| 40 | * X^Td - X^(Tc1+Td) + X^(Tc2-Tc1) - 1 = 0 | |||
| 41 | * | |||
| 42 | * Td, Tc1 and Tc2 are known. | |||
| 43 | * - Solve the power function for X and get the desired R or C. | |||
| 44 | * | |||
| 45 | * We use Newton's method (a fast-converging numerical solver when the 1st derivative is known) | |||
| 46 | * with estimated initial values. | |||
| 47 | */ | |||
| 48 | ||||
| 49 | #include "pch.h" | |||
| 50 | #include "hellen_meta.h" | |||
| 51 | #include "digital_input_exti.h" | |||
| 52 | ||||
| 53 | #include "hellen_board_id.h" | |||
| 54 | ||||
| 55 | /* We use known standard E24 series resistor values (1%) to find the closest match. | |||
| 56 | The 16 major values should have a guaranteed spacing of 15% in a row (1% R tolerance + 10% C tolerance) | |||
| 57 | These should match the values in the gen_board_id script! | |||
| 58 | */ | |||
| 59 | #include "hellen_board_id_resistors.h" | |||
| 60 | ||||
| 61 | //#define HELLEN_BOARD_ID_DEBUG | |||
| 62 | ||||
| 63 | // todo: error: this use of "defined" may not be portable [-Werror=expansion-to-defined ?! | |||
| 64 | // huh? #define HELLEN_BOARD_ID_CODE_NEEDED (defined( HELLEN_BOARD_ID_PIN_1) && !defined(HW_HELLEN_SKIP_BOARD_TYPE)) | |||
| 65 | ||||
| 66 | #if EFI_PROD_CODE && defined( HELLEN_BOARD_ID_PIN_1) && !defined(HW_HELLEN_SKIP_BOARD_TYPE) | |||
| 67 | ||||
| 68 | static void hellenBoardIdInputCallback(void *arg, efitick_t nowNt) { | |||
| 69 | UNUSED(arg); | |||
| 70 | HellenBoardIdFinderState *state = (HellenBoardIdFinderState *)arg; | |||
| 71 | // Now start discharging immediately! This should be the first command in the interrupt handler. | |||
| 72 | palClearPad(state->rOutputPinPort, state->rOutputPinIdx); | |||
| 73 | ||||
| 74 | state->timeChargeNt = nowNt; | |||
| 75 | ||||
| 76 | chibios_rt::CriticalSectionLocker csl; | |||
| 77 | chSemSignalI(&state->boardId_wake); // no need to call chSchRescheduleS() because we're inside the ISR | |||
| 78 | } | |||
| 79 | ||||
| 80 | #endif /* EFI_PROD_CODE && HELLEN_BOARD_ID_CODE_NEEDED */ | |||
| 81 | ||||
| 82 | // Newton's numerical method (x is R and y is C, or vice-versa) | |||
| 83 | 4 | float HellenBoardIdSolver::solve(float Tc1, float Tc2, float x0, float y, float deltaX) { | ||
| 84 | // the discharge time equals to the charge time | |||
| 85 | 4 | float Td = Tc1; | ||
| 86 | ||||
| 87 | 4 | float iC = -1.0f / y; | ||
| 88 | 4 | k1 = iC * Td; | ||
| 89 | 4 | k2 = iC * (Tc1 + Td); | ||
| 90 | 4 | k3 = iC * (Tc1 - Tc2); | ||
| 91 | ||||
| 92 | // the same method works for R (if C is known) or C (if R is known) | |||
| 93 |
1/1✓ Branch 2 taken 4 times.
|
4 | auto result = NewtonsMethodSolver::solve(x0, deltaX, 20); | |
| 94 | ||||
| 95 | // since we had https://github.com/rusefi/rusefi/issues/4084 let's add paranoia check | |||
| 96 | // All real cases seem to converge in <= 5 iterations, so we don't need to try more than 20. | |||
| 97 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 4 times.
|
4 | if (!result) { |
| 98 | ✗ | criticalError("hellen boardID is broken"); | ||
| 99 | ✗ | return 0; | ||
| 100 | } | |||
| 101 | ||||
| 102 | 4 | return result.Value; | ||
| 103 | } | |||
| 104 | ||||
| 105 | 9 | float HellenBoardIdFinderBase::findClosestResistor(float R, bool testOnlyMajorSeries, int *rIdx) { | ||
| 106 | // the first "major" resistor uses less values (with more spacing between them) so that even less precise method cannot fail. | |||
| 107 | static const float rOnlyMajorValues[] = { | |||
| 108 | HELLEN_BOARD_ID_MAJOR_RESISTORS | |||
| 109 | }; | |||
| 110 | // the minor resistor is always measured after the major one, when the exact capacitance is already knows, | |||
| 111 | // so we can use more values and detect them with better precision. | |||
| 112 | static const float rAllValues[] = { | |||
| 113 | // these are equal to the major values and should be used first | |||
| 114 | HELLEN_BOARD_ID_MAJOR_RESISTORS | |||
| 115 | // these are extended series if 256 board IDs aren't enough (16*16). | |||
| 116 | HELLEN_BOARD_ID_MINOR_RESISTORS | |||
| 117 | }; | |||
| 118 | ||||
| 119 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
|
9 | size_t rValueSize = testOnlyMajorSeries ? efi::size(rOnlyMajorValues) : efi::size(rAllValues); | |
| 120 | ||||
| 121 | 9 | *rIdx = -1; | ||
| 122 | 9 | float minDelta = 1.e6f; | ||
| 123 |
2/2✓ Branch 0 taken 209 times.
✓ Branch 1 taken 9 times.
|
2/2✓ Decision 'true' taken 209 times.
✓ Decision 'false' taken 9 times.
|
218 | for (size_t i = 0; i < rValueSize; i++) { |
| 124 | // Find the nearest resistor by least ratio error | |||
| 125 | 209 | float delta = std::abs(1 - (R / rAllValues[i])); | ||
| 126 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 170 times.
|
2/2✓ Decision 'true' taken 39 times.
✓ Decision 'false' taken 170 times.
|
209 | if (delta < minDelta) { |
| 127 | 39 | minDelta = delta; | ||
| 128 | 39 | *rIdx = i; | ||
| 129 | #ifdef HELLEN_BOARD_ID_DEBUG | |||
| 130 | efiPrintf("* [%d] R = %.0f, delta = %f", i, rAllValues[i], delta); | |||
| 131 | #endif /* HELLEN_BOARD_ID_DEBUG */ | |||
| 132 | } | |||
| 133 | } | |||
| 134 | 9 | return rAllValues[*rIdx]; | ||
| 135 | } | |||
| 136 | ||||
| 137 | 1 | float HellenBoardIdFinderBase::calcEstimatedResistance(float Tc1_us, float C) { | ||
| 138 | 1 | constexpr float Vcc = 3.3f - 0.1f; // STM32 digital I/O voltage (adjusted for minor voltage drop) | ||
| 139 | 1 | constexpr float V01 = Vcc * 0.5f; // let it be 1.6 volts (closer to the datasheet value), the exact value doesn't matter | ||
| 140 | // macos compiler doesn't like log() in constexpr | |||
| 141 | 1 | float log1V01Vcc = log(1.0f - V01 / Vcc); | ||
| 142 | // this is only an estimated value, we cannot use it for Board-ID detection! | |||
| 143 | 1 | float Rest = -Tc1_us / (C * log1V01Vcc); | ||
| 144 | 1 | return Rest; | ||
| 145 | } | |||
| 146 | ||||
| 147 | 1 | float HellenBoardIdFinderBase::calc(float Tc1_us, float Tc2_us, float Rest, float C, bool testOnlyMajorSeries, float *Rmeasured, float *newC, int *rIdx) { | ||
| 148 | 1 | constexpr float Cest = HELLEN_BOARD_ID_CAPACITOR; | ||
| 149 | // Now calculate the resistance value | |||
| 150 | 1 | HellenBoardIdSolver rSolver; | ||
| 151 | ||||
| 152 | // solve the equation for R (1 Ohm precision is more than enough) | |||
| 153 |
1/1✓ Branch 1 taken 1 time.
|
1 | *Rmeasured = rSolver.solve(Tc1_us, Tc2_us, Rest, C, 1.0f); | |
| 154 | ||||
| 155 | // add 22 Ohms for pin's internal resistance | |||
| 156 | // (according to the STM32 datasheets, the voltage drop on an output pin can be up to 0.4V for 8 mA current) | |||
| 157 | // Actual measured value was is in the low-20s on most chips. | |||
| 158 | 1 | constexpr float Rinternal = 22.0f; | ||
| 159 |
1/1✓ Branch 1 taken 1 time.
|
1 | float R = findClosestResistor(*Rmeasured - Rinternal, testOnlyMajorSeries, rIdx); | |
| 160 | ||||
| 161 | // Find the 'real' capacitance value and use it for the next resistor iteration (gives more precision) | |||
| 162 | 1 | HellenBoardIdSolver cSolver; | ||
| 163 | ||||
| 164 | // We expect the capacitance to be +-10% | |||
| 165 | 1 | constexpr float capacitorPrecision = 0.1f; | ||
| 166 | 1 | constexpr float Cmin = Cest * (1.0f - capacitorPrecision); | ||
| 167 | 1 | constexpr float Cmax = Cest * (1.0f + capacitorPrecision); | ||
| 168 | ||||
| 169 | // solve the equation for C (1% precision) | |||
| 170 |
1/1✓ Branch 1 taken 1 time.
|
1 | *newC = cSolver.solve(Tc1_us, Tc2_us, Cmin, R + Rinternal, 0.01f); | |
| 171 | // in case something went wrong, we must be in the allowed range | |||
| 172 |
1/1✓ Branch 1 taken 1 time.
|
1 | *newC = clampF(Cmin, *newC, Cmax); | |
| 173 | ||||
| 174 | 2 | return R; | ||
| 175 | } | |||
| 176 | ||||
| 177 | template <size_t NumPins> | |||
| 178 | bool HellenBoardIdFinder<NumPins>::measureChargingTimes(int i, float & Tc1_us, float & Tc2_us) { | |||
| 179 | #if EFI_PROD_CODE && defined( HELLEN_BOARD_ID_PIN_1) && !defined(HW_HELLEN_SKIP_BOARD_TYPE) | |||
| 180 | chSemReset(&state.boardId_wake, 0); | |||
| 181 | ||||
| 182 | // full charge/discharge time, and also 'timeout' time | |||
| 183 | const int Tf_us = 50000; // 50 ms is more than enough to "fully" discharge the capacitor with any two resistors used at the same time. | |||
| 184 | ||||
| 185 | // 1. Fully discharge the capacitor through both resistors (faster) | |||
| 186 | for (size_t k = 0; k < NumPins; k++) { | |||
| 187 | palClearPad(getBrainPinPort(rPins[k]), getBrainPinIndex(rPins[k])); | |||
| 188 | palSetPadMode(getBrainPinPort(rPins[k]), getBrainPinIndex(rPins[k]), PAL_MODE_OUTPUT_PUSHPULL); | |||
| 189 | } | |||
| 190 | // wait max. time because we don't know the resistor values yet | |||
| 191 | chThdSleepMicroseconds(Tf_us); | |||
| 192 | ||||
| 193 | // use one pin as an charge/discharge controlling output | |||
| 194 | state.rOutputPinPort = getBrainPinPort(rPins[i]); | |||
| 195 | state.rOutputPinIdx = getBrainPinIndex(rPins[i]); | |||
| 196 | palSetPadMode(state.rOutputPinPort, state.rOutputPinIdx, PAL_MODE_OUTPUT_PUSHPULL); | |||
| 197 | ||||
| 198 | // use another pin as an input to detect 0->1 crossings | |||
| 199 | int inputIdx = 1 - i; | |||
| 200 | state.rInputPinPort = getBrainPinPort(rPins[inputIdx]); | |||
| 201 | state.rInputPinIdx = getBrainPinIndex(rPins[inputIdx]); | |||
| 202 | // set only high-Z input mode, no pull-ups/pull-downs allowed! | |||
| 203 | palSetPadMode(state.rInputPinPort, state.rInputPinIdx, PAL_MODE_INPUT); | |||
| 204 | efiExtiEnablePin("boardId", rPins[inputIdx], PAL_EVENT_MODE_RISING_EDGE, hellenBoardIdInputCallback, (void *)&state); | |||
| 205 | ||||
| 206 | int pinState = palReadPad(state.rInputPinPort, state.rInputPinIdx); | |||
| 207 | if (pinState != 0) { | |||
| 208 | // the input pin state should be low when the capacitor is fully discharged | |||
| 209 | efiPrintf("* Board detection error!"); | |||
| 210 | return false; | |||
| 211 | } | |||
| 212 | ||||
| 213 | // Timestamps: | |||
| 214 | // t1 = Starts charging from 0v | |||
| 215 | // t2 = Threshold reached, starts discharging | |||
| 216 | // t3 = Random voltage reached, starts charging again | |||
| 217 | // t4 = Threshold reached again, process finished. | |||
| 218 | ||||
| 219 | // 2. Start charging until the input pin triggers (V01 threshold is reached) | |||
| 220 | state.timeChargeNt = 0; | |||
| 221 | efitick_t t1 = getTimeNowNt(); | |||
| 222 | palSetPad(state.rOutputPinPort, state.rOutputPinIdx); | |||
| 223 | chSemWaitTimeout(&state.boardId_wake, TIME_US2I(Tf_us)); | |||
| 224 | efitick_t t2 = state.timeChargeNt; | |||
| 225 | ||||
| 226 | // 3. At the moment, the discharging has already been started! | |||
| 227 | // Meanwhile we need to do some checks - until some pre-selected voltage is presumably reached. | |||
| 228 | ||||
| 229 | // if voltage didn't change on the input pin (or changed impossibly fast), then the charging didn't start, | |||
| 230 | // meaning there's no capacitor and/or resistors on these pins. | |||
| 231 | if (t2 - t1 < US2NT(100)) { | |||
| 232 | efiPrintf("* Hellen Board ID circuitry wasn't detected! Aborting!"); | |||
| 233 | return false; | |||
| 234 | } | |||
| 235 | ||||
| 236 | // 4. calculate the first charging time | |||
| 237 | efidur_t Tc1_nt = t2 - t1; | |||
| 238 | Tc1_us = NT2USF(Tc1_nt); | |||
| 239 | // We use the same 'charging time' to discharge the capacitor to some random voltage below the threshold voltage. | |||
| 240 | efidur_t Td_nt = Tc1_nt; | |||
| 241 | ||||
| 242 | // 5. And now just wait for the rest of the discharge process... | |||
| 243 | // Spin wait since chThdSleepMicroseconds() lacks the resolution we need | |||
| 244 | efitick_t t3 = t2 + Td_nt; | |||
| 245 | while (getTimeNowNt() < t3) ; | |||
| 246 | ||||
| 247 | // the input pin state should be low when the capacitor is discharged to Vl | |||
| 248 | pinState = palReadPad(state.rInputPinPort, state.rInputPinIdx); | |||
| 249 | ||||
| 250 | // 6. And immediately begin charging again until the threshold voltage is reached! | |||
| 251 | state.timeChargeNt = 0; | |||
| 252 | palSetPad(state.rOutputPinPort, state.rOutputPinIdx); | |||
| 253 | ||||
| 254 | // Wait for the charging completion | |||
| 255 | chSemReset(&state.boardId_wake, 0); | |||
| 256 | chSemWaitTimeout(&state.boardId_wake, TIME_US2I(Tf_us)); | |||
| 257 | efitick_t t4 = state.timeChargeNt; | |||
| 258 | ||||
| 259 | // 7. calculate the second charge time | |||
| 260 | Tc2_us = NT2USF(t4 - t3); | |||
| 261 | ||||
| 262 | float Td_us = NT2USF(Td_nt); | |||
| 263 | #ifdef HELLEN_BOARD_ID_DEBUG | |||
| 264 | efiPrintf("* dTime2-1 = %d", (int)(t2 - t1)); | |||
| 265 | efiPrintf("* dTime3-2 = %d", (int)(t3 - t2)); | |||
| 266 | efiPrintf("* dTime4-3 = %d", (int)(t4 - t3)); | |||
| 267 | efiPrintf("* Tc1 = %f, Tc2 = %f, Td = %f", Tc1_us, Tc2_us, Td_us); | |||
| 268 | #endif /* HELLEN_BOARD_ID_DEBUG */ | |||
| 269 | ||||
| 270 | // sanity checks | |||
| 271 | if (pinState != 0) { | |||
| 272 | efiPrintf("* Board detection error! (Td=%f is too small)", Td_us); | |||
| 273 | return false; | |||
| 274 | } | |||
| 275 | ||||
| 276 | if (t4 <= t3) { | |||
| 277 | efiPrintf("* Estimates are out of limit! Something went wrong. Aborting!"); | |||
| 278 | return false; | |||
| 279 | } | |||
| 280 | ||||
| 281 | efiExtiDisablePin(rPins[inputIdx]); | |||
| 282 | #endif /* EFI_PROD_CODE */ | |||
| 283 | return true; | |||
| 284 | } | |||
| 285 | ||||
| 286 | template <size_t NumPins> | |||
| 287 | bool HellenBoardIdFinder<NumPins>::measureChargingTimesAveraged(int i, float & Tc1_us, float & Tc2_us) { | |||
| 288 | const int numTries = 3; | |||
| 289 | ||||
| 290 | Tc1_us = 0; | |||
| 291 | Tc2_us = 0; | |||
| 292 | for (int tries = 0; tries < numTries; tries++) { | |||
| 293 | // get the charging times | |||
| 294 | float Tc1i_us = 0, Tc2i_us = 0; | |||
| 295 | if (!measureChargingTimes(i, Tc1i_us, Tc2i_us)) | |||
| 296 | return false; | |||
| 297 | Tc1_us += Tc1i_us; | |||
| 298 | Tc2_us += Tc2i_us; | |||
| 299 | } | |||
| 300 | ||||
| 301 | // averaging | |||
| 302 | Tc1_us /= numTries; | |||
| 303 | Tc2_us /= numTries; | |||
| 304 | ||||
| 305 | return true; | |||
| 306 | } | |||
| 307 | ||||
| 308 | ✗ | int detectHellenBoardId() { | ||
| 309 | ✗ | int boardId = -1; | ||
| 310 | #if defined( HELLEN_BOARD_ID_PIN_1) && !defined(HW_HELLEN_SKIP_BOARD_TYPE) | |||
| 311 | efiPrintf("Starting Hellen Board ID detection..."); | |||
| 312 | Timer t; | |||
| 313 | t.reset(); | |||
| 314 | ||||
| 315 | const int numPins = 2; | |||
| 316 | Gpio rPins[numPins] = { HELLEN_BOARD_ID_PIN_1, HELLEN_BOARD_ID_PIN_2}; | |||
| 317 | ||||
| 318 | // We start from the estimated capacitance, but the real one can be +-10% | |||
| 319 | float C = HELLEN_BOARD_ID_CAPACITOR; | |||
| 320 | ||||
| 321 | // we need to find the resistor values connected to the mcu pins and to the capacitor. | |||
| 322 | float R[numPins] = { 0 }; | |||
| 323 | int rIdx[numPins] = { 0 }; | |||
| 324 | ||||
| 325 | HellenBoardIdFinder<numPins> finder(rPins); | |||
| 326 | ||||
| 327 | // init some ChibiOs objects | |||
| 328 | chSemObjectInit(&finder.state.boardId_wake, 0); | |||
| 329 | ||||
| 330 | // R1 is the first, R2 is the second | |||
| 331 | for (int i = 0; i < numPins; i++) { | |||
| 332 | #ifdef HELLEN_BOARD_ID_DEBUG | |||
| 333 | efiPrintf("*** Resistor R%d...", i + 1); | |||
| 334 | #endif /* HELLEN_BOARD_ID_DEBUG */ | |||
| 335 | ||||
| 336 | float Tc1_us = 0, Tc2_us = 0; | |||
| 337 | // We need several measurements for each resistor to increase the precision. | |||
| 338 | // But if any of the measurements fails, then abort! | |||
| 339 | if (!finder.measureChargingTimesAveraged(i, Tc1_us, Tc2_us)) | |||
| 340 | break; | |||
| 341 | ||||
| 342 | // Now roughly estimate the resistor value using the approximate threshold voltage. | |||
| 343 | float Rest = finder.calcEstimatedResistance(Tc1_us, C); | |||
| 344 | // check if we are inside the range | |||
| 345 | if (Rest < 300.0f || Rest > 15000.0f) { | |||
| 346 | efiPrintf("* Unrealistic estimated resistor value (%f)! Aborting!", Rest); | |||
| 347 | break; | |||
| 348 | } | |||
| 349 | ||||
| 350 | // for the first resistor, we test only "major" values because we don't know the exact capacitance yet | |||
| 351 | bool testOnlyMajorSeries = (i == 0); | |||
| 352 | ||||
| 353 | float Rmeasured, newC; | |||
| 354 | // Now calculate the R and C | |||
| 355 | R[i] = finder.calc(Tc1_us, Tc2_us, Rest, C, testOnlyMajorSeries, &Rmeasured, &newC, &rIdx[i]); | |||
| 356 | C = newC; | |||
| 357 | ||||
| 358 | #ifdef HELLEN_BOARD_ID_DEBUG | |||
| 359 | efiPrintf("* R = %f, Rmeasured = %f, Rest = %f, Creal = %f", R[i], Rmeasured, Rest, C); | |||
| 360 | #endif /* HELLEN_BOARD_ID_DEBUG */ | |||
| 361 | } | |||
| 362 | ||||
| 363 | // in case the process was aborted | |||
| 364 | for (size_t k = 0; k < numPins; k++) { | |||
| 365 | efiExtiDisablePin(rPins[k]); | |||
| 366 | // release the pins | |||
| 367 | palSetPadMode(getBrainPinPort(rPins[k]), getBrainPinIndex(rPins[k]), PAL_MODE_RESET); | |||
| 368 | } | |||
| 369 | ||||
| 370 | float elapsed_Ms = t.getElapsedSeconds() * 1000; | |||
| 371 | ||||
| 372 | // Check that all resistors were actually detected | |||
| 373 | bool allRValid = true; | |||
| 374 | for (size_t i = 0; i < numPins; i++) { | |||
| 375 | allRValid &= R[i] != 0; | |||
| 376 | } | |||
| 377 | ||||
| 378 | // Decode board ID only if all resistors could be decoded, otherwise we return -1 | |||
| 379 | if (allRValid) { | |||
| 380 | boardId = HELLEN_GET_BOARD_ID(rIdx[0], rIdx[1]); | |||
| 381 | } else { | |||
| 382 | boardId = -1; | |||
| 383 | } | |||
| 384 | ||||
| 385 | efiPrintf("* RESULT: BoardId = %d, R1 = %.0f, R2 = %.0f (Elapsed time: %.1f ms)", boardId, R[0], R[1], elapsed_Ms); | |||
| 386 | #endif /* HELLEN_BOARD_ID_PIN_1 */ | |||
| 387 | ✗ | return boardId; | ||
| 388 | } | |||
| 389 |