| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /* | |||
| 2 | * file can_bench_test.cpp | |||
| 3 | * see also https://github.com/rusefi/rusefi/wiki/CAN BENCH_TEST_BASE_ADDRESS 0x770000 | |||
| 4 | * | |||
| 5 | * primary recipient is https://github.com/rusefi/rusefi-hardware/tree/main/digital-inputs/firmware | |||
| 6 | * | |||
| 7 | * todo: shall we not broadcast by default but wait until stim firmware wakes us up? | |||
| 8 | */ | |||
| 9 | ||||
| 10 | #include "pch.h" | |||
| 11 | #include "bench_test.h" | |||
| 12 | #include "board_id.h" | |||
| 13 | #include "can_bench_test.h" | |||
| 14 | #include "can_msg_tx.h" | |||
| 15 | #include "can_common.h" | |||
| 16 | #include "frequency_sensor.h" | |||
| 17 | #include "settings.h" | |||
| 18 | #include "gpio/gpio_ext.h" | |||
| 19 | ||||
| 20 | #ifdef HW_HELLEN | |||
| 21 | #include "hellen_meta.h" | |||
| 22 | #endif // HW_HELLEN | |||
| 23 | ||||
| 24 | extern PinRepository pinRepository; | |||
| 25 | ||||
| 26 | // todo: WHAT?! document why do we manually truncate higher bits? | |||
| 27 | #define TRUNCATE_TO_BYTE(i) ((i) & 0xff) | |||
| 28 | // raw values are 0..5V, convert it to 8-bit (0..255) | |||
| 29 | #define RAW_TO_BYTE(v) TRUNCATE_TO_BYTE((int)(v * 255.0 / 5.0)) | |||
| 30 | ||||
| 31 | #if EFI_PROD_CODE | |||
| 32 | /** | |||
| 33 | * QC direct output control API is used by https://github.com/rusefi/stim test device | |||
| 34 | * quite different from bench testing user functionality: QC direct should never be engaged on a real vehicle | |||
| 35 | * Once QC direct control mode is activated the only way out is to reboot the unit! | |||
| 36 | */ | |||
| 37 | static bool qcDirectPinControlMode = false; | |||
| 38 | #endif | |||
| 39 | ||||
| 40 | 42123 | /*board public API*/bool isHwQcMode() { | ||
| 41 | #if EFI_PROD_CODE | |||
| 42 | return qcDirectPinControlMode; | |||
| 43 | #else | |||
| 44 | 42123 | return false; | ||
| 45 | #endif // EFI_PROD_CODE | |||
| 46 | } | |||
| 47 | ||||
| 48 | ✗ | void setHwQcMode() { | ||
| 49 | #if EFI_PROD_CODE | |||
| 50 | qcDirectPinControlMode = true; | |||
| 51 | #if HW_HELLEN | |||
| 52 | if (!getHellenBoardEnabled()) { | |||
| 53 | hellenEnableEn("HW QC"); | |||
| 54 | } | |||
| 55 | #endif // HW_HELLEN | |||
| 56 | #endif // EFI_PROD_CODE | |||
| 57 | ✗ | } | ||
| 58 | ||||
| 59 | #if EFI_CAN_SUPPORT | |||
| 60 | ||||
| 61 | static void directWritePad(Gpio pin, int value, const char *msg = "") { | |||
| 62 | if (!isBrainPinValid(pin)) { | |||
| 63 | criticalError("QC of invalid pin %d %s", (int)pin, msg); | |||
| 64 | return; | |||
| 65 | } | |||
| 66 | ||||
| 67 | #if EFI_GPIO_HARDWARE && EFI_PROD_CODE | |||
| 68 | if (brain_pin_is_onchip(pin)) { | |||
| 69 | palWritePad(getHwPort("can_write", pin), getHwPin("can_write", pin), value); | |||
| 70 | } else { | |||
| 71 | #if (BOARD_EXT_GPIOCHIPS > 0) | |||
| 72 | gpiochips_writePad(pin, value); | |||
| 73 | #endif | |||
| 74 | } | |||
| 75 | #endif // EFI_GPIO_HARDWARE && EFI_PROD_CODE | |||
| 76 | } | |||
| 77 | ||||
| 78 | static void qcSetEtbState(uint8_t dcIndex, uint8_t direction) { | |||
| 79 | setHwQcMode(); | |||
| 80 | const dc_io *io = &engineConfiguration->etbIo[dcIndex]; | |||
| 81 | Gpio controlPin = io->controlPin; | |||
| 82 | directWritePad(controlPin, 1, "DC control"); | |||
| 83 | if (engineConfiguration->etb_use_two_wires) { | |||
| 84 | // TLE7209 and L6205 | |||
| 85 | // let's force proper pin mode to work around potentially uninitialized subsystem | |||
| 86 | efiSetPadModeWithoutOwnershipAcquisition("QC_ETB_1", io->directionPin1, PAL_MODE_OUTPUT_PUSHPULL); | |||
| 87 | efiSetPadModeWithoutOwnershipAcquisition("QC_ETB_2", io->directionPin2, PAL_MODE_OUTPUT_PUSHPULL); | |||
| 88 | ||||
| 89 | directWritePad(io->directionPin1, direction, "DC dir1"); | |||
| 90 | directWritePad(io->directionPin2, !direction, "DC dir2"); | |||
| 91 | } else { | |||
| 92 | // TLE9201 and VNH2SP30 | |||
| 93 | efiSetPadModeWithoutOwnershipAcquisition("QC_ETB", controlPin, PAL_MODE_OUTPUT_PUSHPULL); | |||
| 94 | directWritePad(io->directionPin1, direction, "DC dir"); | |||
| 95 | directWritePad(io->disablePin, 0, "DC dis"); // disable pin is inverted - here we ENABLE. direct pin access due to qcDirectPinControlMode | |||
| 96 | } | |||
| 97 | } | |||
| 98 | ||||
| 99 | static void setPin(const CANRxFrame& frame, int value) { | |||
| 100 | size_t outputIndex = frame.data8[2]; | |||
| 101 | if (outputIndex >= getBoardMetaOutputsCount()) { | |||
| 102 | criticalError("QC pin index %d out of range", outputIndex); | |||
| 103 | return; | |||
| 104 | } | |||
| 105 | #if EFI_GPIO_HARDWARE && EFI_PROD_CODE | |||
| 106 | Gpio* boardOutputs = getBoardMetaOutputs(); | |||
| 107 | criticalAssertVoid(boardOutputs != nullptr, "outputs not defined"); | |||
| 108 | Gpio pin = boardOutputs[outputIndex]; | |||
| 109 | ||||
| 110 | int hwIndex = brainPin_to_index(pin); | |||
| 111 | if (pinRepository.getBrainUsedPin(hwIndex) == nullptr) { | |||
| 112 | // if pin is assigned we better configure it | |||
| 113 | efiSetPadModeWithoutOwnershipAcquisition("QC_SET", pin, PAL_MODE_OUTPUT_PUSHPULL); | |||
| 114 | } | |||
| 115 | ||||
| 116 | directWritePad(pin, value); | |||
| 117 | #endif // EFI_GPIO_HARDWARE && EFI_PROD_CODE | |||
| 118 | } | |||
| 119 | ||||
| 120 | void sendQcBenchEventCounters(size_t bus) { | |||
| 121 | #if EFI_SHAFT_POSITION_INPUT | |||
| 122 | CanTxMessage msg(CanCategory::BENCH_TEST, (int)bench_test_packet_ids_e::EVENT_COUNTERS, 8, bus, /*isExtended*/true); | |||
| 123 | ||||
| 124 | int primaryFall = engine->triggerCentral.getHwEventCounter((int)SHAFT_PRIMARY_FALLING); | |||
| 125 | int primaryRise = engine->triggerCentral.getHwEventCounter((int)SHAFT_PRIMARY_RISING); | |||
| 126 | int secondaryFall = engine->triggerCentral.getHwEventCounter((int)SHAFT_SECONDARY_FALLING); | |||
| 127 | int secondaryRise = engine->triggerCentral.getHwEventCounter((int)SHAFT_SECONDARY_RISING); | |||
| 128 | ||||
| 129 | msg[0] = TRUNCATE_TO_BYTE(primaryRise + primaryFall); | |||
| 130 | msg[1] = TRUNCATE_TO_BYTE(secondaryRise + secondaryFall); | |||
| 131 | ||||
| 132 | for (int camIdx = 0; camIdx < 4; camIdx++) { | |||
| 133 | int vvtRise = 0, vvtFall = 0; | |||
| 134 | if (camIdx < CAM_INPUTS_COUNT) { | |||
| 135 | vvtRise = engine->triggerCentral.vvtEventRiseCounter[camIdx]; | |||
| 136 | vvtFall = engine->triggerCentral.vvtEventFallCounter[camIdx]; | |||
| 137 | } | |||
| 138 | ||||
| 139 | msg[2 + camIdx] = TRUNCATE_TO_BYTE(vvtRise + vvtFall); | |||
| 140 | } | |||
| 141 | ||||
| 142 | extern FrequencySensor vehicleSpeedSensor; | |||
| 143 | msg[6] = TRUNCATE_TO_BYTE(vehicleSpeedSensor.eventCounter); | |||
| 144 | #endif // EFI_SHAFT_POSITION_INPUT | |||
| 145 | } | |||
| 146 | ||||
| 147 | void sendQcBenchButtonCounters() { | |||
| 148 | CanTxMessage msg(CanCategory::BENCH_TEST, (int)bench_test_packet_ids_e::BUTTON_COUNTERS, 8, /*bus*/0, /*isExtended*/true); | |||
| 149 | msg[0] = TRUNCATE_TO_BYTE(engine->brakePedalSwitchedState.getCounter()); | |||
| 150 | msg[1] = TRUNCATE_TO_BYTE(engine->clutchUpSwitchedState.getCounter()); | |||
| 151 | msg[2] = TRUNCATE_TO_BYTE(engine->acButtonSwitchedState.getCounter()); | |||
| 152 | // todo: start button | |||
| 153 | } | |||
| 154 | ||||
| 155 | void sendQcBenchAuxDigitalCounters() { | |||
| 156 | CanTxMessage msg(CanCategory::BENCH_TEST, (int)bench_test_packet_ids_e::AUX_DIGITAL_COUNTERS, 8, /*bus*/0, /*isExtended*/true); | |||
| 157 | for (int i =0;i<LUA_DIGITAL_INPUT_COUNT;i++) { | |||
| 158 | msg[i] = TRUNCATE_TO_BYTE(engine->luaDigitalInputState[i].state.getCounter()); | |||
| 159 | } | |||
| 160 | } | |||
| 161 | ||||
| 162 | void sendQcBenchRawAnalogValues(size_t bus) { | |||
| 163 | const float values_1[] = { | |||
| 164 | Sensor::getRaw(SensorType::Tps1Primary), | |||
| 165 | Sensor::getRaw(SensorType::Tps1Secondary), | |||
| 166 | Sensor::getRaw(SensorType::AcceleratorPedalPrimary), | |||
| 167 | Sensor::getRaw(SensorType::AcceleratorPedalSecondary), | |||
| 168 | Sensor::getRaw(SensorType::MapSlow), | |||
| 169 | Sensor::getRaw(SensorType::Clt), | |||
| 170 | Sensor::getRaw(SensorType::Iat), | |||
| 171 | Sensor::getRaw(SensorType::BatteryVoltage), | |||
| 172 | }; | |||
| 173 | ||||
| 174 | const float values_2[] = { | |||
| 175 | Sensor::getRaw(SensorType::Tps2Primary), | |||
| 176 | Sensor::getRaw(SensorType::Tps2Secondary), | |||
| 177 | Sensor::getRaw(SensorType::AuxLinear1), | |||
| 178 | Sensor::getRaw(SensorType::AuxLinear2), | |||
| 179 | Sensor::getRaw(SensorType::OilPressure), | |||
| 180 | Sensor::getRaw(SensorType::FuelPressureLow), | |||
| 181 | Sensor::getRaw(SensorType::FuelPressureHigh), | |||
| 182 | Sensor::getRaw(SensorType::AuxTemp1), | |||
| 183 | }; | |||
| 184 | const float lua_values_1[] = { | |||
| 185 | Sensor::getRaw(SensorType::AuxAnalog1), | |||
| 186 | Sensor::getRaw(SensorType::AuxAnalog2), | |||
| 187 | Sensor::getRaw(SensorType::AuxAnalog3), | |||
| 188 | Sensor::getRaw(SensorType::AuxAnalog4), | |||
| 189 | Sensor::getRaw(SensorType::AuxAnalog5), | |||
| 190 | Sensor::getRaw(SensorType::AuxAnalog6), | |||
| 191 | Sensor::getRaw(SensorType::AuxAnalog7), | |||
| 192 | Sensor::getRaw(SensorType::AuxAnalog8), | |||
| 193 | }; | |||
| 194 | static_assert(efi::size(values_1) <= 8); | |||
| 195 | static_assert(efi::size(values_2) <= 8); | |||
| 196 | static_assert(efi::size(lua_values_1) <= 8); | |||
| 197 | ||||
| 198 | ||||
| 199 | // send the first packet | |||
| 200 | { | |||
| 201 | CanTxMessage msg(CanCategory::BENCH_TEST, (int)bench_test_packet_ids_e::RAW_ANALOG_1, 8, bus, /*isExtended*/true); | |||
| 202 | for (size_t valueIdx = 0; valueIdx < efi::size(values_1); valueIdx++) { | |||
| 203 | msg[valueIdx] = RAW_TO_BYTE(values_1[valueIdx]); | |||
| 204 | } | |||
| 205 | } | |||
| 206 | { | |||
| 207 | CanTxMessage msg(CanCategory::BENCH_TEST, (int)bench_test_packet_ids_e::RAW_ANALOG_2, 8, bus, /*isExtended*/true); | |||
| 208 | for (size_t valueIdx = 0; valueIdx < efi::size(values_2); valueIdx++) { | |||
| 209 | msg[valueIdx] = RAW_TO_BYTE(values_2[valueIdx]); | |||
| 210 | } | |||
| 211 | } | |||
| 212 | // todo: time to extract method already? | |||
| 213 | { | |||
| 214 | CanTxMessage msg(CanCategory::BENCH_TEST, (int)bench_test_packet_ids_e::RAW_LUA_ANALOG_1, 8, bus, /*isExtended*/true); | |||
| 215 | for (size_t valueIdx = 0; valueIdx < efi::size(lua_values_1); valueIdx++) { | |||
| 216 | msg[valueIdx] = RAW_TO_BYTE(lua_values_1[valueIdx]); | |||
| 217 | } | |||
| 218 | } | |||
| 219 | } | |||
| 220 | ||||
| 221 | static void sendOutBoardMeta(size_t bus) { | |||
| 222 | #if EFI_PROD_CODE | |||
| 223 | CanTxMessage msg(CanCategory::BENCH_TEST, (int)bench_test_packet_ids_e::IO_META_INFO, 8, bus, /*isExtended*/true); | |||
| 224 | msg[0] = (int)bench_test_magic_numbers_e::BENCH_HEADER; | |||
| 225 | msg[1] = 0; | |||
| 226 | msg[2] = getBoardMetaOutputsCount(); | |||
| 227 | msg[3] = getBoardMetaLowSideOutputsCount(); | |||
| 228 | msg[4] = getBoardMetaDcOutputsCount(); | |||
| 229 | #endif // EFI_PROD_CODE | |||
| 230 | } | |||
| 231 | ||||
| 232 | void sendQcBenchBoardStatus(size_t bus) { | |||
| 233 | #if EFI_PROD_CODE | |||
| 234 | CanTxMessage msg(CanCategory::BENCH_TEST, (int)bench_test_packet_ids_e::BOARD_STATUS, 8, bus, /*isExtended*/true); | |||
| 235 | ||||
| 236 | int boardId = getBoardId(); | |||
| 237 | msg[0] = TRUNCATE_TO_BYTE(boardId >> 8); | |||
| 238 | msg[1] = TRUNCATE_TO_BYTE(boardId); | |||
| 239 | ||||
| 240 | int numSecondsSinceReset = getTimeNowS(); | |||
| 241 | msg[2] = TRUNCATE_TO_BYTE(numSecondsSinceReset >> 16); | |||
| 242 | msg[3] = TRUNCATE_TO_BYTE(numSecondsSinceReset >> 8); | |||
| 243 | msg[4] = TRUNCATE_TO_BYTE(numSecondsSinceReset); | |||
| 244 | ||||
| 245 | int engineType = (int) engineConfiguration->engineType; | |||
| 246 | msg[5] = engineType >> 8; | |||
| 247 | msg[6] = engineType; | |||
| 248 | sendOutBoardMeta(bus); | |||
| 249 | #endif // EFI_PROD_CODE | |||
| 250 | } | |||
| 251 | ||||
| 252 | #if EFI_PROD_CODE | |||
| 253 | static void sendManualPinTest(int id) { | |||
| 254 | CanTxMessage msg(CanCategory::BENCH_TEST, (int)bench_test_packet_ids_e::MANUAL_PIN_TEST, 8, /*bus*/0, /*isExtended*/true); | |||
| 255 | msg[0] = id; | |||
| 256 | } | |||
| 257 | #endif // EFI_PROD_CODE | |||
| 258 | ||||
| 259 | static void sendPinStatePackets(int pinToggleCounter, uint32_t durationsInStateMs[2]) { | |||
| 260 | CanTxMessage msg(CanCategory::BENCH_TEST, (int)bench_test_packet_ids_e::PIN_STATE, 8, /*bus*/0, /*isExtended*/true); | |||
| 261 | msg[0] = TRUNCATE_TO_BYTE(pinToggleCounter >> 8); | |||
| 262 | msg[1] = TRUNCATE_TO_BYTE(pinToggleCounter); | |||
| 263 | ||||
| 264 | for (int i = 0, mIdx = 2; i < 2; i++) { | |||
| 265 | msg[mIdx++] = TRUNCATE_TO_BYTE(durationsInStateMs[i] >> 16); | |||
| 266 | msg[mIdx++] = TRUNCATE_TO_BYTE(durationsInStateMs[i] >> 8); | |||
| 267 | msg[mIdx++] = TRUNCATE_TO_BYTE(durationsInStateMs[i]); | |||
| 268 | } | |||
| 269 | } | |||
| 270 | ||||
| 271 | // bench test fuel pump pin #5603 | |||
| 272 | static void sendPinStatePackets(bench_mode_e benchModePinIdx) { | |||
| 273 | OutputPin *pin = enginePins.getOutputPinForBenchMode(benchModePinIdx); | |||
| 274 | if (pin == nullptr) | |||
| 275 | return; | |||
| 276 | #if EFI_SIMULATOR | |||
| 277 | sendPinStatePackets(pin->pinToggleCounter, pin->durationsInStateMs); | |||
| 278 | #endif // EFI_SIMULATOR | |||
| 279 | } | |||
| 280 | ||||
| 281 | static void sendSavedBenchStatePackets() { | |||
| 282 | uint32_t savedDurationsInStateMs[2]; | |||
| 283 | int savedPinToggleCounter = getSavedBenchTestPinStates(savedDurationsInStateMs); | |||
| 284 | sendPinStatePackets(savedPinToggleCounter, savedDurationsInStateMs); | |||
| 285 | } | |||
| 286 | ||||
| 287 | static void resetPinStats(bench_mode_e benchModePinIdx) { | |||
| 288 | OutputPin *pin = enginePins.getOutputPinForBenchMode(benchModePinIdx); | |||
| 289 | ||||
| 290 | if (pin == nullptr) | |||
| 291 | return; | |||
| 292 | ||||
| 293 | #if EFI_SIMULATOR | |||
| 294 | pin->resetToggleStats(); | |||
| 295 | #endif // EFI_SIMULATOR | |||
| 296 | } | |||
| 297 | ||||
| 298 | void processCanQcBenchTest(const CANRxFrame& frame) { | |||
| 299 | if (CAN_EID(frame) != (int)bench_test_packet_ids_e::HW_QC_IO_CONTROL) { | |||
| 300 | return; | |||
| 301 | } | |||
| 302 | if (frame.data8[0] != (int)bench_test_magic_numbers_e::BENCH_HEADER) { | |||
| 303 | return; | |||
| 304 | } | |||
| 305 | setHwQcMode(); | |||
| 306 | bench_test_io_control_e command = (bench_test_io_control_e)frame.data8[1]; | |||
| 307 | if (command == bench_test_io_control_e::CAN_BENCH_GET_COUNT) { | |||
| 308 | sendOutBoardMeta(0); | |||
| 309 | } else if (command == bench_test_io_control_e::CAN_QC_OUTPUT_CONTROL_SET) { | |||
| 310 | // see also "bench_setpin" console command | |||
| 311 | setPin(frame, 1); | |||
| 312 | } else if (command == bench_test_io_control_e::CAN_QC_OUTPUT_CONTROL_CLEAR) { | |||
| 313 | setPin(frame, 0); | |||
| 314 | } else if (command == bench_test_io_control_e::CAN_QC_ETB) { | |||
| 315 | uint8_t dcIndex = frame.data8[2]; | |||
| 316 | uint8_t direction = frame.data8[3]; | |||
| 317 | qcSetEtbState(dcIndex, direction); | |||
| 318 | } else if (command == bench_test_io_control_e::CAN_BENCH_SET_ENGINE_TYPE) { | |||
| 319 | int eType = frame.data8[2]; | |||
| 320 | // todo: fix firmware for 'false' to be possible - i.e. more of properties should be applied on the fly | |||
| 321 | setEngineType(eType, true); | |||
| 322 | #if EFI_PROD_CODE | |||
| 323 | scheduleReboot(); | |||
| 324 | #endif // EFI_PROD_CODE | |||
| 325 | } else if (command == bench_test_io_control_e::CAN_BENCH_START_PIN_TEST) { | |||
| 326 | bench_mode_e benchModePinIdx = (bench_mode_e)frame.data8[2]; | |||
| 327 | // ignore previous pin state and stats | |||
| 328 | resetPinStats(benchModePinIdx); | |||
| 329 | } else if (command == bench_test_io_control_e::CAN_BENCH_END_PIN_TEST) { | |||
| 330 | sendSavedBenchStatePackets(); | |||
| 331 | } else if (command == bench_test_io_control_e::CAN_BENCH_EXECUTE_BENCH_TEST) { | |||
| 332 | int benchCommandIdx = frame.data8[2]; | |||
| 333 | handleBenchCategory(benchCommandIdx); | |||
| 334 | } else if (command == bench_test_io_control_e::CAN_BENCH_QUERY_PIN_STATE) { | |||
| 335 | bench_mode_e benchModePinIdx = (bench_mode_e)frame.data8[2]; | |||
| 336 | sendPinStatePackets(benchModePinIdx); | |||
| 337 | } | |||
| 338 | } | |||
| 339 | #endif // EFI_CAN_SUPPORT | |||
| 340 | ||||
| 341 | ✗ | void initQcBenchControls() { | ||
| 342 | #if EFI_CAN_SUPPORT && EFI_PROD_CODE | |||
| 343 | addConsoleActionII("qc_etb", [](int index, int direction) { | |||
| 344 | qcSetEtbState(index, direction); | |||
| 345 | }); | |||
| 346 | ||||
| 347 | addConsoleActionI("qc_output", [](int index) { | |||
| 348 | Gpio* boardOutputs = getBoardMetaOutputs(); | |||
| 349 | criticalAssertVoid(boardOutputs != nullptr, "outputs not defined"); | |||
| 350 | Gpio pin = boardOutputs[index]; | |||
| 351 | efiSetPadModeWithoutOwnershipAcquisition("qc_output", pin, PAL_MODE_OUTPUT_PUSHPULL); | |||
| 352 | ||||
| 353 | int physicalValue = palReadPad(getHwPort("read", pin), getHwPin("read", pin)); | |||
| 354 | efiPrintf("original pin %s value %d", hwPortname(pin), physicalValue); | |||
| 355 | palWritePad(getHwPort("write", pin), getHwPin("write", pin), 1 - physicalValue); | |||
| 356 | sendManualPinTest(index); | |||
| 357 | }); | |||
| 358 | #endif // EFI_PROD_CODE | |||
| 359 | ✗ | } | ||
| 360 |