static void tempTurnPinHigh(InjectorOutputPin *output) { output->overlappingCounter++; #if FUEL_MATH_EXTREME_LOGGING || defined(__DOXYGEN__) printf("seTurnPinHigh %s %d %d\r\n", output->name, output->overlappingCounter, (int)getTimeNowUs()); #endif /* FUEL_MATH_EXTREME_LOGGING */ if (output->overlappingCounter > 1) { // if (output->cancelNextTurningInjectorOff) { // // how comes AutoTest.testFordAspire ends up here? // } else { // /** // * #299 // * this is another kind of overlap which happens in case of a small duty cycle after a large duty cycle // */ #if FUEL_MATH_EXTREME_LOGGING || defined(__DOXYGEN__) printf("overlapping, no need to touch pin %s %d\r\n", output->name, (int)getTimeNowUs()); #endif /* FUEL_MATH_EXTREME_LOGGING */ // output->cancelNextTurningInjectorOff = true; return; // } } #if FUEL_MATH_EXTREME_LOGGING || defined(__DOXYGEN__) const char * w = output->currentLogicValue == true ? "err" : ""; // scheduleMsg(&sharedLogger, "^ %spin=%s eventIndex %d %d", w, output->name, // getRevolutionCounter(), getTimeNowUs()); #endif /* FUEL_MATH_EXTREME_LOGGING */ turnPinHigh(output); }
void WaveReader::onFallEvent() { efitick_t nowUs = getTimeNowUs(); fallEventCounter++; lastActivityTimeUs = nowUs; assertIsrContext(CUSTOM_ERR_6670); addEngineSnifferEvent(name, WC_DOWN); efitick_t width = nowUs - widthEventTimeUs; last_wave_high_widthUs = width; int revolutionCounter = engine->rpmCalculator.getRevolutionCounter(); totalOnTimeAccumulatorUs += width; if (currentRevolutionCounter != revolutionCounter) { /** * We are here in case of a new engine cycle */ currentRevolutionCounter = revolutionCounter; prevTotalOnTimeUs = totalOnTimeAccumulatorUs; totalOnTimeAccumulatorUs = 0; waveOffsetUs = nowUs - previousEngineCycleTimeUs; } periodEventTimeUs = nowUs; // uint32_t period = engineCycleDurationUs; // local copy of volatile variable }
static ALWAYS_INLINE void handleFuelInjectionEvent(int eventIndex, bool limitedFuel, InjectionEvent *event, int rpm DECLARE_ENGINE_PARAMETER_S) { if (limitedFuel) return; // todo: move this check up /** * todo: this is a bit tricky with batched injection. is it? Does the same * wetting coefficient works the same way for any injection mode, or is something * x2 or /2? */ floatms_t injectionDuration = ENGINE(wallFuel).adjust(event->injectorIndex, ENGINE(fuelMs) PASS_ENGINE_PARAMETER); ENGINE(actualLastInjection) = injectionDuration; if (cisnan(injectionDuration)) { warning(OBD_PCM_Processor_Fault, "NaN injection pulse"); return; } if (injectionDuration < 0) { warning(OBD_PCM_Processor_Fault, "Negative injection pulse %f", injectionDuration); return; } if (engine->isCylinderCleanupMode) return; floatus_t injectionStartDelayUs = ENGINE(rpmCalculator.oneDegreeUs) * event->injectionStart.angleOffset; OutputSignal *signal = &ENGINE(engineConfiguration2)->fuelActuators[eventIndex]; if (event->isSimultanious) { if (injectionDuration < 0) { firmwareError("duration cannot be negative: %d", injectionDuration); return; } if (cisnan(injectionDuration)) { firmwareError("NaN in scheduleOutput", injectionDuration); return; } /** * this is pretty much copy-paste of 'scheduleOutput' * 'scheduleOutput' is currently only used for injection, so maybe it should be * changed into 'scheduleInjection' and unified? todo: think about it. */ efiAssertVoid(signal!=NULL, "signal is NULL"); int index = getRevolutionCounter() % 2; scheduling_s * sUp = &signal->signalTimerUp[index]; scheduling_s * sDown = &signal->signalTimerDown[index]; scheduleTask("out up", sUp, (int) injectionStartDelayUs, (schfunc_t) &startSimultaniousInjection, engine); scheduleTask("out down", sDown, (int) injectionStartDelayUs + MS2US(injectionDuration), (schfunc_t) &endSimultaniousInjection, engine); } else { #if EFI_UNIT_TEST || defined(__DOXYGEN__) printf("scheduling injection angle=%f/delay=%f injectionDuration=%f\r\n", event->injectionStart.angleOffset, injectionStartDelayUs, injectionDuration); #endif scheduleOutput(signal, getTimeNowUs(), injectionStartDelayUs, MS2US(injectionDuration), event->output); } }
void Engine::knockLogic(float knockVolts) { this->knockVolts = knockVolts; knockNow = knockVolts > engineConfiguration->knockVThreshold; /** * KnockCount is directly proportional to the degrees of ignition * advance removed * ex: degrees to subtract = knockCount; */ /** * TODO use knockLevel as a factor for amount of ignition advance * to remove * Perhaps allow the user to set a multiplier * ex: degrees to subtract = knockCount + (knockLevel * X) * X = user configurable multiplier */ if (knockNow) { knockEver = true; timeOfLastKnockEvent = getTimeNowUs(); if (knockCount < engineConfiguration->maxKnockSubDeg) knockCount++; } else if (knockCount >= 1) { knockCount--; } else { knockCount = 0; } }
static void testCallback(void *arg) { /** * 0.1ms from now please squirt for 1.6ms */ float delayMs = 0.1; float durationMs = 1.6; efitimeus_t nowUs = getTimeNowUs(); scheduleOutput(&outSignals[0], nowUs, delayMs, durationMs); scheduleOutput(&outSignals[1], nowUs, delayMs, durationMs); scheduleOutput(&outSignals[2], nowUs, delayMs, durationMs); scheduleOutput(&outSignals[3], nowUs, delayMs, durationMs); scheduleOutput(&outSignals[4], nowUs, delayMs, durationMs); scheduleOutput(&outSignals[5], nowUs, delayMs, durationMs); scheduleOutput(&outSignals[6], nowUs, delayMs, durationMs); scheduleOutput(&outSignals[7], nowUs, delayMs, durationMs); /** * this would re-schedule another callback in 2ms from now */ scheduleTask("test", &ioTest, MS2US(2), testCallback, NULL); }
void WaveReader::onFallEvent() { uint64_t nowUs = getTimeNowUs(); eventCounter++; lastActivityTimeUs = nowUs; addWaveChartEvent(name, WC_DOWN, ""); uint64_t width = nowUs - widthEventTimeUs; last_wave_high_widthUs = width; int revolutionCounter = getRevolutionCounter(); totalOnTimeAccumulatorUs += width; if (currentRevolutionCounter != revolutionCounter) { /** * We are here in case of a new engine cycle */ currentRevolutionCounter = revolutionCounter; prevTotalOnTimeUs = totalOnTimeAccumulatorUs; totalOnTimeAccumulatorUs = 0; waveOffsetUs = nowUs - previousEngineCycleTimeUs; } periodEventTimeUs = nowUs; // uint32_t period = engineCycleDurationUs; // local copy of volatile variable }
void scheduleTask(const bool monitorReuse, const char *msg, scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) { if (debugSignalExecutor) { printf("scheduleTask %d\r\n", delayUs); } scheduleByTime(monitorReuse, msg, scheduling, getTimeNowUs() + delayUs, callback, param); }
static float getSignalOnTime(int index) { WaveReader *reader = &readers[index]; ensureInitialized(reader); if (getTimeNowUs() - reader->lastActivityTimeUs > 4 * US_PER_SECOND) { return 0.0f; // dwell time has expired } return reader->last_wave_high_widthUs / 1000.0f; }
static void waTriggerEventListener(trigger_event_e ckpSignalType, uint32_t index DECLARE_ENGINE_PARAMETER_SUFFIX) { (void)ckpSignalType; if (index != 0) { return; } efitick_t nowUs = getTimeNowUs(); engineCycleDurationUs = nowUs - previousEngineCycleTimeUs; previousEngineCycleTimeUs = nowUs; }
/** * I use this questionable feature to tune acceleration enrichment */ static void blipIdle(int idlePosition, int durationMs) { // todo: add 'blip' feature for automatic target control if (timeToStopBlip != 0) { return; // already in idle blip } idlePositionBeforeBlip = boardConfiguration->manIdlePosition; setIdleValvePosition(idlePosition); timeToStopBlip = getTimeNowUs() + 1000 * durationMs; }
static void waTriggerEventListener(trigger_event_e ckpSignalType, uint32_t index, void *arg) { (void)ckpSignalType; (void)arg; if (index != 0) { return; } uint64_t nowUs = getTimeNowUs(); engineCycleDurationUs = nowUs - previousEngineCycleTimeUs; previousEngineCycleTimeUs = nowUs; }
static msg_t mwThread(int param) { chRegSetThreadName("timer watchdog"); while (TRUE) { chThdSleepMilliseconds(1000); // once a second is enough if (getTimeNowUs() >= lastSetTimerTime + 2 * US_PER_SECOND) { strcpy(buff, "no_event"); itoa10(&buff[8], lastSetTimerValue); firmwareError(buff); return -1; } msg = isTimerPending ? "No_cb too long" : "Timer not awhile"; // 2 seconds of inactivity would not look right efiAssert(getTimeNowUs() < lastSetTimerTime + 2 * US_PER_SECOND, msg, -1); } #if defined __GNUC__ return -1; #endif }
static void waAnaWidthCallback(WaveReader *reader) { uint64_t nowUs = getTimeNowUs(); reader->eventCounter++; reader->lastActivityTimeUs = nowUs; addWaveChartEvent(reader->name, WC_UP, ""); uint32_t width = nowUs - reader->periodEventTimeUs; reader->last_wave_low_widthUs = width; reader->signalPeriodUs = nowUs - reader->widthEventTimeUs; reader->widthEventTimeUs = nowUs; }
static void timerCallback(scheduling_s *scheduling) { #if EFI_PRINTF_FUEL_DETAILS if (scheduling->callback == (schfunc_t)&seTurnPinLow) { printf("executing cb=seTurnPinLow p=%d sch=%d now=%d\r\n", (int)scheduling->param, (int)scheduling, (int)getTimeNowUs()); } else { // printf("exec cb=%d p=%d\r\n", (int)scheduling->callback, (int)scheduling->param); } #endif /* EFI_SIMULATOR */ scheduling->callback(scheduling->param); }
static void waAnaWidthCallback(WaveReader *reader) { efitick_t nowUs = getTimeNowUs(); reader->riseEventCounter++; reader->lastActivityTimeUs = nowUs; assertIsrContext(CUSTOM_ERR_6670); addEngineSnifferEvent(reader->name, WC_UP); uint32_t width = nowUs - reader->periodEventTimeUs; reader->last_wave_low_widthUs = width; reader->signalPeriodUs = nowUs - reader->widthEventTimeUs; reader->widthEventTimeUs = nowUs; }
/** * @brief Register an event for digital sniffer */ void addWaveChartEvent3(WaveChart *chart, const char *name, const char * msg, const char * msg2) { efiAssertVoid(chart->isInitialized, "chart not initialized"); #if DEBUG_WAVE scheduleSimpleMsg(&debugLogging, "current", chart->counter); #endif if (isWaveChartFull(chart)) { return; } #if EFI_HISTOGRAMS && EFI_PROD_CODE int beforeCallback = hal_lld_get_counter_value(); #endif int time100 = getTimeNowUs() / 10; bool alreadyLocked = lockOutputBuffer(); // we have multiple threads writing to the same output buffer if (chart->counter == 0) { chart->startTime = time100; } chart->counter++; if (remainingSize(&chart->logging) > 30) { /** * printf is a heavy method, append is used here as a performance optimization */ appendFast(&chart->logging, name); appendFast(&chart->logging, CHART_DELIMETER); appendFast(&chart->logging, msg); appendFast(&chart->logging, CHART_DELIMETER); /** * We want smaller times within a chart in order to reduce packet size. */ time100 -= chart->startTime; itoa10(timeBuffer, time100); appendFast(&chart->logging, timeBuffer); appendFast(&chart->logging, msg2); appendFast(&chart->logging, CHART_DELIMETER); } if (!alreadyLocked) { unlockOutputBuffer(); } #if EFI_HISTOGRAMS && EFI_PROD_CODE int64_t diff = hal_lld_get_counter_value() - beforeCallback; if (diff > 0) { hsAdd(&waveChartHisto, diff); } #endif /* EFI_HISTOGRAMS */ }
static void tempTurnPinLow(InjectorOutputPin *output) { #if FUEL_MATH_EXTREME_LOGGING || defined(__DOXYGEN__) printf("seTurnPinLow %s %d %d\r\n", output->name, output->overlappingCounter, (int)getTimeNowUs()); #endif /* FUEL_MATH_EXTREME_LOGGING */ if (output->cancelNextTurningInjectorOff) { /** * in case of fuel schedule overlap between engine cycles, * and if engine cycle is above say 75% for batch mode on 4 cylinders, * we will get a secondary overlap between the special injection and a normal injection on the same injector. * In such a case want to combine these two injection into one continues injection. * Unneeded turn of injector on is handle while scheduling that second injection, but cancellation * of special injection end has to be taken care of dynamically * */ output->cancelNextTurningInjectorOff = false; #if EFI_SIMULATOR || defined(__DOXYGEN__) printf("was cancelled %s %d\r\n", output->name, (int)getTimeNowUs()); #endif /* EFI_SIMULATOR */ return; } #if FUEL_MATH_EXTREME_LOGGING || defined(__DOXYGEN__) const char * w = output->currentLogicValue == false ? "err" : ""; // scheduleMsg(&sharedLogger, "- %spin=%s eventIndex %d %d", w, output->name, // getRevolutionCounter(), getTimeNowUs()); #endif /* FUEL_MATH_EXTREME_LOGGING */ output->overlappingCounter--; if (output->overlappingCounter > 0) { #if FUEL_MATH_EXTREME_LOGGING || defined(__DOXYGEN__) printf("was overlapping, no need to touch pin %s %d\r\n", output->name, (int)getTimeNowUs()); #endif /* FUEL_MATH_EXTREME_LOGGING */ return; } turnPinLow(output); }
/** * sets the alarm to the specified number of microseconds from now. * This function should be invoked under kernel lock which would disable interrupts. */ void setHardwareUsTimer(int32_t timeUs) { if (timeUs == 1) timeUs = 2; // for some reason '1' does not really work efiAssertVoid(timeUs > 0, "neg timeUs"); efiAssertVoid(timeUs < 10 * US_PER_SECOND, "setHardwareUsTimer() too large"); if (GPTDEVICE.state == GPT_ONESHOT) gptStopTimerI(&GPTDEVICE); gptStartOneShotI(&GPTDEVICE, timeUs); lastSetTimerTime = getTimeNowUs(); lastSetTimerValue = timeUs; isTimerPending = TRUE; timerRestartCounter++; }
/** * @return Next time for signal toggle */ efitimeus_t PwmConfig::togglePwmState() { #if DEBUG_PWM scheduleMsg(&logger, "togglePwmState phaseIndex=%d iteration=%d", safe.phaseIndex, safe.iteration); scheduleMsg(&logger, "period=%.2f safe.period=%.2f", period, safe.period); #endif if (cisnan(periodNt)) { /** * NaN period means PWM is paused */ return getTimeNowUs() + MS2US(100); } handleCycleStart(); /** * Here is where the 'business logic' - the actual pin state change is happening */ // callback state index is offset by one. todo: why? can we simplify this? int cbStateIndex = safe.phaseIndex == 0 ? phaseCount - 1 : safe.phaseIndex - 1; stateChangeCallback(this, cbStateIndex); efitimeus_t nextSwitchTimeUs = getNextSwitchTimeUs(this); #if DEBUG_PWM scheduleMsg(&logger, "%s: nextSwitchTime %d", state->name, nextSwitchTime); #endif /* DEBUG_PWM */ // signed value is needed here // int64_t timeToSwitch = nextSwitchTimeUs - getTimeNowUs(); // if (timeToSwitch < 1) { // /** // * We are here if we are late for a state transition. // * At 12000RPM=200Hz with a 60 toothed wheel we need to change state every // * 1000000 / 200 / 120 = ~41 uS. We are kind of OK. // * // * We are also here after a flash write. Flash write freezes the whole chip for a couple of seconds, // * so PWM generation and trigger simulation generation would have to recover from this time lag. // */ // //todo: introduce error and test this error handling warning(OBD_PCM_Processor_Fault, "PWM: negative switch time"); // timeToSwitch = 10; // } safe.phaseIndex++; if (safe.phaseIndex == phaseCount) { safe.phaseIndex = 0; // restart safe.iteration++; } return nextSwitchTimeUs; }
void initMicrosecondTimer(void) { gptStart(&GPTDEVICE, &gpt5cfg); lastSetTimerTime = getTimeNowUs(); #if EFI_EMULATE_POSITION_SENSORS chThdCreateStatic(mwThreadStack, sizeof(mwThreadStack), NORMALPRIO, (tfunc_t) mwThread, NULL); #endif /* EFI_ENGINE_EMULATOR */ // // test code // chSysLock() // ; // setHardwareUsTimer(300); // chSysUnlock() // ; }
void startIdleBench(void) { timeToStopIdleTest = getTimeNowUs() + MS2US(3000); // 3 seconds scheduleMsg(logger, "idle valve bench test"); showIdleInfo(); }
static void finishIdleTestIfNeeded() { if (timeToStopIdleTest != 0 && getTimeNowUs() > timeToStopIdleTest) timeToStopIdleTest = 0; }
efitick_t getTimeNowNt(void) { return getTimeNowUs() * US_TO_NT_MULTIPLIER; }
// todo; reduce code duplication with prod code? efitimems_t currentTimeMillis(void) { return getTimeNowUs() / 1000; }
static void undoIdleBlipIfNeeded() { if (timeToStopBlip != 0 && getTimeNowUs() > timeToStopBlip) { timeToStopBlip = 0; setIdleValvePosition(idlePositionBeforeBlip); } }
void scheduleByTime(const char *prefix, scheduling_s *scheduling, efitimeus_t time, schfunc_t callback, void *param) { scheduleTask(prefix, scheduling, time - getTimeNowUs(), callback, param); }
void SleepExecutor::scheduleByTimestamp(scheduling_s *scheduling, efitimeus_t timeUs, schfunc_t callback, void *param) { scheduleForLater(scheduling, timeUs - getTimeNowUs(), callback, param); }
/** * @brief Schedule an event * * Invokes event callback after the specified amount of time. * * @param [in, out] scheduling Data structure to keep this event in the collection. * @param [in] delayUs the number of microseconds before the output signal immediate output if delay is zero. * @param [in] dwell the number of ticks of output duration. */ void scheduleTask(const bool monitorReuse, const char *prefix, scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) { // scheduling->name = prefix; instance.scheduleByTime(monitorReuse, scheduling, getTimeNowUs() + delayUs, callback, param); }