GCC Code Coverage Report


Directory: ./
File: firmware/hw_layer/hardware.cpp
Date: 2025-10-24 14:26:41
Coverage Exec Excl Total
Lines: 74.5% 35 0 47
Functions: 50.0% 5 0 10
Branches: 50.0% 3 0 6
Decisions: 60.0% 3 - 5

Line Branch Decision Exec Source
1 /**
2 * @file hardware.cpp
3 * @brief Hardware package entry point
4 *
5 * @date May 27, 2013
6 * @author Andrey Belomutskiy, (c) 2012-2020
7 */
8
9 #include "pch.h"
10
11
12 #include "trigger_input.h"
13 #include "can_hw.h"
14 #include "hardware.h"
15 #include "rtc_helper.h"
16 #include "bench_test.h"
17 #include "yaw_rate_sensor.h"
18 #include "pin_repository.h"
19 #include "logic_analyzer.h"
20 #include "smart_gpio.h"
21 #include "accelerometer.h"
22 #include "eficonsole.h"
23 #include "console_io.h"
24 #include "idle_thread.h"
25 #include "odometer.h"
26 #include "kline.h"
27 #include "dac.h"
28
29 #if EFI_PROD_CODE
30 #include "mpu_util.h"
31 #endif /* EFI_PROD_CODE */
32
33 #include "mmc_card.h"
34
35 #include "adc_device.h"
36 #include "idle_hardware.h"
37
38 #include "histogram.h"
39 #include "gps_uart.h"
40 #include "sent.h"
41 #include "cdm_ion_sense.h"
42 #include "trigger_central.h"
43 #include "vvt.h"
44 #include "trigger_emulator_algo.h"
45 #include "boost_control.h"
46 #if EFI_SOFTWARE_KNOCK
47 #include "software_knock.h"
48 #endif
49 #include "trigger_scope.h"
50 #include "init.h"
51 #if EFI_MC33816
52 #include "mc33816.h"
53 #endif /* EFI_MC33816 */
54 #if EFI_WS2812
55 #include "WS2812.h"
56 #endif /* EFI_WS2812 */
57
58 #if EFI_MAP_AVERAGING && defined (MODULE_MAP_AVERAGING)
59 #include "map_averaging.h"
60 #endif
61
62 #if EFI_CONFIGURATION_STORAGE
63 #include "flash_main.h"
64 #endif
65
66 #if HAL_USE_PAL && EFI_PROD_CODE
67 #include "digital_input_exti.h"
68 #endif // HAL_USE_PAL
69
70 #if EFI_CAN_SUPPORT
71 #include "can_vss.h"
72 #endif
73
74 #include "board_overrides.h"
75
76 std::optional<setup_custom_board_overrides_type> custom_board_InitHardwareEarly;
77 std::optional<setup_custom_board_overrides_type> custom_board_InitHardware;
78 std::optional<setup_custom_board_overrides_type> custom_board_InitHardwareExtra;
79 std::optional<setup_custom_board_overrides_type> custom_board_BeforeTuneDefaults;
80
81 std::optional<setup_custom_board_engine_type_type> custom_board_AfterTuneDefaults;
82 std::optional<setup_custom_board_engine_type_type> custom_board_applyUnknownType;
83
84 #if HAL_USE_SPI
85 /* zero index is SPI_NONE */
86 extern bool isSpiInitialized[SPI_TOTAL_COUNT + 1];
87
88 /* these are common adapters for engineConfiguration access, move to some common file? */
89 brain_pin_e getMisoPin(spi_device_e device) {
90 switch(device) {
91 case SPI_DEVICE_1:
92 return engineConfiguration->spi1misoPin;
93 case SPI_DEVICE_2:
94 return engineConfiguration->spi2misoPin;
95 case SPI_DEVICE_3:
96 return engineConfiguration->spi3misoPin;
97 case SPI_DEVICE_4:
98 return engineConfiguration->spi4misoPin;
99 case SPI_DEVICE_5:
100 return engineConfiguration->spi5misoPin;
101 case SPI_DEVICE_6:
102 return engineConfiguration->spi6misoPin;
103 default:
104 break;
105 }
106 return Gpio::Unassigned;
107 }
108
109 brain_pin_e getMosiPin(spi_device_e device) {
110 switch(device) {
111 case SPI_DEVICE_1:
112 return engineConfiguration->spi1mosiPin;
113 case SPI_DEVICE_2:
114 return engineConfiguration->spi2mosiPin;
115 case SPI_DEVICE_3:
116 return engineConfiguration->spi3mosiPin;
117 case SPI_DEVICE_4:
118 return engineConfiguration->spi4mosiPin;
119 case SPI_DEVICE_5:
120 return engineConfiguration->spi5mosiPin;
121 case SPI_DEVICE_6:
122 return engineConfiguration->spi6mosiPin;
123 default:
124 break;
125 }
126 return Gpio::Unassigned;
127 }
128
129 brain_pin_e getSckPin(spi_device_e device) {
130 switch(device) {
131 case SPI_DEVICE_1:
132 return engineConfiguration->spi1sckPin;
133 case SPI_DEVICE_2:
134 return engineConfiguration->spi2sckPin;
135 case SPI_DEVICE_3:
136 return engineConfiguration->spi3sckPin;
137 case SPI_DEVICE_4:
138 return engineConfiguration->spi4sckPin;
139 case SPI_DEVICE_5:
140 return engineConfiguration->spi5sckPin;
141 case SPI_DEVICE_6:
142 return engineConfiguration->spi6sckPin;
143 default:
144 break;
145 }
146 return Gpio::Unassigned;
147 }
148
149 /**
150 * @return NULL if SPI device not specified
151 */
152 SPIDriver * getSpiDevice(spi_device_e spiDevice) {
153 if (spiDevice == SPI_NONE) {
154 return nullptr;
155 }
156 #if STM32_SPI_USE_SPI1
157 if (spiDevice == SPI_DEVICE_1) {
158 return &SPID1;
159 }
160 #endif
161 #if STM32_SPI_USE_SPI2
162 if (spiDevice == SPI_DEVICE_2) {
163 return &SPID2;
164 }
165 #endif
166 #if STM32_SPI_USE_SPI3
167 if (spiDevice == SPI_DEVICE_3) {
168 return &SPID3;
169 }
170 #endif
171 #if STM32_SPI_USE_SPI4
172 if (spiDevice == SPI_DEVICE_4) {
173 return &SPID4;
174 }
175 #endif
176 #if STM32_SPI_USE_SPI5
177 if (spiDevice == SPI_DEVICE_5) {
178 return &SPID5;
179 }
180 #endif
181 #if STM32_SPI_USE_SPI6
182 if (spiDevice == SPI_DEVICE_6) {
183 return &SPID6;
184 }
185 #endif
186 firmwareError(ObdCode::CUSTOM_ERR_UNEXPECTED_SPI, "Unexpected SPI device: %d", spiDevice);
187 return nullptr;
188 }
189
190 /**
191 * Only one consumer can use SPI bus at a given time
192 */
193 void lockSpi(spi_device_e device) {
194 efiAssertVoid(ObdCode::CUSTOM_STACK_SPI, hasLotsOfRemainingStack(), "lockSpi");
195 spiAcquireBus(getSpiDevice(device));
196 }
197
198 void unlockSpi(spi_device_e device) {
199 spiReleaseBus(getSpiDevice(device));
200 }
201
202 static void initSpiModules() {
203 if (engineConfiguration->is_enabled_spi_1) {
204 turnOnSpi(SPI_DEVICE_1);
205 }
206 if (engineConfiguration->is_enabled_spi_2) {
207 turnOnSpi(SPI_DEVICE_2);
208 }
209 if (engineConfiguration->is_enabled_spi_3) {
210 turnOnSpi(SPI_DEVICE_3);
211 }
212 if (engineConfiguration->is_enabled_spi_4) {
213 turnOnSpi(SPI_DEVICE_4);
214 }
215 if (engineConfiguration->is_enabled_spi_5) {
216 turnOnSpi(SPI_DEVICE_5);
217 }
218 if (engineConfiguration->is_enabled_spi_6) {
219 turnOnSpi(SPI_DEVICE_6);
220 }
221 }
222
223 void stopSpi(spi_device_e device) {
224 if (!isSpiInitialized[device]) {
225 return; // not turned on
226 }
227 isSpiInitialized[device] = false;
228 efiSetPadUnused(getSckPin(device));
229 efiSetPadUnused(getMisoPin(device));
230 efiSetPadUnused(getMosiPin(device));
231 }
232
233 static void stopSpiModules() {
234 if (isConfigurationChanged(is_enabled_spi_1)) {
235 stopSpi(SPI_DEVICE_1);
236 }
237
238 if (isConfigurationChanged(is_enabled_spi_2)) {
239 stopSpi(SPI_DEVICE_2);
240 }
241
242 if (isConfigurationChanged(is_enabled_spi_3)) {
243 stopSpi(SPI_DEVICE_3);
244 }
245
246 if (isConfigurationChanged(is_enabled_spi_4)) {
247 stopSpi(SPI_DEVICE_4);
248 }
249
250 if (isConfigurationChanged(is_enabled_spi_5)) {
251 stopSpi(SPI_DEVICE_5);
252 }
253
254 if (isConfigurationChanged(is_enabled_spi_6)) {
255 stopSpi(SPI_DEVICE_6);
256 }
257 }
258
259 void printSpiConfig(const char *msg, spi_device_e device) {
260 efiPrintf("%s %s mosi=%s", msg, getSpi_device_e(device), hwPortname(getMosiPin(device)));
261 efiPrintf("%s %s miso=%s", msg, getSpi_device_e(device), hwPortname(getMisoPin(device)));
262 efiPrintf("%s %s sck=%s", msg, getSpi_device_e(device), hwPortname(getSckPin(device)));
263 }
264
265 #endif // HAL_USE_SPI
266
267 #if HAL_USE_ADC
268
269 static AdcToken fastMapSampleIndex;
270
271 #if HAL_TRIGGER_USE_ADC
272 static AdcToken triggerSampleIndex;
273 #endif // HAL_TRIGGER_USE_ADC
274
275 /**
276 * This method is not in the adc* lower-level file because it is more business logic then hardware.
277 */
278 void onFastAdcComplete(adcsample_t*) {
279 ScopePerf perf(PE::AdcCallbackFast);
280
281 #if HAL_TRIGGER_USE_ADC
282 // we need to call this ASAP, because trigger processing is time-critical
283 triggerAdcCallback(getFastAdc(triggerSampleIndex));
284 #endif /* HAL_TRIGGER_USE_ADC */
285
286 /**
287 * this callback is executed 10 000 times a second, it needs to be as fast as possible
288 */
289 efiAssertVoid(ObdCode::CUSTOM_STACK_ADC, hasLotsOfRemainingStack(), "lowstck#9b");
290
291 auto mapRaw = adcRawValueToScaledVoltage(getFastAdc(fastMapSampleIndex), engineConfiguration->map.sensor.hwChannel);
292 engine->outputChannels.rawMapFast = mapRaw;
293 #if EFI_MAP_AVERAGING && defined (MODULE_MAP_AVERAGING)
294 mapAveragingAdcCallback(mapRaw);
295 #endif /* EFI_MAP_AVERAGING */
296 }
297 #endif /* HAL_USE_ADC */
298
299
1/1
✓ Decision 'true' taken 805 times.
805 static void calcFastAdcIndexes() {
300 #if HAL_USE_ADC
301 fastMapSampleIndex = enableFastAdcChannel("Fast MAP", engineConfiguration->map.sensor.hwChannel);
302
303 #if HAL_TRIGGER_USE_ADC
304 triggerSampleIndex = enableFastAdcChannel("Trigger ADC", getAdcChannelForTrigger());
305 #endif /* HAL_TRIGGER_USE_ADC */
306
307 #endif/* HAL_USE_ADC */
308 805 }
309
310 /**
311 * this method is NOT currently invoked on ECU start
312 * todo: reduce code duplication by moving more logic into startHardware method
313 */
314 221 void applyNewHardwareSettings() {
315 /**
316 * All 'stop' methods need to go before we begin starting pins.
317 *
318 * We take settings from 'activeConfiguration' not 'engineConfiguration' while stopping hardware.
319 * Some hardware is restart unconditionally on change of parameters while for some systems we make extra effort and restart only
320 * relevant settings were changes.
321 *
322 */
323 221 ButtonDebounce::stopConfigurationList();
324
325 #if EFI_PROD_CODE
326 stopSensors();
327 #endif // EFI_PROD_CODE
328
329 #if EFI_PROD_CODE && EFI_SHAFT_POSITION_INPUT
330 stopTriggerInputPins();
331 #endif /* EFI_SHAFT_POSITION_INPUT */
332
333 #if EFI_PROD_CODE && EFI_SENT_SUPPORT
334 stopSent();
335 #endif // EFI_SENT_SUPPORT
336
337 #if EFI_CAN_SUPPORT
338 stopCanPins();
339 #endif /* EFI_CAN_SUPPORT */
340
341 221 stopKLine();
342
343
344 221 stopHardware();
345
346 #if HAL_USE_SPI
347 stopSpiModules();
348 #endif /* HAL_USE_SPI */
349
350
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 (isPinOrModeChanged(clutchUpPin, clutchUpPinMode)) {
351 // bug? duplication with stopSwitchPins?
352 efiSetPadUnused(activeConfiguration.clutchUpPin);
353 }
354
355 221 enginePins.unregisterPins();
356
357 #if EFI_PROD_CODE
358 reconfigureSensors();
359 #endif /* EFI_PROD_CODE */
360
361 221 ButtonDebounce::startConfigurationList();
362
363 /*******************************************
364 * Start everything back with new settings *
365 ******************************************/
366 221 startHardware();
367
368 #if EFI_PROD_CODE && (BOARD_EXT_GPIOCHIPS > 0)
369 /* TODO: properly restart gpio chips...
370 * This is only workaround for "CS pin lost" bug
371 * see: https://github.com/rusefi/rusefi/issues/2107
372 * We should provide better way to gracefully stop all
373 * gpio chips: set outputs to safe state, release all
374 * on-chip resources (gpios, SPIs, etc) and then restart
375 * with updated settings.
376 * Following code just re-inits CS pins for all external
377 * gpio chips, but does not update CS pin definition in
378 * gpio chips private data/settings. So changing CS pin
379 * on-fly does not work */
380 startSmartCsPins();
381 #endif /* (BOARD_EXT_GPIOCHIPS > 0) */
382
383 221 startKLine();
384
385 #if EFI_PROD_CODE && EFI_IDLE_CONTROL
386 if (isIdleHardwareRestartNeeded()) {
387 initIdleHardware();
388 }
389 #endif
390
391 #if EFI_BOOST_CONTROL
392 221 startBoostPin();
393 #endif
394 #if EFI_EMULATE_POSITION_SENSORS
395 221 startTriggerEmulatorPins();
396 #endif /* EFI_EMULATE_POSITION_SENSORS */
397 #if EFI_LOGIC_ANALYZER
398 startLogicAnalyzerPins();
399 #endif /* EFI_LOGIC_ANALYZER */
400 #if EFI_VVT_PID
401 startVvtControlPins();
402 #endif /* EFI_VVT_PID */
403
404 #if EFI_PROD_CODE && EFI_SENT_SUPPORT
405 startSent();
406 #endif
407
408 221 calcFastAdcIndexes();
409 221 }
410
411 #if EFI_PROD_CODE && EFI_BOR_LEVEL
412 void setBor(int borValue) {
413 efiPrintf("setting BOR to %d", borValue);
414 BOR_Set((BOR_Level_t)borValue);
415 }
416 #endif /* EFI_BOR_LEVEL */
417
418 // Called before configuration is loaded
419 void boardInitHardwareEarly() {
420 // forcing migration to custom_board_InitHardwareEarly
421 }
422 void boardInitHardware() {
423 // time to force migration to custom_board_InitHardware
424 }
425 void boardInitHardwareExtra() {
426 // forcing migration to custom_board_InitHardwareExtra
427 }
428
429 // This function initializes hardware that can do so before configuration is loaded
430 void initHardwareNoConfig() {
431 efiAssertVoid(ObdCode::CUSTOM_IH_STACK, hasLotsOfRemainingStack(), "init h");
432
433 efiPrintf("initHardware()");
434
435 #if EFI_PROD_CODE
436 initPinRepository();
437 #endif
438
439 #if EFI_PROD_CODE
440 call_board_override(custom_board_InitHardwareEarly);
441 #endif
442
443 #if EFI_HISTOGRAMS
444 /**
445 * histograms is a data structure for CPU monitor, it does not depend on configuration
446 */
447 initHistogramsModule();
448 #endif /* EFI_HISTOGRAMS */
449
450 #if EFI_GPIO_HARDWARE
451 /**
452 * We need the LED_ERROR pin even before we read configuration
453 */
454 initPrimaryPins();
455 #endif // EFI_GPIO_HARDWARE
456
457 #if EFI_PROD_CODE && EFI_SIGNAL_EXECUTOR_ONE_TIMER
458 // it's important to initialize this pretty early in the game before any scheduling usages
459 initSingleTimerExecutorHardware();
460 #endif // EFI_PROD_CODE && EFI_SIGNAL_EXECUTOR_ONE_TIMER
461
462 #if EFI_PROD_CODE && EFI_RTC
463 initRtc();
464 #endif // EFI_PROD_CODE && EFI_RTC
465
466 #if EFI_CONFIGURATION_STORAGE
467 initFlash();
468 #endif
469
470 #if EFI_FILE_LOGGING
471 initEarlyMmcCard();
472 #endif // EFI_FILE_LOGGING
473
474 #if HAL_USE_PAL && EFI_PROD_CODE
475 // this should be initialized before detectBoardType()
476 efiExtiInit();
477 #endif // HAL_USE_PAL
478 }
479
480 221 void stopHardware() {
481 221 stopSwitchPins();
482
483 #if EFI_PROD_CODE && (BOARD_EXT_GPIOCHIPS > 0)
484 stopSmartCsPins();
485 #endif /* (BOARD_EXT_GPIOCHIPS > 0) */
486
487 #if EFI_LOGIC_ANALYZER
488 stopLogicAnalyzerPins();
489 #endif /* EFI_LOGIC_ANALYZER */
490
491 #if EFI_EMULATE_POSITION_SENSORS
492 221 stopTriggerEmulatorPins();
493 #endif /* EFI_EMULATE_POSITION_SENSORS */
494
495 #if EFI_VVT_PID
496 stopVvtControlPins();
497 #endif /* EFI_VVT_PID */
498 221 }
499
500 /**
501 * This method is invoked both on ECU start and configuration change
502 * At the moment we have too many system which handle ECU start and configuration change separately
503 * TODO: move move hardware code here
504 */
505 805 void startHardware() {
506 #if EFI_SHAFT_POSITION_INPUT
507 805 initStartStopButton();
508 #endif
509
510 #if EFI_PROD_CODE && EFI_SHAFT_POSITION_INPUT
511 startTriggerInputPins();
512 #endif /* EFI_SHAFT_POSITION_INPUT */
513
514 #if EFI_ENGINE_CONTROL
515 805 enginePins.startPins();
516 #endif /* EFI_ENGINE_CONTROL */
517
518 #if EFI_SHAFT_POSITION_INPUT
519 805 validateTriggerInputs();
520
521 #endif // EFI_SHAFT_POSITION_INPUT
522
523 805 startSwitchPins();
524
525 #if EFI_CAN_SUPPORT
526 startCanPins();
527 #endif /* EFI_CAN_SUPPORT */
528 805 }
529
530 PUBLIC_API_WEAK void setPinConfigurationOverrides() { }
531
532 #if HAL_USE_I2C
533 const I2CConfig i2cfg = {
534 OPMODE_I2C,
535 400000,
536 FAST_DUTY_CYCLE_2,
537 };
538 #endif
539
540 584 void initHardware() {
541
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 584 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 584 times.
584 if (hasFirmwareError()) {
542 return;
543 }
544
545 #if EFI_PROD_CODE && STM32_I2C_USE_I2C3
546 if (engineConfiguration->useEeprom) {
547 i2cStart(&EE_U2CD, &i2cfg);
548 }
549 #endif // STM32_I2C_USE_I2C3
550
551 584 call_board_override(custom_board_InitHardware);
552 #if EFI_PROD_CODE
553 // this applies some board configurations
554 call_board_override(custom_board_OnConfigurationChange, nullptr);
555 #endif // EFI_PROD_CODE
556
557 584 call_board_override(custom_board_InitHardwareExtra);
558
559 #if HAL_USE_ADC
560 initAdcInputs();
561 #endif /* HAL_USE_ADC */
562
563 #if EFI_SOFTWARE_KNOCK
564 initSoftwareKnock();
565 #endif /* EFI_SOFTWARE_KNOCK */
566
567 #ifdef TRIGGER_SCOPE
568 initTriggerScope();
569 #endif // TRIGGER_SCOPE
570
571 #if HAL_USE_SPI
572 initSpiModules();
573 #endif /* HAL_USE_SPI */
574
575 #if (EFI_PROD_CODE && BOARD_EXT_GPIOCHIPS > 0) || EFI_SIMULATOR
576 // initSmartGpio depends on 'initSpiModules'
577 initSmartGpio();
578 #endif
579
580 // output pins potentially depend on 'initSmartGpio'
581 584 initMiscOutputPins();
582
583 #if EFI_MC33816
584 initMc33816();
585 #endif /* EFI_MC33816 */
586
587 #if EFI_CAN_SUPPORT
588 #if EFI_SIMULATOR
589 // Set CAN device name
590 CAND1.deviceName = "can0";
591 #endif
592
593 initCan();
594 #endif /* EFI_CAN_SUPPORT */
595
596
597 #if EFI_PROD_CODE && EFI_SHAFT_POSITION_INPUT
598 onEcuStartTriggerImplementation();
599 #endif /* EFI_SHAFT_POSITION_INPUT */
600 584 onEcuStartDoSomethingTriggerInputPins();
601
602 #if EFI_WS2812
603 initWS2812();
604 #endif /* EFI_LED_WS2812 */
605
606 #if EFI_ONBOARD_MEMS
607 initAccelerometer();
608 #endif
609
610 #if EFI_BOSCH_YAW
611 initBoschYawRateSensor();
612 #endif /* EFI_BOSCH_YAW */
613
614 #if EFI_UART_GPS
615 initGps();
616 #endif
617
618 #if EFI_CAN_SUPPORT
619 initCanVssSupport();
620 #endif // EFI_CAN_SUPPORT
621
622 #if EFI_CDM_INTEGRATION
623 cdmIonInit();
624 #endif // EFI_CDM_INTEGRATION
625
626 #if EFI_PROD_CODE && EFI_SENT_SUPPORT
627 initSent();
628 #endif
629
630 584 initKLine();
631
632 #if EFI_DAC
633 initDac();
634 #endif
635
636 584 calcFastAdcIndexes();
637
638 584 startHardware();
639
640 584 efiPrintf("initHardware() OK!");
641 }
642