fome-fw/firmware/controllers/trigger/trigger_decoder.cpp

407 lines
13 KiB
C++
Raw Normal View History

2014-08-29 07:52:33 -07:00
/**
* @file trigger_decoder.cpp
*
* @date Dec 24, 2013
* @author Andrey Belomutskiy, (c) 2012-2014
*
* This file is part of rusEfi - see http://rusefi.com
*
* rusEfi is free software; you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "main.h"
#include "obd_error_codes.h"
#include "trigger_decoder.h"
#include "cyclic_buffer.h"
#include "trigger_mazda.h"
#include "trigger_chrysler.h"
#include "trigger_gm.h"
#include "trigger_bmw.h"
#include "trigger_mitsubishi.h"
#include "trigger_structure.h"
2014-11-15 06:03:49 -08:00
#include "efiGpio.h"
2014-08-29 07:52:33 -07:00
// todo: better name for this constant
#define HELPER_PERIOD 100000
static cyclic_buffer errorDetection;
2014-10-01 17:02:58 -07:00
#if ! EFI_PROD_CODE
bool printGapRatio = false;
2014-11-02 12:03:03 -08:00
#endif /* ! EFI_PROD_CODE */
2014-10-01 17:02:58 -07:00
2014-11-02 12:03:03 -08:00
#if (EFI_PROD_CODE || EFI_SIMULATOR)
Logging logger;
#endif
2014-11-02 11:04:37 -08:00
2014-08-29 07:52:33 -07:00
/**
* @return TRUE is something is wrong with trigger decoding
*/
2014-11-14 08:03:49 -08:00
bool_t isTriggerDecoderError(void) {
2014-08-29 07:52:33 -07:00
return errorDetection.sum(6) > 4;
}
2014-11-14 11:05:25 -08:00
static ALWAYS_INLINE bool isSynchronizationGap(TriggerState *shaftPositionState, trigger_shape_s const *triggerShape,
2014-08-29 07:52:33 -07:00
const int currentDuration) {
if (!triggerShape->isSynchronizationNeeded) {
return false;
}
2014-10-01 17:02:58 -07:00
#if ! EFI_PROD_CODE
2014-10-29 14:03:10 -07:00
if (printGapRatio) {
2014-10-01 17:02:58 -07:00
float gap = 1.0 * currentDuration / shaftPositionState->toothed_previous_duration;
print("current gap %f\r\n", gap);
}
2014-11-02 11:04:37 -08:00
#else
// float gap = 1.0 * currentDuration / shaftPositionState->toothed_previous_duration;
2014-11-05 06:03:20 -08:00
// scheduleMsg(&logger, "gap=%f @ %d", gap, shaftPositionState->getCurrentIndex());
2014-10-01 17:02:58 -07:00
#endif /* ! EFI_PROD_CODE */
2014-08-29 07:52:33 -07:00
return currentDuration > shaftPositionState->toothed_previous_duration * triggerShape->syncRatioFrom
&& currentDuration < shaftPositionState->toothed_previous_duration * triggerShape->syncRatioTo;
}
2014-11-14 11:05:25 -08:00
static ALWAYS_INLINE bool noSynchronizationResetNeeded(TriggerState *shaftPositionState, trigger_shape_s const *triggerShape) {
2014-08-29 07:52:33 -07:00
if (triggerShape->isSynchronizationNeeded) {
return false;
}
if (!shaftPositionState->shaft_is_synchronized) {
return true;
}
/**
* in case of noise the counter could be above the expected number of events
*/
return shaftPositionState->getCurrentIndex() >= triggerShape->shaftPositionEventCount - 1;
}
float TriggerState::getTriggerDutyCycle(int index) {
float time = prevTotalTime[index];
return 100 * time / prevCycleDuration;
}
static trigger_wheel_e eventIndex[6] = { T_PRIMARY, T_PRIMARY, T_SECONDARY, T_SECONDARY, T_CHANNEL_3, T_CHANNEL_3 };
static trigger_value_e eventType[6] = { TV_LOW, TV_HIGH, TV_LOW, TV_HIGH, TV_LOW, TV_HIGH };
2014-11-11 08:04:06 -08:00
#define getCurrentGapDuration(nowUs) \
(isFirstEvent ? 0 : (nowUs) - toothed_previous_time)
2014-08-29 07:52:33 -07:00
/**
* @brief Trigger decoding happens here
* This method changes the state of trigger_state_s data structure according to the trigger event
*/
void TriggerState::decodeTriggerEvent(trigger_shape_s const*triggerShape, trigger_config_s const*triggerConfig,
2014-11-14 13:03:52 -08:00
trigger_event_e const signal, uint64_t nowNt) {
2014-10-01 17:02:58 -07:00
(void) triggerConfig; // we might want this for logging?
2014-08-29 07:52:33 -07:00
efiAssertVoid(signal <= SHAFT_3RD_UP, "unexpected signal");
trigger_wheel_e triggerWheel = eventIndex[signal];
eventCount[triggerWheel]++;
int isLessImportant = (triggerShape->useRiseEdge && signal != SHAFT_PRIMARY_UP)
|| (!triggerShape->useRiseEdge && signal != SHAFT_PRIMARY_DOWN);
if (isLessImportant) {
/**
* For less important events we simply increment the index.
*/
2014-11-14 13:03:52 -08:00
nextTriggerEvent(triggerWheel, nowNt);
2014-10-29 14:03:10 -07:00
if (triggerShape->gapBothDirections) {
2014-11-14 13:03:52 -08:00
toothed_previous_duration = getCurrentGapDuration(nowNt);
2014-11-11 08:04:06 -08:00
isFirstEvent = false;
2014-11-14 13:03:52 -08:00
toothed_previous_time = nowNt;
2014-10-29 14:03:10 -07:00
}
2014-08-29 07:52:33 -07:00
return;
}
2014-11-15 06:03:49 -08:00
currentDuration = getCurrentGapDuration(nowNt);
2014-11-11 08:04:06 -08:00
isFirstEvent = false;
2014-08-29 07:52:33 -07:00
efiAssertVoid(currentDuration >= 0, "decode: negative duration?");
// todo: skip a number of signal from the beginning
#if EFI_PROD_CODE
// scheduleMsg(&logger, "from %f to %f %d %d", triggerConfig->syncRatioFrom, triggerConfig->syncRatioTo, currentDuration, shaftPositionState->toothed_previous_duration);
// scheduleMsg(&logger, "ratio %f", 1.0 * currentDuration/ shaftPositionState->toothed_previous_duration);
#else
if (toothed_previous_duration != 0) {
// printf("ratio %f: cur=%d pref=%d\r\n", 1.0 * currentDuration / shaftPositionState->toothed_previous_duration,
// currentDuration, shaftPositionState->toothed_previous_duration);
}
#endif
if (noSynchronizationResetNeeded(this, triggerShape) || isSynchronizationGap(this, triggerShape, currentDuration)) {
/**
* We can check if things are fine by comparing the number of events in a cycle with the expected number of event.
*/
bool isDecodingError = eventCount[0] != triggerShape->expectedEventCount[0]
|| eventCount[1] != triggerShape->expectedEventCount[1]
|| eventCount[2] != triggerShape->expectedEventCount[2];
2014-11-15 06:03:49 -08:00
setOutputPinValue(LED_TRIGGER_ERROR, isDecodingError);
2014-11-13 13:03:09 -08:00
if (isDecodingError) {
totalTriggerErrorCounter++;
}
2014-08-29 07:52:33 -07:00
errorDetection.add(isDecodingError);
if (isTriggerDecoderError()) {
warning(OBD_PCM_Processor_Fault, "trigger decoding issue. expected %d/%d/%d got %d/%d/%d",
triggerShape->expectedEventCount[0], triggerShape->expectedEventCount[1],
triggerShape->expectedEventCount[2], eventCount[0], eventCount[1], eventCount[2]);
}
shaft_is_synchronized = true;
// this call would update duty cycle values
2014-11-14 13:03:52 -08:00
nextTriggerEvent(triggerWheel, nowNt);
2014-08-29 07:52:33 -07:00
2014-11-14 13:03:52 -08:00
nextRevolution(triggerShape->shaftPositionEventCount, nowNt);
2014-08-29 07:52:33 -07:00
} else {
2014-11-14 13:03:52 -08:00
nextTriggerEvent(triggerWheel, nowNt);
2014-08-29 07:52:33 -07:00
}
toothed_previous_duration = currentDuration;
2014-11-14 13:03:52 -08:00
toothed_previous_time = nowNt;
2014-08-29 07:52:33 -07:00
}
static void initializeSkippedToothTriggerShape(trigger_shape_s *s, int totalTeethCount, int skippedCount,
operation_mode_e operationMode) {
efiAssertVoid(s != NULL, "trigger_shape_s is NULL");
s->reset(operationMode);
float toothWidth = 0.5;
for (int i = 0; i < totalTeethCount - skippedCount - 1; i++) {
float angleDown = 720.0 / totalTeethCount * (i + toothWidth);
float angleUp = 720.0 / totalTeethCount * (i + 1);
s->addEvent(angleDown, T_PRIMARY, TV_HIGH);
s->addEvent(angleUp, T_PRIMARY, TV_LOW);
}
float angleDown = 720.0 / totalTeethCount * (totalTeethCount - skippedCount - 1 + toothWidth);
s->addEvent(angleDown, T_PRIMARY, TV_HIGH);
s->addEvent(720, T_PRIMARY, TV_LOW);
}
void initializeSkippedToothTriggerShapeExt(trigger_shape_s *s, int totalTeethCount, int skippedCount,
operation_mode_e operationMode) {
efiAssertVoid(totalTeethCount > 0, "totalTeethCount is zero");
s->totalToothCount = totalTeethCount;
s->skippedToothCount = skippedCount;
initializeSkippedToothTriggerShape(s, totalTeethCount, skippedCount, operationMode);
2014-10-01 18:03:03 -07:00
s->assignSize();
2014-08-29 07:52:33 -07:00
}
/**
* External logger is needed because at this point our logger is not yet initialized
*/
2014-09-12 18:05:24 -07:00
void initializeTriggerShape(Logging *logger, engine_configuration_s const *engineConfiguration,
2014-08-29 07:52:33 -07:00
engine_configuration2_s *engineConfiguration2) {
#if EFI_PROD_CODE
scheduleMsg(logger, "initializeTriggerShape()");
#endif
2014-09-12 18:05:24 -07:00
const trigger_config_s *triggerConfig = &engineConfiguration->triggerConfig;
2014-08-29 07:52:33 -07:00
trigger_shape_s *triggerShape = &engineConfiguration2->triggerShape;
setTriggerSynchronizationGap(triggerShape, 2);
2014-10-29 13:04:17 -07:00
triggerShape->useRiseEdge = true;
2014-08-29 07:52:33 -07:00
switch (triggerConfig->triggerType) {
case TT_TOOTHED_WHEEL:
// todo: move to into configuration definition engineConfiguration2->triggerShape.needSecondTriggerInput = false;
engineConfiguration2->triggerShape.isSynchronizationNeeded =
engineConfiguration->triggerConfig.customIsSynchronizationNeeded;
initializeSkippedToothTriggerShapeExt(triggerShape, triggerConfig->customTotalToothCount,
triggerConfig->customSkippedToothCount, getOperationMode(engineConfiguration));
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_MAZDA_MIATA_NA:
initializeMazdaMiataNaShape(triggerShape);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_MAZDA_MIATA_NB:
initializeMazdaMiataNbShape(triggerShape);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
2014-09-27 16:02:54 -07:00
case TT_DODGE_NEON_1995:
2014-10-01 16:03:00 -07:00
configureNeon1995TriggerShape(triggerShape);
2014-10-01 18:03:03 -07:00
break;
2014-10-01 16:03:00 -07:00
case TT_DODGE_NEON_2003:
configureNeon2003TriggerShape(triggerShape);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_FORD_ASPIRE:
2014-09-12 18:05:24 -07:00
configureFordAspireTriggerShape(triggerShape);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_GM_7X:
2014-09-12 18:05:24 -07:00
configureGmTriggerShape(triggerShape);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_FORD_ESCORT_GT:
configureMazdaProtegeLx(triggerShape);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_MINI_COOPER_R50:
configureMiniCooperTriggerShape(triggerShape);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_TOOTHED_WHEEL_60_2:
setToothedWheelConfiguration(triggerShape, 60, 2, engineConfiguration);
setTriggerSynchronizationGap(triggerShape, 2.5);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_TOOTHED_WHEEL_36_1:
setToothedWheelConfiguration(triggerShape, 36, 1, engineConfiguration);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_HONDA_ACCORD_CD_TWO_WIRES:
configureHondaAccordCD(triggerShape, false);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_HONDA_ACCORD_CD:
configureHondaAccordCD(triggerShape, true);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_HONDA_ACCORD_CD_DIP:
configureHondaAccordCDDip(triggerShape);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
case TT_MITSU:
initializeMitsubishi4g18(triggerShape);
2014-10-01 18:03:03 -07:00
break;
2014-08-29 07:52:33 -07:00
default:
firmwareError("initializeTriggerShape() not implemented: %d", triggerConfig->triggerType);
;
2014-10-01 18:03:03 -07:00
return;
2014-08-29 07:52:33 -07:00
}
2014-10-01 18:03:03 -07:00
trigger_shape_s *s = &engineConfiguration2->triggerShape;
s->assignSize();
s->wave.checkSwitchTimes(s->getSize());
2014-08-29 07:52:33 -07:00
}
TriggerStimulatorHelper::TriggerStimulatorHelper() {
primaryWheelState = false;
secondaryWheelState = false;
thirdWheelState = false;
}
void TriggerStimulatorHelper::nextStep(TriggerState *state, trigger_shape_s * shape, int i,
trigger_config_s const*triggerConfig) {
int stateIndex = i % shape->getSize();
int loopIndex = i / shape->getSize();
int time = (int) (HELPER_PERIOD * (loopIndex + shape->wave.getSwitchTime(stateIndex)));
bool newPrimaryWheelState = shape->wave.getChannelState(0, stateIndex);
bool newSecondaryWheelState = shape->wave.getChannelState(1, stateIndex);
bool new3rdWheelState = shape->wave.getChannelState(2, stateIndex);
if (primaryWheelState != newPrimaryWheelState) {
primaryWheelState = newPrimaryWheelState;
trigger_event_e s = primaryWheelState ? SHAFT_PRIMARY_UP : SHAFT_PRIMARY_DOWN;
state->decodeTriggerEvent(shape, triggerConfig, s, time);
}
if (secondaryWheelState != newSecondaryWheelState) {
secondaryWheelState = newSecondaryWheelState;
trigger_event_e s = secondaryWheelState ? SHAFT_SECONDARY_UP : SHAFT_SECONDARY_DOWN;
state->decodeTriggerEvent(shape, triggerConfig, s, time);
}
if (thirdWheelState != new3rdWheelState) {
thirdWheelState = new3rdWheelState;
trigger_event_e s = thirdWheelState ? SHAFT_3RD_UP : SHAFT_3RD_DOWN;
state->decodeTriggerEvent(shape, triggerConfig, s, time);
}
}
static void onFindIndex(TriggerState *state) {
for (int i = 0; i < PWM_PHASE_MAX_WAVE_PER_PWM; i++) {
// todo: that's not the best place for this intermediate data storage, fix it!
2014-11-14 14:03:57 -08:00
state->expectedTotalTime[i] = state->totalTimeNt[i];
2014-08-29 07:52:33 -07:00
}
}
static uint32_t doFindTrigger(TriggerStimulatorHelper *helper, trigger_shape_s * shape,
trigger_config_s const*triggerConfig, TriggerState *state) {
for (int i = 0; i < 4 * PWM_PHASE_MAX_COUNT; i++) {
helper->nextStep(state, shape, i, triggerConfig);
if (state->shaft_is_synchronized)
return i;
}
firmwareError("findTriggerZeroEventIndex() failed");
return EFI_ERROR_CODE;
}
/**
* Trigger shape is defined in a way which is convenient for trigger shape definition
* On the other hand, trigger decoder indexing begins from synchronization event.
*
* This function finds the index of synchronization event within trigger_shape_s
*/
uint32_t findTriggerZeroEventIndex(trigger_shape_s * shape, trigger_config_s const*triggerConfig) {
TriggerState state;
errorDetection.clear();
TriggerStimulatorHelper helper;
uint32_t index = doFindTrigger(&helper, shape, triggerConfig, &state);
if (index == EFI_ERROR_CODE) {
return index;
}
efiAssert(state.getTotalRevolutionCounter() == 1, "totalRevolutionCounter", EFI_ERROR_CODE);
/**
* Now that we have just located the synch point, we can simulate the whole cycle
* in order to calculate expected duty cycle
2014-10-29 13:04:17 -07:00
*
* todo: add a comment why are we doing '2 * shape->getSize()' here?
2014-08-29 07:52:33 -07:00
*/
state.cycleCallback = onFindIndex;
for (uint32_t i = index + 1; i <= index + 2 * shape->getSize(); i++) {
helper.nextStep(&state, shape, i, triggerConfig);
}
2014-10-29 13:04:17 -07:00
efiAssert(state.getTotalRevolutionCounter() == 3, "totalRevolutionCounter2", EFI_ERROR_CODE);
2014-08-29 07:52:33 -07:00
for (int i = 0; i < PWM_PHASE_MAX_WAVE_PER_PWM; i++) {
shape->dutyCycle[i] = 1.0 * state.expectedTotalTime[i] / HELPER_PERIOD;
}
return index % shape->getSize();
}
#if (EFI_PROD_CODE || EFI_SIMULATOR) || defined(__DOXYGEN__)
//static Logging logger;
#endif
void initTriggerDecoder(void) {
2014-11-02 12:03:03 -08:00
#if (EFI_PROD_CODE || EFI_SIMULATOR)
2014-11-02 11:04:37 -08:00
initLogging(&logger, "trigger decoder");
2014-08-29 07:52:33 -07:00
#endif
}