diff --git a/firmware/Makefile b/firmware/Makefile
index 3fcf139b46..ccf2bc06e4 100644
--- a/firmware/Makefile
+++ b/firmware/Makefile
@@ -119,7 +119,6 @@ CSRC = $(PORTSRC) \
$(UTILSRC) \
$(ENGINES_SRC) \
$(CONSOLESRC) \
- $(TUNERSTUDIOSRC) \
$(CONSOLEUTILSRC) \
$(HALSRC) \
$(EMULATIONSRC) \
@@ -145,6 +144,7 @@ CPPSRC = $(CHCPPSRC) \
$(SYSTEMSRC_CPP) \
$(ENGINES_SRC_CPP) \
$(HW_LAYER_SRC_CPP) \
+ $(TUNERSTUDIO_SRC_CPP) \
$(CONSOLE_SRC_CPP) \
$(CONTROLLERS_SENSORS_SRC_CPP) \
$(CONTROLLERS_SRC_CPP) \
diff --git a/firmware/config/boards/arro_board.h b/firmware/config/boards/arro_board.h
index 2d480c0859..55e7c0327f 100644
--- a/firmware/config/boards/arro_board.h
+++ b/firmware/config/boards/arro_board.h
@@ -49,7 +49,7 @@
#define STM32_PWM_USE_TIM8 TRUE // slow adc
#define STM32_PWM_USE_TIM9 FALSE
-#define STM32_SPI_USE_SPI1 FALSE
+#define STM32_SPI_USE_SPI1 TRUE
#define STM32_SPI_USE_SPI2 FALSE // external ADC
#define STM32_SPI_USE_SPI3 TRUE // potentiometer
@@ -80,6 +80,16 @@
//#define SPI_CS4_PIN 10
//#define SPI_SD_MODULE_PORT GPIOD
//#define SPI_SD_MODULE_PIN 11
+
+#define EFI_SPI1_SCK_PORT GPIOB
+#define EFI_SPI1_SCK_PIN 3
+#define EFI_SPI1_MISO_PORT GPIOB
+#define EFI_SPI1_MISO_PIN 4
+#define EFI_SPI1_MOSI_PORT GPIOB
+#define EFI_SPI1_MOSI_PIN 5
+#define EFI_SPI1_AF 5
+
+
#define EFI_SPI2_SCK_PORT GPIOB
#define EFI_SPI2_SCK_PIN 13
#define EFI_SPI2_MISO_PORT GPIOB
@@ -88,17 +98,14 @@
#define EFI_SPI2_MOSI_PIN 15
#define EFI_SPI2_AF 5
+
/**
* This section is for right-side center SPI
*/
-#define SPI_CS1_PORT GPIOD
-#define SPI_CS1_PIN 7
// this is pointing into the sky for now - conflict with I2C
#define SPI_CS2_PORT GPIOH
// this is pointing into the sky for now - conflict with I2C
#define SPI_CS2_PIN 0
-#define SPI_CS3_PORT GPIOD
-#define SPI_CS3_PIN 5
#define SPI_CS4_PORT GPIOD
#define SPI_CS4_PIN 3
#define SPI_SD_MODULE_PORT GPIOD
diff --git a/firmware/config/efifeatures.h b/firmware/config/efifeatures.h
index 0bb4526766..0ba4b9e9bf 100644
--- a/firmware/config/efifeatures.h
+++ b/firmware/config/efifeatures.h
@@ -62,8 +62,8 @@
* MCP42010 digital potentiometer support. This could be useful if you are stimulating some
* stock ECU
*/
-#define EFI_POTENTIOMETER FALSE
-//#define EFI_POTENTIOMETER TRUE
+//#define EFI_POTENTIOMETER FALSE
+#define EFI_POTENTIOMETER TRUE
#define EFI_INTERNAL_ADC TRUE
diff --git a/firmware/config/engines/GY6_139QMB.cpp b/firmware/config/engines/GY6_139QMB.cpp
index 82257d2bdf..a7cafe0f6e 100644
--- a/firmware/config/engines/GY6_139QMB.cpp
+++ b/firmware/config/engines/GY6_139QMB.cpp
@@ -25,8 +25,9 @@ void setGy6139qmbDefaultEngineConfiguration(engine_configuration_s *engineConfig
/**
* We treat the trigger as 1/0 toothed wheel
*/
- engineConfiguration->triggerConfig.totalToothCount = 1;
- engineConfiguration->triggerConfig.skippedToothCount = 0;
- engineConfiguration->triggerConfig.isSynchronizationNeeded = false;
- engineConfiguration->needSecondTriggerInput = false;
+ engineConfiguration->triggerConfig.triggerType = TT_TOOTHED_WHEEL;
+ engineConfiguration->triggerConfig.customTotalToothCount = 1;
+ engineConfiguration->triggerConfig.customSkippedToothCount = 0;
+ engineConfiguration->triggerConfig.customIsSynchronizationNeeded = false;
+//todo engineConfiguration2->triggerShape.needSecondTriggerInput = false;
}
diff --git a/firmware/config/engines/citroenBerlingoTU3JP.cpp b/firmware/config/engines/citroenBerlingoTU3JP.cpp
index ff28d5f41a..c8cffc5444 100644
--- a/firmware/config/engines/citroenBerlingoTU3JP.cpp
+++ b/firmware/config/engines/citroenBerlingoTU3JP.cpp
@@ -15,12 +15,7 @@
void setCitroenBerlingoTU3JPConfiguration(engine_configuration_s *engineConfiguration, board_configuration_s *boardConfiguration) {
engineConfiguration->engineType = CITROEN_TU3JP;
- //engineConfiguration->triggerConfig.triggerType = todo 60_2
-
- setToothedWheelConfiguration(engineConfiguration, 60, 2);
-
- setTriggerSynchronizationGap(&engineConfiguration->triggerConfig, 2.5);
-
+ engineConfiguration->triggerConfig.triggerType = TT_TOOTHED_WHEEL_60_2;
// set_cranking_injection_mode 0
engineConfiguration->crankingInjectionMode = IM_SIMULTANEOUS;
diff --git a/firmware/config/engines/dodge_neon.cpp b/firmware/config/engines/dodge_neon.cpp
index 7a8b918ee6..f3893caa76 100644
--- a/firmware/config/engines/dodge_neon.cpp
+++ b/firmware/config/engines/dodge_neon.cpp
@@ -42,11 +42,6 @@ void setDodgeNeonEngineConfiguration(engine_configuration_s *engineConfiguration
// set_whole_fuel_map 3
setWholeFuelMap(engineConfiguration, 3);
- setTriggerSynchronizationGap(&engineConfiguration->triggerConfig, 0.72);
-
- engineConfiguration->triggerConfig.useRiseEdge = false;
- engineConfiguration->needSecondTriggerInput = true;
-
// set_cranking_injection_mode 0
engineConfiguration->crankingInjectionMode = IM_SIMULTANEOUS;
// set_injection_mode 1
diff --git a/firmware/config/engines/ford_1995_inline_6.cpp b/firmware/config/engines/ford_1995_inline_6.cpp
index 29c5106f45..aa70e30818 100644
--- a/firmware/config/engines/ford_1995_inline_6.cpp
+++ b/firmware/config/engines/ford_1995_inline_6.cpp
@@ -40,14 +40,14 @@ void setFordInline6(engine_configuration_s *engineConfiguration, board_configura
/**
* We treat the trigger as 6/0 toothed wheel
*/
- setToothedWheelConfiguration(engineConfiguration, 6, 0);
- engineConfiguration->triggerConfig.useRiseEdge = TRUE;
- engineConfiguration->needSecondTriggerInput = false;
+ engineConfiguration->triggerConfig.triggerType = TT_TOOTHED_WHEEL;
+ engineConfiguration->triggerConfig.customTotalToothCount = 6;
+ engineConfiguration->triggerConfig.customSkippedToothCount = 0;
+ engineConfiguration->triggerConfig.customIsSynchronizationNeeded = false;
engineConfiguration->globalTriggerAngleOffset = 0;
engineConfiguration->ignitionOffset = 13;
-
setThermistorConfiguration(&engineConfiguration->cltThermistorConf, -10, 160310, 60, 7700, 120.00, 1180);
engineConfiguration->cltThermistorConf.bias_resistor = 2700;
diff --git a/firmware/config/engines/ford_aspire.cpp b/firmware/config/engines/ford_aspire.cpp
index 85994be13e..1bfee66739 100644
--- a/firmware/config/engines/ford_aspire.cpp
+++ b/firmware/config/engines/ford_aspire.cpp
@@ -119,7 +119,6 @@ void setFordAspireEngineConfiguration(engine_configuration_s *engineConfiguratio
setSingleCoilDwell(engineConfiguration);
engineConfiguration->ignitionMode = IM_ONE_COIL;
engineConfiguration->triggerConfig.triggerType = TT_FORD_ASPIRE;
- engineConfiguration->triggerConfig.isSynchronizationNeeded = false;
boardConfiguration->injectionPins[4] = GPIO_NONE;
boardConfiguration->injectionPins[5] = GPIO_NONE;
diff --git a/firmware/config/engines/ford_escort_gt.cpp b/firmware/config/engines/ford_escort_gt.cpp
index 0c905feb3d..531b033f46 100644
--- a/firmware/config/engines/ford_escort_gt.cpp
+++ b/firmware/config/engines/ford_escort_gt.cpp
@@ -12,7 +12,6 @@
void setFordEscortGt(engine_configuration_s *engineConfiguration, board_configuration_s *boardConfiguration) {
engineConfiguration->triggerConfig.triggerType = TT_FORD_ESCORT_GT;
- engineConfiguration->needSecondTriggerInput = FALSE;
engineConfiguration->cylindersCount = 4;
engineConfiguration->firingOrder = FO_1_THEN_3_THEN_4_THEN2;
diff --git a/firmware/config/engines/ford_fiesta.cpp b/firmware/config/engines/ford_fiesta.cpp
index 47132ac390..1fb2b3b109 100644
--- a/firmware/config/engines/ford_fiesta.cpp
+++ b/firmware/config/engines/ford_fiesta.cpp
@@ -20,7 +20,7 @@ void setFordFiestaDefaultEngineConfiguration(engine_configuration_s *engineConfi
engineConfiguration->rpmHardLimit = 7000;
setOperationMode(engineConfiguration, FOUR_STROKE_CRANK_SENSOR);
- setToothedWheelConfiguration(engineConfiguration, 36, 1);
+ engineConfiguration->triggerConfig.triggerType = TT_TOOTHED_WHEEL_36_1;
engineConfiguration->ignitionMode = IM_WASTED_SPARK;
engineConfiguration->firingOrder = FO_1_THEN_3_THEN_4_THEN2;
diff --git a/firmware/config/engines/honda_accord.cpp b/firmware/config/engines/honda_accord.cpp
index cd38d3e251..7e3979a0d8 100644
--- a/firmware/config/engines/honda_accord.cpp
+++ b/firmware/config/engines/honda_accord.cpp
@@ -1,6 +1,12 @@
/**
* @file honda_accord.cpp
*
+ * 1995 Honda Accord EX
+ * http://rusefi.com/wiki/index.php?title=Vehicle:Honda_Accord_1995
+ * http://rusefi.com/forum/viewtopic.php?f=3&t=621
+ *
+ * engine_type 6
+ *
* @date Jan 12, 2014
* @author Andrey Belomutskiy, (c) 2012-2014
*/
@@ -9,8 +15,45 @@
#include "engine_configuration.h"
#include "trigger_decoder.h"
-void setHondaAccordConfiguration(engine_configuration_s *engineConfiguration) {
- engineConfiguration->triggerConfig.totalToothCount = 24;
- engineConfiguration->triggerConfig.skippedToothCount = 2;
+void setHondaAccordConfiguration(engine_configuration_s *engineConfiguration, board_configuration_s *boardConfiguration) {
+ engineConfiguration->engineType = HONDA_ACCORD;
+
+ engineConfiguration->map.sensor.sensorType = MT_HONDA3BAR;
+
+ engineConfiguration->cylindersCount = 4;
+ engineConfiguration->displacement = 2.156;
+
+ // Keihin 06164-P0A-A00
+ engineConfiguration->injectorFlow = 248;
+
+
+ engineConfiguration->algorithm = LM_SPEED_DENSITY;
+
+
+ engineConfiguration->crankingSettings.coolantTempMaxC = 65; // 8ms at 65C
+ engineConfiguration->crankingSettings.fuelAtMaxTempMs = 8;
+
+ engineConfiguration->crankingSettings.coolantTempMinC = 0; // 20ms at 0C
+ engineConfiguration->crankingSettings.fuelAtMinTempMs = 15;
+
+
+ memset(boardConfiguration->adcHwChannelEnabled, 0, sizeof(boardConfiguration->adcHwChannelEnabled));
+ boardConfiguration->adcHwChannelEnabled[0] = ADC_FAST; // ADC0 - PA0 - MAP
+ boardConfiguration->adcHwChannelEnabled[1] = ADC_SLOW;
+ boardConfiguration->adcHwChannelEnabled[2] = ADC_SLOW;
+ boardConfiguration->adcHwChannelEnabled[3] = ADC_SLOW;
+ boardConfiguration->adcHwChannelEnabled[4] = ADC_SLOW;
+
+ boardConfiguration->adcHwChannelEnabled[6] = ADC_SLOW;
+ boardConfiguration->adcHwChannelEnabled[7] = ADC_SLOW;
+ boardConfiguration->adcHwChannelEnabled[11] = ADC_SLOW;
+ boardConfiguration->adcHwChannelEnabled[12] = ADC_SLOW;
+ boardConfiguration->adcHwChannelEnabled[13] = ADC_SLOW;
+
+
+ engineConfiguration->map.sensor.sensorType = MT_MPX4250;
+ engineConfiguration->map.sensor.hwChannel = 0;
+
+
}
diff --git a/firmware/config/engines/honda_accord.h b/firmware/config/engines/honda_accord.h
index 98f42fa006..9fa9da863c 100644
--- a/firmware/config/engines/honda_accord.h
+++ b/firmware/config/engines/honda_accord.h
@@ -10,6 +10,6 @@
#include "engine_configuration.h"
-void setHondaAccordConfiguration(engine_configuration_s *engineConfiguration);
+void setHondaAccordConfiguration(engine_configuration_s *engineConfiguration, board_configuration_s *boardConfiguration);
#endif /* HONDA_ACCORD_H_ */
diff --git a/firmware/config/engines/mazda_323.cpp b/firmware/config/engines/mazda_323.cpp
index cce9d64e9a..571f9bc446 100644
--- a/firmware/config/engines/mazda_323.cpp
+++ b/firmware/config/engines/mazda_323.cpp
@@ -16,5 +16,5 @@ void setMazda323EngineConfiguration(engine_configuration_s *engineConfiguration)
/**
* We treat the trigger as 4/0 toothed wheel
*/
- setToothedWheelConfiguration(engineConfiguration, 4, 0);
+// setToothedWheelConfiguration(engineConfiguration, 4, 0);
}
diff --git a/firmware/config/engines/mazda_miata_nb.cpp b/firmware/config/engines/mazda_miata_nb.cpp
index ef9478f1a0..207b995086 100644
--- a/firmware/config/engines/mazda_miata_nb.cpp
+++ b/firmware/config/engines/mazda_miata_nb.cpp
@@ -17,8 +17,6 @@ void setMazdaMiataNbEngineConfiguration(engine_configuration_s *engineConfigurat
engineConfiguration->triggerConfig.triggerType = TT_MAZDA_MIATA_NB;
- setTriggerSynchronizationGap(&engineConfiguration->triggerConfig, 0.11);
- engineConfiguration->triggerConfig.useRiseEdge = false;
engineConfiguration->globalTriggerAngleOffset = 276;
// set_cranking_injection_mode 0
diff --git a/firmware/config/engines/nissan_primera.cpp b/firmware/config/engines/nissan_primera.cpp
index 460d559d1e..b16e29ea26 100644
--- a/firmware/config/engines/nissan_primera.cpp
+++ b/firmware/config/engines/nissan_primera.cpp
@@ -13,7 +13,7 @@
#include "nissan_primera.h"
void setNissanPrimeraEngineConfiguration(engine_configuration_s *engineConfiguration) {
- setToothedWheelConfiguration(engineConfiguration, 60, 2);
+ engineConfiguration->triggerConfig.triggerType = TT_TOOTHED_WHEEL_60_2;
}
#endif /* EFI_SUPPORT_NISSAN_PRIMERA */
diff --git a/firmware/config/engines/rover_v8.cpp b/firmware/config/engines/rover_v8.cpp
index 10f8665cb6..e1cab7645f 100644
--- a/firmware/config/engines/rover_v8.cpp
+++ b/firmware/config/engines/rover_v8.cpp
@@ -18,7 +18,7 @@ void setRoverv8(engine_configuration_s *engineConfiguration,
board_configuration_s *boardConfiguration) {
setOperationMode(engineConfiguration, FOUR_STROKE_CRANK_SENSOR);
- setToothedWheelConfiguration(engineConfiguration, 36, 1);
+ engineConfiguration->triggerConfig.triggerType = TT_TOOTHED_WHEEL_36_1;
engineConfiguration->displacement = 3.528;
engineConfiguration->cylindersCount = 8;
diff --git a/firmware/console/eficonsole.c b/firmware/console/eficonsole.c
index a4599b00ab..61861c1eee 100644
--- a/firmware/console/eficonsole.c
+++ b/firmware/console/eficonsole.c
@@ -52,7 +52,7 @@ static void myerror(void) {
static void sayHello(void) {
printMsg(&logger, "*** rusEFI (c) Andrey Belomutskiy, 2012-2014. All rights reserved.");
- printMsg(&logger, "rusEFI v%d@%d", getRusEfiVersion(), SVN_VERSION);
+ printMsg(&logger, "rusEFI v%d@%s", getRusEfiVersion(), VCS_VERSION);
printMsg(&logger, "*** Chibios Kernel: %s", CH_KERNEL_VERSION);
printMsg(&logger, "*** Compiled: " __DATE__ " - " __TIME__ "");
printMsg(&logger, "COMPILER=%s", __VERSION__);
diff --git a/firmware/console/status_loop.cpp b/firmware/console/status_loop.cpp
index d71b48fb1b..3d4bd8997e 100644
--- a/firmware/console/status_loop.cpp
+++ b/firmware/console/status_loop.cpp
@@ -129,7 +129,7 @@ void printSensors(void) {
reportSensorI("rpm", getRpm());
reportSensorF("maf", getMaf(), 2);
- if (engineConfiguration2->hasMapSensor) {
+ if (engineConfiguration->hasMapSensor) {
reportSensorF(getCaption(LP_MAP), getMap(), 2);
reportSensorF("map_r", getRawMap(), 2);
}
@@ -141,7 +141,7 @@ void printSensors(void) {
reportSensorF(getCaption(LP_THROTTLE), getTPS(), 2);
- if (engineConfiguration2->hasCltSensor) {
+ if (engineConfiguration->hasCltSensor) {
reportSensorF(getCaption(LP_ECT), getCoolantTemperature(), 2);
}
@@ -167,11 +167,12 @@ void printState(int currentCkpEventCounter) {
// debugFloat(&logger, "table_spark", getAdvance(rpm, getMaf()), 2);
float engineLoad = getEngineLoad();
- debugFloat(&logger, "fuel_base", getBaseFuel(rpm, engineLoad), 2);
+ float baseFuel = getBaseFuel(&engine, rpm);
+ debugFloat(&logger, "fuel_base", baseFuel, 2);
// debugFloat(&logger, "fuel_iat", getIatCorrection(getIntakeAirTemperature()), 2);
// debugFloat(&logger, "fuel_clt", getCltCorrection(getCoolantTemperature()), 2);
debugFloat(&logger, "fuel_lag", getInjectorLag(getVBatt()), 2);
- debugFloat(&logger, "fuel", getRunningFuel(rpm, engineLoad), 2);
+ debugFloat(&logger, "fuel", getRunningFuel(baseFuel, &engine, rpm), 2);
debugFloat(&logger, "timing", getAdvance(rpm, engineLoad), 2);
@@ -187,8 +188,6 @@ void printState(int currentCkpEventCounter) {
static char LOGGING_BUFFER[500];
-#if EFI_PROD_CODE
-
volatile int needToReportStatus = FALSE;
static int prevCkpEventCounter = -1;
@@ -198,38 +197,6 @@ static void printStatus(void) {
needToReportStatus = TRUE;
}
-//float getTCharge1(float tps) {
-// float cltK = tempCtoKelvin(getCoolantTemperature());
-// float iatK = tempCtoKelvin(getIntakeAirTemperature());
-// return getTCharge(getCurrentRpm(), tps, cltK, iatK);
-//}
-
-//#if EFI_CUSTOM_PANIC_METHOD
-//extern char *dbg_panic_file;
-//extern int dbg_panic_line;
-//#endif
-
-//static void checkIfShouldHalt(void) {
-//#if CH_DBG_ENABLED
-// if (hasFatalError()) {
-// /**
-// * low-level function is used here to reduce stack usage
-// */
-// palWritePad(LED_ERROR_PORT, LED_ERROR_PIN, 1);
-//#if EFI_CUSTOM_PANIC_METHOD
-// print("my FATAL [%s] at %s:%d\r\n", dbg_panic_msg, dbg_panic_file, dbg_panic_line);
-//#else
-// print("my FATAL [%s] at %s:%d\r\n", dbg_panic_msg);
-//#endif
-// chThdSleepSeconds(1);
-// // todo: figure out how we halt exactly
-// while (TRUE) {
-// }
-// chSysHalt();
-// }
-//#endif
-//}
-
/**
* Time when the firmware version was reported last time, in seconds
* TODO: implement a request/response instead of just constantly sending this out
@@ -240,7 +207,7 @@ static void printVersion(systime_t nowSeconds) {
if (overflowDiff(nowSeconds, timeOfPreviousPrintVersion) < 4)
return;
timeOfPreviousPrintVersion = nowSeconds;
- appendPrintf(&logger, "rusEfiVersion%s%d@%d %s%s", DELIMETER, getRusEfiVersion(), SVN_VERSION,
+ appendPrintf(&logger, "rusEfiVersion%s%d@%s %s%s", DELIMETER, getRusEfiVersion(), VCS_VERSION,
getConfigurationName(engineConfiguration),
DELIMETER);
}
@@ -259,14 +226,19 @@ void updateDevConsoleState(void) {
// checkIfShouldHalt();
printPending();
+#if EFI_PROD_CODE
+ // todo: unify with simulator!
if (hasFirmwareError()) {
printMsg(&logger, "firmware error: %s", errorMessageBuffer);
warningEnabled = FALSE;
chThdSleepMilliseconds(200);
return;
}
+#endif
+#if EFI_PROD_CODE
pokeAdcInputs();
+#endif
if (!fullLog)
return;
@@ -291,6 +263,8 @@ void updateDevConsoleState(void) {
finishStatusLine();
}
+#if EFI_PROD_CODE
+
/*
* command example:
* sfm 3500 400
@@ -298,7 +272,7 @@ void updateDevConsoleState(void) {
*/
static void showFuelMap2(float rpm, float engineLoad) {
- float baseFuel = getBaseFuel(rpm, engineLoad);
+ float baseFuel = getBaseTableFuel(rpm, engineLoad);
float iatCorrection = getIatCorrection(getIntakeAirTemperature());
float cltCorrection = getCltCorrection(getCoolantTemperature());
@@ -309,7 +283,7 @@ static void showFuelMap2(float rpm, float engineLoad) {
scheduleMsg(&logger2, "iatCorrection=%f cltCorrection=%f injectorLag=%f", iatCorrection, cltCorrection,
injectorLag);
- float value = getRunningFuel(rpm, engineLoad);
+ float value = getRunningFuel(baseFuel, &engine, rpm);
scheduleMsg(&logger2, "injection pulse width: %f", value);
}
diff --git a/firmware/console/status_loop.h b/firmware/console/status_loop.h
index 5fecb76093..a0bb5f1798 100644
--- a/firmware/console/status_loop.h
+++ b/firmware/console/status_loop.h
@@ -19,7 +19,6 @@ void initStatusLoop(void);
void updateDevConsoleState(void);
int getFullLog(void);
void printSensors(void);
-void finishStatusLine(void);
void setFullLog(int value);
void startStatusThreads(void);
void sayOsHello(void);
diff --git a/firmware/console/tunerstudio/tunerstudio.c b/firmware/console/tunerstudio/tunerstudio.cpp
similarity index 82%
rename from firmware/console/tunerstudio/tunerstudio.c
rename to firmware/console/tunerstudio/tunerstudio.cpp
index af6edc1ee5..0d47803aa0 100644
--- a/firmware/console/tunerstudio/tunerstudio.c
+++ b/firmware/console/tunerstudio/tunerstudio.cpp
@@ -1,518 +1,508 @@
-/**
- * @file tunerstudio.c
- * @brief Integration with EFI Analytics Tuner Studio software
- *
- * todo: merge this file with tunerstudio_algo.c?
- *
- * @date Aug 26, 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 .
- *
- */
-
-#include "main.h"
-
-#include "engine_state.h"
-#include "tunerstudio.h"
-
-#include "main_trigger_callback.h"
-#include "flash_main.h"
-
-#include "tunerstudio_algo.h"
-#include "tunerstudio_configuration.h"
-#include "malfunction_central.h"
-#include "wave_math.h"
-#include "console_io.h"
-#include "crc.h"
-
-#if EFI_TUNER_STUDIO
-
-#if EFI_PROD_CODE
-#include "pin_repository.h"
-#include "usbconsole.h"
-#include "map_averaging.h"
-extern SerialUSBDriver SDU1;
-#define CONSOLE_DEVICE &SDU1
-
-#define TS_SERIAL_UART_DEVICE &SD3
-//#define TS_SERIAL_SPEED 115200
-#define TS_SERIAL_SPEED 38400
-
-static SerialConfig tsSerialConfig = { TS_SERIAL_SPEED, 0, USART_CR2_STOP1_BITS | USART_CR2_LINEN, 0 };
-#endif /* EFI_PROD_CODE */
-
-#define MAX_PAGE_ID 0
-#define PAGE_0_SIZE 5804
-
-// in MS, that's 10 seconds
-#define TS_READ_TIMEOUT 10000
-
-#define PROTOCOL "001"
-
-
-BaseChannel * getTsSerialDevice(void) {
-#if EFI_PROD_CODE
- if (isSerialOverUart()) {
- // if console uses UART then TS uses USB
- return (BaseChannel *) &SDU1;
- } else {
- return (BaseChannel *) TS_SERIAL_UART_DEVICE;
- }
-#else
- return (BaseChannel *) TS_SIMULATOR_PORT;
-#endif
-}
-
-static Logging logger;
-
-extern engine_configuration_s *engineConfiguration;
-extern persistent_config_s configWorkingCopy;
-extern persistent_config_container_s persistentState;
-
-static efitimems_t previousWriteReportMs = 0;
-
-static int ts_serail_ready(void) {
-#if EFI_PROD_CODE
- if (isSerialOverUart()) {
- // TS uses USB when console uses serial
- return is_usb_serial_ready();
- } else {
- // TS uses serial when console uses USB
- return TRUE;
- }
-#else
- return TRUE;
-#endif
-}
-
-static THD_WORKING_AREA(TS_WORKING_AREA, UTILITY_THREAD_STACK_SIZE);
-
-static int tsCounter = 0;
-
-//static TunerStudioWriteValueRequest writeValueRequest;
-//static TunerStudioWriteChunkRequest writeChunkRequest;
-
-extern TunerStudioOutputChannels tsOutputChannels;
-
-extern TunerStudioState tsState;
-
-static void printStats(void) {
-#if EFI_PROD_CODE
- if (!isSerialOverUart()) {
- scheduleMsg(&logger, "TS RX on %s%d", portname(TS_SERIAL_RX_PORT), TS_SERIAL_RX_PIN);
- scheduleMsg(&logger, "TS TX on %s%d", portname(TS_SERIAL_TX_PORT), TS_SERIAL_TX_PIN);
- }
-#endif /* EFI_PROD_CODE */
- scheduleMsg(&logger, "TunerStudio total/error counter=%d/%d", tsCounter, tsState.errorCounter);
- scheduleMsg(&logger, "TunerStudio H counter=%d", tsState.queryCommandCounter);
- scheduleMsg(&logger, "TunerStudio O counter=%d size=%d", tsState.outputChannelsCommandCounter,
- sizeof(tsOutputChannels));
- scheduleMsg(&logger, "TunerStudio P counter=%d", tsState.readPageCommandsCounter);
- scheduleMsg(&logger, "TunerStudio B counter=%d", tsState.burnCommandCounter);
- scheduleMsg(&logger, "TunerStudio W counter=%d", tsState.writeValueCommandCounter);
- scheduleMsg(&logger, "TunerStudio C counter=%d", tsState.writeChunkCommandCounter);
- scheduleMsg(&logger, "TunerStudio P counter=%d current page %d", tsState.pageCommandCounter, tsState.currentPageId);
- scheduleMsg(&logger, "pages total size=%d", sizeof(engine_configuration_s));
- scheduleMsg(&logger, "page 0 size=%d", getTunerStudioPageSize(0));
- scheduleMsg(&logger, "page 1 size=%d", getTunerStudioPageSize(1));
-
-// scheduleMsg(&logger, "timingMode %d", (int)(&engineConfiguration->timingMode) - (int)engineConfiguration);
-// scheduleMsg(&logger, "cylindersCount %d", (int)(&engineConfiguration->cylindersCount) - (int)engineConfiguration);
- scheduleMsg(&logger, "analogChartFrequency %d",
- (int) (&engineConfiguration->analogChartFrequency) - (int) engineConfiguration);
-
- int fuelMapOffset = (int) (&engineConfiguration->fuelTable) - (int) engineConfiguration;
- scheduleMsg(&logger, "fuelTable %d", fuelMapOffset);
-
- int offset = (int) (&engineConfiguration->bc.injectionPinMode) - (int) engineConfiguration;
- scheduleMsg(&logger, "injectionPinMode %d", offset);
-
- offset = (int) (&engineConfiguration->bc.idleThreadPeriod) - (int) engineConfiguration;
- scheduleMsg(&logger, "idleThreadPeriod %d", offset);
-
-
- if (sizeof(engine_configuration_s) != getTunerStudioPageSize(0))
- firmwareError("TS page size mismatch");
-}
-
-void tunerStudioWriteData(const uint8_t * buffer, int size) {
- chSequentialStreamWrite(getTsSerialDevice(), buffer, size);
-}
-
-void tunerStudioDebug(const char *msg) {
-#if EFI_TUNER_STUDIO_VERBOSE
- scheduleMsg(&logger, "%s", msg);
- printStats();
-#endif
-}
-
-char *getWorkingPageAddr(int pageIndex) {
- switch (pageIndex) {
- case 0:
- return (char*) &configWorkingCopy.engineConfiguration;
-// case 1:
-// return (char*) &configWorkingCopy.boardConfiguration;
-// case 2: // fuelTable
-// case 3: // ignitionTable
-// case 4: // veTable
-// case 5: // afrTable
-// return (char*) &configWorkingCopy.engineConfiguration + PAGE_0_SIZE + (pageIndex - 2) * 1024;
- }
- return NULL;
-}
-
-int getTunerStudioPageSize(int pageIndex) {
- switch (pageIndex) {
- case 0:
- return PAGE_0_SIZE;
-// case 1:
-// return sizeof(configWorkingCopy.boardConfiguration);
-// case 2:
-// case 3:
-// case 4:
-// return 1024;
- }
- return 0;
-}
-
-void handlePageSelectCommand(ts_response_format_e mode, uint16_t pageId) {
- tsState.pageCommandCounter++;
-
- tsState.currentPageId = pageId;
- scheduleMsg(&logger, "page %d selected", tsState.currentPageId);
- tsSendResponse(mode, NULL, 0);
-}
-
-/**
- * This command is needed to make the whole transfer a bit faster
- * @note See also handleWriteValueCommand
- */
-void handleWriteChunkCommand(ts_response_format_e mode, short offset, short count, void *content) {
- tsState.writeChunkCommandCounter++;
-
- scheduleMsg(&logger, "receiving page %d chunk offset %d size %d", tsState.currentPageId, offset, count);
-
- if (offset > getTunerStudioPageSize(tsState.currentPageId)) {
- scheduleMsg(&logger, "ERROR offset %d", offset);
- tunerStudioError("ERROR: out of range");
- offset = 0;
- }
-
- if (count > getTunerStudioPageSize(tsState.currentPageId)) {
- tunerStudioError("ERROR: unexpected count");
- scheduleMsg(&logger, "ERROR count %d", count);
- count = 0;
- }
-
- uint8_t * addr = (uint8_t *) (getWorkingPageAddr(tsState.currentPageId) + offset);
- memcpy(addr, content, count);
-
- tsSendResponse(mode, NULL, 0);
-}
-
-/**
- * 'Write' command receives a single value at a given offset
- * @note Writing values one by one is pretty slow
- */
-void handleWriteValueCommand(ts_response_format_e mode, uint16_t page, uint16_t offset, uint8_t value) {
- tsState.writeValueCommandCounter++;
-
- tsState.currentPageId = page;
-
-//tunerStudioDebug("got W (Write)"); // we can get a lot of these
-
-#if EFI_TUNER_STUDIO_VERBOSE
-// scheduleMsg(&logger, "Page number %d\r\n", pageId); // we can get a lot of these
-#endif
-
- int size = sizeof(TunerStudioWriteValueRequest);
-// scheduleMsg(&logger, "Reading %d\r\n", size);
-
- if (offset > getTunerStudioPageSize(tsState.currentPageId)) {
- tunerStudioError("ERROR: out of range2");
- scheduleMsg(&logger, "ERROR offset %d", offset);
- offset = 0;
- return;
- }
-
- efitimems_t nowMs = currentTimeMillis();
- if (nowMs - previousWriteReportMs > 5) {
- previousWriteReportMs = nowMs;
- scheduleMsg(&logger, "page %d offset %d: value=%d", tsState.currentPageId, offset, value);
- }
-
- getWorkingPageAddr(tsState.currentPageId)[offset] = value;
-
-// scheduleMsg(&logger, "va=%d", configWorkingCopy.boardConfiguration.idleValvePin);
-}
-
-static void sendErrorCode(void) {
- tunerStudioWriteCrcPacket(TS_RESPONSE_CRC_FAILURE, NULL, 0);
-}
-
-void handlePageReadCommand(ts_response_format_e mode, uint16_t pageId, uint16_t offset, uint16_t count) {
- tsState.readPageCommandsCounter++;
- tunerStudioDebug("got R (Read page)");
- tsState.currentPageId = pageId;
-
-#if EFI_TUNER_STUDIO_VERBOSE
- scheduleMsg(&logger, "Page requested: page %d offset=%d count=%d", tsState.currentPageId, offset, count);
-#endif
-
- if (tsState.currentPageId > MAX_PAGE_ID) {
- scheduleMsg(&logger, "invalid Page number %x", tsState.currentPageId);
-
- // something is not right here
- tsState.currentPageId = 0;
- tunerStudioError("ERROR: invalid page");
- return;
- }
-
- int size = getTunerStudioPageSize(tsState.currentPageId);
-
- if (size < offset + count) {
- scheduleMsg(&logger, "invalid offset/count %d/%d", offset, count);
- sendErrorCode();
- return;
- }
-
- const uint8_t *addr = (const uint8_t *) (getWorkingPageAddr(tsState.currentPageId) + offset);
- tsSendResponse(mode, addr, count);
-#if EFI_TUNER_STUDIO_VERBOSE
- scheduleMsg(&logger, "Sending %d done", count);
-#endif
-}
-
-/**
- * 'Burn' command is a command to commit the changes
- */
-void handleBurnCommand(ts_response_format_e mode, uint16_t page) {
- tsState.burnCommandCounter++;
-
- tunerStudioDebug("got B (Burn)");
-
- tsState.currentPageId = page;
-
-#if EFI_TUNER_STUDIO_VERBOSE
- scheduleMsg(&logger, "Page number %d\r\n", tsState.currentPageId);
-#endif
-
-// todo: how about some multi-threading?
- memcpy(&persistentState.persistentConfiguration, &configWorkingCopy, sizeof(persistent_config_s));
-
- scheduleMsg(&logger, "va1=%d", configWorkingCopy.engineConfiguration.bc.idleValvePin);
- scheduleMsg(&logger, "va2=%d", persistentState.persistentConfiguration.engineConfiguration.bc.idleValvePin);
-
-#if EFI_INTERNAL_FLASH
- writeToFlash();
-#endif
- incrementGlobalConfigurationVersion();
- tunerStudioWriteCrcPacket(TS_RESPONSE_BURN_OK, NULL, 0);
-}
-
-static TunerStudioReadRequest readRequest;
-static short int pageIn;
-
-static bool handlePlainCommand(uint8_t command) {
- if (command == TS_HELLO_COMMAND) {
- scheduleMsg(&logger, "Got naked Query command");
- handleQueryCommand(TS_PLAIN);
- return true;
- } else if (command == 't' || command == 'T') {
- handleTestCommand();
- return true;
- } else if (command == TS_PAGE_COMMAND) {
- int recieved = chSequentialStreamRead(getTsSerialDevice(), (uint8_t *)&pageIn, sizeof(pageIn));
- handlePageSelectCommand(TS_PLAIN, pageIn);
- return true;
- } else if (command == TS_READ_COMMAND) {
- //scheduleMsg(&logger, "Got naked READ PAGE???");
- int recieved = chSequentialStreamRead(getTsSerialDevice(), (uint8_t *)&readRequest, sizeof(readRequest));
- if (recieved != sizeof(readRequest)) {
- // todo: handler error
- return true;
- }
- handlePageReadCommand(TS_PLAIN, readRequest.page, readRequest.offset, readRequest.count);
- return true;
- } else if (command == TS_OUTPUT_COMMAND) {
- //scheduleMsg(&logger, "Got naked Channels???");
- handleOutputChannelsCommand(TS_PLAIN);
- return true;
- } else if (command == 'F') {
- tunerStudioDebug("not ignoring F");
- tunerStudioWriteData((const uint8_t *) PROTOCOL, strlen(PROTOCOL));
- return true;
- } else {
- return false;
- }
-}
-
-static bool isKnownCommand(char command) {
- return command == TS_HELLO_COMMAND || command == TS_READ_COMMAND || command == TS_OUTPUT_COMMAND
- || command == TS_PAGE_COMMAND || command == TS_BURN_COMMAND || command == TS_SINGLE_WRITE_COMMAND
- || command == TS_CHUNK_WRITE_COMMAND;
-}
-
-static uint8_t firstByte;
-static uint8_t secondByte;
-
-// todo: reduce TS page size so that we can reduce buffer size
-static char crcIoBuffer[4096];
-
-static msg_t tsThreadEntryPoint(void *arg) {
- (void) arg;
- chRegSetThreadName("tunerstudio thread");
-
- int wasReady = false;
- while (true) {
- int isReady = ts_serail_ready();
- if (!isReady) {
- chThdSleepMilliseconds(10);
- wasReady = false;
- continue;
- }
-
- if (!wasReady) {
- wasReady = TRUE;
-// scheduleSimpleMsg(&logger, "ts channel is now ready ", hTimeNow());
- }
-
- tsCounter++;
-
- int recieved = chSequentialStreamRead(getTsSerialDevice(), &firstByte, 1);
- if (recieved != 1) {
- tunerStudioError("ERROR: no command");
- continue;
- }
-// scheduleMsg(&logger, "Got first=%x=[%c]", firstByte, firstByte);
- if (handlePlainCommand(firstByte))
- continue;
-
- recieved = chSequentialStreamRead(getTsSerialDevice(), &secondByte, 1);
- if (recieved != 1) {
- tunerStudioError("ERROR: no second");
- continue;
- }
-// scheduleMsg(&logger, "Got secondByte=%x=[%c]", secondByte, secondByte);
-
- int incomingPacketSize = firstByte * 256 + secondByte;
-
- if (incomingPacketSize == 0 || incomingPacketSize > sizeof(crcIoBuffer)) {
- scheduleMsg(&logger, "TunerStudio: invalid size: %d", incomingPacketSize);
- tunerStudioError("ERROR: size");
- sendErrorCode();
- continue;
- }
-
- recieved = chnReadTimeout(getTsSerialDevice(), crcIoBuffer, 1, MS2ST(TS_READ_TIMEOUT));
- if (recieved != 1) {
- tunerStudioError("ERROR: did not receive command");
- continue;
- }
-
- char command = crcIoBuffer[0];
- if (!isKnownCommand(command)) {
- scheduleMsg(&logger, "unexpected command %x", command);
- sendErrorCode();
- continue;
- }
-
-// scheduleMsg(&logger, "TunerStudio: reading %d+4 bytes(s)", incomingPacketSize);
-
- recieved = chnReadTimeout(getTsSerialDevice(), (void * ) (crcIoBuffer + 1), incomingPacketSize + 4 - 1,
- MS2ST(TS_READ_TIMEOUT));
- int expectedSize = incomingPacketSize + 4 - 1;
- if (recieved != expectedSize) {
- scheduleMsg(&logger, "got ONLY %d for packet size %d/%d for command %c", recieved, incomingPacketSize,
- expectedSize, command);
- tunerStudioError("ERROR: not enough");
- continue;
- }
-
- uint32_t expectedCrc = *(uint32_t*) (crcIoBuffer + incomingPacketSize);
-
- expectedCrc = SWAP_UINT32(expectedCrc);
-
- int actualCrc = crc32(crcIoBuffer, incomingPacketSize);
- if (actualCrc != expectedCrc) {
- scheduleMsg(&logger, "TunerStudio: CRC %x %x %x %x", crcIoBuffer[incomingPacketSize + 0],
- crcIoBuffer[incomingPacketSize + 1], crcIoBuffer[incomingPacketSize + 2],
- crcIoBuffer[incomingPacketSize + 3]);
-
- scheduleMsg(&logger, "TunerStudio: command %c actual CRC %x/expected %x", crcIoBuffer[0], actualCrc,
- expectedCrc);
- tunerStudioError("ERROR: CRC issue");
- continue;
- }
-
-// scheduleMsg(&logger, "TunerStudio: P00-07 %x %x %x %x %x %x %x %x", crcIoBuffer[0], crcIoBuffer[1],
-// crcIoBuffer[2], crcIoBuffer[3], crcIoBuffer[4], crcIoBuffer[5], crcIoBuffer[6], crcIoBuffer[7]);
-
- int success = tunerStudioHandleCrcCommand(crcIoBuffer, incomingPacketSize);
- if (!success)
- print("got unexpected TunerStudio command %x:%c\r\n", command, command);
-
- }
-#if defined __GNUC__
- return 0;
-#endif
-}
-
-void syncTunerStudioCopy(void) {
- memcpy(&configWorkingCopy, &persistentState.persistentConfiguration, sizeof(persistent_config_s));
-}
-
-void startTunerStudioConnectivity(void) {
- initLogging(&logger, "tuner studio");
- memset(&tsState, 0, sizeof(tsState));
-#if EFI_PROD_CODE
- if (isSerialOverUart()) {
- print("TunerStudio over USB serial");
- usb_serial_start();
- } else {
-
- print("TunerStudio over USART");
- mySetPadMode("tunerstudio rx", TS_SERIAL_RX_PORT, TS_SERIAL_RX_PIN, PAL_MODE_ALTERNATE(TS_SERIAL_AF));
- mySetPadMode("tunerstudio tx", TS_SERIAL_TX_PORT, TS_SERIAL_TX_PIN, PAL_MODE_ALTERNATE(TS_SERIAL_AF));
-
- sdStart(TS_SERIAL_UART_DEVICE, &tsSerialConfig);
- }
-#endif /* EFI_PROD_CODE */
- syncTunerStudioCopy();
-
- addConsoleAction("tsinfo", printStats);
-
- chThdCreateStatic(TS_WORKING_AREA, sizeof(TS_WORKING_AREA), NORMALPRIO, tsThreadEntryPoint, NULL);
-}
-
-/**
- * Adds size to the beginning of a packet and a crc32 at the end. Then send the packet.
- */
-void tunerStudioWriteCrcPacket(const uint8_t command, const void *buf, const uint16_t size) {
- // todo: max size validation
- *(uint16_t *) crcIoBuffer = SWAP_UINT16(size + 1); // packet size including command
- *(uint8_t *) (crcIoBuffer + 2) = command;
- if (size != 0)
- memcpy(crcIoBuffer + 3, buf, size);
- // CRC on whole packet
- uint32_t crc = crc32((void *) (crcIoBuffer + 2), (uint32_t) (size + 1));
- *(uint32_t *) (crcIoBuffer + 2 + 1 + size) = SWAP_UINT32(crc);
-
-// scheduleMsg(&logger, "TunerStudio: CRC command %x size %d", command, size);
-
- tunerStudioWriteData(crcIoBuffer, size + 2 + 1 + 4); // with size, command and CRC
-}
-
-#endif /* EFI_TUNER_STUDIO */
+/**
+ * @file tunerstudio.cpp
+ * @brief Integration with EFI Analytics Tuner Studio software
+ *
+ * todo: merge this file with tunerstudio_algo.c?
+ *
+ * @date Aug 26, 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 .
+ *
+ */
+
+#include "main.h"
+
+#include "engine_state.h"
+#include "tunerstudio.h"
+
+#include "main_trigger_callback.h"
+#include "flash_main.h"
+
+#include "tunerstudio_algo.h"
+#include "tunerstudio_configuration.h"
+#include "malfunction_central.h"
+#include "wave_math.h"
+#include "console_io.h"
+#include "crc.h"
+
+#if EFI_TUNER_STUDIO
+
+#if EFI_PROD_CODE
+#include "pin_repository.h"
+#include "usbconsole.h"
+#include "map_averaging.h"
+extern SerialUSBDriver SDU1;
+#define CONSOLE_DEVICE &SDU1
+
+#define TS_SERIAL_UART_DEVICE &SD3
+//#define TS_SERIAL_SPEED 115200
+#define TS_SERIAL_SPEED 38400
+
+static SerialConfig tsSerialConfig = { TS_SERIAL_SPEED, 0, USART_CR2_STOP1_BITS | USART_CR2_LINEN, 0 };
+#endif /* EFI_PROD_CODE */
+
+#define MAX_PAGE_ID 0
+#define PAGE_0_SIZE 5824
+
+// in MS, that's 10 seconds
+#define TS_READ_TIMEOUT 10000
+
+#define PROTOCOL "001"
+
+
+BaseChannel * getTsSerialDevice(void) {
+#if EFI_PROD_CODE
+ if (isSerialOverUart()) {
+ // if console uses UART then TS uses USB
+ return (BaseChannel *) &SDU1;
+ } else {
+ return (BaseChannel *) TS_SERIAL_UART_DEVICE;
+ }
+#else
+ return (BaseChannel *) TS_SIMULATOR_PORT;
+#endif
+}
+
+static Logging logger;
+
+extern engine_configuration_s *engineConfiguration;
+extern persistent_config_s configWorkingCopy;
+extern persistent_config_container_s persistentState;
+
+static efitimems_t previousWriteReportMs = 0;
+
+static int ts_serail_ready(void) {
+#if EFI_PROD_CODE
+ if (isSerialOverUart()) {
+ // TS uses USB when console uses serial
+ return is_usb_serial_ready();
+ } else {
+ // TS uses serial when console uses USB
+ return TRUE;
+ }
+#else
+ return TRUE;
+#endif
+}
+
+static THD_WORKING_AREA(TS_WORKING_AREA, UTILITY_THREAD_STACK_SIZE);
+
+static int tsCounter = 0;
+
+//static TunerStudioWriteValueRequest writeValueRequest;
+//static TunerStudioWriteChunkRequest writeChunkRequest;
+
+extern TunerStudioOutputChannels tsOutputChannels;
+
+extern TunerStudioState tsState;
+
+static void printStats(void) {
+#if EFI_PROD_CODE
+ if (!isSerialOverUart()) {
+ scheduleMsg(&logger, "TS RX on %s%d/TX on %s%d", portname(TS_SERIAL_RX_PORT), TS_SERIAL_RX_PIN,
+ portname(TS_SERIAL_TX_PORT), TS_SERIAL_TX_PIN);
+ }
+#endif /* EFI_PROD_CODE */
+ scheduleMsg(&logger, "TunerStudio total/error counter=%d/%d H=%d / O counter=%d size=%d / P=%d / B=%d", tsCounter, tsState.errorCounter, tsState.queryCommandCounter, tsState.outputChannelsCommandCounter,
+ sizeof(tsOutputChannels), tsState.readPageCommandsCounter, tsState.burnCommandCounter);
+ scheduleMsg(&logger, "TunerStudio W counter=%d / C = %d / P = %d / current page %d", tsState.writeValueCommandCounter, tsState.writeChunkCommandCounter,
+ tsState.pageCommandCounter, tsState.currentPageId);
+ scheduleMsg(&logger, "page size=%d", sizeof(engine_configuration_s));
+
+// scheduleMsg(&logger, "analogChartFrequency %d",
+// (int) (&engineConfiguration->analogChartFrequency) - (int) engineConfiguration);
+//
+// int fuelMapOffset = (int) (&engineConfiguration->fuelTable) - (int) engineConfiguration;
+// scheduleMsg(&logger, "fuelTable %d", fuelMapOffset);
+//
+// int offset = (int) (&engineConfiguration->bc.injectionPinMode) - (int) engineConfiguration;
+// scheduleMsg(&logger, "injectionPinMode %d", offset);
+//
+// offset = (int) (&engineConfiguration->bc.idleThreadPeriod) - (int) engineConfiguration;
+// scheduleMsg(&logger, "idleThreadPeriod %d", offset);
+
+ if (sizeof(engine_configuration_s) != getTunerStudioPageSize(0))
+ firmwareError("TS page size mismatch");
+}
+
+void tunerStudioWriteData(const uint8_t * buffer, int size) {
+ chSequentialStreamWrite(getTsSerialDevice(), buffer, size);
+}
+
+void tunerStudioDebug(const char *msg) {
+#if EFI_TUNER_STUDIO_VERBOSE
+ scheduleMsg(&logger, "%s", msg);
+ printStats();
+#endif
+}
+
+char *getWorkingPageAddr(int pageIndex) {
+ switch (pageIndex) {
+ case 0:
+ return (char*) &configWorkingCopy.engineConfiguration;
+// case 1:
+// return (char*) &configWorkingCopy.boardConfiguration;
+// case 2: // fuelTable
+// case 3: // ignitionTable
+// case 4: // veTable
+// case 5: // afrTable
+// return (char*) &configWorkingCopy.engineConfiguration + PAGE_0_SIZE + (pageIndex - 2) * 1024;
+ }
+ return NULL;
+}
+
+int getTunerStudioPageSize(int pageIndex) {
+ switch (pageIndex) {
+ case 0:
+ return PAGE_0_SIZE;
+// case 1:
+// return sizeof(configWorkingCopy.boardConfiguration);
+// case 2:
+// case 3:
+// case 4:
+// return 1024;
+ }
+ return 0;
+}
+
+void handlePageSelectCommand(ts_response_format_e mode, uint16_t pageId) {
+ tsState.pageCommandCounter++;
+
+ tsState.currentPageId = pageId;
+ scheduleMsg(&logger, "page %d selected", tsState.currentPageId);
+ tsSendResponse(mode, NULL, 0);
+}
+
+/**
+ * This command is needed to make the whole transfer a bit faster
+ * @note See also handleWriteValueCommand
+ */
+void handleWriteChunkCommand(ts_response_format_e mode, short offset, short count, void *content) {
+ tsState.writeChunkCommandCounter++;
+
+ scheduleMsg(&logger, "receiving page %d chunk offset %d size %d", tsState.currentPageId, offset, count);
+
+ if (offset > getTunerStudioPageSize(tsState.currentPageId)) {
+ scheduleMsg(&logger, "ERROR offset %d", offset);
+ tunerStudioError("ERROR: out of range");
+ offset = 0;
+ }
+
+ if (count > getTunerStudioPageSize(tsState.currentPageId)) {
+ tunerStudioError("ERROR: unexpected count");
+ scheduleMsg(&logger, "ERROR count %d", count);
+ count = 0;
+ }
+
+ uint8_t * addr = (uint8_t *) (getWorkingPageAddr(tsState.currentPageId) + offset);
+ memcpy(addr, content, count);
+
+ tsSendResponse(mode, NULL, 0);
+}
+
+/**
+ * 'Write' command receives a single value at a given offset
+ * @note Writing values one by one is pretty slow
+ */
+void handleWriteValueCommand(ts_response_format_e mode, uint16_t page, uint16_t offset, uint8_t value) {
+ tsState.writeValueCommandCounter++;
+
+ tsState.currentPageId = page;
+
+//tunerStudioDebug("got W (Write)"); // we can get a lot of these
+
+#if EFI_TUNER_STUDIO_VERBOSE
+// scheduleMsg(&logger, "Page number %d\r\n", pageId); // we can get a lot of these
+#endif
+
+ int size = sizeof(TunerStudioWriteValueRequest);
+// scheduleMsg(&logger, "Reading %d\r\n", size);
+
+ if (offset > getTunerStudioPageSize(tsState.currentPageId)) {
+ tunerStudioError("ERROR: out of range2");
+ scheduleMsg(&logger, "ERROR offset %d", offset);
+ offset = 0;
+ return;
+ }
+
+ efitimems_t nowMs = currentTimeMillis();
+ if (nowMs - previousWriteReportMs > 5) {
+ previousWriteReportMs = nowMs;
+ scheduleMsg(&logger, "page %d offset %d: value=%d", tsState.currentPageId, offset, value);
+ }
+
+ getWorkingPageAddr(tsState.currentPageId)[offset] = value;
+
+// scheduleMsg(&logger, "va=%d", configWorkingCopy.boardConfiguration.idleValvePin);
+}
+
+static void sendErrorCode(void) {
+ tunerStudioWriteCrcPacket(TS_RESPONSE_CRC_FAILURE, NULL, 0);
+}
+
+void handlePageReadCommand(ts_response_format_e mode, uint16_t pageId, uint16_t offset, uint16_t count) {
+ tsState.readPageCommandsCounter++;
+ tunerStudioDebug("got R (Read page)");
+ tsState.currentPageId = pageId;
+
+#if EFI_TUNER_STUDIO_VERBOSE
+ scheduleMsg(&logger, "Page requested: page %d offset=%d count=%d", tsState.currentPageId, offset, count);
+#endif
+
+ if (tsState.currentPageId > MAX_PAGE_ID) {
+ scheduleMsg(&logger, "invalid Page number %x", tsState.currentPageId);
+
+ // something is not right here
+ tsState.currentPageId = 0;
+ tunerStudioError("ERROR: invalid page");
+ return;
+ }
+
+ int size = getTunerStudioPageSize(tsState.currentPageId);
+
+ if (size < offset + count) {
+ scheduleMsg(&logger, "invalid offset/count %d/%d", offset, count);
+ sendErrorCode();
+ return;
+ }
+
+ const uint8_t *addr = (const uint8_t *) (getWorkingPageAddr(tsState.currentPageId) + offset);
+ tsSendResponse(mode, addr, count);
+#if EFI_TUNER_STUDIO_VERBOSE
+ scheduleMsg(&logger, "Sending %d done", count);
+#endif
+}
+
+/**
+ * 'Burn' command is a command to commit the changes
+ */
+void handleBurnCommand(ts_response_format_e mode, uint16_t page) {
+ efitimems_t nowMs = currentTimeMillis();
+ tsState.burnCommandCounter++;
+
+ tunerStudioDebug("got B (Burn)");
+
+ tsState.currentPageId = page;
+
+#if EFI_TUNER_STUDIO_VERBOSE
+ // pointless since we only have one page now
+// scheduleMsg(&logger, "Page number %d", tsState.currentPageId);
+#endif
+
+// todo: how about some multi-threading?
+ memcpy(&persistentState.persistentConfiguration, &configWorkingCopy, sizeof(persistent_config_s));
+
+#if EFI_INTERNAL_FLASH
+ writeToFlash();
+#endif
+ incrementGlobalConfigurationVersion();
+ tunerStudioWriteCrcPacket(TS_RESPONSE_BURN_OK, NULL, 0);
+ scheduleMsg(&logger, "burned in (ms): %d", currentTimeMillis() - nowMs);
+}
+
+static TunerStudioReadRequest readRequest;
+static short int pageIn;
+
+static bool handlePlainCommand(uint8_t command) {
+ if (command == TS_HELLO_COMMAND) {
+ scheduleMsg(&logger, "Got naked Query command");
+ handleQueryCommand(TS_PLAIN);
+ return true;
+ } else if (command == 't' || command == 'T') {
+ handleTestCommand();
+ return true;
+ } else if (command == TS_PAGE_COMMAND) {
+ int recieved = chSequentialStreamRead(getTsSerialDevice(), (uint8_t *)&pageIn, sizeof(pageIn));
+ handlePageSelectCommand(TS_PLAIN, pageIn);
+ return true;
+ } else if (command == TS_READ_COMMAND) {
+ //scheduleMsg(&logger, "Got naked READ PAGE???");
+ int recieved = chSequentialStreamRead(getTsSerialDevice(), (uint8_t *)&readRequest, sizeof(readRequest));
+ if (recieved != sizeof(readRequest)) {
+ // todo: handler error
+ return true;
+ }
+ handlePageReadCommand(TS_PLAIN, readRequest.page, readRequest.offset, readRequest.count);
+ return true;
+ } else if (command == TS_OUTPUT_COMMAND) {
+ //scheduleMsg(&logger, "Got naked Channels???");
+ handleOutputChannelsCommand(TS_PLAIN);
+ return true;
+ } else if (command == 'F') {
+ tunerStudioDebug("not ignoring F");
+ tunerStudioWriteData((const uint8_t *) PROTOCOL, strlen(PROTOCOL));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static bool isKnownCommand(char command) {
+ return command == TS_HELLO_COMMAND || command == TS_READ_COMMAND || command == TS_OUTPUT_COMMAND
+ || command == TS_PAGE_COMMAND || command == TS_BURN_COMMAND || command == TS_SINGLE_WRITE_COMMAND
+ || command == TS_CHUNK_WRITE_COMMAND;
+}
+
+static uint8_t firstByte;
+static uint8_t secondByte;
+
+// todo: reduce TS page size so that we can reduce buffer size
+static uint8_t crcIoBuffer[4096];
+
+static msg_t tsThreadEntryPoint(void *arg) {
+ (void) arg;
+ chRegSetThreadName("tunerstudio thread");
+
+ int wasReady = false;
+ while (true) {
+ int isReady = ts_serail_ready();
+ if (!isReady) {
+ chThdSleepMilliseconds(10);
+ wasReady = false;
+ continue;
+ }
+
+ if (!wasReady) {
+ wasReady = TRUE;
+// scheduleSimpleMsg(&logger, "ts channel is now ready ", hTimeNow());
+ }
+
+ tsCounter++;
+
+ int recieved = chSequentialStreamRead(getTsSerialDevice(), &firstByte, 1);
+ if (recieved != 1) {
+ tunerStudioError("ERROR: no command");
+ continue;
+ }
+// scheduleMsg(&logger, "Got first=%x=[%c]", firstByte, firstByte);
+ if (handlePlainCommand(firstByte))
+ continue;
+
+ recieved = chSequentialStreamRead(getTsSerialDevice(), &secondByte, 1);
+ if (recieved != 1) {
+ tunerStudioError("ERROR: no second");
+ continue;
+ }
+// scheduleMsg(&logger, "Got secondByte=%x=[%c]", secondByte, secondByte);
+
+ int incomingPacketSize = firstByte * 256 + secondByte;
+
+ if (incomingPacketSize == 0 || incomingPacketSize > sizeof(crcIoBuffer)) {
+ scheduleMsg(&logger, "TunerStudio: invalid size: %d", incomingPacketSize);
+ tunerStudioError("ERROR: size");
+ sendErrorCode();
+ continue;
+ }
+
+ recieved = chnReadTimeout(getTsSerialDevice(), crcIoBuffer, 1, MS2ST(TS_READ_TIMEOUT));
+ if (recieved != 1) {
+ tunerStudioError("ERROR: did not receive command");
+ continue;
+ }
+
+ char command = crcIoBuffer[0];
+ if (!isKnownCommand(command)) {
+ scheduleMsg(&logger, "unexpected command %x", command);
+ sendErrorCode();
+ continue;
+ }
+
+// scheduleMsg(&logger, "TunerStudio: reading %d+4 bytes(s)", incomingPacketSize);
+
+ recieved = chnReadTimeout(getTsSerialDevice(), (uint8_t * ) (crcIoBuffer + 1), incomingPacketSize + 4 - 1,
+ MS2ST(TS_READ_TIMEOUT));
+ int expectedSize = incomingPacketSize + 4 - 1;
+ if (recieved != expectedSize) {
+ scheduleMsg(&logger, "got ONLY %d for packet size %d/%d for command %c", recieved, incomingPacketSize,
+ expectedSize, command);
+ tunerStudioError("ERROR: not enough");
+ continue;
+ }
+
+ uint32_t expectedCrc = *(uint32_t*) (crcIoBuffer + incomingPacketSize);
+
+ expectedCrc = SWAP_UINT32(expectedCrc);
+
+ int actualCrc = crc32(crcIoBuffer, incomingPacketSize);
+ if (actualCrc != expectedCrc) {
+ scheduleMsg(&logger, "TunerStudio: CRC %x %x %x %x", crcIoBuffer[incomingPacketSize + 0],
+ crcIoBuffer[incomingPacketSize + 1], crcIoBuffer[incomingPacketSize + 2],
+ crcIoBuffer[incomingPacketSize + 3]);
+
+ scheduleMsg(&logger, "TunerStudio: command %c actual CRC %x/expected %x", crcIoBuffer[0], actualCrc,
+ expectedCrc);
+ tunerStudioError("ERROR: CRC issue");
+ continue;
+ }
+
+// scheduleMsg(&logger, "TunerStudio: P00-07 %x %x %x %x %x %x %x %x", crcIoBuffer[0], crcIoBuffer[1],
+// crcIoBuffer[2], crcIoBuffer[3], crcIoBuffer[4], crcIoBuffer[5], crcIoBuffer[6], crcIoBuffer[7]);
+
+ int success = tunerStudioHandleCrcCommand(crcIoBuffer, incomingPacketSize);
+ if (!success)
+ print("got unexpected TunerStudio command %x:%c\r\n", command, command);
+
+ }
+#if defined __GNUC__
+ return 0;
+#endif
+}
+
+void syncTunerStudioCopy(void) {
+ memcpy(&configWorkingCopy, &persistentState.persistentConfiguration, sizeof(persistent_config_s));
+}
+
+void startTunerStudioConnectivity(void) {
+ initLogging(&logger, "tuner studio");
+ memset(&tsState, 0, sizeof(tsState));
+#if EFI_PROD_CODE
+ if (isSerialOverUart()) {
+ print("TunerStudio over USB serial");
+ usb_serial_start();
+ } else {
+
+ print("TunerStudio over USART");
+ mySetPadMode("tunerstudio rx", TS_SERIAL_RX_PORT, TS_SERIAL_RX_PIN, PAL_MODE_ALTERNATE(TS_SERIAL_AF));
+ mySetPadMode("tunerstudio tx", TS_SERIAL_TX_PORT, TS_SERIAL_TX_PIN, PAL_MODE_ALTERNATE(TS_SERIAL_AF));
+
+ sdStart(TS_SERIAL_UART_DEVICE, &tsSerialConfig);
+ }
+#endif /* EFI_PROD_CODE */
+ syncTunerStudioCopy();
+
+ addConsoleAction("tsinfo", printStats);
+
+ chThdCreateStatic(TS_WORKING_AREA, sizeof(TS_WORKING_AREA), NORMALPRIO, tsThreadEntryPoint, NULL);
+}
+
+/**
+ * Adds size to the beginning of a packet and a crc32 at the end. Then send the packet.
+ */
+void tunerStudioWriteCrcPacket(const uint8_t command, const void *buf, const uint16_t size) {
+ // todo: max size validation
+ *(uint16_t *) crcIoBuffer = SWAP_UINT16(size + 1); // packet size including command
+ *(uint8_t *) (crcIoBuffer + 2) = command;
+ if (size != 0)
+ memcpy(crcIoBuffer + 3, buf, size);
+ // CRC on whole packet
+ uint32_t crc = crc32((void *) (crcIoBuffer + 2), (uint32_t) (size + 1));
+ *(uint32_t *) (crcIoBuffer + 2 + 1 + size) = SWAP_UINT32(crc);
+
+// scheduleMsg(&logger, "TunerStudio: CRC command %x size %d", command, size);
+
+ tunerStudioWriteData(crcIoBuffer, size + 2 + 1 + 4); // with size, command and CRC
+}
+
+#endif /* EFI_TUNER_STUDIO */
diff --git a/firmware/console/tunerstudio/tunerstudio.mk b/firmware/console/tunerstudio/tunerstudio.mk
index 03cc87deda..a0c5d66d6c 100644
--- a/firmware/console/tunerstudio/tunerstudio.mk
+++ b/firmware/console/tunerstudio/tunerstudio.mk
@@ -1,3 +1,3 @@
-TUNERSTUDIOSRC = $(PROJECT_DIR)/console/tunerstudio/tunerstudio_algo.c \
- $(PROJECT_DIR)/console/tunerstudio/tunerstudio.c
+TUNERSTUDIO_SRC_CPP = $(PROJECT_DIR)/console/tunerstudio/tunerstudio_algo.cpp \
+ $(PROJECT_DIR)/console/tunerstudio/tunerstudio.cpp
diff --git a/firmware/console/tunerstudio/tunerstudio_algo.c b/firmware/console/tunerstudio/tunerstudio_algo.cpp
similarity index 95%
rename from firmware/console/tunerstudio/tunerstudio_algo.c
rename to firmware/console/tunerstudio/tunerstudio_algo.cpp
index a6e018f66b..27ab46a87c 100644
--- a/firmware/console/tunerstudio/tunerstudio_algo.c
+++ b/firmware/console/tunerstudio/tunerstudio_algo.cpp
@@ -1,154 +1,154 @@
-/**
- * @file tunerstudio_algo.c
- * @brief Tuner Studio plain protocol implementation
- *
- * This implementation would not happen without the documentation
- * provided by Jon Zeeff (jon@zeeff.com)
- *
- * Tuner Studio has a really simple protocol, a minimal implementation
- * capable of displaying current engine state on the gauges would
- * require only two commands: queryCommand and ochGetCommand
- *
- * queryCommand:
- * Communication initialization command. TunerStudio sends a single byte H
- * ECU response:
- * One of the known ECU id strings. We are using "MShift v0.01" id string.
- *
- * ochGetCommand:
- * Request for output channels state.TunerStudio sends a single byte O
- * ECU response:
- * A snapshot of output channels as described in [OutputChannels] section of the .ini file
- * The length of this block is 'ochBlockSize' property of the .ini file
- *
- * These two commands are enough to get working gauges. In order to start configuring the ECU using
- * tuner studio, three more commands should be implemented:
- *
- * todo: merge this file with tunerstudio.c?
- *
- *
- * @date Oct 22, 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 .
- */
-
-#include
-#include "tunerstudio_algo.h"
-#include "tunerstudio_configuration.h"
-#include "engine_configuration.h"
-#include "tunerstudio.h"
-
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-#ifndef TRUE
-#define TRUE (!FALSE)
-#endif
-
-TunerStudioState tsState;
-TunerStudioOutputChannels tsOutputChannels;
-/**
- * this is a local copy of the configuration. Any changes to this copy
- * have no effect until this copy is explicitly propagated to the main working copy
- */
-persistent_config_s configWorkingCopy;
-
-void tunerStudioError(const char *msg) {
- tunerStudioDebug(msg);
- tsState.errorCounter++;
-}
-
-int tunerStudioHandleCrcCommand(char *data, int incomingPacketSize) {
- char command = data[0];
- data++;
- if (command == TS_HELLO_COMMAND) {
- tunerStudioDebug("got CRC Query");
- handleQueryCommand(TS_CRC);
- } else if (command == TS_OUTPUT_COMMAND) {
- handleOutputChannelsCommand(TS_CRC);
- } else if (command == TS_PAGE_COMMAND) {
- uint16_t page = *(uint16_t *) data;
- handlePageSelectCommand(TS_CRC, page);
- } else if (command == TS_CHUNK_WRITE_COMMAND) {
- uint16_t offset = *(uint16_t *) data;
- uint16_t count = *(uint16_t *) (data + 2);
- handleWriteChunkCommand(TS_CRC, offset, count, data + 4);
- } else if (command == TS_SINGLE_WRITE_COMMAND) {
- uint16_t page = *(uint16_t *) data;
- uint16_t offset = *(uint16_t *) (data + 2);
- uint8_t value = data[4];
- handleWriteValueCommand(TS_CRC, page, offset, value);
- } else if (command == TS_BURN_COMMAND) {
- uint16_t page = *(uint16_t *) data;
- handleBurnCommand(TS_CRC, page);
- } else if (command == TS_READ_COMMAND) {
- uint16_t page = *(uint16_t *) data;
- uint16_t offset = *(uint16_t *) (data + 2);
- uint16_t count = *(uint16_t *) (data + 4);
- handlePageReadCommand(TS_CRC, page, offset, count);
- } else if (command == 't' || command == 'T') {
- handleTestCommand();
- } else if (command == 'F') {
- tunerStudioDebug("ignoring F");
- /**
- * http://www.msextra.com/forums/viewtopic.php?f=122&t=48327
- * Response from TS support: This is an optional command *
- * "The F command is used to find what ini. file needs to be loaded in TunerStudio to match the controller.
- * If you are able to just make your firmware ignore the command that would work.
- * Currently on some firmware versions the F command is not used and is just ignored by the firmware as a unknown command."
- */
- } else {
- tunerStudioError("ERROR: ignoring unexpected command");
- return FALSE;
- }
- return TRUE;
-}
-
-void tsSendResponse(ts_response_format_e mode, const uint8_t * buffer, int size) {
- if (mode == TS_CRC) {
- tunerStudioWriteCrcPacket(TS_RESPONSE_OK, buffer, size);
- } else {
- if (size > 0)
- tunerStudioWriteData(buffer, size);
- }
-}
-
-/**
- * Query with CRC takes place while re-establishing connection
- * Query without CRC takes place on TunerStudio startup
- */
-void handleQueryCommand(ts_response_format_e mode) {
- tsState.queryCommandCounter++;
- tunerStudioDebug("got H (queryCommand)");
- tsSendResponse(mode, (const uint8_t *) TS_SIGNATURE, strlen(TS_SIGNATURE) + 1);
-}
-
-/**
- * @brief 'Output' command sends out a snapshot of current values
- */
-void handleOutputChannelsCommand(ts_response_format_e mode) {
- tsState.outputChannelsCommandCounter++;
- // this method is invoked too often to print any debug information
- tsSendResponse(mode, (const uint8_t *) &tsOutputChannels, sizeof(TunerStudioOutputChannels));
-}
-
-void handleTestCommand(void) {
- /**
- * this is NOT a standard TunerStudio command, this is my own
- * extension of the protocol to simplify troubleshooting
- */
- tunerStudioDebug("got T (Test)");
- tunerStudioWriteData((const uint8_t *) "alive\r\n", 7);
-}
+/**
+ * @file tunerstudio_algo.cpp
+ * @brief Tuner Studio plain protocol implementation
+ *
+ * This implementation would not happen without the documentation
+ * provided by Jon Zeeff (jon@zeeff.com)
+ *
+ * Tuner Studio has a really simple protocol, a minimal implementation
+ * capable of displaying current engine state on the gauges would
+ * require only two commands: queryCommand and ochGetCommand
+ *
+ * queryCommand:
+ * Communication initialization command. TunerStudio sends a single byte H
+ * ECU response:
+ * One of the known ECU id strings. We are using "MShift v0.01" id string.
+ *
+ * ochGetCommand:
+ * Request for output channels state.TunerStudio sends a single byte O
+ * ECU response:
+ * A snapshot of output channels as described in [OutputChannels] section of the .ini file
+ * The length of this block is 'ochBlockSize' property of the .ini file
+ *
+ * These two commands are enough to get working gauges. In order to start configuring the ECU using
+ * tuner studio, three more commands should be implemented:
+ *
+ * todo: merge this file with tunerstudio.c?
+ *
+ *
+ * @date Oct 22, 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 .
+ */
+
+#include
+#include "tunerstudio_algo.h"
+#include "tunerstudio_configuration.h"
+#include "engine_configuration.h"
+#include "tunerstudio.h"
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+TunerStudioState tsState;
+TunerStudioOutputChannels tsOutputChannels;
+/**
+ * this is a local copy of the configuration. Any changes to this copy
+ * have no effect until this copy is explicitly propagated to the main working copy
+ */
+persistent_config_s configWorkingCopy;
+
+void tunerStudioError(const char *msg) {
+ tunerStudioDebug(msg);
+ tsState.errorCounter++;
+}
+
+int tunerStudioHandleCrcCommand(uint8_t *data, int incomingPacketSize) {
+ char command = data[0];
+ data++;
+ if (command == TS_HELLO_COMMAND) {
+ tunerStudioDebug("got CRC Query");
+ handleQueryCommand(TS_CRC);
+ } else if (command == TS_OUTPUT_COMMAND) {
+ handleOutputChannelsCommand(TS_CRC);
+ } else if (command == TS_PAGE_COMMAND) {
+ uint16_t page = *(uint16_t *) data;
+ handlePageSelectCommand(TS_CRC, page);
+ } else if (command == TS_CHUNK_WRITE_COMMAND) {
+ uint16_t offset = *(uint16_t *) data;
+ uint16_t count = *(uint16_t *) (data + 2);
+ handleWriteChunkCommand(TS_CRC, offset, count, data + 4);
+ } else if (command == TS_SINGLE_WRITE_COMMAND) {
+ uint16_t page = *(uint16_t *) data;
+ uint16_t offset = *(uint16_t *) (data + 2);
+ uint8_t value = data[4];
+ handleWriteValueCommand(TS_CRC, page, offset, value);
+ } else if (command == TS_BURN_COMMAND) {
+ uint16_t page = *(uint16_t *) data;
+ handleBurnCommand(TS_CRC, page);
+ } else if (command == TS_READ_COMMAND) {
+ uint16_t page = *(uint16_t *) data;
+ uint16_t offset = *(uint16_t *) (data + 2);
+ uint16_t count = *(uint16_t *) (data + 4);
+ handlePageReadCommand(TS_CRC, page, offset, count);
+ } else if (command == 't' || command == 'T') {
+ handleTestCommand();
+ } else if (command == 'F') {
+ tunerStudioDebug("ignoring F");
+ /**
+ * http://www.msextra.com/forums/viewtopic.php?f=122&t=48327
+ * Response from TS support: This is an optional command *
+ * "The F command is used to find what ini. file needs to be loaded in TunerStudio to match the controller.
+ * If you are able to just make your firmware ignore the command that would work.
+ * Currently on some firmware versions the F command is not used and is just ignored by the firmware as a unknown command."
+ */
+ } else {
+ tunerStudioError("ERROR: ignoring unexpected command");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void tsSendResponse(ts_response_format_e mode, const uint8_t * buffer, int size) {
+ if (mode == TS_CRC) {
+ tunerStudioWriteCrcPacket(TS_RESPONSE_OK, buffer, size);
+ } else {
+ if (size > 0)
+ tunerStudioWriteData(buffer, size);
+ }
+}
+
+/**
+ * Query with CRC takes place while re-establishing connection
+ * Query without CRC takes place on TunerStudio startup
+ */
+void handleQueryCommand(ts_response_format_e mode) {
+ tsState.queryCommandCounter++;
+ tunerStudioDebug("got H (queryCommand)");
+ tsSendResponse(mode, (const uint8_t *) TS_SIGNATURE, strlen(TS_SIGNATURE) + 1);
+}
+
+/**
+ * @brief 'Output' command sends out a snapshot of current values
+ */
+void handleOutputChannelsCommand(ts_response_format_e mode) {
+ tsState.outputChannelsCommandCounter++;
+ // this method is invoked too often to print any debug information
+ tsSendResponse(mode, (const uint8_t *) &tsOutputChannels, sizeof(TunerStudioOutputChannels));
+}
+
+void handleTestCommand(void) {
+ /**
+ * this is NOT a standard TunerStudio command, this is my own
+ * extension of the protocol to simplify troubleshooting
+ */
+ tunerStudioDebug("got T (Test)");
+ tunerStudioWriteData((const uint8_t *) "alive\r\n", 7);
+}
diff --git a/firmware/console/tunerstudio/tunerstudio_algo.h b/firmware/console/tunerstudio/tunerstudio_algo.h
index 1b57c2f9dd..934f4ca4fb 100644
--- a/firmware/console/tunerstudio/tunerstudio_algo.h
+++ b/firmware/console/tunerstudio/tunerstudio_algo.h
@@ -42,7 +42,7 @@ typedef struct {
short currentPageId;
} TunerStudioState;
-int tunerStudioHandleCrcCommand(char *data, int incomingPacketSize);
+int tunerStudioHandleCrcCommand(uint8_t *data, int incomingPacketSize);
void handleTestCommand(void);
void handleQueryCommand(ts_response_format_e mode);
diff --git a/firmware/controllers/algo/accel_enrichment.cpp b/firmware/controllers/algo/accel_enrichment.cpp
index c680fdd64a..1d28d5c578 100644
--- a/firmware/controllers/algo/accel_enrichment.cpp
+++ b/firmware/controllers/algo/accel_enrichment.cpp
@@ -14,6 +14,7 @@
#include "engine_math.h"
#include "signal_executor.h"
+extern Engine engine;
extern engine_configuration_s *engineConfiguration;
static AccelEnrichmemnt instance;
diff --git a/firmware/controllers/algo/ec2.h b/firmware/controllers/algo/ec2.h
index deb6723208..cc3f46e06b 100644
--- a/firmware/controllers/algo/ec2.h
+++ b/firmware/controllers/algo/ec2.h
@@ -42,13 +42,11 @@ extern "C"
class engine_configuration2_s {
public:
engine_configuration2_s();
- int hasMapSensor;
- int hasCltSensor;
Thermistor iat;
Thermistor clt;
- int crankAngleRange;
+// int crankAngleRange;
trigger_shape_s triggerShape;
@@ -83,7 +81,7 @@ void resetConfigurationExt(Logging * logger, engine_type_e engineType,
engine_configuration2_s *engineConfiguration2,
board_configuration_s *boardConfiguration);
void applyNonPersistentConfiguration(Logging * logger, engine_configuration_s *engineConfiguration,
- engine_configuration2_s *engineConfiguration2, engine_type_e engineType);
+ engine_configuration2_s *engineConfiguration2);
void setDefaultNonPersistentConfiguration(engine_configuration2_s *engineConfiguration2);
diff --git a/firmware/controllers/algo/engine_configuration.cpp b/firmware/controllers/algo/engine_configuration.cpp
index cd3ba56611..00fe7668fb 100644
--- a/firmware/controllers/algo/engine_configuration.cpp
+++ b/firmware/controllers/algo/engine_configuration.cpp
@@ -80,6 +80,7 @@ void initBpsxD1Sensor(afr_sensor_s *sensor) {
}
void setWholeVEMap(engine_configuration_s *engineConfiguration, float value) {
+ // todo: table helper?
// for (int l = 0; l < VE_LOAD_COUNT; l++) {
// for (int r = 0; r < VE_RPM_COUNT; r++) {
// engineConfiguration->veTable[l][r] = value;
@@ -88,6 +89,7 @@ void setWholeVEMap(engine_configuration_s *engineConfiguration, float value) {
}
void setWholeFuelMap(engine_configuration_s *engineConfiguration, float value) {
+ // todo: table helper?
for (int l = 0; l < FUEL_LOAD_COUNT; l++) {
for (int r = 0; r < FUEL_RPM_COUNT; r++) {
engineConfiguration->fuelTable[l][r] = value;
@@ -95,20 +97,6 @@ void setWholeFuelMap(engine_configuration_s *engineConfiguration, float value) {
}
}
-void setToothedWheelConfiguration(engine_configuration_s *engineConfiguration, int total, int skipped) {
- engineConfiguration->triggerConfig.triggerType = TT_TOOTHED_WHEEL;
- engineConfiguration->triggerConfig.isSynchronizationNeeded = (skipped != 0);
-
- engineConfiguration->triggerConfig.totalToothCount = total;
- engineConfiguration->triggerConfig.skippedToothCount = skipped;
-}
-
-void setTriggerSynchronizationGap(trigger_config_s *triggerConfig, float synchGap) {
- triggerConfig->isSynchronizationNeeded = TRUE;
- triggerConfig->syncRatioFrom = synchGap * 0.75;
- triggerConfig->syncRatioTo = synchGap * 1.25;
-}
-
/**
* @brief Global default engine configuration
* This method sets the default global engine configuration. These values are later overridden by engine-specific defaults
@@ -158,7 +146,9 @@ void setDefaultConfiguration(engine_configuration_s *engineConfiguration, board_
setTimingRpmBin(engineConfiguration, 800, 7000);
setTableBin2(engineConfiguration->map.samplingAngleBins, MAP_ANGLE_SIZE, 800, 7000, 1);
+ setTableBin2(engineConfiguration->map.samplingAngle, MAP_ANGLE_SIZE, 100, 130, 1);
setTableBin2(engineConfiguration->map.samplingWindowBins, MAP_ANGLE_SIZE, 800, 7000, 1);
+ setTableBin2(engineConfiguration->map.samplingWindow, MAP_ANGLE_SIZE, 50, 50, 1);
// set_whole_timing_map 3
setWholeFuelMap(engineConfiguration, 3);
@@ -230,9 +220,7 @@ void setDefaultConfiguration(engine_configuration_s *engineConfiguration, board_
engineConfiguration->logFormat = LF_NATIVE;
- engineConfiguration->triggerConfig.triggerType = TT_TOOTHED_WHEEL;
- setTriggerSynchronizationGap(&engineConfiguration->triggerConfig, 2);
- engineConfiguration->triggerConfig.useRiseEdge = TRUE;
+ engineConfiguration->triggerConfig.triggerType = TT_TOOTHED_WHEEL_60_2;
engineConfiguration->HD44780width = 16;
engineConfiguration->HD44780height = 2;
@@ -248,8 +236,6 @@ void setDefaultConfiguration(engine_configuration_s *engineConfiguration, board_
engineConfiguration->globalFuelCorrection = 1;
- engineConfiguration->needSecondTriggerInput = TRUE;
-
engineConfiguration->map.sensor.sensorType = MT_MPX4250;
engineConfiguration->baroSensor.sensorType = MT_CUSTOM;
@@ -258,6 +244,10 @@ void setDefaultConfiguration(engine_configuration_s *engineConfiguration, board_
engineConfiguration->diffLoadEnrichmentCoef = 1;
+ engineConfiguration->hasMapSensor = TRUE;
+ engineConfiguration->hasCltSensor = TRUE;
+
+
boardConfiguration->idleValvePin = GPIOE_2;
boardConfiguration->idleValvePinMode = OM_DEFAULT;
boardConfiguration->fuelPumpPin = GPIOC_13;
@@ -335,7 +325,7 @@ void setDefaultConfiguration(engine_configuration_s *engineConfiguration, board_
boardConfiguration->consoleLoopPeriod = 200;
boardConfiguration->lcdThreadPeriod = 300;
boardConfiguration->tunerStudioThreadPeriod = 300;
- boardConfiguration->generalPeriodicThreadPeriod = 200;
+ boardConfiguration->generalPeriodicThreadPeriod = 50;
boardConfiguration->tunerStudioSerialSpeed = 38400;
@@ -344,16 +334,20 @@ void setDefaultConfiguration(engine_configuration_s *engineConfiguration, board_
boardConfiguration->canDeviceMode = CD_USE_CAN2;
boardConfiguration->canTxPin = GPIOB_0;
boardConfiguration->canRxPin = GPIOB_12;
+
+ boardConfiguration->digitalPotentiometerSpiDevice = SPI_NONE;
+ boardConfiguration->digitalPotentiometerChipSelect[0] = GPIOD_7;
+ boardConfiguration->digitalPotentiometerChipSelect[1] = GPIO_NONE;
+ boardConfiguration->digitalPotentiometerChipSelect[2] = GPIOD_5;
+ boardConfiguration->digitalPotentiometerChipSelect[3] = GPIO_NONE;
}
void setDefaultNonPersistentConfiguration(engine_configuration2_s *engineConfiguration2) {
/**
* 720 is the range for four stroke
*/
- engineConfiguration2->crankAngleRange = 720;
+// engineConfiguration2->crankAngleRange = 720;
- engineConfiguration2->hasMapSensor = TRUE;
- engineConfiguration2->hasCltSensor = TRUE;
}
void resetConfigurationExt(Logging * logger, engine_type_e engineType, engine_configuration_s *engineConfiguration,
@@ -388,7 +382,7 @@ void resetConfigurationExt(Logging * logger, engine_type_e engineType, engine_co
break;
#endif
case HONDA_ACCORD:
- setHondaAccordConfiguration(engineConfiguration);
+ setHondaAccordConfiguration(engineConfiguration, boardConfiguration);
break;
#if EFI_SUPPORT_1995_FORD_INLINE_6 || defined(__DOXYGEN__)
case FORD_INLINE_6_1995:
@@ -424,7 +418,7 @@ void resetConfigurationExt(Logging * logger, engine_type_e engineType, engine_co
firmwareError("Unexpected engine type: %d", engineType);
}
- applyNonPersistentConfiguration(logger, engineConfiguration, engineConfiguration2, engineType);
+ applyNonPersistentConfiguration(logger, engineConfiguration, engineConfiguration2);
#if EFI_TUNER_STUDIO
syncTunerStudioCopy();
@@ -435,17 +429,17 @@ engine_configuration2_s::engine_configuration2_s() {
}
void applyNonPersistentConfiguration(Logging * logger, engine_configuration_s *engineConfiguration,
- engine_configuration2_s *engineConfiguration2, engine_type_e engineType) {
+ engine_configuration2_s *engineConfiguration2) {
// todo: this would require 'initThermistors() to re-establish a reference, todo: fix
// memset(engineConfiguration2, 0, sizeof(engine_configuration2_s));
#if EFI_PROD_CODE
- printMsg(logger, "applyNonPersistentConfiguration()");
+ scheduleMsg(logger, "applyNonPersistentConfiguration()");
#endif
engineConfiguration2->isInjectionEnabledFlag = TRUE;
initializeTriggerShape(logger, engineConfiguration, engineConfiguration2);
if (engineConfiguration2->triggerShape.getSize() == 0) {
- firmwareError("size is zero");
+ firmwareError("triggerShape size is zero");
return;
}
if (engineConfiguration2->triggerShape.shaftPositionEventCount == 0) {
diff --git a/firmware/controllers/algo/engine_configuration.h b/firmware/controllers/algo/engine_configuration.h
index e4e249a836..86b5b2dcef 100644
--- a/firmware/controllers/algo/engine_configuration.h
+++ b/firmware/controllers/algo/engine_configuration.h
@@ -92,15 +92,15 @@ typedef struct {
typedef struct {
trigger_type_e triggerType;
- int isSynchronizationNeeded;
+ int customIsSynchronizationNeeded;
- int totalToothCount;
- int skippedToothCount;
+ int customTotalToothCount;
+ int customSkippedToothCount;
- float syncRatioFrom;
- float syncRatioTo;
+ float customSyncRatioFrom;
+ float customSyncRatioTo;
- int useRiseEdge;
+ int customUseRiseEdge;
} trigger_config_s;
@@ -150,7 +150,7 @@ typedef struct {
* Digital Potentiometer is used by stock ECU stimulation code
*/
spi_device_e digitalPotentiometerSpiDevice;
- brain_pin_e digitalPotentiometerChipSelect[4];
+ brain_pin_e digitalPotentiometerChipSelect[DIGIPOT_COUNT];
adc_channel_mode_e adcHwChannelEnabled[HW_MAX_ADC_INDEX];
@@ -335,7 +335,7 @@ typedef struct {
trigger_config_s triggerConfig;
- int needSecondTriggerInput;
+ int space;
int vBattAdcChannel;
float globalFuelCorrection;
@@ -370,6 +370,10 @@ typedef struct {
board_configuration_s bc;
+ int hasMapSensor;
+ int hasCltSensor;
+
+
} engine_configuration_s;
void setOperationMode(engine_configuration_s *engineConfiguration, operation_mode_e mode);
@@ -396,9 +400,6 @@ void setWholeFuelMap(engine_configuration_s *engineConfiguration, float value);
void setConstantDwell(engine_configuration_s *engineConfiguration, float dwellMs);
void printFloatArray(const char *prefix, float array[], int size);
-void setTriggerSynchronizationGap(trigger_config_s *triggerConfig, float synchGap);
-void setToothedWheelConfiguration(engine_configuration_s *engineConfiguration, int total, int skipped);
-
void incrementGlobalConfigurationVersion(void);
int getGlobalConfigurationVersion(void);
diff --git a/firmware/controllers/algo/fuel_math.cpp b/firmware/controllers/algo/fuel_math.cpp
index d0d3c78688..33270acb93 100644
--- a/firmware/controllers/algo/fuel_math.cpp
+++ b/firmware/controllers/algo/fuel_math.cpp
@@ -36,10 +36,12 @@
#include "allsensors.h"
#include "engine_math.h"
#include "rpm_calculator.h"
+#include "speed_density.h"
#if EFI_ACCEL_ENRICHMENT
#include "accel_enrichment.h"
#endif /* EFI_ACCEL_ENRICHMENT */
+extern Engine engine;
extern engine_configuration_s *engineConfiguration;
static Map3D1616 fuelMap;
@@ -83,7 +85,16 @@ float getInjectorLag(float vBatt) {
return engineConfiguration->injectorLag + vBattCorrection;
}
-float getBaseFuel(int rpm, float engineLoad) {
+float getBaseFuel(Engine *engine, int rpm) {
+ if (engine->engineConfiguration->algorithm == LM_SPEED_DENSITY) {
+ return getSpeedDensityFuel(engine, rpm);
+ } else {
+ float engineLoad = getEngineLoadT(engine);
+ return getBaseTableFuel(rpm, engineLoad);
+ }
+}
+
+float getBaseTableFuel(int rpm, float engineLoad) {
efiAssert(!cisnan(engineLoad), "invalid el", NAN);
return fuelMap.getValue(engineLoad, engineConfiguration->fuelLoadBins, rpm,
engineConfiguration->fuelRpmBins);
@@ -96,22 +107,17 @@ float getCrankingFuel(void) {
/**
* @returns Length of fuel injection, in milliseconds
*/
-float getFuelMs(int rpm) {
+float getFuelMs(int rpm, Engine *engine) {
if (isCranking()) {
return getCrankingFuel();
} else {
- float fuel = getRunningFuel(rpm, getEngineLoad());
+ float baseFuel = getBaseFuel(engine, rpm);
+ float fuel = getRunningFuel(baseFuel, engine, rpm);
return fuel;
}
}
-float getRunningFuel(int rpm, float engineLoad) {
- if (cisnan(engineLoad)) {
- // the warning message should be already produced by the sensor decoder
- return NAN;
- }
- float baseFuel = getBaseFuel(rpm, engineLoad);
-
+float getRunningFuel(float baseFuel, Engine *engine, int rpm) {
float iatCorrection = getIatCorrection(getIntakeAirTemperature());
float cltCorrection = getCltCorrection(getCoolantTemperature());
float injectorLag = getInjectorLag(getVBatt());
diff --git a/firmware/controllers/algo/fuel_math.h b/firmware/controllers/algo/fuel_math.h
index fa877e9560..80b5a8ecf9 100644
--- a/firmware/controllers/algo/fuel_math.h
+++ b/firmware/controllers/algo/fuel_math.h
@@ -13,15 +13,18 @@ extern "C"
{
#endif /* __cplusplus */
+#include "engine.h"
+
void prepareFuelMap(void);
-float getBaseFuel(int rpm, float engineLoad);
+float getBaseFuel(Engine *engine, int rpm);
+float getBaseTableFuel(int rpm, float engineLoad);
float getIatCorrection(float iat);
float getInjectorLag(float vBatt);
float getCltCorrection(float clt);
-float getRunningFuel(int rpm, float engineLoad);
+float getRunningFuel(float baseFuel, Engine *engine, int rpm);
float getStartingFuel(float coolantTemperature);
-float getFuelMs(int rpm);
+float getFuelMs(int rpm, Engine *engine);
#ifdef __cplusplus
}
diff --git a/firmware/controllers/algo/main_trigger_callback.h b/firmware/controllers/algo/main_trigger_callback.h
index 3e63f35eb5..bb9949e6dc 100644
--- a/firmware/controllers/algo/main_trigger_callback.h
+++ b/firmware/controllers/algo/main_trigger_callback.h
@@ -19,16 +19,19 @@
#include "engine_configuration.h"
#include "ec2.h"
#include "event_registry.h"
+#include "engine.h"
+
class MainTriggerCallback {
public:
// MainTriggerCallback();
- void init(engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2);
+ void init(Engine *engine, engine_configuration2_s *engineConfiguration2);
+ Engine *engine;
engine_configuration_s *engineConfiguration;
engine_configuration2_s *engineConfiguration2;
};
-void initMainEventListener(engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2);
+void initMainEventListener(Engine *engine, engine_configuration2_s *engineConfiguration2);
void onTriggerEvent(trigger_event_e ckpSignalType, int eventIndex, MainTriggerCallback *mainTriggerCallback);
#endif
diff --git a/firmware/controllers/algo/rusefi_enums.h b/firmware/controllers/algo/rusefi_enums.h
index 45fd936bd3..07d469be49 100644
--- a/firmware/controllers/algo/rusefi_enums.h
+++ b/firmware/controllers/algo/rusefi_enums.h
@@ -17,6 +17,8 @@
// this is about offsets and sizes in TunerStudio
#define ENUM_SIZE_HACK 2000000000
+#define DIGIPOT_COUNT 4
+
typedef enum {
AUDI_AAN = 1,
#if EFI_SUPPORT_DODGE_NEON
@@ -80,7 +82,8 @@ typedef enum {
TT_GM_7X = 5,
TT_MINI_COOPER_R50 = 6,
TT_FORD_ESCORT_GT = 7,
- TT_TT_TOOTHED_WHEEL_60_2 = 8,
+ TT_TOOTHED_WHEEL_60_2 = 8,
+ TT_TOOTHED_WHEEL_36_1 = 9,
Internal_ForceMyEnumIntSize_trigger_type = ENUM_SIZE_HACK,
} trigger_type_e;
@@ -228,6 +231,8 @@ typedef enum {
SPI_NONE = 0,
SPI_DEVICE_1 = 1,
SPI_DEVICE_2 = 2,
+ SPI_DEVICE_3 = 3,
+ SPI_DEVICE_4 = 4,
Internal_ForceMyEnumIntSize_spi_device = ENUM_SIZE_HACK,
} spi_device_e;
@@ -337,6 +342,7 @@ typedef enum {
MT_CUSTOM = 0,
MT_DENSO183 = 1,
MT_MPX4250 = 2,
+ MT_HONDA3BAR = 3,
Internal_ForceMyEnumIntSize_cranking_map_type = ENUM_SIZE_HACK,
} air_pressure_sensor_type_e;
diff --git a/firmware/controllers/algo/signal_executor.c b/firmware/controllers/algo/signal_executor.c
index 9adc1b8814..eb966bba0f 100644
--- a/firmware/controllers/algo/signal_executor.c
+++ b/firmware/controllers/algo/signal_executor.c
@@ -115,8 +115,8 @@ void scheduleOutput(OutputSignal *signal, float delayMs, float durationMs) {
scheduling_s * sUp = &signal->signalTimerUp[index];
scheduling_s * sDown = &signal->signalTimerDown[index];
- scheduleTask(sUp, (int)MS2US(delayMs), (schfunc_t) &turnPinHigh, (void *) signal->io_pin);
- scheduleTask(sDown, (int)MS2US(delayMs + durationMs), (schfunc_t) &turnPinLow, (void*) signal->io_pin);
+ scheduleTask("out up", sUp, (int)MS2US(delayMs), (schfunc_t) &turnPinHigh, (void *) signal->io_pin);
+ scheduleTask("out down", sDown, (int)MS2US(delayMs + durationMs), (schfunc_t) &turnPinLow, (void*) signal->io_pin);
}
const char *getPinName(io_pin_e io_pin) {
diff --git a/firmware/controllers/core/table_helper.h b/firmware/controllers/core/table_helper.h
index 5eee3266d1..58998ac7e0 100644
--- a/firmware/controllers/core/table_helper.h
+++ b/firmware/controllers/core/table_helper.h
@@ -33,13 +33,13 @@ void Map3D::init(float table[RPM_BIN_SIZE][LOAD_BIN
template
float Map3D::getValue(float x, float xBin[], float y, float yBin[]) {
- efiAssert(initialized == MAGIC_TRUE_VALUE, "map initialized", NAN);
+ efiAssert(initialized == MAGIC_TRUE_VALUE, "map not initialized", NAN);
return interpolate3d(x, xBin, LOAD_BIN_SIZE, y, yBin, RPM_BIN_SIZE, pointers);
}
template
void Map3D::setAll(float value) {
- efiAssertVoid(initialized == MAGIC_TRUE_VALUE, "map initialized");
+ efiAssertVoid(initialized == MAGIC_TRUE_VALUE, "map not initialized");
for (int l = 0; l < LOAD_BIN_SIZE; l++) {
for (int r = 0; r < RPM_BIN_SIZE; r++) {
pointers[l][r] = value;
diff --git a/firmware/controllers/engine_controller.cpp b/firmware/controllers/engine_controller.cpp
index fe964b26c8..7e591904df 100644
--- a/firmware/controllers/engine_controller.cpp
+++ b/firmware/controllers/engine_controller.cpp
@@ -156,6 +156,8 @@ static void onEvenyGeneralMilliseconds(void *arg) {
*/
halTime.get(hal_lld_get_counter_value(), true);
+ engine.updateSlowSensors();
+
updateErrorCodes();
fanRelayControl();
@@ -236,6 +238,8 @@ void initEngineContoller(void) {
return;
initLogging(&logger, "Engine Controller");
+ engine.engineConfiguration = engineConfiguration;
+
initSensors();
initPwmGenerator();
@@ -289,7 +293,7 @@ void initEngineContoller(void) {
/**
* This method initialized the main listener which actually runs injectors & ignition
*/
- initMainEventListener(engineConfiguration, engineConfiguration2);
+ initMainEventListener(&engine, engineConfiguration2);
#endif /* EFI_ENGINE_CONTROL */
#if EFI_IDLE_CONTROL
diff --git a/firmware/controllers/flash_main.cpp b/firmware/controllers/flash_main.cpp
index 6af3a630bc..b286fa6dd1 100644
--- a/firmware/controllers/flash_main.cpp
+++ b/firmware/controllers/flash_main.cpp
@@ -57,13 +57,13 @@ void writeToFlash(void) {
scheduleMsg(&logger, "flash compatible with %d", persistentState.version);
crc_t result = flashStateCrc(&persistentState);
persistentState.value = result;
- scheduleMsg(&logger, "Reseting flash, size=%d", PERSISTENT_SIZE);
+ scheduleMsg(&logger, "Reseting flash: size=%d", PERSISTENT_SIZE);
flashErase(FLASH_ADDR, PERSISTENT_SIZE);
scheduleMsg(&logger, "Flashing with CRC=%d", result);
efitimems_t nowMs = currentTimeMillis();
result = flashWrite(FLASH_ADDR, (const char *) &persistentState, PERSISTENT_SIZE);
scheduleMsg(&logger, "Flash programmed in (ms): %d", currentTimeMillis() - nowMs);
- scheduleMsg(&logger, "Flashed: %d", result);
+ scheduleMsg(&logger, "Flashing result: %d", result);
#endif /* EFI_INTERNAL_FLASH */
}
@@ -97,8 +97,7 @@ void readFromFlash(void) {
boardConfiguration);
} else {
printMsg(&logger, "Got valid configuration from flash!");
- applyNonPersistentConfiguration(&logger, engineConfiguration, engineConfiguration2,
- engineConfiguration->engineType);
+ applyNonPersistentConfiguration(&logger, engineConfiguration, engineConfiguration2);
}
// we can only change the state after the CRC check
engineConfiguration->firmwareVersion = getRusEfiVersion();
diff --git a/firmware/controllers/flash_main.h b/firmware/controllers/flash_main.h
index 1e127034eb..a28338d5c2 100644
--- a/firmware/controllers/flash_main.h
+++ b/firmware/controllers/flash_main.h
@@ -11,7 +11,7 @@
#include "engine_configuration.h"
-#define FLASH_DATA_VERSION 3880
+#define FLASH_DATA_VERSION 3975
#ifdef __cplusplus
extern "C"
diff --git a/firmware/controllers/ignition_central.c b/firmware/controllers/ignition_central.c
index 9a5668a5a5..bd03bf4e5d 100644
--- a/firmware/controllers/ignition_central.c
+++ b/firmware/controllers/ignition_central.c
@@ -35,7 +35,7 @@ void initIgnitionCentral(void) {
initLogging(&logger, "IgnitionCentral");
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
- io_pin_e pin = (io_pin_e)((int)INJECTOR_1_OUTPUT + i);
+ io_pin_e pin = (io_pin_e)((int)SPARKOUT_1_OUTPUT + i);
outputPinRegisterExt2(getPinName(pin), pin, boardConfiguration->ignitionPins[i], &boardConfiguration->ignitionPinMode);
}
}
diff --git a/firmware/controllers/map_averaging.cpp b/firmware/controllers/map_averaging.cpp
index ef8c88c8b5..f3b2f8aaab 100644
--- a/firmware/controllers/map_averaging.cpp
+++ b/firmware/controllers/map_averaging.cpp
@@ -37,7 +37,6 @@
#include "analog_chart.h"
#endif /* EFI_ANALOG_CHART */
-
#define FAST_MAP_CHART_SKIP_FACTOR 16
static Logging logger;
@@ -73,12 +72,13 @@ static scheduling_s startTimer[2];
static scheduling_s endTimer[2];
static void startAveraging(void*arg) {
- chSysLockFromIsr()
+ bool wasLocked = lockAnyContext();
;
// with locking we would have a consistent state
v_mapAccumulator = 0;
mapMeasurementsCounter = 0;
- chSysUnlockFromIsr()
+ if (!wasLocked)
+ chSysUnlockFromIsr()
;
}
@@ -111,11 +111,11 @@ void mapAveragingCallback(adcsample_t value) {
}
static void endAveraging(void *arg) {
- chSysLockFromIsr()
- ;
+ bool wasLocked = lockAnyContext();
// with locking we would have a consistent state
v_averagedMapValue = v_mapAccumulator / mapMeasurementsCounter;
- chSysUnlockFromIsr()
+ if (!wasLocked)
+ chSysUnlockFromIsr()
;
}
@@ -129,7 +129,7 @@ static void shaftPositionCallback(trigger_event_e ckpEventType, int index, void
return;
int rpm = getRpm();
- if(!isValidRpm(rpm))
+ if (!isValidRpm(rpm))
return;
perRevolution = perRevolutionCounter;
@@ -139,6 +139,10 @@ static void shaftPositionCallback(trigger_event_e ckpEventType, int index, void
float startAngle = interpolate2d(rpm, config->samplingAngleBins, config->samplingAngle, MAP_ANGLE_SIZE);
float windowAngle = interpolate2d(rpm, config->samplingWindowBins, config->samplingWindow, MAP_WINDOW_SIZE);
+ if (windowAngle <= 0) {
+ firmwareError("map sampling angle should be positive");
+ return;
+ }
int structIndex = getRevolutionCounter() % 2;
// todo: schedule this based on closest trigger event, same as ignition works
@@ -172,7 +176,6 @@ void initMapAveraging(void) {
endTimer[0].name = "map end0";
endTimer[1].name = "map end1";
-
addTriggerEventListener(&shaftPositionCallback, "rpm reporter", NULL);
addConsoleAction("faststat", showMapStats);
}
diff --git a/firmware/controllers/math/engine_math.cpp b/firmware/controllers/math/engine_math.cpp
index aa9b7d12dc..a75065c22f 100644
--- a/firmware/controllers/math/engine_math.cpp
+++ b/firmware/controllers/math/engine_math.cpp
@@ -76,17 +76,19 @@ float fixAngle(float angle) {
* @brief Returns engine load according to selected engine_load_mode
*
*/
-float getEngineLoadT(engine_configuration_s *engineConfiguration) {
+float getEngineLoadT(Engine *engine) {
+ efiAssert(engine!=NULL, "engine 2NULL", NAN);
+ engine_configuration_s *engineConfiguration = engine->engineConfiguration;
+ efiAssert(engineConfiguration!=NULL, "engineConfiguration 2NULL", NAN);
switch (engineConfiguration->algorithm) {
case LM_MAF:
- return getMaf();
+ return getMafT(engineConfiguration);
+ case LM_SPEED_DENSITY:
+ // SD engine load is used for timing lookup but not for fuel calculation
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->algorithm);
return -1;
diff --git a/firmware/controllers/math/engine_math.h b/firmware/controllers/math/engine_math.h
index 6a2409535d..61fcbbfc0c 100644
--- a/firmware/controllers/math/engine_math.h
+++ b/firmware/controllers/math/engine_math.h
@@ -14,6 +14,7 @@
#include "ec2.h"
#include "trigger_structure.h"
#include "table_helper.h"
+#include "engine.h"
void findTriggerPosition(engine_configuration_s const *engineConfiguration, trigger_shape_s * s,
event_trigger_position_s *position, float angleOffset);
@@ -38,8 +39,8 @@ float getCrankshaftRevolutionTimeMs(int rpm);
int isCrankingRT(engine_configuration_s *engineConfiguration, int rpm);
#define isCrankingR(rpm) isCrankingRT(engineConfiguration, rpm)
-float getEngineLoadT(engine_configuration_s *engineConfiguration);
-#define getEngineLoad() getEngineLoadT(engineConfiguration)
+float getEngineLoadT(Engine *engine);
+#define getEngineLoad() getEngineLoadT(&engine)
float getSparkDwellMsT(engine_configuration_s *engineConfiguration, int rpm);
#define getSparkDwellMs(rpm) getSparkDwellMsT(engineConfiguration, rpm)
diff --git a/firmware/controllers/math/speed_density.cpp b/firmware/controllers/math/speed_density.cpp
index afbd26631a..e29549f650 100644
--- a/firmware/controllers/math/speed_density.cpp
+++ b/firmware/controllers/math/speed_density.cpp
@@ -8,7 +8,6 @@
#include "main.h"
#include "speed_density.h"
#include "interpolation.h"
-#include "engine.h"
#include "rpm_calculator.h"
#include "engine_math.h"
#include "engine_state.h"
@@ -49,6 +48,11 @@ float getTCharge(int rpm, int tps, float coolantTemp, float airTemp) {
* @return value in seconds
*/
float sdMath(engine_configuration_s *engineConfiguration, float VE, float MAP, float AFR, float temp) {
+ if (MAP < 0.001 || cisnan(MAP)) {
+ warning(OBD_PCM_Processor_Fault, "invalid MAP value");
+ return 0;
+ }
+
float injectorFlowRate = cc_minute_to_gramm_second(engineConfiguration->injectorFlow);
float Vol = engineConfiguration->displacement / engineConfiguration->cylindersCount;
return (Vol * VE * MAP) / (AFR * injectorFlowRate * GAS_R * temp);
@@ -57,8 +61,8 @@ float sdMath(engine_configuration_s *engineConfiguration, float VE, float MAP, f
/**
* @return value in Milliseconds
*/
-float getSpeedDensityFuel(Engine *engine) {
- int rpm = engine->rpmCalculator->rpm();
+float getSpeedDensityFuel(Engine *engine, int rpm) {
+ //int rpm = engine->rpmCalculator->rpm();
engine_configuration_s *engineConfiguration = engine->engineConfiguration;
diff --git a/firmware/controllers/math/speed_density.h b/firmware/controllers/math/speed_density.h
index c612d2cda7..980ea65ee4 100644
--- a/firmware/controllers/math/speed_density.h
+++ b/firmware/controllers/math/speed_density.h
@@ -9,6 +9,7 @@
#include "engine_configuration.h"
#include "ec2.h"
+#include "engine.h"
float getTCharge(int rpm, int tps, float coolantTemp, float airTemp);
void setDetaultVETable(engine_configuration_s *engineConfiguration);
@@ -20,5 +21,6 @@ float sdMath(engine_configuration_s *engineConfiguration, float VE, float MAP, f
void setDetaultVETable(engine_configuration_s *engineConfiguration);
void initSpeedDensity(engine_configuration_s *engineConfiguration);
+float getSpeedDensityFuel(Engine *engine, int rpm);
#endif /* SPEED_DENSITY_H_ */
diff --git a/firmware/controllers/sensors/allsensors.h b/firmware/controllers/sensors/allsensors.h
index 01c464a2c1..cbdc88845c 100644
--- a/firmware/controllers/sensors/allsensors.h
+++ b/firmware/controllers/sensors/allsensors.h
@@ -19,6 +19,11 @@
#include "ego.h"
#include "voltage.h"
#include "thermistors.h"
+#include "adc_inputs.h"
+
+#if EFI_PROD_CODE || EFI_SIMULATOR
+#include "adc_math.h"
+#endif
#ifdef __cplusplus
diff --git a/firmware/controllers/sensors/maf.c b/firmware/controllers/sensors/maf.c
index 660582a6a7..65e30f251a 100644
--- a/firmware/controllers/sensors/maf.c
+++ b/firmware/controllers/sensors/maf.c
@@ -2,9 +2,10 @@
#include "boards.h"
#include "engine_configuration.h"
#include "adc_inputs.h"
+#include "maf.h"
extern engine_configuration_s *engineConfiguration;
float getMaf(void) {
- return getVoltageDivided(engineConfiguration->mafAdcChannel);
+ return getMafT(engineConfiguration);
}
diff --git a/firmware/controllers/sensors/maf.h b/firmware/controllers/sensors/maf.h
index 69d6a08c7b..ba833ddbfc 100644
--- a/firmware/controllers/sensors/maf.h
+++ b/firmware/controllers/sensors/maf.h
@@ -17,6 +17,8 @@ extern "C"
{
#endif /* __cplusplus */
+#define getMafT(ec) (getVoltageDivided(ec->mafAdcChannel))
+
float getMaf(void);
#ifdef __cplusplus
diff --git a/firmware/controllers/sensors/map.cpp b/firmware/controllers/sensors/map.cpp
index c1199c6c83..6ea32eb324 100644
--- a/firmware/controllers/sensors/map.cpp
+++ b/firmware/controllers/sensors/map.cpp
@@ -18,6 +18,9 @@ extern engine_configuration_s * engineConfiguration;
*/
static FastInterpolation denso183(0, -6.64, 5, 182.78);
+// todo: figure out real values
+static FastInterpolation honda3bar(0.32, -95.8371264, 4.84, 300);
+
static FastInterpolation mpx4250(0, 8, 5, 260);
float decodePressure(float voltage, air_pressure_sensor_config_s * config) {
@@ -29,6 +32,8 @@ float decodePressure(float voltage, air_pressure_sensor_config_s * config) {
return denso183.getValue(voltage);
case MT_MPX4250:
return mpx4250.getValue(voltage);
+ case MT_HONDA3BAR:
+ return honda3bar.getValue(voltage);
default:
firmwareError("Unknown MAP type: %d", config->sensorType);
return NAN;
diff --git a/firmware/controllers/settings.cpp b/firmware/controllers/settings.cpp
index 5fd3bfb01d..ee23cc16f8 100644
--- a/firmware/controllers/settings.cpp
+++ b/firmware/controllers/settings.cpp
@@ -125,7 +125,7 @@ static const char * boolToString(bool value) {
*/
void printConfiguration(engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2) {
- scheduleMsg(&logger, getConfigurationName(engineConfiguration));
+ scheduleMsg(&logger, "Template %s trigger %d", getConfigurationName(engineConfiguration), engineConfiguration->triggerConfig.triggerType);
scheduleMsg(&logger, "configurationVersion=%d", getGlobalConfigurationVersion());
@@ -172,10 +172,11 @@ void printConfiguration(engine_configuration_s *engineConfiguration, engine_conf
// scheduleMsg(&logger, "crankingRpm: %d", engineConfiguration->crankingSettings.crankingRpm);
scheduleMsg(&logger, "idlePinMode: %s", pinModeToString(boardConfiguration->idleValvePinMode));
- scheduleMsg(&logger, "malfunctionIndicatorPinMode: %s", pinModeToString(boardConfiguration->malfunctionIndicatorPinMode));
+ scheduleMsg(&logger, "malfunctionIndicatorPinMode: %s",
+ pinModeToString(boardConfiguration->malfunctionIndicatorPinMode));
scheduleMsg(&logger, "analogInputDividerCoefficient: %f", engineConfiguration->analogInputDividerCoefficient);
- scheduleMsg(&logger, "needSecondTriggerInput: %s", boolToString(engineConfiguration->needSecondTriggerInput));
+ scheduleMsg(&logger, "needSecondTriggerInput: %s", boolToString(engineConfiguration2->triggerShape.needSecondTriggerInput));
#if EFI_PROD_CODE
scheduleMsg(&logger, "idleValvePin: %s", hwPortname(boardConfiguration->idleValvePin));
@@ -204,12 +205,15 @@ void printConfiguration(engine_configuration_s *engineConfiguration, engine_conf
scheduleMsg(&logger, "primary trigger input: %s", hwPortname(boardConfiguration->primaryTriggerInputPin));
scheduleMsg(&logger, "boardTestModeJumperPin: %s", hwPortname(boardConfiguration->boardTestModeJumperPin));
+
+ scheduleMsg(&logger, "digitalPotentiometerSpiDevice %d", boardConfiguration->digitalPotentiometerSpiDevice);
+
+ for (int i = 0; i < DIGIPOT_COUNT; i++) {
+ scheduleMsg(&logger, "digitalPotentiometer CS%d %s", i, hwPortname(boardConfiguration->digitalPotentiometerChipSelect[i]));
+ }
#endif /* EFI_PROD_CODE */
scheduleMsg(&logger, "isInjectionEnabledFlag %s", boolToString(engineConfiguration2->isInjectionEnabledFlag));
-
- // appendPrintf(&logger, DELIMETER);
-// scheduleLogging(&logger);
}
static void setFixedModeTiming(int value) {
@@ -369,6 +373,11 @@ static void setCrankingRpm(int value) {
doPrintConfiguration();
}
+static void setAlgorithm(int value) {
+ engineConfiguration->algorithm = (engine_load_mode_e) value;
+ doPrintConfiguration();
+}
+
static void setFiringOrder(int value) {
engineConfiguration->firingOrder = (firing_order_e) value;
doPrintConfiguration();
@@ -415,9 +424,15 @@ static void setIgnitionMode(int value) {
doPrintConfiguration();
}
+static void setTriggerType(int value) {
+ engineConfiguration->triggerConfig.triggerType = (trigger_type_e)value;
+ incrementGlobalConfigurationVersion();
+ doPrintConfiguration();
+}
+
static void setToothedWheel(int total, int skipped) {
- setToothedWheelConfiguration(engineConfiguration, total, skipped);
- initializeTriggerShape(&logger, engineConfiguration, engineConfiguration2);
+ setToothedWheelConfiguration(&engineConfiguration2->triggerShape, total, skipped, engineConfiguration);
+// initializeTriggerShape(&logger, engineConfiguration, engineConfiguration2);
incrementGlobalConfigurationVersion();
doPrintConfiguration();
}
@@ -436,6 +451,7 @@ static void setGlobalFuelCorrection(float value) {
}
static void setWholeTimingMap(float value) {
+ // todo: table helper?
scheduleMsg(&logger, "Setting whole timing map to %f", value);
for (int l = 0; l < IGN_LOAD_COUNT; l++) {
for (int r = 0; r < IGN_RPM_COUNT; r++) {
@@ -549,9 +565,11 @@ void initSettings(void) {
addConsoleActionI("set_rpm_hard_limit", setRpmHardLimit);
addConsoleActionI("set_firing_order", setFiringOrder);
+ addConsoleActionI("set_algorithm", setAlgorithm);
addConsoleAction("enable_injection", enableInjection);
addConsoleAction("disable_injection", disableInjection);
addConsoleActionII("set_toothed_wheel", setToothedWheel);
+ addConsoleActionI("set_trigger_type", setTriggerType);
}
diff --git a/firmware/controllers/system/SingleTimerExecutor.cpp b/firmware/controllers/system/SingleTimerExecutor.cpp
index a198935186..078e4af04a 100644
--- a/firmware/controllers/system/SingleTimerExecutor.cpp
+++ b/firmware/controllers/system/SingleTimerExecutor.cpp
@@ -96,8 +96,11 @@ void Executor::doExecute(uint64_t nowUs) {
* @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(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) {
- efiAssertVoid(delayUs >= 0, "Negative delayUs");
+void scheduleTask(const char *prefix, scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) {
+ if (delayUs < 0) {
+ firmwareError("Negative delayUs %s: %d", prefix, delayUs);
+ return;
+ }
if (delayUs == 0) {
callback(param);
return;
diff --git a/firmware/controllers/system/pwm_generator_logic.cpp b/firmware/controllers/system/pwm_generator_logic.cpp
index 5a65515ad6..57703a54a4 100644
--- a/firmware/controllers/system/pwm_generator_logic.cpp
+++ b/firmware/controllers/system/pwm_generator_logic.cpp
@@ -47,16 +47,16 @@ static uint64_t getNextSwitchTimeUs(PwmConfig *state) {
efiAssert(state->safe.phaseIndex < PWM_PHASE_MAX_COUNT, "phaseIndex range", 0);
int iteration = state->safe.iteration;
float switchTime = state->multiWave.getSwitchTime(state->safe.phaseIndex);
- float periodMs = state->safe.periodMs;
+ float periodUs = state->safe.periodUs;
#if DEBUG_PWM
scheduleMsg(&logger, "iteration=%d switchTime=%f period=%f", iteration, switchTime, period);
#endif
/**
- * todo: once 'iteration' gets relatively high, we might lose calculation precision here
- * todo: double-check this spot
+ * Once 'iteration' gets relatively high, we might lose calculation precision here.
+ * This is addressed by ITERATION_LIMIT
*/
- uint64_t timeToSwitchUs = (iteration + switchTime) * periodMs * 1000;
+ uint64_t timeToSwitchUs = (iteration + switchTime) * periodUs;
#if DEBUG_PWM
scheduleMsg(&logger, "start=%d timeToSwitch=%d", state->safe.start, timeToSwitch);
@@ -64,6 +64,25 @@ static uint64_t getNextSwitchTimeUs(PwmConfig *state) {
return state->safe.startUs + timeToSwitchUs;
}
+void PwmConfig::handleCycleStart() {
+ if (safe.phaseIndex == 0) {
+ if (cycleCallback != NULL)
+ cycleCallback(this);
+ efiAssertVoid(periodUs != 0, "period not initialized");
+ if (safe.periodUs != periodUs || safe.iteration == ITERATION_LIMIT) {
+ /**
+ * period length has changed - we need to reset internal state
+ */
+ safe.startUs = getTimeNowUs();
+ safe.iteration = 0;
+ safe.periodUs = periodUs;
+#if DEBUG_PWM
+ scheduleMsg(&logger, "state reset start=%d iteration=%d", state->safe.start, state->safe.iteration);
+#endif
+ }
+ }
+}
+
/**
* @return Next time for signal toggle
*/
@@ -73,31 +92,21 @@ static uint64_t togglePwmState(PwmConfig *state) {
scheduleMsg(&logger, "state->period=%f state->safe.period=%f", state->period, state->safe.period);
#endif
- if (state->safe.phaseIndex == 0) {
- if (cisnan(state->periodMs)) {
- /**
- * zero period means PWM is paused
- */
- return 1;
- }
- if (state->cycleCallback != NULL)
- state->cycleCallback(state);
- efiAssert(state->periodMs != 0, "period not initialized", 0);
- if (state->safe.periodMs != state->periodMs || state->safe.iteration == ITERATION_LIMIT) {
- /**
- * period length has changed - we need to reset internal state
- */
- state->safe.startUs = getTimeNowUs();
- state->safe.iteration = 0;
- state->safe.periodMs = state->periodMs;
-#if DEBUG_PWM
- scheduleMsg(&logger, "state reset start=%d iteration=%d", state->safe.start, state->safe.iteration);
-#endif
- }
+ if (cisnan(state->periodUs)) {
+ /**
+ * zero period means PWM is paused
+ */
+ return 1;
}
- state->stateChangeCallback(state,
- state->safe.phaseIndex == 0 ? state->phaseCount - 1 : state->safe.phaseIndex - 1);
+ state->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 = state->safe.phaseIndex == 0 ? state->phaseCount - 1 : state->safe.phaseIndex - 1;
+ state->stateChangeCallback(state, cbStateIndex);
uint64_t nextSwitchTimeUs = getNextSwitchTimeUs(state);
#if DEBUG_PWM
@@ -106,8 +115,13 @@ static uint64_t togglePwmState(PwmConfig *state) {
// signed value is needed here
int64_t timeToSwitch = nextSwitchTimeUs - getTimeNowUs();
if (timeToSwitch < 1) {
-//todo: introduce error and test this error handling warning(OBD_PCM_Processor_Fault, "PWM: negative switch time");
- timeToSwitch = 1000;
+ /**
+ * 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.
+ */
+ //todo: introduce error and test this error handling warning(OBD_PCM_Processor_Fault, "PWM: negative switch time");
+ timeToSwitch = 10;
}
state->safe.phaseIndex++;
@@ -118,9 +132,12 @@ static uint64_t togglePwmState(PwmConfig *state) {
return timeToSwitch;
}
+/**
+ * Main PWM loop: toggle pin & schedule next invocation
+ */
static void timerCallback(PwmConfig *state) {
time_t timeToSleepUs = togglePwmState(state);
- scheduleTask(&state->scheduling, timeToSleepUs, (schfunc_t) timerCallback, state);
+ scheduleTask("pwm", &state->scheduling, timeToSleepUs, (schfunc_t) timerCallback, state);
}
/**
@@ -144,7 +161,7 @@ void copyPwmParameters(PwmConfig *state, int phaseCount, float *switchTimes, int
void weComplexInit(const char *msg, PwmConfig *state, int phaseCount, float *switchTimes, int waveCount,
int **pinStates, pwm_cycle_callback *cycleCallback, pwm_gen_callback *stateChangeCallback) {
- efiAssertVoid(state->periodMs != 0, "period is not initialized");
+ efiAssertVoid(state->periodUs != 0, "period is not initialized");
if (phaseCount == 0) {
firmwareError("signal length cannot be zero");
return;
@@ -168,9 +185,10 @@ void weComplexInit(const char *msg, PwmConfig *state, int phaseCount, float *swi
state->stateChangeCallback = stateChangeCallback;
state->safe.phaseIndex = 0;
- state->safe.periodMs = -1;
+ state->safe.periodUs = -1;
state->safe.iteration = -1;
state->name = msg;
+ // let's start the indefinite callback loop of PWM generation
timerCallback(state);
}
diff --git a/firmware/controllers/system/pwm_generator_logic.h b/firmware/controllers/system/pwm_generator_logic.h
index 0ca582e7db..06da141c9b 100644
--- a/firmware/controllers/system/pwm_generator_logic.h
+++ b/firmware/controllers/system/pwm_generator_logic.h
@@ -18,7 +18,7 @@ typedef struct {
* a copy so that all phases are executed on the same period, even if another thread
* would be adjusting PWM parameters
*/
- float periodMs;
+ float periodUs;
/**
* Iteration counter
*/
@@ -43,6 +43,10 @@ public:
PwmConfig();
PwmConfig(float *switchTimes, single_wave_s *waves);
void init(float *switchTimes, single_wave_s *waves);
+
+ void handleCycleStart();
+
+
io_pin_e outputPins[PWM_PHASE_MAX_WAVE_PER_PWM];
multi_wave_s multiWave;
const char *name;
@@ -50,7 +54,7 @@ public:
* float value of PWM period
* PWM generation is not happening while this value is zero
*/
- float periodMs;
+ float periodUs;
scheduling_s scheduling;
diff --git a/firmware/controllers/system/scheduler.h b/firmware/controllers/system/scheduler.h
index 992795ba4e..4651924a44 100644
--- a/firmware/controllers/system/scheduler.h
+++ b/firmware/controllers/system/scheduler.h
@@ -29,7 +29,7 @@ extern "C"
{
#endif /* __cplusplus */
-void scheduleTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param);
+void scheduleTask(const char *prefix, scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param);
#ifdef __cplusplus
}
diff --git a/firmware/controllers/system/signal_executor_sleep.c b/firmware/controllers/system/signal_executor_sleep.c
index 51af8eef87..57151bf861 100644
--- a/firmware/controllers/system/signal_executor_sleep.c
+++ b/firmware/controllers/system/signal_executor_sleep.c
@@ -28,7 +28,7 @@
#if EFI_SIGNAL_EXECUTOR_SLEEP || defined(__DOXYGEN__)
-void scheduleTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) {
+void scheduleTask(const char *prefix, scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) {
int delaySt = delayUs * CH_FREQUENCY / 1000000;
if (delaySt == 0) {
/**
diff --git a/firmware/controllers/trigger/main_trigger_callback.cpp b/firmware/controllers/trigger/main_trigger_callback.cpp
index 06b4d9bf9f..4956477f33 100644
--- a/firmware/controllers/trigger/main_trigger_callback.cpp
+++ b/firmware/controllers/trigger/main_trigger_callback.cpp
@@ -78,7 +78,7 @@ static cyclic_buffer ignitionErrorDetection;
static Logging logger;
static void handleFuelInjectionEvent(MainTriggerCallback *mainTriggerCallback, ActuatorEvent *event, int rpm) {
- float fuelMs = getFuelMs(rpm) * mainTriggerCallback->engineConfiguration->globalFuelCorrection;
+ float fuelMs = getFuelMs(rpm, mainTriggerCallback->engine) * mainTriggerCallback->engineConfiguration->globalFuelCorrection;
if (cisnan(fuelMs)) {
warning(OBD_PCM_Processor_Fault, "NaN injection pulse");
return;
@@ -156,7 +156,7 @@ static void handleSparkEvent(MainTriggerCallback *mainTriggerCallback, int event
/**
* The start of charge is always within the current trigger event range, so just plain time-based scheduling
*/
- scheduleTask(sUp, (int) MS2US(sparkDelay), (schfunc_t) &turnPinHigh, (void *) iEvent->io_pin);
+ scheduleTask("spark up", sUp, (int) MS2US(sparkDelay), (schfunc_t) &turnPinHigh, (void *) iEvent->io_pin);
/**
* Spark event is often happening during a later trigger event timeframe
* TODO: improve precision
@@ -171,7 +171,7 @@ static void handleSparkEvent(MainTriggerCallback *mainTriggerCallback, int event
*/
float timeTillIgnitionUs = getOneDegreeTimeUs(rpm) * iEvent->sparkPosition.angleOffset;
- scheduleTask(sDown, (int) timeTillIgnitionUs, (schfunc_t) &turnPinLow, (void*) iEvent->io_pin);
+ scheduleTask("spark 1down", sDown, (int) timeTillIgnitionUs, (schfunc_t) &turnPinLow, (void*) iEvent->io_pin);
} else {
/**
* Spark should be scheduled in relation to some future trigger event, this way we get better firing precision
@@ -204,7 +204,7 @@ static void handleSpark(MainTriggerCallback *mainTriggerCallback, int eventIndex
scheduling_s * sDown = ¤t->signalTimerDown;
float timeTillIgnitionUs = getOneDegreeTimeUs(rpm) * current->sparkPosition.angleOffset;
- scheduleTask(sDown, (int) timeTillIgnitionUs, (schfunc_t) &turnPinLow, (void*) current->io_pin);
+ scheduleTask("spark 2down", sDown, (int) timeTillIgnitionUs, (schfunc_t) &turnPinLow, (void*) current->io_pin);
}
}
@@ -276,7 +276,7 @@ void onTriggerEvent(trigger_event_e ckpSignalType, int eventIndex, MainTriggerCa
firmwareError("invalid dwell: %f at %d", dwellMs, rpm);
return;
}
- float advance = getAdvance(rpm, getEngineLoadT(mainTriggerCallback->engineConfiguration));
+ float advance = getAdvance(rpm, getEngineLoadT(mainTriggerCallback->engine));
float dwellAngle = dwellMs / getOneDegreeTimeMs(rpm);
@@ -293,7 +293,7 @@ void onTriggerEvent(trigger_event_e ckpSignalType, int eventIndex, MainTriggerCa
#if EFI_HISTOGRAMS && EFI_PROD_CODE
int diff = hal_lld_get_counter_value() - beforeCallback;
if (diff > 0)
- hsAdd(&mainLoopHisto, diff);
+ hsAdd(&mainLoopHisto, diff);
#endif /* EFI_HISTOGRAMS */
}
@@ -309,21 +309,26 @@ static void showTriggerHistogram(void) {
static void showMainInfo(void) {
int rpm = getRpm();
- float el = getEngineLoadT(mainTriggerCallbackInstance.engineConfiguration);
+ float el = getEngineLoadT(mainTriggerCallbackInstance.engine);
#if EFI_PROD_CODE
scheduleMsg(&logger, "rpm %d engine_load %f", rpm, el);
- scheduleMsg(&logger, "fuel %fms timing %f", getFuelMs(rpm), getAdvance(rpm, el));
+ scheduleMsg(&logger, "fuel %fms timing %f", getFuelMs(rpm, mainTriggerCallbackInstance.engine), getAdvance(rpm, el));
#endif
}
-void MainTriggerCallback::init(engine_configuration_s *engineConfiguration,
- engine_configuration2_s *engineConfiguration2) {
- this->engineConfiguration = engineConfiguration;
+void MainTriggerCallback::init(Engine *engine, engine_configuration2_s *engineConfiguration2) {
+ efiAssertVoid(engine!=NULL, "engine NULL");
+ this->engine = engine;
+ this->engineConfiguration = engine->engineConfiguration;
+ efiAssertVoid(engineConfiguration!=NULL, "engineConfiguration NULL");
this->engineConfiguration2 = engineConfiguration2;
}
-void initMainEventListener(engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2) {
- mainTriggerCallbackInstance.init(engineConfiguration, engineConfiguration2);
+void initMainEventListener(Engine *engine, engine_configuration2_s *engineConfiguration2) {
+ efiAssertVoid(engine!=NULL, "null engine");
+ engine_configuration_s *engineConfiguration = engine->engineConfiguration;
+
+ mainTriggerCallbackInstance.init(engine, engineConfiguration2);
#if EFI_PROD_CODE
addConsoleAction("performanceinfo", showTriggerHistogram);
@@ -332,7 +337,7 @@ void initMainEventListener(engine_configuration_s *engineConfiguration, engine_c
initLogging(&logger, "main event handler");
printMsg(&logger, "initMainLoop: %d", currentTimeMillis());
if (!isInjectionEnabled(mainTriggerCallbackInstance.engineConfiguration2))
- printMsg(&logger, "!!!!!!!!!!!!!!!!!!! injection disabled");
+ printMsg(&logger, "!!!!!!!!!!!!!!!!!!! injection disabled");
#endif
#if EFI_HISTOGRAMS
diff --git a/firmware/controllers/trigger/rpm_calculator.cpp b/firmware/controllers/trigger/rpm_calculator.cpp
index f1209a22b1..874ff50b90 100644
--- a/firmware/controllers/trigger/rpm_calculator.cpp
+++ b/firmware/controllers/trigger/rpm_calculator.cpp
@@ -231,7 +231,7 @@ void scheduleByAngle(scheduling_s *timer, float angle, schfunc_t callback, void
firmwareError("NaN delay?");
return;
}
- scheduleTask(timer, (int)MS2US(delayMs), callback, param);
+ scheduleTask("by angle", timer, (int)MS2US(delayMs), callback, param);
}
#endif
diff --git a/firmware/controllers/trigger/trigger_bmw.cpp b/firmware/controllers/trigger/trigger_bmw.cpp
index 06b48feaa2..7798843874 100644
--- a/firmware/controllers/trigger/trigger_bmw.cpp
+++ b/firmware/controllers/trigger/trigger_bmw.cpp
@@ -15,8 +15,7 @@ static inline float addPair(trigger_shape_s *s, float a, float w) {
return a;
}
-void configureMiniCooperTriggerShape(trigger_config_s *triggerConfig,
- trigger_shape_s *s) {
+void configureMiniCooperTriggerShape(trigger_shape_s *s) {
s->reset(FOUR_STROKE_CAM_SENSOR);
@@ -67,5 +66,5 @@ void configureMiniCooperTriggerShape(trigger_config_s *triggerConfig,
/**
* With just one tooth on camshaft synchronization is not needed
*/
- triggerConfig->isSynchronizationNeeded = FALSE;
+ s->isSynchronizationNeeded = FALSE;
}
diff --git a/firmware/controllers/trigger/trigger_bmw.h b/firmware/controllers/trigger/trigger_bmw.h
index 29a8c4c4fd..5d0fe6fbf9 100644
--- a/firmware/controllers/trigger/trigger_bmw.h
+++ b/firmware/controllers/trigger/trigger_bmw.h
@@ -10,7 +10,6 @@
#include "engine_configuration.h"
#include "ec2.h"
-void configureMiniCooperTriggerShape(trigger_config_s *triggerConfig,
- trigger_shape_s *s);
+void configureMiniCooperTriggerShape(trigger_shape_s *s);
#endif /* TRIGGER_BMW_H_ */
diff --git a/firmware/controllers/trigger/trigger_chrysler.cpp b/firmware/controllers/trigger/trigger_chrysler.cpp
index 986e0b56a7..2ecffcfcab 100644
--- a/firmware/controllers/trigger/trigger_chrysler.cpp
+++ b/firmware/controllers/trigger/trigger_chrysler.cpp
@@ -7,9 +7,15 @@
#include "trigger_chrysler.h"
-void configureNeonTriggerShape(trigger_config_s *triggerConfig, trigger_shape_s *s) {
+void configureNeonTriggerShape(trigger_shape_s *s) {
s->reset(FOUR_STROKE_CAM_SENSOR);
+ setTriggerSynchronizationGap(s, 0.72);
+
+ s->useRiseEdge = false;
+ s->needSecondTriggerInput = true;
+
+
// voodoo magic - we always need 720 at the end
int base = 720 - 560;
diff --git a/firmware/controllers/trigger/trigger_chrysler.h b/firmware/controllers/trigger/trigger_chrysler.h
index 66fbf3bcfa..4cea93ce97 100644
--- a/firmware/controllers/trigger/trigger_chrysler.h
+++ b/firmware/controllers/trigger/trigger_chrysler.h
@@ -10,6 +10,6 @@
#include "trigger_structure.h"
-void configureNeonTriggerShape(trigger_config_s *triggerConfig, trigger_shape_s *s);
+void configureNeonTriggerShape(trigger_shape_s *s);
#endif /* TRIGGER_CHRYSLER_H_ */
diff --git a/firmware/controllers/trigger/trigger_decoder.cpp b/firmware/controllers/trigger/trigger_decoder.cpp
index d341766095..f71d8c0ef0 100644
--- a/firmware/controllers/trigger/trigger_decoder.cpp
+++ b/firmware/controllers/trigger/trigger_decoder.cpp
@@ -47,16 +47,16 @@ int isTriggerDecoderError(void) {
static inline int isSynchronizationGap(TriggerState const *shaftPositionState, trigger_shape_s const *triggerShape,
trigger_config_s const *triggerConfig, const int currentDuration) {
- if (!triggerConfig->isSynchronizationNeeded)
+ if (!triggerShape->isSynchronizationNeeded)
return false;
- return currentDuration > shaftPositionState->toothed_previous_duration * triggerConfig->syncRatioFrom
- && currentDuration < shaftPositionState->toothed_previous_duration * triggerConfig->syncRatioTo;
+ return currentDuration > shaftPositionState->toothed_previous_duration * triggerShape->syncRatioFrom
+ && currentDuration < shaftPositionState->toothed_previous_duration * triggerShape->syncRatioTo;
}
static inline int noSynchronizationResetNeeded(TriggerState *shaftPositionState, trigger_shape_s const *triggerShape,
trigger_config_s const*triggerConfig) {
- if (triggerConfig->isSynchronizationNeeded)
+ if (triggerShape->isSynchronizationNeeded)
return false;
if (!shaftPositionState->shaft_is_synchronized)
return TRUE;
@@ -73,8 +73,8 @@ static inline int noSynchronizationResetNeeded(TriggerState *shaftPositionState,
void TriggerState::decodeTriggerEvent(trigger_shape_s const*triggerShape, trigger_config_s const*triggerConfig,
trigger_event_e signal, uint64_t nowUs) {
- int isLessImportant = (triggerConfig->useRiseEdge && signal != SHAFT_PRIMARY_UP)
- || (!triggerConfig->useRiseEdge && signal != SHAFT_PRIMARY_DOWN);
+ int isLessImportant = (triggerShape->useRiseEdge && signal != SHAFT_PRIMARY_UP)
+ || (!triggerShape->useRiseEdge && signal != SHAFT_PRIMARY_DOWN);
if (isLessImportant) {
/**
@@ -139,11 +139,12 @@ static void initializeSkippedToothTriggerShape(trigger_shape_s *s, int totalTeet
s->addEvent(720, T_PRIMARY, TV_LOW);
}
-void initializeSkippedToothTriggerShapeExt(engine_configuration2_s *engineConfiguration2, int totalTeethCount,
+void initializeSkippedToothTriggerShapeExt(trigger_shape_s *s, int totalTeethCount,
int skippedCount, operation_mode_e operationMode) {
efiAssertVoid(totalTeethCount > 0, "totalTeethCount is zero");
- trigger_shape_s *s = &engineConfiguration2->triggerShape;
+ s->totalToothCount = totalTeethCount;
+ s->skippedToothCount = skippedCount;
initializeSkippedToothTriggerShape(s, totalTeethCount, skippedCount, operationMode);
s->shaftPositionEventCount = ((totalTeethCount - skippedCount) * 2);
@@ -151,6 +152,7 @@ void initializeSkippedToothTriggerShapeExt(engine_configuration2_s *engineConfig
}
static void configureFordAspireTriggerShape(trigger_config_s *triggerConfig, trigger_shape_s * s) {
+ s->isSynchronizationNeeded = false;
s->reset(FOUR_STROKE_CAM_SENSOR);
s->shaftPositionEventCount = 10;
@@ -174,23 +176,34 @@ static void configureFordAspireTriggerShape(trigger_config_s *triggerConfig, tri
void initializeTriggerShape(Logging *logger, engine_configuration_s *engineConfiguration,
engine_configuration2_s *engineConfiguration2) {
#if EFI_PROD_CODE
- printMsg(logger, "initializeTriggerShape()");
+ scheduleMsg(logger, "initializeTriggerShape()");
#endif
trigger_config_s *triggerConfig = &engineConfiguration->triggerConfig;
trigger_shape_s *triggerShape = &engineConfiguration2->triggerShape;
+
+ setTriggerSynchronizationGap(triggerShape, 2);
+ triggerShape->useRiseEdge = TRUE;
+ triggerShape->needSecondTriggerInput = TRUE;
+
+
switch (triggerConfig->triggerType) {
case TT_TOOTHED_WHEEL:
- initializeSkippedToothTriggerShapeExt(engineConfiguration2, triggerConfig->totalToothCount, triggerConfig->skippedToothCount,
+ engineConfiguration2->triggerShape.needSecondTriggerInput = false;
+
+ engineConfiguration2->triggerShape.isSynchronizationNeeded = engineConfiguration->triggerConfig.customIsSynchronizationNeeded;
+
+ initializeSkippedToothTriggerShapeExt(triggerShape, triggerConfig->customTotalToothCount,
+ triggerConfig->customSkippedToothCount,
getOperationMode(engineConfiguration));
return;
case TT_MAZDA_MIATA_NB:
- initializeMazdaMiataNbShape(triggerConfig, triggerShape);
+ initializeMazdaMiataNbShape(triggerShape);
return;
case TT_DODGE_NEON:
- configureNeonTriggerShape(triggerConfig, triggerShape);
+ configureNeonTriggerShape(triggerShape);
return;
case TT_FORD_ASPIRE:
@@ -202,11 +215,20 @@ void initializeTriggerShape(Logging *logger, engine_configuration_s *engineConfi
return;
case TT_FORD_ESCORT_GT:
- configureMazdaProtegeLx(triggerConfig, triggerShape);
+ configureMazdaProtegeLx(triggerShape);
return;
case TT_MINI_COOPER_R50:
- configureMiniCooperTriggerShape(triggerConfig, triggerShape);
+ configureMiniCooperTriggerShape(triggerShape);
+ return;
+
+ case TT_TOOTHED_WHEEL_60_2:
+ setToothedWheelConfiguration(triggerShape, 60, 2, engineConfiguration);
+ setTriggerSynchronizationGap(triggerShape, 2.5);
+ return;
+
+ case TT_TOOTHED_WHEEL_36_1:
+ setToothedWheelConfiguration(triggerShape, 36, 1, engineConfiguration);
return;
default:
diff --git a/firmware/controllers/trigger/trigger_decoder.h b/firmware/controllers/trigger/trigger_decoder.h
index f31c25e97f..8922c881f7 100644
--- a/firmware/controllers/trigger/trigger_decoder.h
+++ b/firmware/controllers/trigger/trigger_decoder.h
@@ -23,7 +23,7 @@ private:
bool secondaryWheelState;
};
-void initializeSkippedToothTriggerShapeExt(engine_configuration2_s *engineConfiguration2, int totalTeethCount, int skippedCount, operation_mode_e operationMode);
+void initializeSkippedToothTriggerShapeExt(trigger_shape_s *s, int totalTeethCount, int skippedCount, operation_mode_e operationMode);
int findTriggerZeroEventIndex(trigger_shape_s * shape, trigger_config_s const*triggerConfig);
void initializeTriggerShape(Logging *logger, engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2);
void initTriggerDecoder(void);
diff --git a/firmware/controllers/trigger/trigger_emulator_algo.cpp b/firmware/controllers/trigger/trigger_emulator_algo.cpp
index 8f5039b085..b9bf2cd808 100644
--- a/firmware/controllers/trigger/trigger_emulator_algo.cpp
+++ b/firmware/controllers/trigger/trigger_emulator_algo.cpp
@@ -36,22 +36,25 @@ void setTriggerEmulatorRPM(int rpm) {
* togglePwmState() would see that the periodMs has changed and act accordingly
*/
if (rpm == 0) {
- triggerSignal.periodMs = NAN;
+ triggerSignal.periodUs = NAN;
} else {
float gRpm = rpm * engineConfiguration->rpmMultiplier / 60.0; // per minute converted to per second
- triggerSignal.periodMs = frequency2period(gRpm);
+ triggerSignal.periodUs = frequency2periodUs(gRpm);
}
scheduleMsg(&logger, "Emulating position sensor(s). RPM=%d", rpm);
}
static void updateTriggerShapeIfNeeded(PwmConfig *state) {
- if(localVersion.isOld()) {
+ if (localVersion.isOld()) {
scheduleMsg(&logger, "Stimulator: updating trigger shape: %d/%d %d", localVersion.getVersion(), getGlobalConfigurationVersion(), currentTimeMillis());
+
+ applyNonPersistentConfiguration(&logger, engineConfiguration, engineConfiguration2);
+
trigger_shape_s *s = &engineConfiguration2->triggerShape;
int *pinStates[2] = {s->wave.waves[0].pinStates, s->wave.waves[1].pinStates};
copyPwmParameters(state, s->getSize(), s->wave.switchTimes, 2, pinStates);
- state->safe.periodMs = -1; // this would cause loop re-initialization
+ state->safe.periodUs = -1; // this would cause loop re-initialization
}
}
diff --git a/firmware/controllers/trigger/trigger_mazda.cpp b/firmware/controllers/trigger/trigger_mazda.cpp
index 98702868fd..bc0629e7a6 100644
--- a/firmware/controllers/trigger/trigger_mazda.cpp
+++ b/firmware/controllers/trigger/trigger_mazda.cpp
@@ -20,7 +20,10 @@
#include "trigger_mazda.h"
-void initializeMazdaMiataNbShape(trigger_config_s *triggerConfig, trigger_shape_s *s) {
+void initializeMazdaMiataNbShape(trigger_shape_s *s) {
+ setTriggerSynchronizationGap(s, 0.11);
+ s->useRiseEdge = false;
+
s->reset(FOUR_STROKE_CAM_SENSOR);
/**
@@ -57,7 +60,10 @@ void initializeMazdaMiataNbShape(trigger_config_s *triggerConfig, trigger_shape_
s->shaftPositionEventCount = 6 + 16;
}
-void configureMazdaProtegeLx(trigger_config_s *triggerConfig, trigger_shape_s *s) {
+void configureMazdaProtegeLx(trigger_shape_s *s) {
+
+ s->needSecondTriggerInput = FALSE;
+
s->reset(FOUR_STROKE_CAM_SENSOR);
// s->initialState[0] = 1;
@@ -97,5 +103,5 @@ void configureMazdaProtegeLx(trigger_config_s *triggerConfig, trigger_shape_s *s
// s->shaftPositionEventCount = 2 + 8;
s->shaftPositionEventCount = 8;
- triggerConfig->isSynchronizationNeeded = false;
+ s->isSynchronizationNeeded = false;
}
diff --git a/firmware/controllers/trigger/trigger_mazda.h b/firmware/controllers/trigger/trigger_mazda.h
index 0cc01f3d17..87e1b2b6da 100644
--- a/firmware/controllers/trigger/trigger_mazda.h
+++ b/firmware/controllers/trigger/trigger_mazda.h
@@ -12,7 +12,7 @@
#include "engine_configuration.h"
#include "ec2.h"
-void initializeMazdaMiataNbShape(trigger_config_s *triggerConfig, trigger_shape_s *s);
-void configureMazdaProtegeLx(trigger_config_s *triggerConfig, trigger_shape_s *s);
+void initializeMazdaMiataNbShape(trigger_shape_s *s);
+void configureMazdaProtegeLx(trigger_shape_s *s);
#endif /* TRIGGER_MAZDA_H_ */
diff --git a/firmware/controllers/trigger/trigger_structure.cpp b/firmware/controllers/trigger/trigger_structure.cpp
index 215f942fc3..a11cdb8ac2 100644
--- a/firmware/controllers/trigger/trigger_structure.cpp
+++ b/firmware/controllers/trigger/trigger_structure.cpp
@@ -21,6 +21,7 @@
#include "main.h"
#include "trigger_structure.h"
#include "error_handling.h"
+#include "trigger_decoder.h"
trigger_shape_helper::trigger_shape_helper() {
waves[0].init(pinStates0);
@@ -188,3 +189,22 @@ void trigger_shape_s::setSwitchTime(int index, float angle) {
void multi_wave_s::checkSwitchTimes(int size) {
checkSwitchTimes2(size, switchTimes);
}
+
+void setToothedWheelConfiguration(trigger_shape_s *s, int total, int skipped, engine_configuration_s const *engineConfiguration) {
+ s->isSynchronizationNeeded = (skipped != 0);
+
+ s->totalToothCount = total;
+ s->skippedToothCount = skipped;
+ s->needSecondTriggerInput = false;
+ s->useRiseEdge = TRUE;
+
+ initializeSkippedToothTriggerShapeExt(s, s->totalToothCount,
+ s->skippedToothCount,
+ getOperationMode(engineConfiguration));
+}
+
+void setTriggerSynchronizationGap(trigger_shape_s *s, float synchGap) {
+ s->isSynchronizationNeeded = TRUE;
+ s->syncRatioFrom = synchGap * 0.75;
+ s->syncRatioTo = synchGap * 1.25;
+}
diff --git a/firmware/controllers/trigger/trigger_structure.h b/firmware/controllers/trigger/trigger_structure.h
index ede13a3e8b..ee79de4b38 100644
--- a/firmware/controllers/trigger/trigger_structure.h
+++ b/firmware/controllers/trigger/trigger_structure.h
@@ -71,6 +71,19 @@ private:
trigger_shape_helper h;
int size;
public:
+ int isSynchronizationNeeded;
+
+ int totalToothCount;
+ int skippedToothCount;
+
+ float syncRatioFrom;
+ float syncRatioTo;
+
+ int useRiseEdge;
+
+ bool needSecondTriggerInput;
+
+
trigger_shape_s();
void addEvent(float angle, trigger_wheel_e waveIndex, trigger_value_e state);
float getAngle(int phaseIndex) const;
@@ -123,4 +136,7 @@ private:
operation_mode_e operationMode;
};
+void setTriggerSynchronizationGap(trigger_shape_s *s, float synchGap);
+void setToothedWheelConfiguration(trigger_shape_s *s, int total, int skipped, engine_configuration_s const *engineConfiguration);
+
#endif /* TRIGGER_STRUCTURE_H_ */
diff --git a/firmware/emulation/engine_emulator.cpp b/firmware/emulation/engine_emulator.cpp
index 6d0bdd1319..c7ee574e2d 100644
--- a/firmware/emulation/engine_emulator.cpp
+++ b/firmware/emulation/engine_emulator.cpp
@@ -1,16 +1,16 @@
/**
- * @file engine_emulator.c
+ * @file engine_emulator.cpp
* @brief Entry point for all the emulation and analysis code
*
* @date Mar 15, 2013
* @author Andrey Belomutskiy, (c) 2012-2014
*/
-extern "C" {
-
#include "main.h"
#include "engine_emulator.h"
+extern "C" {
+
#include "status_loop.h"
#include "advance_map.h"
#include "wave_analyzer.h"
@@ -92,12 +92,12 @@ static void initECUstimulator(void) {
chThdCreateStatic(eeThreadStack, sizeof(eeThreadStack), NORMALPRIO, (tfunc_t) eeThread, NULL);
}
-void initEngineEmulator(void) {
+void initEngineEmulator(board_configuration_s *boardConfiguration) {
if (hasFirmwareError())
return;
#if EFI_POTENTIOMETER
- initPotentiometers();
+ initPotentiometers(boardConfiguration);
#endif /* EFI_POTENTIOMETER */
//initECUstimulator();
diff --git a/firmware/emulation/engine_emulator.h b/firmware/emulation/engine_emulator.h
index 6494f25b9a..8de4135040 100644
--- a/firmware/emulation/engine_emulator.h
+++ b/firmware/emulation/engine_emulator.h
@@ -8,18 +8,7 @@
#ifndef ENGINE_EMULATOR_H_
#define ENGINE_EMULATOR_H_
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-void initEngineEmulator(void);
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
+#include "engine_configuration.h"
+void initEngineEmulator(board_configuration_s *boardConfiguration);
#endif /* ENGINE_EMULATOR_H_ */
diff --git a/firmware/emulation/hw_layer/poten.c b/firmware/emulation/hw_layer/poten.c
index 1e54aa0f07..a443e3ee71 100644
--- a/firmware/emulation/hw_layer/poten.c
+++ b/firmware/emulation/hw_layer/poten.c
@@ -11,6 +11,7 @@
#include "eficonsole.h"
#include "pin_repository.h"
#include "engine_configuration.h"
+#include "hardware.h"
/**
* MCP42010 digital potentiometer driver
@@ -35,40 +36,30 @@
*
*/
-SPIDriver * getDigiralPotDevice(void) {
-#if STM32_SPI_USE_SPI2 || defined(__DOXYGEN__)
-// return &SPID2;
+SPIDriver * getDigiralPotDevice(spi_device_e spiDevice) {
+#if STM32_SPI_USE_SPI1 || defined(__DOXYGEN__)
+ if (spiDevice == SPI_DEVICE_1)
+ return &SPID1;
#endif
- return &SPID3;
+#if STM32_SPI_USE_SPI2 || defined(__DOXYGEN__)
+ if (spiDevic e== SPI_DEVICE_2)
+ return &SPID2;
+#endif
+#if STM32_SPI_USE_SPI3 || defined(__DOXYGEN__)
+ if (spiDevice == SPI_DEVICE_3)
+ return &SPID3;
+#endif
+ firmwareError("Unexpected SPI device: %d", spiDevice);
+ return NULL;
}
-
-//#define POTEN_CS_PORT GPIOB
-//#define POTEN_CS_PIN 12
-
-
-//#define POT_SPI &SPID1
-
-// PA13 & PA14 are system pins
-
-//// chip select
-//#define POTEN_CS_PORT GPIOE
-//#define POTEN_CS_PIN 15
-
-// chip select
-#define POTEN_CS_PORT GPIOA
-#define POTEN_CS_PIN 10
-//#define POT_SPI &SPID3
-
-
/* Low speed SPI configuration (281.250kHz, CPHA=0, CPOL=0, MSb first).*/
#define SPI_POT_CONFIG SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_DFF
static Logging logger;
-
#if EFI_POTENTIOMETER
-Mcp42010Driver config0;
+Mcp42010Driver config[DIGIPOT_COUNT];
void initPotentiometer(Mcp42010Driver *driver, SPIDriver *spi, ioportid_t port, ioportmask_t pin) {
driver->spiConfig.end_cb = NULL;
@@ -83,14 +74,15 @@ static int getPotStep(int resistanceWA) {
return 256 - (int) ((resistanceWA - 52) * 256 / 10000);
}
-
static void sendToPot(Mcp42010Driver *driver, int channel, int value) {
+ lockSpi(SPI_NONE);
spiStart(driver->spi, &driver->spiConfig);
spiSelect(driver->spi);
int word = (17 + channel) * 256 + value;
spiSend(driver->spi, 1, &word);
spiUnselect(driver->spi);
spiStop(driver->spi);
+ unlockSpi();
}
void setPotResistance(Mcp42010Driver *driver, int channel, int resistance) {
@@ -105,32 +97,42 @@ void setPotResistance(Mcp42010Driver *driver, int channel, int resistance) {
appendPrintf(&logger, "%d for R=%d", value, resistance);
appendMsgPostfix(logging);
-
scheduleLogging(logging);
sendToPot(driver, channel, value);
}
-
static void setPotResistance0(int value) {
- setPotResistance(&config0, 0, value);
+ setPotResistance(&config[0], 0, value);
}
static void setPotResistance1(int value) {
- setPotResistance(&config0, 1, value);
+ setPotResistance(&config[0], 1, value);
}
static void setPotValue1(int value) {
- sendToPot(&config0, 1, value);
+ sendToPot(&config[0], 1, value);
}
#endif /* EFI_POTENTIOMETER */
-void initPotentiometers() {
+void initPotentiometers(board_configuration_s *boardConfiguration) {
#if EFI_POTENTIOMETER
initLogging(&logger, "potentiometer");
+ if (boardConfiguration->digitalPotentiometerSpiDevice == SPI_NONE) {
+ scheduleMsg(&logger, "digiPot spi disabled");
+ return;
+ }
+ turnOnSpi(boardConfiguration->digitalPotentiometerSpiDevice);
- initPotentiometer(&config0, getDigiralPotDevice(), POTEN_CS_PORT, POTEN_CS_PIN);
+ for (int i = 0; i < DIGIPOT_COUNT; i++) {
+ brain_pin_e csPin = boardConfiguration->digitalPotentiometerChipSelect[i];
+ if (csPin == GPIO_NONE)
+ continue;
+
+ initPotentiometer(&config[i], getDigiralPotDevice(boardConfiguration->digitalPotentiometerSpiDevice),
+ getHwPort(csPin), getHwPin(csPin));
+ }
addConsoleActionI("pot0", setPotResistance0);
addConsoleActionI("pot1", setPotResistance1);
@@ -140,6 +142,6 @@ void initPotentiometers() {
setPotResistance0(3000);
setPotResistance1(7000);
#else
- print("potentiometer disabled\r\n");
+ print("digiPot logic disabled\r\n");
#endif
}
diff --git a/firmware/emulation/hw_layer/poten.h b/firmware/emulation/hw_layer/poten.h
index 8c53bfeaa0..c15de7de95 100644
--- a/firmware/emulation/hw_layer/poten.h
+++ b/firmware/emulation/hw_layer/poten.h
@@ -10,6 +10,7 @@
#define POTEN_H_
#include "main.h"
+#include "engine_configuration.h"
typedef struct {
SPIDriver *spi;
@@ -17,7 +18,7 @@ typedef struct {
} Mcp42010Driver;
void initPotentiometer(Mcp42010Driver *driver, SPIDriver *spi, ioportid_t port, ioportmask_t pin);
-void initPotentiometers(void);
+void initPotentiometers(board_configuration_s *boardConfiguration);
void setPotResistance(Mcp42010Driver *driver, int channel, int resistance);
#endif /* POTEN_H_ */
diff --git a/firmware/emulation/rfi_perftest.cpp b/firmware/emulation/rfi_perftest.cpp
index a071c80324..0bd8eebb9b 100644
--- a/firmware/emulation/rfi_perftest.cpp
+++ b/firmware/emulation/rfi_perftest.cpp
@@ -76,17 +76,17 @@ static void testRusefiMethods(const int count) {
start = currentTimeMillis();
for (int i = 0; i < count; i++)
- tempi += getBaseFuel(4020, 2.21111);
+ tempi += getBaseTableFuel(4020, 2.21111);
time = currentTimeMillis() - start;
if (tempi != 0)
scheduleMsg(&logger, "Finished %d iterations of getBaseFuel in %dms", count, time);
- start = currentTimeMillis();
- for (int i = 0; i < count; i++)
- tempi += getFuelMs(1200);
- time = currentTimeMillis() - start;
- if (tempi != 0)
- scheduleMsg(&logger, "Finished %d iterations of getFuelMs in %dms", count, time);
+// start = currentTimeMillis();
+// for (int i = 0; i < count; i++)
+// tempi += getFuelMs(1200, NULL); // todo
+// time = currentTimeMillis() - start;
+// if (tempi != 0)
+// scheduleMsg(&logger, "Finished %d iterations of getFuelMs in %dms", count, time);
start = currentTimeMillis();
for (int i = 0; i < count; i++) {
diff --git a/firmware/hw_layer/AdcConfiguration.h b/firmware/hw_layer/AdcConfiguration.h
index 15e579741f..39fdebcfa5 100644
--- a/firmware/hw_layer/AdcConfiguration.h
+++ b/firmware/hw_layer/AdcConfiguration.h
@@ -16,6 +16,7 @@ public:
int size();
void init(void);
int conversionCount;
+ int errorsCount;
private:
ADCConversionGroup* hwConfig;
/**
diff --git a/firmware/hw_layer/adc_inputs.cpp b/firmware/hw_layer/adc_inputs.cpp
index b9c93a6ce0..b8a95ac414 100644
--- a/firmware/hw_layer/adc_inputs.cpp
+++ b/firmware/hw_layer/adc_inputs.cpp
@@ -1,5 +1,5 @@
/**
- * @file adc_inputs.c
+ * @file adc_inputs.cpp
* @brief Low level ADC code
*
* @date Jan 14, 2013
@@ -33,6 +33,8 @@ AdcConfiguration::AdcConfiguration(ADCConversionGroup* hwConfig) {
#define ADC_NUMBER_CHANNELS_FAST 1
// todo: migrate from hardware timer to software ADC conversion triggering
+// todo: I guess we would have to use ChibiOS timer and not our own timer because
+// todo: adcStartConversionI requires OS lock. currently slow ADC is 10Hz (?)
#define PWM_FREQ_SLOW 5000 /* PWM clock frequency. I wonder what does this setting mean? */
#define PWM_PERIOD_SLOW 500 /* PWM period (in PWM ticks). */
@@ -40,7 +42,8 @@ AdcConfiguration::AdcConfiguration(ADCConversionGroup* hwConfig) {
* 8000 RPM is 133Hz
* If we want to sample MAP once per 5 degrees we need 133Hz * (360 / 5) = 9576Hz of fast ADC
*/
-// todo: migrate to continues ADC mode?
+// todo: migrate to continues ADC mode? probably not - we cannot afford the callback in
+// todo: continues mode. todo: look into our options
#define PWM_FREQ_FAST 100000 /* PWM clock frequency. I wonder what does this setting mean? */
#define PWM_PERIOD_FAST 10 /* PWM period (in PWM ticks). */
@@ -158,11 +161,13 @@ static void pwmpcb_slow(PWMDriver *pwmp) {
if (ADC_FAST_DEVICE.state != ADC_READY &&
ADC_FAST_DEVICE.state != ADC_COMPLETE &&
ADC_FAST_DEVICE.state != ADC_ERROR) {
- firmwareError("ADC slow not ready?");
+ // todo: why and when does this happen? firmwareError("ADC slow not ready?");
+ slowAdc.errorsCount++;
chSysUnlockFromIsr()
;
return;
}
+ slowAdc.errorsCount++;
adcStartConversionI(&ADC_SLOW_DEVICE, &adcgrpcfgSlow, slowAdcState.samples, ADC_GRP1_BUF_DEPTH_SLOW);
chSysUnlockFromIsr()
;
@@ -184,7 +189,8 @@ static void pwmpcb_fast(PWMDriver *pwmp) {
if (ADC_FAST_DEVICE.state != ADC_READY &&
ADC_FAST_DEVICE.state != ADC_COMPLETE &&
ADC_FAST_DEVICE.state != ADC_ERROR) {
- firmwareError("ADC fast not ready?");
+ fastAdc.errorsCount++;
+ // todo: when? why? firmwareError("ADC fast not ready?");
chSysUnlockFromIsr()
;
return;
diff --git a/firmware/hw_layer/board_test.cpp b/firmware/hw_layer/board_test.cpp
index ebb385ea93..102a892c3e 100644
--- a/firmware/hw_layer/board_test.cpp
+++ b/firmware/hw_layer/board_test.cpp
@@ -140,7 +140,7 @@ void initBoardTest(void) {
// print("ADC%d val= %d%s", hwIndex, value, DELIMETER);
float volts = adcToVolts(adcValue) * 2;
- print("v=%f adc=%d c=%d\r\n", volts, adcValue, c++);
+ print("v=%f adc=%d c=%d (hit 'n' for next step\r\n", volts, adcValue, c++);
chThdSleepMilliseconds(300);
diff --git a/firmware/hw_layer/gpio_helper.c b/firmware/hw_layer/gpio_helper.c
index 8f989df2a3..39109e0106 100644
--- a/firmware/hw_layer/gpio_helper.c
+++ b/firmware/hw_layer/gpio_helper.c
@@ -31,6 +31,11 @@
* @brief Initialize the hardware output pin while also assigning it a logical name
*/
void initOutputPinExt(const char *msg, OutputPin *outputPin, GPIO_TypeDef *port, uint32_t pinNumber, iomode_t mode) {
+// if (outputPin->port != NULL) {
+// todo: need to clear '&outputs' in io_pins.c
+// firmwareError("outputPin already assigned to %x%d", outputPin->port, outputPin->pin);
+// return;
+// }
outputPin->currentLogicValue = -1;
outputPin->port = port;
outputPin->pin = pinNumber;
diff --git a/firmware/hw_layer/hardware.cpp b/firmware/hw_layer/hardware.cpp
index 8ef625b95b..4833a720ae 100644
--- a/firmware/hw_layer/hardware.cpp
+++ b/firmware/hw_layer/hardware.cpp
@@ -39,12 +39,12 @@
#include "engine_configuration.h"
#include "ec2.h"
-McpAdcState adcState;
-
extern engine_configuration_s *engineConfiguration;
extern engine_configuration2_s * engineConfiguration2;
extern board_configuration_s *boardConfiguration;
+static bool isSpiInitialized[5] = { false, false, false, false, false };
+
static void initSpiModule(SPIDriver *driver, ioportid_t sckPort, ioportmask_t sckPin, ioportid_t misoPort,
ioportmask_t misoPin, ioportid_t mosiPort, ioportmask_t mosiPin, int af) {
mySetPadMode("SPI clock", sckPort, sckPin, PAL_MODE_ALTERNATE(af));
@@ -53,23 +53,59 @@ static void initSpiModule(SPIDriver *driver, ioportid_t sckPort, ioportmask_t sc
mySetPadMode("SPI master in ", misoPort, misoPin, PAL_MODE_ALTERNATE(af));
}
-void initSpiModules(void) {
+static Mutex spiMtx;
+
+/**
+ * Only one consumer can use SPI bus at a given time
+ */
+void lockSpi(spi_device_e device) {
+ // todo: different locks for different SPI devices!
+ chMtxLock(&spiMtx);
+}
+
+void unlockSpi(void) {
+ chMtxUnlock();
+}
+
+void turnOnSpi(spi_device_e device) {
+ if (isSpiInitialized[device])
+ return; // already initialized
+ isSpiInitialized[device] = true;
+ if (device == SPI_DEVICE_1) {
+#if STM32_SPI_USE_SPI1
+// scheduleMsg(&logging, "Turning on SPI1 pins");
+ initSpiModule(&SPID1,
+ EFI_SPI1_SCK_PORT, EFI_SPI1_SCK_PIN,
+ EFI_SPI1_MISO_PORT, EFI_SPI1_MISO_PIN,
+ EFI_SPI1_MOSI_PORT, EFI_SPI1_MOSI_PIN,
+ EFI_SPI1_AF);
+#endif
+ }
+ if (device == SPI_DEVICE_2) {
#if STM32_SPI_USE_SPI2
// scheduleMsg(&logging, "Turning on SPI2 pins");
- initSpiModule(&SPID2,
- EFI_SPI2_SCK_PORT, EFI_SPI2_SCK_PIN,
- EFI_SPI2_MISO_PORT, EFI_SPI2_MISO_PIN,
- EFI_SPI2_MOSI_PORT, EFI_SPI2_MOSI_PIN,
- EFI_SPI2_AF);
+ initSpiModule(&SPID2,
+ EFI_SPI2_SCK_PORT, EFI_SPI2_SCK_PIN,
+ EFI_SPI2_MISO_PORT, EFI_SPI2_MISO_PIN,
+ EFI_SPI2_MOSI_PORT, EFI_SPI2_MOSI_PIN,
+ EFI_SPI2_AF);
#endif
+ }
+ if (device == SPI_DEVICE_3) {
#if STM32_SPI_USE_SPI3
// scheduleMsg(&logging, "Turning on SPI3 pins");
- initSpiModule(&SPID3,
- EFI_SPI3_SCK_PORT, EFI_SPI3_SCK_PIN,
- EFI_SPI3_MISO_PORT, EFI_SPI3_MISO_PIN,
- EFI_SPI3_MOSI_PORT, EFI_SPI3_MOSI_PIN,
- EFI_SPI3_AF);
+ initSpiModule(&SPID3,
+ EFI_SPI3_SCK_PORT, EFI_SPI3_SCK_PIN,
+ EFI_SPI3_MISO_PORT, EFI_SPI3_MISO_PIN,
+ EFI_SPI3_MOSI_PORT, EFI_SPI3_MOSI_PIN,
+ EFI_SPI3_AF);
#endif
+ }
+}
+
+void initSpiModules(void) {
+ turnOnSpi(SPI_DEVICE_2);
+ turnOnSpi(SPI_DEVICE_3);
}
static I2CConfig i2cfg = { OPMODE_I2C, 100000, STD_DUTY_CYCLE, };
@@ -100,6 +136,9 @@ void initHardware(Logging *logger) {
// 10 extra seconds to re-flash the chip
//flashProtect();
+ chMtxInit(&spiMtx);
+
+
#if EFI_HISTOGRAMS
/**
* histograms is a data structure for CPU monitor, it does not depend on configuration
@@ -199,9 +238,7 @@ void initHardware(Logging *logger) {
if (hasFirmwareError())
return;
- char buffer[16];
- itoa10(buffer, SVN_VERSION);
- lcd_HD44780_print_string(buffer);
+ lcd_HD44780_print_string(VCS_VERSION);
#endif /* EFI_HD44780_LCD */
diff --git a/firmware/hw_layer/hardware.h b/firmware/hw_layer/hardware.h
index 7e024b24ad..3e7641556d 100644
--- a/firmware/hw_layer/hardware.h
+++ b/firmware/hw_layer/hardware.h
@@ -10,6 +10,19 @@
#include "main.h"
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+void turnOnSpi(spi_device_e device);
+void lockSpi(spi_device_e device);
+void unlockSpi(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
#define GET_BOARD_TEST_MODE_VALUE() (!palReadPad(getHwPort(boardConfiguration->boardTestModeJumperPin), getHwPin(boardConfiguration->boardTestModeJumperPin)))
void initHardware(Logging *logging);
diff --git a/firmware/hw_layer/io_pins.c b/firmware/hw_layer/io_pins.c
index 6a386fd6c4..da52cdca67 100644
--- a/firmware/hw_layer/io_pins.c
+++ b/firmware/hw_layer/io_pins.c
@@ -114,6 +114,7 @@ static void errBlinkingThread(void *arg) {
static void outputPinRegisterExt(const char *msg, io_pin_e ioPin, GPIO_TypeDef *port, uint32_t pin,
pin_output_mode_e *outputMode) {
+ efiAssertVoid((int)ioPin < IO_PIN_COUNT, "io pin out of range");
if (port == GPIO_NULL) {
// that's for GRIO_NONE
outputs[ioPin].port = port;
@@ -184,6 +185,13 @@ void initOutputPins(void) {
outputPinRegister("is running status", LED_RUNNING, LED_RUNNING_STATUS_PORT, LED_RUNNING_STATUS_PIN);
outputPinRegister("communication status 1", LED_COMMUNICATION_1, LED_COMMUNICATION_PORT, LED_COMMUNICATION_PIN);
+ /**
+ * want to make sure it's all zeros so that we can compare in initOutputPinExt() method
+ */
+// todo: it's too late to clear now? this breaks default status LEDs
+// todo: fix this?
+// memset(&outputs, 0, sizeof(outputs));
+
// outputPinRegister("ext led 1", LED_EXT_1, EXTRA_LED_1_PORT, EXTRA_LED_1_PIN);
// outputPinRegister("ext led 2", LED_EXT_2, EXTRA_LED_2_PORT, EXTRA_LED_2_PIN);
// outputPinRegister("ext led 3", LED_EXT_3, EXTRA_LED_2_PORT, EXTRA_LED_3_PIN);
@@ -192,10 +200,11 @@ void initOutputPins(void) {
outputPinRegister("MalfunctionIndicator", LED_CHECK_ENGINE, getHwPort(boardConfiguration->malfunctionIndicatorPin),
getHwPin(boardConfiguration->malfunctionIndicatorPin));
- outputPinRegister("spi CS1", SPI_CS_1, SPI_CS1_PORT, SPI_CS1_PIN);
- outputPinRegister("spi CS2", SPI_CS_2, SPI_CS2_PORT, SPI_CS2_PIN);
- outputPinRegister("spi CS3", SPI_CS_3, SPI_CS3_PORT, SPI_CS3_PIN);
- outputPinRegister("spi CS4", SPI_CS_4, SPI_CS4_PORT, SPI_CS4_PIN);
+// todo: are these needed here? todo: make configurable
+// outputPinRegister("spi CS1", SPI_CS_1, SPI_CS1_PORT, SPI_CS1_PIN);
+// outputPinRegister("spi CS2", SPI_CS_2, SPI_CS2_PORT, SPI_CS2_PIN);
+// outputPinRegister("spi CS3", SPI_CS_3, SPI_CS3_PORT, SPI_CS3_PIN);
+// outputPinRegister("spi CS4", SPI_CS_4, SPI_CS4_PORT, SPI_CS4_PIN);
outputPinRegister("spi CS5", SPI_CS_SD_MODULE, SPI_SD_MODULE_PORT, SPI_SD_MODULE_PIN);
// todo: should we move this code closer to the fuel pump logic?
diff --git a/firmware/hw_layer/lcd/lcd_HD44780.c b/firmware/hw_layer/lcd/lcd_HD44780.c
index 2a9945f18a..3a072a6d2b 100644
--- a/firmware/hw_layer/lcd/lcd_HD44780.c
+++ b/firmware/hw_layer/lcd/lcd_HD44780.c
@@ -150,7 +150,7 @@ void lcd_HD44780_print_char(char data) {
}
}
-void lcd_HD44780_print_string(char* string) {
+void lcd_HD44780_print_string(const char* string) {
while (*string != 0x00)
lcd_HD44780_print_char(*string++);
}
diff --git a/firmware/hw_layer/lcd/lcd_HD44780.h b/firmware/hw_layer/lcd/lcd_HD44780.h
index 7221df151a..c4f95c8e36 100644
--- a/firmware/hw_layer/lcd/lcd_HD44780.h
+++ b/firmware/hw_layer/lcd/lcd_HD44780.h
@@ -16,7 +16,7 @@ extern "C"
void lcd_HD44780_init(void);
void lcd_HD44780_set_position(uint8_t row, uint8_t column);
void lcd_HD44780_print_char(char data);
-void lcd_HD44780_print_string(char *string);
+void lcd_HD44780_print_string(const char *string);
void lcdShowFatalMessage(char *message);
diff --git a/firmware/hw_layer/mmc_card.c b/firmware/hw_layer/mmc_card.c
index 813e7814dc..c65f68a633 100644
--- a/firmware/hw_layer/mmc_card.c
+++ b/firmware/hw_layer/mmc_card.c
@@ -19,6 +19,7 @@
#include "mmc_card.h"
#include "pin_repository.h"
#include "ff.h"
+#include "hardware.h"
#define PUSHPULLDELAY 500
@@ -88,20 +89,24 @@ static void sdStatistics(void) {
* so that we can later append to that file
*/
static void createLogFile(void) {
+ lockSpi(SPI_NONE);
memset(&FDLogFile, 0, sizeof(FIL)); // clear the memory
FRESULT err = f_open(&FDLogFile, "rusefi.log", FA_OPEN_ALWAYS | FA_WRITE); // Create new file
if (err != FR_OK && err != FR_EXIST) {
+ unlockSpi();
printError("Card mounted...\r\nCan't create Log file, check your SD.\r\nFS mount failed", err); // else - show error
return;
}
err = f_lseek(&FDLogFile, f_size(&FDLogFile)); // Move to end of the file to append data
if (err) {
+ unlockSpi();
printError("Seek error", err);
return;
}
f_sync(&FDLogFile);
fs_ready = TRUE; // everything Ok
+ unlockSpi();
}
static void ff_cmd_dir(char *path) {
@@ -163,11 +168,13 @@ void appendToLog(char *line) {
}
int lineLength = strlen(line);
totalLoggedBytes += lineLength;
+ lockSpi(SPI_NONE);
FRESULT err = f_write(&FDLogFile, line, lineLength, &bytesWrited);
if (bytesWrited < lineLength) {
printError("write error or disk full", err); // error or disk full
}
f_sync(&FDLogFile);
+ unlockSpi();
}
/*
@@ -203,11 +210,14 @@ static void MMCmount(void) {
mmcStart(&MMCD1, &mmccfg); // Configures and activates the MMC peripheral.
// Performs the initialization procedure on the inserted card.
+ lockSpi(SPI_NONE);
if (mmcConnect(&MMCD1) != CH_SUCCESS) {
- scheduleMsg(&logger, "Can't connect or mount MMC/SD");
+ warning(OBD_PCM_Processor_Fault, "Can't connect or mount MMC/SD");
+ unlockSpi();
return;
}
+ unlockSpi();
// if Ok - mount FS now
memset(&MMC_FS, 0, sizeof(FATFS)); // reserve the memory
if (f_mount(0, &MMC_FS) == FR_OK) {
diff --git a/firmware/hw_layer/pwm_generator.cpp b/firmware/hw_layer/pwm_generator.cpp
index 111f03ae41..3572f2a213 100644
--- a/firmware/hw_layer/pwm_generator.cpp
+++ b/firmware/hw_layer/pwm_generator.cpp
@@ -44,7 +44,7 @@ void startSimplePwm(PwmConfig *state, const char *msg, io_pin_e ioPin,
state->outputPins[0] = ioPin;
- state->periodMs = frequency2period(frequency);
+ state->periodUs = frequency2periodUs(frequency);
weComplexInit(msg, state, 2, switchTimes, 1, pinStates, NULL, applyPinState);
}
diff --git a/firmware/hw_layer/pwm_generator.h b/firmware/hw_layer/pwm_generator.h
index a86ac7c83f..3f7ba6043a 100644
--- a/firmware/hw_layer/pwm_generator.h
+++ b/firmware/hw_layer/pwm_generator.h
@@ -15,16 +15,16 @@
#include "gpio_helper.h"
+void startSimplePwm(PwmConfig *state, const char *msg, io_pin_e ioPin,
+ float dutyCycle, float frequency);
+void applyPinState(PwmConfig *state, int stateIndex);
+
+
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
-void applyPinState(PwmConfig *state, int stateIndex);
-
-void startSimplePwm(PwmConfig *state, const char *msg, io_pin_e ioPin,
- float dutyCycle, float frequency);
-
void startSimplePwmExt(PwmConfig *state, const char *msg, brain_pin_e brainPin, io_pin_e ioPin,
float frequency, float dutyCycle);
diff --git a/firmware/hw_layer/serial_over_usb/usbconsole.h b/firmware/hw_layer/serial_over_usb/usbconsole.h
index 24b3736655..cc9b6f9d7a 100644
--- a/firmware/hw_layer/serial_over_usb/usbconsole.h
+++ b/firmware/hw_layer/serial_over_usb/usbconsole.h
@@ -8,7 +8,16 @@
#ifndef USBCONSOLE_H_
#define USBCONSOLE_H_
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
void usb_serial_start(void);
int is_usb_serial_ready(void);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
#endif /* USBCONSOLE_H_ */
diff --git a/firmware/hw_layer/trigger_input.cpp b/firmware/hw_layer/trigger_input.cpp
index 4aba3f5d41..cb65eda94f 100644
--- a/firmware/hw_layer/trigger_input.cpp
+++ b/firmware/hw_layer/trigger_input.cpp
@@ -22,6 +22,7 @@ static WaveReaderHw primaryCrankInput;
static WaveReaderHw secondaryCrankInput;
extern engine_configuration_s *engineConfiguration;
+extern engine_configuration2_s *engineConfiguration2;
extern board_configuration_s *boardConfiguration;
@@ -39,7 +40,7 @@ static inline ICUDriver *getSecondaryInputCaptureDriver(void) {
*/
static void shaft_icu_width_callback(ICUDriver *icup) {
int isPrimary = icup == getPrimaryInputCaptureDriver();
- if (!isPrimary && !engineConfiguration->needSecondTriggerInput)
+ if (!isPrimary && !engineConfiguration2->triggerShape.needSecondTriggerInput)
return;
// icucnt_t last_width = icuGetWidth(icup); so far we are fine with system time
trigger_event_e signal = isPrimary ? SHAFT_PRIMARY_UP : SHAFT_SECONDARY_UP;
@@ -49,7 +50,7 @@ static void shaft_icu_width_callback(ICUDriver *icup) {
static void shaft_icu_period_callback(ICUDriver *icup) {
int isPrimary = icup == getPrimaryInputCaptureDriver();
- if (!isPrimary && !engineConfiguration->needSecondTriggerInput)
+ if (!isPrimary && !engineConfiguration2->triggerShape.needSecondTriggerInput)
return;
// icucnt_t last_period = icuGetPeriod(icup); so far we are fine with system time
diff --git a/firmware/iar/ch.ewp b/firmware/iar/ch.ewp
index 016d134ac0..49673c971f 100644
--- a/firmware/iar/ch.ewp
+++ b/firmware/iar/ch.ewp
@@ -2034,13 +2034,13 @@
tunerstudio
- $PROJ_DIR$\..\console\tunerstudio\tunerstudio.c
+ $PROJ_DIR$\..\console\tunerstudio\tunerstudio.cpp
$PROJ_DIR$\..\console\tunerstudio\tunerstudio.h
- $PROJ_DIR$\..\console\tunerstudio\tunerstudio_algo.c
+ $PROJ_DIR$\..\console\tunerstudio\tunerstudio_algo.cpp
$PROJ_DIR$\..\console\tunerstudio\tunerstudio_algo.h
@@ -2111,6 +2111,12 @@
$PROJ_DIR$\..\controllers\algo\ec2.h
+
+ $PROJ_DIR$\..\controllers\algo\engine.cpp
+
+
+ $PROJ_DIR$\..\controllers\algo\engine.h
+
$PROJ_DIR$\..\controllers\algo\engine_configuration.cpp
@@ -2153,6 +2159,12 @@
$PROJ_DIR$\..\controllers\algo\malfunction_central.h
+
+ $PROJ_DIR$\..\controllers\algo\map_adjuster.c
+
+
+ $PROJ_DIR$\..\controllers\algo\map_adjuster.h
+
$PROJ_DIR$\..\controllers\algo\nmea.c
@@ -2210,6 +2222,12 @@
$PROJ_DIR$\..\controllers\core\signal_filtering.h
+
+ $PROJ_DIR$\..\controllers\core\table_helper.cpp
+
+
+ $PROJ_DIR$\..\controllers\core\table_helper.h
+
math
@@ -2450,6 +2468,12 @@
emulation
hw_layer
+
+ $PROJ_DIR$\..\emulation\hw_layer\poten.c
+
+
+ $PROJ_DIR$\..\emulation\hw_layer\poten.h
+
test
diff --git a/firmware/rusefi.cpp b/firmware/rusefi.cpp
index 81435cef0f..49ab99623e 100644
--- a/firmware/rusefi.cpp
+++ b/firmware/rusefi.cpp
@@ -150,7 +150,7 @@ void runRusEfi(void) {
#endif
#if EFI_ENGINE_EMULATOR
- initEngineEmulator();
+ initEngineEmulator(boardConfiguration);
#endif
startStatusThreads();
@@ -253,5 +253,5 @@ void firmwareError(const char *fmt, ...) {
}
int getRusEfiVersion(void) {
- return 20140709;
+ return 20140724;
}
diff --git a/firmware/svnversion.h b/firmware/svnversion.h
index 4b61c21238..6fe1449f9e 100644
--- a/firmware/svnversion.h
+++ b/firmware/svnversion.h
@@ -1,5 +1,5 @@
// This file was generated by Version2Header
-// Sat Jul 05 19:09:48 EDT 2014
-#ifndef SVN_VERSION
-#define SVN_VERSION 3860
+// Wed Jul 23 08:21:40 EDT 2014
+#ifndef VCS_VERSION
+#define VCS_VERSION "4047"
#endif
diff --git a/firmware/tunerstudio/rusefi.ini b/firmware/tunerstudio/rusefi.ini
index bbd8e9a83c..473a6c7623 100644
--- a/firmware/tunerstudio/rusefi.ini
+++ b/firmware/tunerstudio/rusefi.ini
@@ -1,6 +1,6 @@
; this is TunerStudio project for www.rusefi.com DIY engine management system
-; version 20140706
+; version 20140721
; this should stop TS from looking for the CAN ID in the 2nd byte location and allow the page reads to work correctly.
enable2ndByteCanID = false
@@ -15,7 +15,7 @@ enable2ndByteCanID = false
endianness = little
nPages = 1
- pageSize = 5804
+ pageSize = 5824
pageIdentifier = "\x00\x00"
pageReadCommand = "R\x00\x00%2o%2c"
@@ -110,7 +110,7 @@ enable2ndByteCanID = false
fuelKeyBins = array, F32, 792, [16], "V", 1, 0, 0.0, 10.0, 2; size 132
fuelRpmBins = array, F32, 856, [16], "RPM", 1, 0, 0.0, 25500.0, 2; size 92
displacement = scalar, F32, 920, "°C", 1, 0, 0, 1000.0, 2 ; size 4
- rpmHardLimit = scalar, U32, 924, "°C", 1, 0, 0, 1000.0, 2 ; size 4
+ rpmHardLimit = scalar, U32, 924, "°C", 1, 0, 0, 10000.0, 2 ; size 4
crankingInjectionMode = scalar, U32, 928, "°C", 1, 0, 0, 1000.0, 2 ; size 4
injectionMode = scalar, U32, 932, "°C", 1, 0, 0, 1000.0, 2 ; size 4
globalTriggerOffsetAngle = scalar, F32, 936, "RPM", 1, 0, 0, 720, 0 ; size 4
@@ -135,7 +135,7 @@ enable2ndByteCanID = false
tpsAdcInput = bits, U32, 1012, [0:4] "PA0", "PA1", "PA2", "PA3", "PA4", "PA5", "PA6", "PA7", "PB0", "PB1", "PB0", "PB1", "PB2", "PB3", "PB4", "PB5"
overrideCrankingIgnition= scalar, F32, 1016, "RPM", 1, 0.0, 0, 1000.0, 2 ; size 4
analogChartFrequency = scalar, U32, 1020, "index", 1, 0, 0, 300, 0 ; size 4
- trigger_type = bits, U32, 1024, [0:1], "toothed wheel", "ford aspire", "dodge neon", "INVALID"
+ trigger_type = bits, U32, 1024, [0:3], "custom toothed wheel", "ford aspire", "dodge neon", "Miata NA", "Miata NB", "GM_7X", "Cooper", "Escort GT", "60/2", "36/1", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
isSynchronizationNeeded = bits, U32, 1028, [0:0], "false", "true"
totalToothCount = scalar, F32, 1032, "RPM", 1, 0.0, 0, 1000.0, 2 ; size 4
skippedToothCount = scalar, F32, 1036, "RPM", 1, 0.0, 0, 1000.0, 2 ; size 4
@@ -210,9 +210,30 @@ enable2ndByteCanID = false
electronicThrottlePin1 = bits, U32, 5612, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
electronicThrottlePin1Mode = bits, U32, 5616, [0:1], "default", "default inverted", "open", "open inverted"
-; idleSwitchPin
+ idleSwitchPin = bits, U32, 5620, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+; idleSwitchPinMode
+ alternatorControlPin = bits, U32, 5628, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+; alternatorControlPinMode
;todo
+ HD44780_rs = bits, U32, 5636, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+ HD44780_e = bits, U32, 5640, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+ HD44780_db4 = bits, U32, 5644, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+ HD44780_db5 = bits, U32, 5648, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+ HD44780_db6 = bits, U32, 5652, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+ HD44780_db7 = bits, U32, 5656, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+
+ ;triggerSimulatorPin0
+ ;triggerSimulatorPin1
+ ;triggerSimulatorPinModes
+
+
+ digitalPotentiometerSpiDevice=bits,U32, 5676, [0:1], "Off", "SPI1", "SPI2", "SPI3"
+ digitalPotentiometerChipSelect0=bits,U32, 5680, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+ digitalPotentiometerChipSelect1=bits,U32, 5684, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+ digitalPotentiometerChipSelect2=bits,U32, 5688, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+ digitalPotentiometerChipSelect3=bits,U32, 5692, [0:6], "GPIOA_0", "GPIOA_1", "GPIOA_2", "GPIOA_3", "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7", "GPIOA_8", "GPIOA_9", "GPIOA_10", "GPIOA_11", "GPIOA_12", "GPIOA_13", "GPIOA_14", "GPIOA_15", "GPIOB_0", "GPIOB_1", "GPIOB_2", "GPIOB_3", "GPIOB_4", "GPIOB_5", "GPIOB_6", "GPIOB_7", "GPIOB_8", "GPIOB_9", "GPIOB_10", "GPIOB_11", "GPIOB_12", "GPIOB_13", "GPIOB_14", "GPIOB_15", "GPIOC_0", "GPIOC_1", "GPIOC_2", "GPIOC_3", "GPIOC_4", "GPIOC_5", "GPIOC_6", "GPIOC_7", "GPIOC_8", "GPIOC_9", "GPIOC_10", "GPIOC_11", "GPIOC_12", "GPIOC_13", "GPIOC_14", "GPIOC_15", "GPIOD_0", "GPIOD_1", "GPIOD_2", "GPIOD_3", "GPIOD_4", "GPIOD_5", "GPIOD_6", "GPIOD_7", "GPIOD_8", "GPIOD_9", "GPIOD_10", "GPIOD_11", "GPIOD_12", "GPIOD_13", "GPIOD_14", "GPIOD_15", "GPIOE_0", "GPIOE_1", "GPIOE_2", "GPIOE_3", "GPIOE_4", "GPIOE_5", "GPIOE_6", "GPIOE_7", "GPIOE_8", "GPIOE_9", "GPIOE_10", "GPIOE_11", "GPIOE_12", "GPIOE_13", "GPIOE_14", "GPIOE_15", "NONE", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
+
adcModePA0 = bits, U32, 5696, [0:1], "Off", "Slow", "Fast", "INVALID"
adcModePA1 = bits, U32, 5700, [0:1], "Off", "Slow", "Fast", "INVALID"
adcModePA2 = bits, U32, 5704, [0:1], "Off", "Slow", "Fast", "INVALID"
@@ -409,12 +430,14 @@ enable2ndByteCanID = false
menu = "&Engine"
subMenu = engineChars, "&Engine Characteristics"
+ subMenu = lcdScreen, "LCD screen"
subMenu = boardInputs, "&Board inputs"
subMenu = boardInputMode, "Board inputs mode"
subMenu = boardOutputs1, "Board outputs #1"
subMenu = boardOutputs2, "Board outputs #2"
subMenu = injChars, "&injector Characteristicks"
subMenu = injectorsDeadTime, "&Injectors Dead Time"
+ subMenu = ecuStimulator, "ECU stimulator"
menu = "&Sensors"
subMenu = clt_thermistor, "&CLT Thermistor"
subMenu = iat_thermistor, "&IAT Thermistor"
@@ -471,9 +494,17 @@ enable2ndByteCanID = false
field = "Ignition Mode", IgnitionMode
field = "Firing Order", FiringOrder
field = "rpm Multiplier", rpmMultiplier
- field = "display Mode", displayMode
field = "log format", logFormat
+ dialog = lcdScreen, "LCD screen"
+ field = "display Mode", displayMode
+ field = "RS pin", HD44780_rs
+ field = "E pin", HD44780_e
+ field = "D4 pin", HD44780_db4
+ field = "D5 pin", HD44780_db5
+ field = "D6 pin", HD44780_db6
+ field = "D7 pin", HD44780_db7
+
dialog = boardInputs, "Board inputs"
field = "Analog Input Divider Coefficient", analogInputDividerCoefficient
field = "Battery Input Divider Coefficient", VBattDividerCoefficient
@@ -503,6 +534,12 @@ enable2ndByteCanID = false
field = "ADC on PC4", adcModePC4
field = "ADC on PC5", adcModePC5
+ dialog = ecuStimulator, "ECU stimulator"
+ field = "digipot spi", digitalPotentiometerSpiDevice
+ field = "digipot CS #0", digitalPotentiometerChipSelect0
+ field = "digipot CS #1", digitalPotentiometerChipSelect1
+ field = "digipot CS #2", digitalPotentiometerChipSelect2
+ field = "digipot CS #3", digitalPotentiometerChipSelect3
dialog = boardOutputs1, "Board properties #1"
field = "Injection Pin 1", injectionPin1
@@ -585,13 +622,13 @@ enable2ndByteCanID = false
dialog = triggerConfiguration, "Trigger configuration"
field = "trigger type", trigger_type
- field = "Synchronization Needed?", isSynchronizationNeeded
- field = "total Tooth Count", totalToothCount
- field = "skipped Tooth Count", skippedToothCount
- field = "sync Ratio From", syncRatioFrom
- field = "sync Ratio To", syncRatioTo
- field = "use Rise Edge?", useRiseEdge
- field = "needed Second Trigger Input?", needSecondTriggerInput
+ field = "Synchronization Needed?", isSynchronizationNeeded, {trigger_type == 0}
+ field = "total Tooth Count", totalToothCount, {trigger_type == 0}
+ field = "skipped Tooth Count", skippedToothCount, {trigger_type == 0}
+ field = "sync Ratio From", syncRatioFrom, {trigger_type == 0}
+ field = "sync Ratio To", syncRatioTo, {trigger_type == 0}
+ field = "use Rise Edge?", useRiseEdge, {trigger_type == 0}
+ field = "needed Second Trigger Input?", needSecondTriggerInput, {trigger_type == 0}
dialog = crankingFuel, "Cranking Fuel"
diff --git a/firmware/util/efilib.h b/firmware/util/efilib.h
index d5df350370..c3462f9f7a 100644
--- a/firmware/util/efilib.h
+++ b/firmware/util/efilib.h
@@ -11,7 +11,11 @@
#include "stdint.h"
// number of milliseconds in one period of given frequency (per second)
-#define frequency2period(freq) (((float)1000) / (freq))
+#define frequency2periodMs(freq) ((1000.0f) / (freq))
+
+// number of microseconds in one period of given frequency (per second)
+#define frequency2periodUs(freq) ((1000000.0f) / (freq))
+
#ifndef FALSE
#define FALSE 0