syncing
This commit is contained in:
parent
2ab26f8aaf
commit
d55a41c1b6
340
engine_math.cpp
340
engine_math.cpp
|
@ -1,340 +0,0 @@
|
||||||
/**
|
|
||||||
* @file engine_math.cpp
|
|
||||||
* @brief
|
|
||||||
*
|
|
||||||
* @date Jul 13, 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 "engine_math.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "engine_configuration.h"
|
|
||||||
#include "interpolation.h"
|
|
||||||
#include "allsensors.h"
|
|
||||||
#include "io_pins.h"
|
|
||||||
#include "OutputSignalList.h"
|
|
||||||
#include "trigger_decoder.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* default Volumetric Efficiency
|
|
||||||
*/
|
|
||||||
//float getDefaultVE(int rpm) {
|
|
||||||
// if (rpm > 5000)
|
|
||||||
// return interpolate(5000, 1.1, 8000, 1, rpm);
|
|
||||||
// return interpolate(500, 0.5, 5000, 1.1, rpm);
|
|
||||||
//}
|
|
||||||
//#define K_AT_MIN_RPM_MIN_TPS 0.25
|
|
||||||
//#define K_AT_MIN_RPM_MAX_TPS 0.25
|
|
||||||
//#define K_AT_MAX_RPM_MIN_TPS 0.25
|
|
||||||
//#define K_AT_MAX_RPM_MAX_TPS 0.9
|
|
||||||
//
|
|
||||||
//#define rpmMin 500
|
|
||||||
//#define rpmMax 8000
|
|
||||||
//
|
|
||||||
//#define tpMin 0
|
|
||||||
//#define tpMax 100
|
|
||||||
//
|
|
||||||
// http://rusefi.com/math/t_charge.html
|
|
||||||
// /
|
|
||||||
//float getTCharge(int rpm, int tps, float coolantTemp, float airTemp) {
|
|
||||||
// float minRpmKcurrentTPS = interpolate(tpMin, K_AT_MIN_RPM_MIN_TPS, tpMax,
|
|
||||||
// K_AT_MIN_RPM_MAX_TPS, tps);
|
|
||||||
// float maxRpmKcurrentTPS = interpolate(tpMin, K_AT_MAX_RPM_MIN_TPS, tpMax,
|
|
||||||
// K_AT_MAX_RPM_MAX_TPS, tps);
|
|
||||||
//
|
|
||||||
// float Tcharge_coff = interpolate(rpmMin, minRpmKcurrentTPS, rpmMax,
|
|
||||||
// maxRpmKcurrentTPS, rpm);
|
|
||||||
//
|
|
||||||
// float Tcharge = coolantTemp * (1 - Tcharge_coff) + airTemp * Tcharge_coff;
|
|
||||||
//
|
|
||||||
// return Tcharge;
|
|
||||||
//}
|
|
||||||
#define MAX_STARTING_FUEL 15
|
|
||||||
#define MIN_STARTING_FUEL 8
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return time needed to rotate crankshaft by one degree, in milliseconds.
|
|
||||||
*/
|
|
||||||
float getOneDegreeTimeMs(int rpm) {
|
|
||||||
return 1000.0 * 60 / 360 / rpm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return number of milliseconds in one crankshaft revolution
|
|
||||||
*/
|
|
||||||
float getCrankshaftRevolutionTimeMs(int rpm) {
|
|
||||||
return 360 * getOneDegreeTimeMs(rpm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Shifts angle into the [0..720) range
|
|
||||||
* TODO: should be 'crankAngleRange' range?
|
|
||||||
*/
|
|
||||||
float fixAngle(float angle) {
|
|
||||||
// I guess this implementation would be faster than 'angle % 720'
|
|
||||||
while (angle < 0)
|
|
||||||
angle += 720;
|
|
||||||
while (angle > 720)
|
|
||||||
angle -= 720;
|
|
||||||
return angle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns engine load according to selected engine_load_mode
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
float getEngineLoadT(engine_configuration_s *engineConfiguration) {
|
|
||||||
switch (engineConfiguration->engineLoadMode) {
|
|
||||||
case LM_MAF:
|
|
||||||
return getMaf();
|
|
||||||
case LM_MAP:
|
|
||||||
return getMap();
|
|
||||||
case LM_TPS:
|
|
||||||
return getTPS();
|
|
||||||
case LM_SPEED_DENSITY:
|
|
||||||
// TODO: real implementation
|
|
||||||
return getMap();
|
|
||||||
default:
|
|
||||||
firmwareError("Unexpected engine load parameter: %d", engineConfiguration->engineLoadMode);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSingleCoilDwell(engine_configuration_s *engineConfiguration) {
|
|
||||||
for (int i = 0; i < DWELL_CURVE_SIZE; i++) {
|
|
||||||
engineConfiguration->sparkDwellBins[i] = 0;
|
|
||||||
engineConfiguration->sparkDwell[i] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
engineConfiguration->sparkDwellBins[5] = 1;
|
|
||||||
engineConfiguration->sparkDwell[5] = 4;
|
|
||||||
|
|
||||||
engineConfiguration->sparkDwellBins[6] = 4500;
|
|
||||||
engineConfiguration->sparkDwell[6] = 4;
|
|
||||||
|
|
||||||
engineConfiguration->sparkDwellBins[7] = 12500;
|
|
||||||
engineConfiguration->sparkDwell[7] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int isCrankingRT(engine_configuration_s *engineConfiguration, int rpm) {
|
|
||||||
return rpm > 0 && rpm < engineConfiguration->crankingSettings.crankingRpm;
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputSignalList ignitionSignals;
|
|
||||||
OutputSignalList injectonSignals;
|
|
||||||
|
|
||||||
void initializeIgnitionActions(float baseAngle, engine_configuration_s *engineConfiguration,
|
|
||||||
engine_configuration2_s *engineConfiguration2) {
|
|
||||||
chDbgCheck(engineConfiguration->cylindersCount > 0, "cylindersCount");
|
|
||||||
ignitionSignals.clear();
|
|
||||||
|
|
||||||
EventHandlerConfiguration *config = &engineConfiguration2->engineEventConfiguration;
|
|
||||||
resetEventList(&config->ignitionEvents);
|
|
||||||
|
|
||||||
switch (engineConfiguration->ignitionMode) {
|
|
||||||
case IM_ONE_COIL:
|
|
||||||
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
|
|
||||||
// todo: extract method
|
|
||||||
float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount;
|
|
||||||
|
|
||||||
registerActuatorEventExt(engineConfiguration, &engineConfiguration2->triggerShape, &config->ignitionEvents,
|
|
||||||
ignitionSignals.add(SPARKOUT_1_OUTPUT), angle);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IM_WASTED_SPARK:
|
|
||||||
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
|
|
||||||
float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount;
|
|
||||||
|
|
||||||
int wastedIndex = i % (engineConfiguration->cylindersCount / 2);
|
|
||||||
|
|
||||||
int id = (getCylinderId(engineConfiguration->firingOrder, wastedIndex) - 1);
|
|
||||||
io_pin_e ioPin = (io_pin_e) (SPARKOUT_1_OUTPUT + id);
|
|
||||||
|
|
||||||
registerActuatorEventExt(engineConfiguration, &engineConfiguration2->triggerShape, &config->ignitionEvents,
|
|
||||||
ignitionSignals.add(ioPin), angle);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case IM_INDIVIDUAL_COILS:
|
|
||||||
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
|
|
||||||
float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount;
|
|
||||||
|
|
||||||
io_pin_e pin = (io_pin_e) ((int) SPARKOUT_1_OUTPUT + getCylinderId(engineConfiguration->firingOrder, i) - 1);
|
|
||||||
registerActuatorEventExt(engineConfiguration, &engineConfiguration2->triggerShape, &config->ignitionEvents,
|
|
||||||
ignitionSignals.add(pin), angle);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
firmwareError("unsupported ignitionMode %d in initializeIgnitionActions()", engineConfiguration->ignitionMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addFuelEvents(engine_configuration_s const *e, engine_configuration2_s *engineConfiguration2,
|
|
||||||
ActuatorEventList *list, injection_mode_e mode) {
|
|
||||||
resetEventList(list);
|
|
||||||
|
|
||||||
trigger_shape_s *s = &engineConfiguration2->triggerShape;
|
|
||||||
|
|
||||||
float baseAngle = e->globalTriggerAngleOffset + e->injectionOffset;
|
|
||||||
|
|
||||||
switch (mode) {
|
|
||||||
case IM_SEQUENTIAL:
|
|
||||||
for (int i = 0; i < e->cylindersCount; i++) {
|
|
||||||
io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + getCylinderId(e->firingOrder, i) - 1);
|
|
||||||
float angle = baseAngle + i * 720.0 / e->cylindersCount;
|
|
||||||
registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IM_SIMULTANEOUS:
|
|
||||||
for (int i = 0; i < e->cylindersCount; i++) {
|
|
||||||
float angle = baseAngle + i * 720.0 / e->cylindersCount;
|
|
||||||
|
|
||||||
for (int j = 0; j < e->cylindersCount; j++) {
|
|
||||||
io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + j);
|
|
||||||
registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IM_BATCH:
|
|
||||||
for (int i = 0; i < e->cylindersCount; i++) {
|
|
||||||
io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + (i % 2));
|
|
||||||
float angle = baseAngle + i * 720.0 / e->cylindersCount;
|
|
||||||
registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
firmwareError("Unexpected injection mode %d", mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Spark dwell time, in milliseconds.
|
|
||||||
*/
|
|
||||||
float getSparkDwellMsT(engine_configuration_s *engineConfiguration, int rpm) {
|
|
||||||
if (isCrankingR(rpm)) {
|
|
||||||
// technically this could be implemented via interpolate2d
|
|
||||||
float angle = engineConfiguration->crankingChargeAngle;
|
|
||||||
return getOneDegreeTimeMs(rpm) * angle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rpm > engineConfiguration->rpmHardLimit) {
|
|
||||||
// technically this could be implemented via interpolate2d by setting everything above rpmHardLimit to zero
|
|
||||||
warning(OBD_PCM_Processor_Fault, "skipping spark due to rpm=%d", rpm);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return interpolate2d(rpm, engineConfiguration->sparkDwellBins, engineConfiguration->sparkDwell, DWELL_CURVE_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void registerActuatorEventExt(engine_configuration_s const *engineConfiguration, trigger_shape_s * s,
|
|
||||||
ActuatorEventList *list, OutputSignal *actuator, float angleOffset) {
|
|
||||||
chDbgCheck(s->size > 0, "uninitialized trigger_shape_s");
|
|
||||||
|
|
||||||
angleOffset = fixAngle(angleOffset + engineConfiguration->globalTriggerAngleOffset);
|
|
||||||
|
|
||||||
int triggerIndexOfZeroEvent = s->triggerShapeSynchPointIndex;
|
|
||||||
|
|
||||||
// todo: migrate to crankAngleRange?
|
|
||||||
float firstAngle = s->wave.switchTimes[triggerIndexOfZeroEvent] * 720;
|
|
||||||
|
|
||||||
// let's find the last trigger angle which is less or equal to the desired angle
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < s->size - 1; i++) {
|
|
||||||
// todo: we need binary search here
|
|
||||||
float angle = fixAngle(s->wave.switchTimes[(triggerIndexOfZeroEvent + i + 1) % s->size] * 720 - firstAngle);
|
|
||||||
if (angle > angleOffset)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// explicit check for zero to avoid issues where logical zero is not exactly zero due to float nature
|
|
||||||
float angle =
|
|
||||||
i == 0 ? 0 : fixAngle(s->wave.switchTimes[(triggerIndexOfZeroEvent + i) % s->size] * 720 - firstAngle);
|
|
||||||
|
|
||||||
chDbgCheck(angleOffset >= angle, "angle constraint violation in registerActuatorEventExt()");
|
|
||||||
|
|
||||||
registerActuatorEvent(list, i, actuator, angleOffset - angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
//float getTriggerEventAngle(int triggerEventIndex) {
|
|
||||||
// return 0;
|
|
||||||
//}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* there is some BS related to isnan in MinGW, so let's have all the issues in one place
|
|
||||||
*/
|
|
||||||
int cisnan(float f) {
|
|
||||||
return *(((int*) (&f))) == 0x7FC00000;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int order_1_THEN_3_THEN_4_THEN2[] = { 1, 3, 4, 2 };
|
|
||||||
|
|
||||||
static int order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[] = { 1, 5, 3, 6, 2, 4 };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param index from zero to cylindersCount - 1
|
|
||||||
* @return cylinderId from one to cylindersCount
|
|
||||||
*/
|
|
||||||
int getCylinderId(firing_order_e firingOrder, int index) {
|
|
||||||
|
|
||||||
switch (firingOrder) {
|
|
||||||
case FO_ONE_CYLINDER:
|
|
||||||
return 1;
|
|
||||||
case FO_1_THEN_3_THEN_4_THEN2:
|
|
||||||
return order_1_THEN_3_THEN_4_THEN2[index];
|
|
||||||
case FO_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4:
|
|
||||||
return order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[index];
|
|
||||||
|
|
||||||
default:
|
|
||||||
firmwareError("getCylinderId not supported for %d", firingOrder);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void prepareOutputSignals(engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2) {
|
|
||||||
|
|
||||||
// todo: move this reset into decoder
|
|
||||||
engineConfiguration2->triggerShape.triggerShapeSynchPointIndex = findTriggerZeroEventIndex(
|
|
||||||
&engineConfiguration2->triggerShape, &engineConfiguration->triggerConfig);
|
|
||||||
|
|
||||||
injectonSignals.clear();
|
|
||||||
EventHandlerConfiguration *config = &engineConfiguration2->engineEventConfiguration;
|
|
||||||
addFuelEvents(engineConfiguration, engineConfiguration2, &config->crankingInjectionEvents,
|
|
||||||
engineConfiguration->crankingInjectionMode);
|
|
||||||
addFuelEvents(engineConfiguration, engineConfiguration2, &config->injectionEvents,
|
|
||||||
engineConfiguration->injectionMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTableBin(float array[], int size, float l, float r) {
|
|
||||||
for (int i = 0; i < size; i++)
|
|
||||||
array[i] = interpolate(0, l, size - 1, r, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFuelRpmBin(engine_configuration_s *engineConfiguration, float l, float r) {
|
|
||||||
setTableBin(engineConfiguration->fuelRpmBins, FUEL_RPM_COUNT, l, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFuelLoadBin(engine_configuration_s *engineConfiguration, float l, float r) {
|
|
||||||
setTableBin(engineConfiguration->fuelLoadBins, FUEL_LOAD_COUNT, l, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTimingRpmBin(engine_configuration_s *engineConfiguration, float l, float r) {
|
|
||||||
setTableBin(engineConfiguration->ignitionRpmBins, IGN_RPM_COUNT, l, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTimingLoadBin(engine_configuration_s *engineConfiguration, float l, float r) {
|
|
||||||
setTableBin(engineConfiguration->ignitionLoadBins, IGN_LOAD_COUNT, l, r);
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
/**
|
|
||||||
* @file engine_math.h
|
|
||||||
*
|
|
||||||
* @date Jul 13, 2013
|
|
||||||
* @author Andrey Belomutskiy, (c) 2012-2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ENGINE_MATH_H_
|
|
||||||
#define ENGINE_MATH_H_
|
|
||||||
|
|
||||||
#include "engine_configuration.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
|
|
||||||
int cisnan(float f);
|
|
||||||
|
|
||||||
//float getDefaultVE(int rpm);
|
|
||||||
|
|
||||||
float getDefaultFuel(int rpm, float map);
|
|
||||||
//float getTCharge(int rpm, int tps, float coolantTemp, float airTemp);
|
|
||||||
|
|
||||||
float getOneDegreeTimeMs(int rpm);
|
|
||||||
float getCrankshaftRevolutionTimeMs(int rpm);
|
|
||||||
|
|
||||||
int isCrankingRT(engine_configuration_s *engineConfiguration, int rpm);
|
|
||||||
#define isCrankingR(rpm) isCrankingRT(engineConfiguration, rpm)
|
|
||||||
|
|
||||||
float fixAngle(float angle);
|
|
||||||
float getTriggerEventAngle(int triggerEventIndex);
|
|
||||||
|
|
||||||
float getEngineLoadT(engine_configuration_s *engineConfiguration);
|
|
||||||
#define getEngineLoad() getEngineLoadT(engineConfiguration)
|
|
||||||
|
|
||||||
void initializeIgnitionActions(float baseAngle, engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2);
|
|
||||||
void addFuelEvents(engine_configuration_s const *e, engine_configuration2_s *engineConfiguration2, ActuatorEventList *list, injection_mode_e mode);
|
|
||||||
|
|
||||||
float getSparkDwellMsT(engine_configuration_s *engineConfiguration, int rpm);
|
|
||||||
#define getSparkDwellMs(rpm) getSparkDwellMsT(engineConfiguration, rpm)
|
|
||||||
|
|
||||||
void registerActuatorEventExt(engine_configuration_s const *engineConfiguration, trigger_shape_s * s, ActuatorEventList *list, OutputSignal *actuator, float angleOffset);
|
|
||||||
|
|
||||||
int getCylinderId(firing_order_e firingOrder, int index);
|
|
||||||
void prepareOutputSignals(engine_configuration_s *engineConfiguration,
|
|
||||||
engine_configuration2_s *engineConfiguration2);
|
|
||||||
|
|
||||||
void setTableBin(float array[], int size, float l, float r);
|
|
||||||
void setFuelRpmBin(engine_configuration_s *engineConfiguration, float l, float r);
|
|
||||||
void setFuelLoadBin(engine_configuration_s *engineConfiguration, float l, float r);
|
|
||||||
void setTimingRpmBin(engine_configuration_s *engineConfiguration, float l, float r);
|
|
||||||
void setTimingLoadBin(engine_configuration_s *engineConfiguration, float l, float r);
|
|
||||||
|
|
||||||
void setSingleCoilDwell(engine_configuration_s *engineConfiguration);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
#endif /* ENGINE_MATH_H_ */
|
|
|
@ -1,89 +0,0 @@
|
||||||
/**
|
|
||||||
* @file event_queue.cpp
|
|
||||||
* This is a data structure which keeps track of all pending events
|
|
||||||
* Implemented as a linked list, which is fine since the number of
|
|
||||||
* pending events is pretty low
|
|
||||||
* todo: MAYBE migrate to a better data structure, but that's low priority
|
|
||||||
*
|
|
||||||
* this data structure is NOT thread safe
|
|
||||||
*
|
|
||||||
* @date Apr 17, 2014
|
|
||||||
* @author Andrey Belomutskiy, (c) 2012-2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "event_queue.h"
|
|
||||||
#include "efitime.h"
|
|
||||||
#include "utlist.h"
|
|
||||||
|
|
||||||
EventQueue::EventQueue() {
|
|
||||||
head = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventQueue::insertTask(scheduling_s *scheduling, uint64_t nowUs, int delayUs, schfunc_t callback, void *param) {
|
|
||||||
if (callback == NULL)
|
|
||||||
firmwareError("NULL callback");
|
|
||||||
uint64_t time = nowUs + delayUs;
|
|
||||||
|
|
||||||
scheduling->momentUs = time;
|
|
||||||
#if EFI_SIGNAL_EXECUTOR_ONE_TIMER
|
|
||||||
scheduling->callback = callback;
|
|
||||||
scheduling->param = param;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
scheduling_s * elt;
|
|
||||||
LL_FOREACH(head, elt)
|
|
||||||
{
|
|
||||||
if (elt == scheduling) {
|
|
||||||
firmwareError("re-adding element");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LL_PREPEND(head, scheduling);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventQueue::insertTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) {
|
|
||||||
insertTask(scheduling, getTimeNowUs(), delayUs, callback, param);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the timestamp of the soonest pending action
|
|
||||||
*/
|
|
||||||
uint64_t EventQueue::getNextEventTime(uint64_t nowUs) {
|
|
||||||
scheduling_s * elt;
|
|
||||||
// this is a large value which is expected to be larger than any real time
|
|
||||||
uint64_t result = EMPTY_QUEUE;
|
|
||||||
|
|
||||||
LL_FOREACH(head, elt)
|
|
||||||
{
|
|
||||||
if (elt->momentUs <= nowUs) {
|
|
||||||
// todo: I am not so sure about this branch
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (elt->momentUs < result)
|
|
||||||
result = elt->momentUs;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoke all pending actions prior to specified timestamp
|
|
||||||
*/
|
|
||||||
void EventQueue::executeAll(uint64_t now) {
|
|
||||||
scheduling_s * elt, *tmp;
|
|
||||||
|
|
||||||
// here we need safe iteration because we are removing elements
|
|
||||||
LL_FOREACH_SAFE(head, elt, tmp)
|
|
||||||
{
|
|
||||||
if (elt->momentUs <= now) {
|
|
||||||
LL_DELETE(head, elt);
|
|
||||||
#if EFI_SIGNAL_EXECUTOR_ONE_TIMER
|
|
||||||
elt->callback(elt->param);
|
|
||||||
#endif /* EFI_SIGNAL_EXECUTOR_ONE_TIMER */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventQueue::clear(void) {
|
|
||||||
head = NULL;
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/**
|
|
||||||
* @file event_queue.h
|
|
||||||
*
|
|
||||||
* @date Apr 17, 2014
|
|
||||||
* @author Andrey Belomutskiy, (c) 2012-2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "signal_executor.h"
|
|
||||||
|
|
||||||
#ifndef EVENT_SCHEDULER_H_
|
|
||||||
#define EVENT_SCHEDULER_H_
|
|
||||||
|
|
||||||
#define EMPTY_QUEUE 0x0FFFFFFFFFFFFFFFLL
|
|
||||||
|
|
||||||
class EventQueue {
|
|
||||||
public:
|
|
||||||
EventQueue();
|
|
||||||
|
|
||||||
void insertTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param);
|
|
||||||
void insertTask(scheduling_s *scheduling, uint64_t nowUs, int delayUs, schfunc_t callback, void *param);
|
|
||||||
|
|
||||||
void executeAll(uint64_t now);
|
|
||||||
|
|
||||||
uint64_t getNextEventTime(uint64_t nowUs);
|
|
||||||
void clear(void);
|
|
||||||
private:
|
|
||||||
scheduling_s *head;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* EVENT_SCHEDULER_H_ */
|
|
|
@ -10,40 +10,5 @@
|
||||||
|
|
||||||
#if EFI_ENGINE_SNOW_BLOWER
|
#if EFI_ENGINE_SNOW_BLOWER
|
||||||
|
|
||||||
extern OutputSignal injectorOut1;
|
|
||||||
|
|
||||||
static Logging logger;
|
|
||||||
|
|
||||||
float getVRef(void) {
|
|
||||||
return 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getFuelMs() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define STROKE_TIME_CONSTANT2 (1000 * 60 * RPM_MULT * TICKS_IN_MS)
|
|
||||||
|
|
||||||
static int convertAngleToSysticks(int rpm, int advance) {
|
|
||||||
return (int) (advance * STROKE_TIME_CONSTANT2 / 360 / rpm);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void onShaftSignal(int ckpSignalType) {
|
|
||||||
|
|
||||||
if (ckpSignalType != CKP_PRIMARY_DOWN)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int offset = convertAngleToSysticks(getCurrentRpm(), 10);
|
|
||||||
|
|
||||||
scheduleOutput(&injectorOut1, offset, TICKS_IN_MS);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void initMainEventListener() {
|
|
||||||
initLogging(&logger, "main event handler", logger.DEFAULT_BUFFER, sizeof(logger.DEFAULT_BUFFER));
|
|
||||||
|
|
||||||
registerCkpListener(&onShaftSignal, "main loop");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -125,6 +125,19 @@ uint64_t getTimeNowUs(void) {
|
||||||
return halTime.get() / (CORE_CLOCK / 1000000);
|
return halTime.get() / (CORE_CLOCK / 1000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t getHalTimer(void) {
|
||||||
|
return halTime.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
efitimems_t currentTimeMillis(void) {
|
||||||
|
// todo: migrate to getTimeNowUs? or not?
|
||||||
|
return chTimeNow() / TICKS_IN_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getTimeNowSeconds(void) {
|
||||||
|
return chTimeNow() / CH_FREQUENCY;
|
||||||
|
}
|
||||||
|
|
||||||
static void onEveny10Milliseconds(void *arg) {
|
static void onEveny10Milliseconds(void *arg) {
|
||||||
/**
|
/**
|
||||||
* We need to push current value into the 64 bit counter often enough so that we do not miss an overflow
|
* We need to push current value into the 64 bit counter often enough so that we do not miss an overflow
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "error_handling.h"
|
#include "error_handling.h"
|
||||||
#include "wave_math.h"
|
#include "wave_math.h"
|
||||||
|
|
||||||
static time_t timeOfPreviousWarning = (systime_t) -10 * CH_FREQUENCY;
|
static time_t timeOfPreviousWarning = -10;
|
||||||
|
|
||||||
static Logging logger;
|
static Logging logger;
|
||||||
|
|
||||||
|
@ -19,8 +19,8 @@ extern int warningEnabled;
|
||||||
* @returns TRUE in case there are too many warnings
|
* @returns TRUE in case there are too many warnings
|
||||||
*/
|
*/
|
||||||
int warning(obd_code_e code, const char *fmt, ...) {
|
int warning(obd_code_e code, const char *fmt, ...) {
|
||||||
time_t now = chTimeNow();
|
int now = getTimeNowSeconds();
|
||||||
if (overflowDiff(now, timeOfPreviousWarning) < CH_FREQUENCY || !warningEnabled)
|
if (now == timeOfPreviousWarning || !warningEnabled)
|
||||||
return TRUE; // we just had another warning, let's not spam
|
return TRUE; // we just had another warning, let's not spam
|
||||||
timeOfPreviousWarning = now;
|
timeOfPreviousWarning = now;
|
||||||
|
|
||||||
|
|
|
@ -62,9 +62,9 @@ void writeToFlash(void) {
|
||||||
scheduleMsg(&logger, "Reseting flash=%d", FLASH_USAGE);
|
scheduleMsg(&logger, "Reseting flash=%d", FLASH_USAGE);
|
||||||
flashErase(FLASH_ADDR, FLASH_USAGE);
|
flashErase(FLASH_ADDR, FLASH_USAGE);
|
||||||
scheduleMsg(&logger, "Flashing with CRC=%d", result);
|
scheduleMsg(&logger, "Flashing with CRC=%d", result);
|
||||||
time_t now = chTimeNow();
|
efitimems_t nowMs = currentTimeMillis();
|
||||||
result = flashWrite(FLASH_ADDR, (const char *) &flashState, FLASH_USAGE);
|
result = flashWrite(FLASH_ADDR, (const char *) &flashState, FLASH_USAGE);
|
||||||
scheduleMsg(&logger, "Flash programmed in (ms): %d", chTimeNow() - now);
|
scheduleMsg(&logger, "Flash programmed in (ms): %d", currentTimeMillis() - nowMs);
|
||||||
scheduleMsg(&logger, "Flashed: %d", result);
|
scheduleMsg(&logger, "Flashed: %d", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ static msg_t ivThread(int param) {
|
||||||
if (!isIdleControlActive)
|
if (!isIdleControlActive)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int nowSec = chTimeNowSeconds();
|
int nowSec = getTimeNowSeconds();
|
||||||
|
|
||||||
int newValue = getIdle(&idle, getRpm(), nowSec);
|
int newValue = getIdle(&idle, getRpm(), nowSec);
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ void mapAveragingCallback(adcsample_t value) {
|
||||||
|
|
||||||
if (engineConfiguration->analogChartMode == AC_MAP)
|
if (engineConfiguration->analogChartMode == AC_MAP)
|
||||||
if (perRevolutionCounter % FAST_MAP_CHART_SKIP_FACTOR == 0)
|
if (perRevolutionCounter % FAST_MAP_CHART_SKIP_FACTOR == 0)
|
||||||
acAddData(getCrankshaftAngle(chTimeNow()), currentPressure);
|
acAddData(getCrankshaftAngle(getTimeNowUs()), currentPressure);
|
||||||
|
|
||||||
currentMaxPressure = maxF(currentMaxPressure, currentPressure);
|
currentMaxPressure = maxF(currentMaxPressure, currentPressure);
|
||||||
|
|
||||||
|
|
|
@ -1,163 +0,0 @@
|
||||||
/**
|
|
||||||
* @file signal_executor.c
|
|
||||||
*
|
|
||||||
* todo: we should split this file into two:
|
|
||||||
* one for pure scheduling and another one for signal output which would
|
|
||||||
* use the scheduling
|
|
||||||
*
|
|
||||||
* @date Dec 4, 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 "signal_executor.h"
|
|
||||||
|
|
||||||
#if EFI_WAVE_CHART
|
|
||||||
#include "rpm_calculator.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if EFI_WAVE_ANALYZER
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signal executors feed digital events right into WaveChart used by Sniffer tab of Dev Console
|
|
||||||
*/
|
|
||||||
#include "wave_analyzer.h"
|
|
||||||
|
|
||||||
#endif /* EFI_WAVE_ANALYZER */
|
|
||||||
|
|
||||||
#if EFI_PROD_CODE || EFI_SIMULATOR
|
|
||||||
static Logging logger;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void initSignalExecutor(void) {
|
|
||||||
#if EFI_PROD_CODE || EFI_SIMULATOR
|
|
||||||
initLogging(&logger, "s exec");
|
|
||||||
#endif
|
|
||||||
initSignalExecutorImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
void initOutputSignalBase(OutputSignal *signal) {
|
|
||||||
signal->status = IDLE;
|
|
||||||
// signal->last_scheduling_time = 0;
|
|
||||||
signal->initialized = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void turnHigh(OutputSignal *signal) {
|
|
||||||
#if EFI_DEFAILED_LOGGING
|
|
||||||
// signal->hi_time = hTimeNow();
|
|
||||||
#endif /* EFI_DEFAILED_LOGGING */
|
|
||||||
io_pin_e pin = signal->io_pin;
|
|
||||||
// turn the output level ACTIVE
|
|
||||||
// todo: this XOR should go inside the setOutputPinValue method
|
|
||||||
setOutputPinValue(pin, TRUE);
|
|
||||||
// sleep for the needed duration
|
|
||||||
|
|
||||||
#if EFI_PROD_CODE || EFI_SIMULATOR
|
|
||||||
if(
|
|
||||||
pin == SPARKOUT_1_OUTPUT ||
|
|
||||||
pin == SPARKOUT_3_OUTPUT) {
|
|
||||||
// time_t now = hTimeNow();
|
|
||||||
// float an = getCrankshaftAngle(now);
|
|
||||||
// scheduleMsg(&logger, "spark up%d %d", pin, now);
|
|
||||||
// scheduleMsg(&logger, "spark angle %d %f", (int)an, an);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if EFI_WAVE_CHART
|
|
||||||
addWaveChartEvent(signal->name, "up", "");
|
|
||||||
#endif /* EFI_WAVE_ANALYZER */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void turnLow(OutputSignal *signal) {
|
|
||||||
// turn off the output
|
|
||||||
// todo: this XOR should go inside the setOutputPinValue method
|
|
||||||
setOutputPinValue(signal->io_pin, FALSE);
|
|
||||||
|
|
||||||
#if EFI_DEFAILED_LOGGING
|
|
||||||
systime_t after = hTimeNow();
|
|
||||||
debugInt(&signal->logging, "a_time", after - signal->hi_time);
|
|
||||||
scheduleLogging(&signal->logging);
|
|
||||||
#endif /* EFI_DEFAILED_LOGGING */
|
|
||||||
|
|
||||||
#if EFI_WAVE_CHART
|
|
||||||
addWaveChartEvent(signal->name, "down", "");
|
|
||||||
#endif /* EFI_WAVE_ANALYZER */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param delay the number of ticks before the output signal
|
|
||||||
* immediate output if delay is zero
|
|
||||||
* @param dwell the number of ticks of output duration
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int getRevolutionCounter(void);
|
|
||||||
|
|
||||||
void scheduleOutput(OutputSignal *signal, float delayMs, float durationMs) {
|
|
||||||
if (durationMs < 0) {
|
|
||||||
firmwareError("duration cannot be negative: %d", durationMs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduleOutputBase(signal, delayMs, durationMs);
|
|
||||||
|
|
||||||
int index = getRevolutionCounter() % 2;
|
|
||||||
scheduling_s * sUp = &signal->signalTimerUp[index];
|
|
||||||
scheduling_s * sDown = &signal->signalTimerDown[index];
|
|
||||||
|
|
||||||
scheduleTask(sUp, MS2US(delayMs), (schfunc_t) &turnHigh, (void *) signal);
|
|
||||||
scheduleTask(sDown, MS2US(delayMs + durationMs), (schfunc_t) &turnLow, (void*)signal);
|
|
||||||
|
|
||||||
// signal->last_scheduling_time = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduleOutputBase(OutputSignal *signal, float delayMs, float durationMs) {
|
|
||||||
/**
|
|
||||||
* it's better to check for the exact 'TRUE' value since otherwise
|
|
||||||
* we would accept any memory garbage
|
|
||||||
*/
|
|
||||||
chDbgCheck(signal->initialized == TRUE, "Signal not initialized");
|
|
||||||
// signal->offset = offset;
|
|
||||||
// signal->duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char *getPinName(io_pin_e io_pin) {
|
|
||||||
switch (io_pin) {
|
|
||||||
case SPARKOUT_1_OUTPUT:
|
|
||||||
return "Spark 1";
|
|
||||||
case SPARKOUT_2_OUTPUT:
|
|
||||||
return "Spark 2";
|
|
||||||
case SPARKOUT_3_OUTPUT:
|
|
||||||
return "Spark 3";
|
|
||||||
case SPARKOUT_4_OUTPUT:
|
|
||||||
return "Spark 4";
|
|
||||||
|
|
||||||
case INJECTOR_1_OUTPUT:
|
|
||||||
return "Injector 1";
|
|
||||||
case INJECTOR_2_OUTPUT:
|
|
||||||
return "Injector 2";
|
|
||||||
case INJECTOR_3_OUTPUT:
|
|
||||||
return "Injector 3";
|
|
||||||
case INJECTOR_4_OUTPUT:
|
|
||||||
return "Injector 4";
|
|
||||||
case INJECTOR_5_OUTPUT:
|
|
||||||
return "Injector 5";
|
|
||||||
default:
|
|
||||||
return "No name";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
/**
|
|
||||||
* @file signal_executor.h
|
|
||||||
* @brief Asynchronous output signal header
|
|
||||||
*
|
|
||||||
* @date Feb 10, 2013
|
|
||||||
* @author Andrey Belomutskiy, (c) 2012-2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SPARKOUT_H_
|
|
||||||
#define SPARKOUT_H_
|
|
||||||
|
|
||||||
#include "rusefi_enums.h"
|
|
||||||
#include "global.h"
|
|
||||||
#include "efifeatures.h"
|
|
||||||
#include "io_pins.h"
|
|
||||||
|
|
||||||
#if EFI_PROD_CODE
|
|
||||||
#include "datalogging.h"
|
|
||||||
#endif /* EFI_PROD_CODE */
|
|
||||||
|
|
||||||
#if EFI_SIGNAL_EXECUTOR_SLEEP
|
|
||||||
#include "signal_executor_sleep.h"
|
|
||||||
#endif /* EFI_SIGNAL_EXECUTOR_SLEEP */
|
|
||||||
|
|
||||||
#if EFI_SIGNAL_EXECUTOR_SINGLE_TIMER
|
|
||||||
#include "signal_executor_single_timer.h"
|
|
||||||
#endif /* EFI_SIGNAL_EXECUTOR_SINGLE_TIMER */
|
|
||||||
|
|
||||||
typedef void (*schfunc_t)(void *);
|
|
||||||
|
|
||||||
typedef struct scheduling_struct scheduling_s;
|
|
||||||
struct scheduling_struct {
|
|
||||||
//int initialized;
|
|
||||||
#if EFI_SIGNAL_EXECUTOR_SLEEP
|
|
||||||
VirtualTimer timer;
|
|
||||||
#endif /* EFI_SIGNAL_EXECUTOR_SLEEP */
|
|
||||||
#if EFI_SIGNAL_EXECUTOR_SINGLE_TIMER
|
|
||||||
volatile time_t moment;
|
|
||||||
#endif /* EFI_SIGNAL_EXECUTOR_SINGLE_TIMER */
|
|
||||||
|
|
||||||
volatile uint64_t momentUs;
|
|
||||||
#if EFI_SIGNAL_EXECUTOR_ONE_TIMER
|
|
||||||
schfunc_t callback;
|
|
||||||
void *param;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
scheduling_s *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
IDLE = 0, ACTIVE
|
|
||||||
} executor_status_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Asynchronous output signal data structure
|
|
||||||
*/
|
|
||||||
typedef struct OutputSignal_struct OutputSignal;
|
|
||||||
struct OutputSignal_struct {
|
|
||||||
/**
|
|
||||||
* name of this signal
|
|
||||||
*/
|
|
||||||
char *name;
|
|
||||||
io_pin_e io_pin;
|
|
||||||
#if 0 // depricated
|
|
||||||
// time in system ticks
|
|
||||||
volatile int offset;
|
|
||||||
// time in system ticks
|
|
||||||
volatile int duration;
|
|
||||||
#endif
|
|
||||||
int initialized;
|
|
||||||
|
|
||||||
// time_t last_scheduling_time;
|
|
||||||
// time_t hi_time;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We are alternating instances so that events which extend into next revolution are not overriden while
|
|
||||||
* scheduling next revolution events
|
|
||||||
*/
|
|
||||||
scheduling_s signalTimerUp[2];
|
|
||||||
scheduling_s signalTimerDown[2];
|
|
||||||
|
|
||||||
executor_status_t status;
|
|
||||||
|
|
||||||
#if EFI_SIGNAL_EXECUTOR_HW_TIMER
|
|
||||||
// todo
|
|
||||||
#endif
|
|
||||||
|
|
||||||
OutputSignal *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
void initOutputSignal(OutputSignal *signal, io_pin_e ioPin);
|
|
||||||
void scheduleOutput(OutputSignal *signal, float delayMs, float durationMs);
|
|
||||||
void initOutputSignalBase(OutputSignal *signal);
|
|
||||||
void scheduleOutputBase(OutputSignal *signal, float delayMs, float durationMs);
|
|
||||||
|
|
||||||
void initSignalExecutor(void);
|
|
||||||
void initSignalExecutorImpl(void);
|
|
||||||
void scheduleTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param);
|
|
||||||
void scheduleByAngle(scheduling_s *timer, float angle, schfunc_t callback, void *param);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
#endif /* SPARKOUT_H_ */
|
|
|
@ -1,74 +0,0 @@
|
||||||
/**
|
|
||||||
* @file signal_executor_single_timer_algo.c
|
|
||||||
*
|
|
||||||
* @date Nov 28, 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 "signal_executor.h"
|
|
||||||
#include "signal_executor_single_timer_algo.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "utlist.h"
|
|
||||||
#include "io_pins.h"
|
|
||||||
|
|
||||||
#if EFI_WAVE_ANALYZER
|
|
||||||
#include "wave_analyzer.h"
|
|
||||||
#include "wave_chart.h"
|
|
||||||
extern WaveChart waveChart;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if EFI_SIGNAL_EXECUTOR_SINGLE_TIMER
|
|
||||||
/**
|
|
||||||
* @brief Output list
|
|
||||||
*
|
|
||||||
* List of all active output signals
|
|
||||||
* This is actually the head of the list.
|
|
||||||
* When the list is empty (initial state) the head of the list should be NULL.
|
|
||||||
* This is by design.
|
|
||||||
*/
|
|
||||||
OutputSignal *st_output_list = NULL;
|
|
||||||
|
|
||||||
inline void registerSignal(OutputSignal *signal) {
|
|
||||||
LL_APPEND(st_output_list, signal);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setOutputPinValue(io_pin_e pin, int value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return time of next event within for this signal
|
|
||||||
* @todo Find better name.
|
|
||||||
*/
|
|
||||||
inline time_t toggleSignalIfNeeded(OutputSignal *out, time_t now) {
|
|
||||||
// chDbgCheck(out!=NULL, "out is NULL");
|
|
||||||
// chDbgCheck(out->io_pin < IO_PIN_COUNT, "pin assertion");
|
|
||||||
time_t last = out->last_scheduling_time;
|
|
||||||
//estimated = last + out->timing[out->status];
|
|
||||||
time_t estimated = last + GET_DURATION(out);
|
|
||||||
if (now >= estimated) {
|
|
||||||
out->status ^= 1; /* toggle status */
|
|
||||||
//setOutputPinValue(out->io_pin, out->status); /* Toggle output */
|
|
||||||
palWritePad(GPIOE, 5, out->status);
|
|
||||||
#if EFI_WAVE_ANALYZER
|
|
||||||
// addWaveChartEvent(out->name, out->status ? "up" : "down", "");
|
|
||||||
#endif /* EFI_WAVE_ANALYZER */
|
|
||||||
|
|
||||||
// out->last_scheduling_time = now; /* store last update */
|
|
||||||
estimated = now + GET_DURATION(out); /* update estimation */
|
|
||||||
}
|
|
||||||
return estimated - now;
|
|
||||||
}
|
|
||||||
#endif /* EFI_SIGNAL_EXECUTOR_SINGLE_TIMER */
|
|
|
@ -1,14 +0,0 @@
|
||||||
/**
|
|
||||||
* @file snow_blower.c
|
|
||||||
* @brief Default configuration of a single-cylinder engine
|
|
||||||
*
|
|
||||||
* @date Sep 9, 2013
|
|
||||||
* @author Andrey Belomutskiy, (c) 2012-2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
#if EFI_ENGINE_SNOW_BLOWER
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
146
wave_chart.c
146
wave_chart.c
|
@ -1,146 +0,0 @@
|
||||||
/**
|
|
||||||
* @file wave_chart.c
|
|
||||||
* @brief Dev console wave sniffer logic
|
|
||||||
*
|
|
||||||
* Here we have our own build-in logic analyzer. The data we aggregate here is sent to the
|
|
||||||
* java UI Dev Console so that it can be displayed nicely in the Sniffer tab.
|
|
||||||
*
|
|
||||||
* Both external events (see wave_analyzer.c) and internal (see signal executors) are supported
|
|
||||||
*
|
|
||||||
* @date Jun 23, 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 "wave_chart.h"
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
#if EFI_WAVE_CHART
|
|
||||||
|
|
||||||
#include "eficonsole.h"
|
|
||||||
#include "status_loop.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define CHART_DELIMETER "!"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the number of events in the digital chart which would be displayed
|
|
||||||
* on the 'digital sniffer' pane
|
|
||||||
*/
|
|
||||||
#if EFI_PROD_CODE
|
|
||||||
static volatile int chartSize = 100;
|
|
||||||
#else
|
|
||||||
// need more events for automated test
|
|
||||||
static volatile int chartSize = 200;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int isChartActive = TRUE;
|
|
||||||
//static int isChartActive = FALSE;
|
|
||||||
|
|
||||||
//#define DEBUG_WAVE 1
|
|
||||||
|
|
||||||
#if DEBUG_WAVE
|
|
||||||
static Logging debugLogging;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static Logging logger;
|
|
||||||
|
|
||||||
void resetWaveChart(WaveChart *chart) {
|
|
||||||
#if DEBUG_WAVE
|
|
||||||
scheduleSimpleMsg(&debugLogging, "reset while at ", chart->counter);
|
|
||||||
#endif
|
|
||||||
resetLogging(&chart->logging);
|
|
||||||
chart->counter = 0;
|
|
||||||
appendPrintf(&chart->logging, "wave_chart%s", DELIMETER);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char LOGGING_BUFFER[5000] __attribute__((section(".ccm")));
|
|
||||||
|
|
||||||
static void printStatus(void) {
|
|
||||||
scheduleIntValue(&logger, "chart", isChartActive);
|
|
||||||
scheduleIntValue(&logger, "chartsize", chartSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setChartActive(int value) {
|
|
||||||
isChartActive = value;
|
|
||||||
printStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setChartSize(int newSize) {
|
|
||||||
if (newSize < 5)
|
|
||||||
return;
|
|
||||||
chartSize = newSize;
|
|
||||||
printStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void publishChartIfFull(WaveChart *chart) {
|
|
||||||
if (isWaveChartFull(chart)) {
|
|
||||||
publishChart(chart);
|
|
||||||
resetWaveChart(chart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int isWaveChartFull(WaveChart *chart) {
|
|
||||||
return chart->counter >= chartSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void publishChart(WaveChart *chart) {
|
|
||||||
appendPrintf(&chart->logging, DELIMETER);
|
|
||||||
#if DEBUG_WAVE
|
|
||||||
Logging *l = &chart->logging;
|
|
||||||
scheduleSimpleMsg(&debugLogging, "IT'S TIME", strlen(l->buffer));
|
|
||||||
#endif
|
|
||||||
if (isChartActive && getFullLog())
|
|
||||||
scheduleLogging(&chart->logging);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Register a change in sniffed signal
|
|
||||||
*/
|
|
||||||
void addWaveChartEvent3(WaveChart *chart, char *name, char * msg, char * msg2) {
|
|
||||||
chDbgCheck(chart->isInitialized, "chart not initialized");
|
|
||||||
#if DEBUG_WAVE
|
|
||||||
scheduleSimpleMsg(&debugLogging, "current", chart->counter);
|
|
||||||
#endif
|
|
||||||
if (isWaveChartFull(chart))
|
|
||||||
return;
|
|
||||||
lockOutputBuffer(); // we have multiple threads writing to the same output buffer
|
|
||||||
appendPrintf(&chart->logging, "%s%s%s%s", name, CHART_DELIMETER, msg, CHART_DELIMETER);
|
|
||||||
int time100 = getTimeNowUs() / 10;
|
|
||||||
appendPrintf(&chart->logging, "%d%s%s", time100, msg2, CHART_DELIMETER);
|
|
||||||
chart->counter++;
|
|
||||||
unlockOutputBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
void initWaveChart(WaveChart *chart) {
|
|
||||||
initLogging(&logger, "wave info");
|
|
||||||
|
|
||||||
if (!isChartActive)
|
|
||||||
printMsg(&logger, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! chart disabled");
|
|
||||||
|
|
||||||
printStatus();
|
|
||||||
|
|
||||||
initLoggingExt(&chart->logging, "wave chart", LOGGING_BUFFER, sizeof(LOGGING_BUFFER));
|
|
||||||
chart->isInitialized = TRUE;
|
|
||||||
#if DEBUG_WAVE
|
|
||||||
initLoggingExt(&debugLogging, "wave chart debug", &debugLogging.DEFAULT_BUFFER, sizeof(debugLogging.DEFAULT_BUFFER));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
resetWaveChart(chart);
|
|
||||||
addConsoleActionI("chartsize", setChartSize);
|
|
||||||
addConsoleActionI("chart", setChartActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* EFI_WAVE_CHART */
|
|
Loading…
Reference in New Issue