diff --git a/firmware/.cproject b/firmware/.cproject
index dce069900e..86ebb61d2e 100644
--- a/firmware/.cproject
+++ b/firmware/.cproject
@@ -50,6 +50,7 @@
+
@@ -59,7 +60,10 @@
+
+
+
@@ -67,14 +71,11 @@
-
-
-
+
+
-
-
@@ -110,6 +111,7 @@
+
@@ -119,7 +121,10 @@
+
+
+
@@ -127,17 +132,14 @@
-
-
-
-
-
-
+
+
+
-
+
@@ -188,13 +190,14 @@
-
+
-
+
+
diff --git a/firmware/Makefile b/firmware/Makefile
index 3361d87d85..ad6e471136 100644
--- a/firmware/Makefile
+++ b/firmware/Makefile
@@ -18,7 +18,7 @@ endif
# C++ specific options here (added to USE_OPT).
ifeq ($(USE_CPPOPT),)
- USE_CPPOPT = -std=c++11 -fno-rtti -fno-exceptions -fno-use-cxa-atexit
+ USE_CPPOPT = -std=c++11 -fno-rtti -fno-exceptions -fno-use-cxa-atexit -Werror=write-strings
endif
# Enable this if you want the linker to remove unused code and data
@@ -196,8 +196,9 @@ INCDIR = $(PORTINC) $(KERNINC) $(TESTINC) \
hw_layer/serial_over_usb \
hw_layer/algo \
hw_layer/lcd \
- emulation/hw_layer \
emulation \
+ emulation/hw_layer \
+ emulation/test \
controllers \
controllers/sensors \
controllers/system \
@@ -292,7 +293,7 @@ ULIBS = -lm
##############################################################################
ifeq ($(USE_FPU),yes)
- USE_OPT += -mfloat-abi=softfp -mfpu=fpv4-sp-d16 -fsingle-precision-constant
+ USE_OPT += -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fsingle-precision-constant
DDEFS += -DCORTEX_USE_FPU=TRUE
else
DDEFS += -DCORTEX_USE_FPU=FALSE
diff --git a/firmware/chibios/os/hal/platforms/STM32/USARTv1/serial_lld.c b/firmware/chibios/os/hal/platforms/STM32/USARTv1/serial_lld.c
index eb344ebc0f..086760fe6d 100644
--- a/firmware/chibios/os/hal/platforms/STM32/USARTv1/serial_lld.c
+++ b/firmware/chibios/os/hal/platforms/STM32/USARTv1/serial_lld.c
@@ -192,10 +192,11 @@ static void serve_interrupt(SerialDriver *sdp) {
/* Physical transmission end.*/
if (sr & USART_SR_TC) {
chSysLockFromIsr();
- chnAddFlagsI(sdp, CHN_TRANSMISSION_END);
- chSysUnlockFromIsr();
- u->CR1 = cr1 & ~(USART_CR1_TXEIE | USART_CR1_TCIE);
+ if (chOQIsEmptyI(&sdp->oqueue))
+ chnAddFlagsI(sdp, CHN_TRANSMISSION_END);
+ u->CR1 = cr1 & ~USART_CR1_TCIE;
u->SR = ~USART_SR_TC;
+ chSysUnlockFromIsr();
}
}
diff --git a/firmware/chibios/os/ports/GCC/ARMCMx/chcore_v7m.c b/firmware/chibios/os/ports/GCC/ARMCMx/chcore_v7m.c
index 307ef8ce94..98d2ad5cd3 100644
--- a/firmware/chibios/os/ports/GCC/ARMCMx/chcore_v7m.c
+++ b/firmware/chibios/os/ports/GCC/ARMCMx/chcore_v7m.c
@@ -35,6 +35,28 @@
#include "ch.h"
+extern stkalign_t __main_stack_base__;
+
+int getRemainingStack(Thread *otp) {
+#if CH_DBG_ENABLE_STACK_CHECK
+ register struct intctx *r13 asm ("r13");
+ otp->activeStack = r13;
+
+ int rs;
+ if (dbg_isr_cnt > 0) {
+ // ISR context
+ rs = (stkalign_t *) (r13 - 1) - &__main_stack_base__;
+ } else {
+
+ rs = (stkalign_t *) (r13 - 1) - otp->p_stklimit;
+ }
+ otp->remainingStack = rs;
+ return rs;
+#else
+ return 99999;
+#endif /* CH_DBG_ENABLE_STACK_CHECK */
+}
+
/*===========================================================================*/
/* Port interrupt handlers. */
/*===========================================================================*/
diff --git a/firmware/chibios/os/ports/GCC/ARMCMx/chcore_v7m.h b/firmware/chibios/os/ports/GCC/ARMCMx/chcore_v7m.h
index 80d0c7ab15..81c1e19d5f 100644
--- a/firmware/chibios/os/ports/GCC/ARMCMx/chcore_v7m.h
+++ b/firmware/chibios/os/ports/GCC/ARMCMx/chcore_v7m.h
@@ -490,6 +490,8 @@ struct context {
void chDbgStackOverflowPanic(Thread *otp);
+int getRemainingStack(Thread *otp);
+
/**
* @brief Performs a context switch between two threads.
* @details This is the most critical code in any port, this function
@@ -504,8 +506,7 @@ void chDbgStackOverflowPanic(Thread *otp);
#define port_switch(ntp, otp) _port_switch(ntp, otp)
#else
#define port_switch(ntp, otp) { \
- register struct intctx *r13 asm ("r13"); \
- if ((stkalign_t *)(r13 - 1) < otp->p_stklimit) \
+ if (getRemainingStack(otp) < 0) \
chDbgStackOverflowPanic(otp); \
_port_switch(ntp, otp); \
}
diff --git a/firmware/chibios/os/ports/IAR/ARMCMx/chcore_v7m.c b/firmware/chibios/os/ports/IAR/ARMCMx/chcore_v7m.c
index 33c78403b4..3518fecd37 100644
--- a/firmware/chibios/os/ports/IAR/ARMCMx/chcore_v7m.c
+++ b/firmware/chibios/os/ports/IAR/ARMCMx/chcore_v7m.c
@@ -39,6 +39,21 @@
/* Port interrupt handlers. */
/*===========================================================================*/
+int getRemainingStack(Thread *otp) {
+#if CH_DBG_ENABLE_STACK_CHECK || defined(__DOXYGEN__)
+ int remainingStack;
+ if (dbg_isr_cnt > 0) {
+ remainingStack = 999; // todo
+ } else {
+ remainingStack = (stkalign_t *)(__get_SP() - sizeof(struct intctx)) - otp->p_stklimit;
+ }
+ otp->remainingStack = remainingStack;
+ return remainingStack;
+#else
+ return 999999;
+#endif
+}
+
/**
* @brief System Timer vector.
* @details This interrupt is used as system tick.
diff --git a/firmware/chibios/os/ports/IAR/ARMCMx/chcore_v7m.h b/firmware/chibios/os/ports/IAR/ARMCMx/chcore_v7m.h
index 3e49b4887b..8e9a38f76b 100644
--- a/firmware/chibios/os/ports/IAR/ARMCMx/chcore_v7m.h
+++ b/firmware/chibios/os/ports/IAR/ARMCMx/chcore_v7m.h
@@ -470,6 +470,8 @@ struct context {
#define port_wait_for_interrupt()
#endif
+int getRemainingStack(Thread *otp);
+
/**
* @brief Performs a context switch between two threads.
* @details This is the most critical code in any port, this function
@@ -484,7 +486,7 @@ struct context {
#define port_switch(ntp, otp) _port_switch(ntp, otp)
#else
#define port_switch(ntp, otp) { \
- if ((stkalign_t *)(__get_SP() - sizeof(struct intctx)) < otp->p_stklimit) \
+ if (getRemainingStack(otp) < 0) \
chDbgPanic("stack overflow"); \
_port_switch(ntp, otp); \
}
diff --git a/firmware/config/boards/arro_board.h b/firmware/config/boards/arro_board.h
index bfc7eb63eb..f2da18c654 100644
--- a/firmware/config/boards/arro_board.h
+++ b/firmware/config/boards/arro_board.h
@@ -205,8 +205,8 @@
#define TS_SERIAL_RX_PIN 11
#define TS_SERIAL_AF 7
-#define LED_CRANKING_STATUS_PORT GPIOD
-#define LED_CRANKING_STATUS_PIN GPIOD_LED3
+#define LED_WARNING_PORT GPIOD
+#define LED_WARNING_PIN GPIOD_LED3
#define LED_RUNNING_STATUS_PORT GPIOD
#define LED_RUNNING_STATUS_PIN GPIOD_LED4
diff --git a/firmware/config/efifeatures.h b/firmware/config/efifeatures.h
index a6a59bcad0..11cc662b57 100644
--- a/firmware/config/efifeatures.h
+++ b/firmware/config/efifeatures.h
@@ -10,15 +10,17 @@
#ifndef EFIFEATURES_H_
#define EFIFEATURES_H_
+#define EFI_USE_CCM TRUE
+
+//#define EFI_UART_ECHO_TEST_MODE TRUE
+
+#define EFI_USE_UART_FOR_CONSOLE FALSE
+
/**
* Build-in logic analyzer support. Logic analyzer viewer is one of the java console panes.
*/
#define EFI_WAVE_ANALYZER TRUE
-
-#define EFI_WAVE_CHART TRUE
-#define EFI_ANALOG_CHART TRUE
-
//#define SERIAL_SPEED (8 * 115200)
//#define SERIAL_SPEED (2 * 115200)
#define SERIAL_SPEED 115200
@@ -28,10 +30,6 @@
*/
#define EFI_TUNER_STUDIO TRUE
-#define EFI_SERIAL_OVER_USB TRUE
-#define EFI_SERIAL_OVER_UART FALSE
-//#define EFI_TUNER_STUDIO_OVER_USB TRUE
-
/**
* TunerStudio debug output
*/
@@ -82,19 +80,6 @@
#define EFI_ENGINE_EMULATOR TRUE
#define EFI_EMULATE_POSITION_SENSORS TRUE
-#if EFI_TUNER_STUDIO
-#if EFI_SERIAL_OVER_USB
-#if EFI_TUNER_STUDIO_OVER_USB
- #pragma "Cannot be EFI_SERIAL_OVER_USB and EFI_TUNER_STUDIO_OVER_USB at the same time"
-#endif
-#else
-#if EFI_TUNER_STUDIO_OVER_USB
-#else
- #pragma "Cannot be serial over USART and TUNER_STUDIO over USART at the same time"
-#endif
-#endif /* EFI_TUNER_STUDIO */
-#endif /* EFI_SERIAL_OVER_USB */
-
/**
* This macros is used to hide pieces of the code from unit tests, so it only makes sense in folders exposed to the tests project.
* This macros is NOT about taking out logging in general.
@@ -120,7 +105,18 @@
#define EFI_SUPPORT_NISSAN_PRIMERA TRUE
#define EFI_SUPPORT_1995_FORD_INLINE_6 TRUE
+#if defined __GNUC__
#define EFI_PERF_METRICS TRUE
+#define EFI_ANALOG_CHART TRUE
+#define EFI_WAVE_CHART TRUE
+#define DL_OUTPUT_BUFFER 9000
+#else
+// todo: CCM usage for IAR?
+#define EFI_PERF_METRICS FALSE
+#define EFI_ANALOG_CHART FALSE
+#define EFI_WAVE_CHART FALSE
+#define DL_OUTPUT_BUFFER 6000
+#endif
/**
* Do we need GPS logic?
diff --git a/firmware/config/engines/GY6_139QMB.cpp b/firmware/config/engines/GY6_139QMB.cpp
index 7b3bf29221..59994cbe8e 100644
--- a/firmware/config/engines/GY6_139QMB.cpp
+++ b/firmware/config/engines/GY6_139QMB.cpp
@@ -1,5 +1,5 @@
/**
- * @file GY6_139QMB.c
+ * @file GY6_139QMB.cpp
* @brief 139qmb default engine configuration
*
* @date Feb 13, 2014
@@ -7,6 +7,7 @@
* @author Andrey Belomutskiy, (c) 2012-2014
*/
+#include "main.h"
#include "GY6_139QMB.h"
void setGy6139qmbDefaultEngineConfiguration(engine_configuration_s *engineConfiguration) {
@@ -17,7 +18,7 @@ void setGy6139qmbDefaultEngineConfiguration(engine_configuration_s *engineConfig
engineConfiguration->globalTriggerAngleOffset = 15;
engineConfiguration->analogChartMode = AC_MAP;
engineConfiguration->cylindersCount = 1;
- engineConfiguration->rpmMultiplier = 1;
+ setOperationMode(engineConfiguration, FOUR_STROKE_CRANK_SENSOR);
engineConfiguration->firingOrder = FO_ONE_CYLINDER;
diff --git a/firmware/config/engines/audi_aan.cpp b/firmware/config/engines/audi_aan.cpp
index dec37448bd..1b299d356a 100644
--- a/firmware/config/engines/audi_aan.cpp
+++ b/firmware/config/engines/audi_aan.cpp
@@ -1,5 +1,5 @@
/**
- * @file audi_aan.c
+ * @file audi_aan.cpp
* @brief Audo AAN default engine configuration
*
* @date Nov 24, 2013
diff --git a/firmware/config/engines/dodge_neon.cpp b/firmware/config/engines/dodge_neon.cpp
index e44202ca7d..68e2290391 100644
--- a/firmware/config/engines/dodge_neon.cpp
+++ b/firmware/config/engines/dodge_neon.cpp
@@ -1,5 +1,5 @@
/**
- * @file dodge_neon.c
+ * @file dodge_neon.cpp
*
* DODGE_NEON_1995 = 2
*
@@ -18,6 +18,7 @@
void setDodgeNeonEngineConfiguration(engine_configuration_s *engineConfiguration,
board_configuration_s *boardConfiguration) {
+
engineConfiguration->triggerConfig.triggerType = TT_DODGE_NEON;
engineConfiguration->engineLoadMode = LM_TPS;
@@ -39,10 +40,8 @@ void setDodgeNeonEngineConfiguration(engine_configuration_s *engineConfiguration
// set_whole_fuel_map 3
setWholeFuelMap(engineConfiguration, 3);
- engineConfiguration->triggerConfig.syncRatioFrom = 0.72 * 0.8;
- engineConfiguration->triggerConfig.syncRatioTo = 0.72 * 1.3;
+ setTriggerSynchronizationGap(engineConfiguration, 0.72);
- engineConfiguration->triggerConfig.isSynchronizationNeeded = TRUE;
engineConfiguration->triggerConfig.useRiseEdge = FALSE;
engineConfiguration->needSecondTriggerInput = TRUE;
diff --git a/firmware/config/engines/engines.h b/firmware/config/engines/engines.h
deleted file mode 100644
index 37918e86a7..0000000000
--- a/firmware/config/engines/engines.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * @file engines.h
- *
- * @date Aug 30, 2013
- * @author Andrey Belomutskiy, (c) 2012-2014
- */
-
-#ifndef ENGINE_H_
-#define ENGINE_H_
-
-#include "boards.h"
-#include "efifeatures.h"
-#include "rusefi_enums.h"
-
-#endif /* ENGINE_H_ */
diff --git a/firmware/config/engines/engines.mk b/firmware/config/engines/engines.mk
index 06abcf8e06..737ca5ae24 100644
--- a/firmware/config/engines/engines.mk
+++ b/firmware/config/engines/engines.mk
@@ -14,5 +14,6 @@ ENGINES_SRC_CPP = $(PROJECT_DIR)/config/engines/ford_aspire.cpp \
$(PROJECT_DIR)/config/engines/honda_accord.cpp \
$(PROJECT_DIR)/config/engines/snow_blower.cpp \
$(PROJECT_DIR)/config/engines/GY6_139QMB.cpp \
+ $(PROJECT_DIR)/config/engines/rover_v8.cpp \
$(PROJECT_DIR)/config/engines/mazda_323.cpp \
$(PROJECT_DIR)/config/engines/saturn_ion.cpp
\ No newline at end of file
diff --git a/firmware/config/engines/ford_1995_inline_6.cpp b/firmware/config/engines/ford_1995_inline_6.cpp
index 5d85eb9a0f..18b670125c 100644
--- a/firmware/config/engines/ford_1995_inline_6.cpp
+++ b/firmware/config/engines/ford_1995_inline_6.cpp
@@ -1,5 +1,5 @@
/**
- * @file ford_1995_inline_6.c
+ * @file ford_1995_inline_6.cpp
* @brief Default engine configuration for a 1995 Ford inline 6 engine
*
* http://rusefi.com/forum/viewtopic.php?f=3&t=469
@@ -23,10 +23,7 @@
void setFordInline6(engine_configuration_s *engineConfiguration, board_configuration_s *boardConfiguration) {
engineConfiguration->cylindersCount = 6;
- /**
- * 0.5 means primary position sensor is on a camshaft
- */
- engineConfiguration->rpmMultiplier = 0.5;
+ setOperationMode(engineConfiguration, FOUR_STROKE_CAM_SENSOR);
engineConfiguration->ignitionMode = IM_ONE_COIL;
engineConfiguration->firingOrder = FO_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4;
@@ -41,6 +38,7 @@ void setFordInline6(engine_configuration_s *engineConfiguration, board_configura
/**
* We treat the trigger as 6/0 toothed wheel
*/
+ engineConfiguration->triggerConfig.triggerType = TT_TOOTHED_WHEEL;
engineConfiguration->triggerConfig.totalToothCount = 6;
engineConfiguration->triggerConfig.skippedToothCount = 0;
engineConfiguration->triggerConfig.isSynchronizationNeeded = FALSE;
diff --git a/firmware/config/engines/ford_aspire.cpp b/firmware/config/engines/ford_aspire.cpp
index e92079c238..24459b1f4d 100644
--- a/firmware/config/engines/ford_aspire.cpp
+++ b/firmware/config/engines/ford_aspire.cpp
@@ -13,7 +13,6 @@
#include "ford_aspire.h"
#include "allsensors.h"
-#include "engines.h"
#include "engine_math.h"
#include "advance_map.h"
#include "engine_configuration.h"
@@ -103,6 +102,10 @@ void setFordAspireEngineConfiguration(engine_configuration_s *engineConfiguratio
// engineConfiguration->ignitionPinMode = OM_INVERTED;
engineConfiguration->cylindersCount = 4;
+ engineConfiguration->displacement = 1.3;
+ // Denso 195500-2110
+ engineConfiguration->injectorFlow = 119.8;
+
engineConfiguration->firingOrder = FO_1_THEN_3_THEN_4_THEN2;
engineConfiguration->globalTriggerAngleOffset = 175;
engineConfiguration->ignitionOffset = 98 - 11;
diff --git a/firmware/config/engines/ford_fiesta.cpp b/firmware/config/engines/ford_fiesta.cpp
index 3e089a98ce..ef6bc214e8 100644
--- a/firmware/config/engines/ford_fiesta.cpp
+++ b/firmware/config/engines/ford_fiesta.cpp
@@ -1,5 +1,5 @@
/**
- * @file ford_fiesta.c
+ * @file ford_fiesta.cpp
* @brief European 1990 Ford Fiesta
*
* FORD_FIESTA = 4
@@ -18,8 +18,7 @@
void setFordFiestaDefaultEngineConfiguration(engine_configuration_s *engineConfiguration) {
engineConfiguration->rpmHardLimit = 7000;
- // only crankshaft sensor so far
- engineConfiguration->rpmMultiplier = 1;
+ setOperationMode(engineConfiguration, FOUR_STROKE_CRANK_SENSOR);
engineConfiguration->triggerConfig.totalToothCount = 36;
engineConfiguration->triggerConfig.skippedToothCount = 1;
diff --git a/firmware/config/engines/honda_accord.cpp b/firmware/config/engines/honda_accord.cpp
index bc5198458f..cd38d3e251 100644
--- a/firmware/config/engines/honda_accord.cpp
+++ b/firmware/config/engines/honda_accord.cpp
@@ -1,5 +1,5 @@
/**
- * @file honda_accord.c
+ * @file honda_accord.cpp
*
* @date Jan 12, 2014
* @author Andrey Belomutskiy, (c) 2012-2014
diff --git a/firmware/config/engines/mazda_323.cpp b/firmware/config/engines/mazda_323.cpp
index 568a167f1b..be62f2406b 100644
--- a/firmware/config/engines/mazda_323.cpp
+++ b/firmware/config/engines/mazda_323.cpp
@@ -1,5 +1,5 @@
/**
- * @file mazda_323.c
+ * @file mazda_323.cpp
*
* @date Mar 8, 2014
* @author Andrey Belomutskiy, (c) 2012-2014
@@ -8,7 +8,8 @@
#include "mazda_323.h"
void setMazda323EngineConfiguration(engine_configuration_s *engineConfiguration) {
- engineConfiguration->cylindersCount = 6;
+ engineConfiguration->cylindersCount = 4;
+ engineConfiguration->displacement = 1.6;
engineConfiguration->ignitionMode = IM_ONE_COIL;
diff --git a/firmware/config/engines/mazda_miata_nb.cpp b/firmware/config/engines/mazda_miata_nb.cpp
index a6d509342f..fc130e9bd3 100644
--- a/firmware/config/engines/mazda_miata_nb.cpp
+++ b/firmware/config/engines/mazda_miata_nb.cpp
@@ -1,5 +1,5 @@
/**
- * @file mazda_miata_nb.c
+ * @file mazda_miata_nb.cpp
*
* MAZDA_MIATA_NB = 9
*
@@ -16,10 +16,8 @@ void setMazdaMiataNbEngineConfiguration(engine_configuration_s *engineConfigurat
engineConfiguration->rpmHardLimit = 3000; // yes, 3k. let's play it safe for now
engineConfiguration->triggerConfig.triggerType = TT_MAZDA_MIATA_NB;
- engineConfiguration->triggerConfig.isSynchronizationNeeded = TRUE;
- engineConfiguration->triggerConfig.syncRatioFrom = 0.11 * 0.7;
- engineConfiguration->triggerConfig.syncRatioTo = 0.11 * 1.3;
+ setTriggerSynchronizationGap(engineConfiguration, 0.11);
engineConfiguration->triggerConfig.useRiseEdge = FALSE;
engineConfiguration->globalTriggerAngleOffset = 276;
diff --git a/firmware/config/engines/nissan_primera.cpp b/firmware/config/engines/nissan_primera.cpp
index 36937c432e..0f8ca08670 100644
--- a/firmware/config/engines/nissan_primera.cpp
+++ b/firmware/config/engines/nissan_primera.cpp
@@ -1,5 +1,7 @@
/**
- * @file nissan_primera.c
+ * @file nissan_primera.cpp
+ *
+ * engine_type 5
*
* @date Oct 14, 2013
* @author Andrey Belomutskiy, (c) 2012-2014
diff --git a/firmware/config/engines/rover_v8.cpp b/firmware/config/engines/rover_v8.cpp
new file mode 100644
index 0000000000..1e33f1ec27
--- /dev/null
+++ b/firmware/config/engines/rover_v8.cpp
@@ -0,0 +1,70 @@
+/**
+ * @file rover_v8.cpp
+ *
+ * V8, firing order 18436572
+ *
+ * ROVER_V8 = 10
+ *
+ * @date Jun 27, 2014
+ * @author Andrey Belomutskiy, (c) 2012-2014
+ */
+
+#include "main.h"
+#include "rover_v8.h"
+
+void setRoverv8(engine_configuration_s *engineConfiguration,
+ board_configuration_s *boardConfiguration) {
+
+ setOperationMode(engineConfiguration, FOUR_STROKE_CRANK_SENSOR);
+ engineConfiguration->triggerConfig.totalToothCount = 36;
+ engineConfiguration->triggerConfig.skippedToothCount = 1;
+
+ // todo: displacement? 4 liters?
+ engineConfiguration->displacement = 4;
+ engineConfiguration->cylindersCount = 8;
+ engineConfiguration->firingOrder = FO_1_8_4_3_6_5_7_2;
+
+ // set_rpm_hard_limit 4000
+ engineConfiguration->rpmHardLimit = 4000; // yes, 4k. let's play it safe for now
+ // set_cranking_rpm 550
+ engineConfiguration->crankingSettings.crankingRpm = 550;
+
+ // set_whole_fuel_map 3
+ setWholeFuelMap(engineConfiguration, 3);
+
+
+ // set_cranking_injection_mode 0
+ engineConfiguration->crankingInjectionMode = IM_SEQUENTIAL;
+ // set_injection_mode 1
+ engineConfiguration->injectionMode = IM_SEQUENTIAL;
+
+ // set_ignition_mode 2
+ engineConfiguration->ignitionMode = IM_WASTED_SPARK;
+
+
+ // Frankenstein: low side - inj #1: PC14
+ // Frankenstein: low side - inj #2: PC15
+ // Frankenstein: low side - inj #3: PE6
+ // Frankenstein: low side - inj #4: PC13
+ // Frankenstein: low side - inj #5: PE4
+ // Frankenstein: low side - inj #6: PE5
+ // Frankenstein: low side - inj #7: PE2
+ // Frankenstein: low side - inj #8: PE3
+ // Frankenstein: low side - inj #9: PE0
+ // Frankenstein: low side - inj #10: PE1
+ // Frankenstein: low side - inj #11: PB8
+ // Frankenstein: low side - inj #12: PB9
+
+ boardConfiguration->injectionPins[0] = GPIOB_9; // Frankenstein: low side - inj #12
+ boardConfiguration->injectionPins[1] = GPIOB_8; // Frankenstein: low side - inj #11
+ boardConfiguration->injectionPins[2] = GPIOE_3; // Frankenstein: low side - inj #8
+ boardConfiguration->injectionPins[3] = GPIOE_5; // Frankenstein: low side - inj #6
+
+ boardConfiguration->fuelPumpPin = GPIOC_13; // Frankenstein: low side - inj #4
+ boardConfiguration->fuelPumpPinMode = OM_DEFAULT;
+
+ // set_injection_pin_mode 0
+ boardConfiguration->injectionPinMode = OM_DEFAULT;
+
+
+}
diff --git a/firmware/config/engines/rover_v8.h b/firmware/config/engines/rover_v8.h
new file mode 100644
index 0000000000..30a3f6f319
--- /dev/null
+++ b/firmware/config/engines/rover_v8.h
@@ -0,0 +1,15 @@
+/**
+ * @file rover_v8.h
+ *
+ * @date Jun 27, 2014
+ * @author Andrey Belomutskiy, (c) 2012-2014
+ */
+#ifndef ROVER_V8_H_
+#define ROVER_V8_H_
+
+#include "engine_configuration.h"
+
+void setRoverv8(engine_configuration_s *engineConfiguration,
+ board_configuration_s *boardConfiguration);
+
+#endif /* ROVER_V8_H_ */
diff --git a/firmware/config/engines/snow_blower.cpp b/firmware/config/engines/snow_blower.cpp
index c09ccfc50c..71ca161a6a 100644
--- a/firmware/config/engines/snow_blower.cpp
+++ b/firmware/config/engines/snow_blower.cpp
@@ -1,5 +1,5 @@
/**
- * @file snow_blower.c
+ * @file snow_blower.cpp
* @brief Default configuration of a single-cylinder engine
*
* @date Sep 9, 2013
@@ -7,8 +7,3 @@
*/
#include "main.h"
-
-#if EFI_ENGINE_SNOW_BLOWER
-
-
-#endif
diff --git a/firmware/config/system/STM32F407xG_CCM.ld b/firmware/config/system/STM32F407xG_CCM.ld
index 9ce0648c60..2e17f8f38f 100644
--- a/firmware/config/system/STM32F407xG_CCM.ld
+++ b/firmware/config/system/STM32F407xG_CCM.ld
@@ -28,8 +28,8 @@
/*
* ST32F407xG memory setup.
*/
-__main_stack_size__ = 0x0400;
-__process_stack_size__ = 0x0400;
+__main_stack_size__ = 0x0600;
+__process_stack_size__ = 0x0600;
MEMORY
{
@@ -127,7 +127,7 @@ SECTIONS
__main_thread_stack_end__ = .;
} > ram
- .ccm :
+ .ccm (NOLOAD) :
{
PROVIDE(_cmm_start = .);
. = ALIGN(4);
diff --git a/firmware/config/system/chconf.h b/firmware/config/system/chconf.h
index f649138817..3c514ea278 100644
--- a/firmware/config/system/chconf.h
+++ b/firmware/config/system/chconf.h
@@ -45,11 +45,11 @@
}
-#define PORT_IDLE_THREAD_STACK_SIZE 256
+#define PORT_IDLE_THREAD_STACK_SIZE 1024
-#define PORT_INT_REQUIRED_STACK 128
+#define PORT_INT_REQUIRED_STACK 768
-#define CHPRINTF_USE_FLOAT TRUE
+#define CHPRINTF_USE_FLOAT TRUE
/**
* number of ticks per second
@@ -480,6 +480,8 @@
*/
#if !defined(THREAD_EXT_FIELDS) || defined(__DOXYGEN__)
#define THREAD_EXT_FIELDS \
+ void *activeStack; \
+ int remainingStack; \
/* Add threads custom fields here.*/
#endif
diff --git a/firmware/config/system/mcuconf.h b/firmware/config/system/mcuconf.h
index e7387825f4..5335d7a712 100644
--- a/firmware/config/system/mcuconf.h
+++ b/firmware/config/system/mcuconf.h
@@ -30,7 +30,9 @@
#define STM32F4xx_MCUCONF
-#include "engines.h"
+#include "boards.h"
+#include "efifeatures.h"
+#include "rusefi_enums.h"
/*
* HAL driver system settings.
diff --git a/firmware/console/console_io.c b/firmware/console/console_io.c
index b02900e81a..445d4f6871 100644
--- a/firmware/console/console_io.c
+++ b/firmware/console/console_io.c
@@ -22,12 +22,19 @@
#include "console_io.h"
#if EFI_PROD_CODE
+extern SerialUSBDriver SDU1;
+#include "usbcfg.h"
#include "usbconsole.h"
#endif
#include "rfiutil.h"
+int lastWriteSize;
+int lastWriteActual;
+
static bool_t isSerialConsoleStarted = FALSE;
+static EventListener consoleEventListener;
+
/**
* @brief Reads a whole line from the input channel.
*
@@ -38,7 +45,7 @@ static bool_t isSerialConsoleStarted = FALSE;
* @retval TRUE the channel was reset or CTRL-D pressed.
* @retval FALSE operation successful.
*/
-static bool_t getConsoleLine(BaseSequentialStream *chp, char *line, unsigned size) {
+static bool getConsoleLine(BaseSequentialStream *chp, char *line, unsigned size) {
char *p = line;
while (TRUE) {
@@ -49,6 +56,30 @@ static bool_t getConsoleLine(BaseSequentialStream *chp, char *line, unsigned siz
}
short c = (short) chSequentialStreamGet(chp);
+
+ if (isSerialOverUart()) {
+ uint32_t flags;
+ chSysLock()
+ ;
+
+ flags = chEvtGetAndClearFlagsI(&consoleEventListener);
+ chSysUnlock()
+ ;
+
+ if (flags & SD_OVERRUN_ERROR) {
+// firmwareError("serial overrun");
+ }
+
+ }
+
+#if EFI_UART_ECHO_TEST_MODE
+ /**
+ * That's test code - let's test connectivity
+ */
+ consolePutChar((uint8_t) c);
+ continue;
+#endif
+
if (c < 0)
return TRUE;
if (c == 4) {
@@ -84,13 +115,19 @@ static char consoleInput[] = "
void (*console_line_callback)(char *);
-static WORKING_AREA(consoleThreadStack, 2 * UTILITY_THREAD_STACK_SIZE);
+static bool is_serial_over_uart;
+
+bool isSerialOverUart(void) {
+ return is_serial_over_uart;
+}
+
+static THD_WORKING_AREA(consoleThreadStack, 2 * UTILITY_THREAD_STACK_SIZE);
static msg_t consoleThreadThreadEntryPoint(void *arg) {
(void) arg;
chRegSetThreadName("console thread");
while (TRUE) {
- bool_t end = getConsoleLine((BaseSequentialStream *) CONSOLE_CHANNEL, consoleInput, sizeof(consoleInput));
+ bool end = getConsoleLine((BaseSequentialStream*) getConsoleChannel(), consoleInput, sizeof(consoleInput));
if (end) {
// firmware simulator is the only case when this happens
continue;
@@ -103,47 +140,68 @@ static msg_t consoleThreadThreadEntryPoint(void *arg) {
#endif
}
-#if EFI_SERIAL_OVER_USB
-#else
-#if EFI_SERIAL_OVER_UART
-static SerialConfig serialConfig = {SERIAL_SPEED, 0, USART_CR2_STOP1_BITS | USART_CR2_LINEN, 0};
-#endif /* EFI_SERIAL_OVER_UART */
-#endif /* EFI_SERIAL_OVER_USB */
+#if EFI_PROD_CODE
-#if ! EFI_SERIAL_OVER_USB && ! EFI_SIMULATOR
-int isConsoleReady(void) {
- return isSerialConsoleStarted;
+static SerialConfig serialConfig = { SERIAL_SPEED, 0, USART_CR2_STOP1_BITS | USART_CR2_LINEN, 0 };
+
+SerialDriver * getConsoleChannel(void) {
+ if (isSerialOverUart()) {
+ return (SerialDriver *) EFI_CONSOLE_UART_DEVICE;
+ } else {
+ return (SerialDriver *) &SDU1;
+ }
}
-#endif
+
+int isConsoleReady(void) {
+ if (isSerialOverUart()) {
+ return isSerialConsoleStarted;
+ } else {
+ return is_usb_serial_ready();
+ }
+}
+
+#endif /* EFI_PROD_CODE */
void consolePutChar(int x) {
- chSequentialStreamPut(CONSOLE_CHANNEL, (uint8_t )(x));
+ chSequentialStreamPut(getConsoleChannel(), (uint8_t )(x));
}
+// 10 seconds
+#define CONSOLE_WRITE_TIMEOUT 10000
+
void consoleOutputBuffer(const int8_t *buf, int size) {
- chSequentialStreamWrite(CONSOLE_CHANNEL, buf, size);
+ lastWriteSize = size;
+#if !EFI_UART_ECHO_TEST_MODE
+ lastWriteActual = chnWriteTimeout(getConsoleChannel(), buf, size, CONSOLE_WRITE_TIMEOUT);
+// if (r != size)
+// firmwareError("Partial console write");
+#endif /* EFI_UART_ECHO_TEST_MODE */
}
void startConsole(void (*console_line_callback_p)(char *)) {
console_line_callback = console_line_callback_p;
-#if EFI_SERIAL_OVER_USB
- usb_serial_start();
-#else
-#if EFI_SERIAL_OVER_UART
- /*
- * Activates the serial using the driver default configuration (that's 38400)
- * it is important to set 'NONE' as flow control! in terminal application on the PC
- */
- sdStart(CONSOLE_CHANNEL, &serialConfig);
+#if EFI_PROD_CODE
+ is_serial_over_uart = palReadPad(GPIOA, GPIOA_BUTTON) != EFI_USE_UART_FOR_CONSOLE;
- // cannot use pin repository here because pin repository prints to console
- palSetPadMode(EFI_CONSOLE_RX_PORT, EFI_CONSOLE_RX_PIN, PAL_MODE_ALTERNATE(EFI_CONSOLE_AF));
- palSetPadMode(EFI_CONSOLE_TX_PORT, EFI_CONSOLE_TX_PIN, PAL_MODE_ALTERNATE(EFI_CONSOLE_AF));
+ if (isSerialOverUart()) {
+ /*
+ * Activates the serial using the driver default configuration (that's 38400)
+ * it is important to set 'NONE' as flow control! in terminal application on the PC
+ */
+ sdStart(EFI_CONSOLE_UART_DEVICE, &serialConfig);
- isSerialConsoleStarted = TRUE;
-#endif /* EFI_SERIAL_OVER_UART */
-#endif /* EFI_SERIAL_OVER_USB */
+// cannot use pin repository here because pin repository prints to console
+ palSetPadMode(EFI_CONSOLE_RX_PORT, EFI_CONSOLE_RX_PIN, PAL_MODE_ALTERNATE(EFI_CONSOLE_AF));
+ palSetPadMode(EFI_CONSOLE_TX_PORT, EFI_CONSOLE_TX_PIN, PAL_MODE_ALTERNATE(EFI_CONSOLE_AF));
+
+ isSerialConsoleStarted = TRUE;
+
+ chEvtRegisterMask((EventSource *) chnGetEventSource(EFI_CONSOLE_UART_DEVICE), &consoleEventListener, 1);
+ } else {
+ usb_serial_start();
+ }
+#endif /* EFI_PROD_CODE */
chThdCreateStatic(consoleThreadStack, sizeof(consoleThreadStack), NORMALPRIO, consoleThreadThreadEntryPoint, NULL);
}
@@ -152,7 +210,7 @@ extern cnt_t dbg_isr_cnt;
/**
* @return TRUE if already in locked context
*/
-bool_t lockAnyContext(void) {
+bool lockAnyContext(void) {
int alreadyLocked = isLocked();
if (alreadyLocked)
return TRUE;
diff --git a/firmware/console/console_io.h b/firmware/console/console_io.h
index d7dd52355a..c36dd8395a 100644
--- a/firmware/console/console_io.h
+++ b/firmware/console/console_io.h
@@ -21,23 +21,18 @@
#include "efifeatures.h"
#include "boards.h"
-#if EFI_SERIAL_OVER_USB
- #include "usbcfg.h"
- extern SerialUSBDriver SDU1;
- #define CONSOLE_CHANNEL (&SDU1)
-#else
- #define CONSOLE_CHANNEL EFI_CONSOLE_UART_DEVICE
-#endif
-
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
+SerialDriver * getConsoleChannel(void);
+
void consolePutChar(int x);
void consoleOutputBuffer(const int8_t *buf, int size);
void startConsole(void (*console_line_callback_p)(char *));
int isConsoleReady(void);
+bool isSerialOverUart(void);
#ifdef __cplusplus
}
diff --git a/firmware/console/eficonsole.c b/firmware/console/eficonsole.c
index 627e2d0d69..a4599b00ab 100644
--- a/firmware/console/eficonsole.c
+++ b/firmware/console/eficonsole.c
@@ -46,6 +46,10 @@ static void myfatal(void) {
chDbgCheck(0, "my fatal");
}
+static void myerror(void) {
+ firmwareError("firmwareError: %d", getRusEfiVersion());
+}
+
static void sayHello(void) {
printMsg(&logger, "*** rusEFI (c) Andrey Belomutskiy, 2012-2014. All rights reserved.");
printMsg(&logger, "rusEFI v%d@%d", getRusEfiVersion(), SVN_VERSION);
@@ -69,6 +73,9 @@ static void sayHello(void) {
printMsg(&logger, "STM32_PCLK2=%d", STM32_PCLK2);
#endif
+
+ printMsg(&logger, "PORT_IDLE_THREAD_STACK_SIZE=%d", PORT_IDLE_THREAD_STACK_SIZE);
+
printMsg(&logger, "CH_DBG_ENABLE_ASSERTS=%d", CH_DBG_ENABLE_ASSERTS);
printMsg(&logger, "CH_DBG_ENABLED=%d", CH_DBG_ENABLED);
printMsg(&logger, "CH_DBG_SYSTEM_STATE_CHECK=%d", CH_DBG_SYSTEM_STATE_CHECK);
@@ -91,19 +98,8 @@ static void sayHello(void) {
printMsg(&logger, "EFI_SIGNAL_EXECUTOR_HW_TIMER=%d", EFI_SIGNAL_EXECUTOR_HW_TIMER);
#endif
-
-
-#ifdef EFI_TUNER_STUDIO_OVER_USB
- printMsg(&logger, "EFI_TUNER_STUDIO_OVER_USB=%d", EFI_TUNER_STUDIO_OVER_USB);
-#else
- printMsg(&logger, "EFI_TUNER_STUDIO_OVER_USB=%d", 0);
-#endif
-#ifdef EFI_SHAFT_POSITION_INPUT
printMsg(&logger, "EFI_SHAFT_POSITION_INPUT=%d", EFI_SHAFT_POSITION_INPUT);
-#endif
-#ifdef EFI_INTERNAL_ADC
printMsg(&logger, "EFI_INTERNAL_ADC=%d", EFI_INTERNAL_ADC);
-#endif
// printSimpleMsg(&logger, "", );
// printSimpleMsg(&logger, "", );
@@ -119,6 +115,7 @@ static void sayHello(void) {
* This methods prints all threads and their total times
*/
static void cmd_threads(void) {
+#if CH_DBG_THREADS_PROFILING || defined(__DOXYGEN__)
static const char *states[] = { THD_STATE_NAMES };
Thread *tp;
@@ -130,6 +127,7 @@ static void cmd_threads(void) {
states[tp->p_state], (uint32_t) tp->p_time, tp->p_name);
tp = chRegNextThread(tp);
} while (tp != NULL );
+#endif
}
void sendOutConfirmation(char *value, int i) {
@@ -140,12 +138,14 @@ void sendOutConfirmation(char *value, int i) {
* This methods prints the message to whatever is configured as our primary console
*/
void print(const char *format, ...) {
+#if !EFI_UART_ECHO_TEST_MODE
if (!isConsoleReady())
return;
va_list ap;
va_start(ap, format);
- chvprintf((BaseSequentialStream *)CONSOLE_CHANNEL, format, ap);
+ chvprintf((BaseSequentialStream*)getConsoleChannel(), format, ap);
va_end(ap);
+#endif /* EFI_UART_ECHO_TEST_MODE */
}
void initializeConsole() {
@@ -163,5 +163,6 @@ void initializeConsole() {
#endif
addConsoleAction("fatal", myfatal);
+ addConsoleAction("error", myerror);
addConsoleAction("threadsinfo", cmd_threads);
}
diff --git a/firmware/console/status_loop.cpp b/firmware/console/status_loop.cpp
index 2cb0cd184b..8b1aea486a 100644
--- a/firmware/console/status_loop.cpp
+++ b/firmware/console/status_loop.cpp
@@ -38,10 +38,8 @@
#include "io_pins.h"
#include "mmc_card.h"
#include "console_io.h"
-
-#define PRINT_FIRMWARE_ONCE TRUE
-
-static bool_t firmwareErrorReported = FALSE;
+#include "malfunction_central.h"
+#include "speed_density.h"
#include "advance_map.h"
#if EFI_TUNER_STUDIO
@@ -57,6 +55,7 @@ static bool_t firmwareErrorReported = FALSE;
#include "engine_configuration.h"
#include "rfiutil.h"
#include "svnversion.h"
+#include "engine.h"
#if EFI_PROD_CODE
// todo: move this logic to algo folder!
@@ -65,6 +64,8 @@ static bool_t firmwareErrorReported = FALSE;
#include "rusefi.h"
#endif
+extern Engine engine;
+
// this 'true' value is needed for simulator
static volatile int fullLog = TRUE;
int warningEnabled = TRUE;
@@ -72,6 +73,7 @@ int warningEnabled = TRUE;
extern engine_configuration_s * engineConfiguration;
extern engine_configuration2_s * engineConfiguration2;
+extern board_configuration_s *boardConfiguration;
#define FULL_LOGGING_KEY "fl"
#if EFI_PROD_CODE || EFI_SIMULATOR
@@ -97,7 +99,7 @@ static void reportSensorF(const char *caption, float value, int precision) {
#endif /* EFI_FILE_LOGGING */
}
-static void reportSensorI(char *caption, int value) {
+static void reportSensorI(const char *caption, int value) {
#if EFI_PROD_CODE || EFI_SIMULATOR
debugInt(&logger, caption, value);
#endif /* EFI_PROD_CODE || EFI_SIMULATOR */
@@ -154,6 +156,7 @@ void printSensors(void) {
}
void printState(int currentCkpEventCounter) {
+#if EFI_SHAFT_POSITION_INPUT
printSensors();
int rpm = getRpm();
@@ -177,6 +180,7 @@ void printState(int currentCkpEventCounter) {
// float fuel = getDefaultFuel(rpm, map);
// debugFloat(&logger, "d_fuel", fuel, 2);
+#endif /* EFI_SHAFT_POSITION_INPUT */
}
#define INITIAL_FULL_LOG TRUE
@@ -201,32 +205,31 @@ static void printStatus(void) {
// return getTCharge(getCurrentRpm(), tps, cltK, iatK);
//}
-#if EFI_CUSTOM_PANIC_METHOD
-extern char *dbg_panic_file;
-extern int dbg_panic_line;
-#endif
+//#if EFI_CUSTOM_PANIC_METHOD
+//extern char *dbg_panic_file;
+//extern int dbg_panic_line;
+//#endif
-bool_t hasFatalError(void) {
- return dbg_panic_msg != NULL;
-}
-
-static void checkIfShouldHalt(void) {
-#if CH_DBG_ENABLED
- if (hasFatalError()) {
- setOutputPinValue(LED_ERROR, 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
-}
+//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
@@ -253,14 +256,14 @@ extern char errorMessageBuffer[200];
void updateDevConsoleState(void) {
if (!isConsoleReady())
return;
- checkIfShouldHalt();
+// looks like this is not needed anymore
+// checkIfShouldHalt();
printPending();
if (hasFirmwareError()) {
- if (!firmwareErrorReported || !PRINT_FIRMWARE_ONCE)
- printMsg(&logger, "firmware error: %s", errorMessageBuffer);
- firmwareErrorReported = TRUE;
+ printMsg(&logger, "firmware error: %s", errorMessageBuffer);
warningEnabled = FALSE;
+ chThdSleepMilliseconds(200);
return;
}
@@ -324,7 +327,7 @@ void updateHD44780lcd(void) {
lcd_HD44780_print_char('R');
lcd_HD44780_set_position(0, 10);
- char * ptr = itoa10((uint8_t*) buffer, getRpm());
+ char * ptr = itoa10(buffer, getRpm());
ptr[0] = 0;
int len = ptr - buffer;
for (int i = 0; i < 6 - len; i++)
@@ -346,7 +349,7 @@ void updateHD44780lcd(void) {
}
#endif /* EFI_PROD_CODE */
-static WORKING_AREA(lcdThreadStack, UTILITY_THREAD_STACK_SIZE);
+static THD_WORKING_AREA(lcdThreadStack, UTILITY_THREAD_STACK_SIZE);
static void lcdThread(void *arg) {
chRegSetThreadName("lcd");
@@ -354,20 +357,49 @@ static void lcdThread(void *arg) {
#if EFI_HD44780_LCD
updateHD44780lcd();
#endif
- chThdSleepMilliseconds(50);
+ chThdSleepMilliseconds(boardConfiguration->lcdThreadPeriod);
}
}
-static WORKING_AREA(tsThreadStack, UTILITY_THREAD_STACK_SIZE);
+static THD_WORKING_AREA(tsThreadStack, UTILITY_THREAD_STACK_SIZE);
+
+#if EFI_TUNER_STUDIO
+extern TunerStudioOutputChannels tsOutputChannels;
+
+void updateTunerStudioState(TunerStudioOutputChannels *tsOutputChannels) {
+#if EFI_SHAFT_POSITION_INPUT
+ int rpm = getRpm();
+#else
+ int rpm = 0;
+#endif
+
+ float tps = getTPS();
+ float coolant = getCoolantTemperature();
+ float intake = getIntakeAirTemperature();
+
+ tsOutputChannels->rpm = rpm;
+ tsOutputChannels->coolant_temperature = coolant;
+ tsOutputChannels->intake_air_temperature = intake;
+ tsOutputChannels->throttle_positon = tps;
+ tsOutputChannels->mass_air_flow = getMaf();
+ tsOutputChannels->air_fuel_ratio = getAfr();
+ tsOutputChannels->v_batt = getVBatt();
+ tsOutputChannels->tpsADC = getTPS10bitAdc();
+ tsOutputChannels->atmospherePressure = getBaroPressure();
+ tsOutputChannels->manifold_air_pressure = getMap();
+ tsOutputChannels->checkEngine = hasErrorCodes();
+ tsOutputChannels->tCharge = getTCharge(rpm, tps, coolant, intake);
+}
+#endif /* EFI_TUNER_STUDIO */
static void tsStatusThread(void *arg) {
chRegSetThreadName("tuner s");
while (true) {
#if EFI_TUNER_STUDIO
// sensor state for EFI Analytics Tuner Studio
- updateTunerStudioState();
+ updateTunerStudioState(&tsOutputChannels);
#endif /* EFI_TUNER_STUDIO */
- chThdSleepMilliseconds(50);
+ chThdSleepMilliseconds(boardConfiguration->tunerStudioThreadPeriod);
}
}
diff --git a/firmware/console/tunerstudio/tunerstudio.c b/firmware/console/tunerstudio/tunerstudio.c
index 46c046d28a..2d9e7cfdc2 100644
--- a/firmware/console/tunerstudio/tunerstudio.c
+++ b/firmware/console/tunerstudio/tunerstudio.c
@@ -37,13 +37,37 @@
#include "tunerstudio_configuration.h"
#include "malfunction_central.h"
#include "wave_math.h"
+#include "console_io.h"
+#include "crc.h"
#if EFI_TUNER_STUDIO
+#define MAX_PAGE_ID 5
+#define PAGE_0_SIZE 1356
+
+// in MS, that's 10 seconds
+#define TS_READ_TIMEOUT 10000
+
+#define TS_SERIAL_UART_DEVICE &SD3
+//#define TS_SERIAL_SPEED 115200
+#define TS_SERIAL_SPEED 38400
+
+#define PROTOCOL "001"
+
+extern SerialUSBDriver SDU1;
+
+BaseChannel * getTsSerialDevice(void) {
+ if (isSerialOverUart()) {
+ // if console uses UART then TS uses USB
+ return (BaseChannel *) &SDU1;
+ } else {
+ return (BaseChannel *) TS_SERIAL_UART_DEVICE;
+ }
+}
+
static Logging logger;
extern engine_configuration_s *engineConfiguration;
-extern board_configuration_s *boardConfiguration;
extern persistent_config_s configWorkingCopy;
extern persistent_config_container_s persistentState;
@@ -52,47 +76,49 @@ extern SerialUSBDriver SDU1;
static efitimems_t previousWriteReportMs = 0;
-#if EFI_TUNER_STUDIO_OVER_USB
-#define ts_serail_ready() is_usb_serial_ready()
-#else
-#define ts_serail_ready() TRUE
-static SerialConfig tsSerialConfig = { TS_SERIAL_SPEED, 0, USART_CR2_STOP1_BITS | USART_CR2_LINEN, 0 };
-#endif /* EFI_TUNER_STUDIO_OVER_USB */
+static int ts_serail_ready(void) {
+ if (isSerialOverUart()) {
+ // TS uses USB when console uses serial
+ return is_usb_serial_ready();
+ } else {
+ // TS uses serial when console uses USB
+ return TRUE;
+ }
+}
-static WORKING_AREA(TS_WORKING_AREA, UTILITY_THREAD_STACK_SIZE);
+static SerialConfig tsSerialConfig = { TS_SERIAL_SPEED, 0, USART_CR2_STOP1_BITS | USART_CR2_LINEN, 0 };
+
+static THD_WORKING_AREA(TS_WORKING_AREA, UTILITY_THREAD_STACK_SIZE);
static int tsCounter = 0;
-static int writeCounter = 0;
-static short pageId;
-
-static TunerStudioWriteRequest writeRequest;
+//static TunerStudioWriteValueRequest writeValueRequest;
+//static TunerStudioWriteChunkRequest writeChunkRequest;
extern TunerStudioOutputChannels tsOutputChannels;
-//char *constantsAsPtr = (char *) &configWorkingCopy;
-
extern TunerStudioState tsState;
static void printStats(void) {
-#if EFI_TUNER_STUDIO_OVER_USB
-#else
- 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_TUNER_STUDIO_OVER_USB */
+ 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);
+ }
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 C counter=%d", tsState.readPageCommandsCounter);
+ scheduleMsg(&logger, "TunerStudio P counter=%d", tsState.readPageCommandsCounter);
scheduleMsg(&logger, "TunerStudio B counter=%d", tsState.burnCommandCounter);
- scheduleMsg(&logger, "TunerStudio W counter=%d", writeCounter);
+ 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, "page 0 size=%d", getTunerStudioPageSize(0));
scheduleMsg(&logger, "page 1 size=%d", getTunerStudioPageSize(1));
}
void tunerStudioWriteData(const uint8_t * buffer, int size) {
- chSequentialStreamWrite(TS_SERIAL_DEVICE, buffer, size);
+ chSequentialStreamWrite(getTsSerialDevice(), buffer, size);
}
void tunerStudioDebug(char *msg) {
@@ -108,6 +134,11 @@ char *getWorkingPageAddr(int pageIndex) {
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;
}
@@ -115,86 +146,143 @@ char *getWorkingPageAddr(int pageIndex) {
int getTunerStudioPageSize(int pageIndex) {
switch (pageIndex) {
case 0:
- return sizeof(configWorkingCopy.engineConfiguration);
+ return PAGE_0_SIZE;
case 1:
return sizeof(configWorkingCopy.boardConfiguration);
+ case 2:
+ case 3:
+ case 4:
+ return 1024;
}
return 0;
+}
+void handlePageSelectCommand(uint16_t pageId) {
+ tsState.pageCommandCounter++;
+
+ tsState.currentPageId = pageId;
+ scheduleMsg(&logger, "page %d selected", tsState.currentPageId);
+ tunerStudioWriteCrcPacket(TS_RESPONSE_OK, NULL, 0);
+}
+
+/**
+ * This command is needed to make the whole transfer a bit faster
+ * @note See also handleWriteValueCommand
+ */
+void handleWriteChunkCommand(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);
+ // out of range
+ tsState.errorCounter++;
+ offset = 0;
+ }
+
+ if (count > getTunerStudioPageSize(tsState.currentPageId)) {
+ scheduleMsg(&logger, "ERROR count %d", count);
+ // out of range
+ tsState.errorCounter++;
+ count = 0;
+ }
+
+ uint8_t * addr = (uint8_t *) (getWorkingPageAddr(tsState.currentPageId) + offset);
+// memcpy(addr, content, count);
+
+ tunerStudioWriteCrcPacket(TS_RESPONSE_OK, NULL, 0);
}
/**
* 'Write' command receives a single value at a given offset
+ * @note Writing values one by one is pretty slow
*/
-void handleValueWriteCommand(void) {
- writeCounter++;
+void handleWriteValueCommand(uint16_t page, uint16_t offset, uint8_t value) {
+ tsState.writeValueCommandCounter++;
- //tunerStudioDebug("got W (Write)"); // we can get a lot of these
+ tsState.currentPageId = page;
+
+//tunerStudioDebug("got W (Write)"); // we can get a lot of these
- int recieved = chSequentialStreamRead(TS_SERIAL_DEVICE, (uint8_t *)&pageId, 2);
- if (recieved != 2) {
- tsState.errorCounter++;
- return;
- }
#if EFI_TUNER_STUDIO_VERBOSE
// scheduleMsg(&logger, "Page number %d\r\n", pageId); // we can get a lot of these
#endif
- int size = sizeof(TunerStudioWriteRequest);
+ int size = sizeof(TunerStudioWriteValueRequest);
// scheduleMsg(&logger, "Reading %d\r\n", size);
- recieved = chSequentialStreamRead(TS_SERIAL_DEVICE, (uint8_t *)&writeRequest, size);
-// scheduleMsg(&logger, "got %d", recieved);
-
-// unsigned char offset = writeBuffer[0];
-// unsigned char value = writeBuffer[1];
-//
+ if (offset > getTunerStudioPageSize(tsState.currentPageId)) {
+ scheduleMsg(&logger, "ERROR offset %d", offset);
+ // out of range
+ tsState.errorCounter++;
+ offset = 0;
+ return;
+ }
efitimems_t nowMs = currentTimeMillis();
if (nowMs - previousWriteReportMs > 5) {
previousWriteReportMs = nowMs;
-// scheduleMsg(&logger, "page %d offset %d: value=%d", pageId, writeRequest.offset, writeRequest.value);
+ scheduleMsg(&logger, "page %d offset %d: value=%d", tsState.currentPageId, offset, value);
}
- getWorkingPageAddr(pageId)[writeRequest.offset] = writeRequest.value;
+ getWorkingPageAddr(tsState.currentPageId)[offset] = value;
// scheduleMsg(&logger, "va=%d", configWorkingCopy.boardConfiguration.idleValvePin);
}
-void handlePageReadCommand(void) {
+static void sendErrorCode(void) {
+ tunerStudioWriteCrcPacket(TS_RESPONSE_CRC_FAILURE, NULL, 0);
+}
+
+void handlePageReadCommand(uint16_t pageId, uint16_t offset, uint16_t count) {
tsState.readPageCommandsCounter++;
- tunerStudioDebug("got C (Constants)");
- int recieved = chSequentialStreamRead(TS_SERIAL_DEVICE, (uint8_t *)&pageId, 2);
- if (recieved != 2) {
+ 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;
tsState.errorCounter++;
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);
+ tunerStudioWriteCrcPacket(TS_RESPONSE_OK, addr, count);
#if EFI_TUNER_STUDIO_VERBOSE
- scheduleMsg(&logger, "Page number %d", pageId);
+ scheduleMsg(&logger, "Sending %d done", size);
#endif
-
- tunerStudioWriteData((const uint8_t *) getWorkingPageAddr(pageId), getTunerStudioPageSize(pageId));
}
-
/**
* 'Burn' command is a command to commit the changes
*/
-void handleBurnCommand(void) {
+void handleBurnCommand(uint16_t page) {
tsState.burnCommandCounter++;
tunerStudioDebug("got B (Burn)");
- int recieved = chSequentialStreamRead(TS_SERIAL_DEVICE, (uint8_t *)&pageId, 2);
- if (recieved != 2) {
- tsState.errorCounter++;
- return;
- }
+ tsState.currentPageId = page;
+
#if EFI_TUNER_STUDIO_VERBOSE
- scheduleMsg(&logger, "Page number %d\r\n", pageId);
+ scheduleMsg(&logger, "Page number %d\r\n", tsState.currentPageId);
#endif
- // todo: how about some multi-threading?
+// todo: how about some multi-threading?
memcpy(&persistentState.persistentConfiguration, &configWorkingCopy, sizeof(persistent_config_s));
scheduleMsg(&logger, "va1=%d", configWorkingCopy.boardConfiguration.idleValvePin);
@@ -202,8 +290,15 @@ void handleBurnCommand(void) {
writeToFlash();
incrementGlobalConfigurationVersion();
+ tunerStudioWriteCrcPacket(TS_RESPONSE_BURN_OK, NULL, 0);
}
+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");
@@ -216,41 +311,128 @@ static msg_t tsThreadEntryPoint(void *arg) {
wasReady = FALSE;
continue;
}
+
if (!wasReady) {
wasReady = TRUE;
// scheduleSimpleMsg(&logger, "ts channel is now ready ", hTimeNow());
}
- short command = (short) chSequentialStreamGet(TS_SERIAL_DEVICE);
- int success = tunerStudioHandleCommand(command);
- if (!success && command != 0)
- print("got unexpected TunerStudio command %c:%d\r\n", command, command);
-
tsCounter++;
+
+ int recieved = chSequentialStreamRead(getTsSerialDevice(), &firstByte, 1);
+ if (recieved != 1) {
+ tsState.errorCounter++;
+ continue;
+ }
+// scheduleMsg(&logger, "Got first=%x=[%c]", firstByte, firstByte);
+ if (firstByte == TS_HELLO_COMMAND) {
+ scheduleMsg(&logger, "Got naked Query command");
+ handleQueryCommand(FALSE);
+ continue;
+ } else if (firstByte == 't' || firstByte == 'T') {
+ handleTestCommand();
+ continue;
+ } else if (firstByte == TS_READ_COMMAND) {
+ scheduleMsg(&logger, "Got naked READ PAGE???");
+ continue;
+ } else if (firstByte == TS_OUTPUT_COMMAND) {
+ scheduleMsg(&logger, "Got naked Channels???");
+ continue;
+ } else if (firstByte == 'F') {
+ tunerStudioDebug("not ignoring F");
+ tunerStudioWriteData((const uint8_t *) PROTOCOL, strlen(PROTOCOL));
+ continue;
+ }
+
+ recieved = chSequentialStreamRead(getTsSerialDevice(), &secondByte, 1);
+ if (recieved != 1) {
+ tsState.errorCounter++;
+ 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);
+ tsState.errorCounter++;
+ sendErrorCode();
+ continue;
+ }
+
+ recieved = chnReadTimeout(getTsSerialDevice(), crcIoBuffer, 1, MS2ST(TS_READ_TIMEOUT));
+ if (recieved != 1) {
+ scheduleMsg(&logger, "did not receive command");
+ tsState.errorCounter++;
+ continue;
+ }
+
+ char command = crcIoBuffer[0];
+ if (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) {
+ 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));
+ if (recieved != incomingPacketSize + 4 - 1) {
+ scheduleMsg(&logger, "got ONLY %d", recieved);
+ tsState.errorCounter++;
+ 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);
+ tsState.errorCounter++;
+ 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 = tunerStudioHandleCommand(crcIoBuffer, incomingPacketSize);
+ if (!success)
+ print("got unexpected TunerStudio command %x:%c\r\n", command, command);
+
}
#if defined __GNUC__
return 0;
#endif
}
-extern engine_configuration_s *engineConfiguration;
-
void syncTunerStudioCopy(void) {
memcpy(&configWorkingCopy, &persistentState.persistentConfiguration, sizeof(persistent_config_s));
}
void startTunerStudioConnectivity(void) {
initLogging(&logger, "tuner studio");
-#if EFI_TUNER_STUDIO_OVER_USB
- 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));
+ memset(&tsState, 0, sizeof(tsState));
+ if (isSerialOverUart()) {
+ print("TunerStudio over USB serial");
+ usb_serial_start();
+ } else {
- sdStart(TS_SERIAL_DEVICE, &tsSerialConfig);
-#endif
+ 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);
+ }
syncTunerStudioCopy();
@@ -259,18 +441,22 @@ void startTunerStudioConnectivity(void) {
chThdCreateStatic(TS_WORKING_AREA, sizeof(TS_WORKING_AREA), NORMALPRIO, tsThreadEntryPoint, NULL);
}
-void updateTunerStudioState() {
- tsOutputChannels.rpm = getRpm();
- tsOutputChannels.coolant_temperature = getCoolantTemperature();
- tsOutputChannels.intake_air_temperature = getIntakeAirTemperature();
- tsOutputChannels.throttle_positon = getTPS();
- tsOutputChannels.mass_air_flow = getMaf();
- tsOutputChannels.air_fuel_ratio = getAfr();
- tsOutputChannels.v_batt = getVBatt();
- tsOutputChannels.tpsADC = getTPS10bitAdc();
- tsOutputChannels.atmospherePressure = getBaroPressure();
- tsOutputChannels.manifold_air_pressure = getMap();
- tsOutputChannels.checkEngine = hasErrorCodes();
+/**
+ * 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.h b/firmware/console/tunerstudio/tunerstudio.h
index c37a65f100..b81b5114a0 100644
--- a/firmware/console/tunerstudio/tunerstudio.h
+++ b/firmware/console/tunerstudio/tunerstudio.h
@@ -8,13 +8,18 @@
#ifndef TUNERSTUDIO_H_
#define TUNERSTUDIO_H_
-#if EFI_TUNER_STUDIO_OVER_USB
-#define TS_SERIAL_DEVICE (&SDU1)
-#else
-#define TS_SERIAL_DEVICE &SD3
-#define TS_SERIAL_SPEED 115200
+#include "tunerstudio_configuration.h"
-#endif /* EFI_TUNER_STUDIO_OVER_USB */
+#if defined __GNUC__
+typedef struct
+ __attribute__((packed)) {
+#else
+ typedef __packed struct {
+#endif
+ short int offset;
+ short int count;
+
+ } TunerStudioWriteChunkRequest;
#if defined __GNUC__
typedef struct
@@ -25,19 +30,19 @@ typedef struct
short int offset;
unsigned char value;
- } TunerStudioWriteRequest;
+ } TunerStudioWriteValueRequest;
#ifdef __cplusplus
-extern "C"
-{
+ extern "C" {
#endif /* __cplusplus */
void startTunerStudioConnectivity(void);
void syncTunerStudioCopy(void);
- void updateTunerStudioState(void);
+ void updateTunerStudioState(TunerStudioOutputChannels *tsOutputChannels);
+ void tunerStudioWriteCrcPacket(const uint8_t command, const void *buf, const uint16_t size);
#ifdef __cplusplus
-}
+ }
#endif /* __cplusplus */
#endif /* TUNERSTUDIO_H_ */
diff --git a/firmware/console/tunerstudio/tunerstudio_algo.c b/firmware/console/tunerstudio/tunerstudio_algo.c
index 75dabeb098..7282d372db 100644
--- a/firmware/console/tunerstudio/tunerstudio_algo.c
+++ b/firmware/console/tunerstudio/tunerstudio_algo.c
@@ -2,6 +2,8 @@
* @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
@@ -63,17 +65,34 @@ TunerStudioOutputChannels tsOutputChannels;
*/
persistent_config_s configWorkingCopy;
-int tunerStudioHandleCommand(short command) {
- if (command == 'H') {
- handleQueryCommand();
- } else if (command == 'O') {
+int tunerStudioHandleCommand(char *data, int incomingPacketSize) {
+ char command = data[0];
+ data++;
+ if (command == TS_HELLO_COMMAND) {
+ tunerStudioDebug("got CRC Query");
+ handleQueryCommand(TRUE);
+ } else if (command == TS_OUTPUT_COMMAND) {
handleOutputChannelsCommand();
- } else if (command == 'W') {
- handleValueWriteCommand();
- } else if (command == 'B') {
- handleBurnCommand();
- } else if (command == 'C') {
- handlePageReadCommand();
+ } else if (command == TS_PAGE_COMMAND) {
+ uint16_t page = *(uint16_t *) data;
+ handlePageSelectCommand(page);
+ } else if (command == TS_CHUNK_WRITE_COMMAND) {
+ uint16_t offset = *(uint16_t *) data;
+ uint16_t count = *(uint16_t *) (data + 2);
+ handleWriteChunkCommand(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(page, offset, value);
+ } else if (command == TS_BURN_COMMAND) {
+ uint16_t page = *(uint16_t *) data;
+ handleBurnCommand(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(page, offset, count);
} else if (command == 't' || command == 'T') {
handleTestCommand();
} else if (command == 'F') {
@@ -86,23 +105,23 @@ int tunerStudioHandleCommand(short command) {
* Currently on some firmware versions the F command is not used and is just ignored by the firmware as a unknown command."
*/
} else {
-#if EFI_TUNER_STUDIO_OVER_USB
- /**
- * With TTL there is a real chance of corrupted messages.
- * With serial-over-USB we are not expecting communication errors
- */
-// fatal("unexpected TunerStudio command in USB mode");
-#endif /* EFI_TUNER_STUDIO_OVER_USB */
+ tunerStudioDebug("ignoring unexpected");
tsState.errorCounter++;
return FALSE;
}
return TRUE;
}
-void handleQueryCommand(void) {
+void handleQueryCommand(int needCrc) {
tsState.queryCommandCounter++;
tunerStudioDebug("got H (queryCommand)");
- tunerStudioWriteData((const uint8_t *) TS_SIGNATURE, strlen(TS_SIGNATURE) + 1);
+ if (needCrc) {
+ // Query with CRC takes place while re-establishing connection
+ tunerStudioWriteCrcPacket(TS_RESPONSE_OK, (const uint8_t *) TS_SIGNATURE, strlen(TS_SIGNATURE) + 1);
+ } else {
+ // Query without CRC takes place on TunerStudio startup
+ tunerStudioWriteData((const uint8_t *) TS_SIGNATURE, strlen(TS_SIGNATURE) + 1);
+ }
}
/**
@@ -111,7 +130,7 @@ void handleQueryCommand(void) {
void handleOutputChannelsCommand(void) {
tsState.outputChannelsCommandCounter++;
// this method is invoked too often to print any debug information
- tunerStudioWriteData((const uint8_t *) &tsOutputChannels, sizeof(TunerStudioOutputChannels));
+ tunerStudioWriteCrcPacket(TS_RESPONSE_OK, (const uint8_t *) &tsOutputChannels, sizeof(TunerStudioOutputChannels));
}
void handleTestCommand(void) {
@@ -119,6 +138,6 @@ 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)\r\n");
+ 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 48ac238c08..478b9c6dab 100644
--- a/firmware/console/tunerstudio/tunerstudio_algo.h
+++ b/firmware/console/tunerstudio/tunerstudio_algo.h
@@ -12,27 +12,55 @@
#include
+// http://en.wikipedia.org/wiki/Endianness
+
+#define SWAP_UINT16(x) ((x) << 8) | ((x) >> 8)
+
+#define SWAP_UINT32(x) (((x) >> 24) & 0xff) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) | (((x) << 24) & 0xff000000)
+
+// response codes
+
+#define TS_RESPONSE_OK 0x00
+#define TS_RESPONSE_BURN_OK 0x04
+#define TS_RESPONSE_CRC_FAILURE 0x82
+
typedef struct {
int queryCommandCounter;
int outputChannelsCommandCounter;
int readPageCommandsCounter;
int burnCommandCounter;
+ int pageCommandCounter;
+ int writeValueCommandCounter;
+ int writeChunkCommandCounter;
int errorCounter;
+ // this field is in the end to simply aligning situation
+ short currentPageId;
} TunerStudioState;
-int tunerStudioHandleCommand(short command);
+int tunerStudioHandleCommand(char *data, int incomingPacketSize);
void handleTestCommand(void);
-void handleQueryCommand(void);
+void handleQueryCommand(int needCrc);
void handleOutputChannelsCommand(void);
char *getWorkingPageAddr(int pageIndex);
int getTunerStudioPageSize(int pageIndex);
-void handleValueWriteCommand(void);
-void handlePageReadCommand(void);
-void handleBurnCommand(void);
+void handleWriteValueCommand(uint16_t page, uint16_t offset, uint8_t value);
+void handleWriteChunkCommand(short offset, short count, void *content);
+void handlePageSelectCommand(uint16_t pageId);
+void handlePageReadCommand(uint16_t pageId, uint16_t offset, uint16_t count);
+void handleBurnCommand(uint16_t page);
void tunerStudioWriteData(const uint8_t * buffer, int size);
void tunerStudioDebug(char *msg);
+#define TS_HELLO_COMMAND 'H'
+#define TS_OUTPUT_COMMAND 'O'
+#define TS_READ_COMMAND 'R'
+#define TS_PAGE_COMMAND 'P'
+
+#define TS_SINGLE_WRITE_COMMAND 'W'
+#define TS_CHUNK_WRITE_COMMAND 'C'
+#define TS_BURN_COMMAND 'B'
+
#endif /* TUNERSTUDIO_ALGO_H_ */
diff --git a/firmware/console/tunerstudio/tunerstudio_configuration.h b/firmware/console/tunerstudio/tunerstudio_configuration.h
index f46f28006a..a3379fb6ea 100644
--- a/firmware/console/tunerstudio/tunerstudio_configuration.h
+++ b/firmware/console/tunerstudio/tunerstudio_configuration.h
@@ -29,6 +29,7 @@ typedef struct {
float atmospherePressure; // size 4, offset 36
float manifold_air_pressure; // size 4, offset 40
int checkEngine; // size 4, offset 44
+ float tCharge;
} TunerStudioOutputChannels;
#endif /* TUNERSTUDIO_CONFIGURATION_H_ */
diff --git a/firmware/console_util/datalogging.c b/firmware/console_util/datalogging.c
index bea72eb8b8..8f6da7f70e 100644
--- a/firmware/console_util/datalogging.c
+++ b/firmware/console_util/datalogging.c
@@ -38,7 +38,6 @@
#include "memstreams.h"
#include "console_io.h"
-#define OUTPUT_BUFFER 9000
/**
* This is the size of the MemoryStream used by chvprintf
*/
@@ -50,18 +49,15 @@
/**
* This is the buffer into which all the data providers write
*/
-#if defined __GNUC__
-static char pendingBuffer[OUTPUT_BUFFER] __attribute__((section(".ccm")));
-#else
-static char pendingBuffer[OUTPUT_BUFFER];
-#endif
+static char pendingBuffer[DL_OUTPUT_BUFFER] CCM_OPTIONAL;
+
/**
* We copy all the pending data into this buffer once we are ready to push it out
*/
-static char outputBuffer[OUTPUT_BUFFER];
+static char outputBuffer[DL_OUTPUT_BUFFER];
static MemoryStream intermediateLoggingBuffer;
-static uint8_t intermediateLoggingBufferData[INTERMEDIATE_LOGGING_BUFFER_SIZE]; //todo define max-printf-buffer
+static uint8_t intermediateLoggingBufferData[INTERMEDIATE_LOGGING_BUFFER_SIZE] CCM_OPTIONAL; //todo define max-printf-buffer
static bool intermediateLoggingBufferInited = FALSE;
static int validateBuffer(Logging *logging, int extraLen, const char *text) {
@@ -76,7 +72,7 @@ static int validateBuffer(Logging *logging, int extraLen, const char *text) {
strcat(logging->SMALL_BUFFER, logging->name);
strcat(logging->SMALL_BUFFER, "/");
strcat(logging->SMALL_BUFFER, text);
- fatal(logging->SMALL_BUFFER);
+ firmwareError(logging->SMALL_BUFFER);
// unlockOutputBuffer();
// resetLogging(logging);
return TRUE;
@@ -85,7 +81,7 @@ static int validateBuffer(Logging *logging, int extraLen, const char *text) {
}
void append(Logging *logging, const char *text) {
- chDbgCheck(text!=NULL, "append NULL");
+ efiAssertVoid(text != NULL, "append NULL");
int extraLen = strlen(text);
int errcode = validateBuffer(logging, extraLen, text);
if (errcode)
@@ -102,8 +98,9 @@ static void vappendPrintfI(Logging *logging, const char *fmt, va_list arg) {
}
void vappendPrintf(Logging *logging, const char *fmt, va_list arg) {
+ efiAssertVoid(getRemainingStack(chThdSelf()) > 16, "stack#5b");
if (!intermediateLoggingBufferInited) {
- fatal("intermediateLoggingBufferInited not inited!");
+ firmwareError("intermediateLoggingBufferInited not inited!");
return;
}
int is_locked = isLocked();
@@ -128,6 +125,7 @@ void vappendPrintf(Logging *logging, const char *fmt, va_list arg) {
}
void appendPrintf(Logging *logging, const char *fmt, ...) {
+ efiAssertVoid(getRemainingStack(chThdSelf()) > 16, "stack#4");
va_list ap;
va_start(ap, fmt);
vappendPrintf(logging, fmt, ap);
@@ -154,7 +152,7 @@ char* getCaption(LoggingPoints loggingPoint) {
case LP_MAP_RAW:
return "MAP_R";
}
- fatal("No such loggingPoint");
+ firmwareError("No such loggingPoint");
return NULL;
}
@@ -176,7 +174,7 @@ static char* get2ndCaption(int loggingPoint) {
case LP_MAF:
return "MAF";
}
- fatal("No such loggingPoint");
+ firmwareError("No such loggingPoint");
return NULL;
}
@@ -204,7 +202,11 @@ void debugInt(Logging *logging, const char *caption, int value) {
}
void appendFloat(Logging *logging, float value, int precision) {
- // todo: this implementation is less than perfect
+ /**
+ * todo: #1 this implementation is less than perfect
+ * todo: #2 The only way to avoid double promotion would probably be using *float instead of float
+ * See also http://stackoverflow.com/questions/5522051/printing-a-float-in-c-while-avoiding-variadic-parameter-promotion-to-double
+ */
switch (precision) {
case 1:
appendPrintf(logging, "%..10f", value);
@@ -324,7 +326,7 @@ void scheduleMsg(Logging *logging, const char *fmt, ...) {
}
// todo: remove this method, replace with 'scheduleMsg'
-void scheduleIntValue(Logging *logging, char *msg, int value) {
+void scheduleIntValue(Logging *logging, const char *msg, int value) {
resetLogging(logging);
append(logging, msg);
@@ -339,10 +341,10 @@ void scheduleLogging(Logging *logging) {
// this could be done without locking
int newLength = strlen(logging->buffer);
- bool_t alreadyLocked = lockOutputBuffer();
+ bool alreadyLocked = lockOutputBuffer();
// I hope this is fast enough to operate under sys lock
int curLength = strlen(pendingBuffer);
- if (curLength + newLength >= OUTPUT_BUFFER) {
+ if (curLength + newLength >= DL_OUTPUT_BUFFER) {
/**
* if no one is consuming the data we have to drop it
* this happens in case of serial-over-USB, todo: find a better solution
diff --git a/firmware/console_util/datalogging.h b/firmware/console_util/datalogging.h
index a960f2c2c2..74aebbbbdc 100644
--- a/firmware/console_util/datalogging.h
+++ b/firmware/console_util/datalogging.h
@@ -81,7 +81,7 @@ void append(Logging *logging, const char *text);
void scheduleLogging(Logging *logging);
-void scheduleIntValue(Logging *logging, char *msg, int value);
+void scheduleIntValue(Logging *logging, const char *msg, int value);
/**
* this should only be invoked by the 'main' thread in order to keep the console safe
diff --git a/firmware/console_util/rfiutil.c b/firmware/console_util/rfiutil.c
index 82874c8229..dde04d8fb8 100644
--- a/firmware/console_util/rfiutil.c
+++ b/firmware/console_util/rfiutil.c
@@ -22,6 +22,8 @@
#include
#include "rfiutil.h"
+/*
+not used, not sure if we still need it. I guess we will remove it in 2015
int mylog10(int param) {
if (param < 10)
return 0;
@@ -39,49 +41,10 @@ int mylog10(int param) {
return 6;
if (param < 100000000)
return 7;
+ #warning This would be better without recursion
return mylog10(param / 10) + 1;
}
-
-static char *ltoa_internal(char *p, long num, unsigned radix) {
- int i;
- char *q;
-
- q = p + _MAX_FILLER;
- do {
- i = (int) (num % radix);
- i += '0';
- if (i > '9')
- i += 'A' - '0' - 10;
- *--q = i;
- } while ((num /= radix) != 0);
-
- i = (int) (p + _MAX_FILLER - q);
- do
- *p++ = *q++;
- while (--i);
-
- return p;
-}
-
-static char* itoa_signed(uint8_t *p, int num, unsigned radix) {
- if (num < 0) {
- *p++ = '-';
- char *end = ltoa_internal(p, -num, radix);
- *end = 0;
- return end;
- }
- char *end = ltoa_internal(p, num, radix);
- *end = 0;
- return end;
-}
-
-/**
- * Integer to string
- */
-char* itoa10(uint8_t *p, int num) {
-// todo: unit test
- return itoa_signed(p, num, 10);
-}
+*/
char hexChar(int v) {
v = v & 0xF;
@@ -103,11 +66,15 @@ int isLocked(void) {
return dbg_lock_cnt > 0;
}
-void chVTSetAny(VirtualTimer *vtp, systime_t time, vtfunc_t vtfunc, void *par) {
+void chVTSetAny(virtual_timer_t *vtp, systime_t time, vtfunc_t vtfunc, void *par) {
if (isIsrContext()) {
chSysLockFromIsr()
;
+ /**
+ * todo: this could be simplified once we migrate to ChibiOS 3.0
+ * See http://www.chibios.org/dokuwiki/doku.php?id=chibios:howtos:porting_from_2_to_3
+ */
if (chVTIsArmedI(vtp))
chVTResetI(vtp);
diff --git a/firmware/console_util/rfiutil.h b/firmware/console_util/rfiutil.h
index 8c279b2560..2eeb3e38aa 100644
--- a/firmware/console_util/rfiutil.h
+++ b/firmware/console_util/rfiutil.h
@@ -13,23 +13,15 @@
#include "histogram.h"
#include "datalogging.h"
-#ifndef TRUE
-#define TRUE 1
-#define FALSE 0
-#endif
-
-#define _MAX_FILLER 11
-
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
-char* itoa10(uint8_t *p, int num);
char hexC(int v);
int isIsrContext(void);
int isLocked(void);
-void chVTSetAny(VirtualTimer *vtp, systime_t time, vtfunc_t vtfunc, void *par);
+void chVTSetAny(virtual_timer_t *vtp, systime_t time, vtfunc_t vtfunc, void *par);
void printHistogram(Logging *logging, histogram_s *histogram);
#ifdef __cplusplus
diff --git a/firmware/controllers/PwmTester.cpp b/firmware/controllers/PwmTester.cpp
index 0945fa4b5d..ee48b24199 100644
--- a/firmware/controllers/PwmTester.cpp
+++ b/firmware/controllers/PwmTester.cpp
@@ -15,18 +15,7 @@
static Logging logger;
-static float _switchTimes[2];
-
-// todo: extract helper for simple PWM?
-static int pinStates[2];
-static single_wave_s wave(pinStates);
-static single_wave_s sr[1] = { wave };
-
-static PwmConfig pwmTest[5] = { PwmConfig(_switchTimes, sr),
- PwmConfig(_switchTimes, sr),
- PwmConfig(_switchTimes, sr),
- PwmConfig(_switchTimes, sr),
- PwmConfig(_switchTimes, sr)};
+static SimplePwm pwmTest[5];
extern board_configuration_s *boardConfiguration;
@@ -34,20 +23,15 @@ static void startPwmTest(int freq) {
scheduleMsg(&logger, "running pwm test @%d", freq);
// PD13, GPIO_NONE because pin is initialized elsewhere already
- startSimplePwm(&pwmTest[0], "tester", GPIO_NONE,
- LED_CRANKING, 0.5, 10, FALSE);
- // currently this is PB9 by default
- startSimplePwm(&pwmTest[1], "tester", boardConfiguration->injectionPins[0],
- INJECTOR_1_OUTPUT, 0.5, freq / 1.3333333333, FALSE);
+ startSimplePwm(&pwmTest[0], "tester", LED_WARNING, 10, 0.5);
+ // currently this is PB9 by default - see boardConfiguration->injectionPins
+ startSimplePwm(&pwmTest[1], "tester", INJECTOR_1_OUTPUT, freq / 1.3333333333, 0.5);
// currently this is PB8 by default
- startSimplePwm(&pwmTest[2], "tester", GPIO_NONE,
- INJECTOR_2_OUTPUT, 0.5, freq / 1000, FALSE);
+ startSimplePwm(&pwmTest[2], "tester", INJECTOR_2_OUTPUT, freq / 1000, 0.5);
// currently this is PE3 by default
- startSimplePwm(&pwmTest[3], "tester", GPIO_NONE,
- INJECTOR_3_OUTPUT, 0.5, freq, FALSE);
+ startSimplePwm(&pwmTest[3], "tester", INJECTOR_3_OUTPUT, freq, 0.5);
// currently this is PE5 by default
- startSimplePwm(&pwmTest[4], "tester", GPIO_NONE,
- INJECTOR_4_OUTPUT, 0.5, freq / 33.33333333333, FALSE);
+ startSimplePwm(&pwmTest[4], "tester", INJECTOR_4_OUTPUT, freq / 33.33333333333, 0.5);
}
void initPwmTester(void) {
diff --git a/firmware/controllers/algo/accel_enrichment.cpp b/firmware/controllers/algo/accel_enrichment.cpp
index 6cbe515041..c680fdd64a 100644
--- a/firmware/controllers/algo/accel_enrichment.cpp
+++ b/firmware/controllers/algo/accel_enrichment.cpp
@@ -18,25 +18,30 @@ extern engine_configuration_s *engineConfiguration;
static AccelEnrichmemnt instance;
void AccelEnrichmemnt::updateDiffEnrichment(engine_configuration_s *engineConfiguration, float engineLoad) {
- for (int i = 1; i < 4; i++) {
+ for (int i = 3; i == 1; i--)
engineLoadD[i] = engineLoadD[i - 1];
- }
+
engineLoadD[0] = engineLoad;
- float Dcurr = engineLoadD[0] - engineLoadD[1];
- float Dold = engineLoadD[2] - engineLoadD[3];
- diffEnrichment = ((3 * Dcurr + Dold) / 4) * (engineConfiguration->diffLoadEnrichmentCoef);
+
+ diffEnrichment = ((3 * (engineLoadD[0] - engineLoadD[1]) + (engineLoadD[2] - engineLoadD[3])) / 4)
+ * (engineConfiguration->diffLoadEnrichmentCoef);
}
float AccelEnrichmemnt::getDiffEnrichment() {
return diffEnrichment;
}
+AccelEnrichmemnt::AccelEnrichmemnt() {
+ for (int i = 0; i < 4; i++)
+ engineLoadD[i] = 0;
+ diffEnrichment = 0;
+}
float getAccelEnrichment(void) {
return instance.getDiffEnrichment();
}
#if EFI_PROD_CODE
-static WORKING_AREA(aeThreadStack, UTILITY_THREAD_STACK_SIZE);
+static THD_WORKING_AREA(aeThreadStack, UTILITY_THREAD_STACK_SIZE);
static msg_t DiffEnrichmentThread(int param) {
chRegSetThreadName("Diff Enrichment");
@@ -54,3 +59,4 @@ void initDiffEnrichment(void) {
}
#endif
+
diff --git a/firmware/controllers/algo/accel_enrichment.h b/firmware/controllers/algo/accel_enrichment.h
index 7b3440a8e4..554eb336b1 100644
--- a/firmware/controllers/algo/accel_enrichment.h
+++ b/firmware/controllers/algo/accel_enrichment.h
@@ -14,14 +14,18 @@
class AccelEnrichmemnt {
public:
- void updateDiffEnrichment(engine_configuration_s *engineConfiguration, float engineLoad);
+ AccelEnrichmemnt();
+ void updateDiffEnrichment(engine_configuration_s *engineConfiguration,
+ float engineLoad);
float getDiffEnrichment(void);
private:
- float engineLoadD[5];
+ float engineLoadD[4];
float diffEnrichment;
};
void initDiffEnrichment(void);
float getAccelEnrichment(void);
+
#endif /* ACC_ENRICHMENT_H_ */
+
diff --git a/firmware/controllers/algo/advance_map.c b/firmware/controllers/algo/advance_map.cpp
similarity index 71%
rename from firmware/controllers/algo/advance_map.c
rename to firmware/controllers/algo/advance_map.cpp
index 09c6e2fd01..91f0813e6d 100644
--- a/firmware/controllers/algo/advance_map.c
+++ b/firmware/controllers/algo/advance_map.cpp
@@ -1,58 +1,54 @@
-/**
- * @file advance_map.c
- *
- * @date Mar 27, 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 "advance_map.h"
-#include "interpolation.h"
-// that's for 'max' function
-#include "idle_controller.h"
-
-#include "engine_configuration.h"
-#include "engine_math.h"
-
-extern engine_configuration_s *engineConfiguration;
-//extern engine_configuration2_s *engineConfiguration2;
-
-static float *timing_ptrs[AD_LOAD_COUNT];
-static int initialized = FALSE;
-
-float getBaseAdvance(int rpm, float engineLoad) {
- chDbgAssert(initialized, "fuel map initialized", NULL);
- efiAssert(!cisnan(engineLoad), "invalid el");
- efiAssert(!cisnan(engineLoad), "invalid rpm");
- return interpolate3d(engineLoad, engineConfiguration->ignitionLoadBins, AD_LOAD_COUNT, rpm,
- engineConfiguration->ignitionRpmBins,
- AD_RPM_COUNT, timing_ptrs);
-}
-
-float getAdvance(int rpm, float engineLoad) {
- float angle;
- if (isCrankingR(rpm)) {
- angle = engineConfiguration->crankingTimingAngle;
- } else {
- angle = getBaseAdvance(rpm, engineLoad);
- }
- return fixAngle(angle + engineConfiguration->ignitionOffset);
-}
-
-void prepareTimingMap(void) {
- for (int k = 0; k < AD_LOAD_COUNT; k++)
- timing_ptrs[k] = engineConfiguration->ignitionTable[k];
- initialized = TRUE;
-}
+/**
+ * @file advance_map.c
+ *
+ * @date Mar 27, 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 "advance_map.h"
+#include "interpolation.h"
+// that's for 'max' function
+#include "idle_controller.h"
+
+#include "engine_configuration.h"
+#include "engine_math.h"
+
+extern engine_configuration_s *engineConfiguration;
+//extern engine_configuration2_s *engineConfiguration2;
+
+static Map3D1616 advanceMap;
+
+float getBaseAdvance(int rpm, float engineLoad) {
+ efiAssert(!cisnan(engineLoad), "invalid el", NAN);
+ efiAssert(!cisnan(engineLoad), "invalid rpm", NAN);
+ return advanceMap.getValue(engineLoad, engineConfiguration->ignitionLoadBins, rpm,
+ engineConfiguration->ignitionRpmBins);
+}
+
+float getAdvance(int rpm, float engineLoad) {
+ float angle;
+ if (isCrankingR(rpm)) {
+ angle = engineConfiguration->crankingTimingAngle;
+ } else {
+ angle = getBaseAdvance(rpm, engineLoad);
+ }
+ return fixAngle(angle + engineConfiguration->ignitionOffset);
+}
+
+void prepareTimingMap(void) {
+ advanceMap.init(engineConfiguration->ignitionTable);
+}
diff --git a/firmware/controllers/algo/advance_map.h b/firmware/controllers/algo/advance_map.h
index eeda60f28c..3f55d28b18 100644
--- a/firmware/controllers/algo/advance_map.h
+++ b/firmware/controllers/algo/advance_map.h
@@ -1,5 +1,5 @@
/*
- * advance.h
+ * @file advance_map.h
*
* @date Mar 27, 2013
* @author Andrey Belomutskiy, (c) 2012-2014
diff --git a/firmware/controllers/algo/algo.mk b/firmware/controllers/algo/algo.mk
index 6871e4e553..8fb5d3c3cc 100644
--- a/firmware/controllers/algo/algo.mk
+++ b/firmware/controllers/algo/algo.mk
@@ -1,16 +1,17 @@
CONTROLLERS_ALGO_SRC = $(PROJECT_DIR)/controllers/algo/map_adjuster.c \
- $(PROJECT_DIR)/controllers/algo/advance_map.c \
$(PROJECT_DIR)/controllers/algo/signal_executor.c \
$(PROJECT_DIR)/controllers/algo/malfunction_central.c \
- $(PROJECT_DIR)/controllers/algo/event_registry.c \
$(PROJECT_DIR)/controllers/algo/idle_controller.c \
$(PROJECT_DIR)/controllers/algo/wave_chart.c \
$(PROJECT_DIR)/controllers/algo/nmea.c
CONTROLLERS_ALGO_SRC_CPP = $(PROJECT_DIR)/controllers/algo/OutputSignalArray.cpp \
+ $(PROJECT_DIR)/controllers/algo/advance_map.cpp \
$(PROJECT_DIR)/controllers/algo/fuel_math.cpp \
$(PROJECT_DIR)/controllers/algo/accel_enrichment.cpp \
$(PROJECT_DIR)/controllers/algo/engine_configuration.cpp \
+ $(PROJECT_DIR)/controllers/algo/engine.cpp \
+ $(PROJECT_DIR)/controllers/algo/event_registry.cpp \
$(PROJECT_DIR)/controllers/algo/algo.cpp
diff --git a/firmware/controllers/algo/ec2.h b/firmware/controllers/algo/ec2.h
index f03910c61a..6987070e8f 100644
--- a/firmware/controllers/algo/ec2.h
+++ b/firmware/controllers/algo/ec2.h
@@ -13,8 +13,19 @@
#define EC2_H_
#include "engine_configuration.h"
+#include "event_registry.h"
#include "trigger_structure.h"
+/**
+ * @brief Here we store information about which injector or spark should be fired when.
+ */
+typedef struct {
+ ActuatorEventList crankingInjectionEvents;
+ ActuatorEventList injectionEvents;
+ IgnitionEventList ignitionEvents[2];
+} EventHandlerConfiguration;
+
+
#ifdef __cplusplus
extern "C"
{
@@ -42,15 +53,26 @@ public:
EventHandlerConfiguration engineEventConfiguration;
int isInjectionEnabledFlag;
+
+ /**
+ * This coefficient translates ADC value directly into voltage adjusted according to
+ * voltage divider configuration.
+ */
+ float adcToVoltageInputDividerCoefficient;
};
+typedef struct {
+ engine_configuration_s *engineConfiguration;
+ engine_configuration2_s *engineConfiguration2;
+} configuration_s;
+
void prepareOutputSignals(engine_configuration_s *engineConfiguration,
engine_configuration2_s *engineConfiguration2);
-void initializeIgnitionActions(float baseAngle, engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2, float dwellMs, ActuatorEventList *list);
+void initializeIgnitionActions(float advance, float dwellAngle, engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2, IgnitionEventList *list);
void addFuelEvents(engine_configuration_s const *e, engine_configuration2_s *engineConfiguration2, ActuatorEventList *list, injection_mode_e mode);
-void registerActuatorEventExt(engine_configuration_s const *engineConfiguration, trigger_shape_s * s, ActuatorEventList *list, OutputSignal *actuator, float angleOffset);
+void registerActuatorEventExt(engine_configuration_s const *engineConfiguration, trigger_shape_s * s, ActuatorEvent *e, OutputSignal *actuator, float angleOffset);
void resetConfigurationExt(Logging * logger, engine_type_e engineType,
engine_configuration_s *engineConfiguration,
diff --git a/firmware/controllers/algo/engine.cpp b/firmware/controllers/algo/engine.cpp
new file mode 100644
index 0000000000..95bf303510
--- /dev/null
+++ b/firmware/controllers/algo/engine.cpp
@@ -0,0 +1,13 @@
+/**
+ * @file engine.cpp
+ *
+ *
+ * This might be a http://en.wikipedia.org/wiki/God_object but that's best way I can
+ * express myself in C/C++. I am open for suggestions :)
+ *
+ * @date May 21, 2014
+ * @author Andrey Belomutskiy, (c) 2012-2014
+ */
+
+
+
diff --git a/firmware/controllers/algo/engine.h b/firmware/controllers/algo/engine.h
new file mode 100644
index 0000000000..d1e92cf06b
--- /dev/null
+++ b/firmware/controllers/algo/engine.h
@@ -0,0 +1,21 @@
+/**
+ * @file engine.h
+ *
+ * @date May 21, 2014
+ * @author Andrey Belomutskiy, (c) 2012-2014
+ */
+#ifndef ENGINE_H_
+#define ENGINE_H_
+
+#include "main.h"
+#include "engine_configuration.h"
+
+class RpmCalculator;
+
+class Engine {
+public:
+ RpmCalculator *rpmCalculator;
+ engine_configuration_s *engineConfiguration;
+};
+
+#endif /* ENGINE_H_ */
diff --git a/firmware/controllers/algo/engine_configuration.cpp b/firmware/controllers/algo/engine_configuration.cpp
index 0d8631ac59..418090ca9b 100644
--- a/firmware/controllers/algo/engine_configuration.cpp
+++ b/firmware/controllers/algo/engine_configuration.cpp
@@ -1,5 +1,5 @@
/**
- * @file engine_controller.c
+ * @file engine_configuration.cpp
* @brief Utility method related to the engine configuration data structure.
*
* @date Nov 22, 2013
@@ -46,6 +46,7 @@
#include "MiniCooperR50.h"
#include "ford_escort_gt.h"
#include "citroenBerlingoTU3JP.h"
+#include "rover_v8.h"
static volatile int globalConfigurationVersion = 0;
@@ -77,6 +78,14 @@ void initBpsxD1Sensor(afr_sensor_s *sensor) {
sensor->value2 = 19;
}
+void setWholeVEMap(engine_configuration_s *engineConfiguration, float value) {
+// for (int l = 0; l < VE_LOAD_COUNT; l++) {
+// for (int r = 0; r < VE_RPM_COUNT; r++) {
+// engineConfiguration->veTable[l][r] = value;
+// }
+// }
+}
+
void setWholeFuelMap(engine_configuration_s *engineConfiguration, float value) {
for (int l = 0; l < FUEL_LOAD_COUNT; l++) {
for (int r = 0; r < FUEL_RPM_COUNT; r++) {
@@ -85,6 +94,13 @@ void setWholeFuelMap(engine_configuration_s *engineConfiguration, float value) {
}
}
+void setTriggerSynchronizationGap(engine_configuration_s *engineConfiguration, float synchGap) {
+ engineConfiguration->triggerConfig.isSynchronizationNeeded = TRUE;
+
+ engineConfiguration->triggerConfig.syncRatioFrom = synchGap * 0.75;
+ engineConfiguration->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
@@ -192,20 +208,20 @@ void setDefaultConfiguration(engine_configuration_s *engineConfiguration, board_
engineConfiguration->canReadEnabled = TRUE;
engineConfiguration->canWriteEnabled = FALSE;
- /**
- * 0.5 means primary position sensor is on a camshaft
- */
- engineConfiguration->rpmMultiplier = 0.5;
+ setOperationMode(engineConfiguration, FOUR_STROKE_CAM_SENSOR);
engineConfiguration->cylindersCount = 4;
+ engineConfiguration->displacement = 2;
+ /**
+ * By the way http://users.erols.com/srweiss/tableifc.htm has a LOT of data
+ */
+ engineConfiguration->injectorFlow = 200;
engineConfiguration->displayMode = DM_HD44780;
engineConfiguration->logFormat = LF_NATIVE;
engineConfiguration->triggerConfig.triggerType = TT_TOOTHED_WHEEL;
- engineConfiguration->triggerConfig.syncRatioFrom = 1.5;
- engineConfiguration->triggerConfig.syncRatioTo = 3;
- engineConfiguration->triggerConfig.isSynchronizationNeeded = TRUE;
+ setTriggerSynchronizationGap(engineConfiguration, 2);
engineConfiguration->triggerConfig.useRiseEdge = TRUE;
engineConfiguration->HD44780width = 16;
@@ -291,6 +307,13 @@ void setDefaultConfiguration(engine_configuration_s *engineConfiguration, board_
boardConfiguration->primaryLogicAnalyzerPin = GPIOA_8;
boardConfiguration->secondaryLogicAnalyzerPin = GPIOE_7;
+ boardConfiguration->idleThreadPeriod = 100;
+ boardConfiguration->consoleLoopPeriod = 200;
+ boardConfiguration->lcdThreadPeriod = 300;
+ boardConfiguration->tunerStudioThreadPeriod = 300;
+ boardConfiguration->generalPeriodicThreadPeriod = 200;
+
+ boardConfiguration->tunerStudioSerialSpeed = 38400;
}
void setDefaultNonPersistentConfiguration(engine_configuration2_s *engineConfiguration2) {
@@ -363,6 +386,10 @@ void resetConfigurationExt(Logging * logger, engine_type_e engineType, engine_co
case CITROEN_TU3JP:
setCitroenBerlingoTU3JPConfiguration(engineConfiguration, boardConfiguration);
break;
+ case ROVER_V8:
+ setRoverv8(engineConfiguration, boardConfiguration);
+ break;
+
default:
firmwareError("Unexpected engine type: %d", engineType);
@@ -398,6 +425,22 @@ void applyNonPersistentConfiguration(Logging * logger, engine_configuration_s *e
prepareOutputSignals(engineConfiguration, engineConfiguration2);
// todo: looks like this is here only for unit tests. todo: remove
- initializeIgnitionActions(0, engineConfiguration, engineConfiguration2, 0, &engineConfiguration2->engineEventConfiguration.ignitionEvents[0]);
+ initializeIgnitionActions(0, 0, engineConfiguration, engineConfiguration2,
+ &engineConfiguration2->engineEventConfiguration.ignitionEvents[0]);
+
+}
+
+void setOperationMode(engine_configuration_s *engineConfiguration, operation_mode_e mode) {
+ if (mode == FOUR_STROKE_CAM_SENSOR) {
+ engineConfiguration->rpmMultiplier = 0.5;
+ } else if (mode == FOUR_STROKE_CRANK_SENSOR) {
+ engineConfiguration->rpmMultiplier = 1;
+ }
+}
+
+operation_mode_e getOperationMode( engine_configuration_s const *engineConfiguration) {
+ if(engineConfiguration->rpmMultiplier == 1)
+ return FOUR_STROKE_CRANK_SENSOR;
+ return FOUR_STROKE_CAM_SENSOR;
}
diff --git a/firmware/controllers/algo/engine_configuration.h b/firmware/controllers/algo/engine_configuration.h
index f7ca04c757..71c16bf78d 100644
--- a/firmware/controllers/algo/engine_configuration.h
+++ b/firmware/controllers/algo/engine_configuration.h
@@ -13,7 +13,6 @@
#include "crc.h"
#include "sensor_types.h"
#include "can_header.h"
-#include "event_registry.h"
#include "rusefi_enums.h"
typedef struct {
@@ -29,17 +28,12 @@ typedef struct {
short int crankingRpm;
} cranking_parameters_s;
-/**
- * @brief Here we store information about which injector or spark should be fired when.
- */
-typedef struct {
- ActuatorEventList crankingInjectionEvents;
- ActuatorEventList injectionEvents;
- ActuatorEventList ignitionEvents[2];
-} EventHandlerConfiguration;
-
#define FUEL_RPM_COUNT 16
#define FUEL_LOAD_COUNT 16
+#define VE_RPM_COUNT 16
+#define VE_LOAD_COUNT 16
+#define AFR_RPM_COUNT 16
+#define AFR_LOAD_COUNT 16
#define CLT_CURVE_SIZE 16
#define IAT_CURVE_SIZE 16
@@ -115,6 +109,14 @@ typedef struct {
*/
typedef struct {
float injectorLag; // size 4, offset 0
+ /**
+ * cc/min, cubic centimeter per minute
+ *
+ * By the way, g/s = 0.125997881 * (lb/hr)
+ * g/s = 0.125997881 * (cc/min)/10.5
+ * g/s = 0.0119997981 * cc/min
+ *
+ */
float injectorFlow; // size 4, offset 4
float battInjectorLagCorrBins[VBAT_INJECTOR_CURVE_SIZE]; // size 32, offset 8
float battInjectorLagCorr[VBAT_INJECTOR_CURVE_SIZE]; // size 32, offset 40
@@ -145,9 +147,8 @@ typedef struct {
float sparkDwellBins[DWELL_COUNT]; // offset 580
float sparkDwell[DWELL_COUNT];
- float ignitionTable[IGN_LOAD_COUNT][IGN_RPM_COUNT];
- float ignitionLoadBins[IGN_LOAD_COUNT]; // offset 3450
- float ignitionRpmBins[IGN_RPM_COUNT]; // offset 3542
+ float ignitionLoadBins[IGN_LOAD_COUNT];
+ float ignitionRpmBins[IGN_RPM_COUNT];
/**
* this value could be used to offset the whole ignition timing table by a constant
@@ -170,12 +171,16 @@ typedef struct {
// WARNING: by default, our small enums are ONE BYTE. but if the are surrounded by non-enums - alignments do the trick
engine_type_e engineType;
- float fuelTable[FUEL_LOAD_COUNT][FUEL_RPM_COUNT]; // size 1024, offset 1816
- float fuelLoadBins[FUEL_LOAD_COUNT]; // offset 2840
+ float fuelLoadBins[FUEL_LOAD_COUNT]; //
// RPM is float and not integer in order to use unified methods for interpolation
- float fuelRpmBins[FUEL_RPM_COUNT]; // offset 3542
+ float fuelRpmBins[FUEL_RPM_COUNT]; //
- int unused[3];
+ /**
+ * Engine displacement, in liters
+ * see also cylindersCount
+ */
+ float displacement;
+ int unused[2];
injection_mode_e crankingInjectionMode;
injection_mode_e injectionMode;
@@ -270,8 +275,24 @@ typedef struct {
float diffLoadEnrichmentCoef;
air_pressure_sensor_config_s baroSensor;
+
+ float veLoadBins[VE_LOAD_COUNT];
+ float veRpmBins[VE_RPM_COUNT];
+ float afrLoadBins[AFR_LOAD_COUNT];
+ float afrRpmBins[AFR_RPM_COUNT];
+
+ // the large tables are always in the end - that's related to TunerStudio paging implementation
+ float fuelTable[FUEL_LOAD_COUNT][FUEL_RPM_COUNT]; // size 1024
+ float ignitionTable[IGN_LOAD_COUNT][IGN_RPM_COUNT]; // size 1024
+
+ float veTable[VE_LOAD_COUNT][VE_RPM_COUNT]; // size 1024
+ float afrTable[AFR_LOAD_COUNT][AFR_RPM_COUNT]; // size 1024
+
} engine_configuration_s;
+void setOperationMode(engine_configuration_s *engineConfiguration, operation_mode_e mode);
+operation_mode_e getOperationMode(engine_configuration_s const *engineConfiguration);
+
#define HW_MAX_ADC_INDEX 16
typedef struct {
@@ -326,6 +347,14 @@ typedef struct {
brain_pin_e primaryLogicAnalyzerPin;
brain_pin_e secondaryLogicAnalyzerPin;
+ int idleThreadPeriod;
+ int consoleLoopPeriod;
+ int lcdThreadPeriod;
+ int tunerStudioThreadPeriod;
+ int generalPeriodicThreadPeriod;
+
+ int tunerStudioSerialSpeed;
+
} board_configuration_s;
typedef struct {
@@ -350,6 +379,7 @@ 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(engine_configuration_s *engineConfiguration, float synchGap);
void incrementGlobalConfigurationVersion(void);
int getGlobalConfigurationVersion(void);
diff --git a/firmware/controllers/algo/error_handling.h b/firmware/controllers/algo/error_handling.h
index a090d25534..47e7b0421d 100644
--- a/firmware/controllers/algo/error_handling.h
+++ b/firmware/controllers/algo/error_handling.h
@@ -28,11 +28,17 @@ int warning(obd_code_e code, const char *fmt, ...);
* todo: better method name?
*/
void firmwareError(const char *fmt, ...);
-bool_t hasFirmwareError(void);
+bool hasFirmwareError(void);
-bool_t hasFatalError(void);
-void fatal3(char *msg, char *file, int line);
-#define fatal(x) (fatal3(x, __FILE__, __LINE__));
+/**
+ * declared as a macro so that this code does not use stack
+ * so that it would not crash the error handler in case of stack issues
+ */
+#if CH_DBG_SYSTEM_STATE_CHECK
+#define hasFatalError() (dbg_panic_msg != NULL)
+#else
+#define hasFatalError() (FALSE)
+#endif
void chDbgPanic3(const char *msg, const char * file, int line);
@@ -45,7 +51,10 @@ int getRusEfiVersion(void);
* @deprecated Global panic is inconvenient because it's hard to deliver the error message while whole instance
* is stopped. Please use firmwareWarning() instead
*/
-#define efiAssert(x, y) chDbgAssert(x, y, NULL)
+#define efiAssert(condition, message, result) { if (!(condition)) { firmwareError(message); return result; } }
+
+#define efiAssertVoid(condition, message) { if (!(condition)) { firmwareError(message); return; } }
+
#ifdef __cplusplus
}
diff --git a/firmware/controllers/algo/event_registry.c b/firmware/controllers/algo/event_registry.c
deleted file mode 100644
index ceb89b6561..0000000000
--- a/firmware/controllers/algo/event_registry.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * @file event_registry.c
- * @brief This data structure knows when to do what
- *
- * @date Nov 27, 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 "event_registry.h"
-#include "main.h"
-#include "engine_math.h"
-
-extern engine_configuration_s *engineConfiguration;
-//extern engine_configuration2_s *engineConfiguration2;
-
-void resetEventList(ActuatorEventList *list) {
- list->size = 0;
-}
-
-static void copyActuatorEvent(ActuatorEvent *source, ActuatorEvent*target) {
- target->eventIndex = source->eventIndex;
- target->actuator = source->actuator;
- target->angleOffset = source->angleOffset;
-}
-
-void registerActuatorEvent(ActuatorEventList *list, int eventIndex, OutputSignal *actuator, float angleOffset) {
- if (list->size == MAX_EVENT_COUNT) {
- fatal("registerActuatorEvent() too many events");
- return;
- }
- ActuatorEvent *e = &list->events[list->size++];
- e->eventIndex = eventIndex;
- e->actuator = actuator;
- e->angleOffset = angleOffset;
-}
-
-void findEvents(int eventIndex, ActuatorEventList *source, ActuatorEventList *target) {
- resetEventList(target);
- // todo: implement something faster
- for (int i = 0; i < source->size; i++) {
- ActuatorEvent *s = &source->events[i];
- if (s->eventIndex != eventIndex)
- continue;
- // todo: migrate to pointers instead of copying an object?
- copyActuatorEvent(s, &target->events[target->size++]);
- }
-}
diff --git a/firmware/controllers/algo/event_registry.cpp b/firmware/controllers/algo/event_registry.cpp
new file mode 100644
index 0000000000..82407f11a3
--- /dev/null
+++ b/firmware/controllers/algo/event_registry.cpp
@@ -0,0 +1,34 @@
+/**
+ * @file event_registry.cpp
+ * @brief This data structure knows when to do what
+ *
+ * @date Nov 27, 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 "event_registry.h"
+#include "main.h"
+#include "engine_math.h"
+
+void registerActuatorEvent(ActuatorEventList *list, int eventIndex, OutputSignal *actuator, float angleOffset) {
+ ActuatorEvent *e = list->getNextActuatorEvent();
+ if (e == NULL)
+ return; // error already reported
+ e->position.eventIndex = eventIndex;
+ e->actuator = actuator;
+ e->position.angleOffset = angleOffset;
+}
diff --git a/firmware/controllers/algo/event_registry.h b/firmware/controllers/algo/event_registry.h
index 7aa41c6c71..1a8fb94a73 100644
--- a/firmware/controllers/algo/event_registry.h
+++ b/firmware/controllers/algo/event_registry.h
@@ -13,24 +13,60 @@
#define MAX_EVENT_COUNT 40
+/**
+ * This structure defines an angle position within the trigger
+ */
typedef struct {
+ /**
+ * That's trigger event index
+ */
int eventIndex;
- OutputSignal *actuator;
+ float eventAngle;
+ /**
+ * Angle offset from the trigger event
+ */
float angleOffset;
+} event_trigger_position_s;
+
+typedef struct {
+ event_trigger_position_s position;
+ OutputSignal *actuator;
+ scheduling_s signalTimer;
} ActuatorEvent;
-typedef struct {
+typedef struct IgnitionEvent_struct IgnitionEvent;
+
+struct IgnitionEvent_struct {
+ ActuatorEvent actuator;
+ float advance;
+ event_trigger_position_s sparkPosition;
+ IgnitionEvent *next;
+ char *name;
+};
+
+template
+class ArrayList {
+public:
int size;
- ActuatorEvent events[MAX_EVENT_COUNT];
-} ActuatorEventList;
+ Type events[Dimention];
+ void resetEventList(void);
+ Type *getNextActuatorEvent(void);
+};
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
+template
+void ArrayList< Type, Dimention>::resetEventList(void) {
+ size = 0;
+}
+template
+Type * ArrayList< Type, Dimention>::getNextActuatorEvent(void) {
+ efiAssert(size < Dimention, "registerActuatorEvent() too many events", NULL);
+ return &events[size++];
+}
-void resetEventList(ActuatorEventList *list);
+typedef ArrayList ActuatorEventList;
+
+typedef ArrayList IgnitionEventList;
/**
* this is an intermediate implementation of flexible event handling.
@@ -42,11 +78,4 @@ void resetEventList(ActuatorEventList *list);
*/
void registerActuatorEvent(ActuatorEventList *list, int eventIndex, OutputSignal *actuator, float angleOffset);
-void findEvents(int eventIndex, ActuatorEventList *source, ActuatorEventList *target);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-
#endif /* EVENT_REGISTRY_H_ */
diff --git a/firmware/controllers/algo/fuel_math.cpp b/firmware/controllers/algo/fuel_math.cpp
index 313212ad45..f1c448f02a 100644
--- a/firmware/controllers/algo/fuel_math.cpp
+++ b/firmware/controllers/algo/fuel_math.cpp
@@ -40,19 +40,17 @@
#include "accel_enrichment.h"
#endif /* EFI_ACCEL_ENRICHMENT */
-static float *fuel_ptrs[FUEL_LOAD_COUNT];
-static int initialized = FALSE;
extern engine_configuration_s *engineConfiguration;
+static Map3D1616 fuelMap;
+
/**
* @brief Initialize fuel map data structure
* @note this method has nothing to do with fuel map VALUES - it's job
* is to prepare the fuel map data structure for 3d interpolation
*/
void prepareFuelMap(void) {
- for (int k = 0; k < FUEL_LOAD_COUNT; k++)
- fuel_ptrs[k] = engineConfiguration->fuelTable[k];
- initialized = TRUE;
+ fuelMap.init(engineConfiguration->fuelTable);
}
/**
@@ -86,11 +84,9 @@ float getInjectorLag(float vBatt) {
}
float getBaseFuel(int rpm, float engineLoad) {
- chDbgCheck(initialized, "fuel map initialized");
- efiAssert(!cisnan(engineLoad), "invalid el");
- efiAssert(!cisnan(engineLoad), "invalid rpm");
- return interpolate3d(engineLoad, engineConfiguration->fuelLoadBins, FUEL_LOAD_COUNT, rpm,
- engineConfiguration->fuelRpmBins, FUEL_RPM_COUNT, fuel_ptrs);
+ efiAssert(!cisnan(engineLoad), "invalid el", NAN);
+ return fuelMap.getValue(engineLoad, engineConfiguration->fuelLoadBins, rpm,
+ engineConfiguration->fuelRpmBins);
}
float getCrankingFuel(void) {
@@ -157,7 +153,7 @@ inline static int getElectricalValue1(pin_output_mode_e mode) {
// todo: this method is here for unit test visibility. todo: move to a bette place!
int getElectricalValue(int logicalValue, pin_output_mode_e mode) {
- chDbgCheck(mode <= OM_OPENDRAIN_INVERTED, "invalid pin_output_mode_e");
+ efiAssert(mode <= OM_OPENDRAIN_INVERTED, "invalid pin_output_mode_e", -1);
return logicalValue ? getElectricalValue1(mode) : getElectricalValue0(mode);
}
diff --git a/firmware/controllers/algo/io_pins.h b/firmware/controllers/algo/io_pins.h
index 3364cbf698..a1d869c206 100644
--- a/firmware/controllers/algo/io_pins.h
+++ b/firmware/controllers/algo/io_pins.h
@@ -13,7 +13,7 @@
#define GPIO_NULL NULL
typedef enum {
- LED_CRANKING, // Orange on-board LED
+ LED_WARNING, // Orange on-board LED
LED_RUNNING, // Green on-board LED
LED_ERROR, // Red on-board LED
LED_COMMUNICATION_1, // Blue on-board LED
diff --git a/firmware/controllers/algo/main_trigger_callback.h b/firmware/controllers/algo/main_trigger_callback.h
index 8304b44bb1..3e63f35eb5 100644
--- a/firmware/controllers/algo/main_trigger_callback.h
+++ b/firmware/controllers/algo/main_trigger_callback.h
@@ -15,14 +15,31 @@
#define MAX_INJECTOR_COUNT 12
#define MAX_IGNITER_COUNT 4
+#ifdef __cplusplus
+#include "engine_configuration.h"
+#include "ec2.h"
+#include "event_registry.h"
+class MainTriggerCallback {
+public:
+// MainTriggerCallback();
+ void init(engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2);
+
+ engine_configuration_s *engineConfiguration;
+ engine_configuration2_s *engineConfiguration2;
+
+};
+void initMainEventListener(engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2);
+void onTriggerEvent(trigger_event_e ckpSignalType, int eventIndex, MainTriggerCallback *mainTriggerCallback);
+#endif
+
+
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
-#include "event_registry.h"
-void initMainEventListener(void);
+
void showMainHistogram(void);
void onEveryMillisecondTimerSignal(void);
int isIgnitionTimingError(void);
diff --git a/firmware/controllers/algo/malfunction_central.c b/firmware/controllers/algo/malfunction_central.c
index 7ea1dadc43..20bdc9803c 100644
--- a/firmware/controllers/algo/malfunction_central.c
+++ b/firmware/controllers/algo/malfunction_central.c
@@ -55,6 +55,6 @@ void getErrorCodes(error_codes_set_s * copy) {
copy->error_codes[i] = error_codes_set.error_codes[i];
}
-bool_t hasErrorCodes(void) {
+bool hasErrorCodes(void) {
return error_codes_set.count > 0;
}
diff --git a/firmware/controllers/algo/malfunction_central.h b/firmware/controllers/algo/malfunction_central.h
index 4ce78674c8..97babfc0ad 100644
--- a/firmware/controllers/algo/malfunction_central.h
+++ b/firmware/controllers/algo/malfunction_central.h
@@ -48,11 +48,10 @@ void setError(int flag, obd_code_e errorCode);
*/
void getErrorCodes(error_codes_set_s * buffer);
-bool_t hasErrorCodes(void);
+bool hasErrorCodes(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
-
#endif /* MALFUNCTION_CENTRAL_H_ */
diff --git a/firmware/controllers/algo/rusefi_enums.h b/firmware/controllers/algo/rusefi_enums.h
index 5143e0ad91..34e450cd25 100644
--- a/firmware/controllers/algo/rusefi_enums.h
+++ b/firmware/controllers/algo/rusefi_enums.h
@@ -97,7 +97,7 @@ typedef enum {
SHAFT_PRIMARY_DOWN = 1,
SHAFT_SECONDARY_UP = 2,
SHAFT_SECONDARY_DOWN = 3,
-} ShaftEvents;
+} trigger_event_e;
/**
* This enum is used to select your desired Engine Load calculation algorithm
@@ -179,6 +179,14 @@ typedef enum {
Internal_ForceMyEnumIntSize_firing_order = ENUM_SIZE_HACK,
} firing_order_e;
+// todo: better enum name
+typedef enum {
+ FOUR_STROKE_CRANK_SENSOR = 0,
+ FOUR_STROKE_CAM_SENSOR = 1,
+
+ Internal_ForceMyEnumIntSize_operation_mode_e = ENUM_SIZE_HACK,
+} operation_mode_e;
+
/**
* @brief Ignition Mode
*/
diff --git a/firmware/controllers/algo/signal_executor.c b/firmware/controllers/algo/signal_executor.c
index 05edc17060..3beb39b8b4 100644
--- a/firmware/controllers/algo/signal_executor.c
+++ b/firmware/controllers/algo/signal_executor.c
@@ -56,12 +56,10 @@ void initOutputSignal(OutputSignal *signal, io_pin_e ioPin) {
}
void initOutputSignalBase(OutputSignal *signal) {
- signal->status = IDLE;
-// signal->last_scheduling_time = 0;
signal->initialized = TRUE;
}
-static void turnHigh(OutputSignal *signal) {
+void turnPinHigh(OutputSignal *signal) {
#if EFI_DEFAILED_LOGGING
// signal->hi_time = hTimeNow();
#endif /* EFI_DEFAILED_LOGGING */
@@ -85,7 +83,7 @@ static void turnHigh(OutputSignal *signal) {
#endif /* EFI_WAVE_ANALYZER */
}
-static void turnLow(OutputSignal *signal) {
+void turnPinLow(OutputSignal *signal) {
// turn off the output
// todo: this XOR should go inside the setOutputPinValue method
setOutputPinValue(signal->io_pin, FALSE);
@@ -126,8 +124,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) &turnHigh, (void *) signal);
- scheduleTask(sDown, (int)MS2US(delayMs + durationMs), (schfunc_t) &turnLow, (void*) signal);
+ scheduleTask(sUp, (int)MS2US(delayMs), (schfunc_t) &turnPinHigh, (void *) signal);
+ scheduleTask(sDown, (int)MS2US(delayMs + durationMs), (schfunc_t) &turnPinLow, (void*) signal);
// signal->last_scheduling_time = now;
}
@@ -137,7 +135,7 @@ void scheduleOutputBase(OutputSignal *signal, float delayMs, float durationMs) {
* it's better to check for the exact 'TRUE' value since otherwise
* we would accept any memory garbage
*/
- chDbgCheck(signal->initialized == TRUE, "Signal not initialized");
+ efiAssertVoid(signal->initialized == TRUE, "Signal not initialized");
// signal->offset = offset;
// signal->duration = duration;
}
diff --git a/firmware/controllers/algo/signal_executor.h b/firmware/controllers/algo/signal_executor.h
index adfb53ee0b..919e63a17d 100644
--- a/firmware/controllers/algo/signal_executor.h
+++ b/firmware/controllers/algo/signal_executor.h
@@ -23,10 +23,6 @@
#include "signal_executor_sleep.h"
#endif /* EFI_SIGNAL_EXECUTOR_SLEEP */
-typedef enum {
- IDLE = 0, ACTIVE
-} executor_status_t;
-
/**
* @brief Asynchronous output signal data structure
*/
@@ -40,19 +36,14 @@ struct OutputSignal_struct {
int initialized;
/**
- * We are alternating instances so that events which extend into next revolution are not overriden while
+ * We are alternating instances so that events which extend into next revolution are not reused while
* scheduling next revolution events
*/
scheduling_s signalTimerUp[2];
scheduling_s signalTimerDown[2];
- executor_status_t status;
-
-#if EFI_SIGNAL_EXECUTOR_HW_TIMER
- // todo
-#endif
-
-// OutputSignal *next;
+ scheduling_s triggerEvent;
+ float angleOffsetParam;
};
#ifdef __cplusplus
@@ -65,6 +56,9 @@ void scheduleOutput(OutputSignal *signal, float delayMs, float durationMs);
void initOutputSignalBase(OutputSignal *signal);
void scheduleOutputBase(OutputSignal *signal, float delayMs, float durationMs);
+void turnPinHigh(OutputSignal *signal);
+void turnPinLow(OutputSignal *signal);
+
void initSignalExecutor(void);
void initSignalExecutorImpl(void);
void scheduleByAngle(scheduling_s *timer, float angle, schfunc_t callback, void *param);
diff --git a/firmware/controllers/algo/wave_chart.c b/firmware/controllers/algo/wave_chart.c
index b5478d0ea0..e32e40683a 100644
--- a/firmware/controllers/algo/wave_chart.c
+++ b/firmware/controllers/algo/wave_chart.c
@@ -40,9 +40,11 @@
*/
#if EFI_PROD_CODE
static volatile int chartSize = 100;
+#define WAVE_LOGGING_SIZE 5000
#else
// need more events for automated test
-static volatile int chartSize = 200;
+static volatile int chartSize = 400;
+#define WAVE_LOGGING_SIZE 35000
#endif
static int isChartActive = TRUE;
@@ -65,11 +67,7 @@ void resetWaveChart(WaveChart *chart) {
appendPrintf(&chart->logging, "wave_chart%s", DELIMETER);
}
-#if defined __GNUC__
-static char WAVE_LOGGING_BUFFER[5000] __attribute__((section(".ccm")));
-#else
-static char WAVE_LOGGING_BUFFER[5000];
-#endif
+static char WAVE_LOGGING_BUFFER[WAVE_LOGGING_SIZE] CCM_OPTIONAL;
static void printStatus(void) {
scheduleIntValue(&logger, "chart", isChartActive);
@@ -113,13 +111,13 @@ void publishChart(WaveChart *chart) {
* @brief Register a change in sniffed signal
*/
void addWaveChartEvent3(WaveChart *chart, const char *name, const char * msg, const char * msg2) {
- chDbgCheck(chart->isInitialized, "chart not initialized");
+ efiAssertVoid(chart->isInitialized, "chart not initialized");
#if DEBUG_WAVE
scheduleSimpleMsg(&debugLogging, "current", chart->counter);
#endif
if (isWaveChartFull(chart))
return;
- bool_t alreadyLocked = lockOutputBuffer(); // we have multiple threads writing to the same output buffer
+ bool alreadyLocked = lockOutputBuffer(); // we have multiple threads writing to the same output buffer
appendPrintf(&chart->logging, "%s%s%s%s", name, CHART_DELIMETER, msg, CHART_DELIMETER);
int time100 = getTimeNowUs() / 10;
appendPrintf(&chart->logging, "%d%s%s", time100, msg2, CHART_DELIMETER);
diff --git a/firmware/controllers/alternatorController.cpp b/firmware/controllers/alternatorController.cpp
index 3420f9d140..6d69acf09e 100644
--- a/firmware/controllers/alternatorController.cpp
+++ b/firmware/controllers/alternatorController.cpp
@@ -24,7 +24,7 @@ extern board_configuration_s *boardConfiguration;
static PwmConfig alternatorControl;
-static WORKING_AREA(ivThreadStack, UTILITY_THREAD_STACK_SIZE);
+static THD_WORKING_AREA(ivThreadStack, UTILITY_THREAD_STACK_SIZE);
static msg_t AltCtrlThread(int param) {
chRegSetThreadName("AlternatorController");
diff --git a/firmware/controllers/controllers.mk b/firmware/controllers/controllers.mk
index 24674a03f7..3c6995a100 100644
--- a/firmware/controllers/controllers.mk
+++ b/firmware/controllers/controllers.mk
@@ -2,12 +2,12 @@
CONTROLLERSSRC = \
controllers/ignition_central.c \
$(PROJECT_DIR)/controllers/malfunction_indicator.c \
- $(PROJECT_DIR)/controllers/error_handling.c \
- controllers/map_averaging.c \
- controllers/map_multiplier_thread.c
+ $(PROJECT_DIR)/controllers/error_handling.c
CONTROLLERS_SRC_CPP = $(PROJECT_DIR)/controllers/settings.cpp \
controllers/electronic_throttle.cpp \
+ controllers/map_averaging.cpp \
+ controllers/map_multiplier_thread.cpp \
controllers/flash_main.cpp \
controllers/injector_central.cpp \
controllers/idle_thread.cpp \
diff --git a/firmware/controllers/core/EfiWave.cpp b/firmware/controllers/core/EfiWave.cpp
index a2c6b783f9..c86175c925 100644
--- a/firmware/controllers/core/EfiWave.cpp
+++ b/firmware/controllers/core/EfiWave.cpp
@@ -7,6 +7,7 @@
#include "main.h"
#include "EfiWave.h"
+#include "trigger_structure.h"
single_wave_s::single_wave_s() {
init(NULL);
@@ -20,16 +21,40 @@ void single_wave_s::init(int *pinStates) {
this->pinStates = pinStates;
}
+multi_wave_s::multi_wave_s() {
+}
+
multi_wave_s::multi_wave_s(float *switchTimes, single_wave_s *waves) {
+ init(switchTimes, waves);
+}
+
+void multi_wave_s::init(float *switchTimes, single_wave_s *waves) {
this->switchTimes = switchTimes;
this->waves = waves;
}
void multi_wave_s::reset(void) {
- phaseCount = 0;
waveCount = 0;
}
+float multi_wave_s::getAngle(int index, engine_configuration_s const *engineConfiguration, trigger_shape_s * s) const {
+ if (getOperationMode(engineConfiguration) == FOUR_STROKE_CAM_SENSOR)
+ return getSwitchTime(index) * 720.0;
+ /**
+ * FOUR_STROKE_CRANK_SENSOR magic:
+ * We have two crank shaft revolutions for each engine cycle
+ * See also trigger_central.cpp
+ * See also getEngineCycleEventCount()
+ */
+ int triggerEventCounter = s->getSize();
+
+ if (index < triggerEventCounter) {
+ return getSwitchTime(index) * 360.0;
+ } else {
+ return 360 + getSwitchTime(index - triggerEventCounter) * 360.0;
+ }
+}
+
float multi_wave_s::getSwitchTime(int index) const {
return switchTimes[index];
}
diff --git a/firmware/controllers/core/EfiWave.h b/firmware/controllers/core/EfiWave.h
index 3f86ee4df9..7489498b46 100644
--- a/firmware/controllers/core/EfiWave.h
+++ b/firmware/controllers/core/EfiWave.h
@@ -7,7 +7,9 @@
#ifndef EFI_WAVE_H_
#define EFI_WAVE_H_
-#define PWM_PHASE_MAX_COUNT 150
+#include "engine_configuration.h"
+
+#define PWM_PHASE_MAX_COUNT 250
#define PWM_PHASE_MAX_WAVE_PER_PWM 2
/**
@@ -21,18 +23,19 @@ public:
int *pinStates;
};
+class trigger_shape_s;
+
class multi_wave_s {
public:
+ multi_wave_s();
multi_wave_s(float *st, single_wave_s *waves);
+ void init(float *st, single_wave_s *waves);
void reset(void);
float getSwitchTime(int phaseIndex) const;
+ float getAngle(int phaseIndex, engine_configuration_s const *engineConfiguration, trigger_shape_s * s) const;
void setSwitchTime(int phaseIndex, float value);
void checkSwitchTimes(int size);
int getChannelState(int channelIndex, int phaseIndex) const;
- /**
- * Number of events in the cycle
- */
- int phaseCount;
/**
* Number of signal wires
*/
diff --git a/firmware/controllers/core/core.mk b/firmware/controllers/core/core.mk
index a4f3a689bf..9c12789e90 100644
--- a/firmware/controllers/core/core.mk
+++ b/firmware/controllers/core/core.mk
@@ -1,6 +1,6 @@
-CONTROLLERS_CORE_SRC = $(PROJECT_DIR)/controllers/core/interpolation.c \
- $(PROJECT_DIR)/controllers/core/avg_values.c
+CONTROLLERS_CORE_SRC = $(PROJECT_DIR)/controllers/core/avg_values.c
-CONTROLLERS_CORE_SRC_CPP = $(PROJECT_DIR)/controllers/core/EfiWave.cpp
+CONTROLLERS_CORE_SRC_CPP = $(PROJECT_DIR)/controllers/core/EfiWave.cpp \
+ $(PROJECT_DIR)/controllers/core/interpolation.cpp \
diff --git a/firmware/controllers/core/interpolation.c b/firmware/controllers/core/interpolation.cpp
similarity index 90%
rename from firmware/controllers/core/interpolation.c
rename to firmware/controllers/core/interpolation.cpp
index 2d1a1f80d7..776d2f33b9 100644
--- a/firmware/controllers/core/interpolation.c
+++ b/firmware/controllers/core/interpolation.cpp
@@ -1,5 +1,5 @@
/**
- * @file interpolation.c
+ * @file interpolation.cpp
* @brief Linear interpolation algorithms
*
* @date Oct 17, 2013
@@ -14,12 +14,30 @@
#include "main.h"
#include "interpolation.h"
-//#include "engine_math.h"
#define INTERPOLATION_A(x1, y1, x2, y2) ((y1 - y2) / (x1 - x2))
int needInterpolationLogging = TRUE;
+
+FastInterpolation::FastInterpolation(float x1, float y1, float x2, float y2) {
+ init(x1, y1, x2, y2);
+}
+
+void FastInterpolation::init(float x1, float y1, float x2, float y2) {
+ if (x1 == x2) {
+ firmwareError("Same x1 and x2 in interpolate: %f/%f", x1, x2);
+ return;
+ }
+ a = INTERPOLATION_A(x1, y1, x2, y2);
+ b = y1 - a * x1;
+}
+
+float FastInterpolation::getValue(float x) {
+ return a * x + b;
+}
+
+
/** @brief Linear interpolation by two points
*
* @param x1 key of the first point
@@ -39,7 +57,7 @@ float interpolate(float x1, float y1, float x2, float y2, float x) {
// a*x1 + b = y1
// a*x2 + b = y2
-// chDbgCheck(x1 != x2, "no way we can interpolate");
+// efiAssertVoid(x1 != x2, "no way we can interpolate");
float a = INTERPOLATION_A(x1, y1, x2, y2);
float b = y1 - a * x1;
float result = a * x + b;
@@ -55,8 +73,7 @@ float interpolate(float x1, float y1, float x2, float y2, float x) {
* @note If the parameter is smaller than the first element of the array, -1 is returned.
*/
int findIndex(float array[], int size, float value) {
- if (cisnan(value))
- fatal("NaN in findIndex\r\n");
+ efiAssert(!cisnan(value), "NaN in findIndex", 0);
if (value < array[0])
return -1;
@@ -67,7 +84,7 @@ int findIndex(float array[], int size, float value) {
while (1) {
if (size-- == 0)
- fatal("Unexpected state in binary search.");
+ efiAssert(FALSE, "Unexpected state in binary search", 0);
middle = (left + right) / 2;
diff --git a/firmware/controllers/core/interpolation.h b/firmware/controllers/core/interpolation.h
index 5f61f23b76..856705830b 100644
--- a/firmware/controllers/core/interpolation.h
+++ b/firmware/controllers/core/interpolation.h
@@ -8,19 +8,19 @@
#ifndef INTERPOLATION_3D_H_
#define INTERPOLATION_3D_H_
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
int findIndex(float array[], int size, float value);
float interpolate(float x1, float y1, float x2, float y2, float x);
float interpolate2d(float value, float bin[], float values[], int size);
float interpolate3d(float x, float xBin[], int xBinSize, float y, float yBin[], int yBinSize, float* map[]);
void setTableValue(float bins[], float values[], int size, float key, float value);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+class FastInterpolation {
+public:
+ FastInterpolation(float x1, float y1, float x2, float y2);
+ void init(float x1, float y1, float x2, float y2);
+ float getValue(float x);
+private:
+ float a, b;
+};
#endif /* INTERPOLATION_3D_H_ */
diff --git a/firmware/controllers/electronic_throttle.cpp b/firmware/controllers/electronic_throttle.cpp
index fb6fb7a411..f8024535bc 100644
--- a/firmware/controllers/electronic_throttle.cpp
+++ b/firmware/controllers/electronic_throttle.cpp
@@ -38,11 +38,11 @@ static Logging logger;
/**
* @brief Control Thread stack
*/
-static WORKING_AREA(etbTreadStack, UTILITY_THREAD_STACK_SIZE);
+static THD_WORKING_AREA(etbTreadStack, UTILITY_THREAD_STACK_SIZE);
/**
* @brief Pulse-Width Modulation state
*/
-static PwmConfig etbPwm;
+static SimplePwm etbPwm;
static float prevTps;
@@ -69,7 +69,7 @@ static msg_t etbThread(void *arg) {
static void setThrottleConsole(int level) {
scheduleMsg(&logger, "setting throttle=%d", level);
- etbPwm.multiWave.switchTimes[0] = 0.01 + (min(level, 98)) / 100.0;
+ etbPwm.multiWave.switchTimes[0] = 0.01 + (minI(level, 98)) / 100.0;
print("st = %f\r\n", etbPwm.multiWave.switchTimes[0]);
}
@@ -84,11 +84,11 @@ void initElectronicThrottle(void) {
// outputPinRegister("etb2", ELECTRONIC_THROTTLE_CONTROL_2, ETB_CONTROL_LINE_2_PORT, ETB_CONTROL_LINE_2_PIN);
// this line used for PWM
- startSimplePwm(&etbPwm, "etb",
+ startSimplePwmExt(&etbPwm, "etb",
boardConfiguration->electronicThrottlePin1,
ELECTRONIC_THROTTLE_CONTROL_1,
- 0.80,
- 500);
+ 500,
+ 0.80);
addConsoleActionI("e", setThrottleConsole);
chThdCreateStatic(etbTreadStack, sizeof(etbTreadStack), NORMALPRIO, (tfunc_t) etbThread, NULL);
diff --git a/firmware/controllers/engine_controller.cpp b/firmware/controllers/engine_controller.cpp
index 4c3d36fe83..66f2f7c43f 100644
--- a/firmware/controllers/engine_controller.cpp
+++ b/firmware/controllers/engine_controller.cpp
@@ -50,15 +50,11 @@
#include "efilib2.h"
#include "ec2.h"
#include "PwmTester.h"
+#include "engine.h"
+extern board_configuration_s *boardConfiguration;
-#define _10_MILLISECONDS (10 * TICKS_IN_MS)
-
-#if defined __GNUC__
-persistent_config_container_s persistentState __attribute__((section(".ccm")));
-#else
-persistent_config_container_s persistentState;
-#endif
+persistent_config_container_s persistentState CCM_OPTIONAL;
engine_configuration_s *engineConfiguration = &persistentState.persistentConfiguration.engineConfiguration;
board_configuration_s *boardConfiguration = &persistentState.persistentConfiguration.boardConfiguration;
@@ -73,11 +69,18 @@ static VirtualTimer fuelPumpTimer;
static Logging logger;
-static engine_configuration2_s ec2;
+static engine_configuration2_s ec2 CCM_OPTIONAL;
engine_configuration2_s * engineConfiguration2 = &ec2;
+static configuration_s cfg = {&persistentState.persistentConfiguration.engineConfiguration, &ec2};
+
+configuration_s * configuration = &cfg;
+
+Engine engine;
+
static msg_t csThread(void) {
chRegSetThreadName("status");
+#if EFI_SHAFT_POSITION_INPUT
while (TRUE) {
int is_cranking = isCranking();
int is_running = getRpm() > 0 && !is_cranking;
@@ -93,6 +96,8 @@ static msg_t csThread(void) {
chThdSleepMilliseconds(100);
}
}
+#endif /* EFI_SHAFT_POSITION_INPUT */
+ return -1;
}
static void updateErrorCodes(void) {
@@ -129,14 +134,12 @@ static void fanRelayControl(void) {
Overflow64Counter halTime;
uint64_t getTimeNowUs(void) {
- // todo: synchronization? multi-threading?
- halTime.offer(hal_lld_get_counter_value());
- return halTime.get() / (CORE_CLOCK / 1000000);
+ return halTime.get(hal_lld_get_counter_value(), false) / (CORE_CLOCK / 1000000);
}
-uint64_t getHalTimer(void) {
- return halTime.get();
-}
+//uint64_t getHalTimer(void) {
+// return halTime.get();
+//}
efitimems_t currentTimeMillis(void) {
// todo: migrate to getTimeNowUs? or not?
@@ -147,23 +150,23 @@ int getTimeNowSeconds(void) {
return chTimeNow() / CH_FREQUENCY;
}
-static void onEveny10Milliseconds(void *arg) {
+static void onEvenyGeneralMilliseconds(void *arg) {
/**
* We need to push current value into the 64 bit counter often enough so that we do not miss an overflow
*/
- halTime.offer(hal_lld_get_counter_value());
+ halTime.get(hal_lld_get_counter_value(), true);
updateErrorCodes();
fanRelayControl();
// schedule next invocation
- chVTSetAny(&everyMsTimer, _10_MILLISECONDS, &onEveny10Milliseconds, 0);
+ chVTSetAny(&everyMsTimer, boardConfiguration->generalPeriodicThreadPeriod * TICKS_IN_MS, &onEvenyGeneralMilliseconds, 0);
}
static void initPeriodicEvents(void) {
// schedule first invocation
- chVTSetAny(&everyMsTimer, _10_MILLISECONDS, &onEveny10Milliseconds, 0);
+ chVTSetAny(&everyMsTimer, boardConfiguration->generalPeriodicThreadPeriod * TICKS_IN_MS, &onEvenyGeneralMilliseconds, 0);
}
static void fuelPumpOff(void *arg) {
@@ -173,7 +176,7 @@ static void fuelPumpOff(void *arg) {
turnOutputPinOff(FUEL_PUMP_RELAY);
}
-static void fuelPumpOn(ShaftEvents signal, int index) {
+static void fuelPumpOn(trigger_event_e signal, int index, void *arg) {
if (index != 0)
return; // let's not abuse the timer - one time per revolution would be enough
// todo: the check about GPIO_NONE should be somewhere else!
@@ -191,26 +194,27 @@ static void fuelPumpOn(ShaftEvents signal, int index) {
}
static void initFuelPump(void) {
- addTriggerEventListener(&fuelPumpOn, "fuel pump");
- fuelPumpOn(SHAFT_PRIMARY_UP, 0);
+ addTriggerEventListener(&fuelPumpOn, "fuel pump", NULL);
+ fuelPumpOn(SHAFT_PRIMARY_UP, 0, NULL);
}
-char * getPinNameByAdcChannel(int hwChannel, uint8_t *buffer) {
+char * getPinNameByAdcChannel(int hwChannel, char *buffer) {
strcpy((char*) buffer, portname(getAdcChannelPort(hwChannel)));
itoa10(&buffer[2], getAdcChannelPin(hwChannel));
return (char*) buffer;
}
-static uint8_t pinNameBuffer[16];
+static char pinNameBuffer[16];
static void printAnalogChannelInfoExt(const char *name, int hwChannel,
- float voltage) {
- scheduleMsg(&logger, "%s ADC%d %s value=%fv", name, hwChannel,
- getPinNameByAdcChannel(hwChannel, pinNameBuffer), voltage);
+ float adcVoltage) {
+ float voltage = adcVoltage * engineConfiguration->analogInputDividerCoefficient;
+ scheduleMsg(&logger, "%s ADC%d %s rawValue=%f/divided=%fv", name, hwChannel,
+ getPinNameByAdcChannel(hwChannel, pinNameBuffer), adcVoltage, voltage);
}
static void printAnalogChannelInfo(const char *name, int hwChannel) {
- printAnalogChannelInfoExt(name, hwChannel, getVoltageDivided(hwChannel));
+ printAnalogChannelInfoExt(name, hwChannel, getVoltage(hwChannel));
}
static void printAnalogInfo(void) {
@@ -225,7 +229,7 @@ static void printAnalogInfo(void) {
getVBatt());
}
-static WORKING_AREA(csThreadStack, UTILITY_THREAD_STACK_SIZE);// declare thread stack
+static THD_WORKING_AREA(csThreadStack, UTILITY_THREAD_STACK_SIZE);// declare thread stack
void initEngineContoller(void) {
if (hasFirmwareError())
@@ -246,11 +250,13 @@ void initEngineContoller(void) {
initWaveAnalyzer();
#endif /* EFI_WAVE_ANALYZER */
+#if EFI_SHAFT_POSITION_INPUT
/**
* there is an implicit dependency on the fact that 'tachometer' listener is the 1st listener - this case
* other listeners can access current RPM value
*/
initRpmCalculator();
+#endif /* EFI_SHAFT_POSITION_INPUT */
#if EFI_TUNER_STUDIO
startTunerStudioConnectivity();
@@ -283,7 +289,7 @@ void initEngineContoller(void) {
/**
* This method initialized the main listener which actually runs injectors & ignition
*/
- initMainEventListener();
+ initMainEventListener(engineConfiguration, engineConfiguration2);
#endif /* EFI_ENGINE_CONTROL */
#if EFI_IDLE_CONTROL
diff --git a/firmware/controllers/engine_controller.h b/firmware/controllers/engine_controller.h
index 58ce571d9f..2452d32adb 100644
--- a/firmware/controllers/engine_controller.h
+++ b/firmware/controllers/engine_controller.h
@@ -18,7 +18,7 @@ extern "C"
{
#endif /* __cplusplus */
-char * getPinNameByAdcChannel(int hwChannel, uint8_t *buffer);
+char * getPinNameByAdcChannel(int hwChannel, char *buffer);
void initEngineContoller(void);
#ifdef __cplusplus
diff --git a/firmware/controllers/error_handling.c b/firmware/controllers/error_handling.c
index 5a137a7871..ad68fe2fc5 100644
--- a/firmware/controllers/error_handling.c
+++ b/firmware/controllers/error_handling.c
@@ -27,10 +27,14 @@ void chDbgPanic3(const char *msg, const char * file, int line) {
return;
dbg_panic_file = file;
dbg_panic_line = line;
+#if CH_DBG_SYSTEM_STATE_CHECK
dbg_panic_msg = msg;
+#endif /* CH_DBG_SYSTEM_STATE_CHECK */
-
- setOutputPinValue(LED_ERROR, 1);
+ /**
+ * low-level function is used here to reduce stack usage
+ */
+ palWritePad(LED_ERROR_PORT, LED_ERROR_PIN, 1);
#if EFI_HD44780_LCD
lcdShowFatalMessage((char *) msg);
#endif /* EFI_HD44780_LCD */
diff --git a/firmware/controllers/flash_main.cpp b/firmware/controllers/flash_main.cpp
index 318ed47bc6..cb5a4e4513 100644
--- a/firmware/controllers/flash_main.cpp
+++ b/firmware/controllers/flash_main.cpp
@@ -1,5 +1,5 @@
/**
- * @file flash_main.c
+ * @file flash_main.cpp
* @brief Higher-level logic of saving data into internal flash memory
*
*
@@ -52,6 +52,7 @@ crc_t flashStateCrc(persistent_config_container_s *state) {
}
void writeToFlash(void) {
+#if EFI_INTERNAL_FLASH
persistentState.size = PERSISTENT_SIZE;
persistentState.version = FLASH_DATA_VERSION;
scheduleMsg(&logger, "FLASH_DATA_VERSION=%d", persistentState.version);
@@ -64,6 +65,7 @@ void writeToFlash(void) {
result = flashWrite(FLASH_ADDR, (const char *) &persistentState, PERSISTENT_SIZE);
scheduleMsg(&logger, "Flash programmed in (ms): %d", currentTimeMillis() - nowMs);
scheduleMsg(&logger, "Flashed: %d", result);
+#endif /* EFI_INTERNAL_FLASH */
}
static int isValidCrc(persistent_config_container_s *state) {
diff --git a/firmware/controllers/flash_main.h b/firmware/controllers/flash_main.h
index 43864ce013..d6c1594b28 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 3002
+#define FLASH_DATA_VERSION 3601
#ifdef __cplusplus
extern "C"
diff --git a/firmware/controllers/idle_thread.cpp b/firmware/controllers/idle_thread.cpp
index 7a1b097869..566b5de12b 100644
--- a/firmware/controllers/idle_thread.cpp
+++ b/firmware/controllers/idle_thread.cpp
@@ -31,10 +31,11 @@
#include "idle_thread.h"
#include "pin_repository.h"
#include "engine_configuration.h"
+#include "engine.h"
#define IDLE_AIR_CONTROL_VALVE_PWM_FREQUENCY 200
-static WORKING_AREA(ivThreadStack, UTILITY_THREAD_STACK_SIZE);
+static THD_WORKING_AREA(ivThreadStack, UTILITY_THREAD_STACK_SIZE);
static volatile int isIdleControlActive = EFI_IDLE_CONTROL;
extern board_configuration_s *boardConfiguration;
@@ -45,15 +46,9 @@ extern board_configuration_s *boardConfiguration;
static volatile int idleSwitchState;
static Logging logger;
+extern Engine engine;
-static float _switchTimes[PWM_PHASE_MAX_COUNT];
-
-// todo: extract helper for simple PWM?
-static int pinStates[2];
-static single_wave_s wave(pinStates);
-static single_wave_s sr[1] = {wave};
-
-static PwmConfig idleValve(_switchTimes, sr);
+static SimplePwm idleValve;
/**
* Idle level calculation algorithm lives in idle_controller.c
@@ -80,10 +75,10 @@ static void setIdleValvePwm(int value) {
return;
scheduleMsg(&logger, "setting idle valve PWM %d", value);
/**
- * currently IDEL level is an integer per mil (0-1000 range), and PWM takes a fioat in the 0..1 range
+ * currently idle level is an integer per mil (0-1000 range), and PWM takes a float in the 0..1 range
* todo: unify?
*/
- setSimplePwmDutyCycle(&idleValve, 0.001 * value);
+ idleValve.setSimplePwmDutyCycle(0.001 * value);
}
static msg_t ivThread(int param) {
@@ -91,7 +86,7 @@ static msg_t ivThread(int param) {
int currentIdleValve = -1;
while (TRUE) {
- chThdSleepMilliseconds(100);
+ chThdSleepMilliseconds(boardConfiguration->idleThreadPeriod);
// this value is not used yet
idleSwitchState = palReadPad(getHwPort(boardConfiguration->idleSwitchPin), getHwPin(boardConfiguration->idleSwitchPin));
@@ -122,12 +117,11 @@ static void setIdleRpmAction(int value) {
void startIdleThread() {
initLogging(&logger, "Idle Valve Control");
- startSimplePwm(&idleValve, "Idle Valve",
+ startSimplePwmExt(&idleValve, "Idle Valve",
boardConfiguration->idleValvePin,
IDLE_VALVE,
- 0.5,
IDLE_AIR_CONTROL_VALVE_PWM_FREQUENCY,
- TRUE);
+ 0.5);
idleInit(&idle);
scheduleMsg(&logger, "initial idle %d", idle.value);
diff --git a/firmware/controllers/injector_central.cpp b/firmware/controllers/injector_central.cpp
index d6017b99c8..bce7c23299 100644
--- a/firmware/controllers/injector_central.cpp
+++ b/firmware/controllers/injector_central.cpp
@@ -1,5 +1,5 @@
/**
- * @file injector_central.c
+ * @file injector_central.cpp
* @brief Utility methods related to fuel injection.
*
*
@@ -22,7 +22,6 @@
#include "injector_central.h"
#include "main.h"
-#include "engines.h"
#include "io_pins.h"
#include "signal_executor.h"
#include "main_trigger_callback.h"
@@ -38,17 +37,13 @@ extern board_configuration_s *boardConfiguration;
static int is_injector_enabled[MAX_INJECTOR_COUNT];
-int isInjectionEnabled(void) {
- return engineConfiguration2->isInjectionEnabledFlag;
-}
-
-void assertCylinderId(int cylinderId, char *msg) {
+void assertCylinderId(int cylinderId, const char *msg) {
int isValid = cylinderId >= 1 && cylinderId <= engineConfiguration->cylindersCount;
if (!isValid) {
// we are here only in case of a fatal issue - at this point it is fine to make some blocking i-o
//scheduleSimpleMsg(&logger, "cid=", cylinderId);
print("ERROR [%s] cid=%d\r\n", msg, cylinderId);
- chDbgAssert(TRUE, "Cylinder ID", null);
+ efiAssertVoid(FALSE, "Cylinder ID");
}
}
@@ -76,7 +71,7 @@ static void printStatus(void) {
}
static void setInjectorEnabled(int id, int value) {
- chDbgCheck(id >= 0 && id < engineConfiguration->cylindersCount, "injector id");
+ efiAssertVoid(id >= 0 && id < engineConfiguration->cylindersCount, "injector id");
is_injector_enabled[id] = value;
printStatus();
}
@@ -121,14 +116,14 @@ static void sparkbench(char * onStr, char *offStr, char *countStr) {
needToRunBench = TRUE;
}
-static WORKING_AREA(benchThreadStack, UTILITY_THREAD_STACK_SIZE);
+static THD_WORKING_AREA(benchThreadStack, UTILITY_THREAD_STACK_SIZE);
static msg_t benchThread(int param) {
chRegSetThreadName("BenchThread");
while (TRUE) {
while (!needToRunBench) {
- chThdSleepMilliseconds(50);
+ chThdSleepMilliseconds(200);
}
needToRunBench = FALSE;
runBench(brainPin, pin, onTime, offTime, count);
diff --git a/firmware/controllers/injector_central.h b/firmware/controllers/injector_central.h
index 167833cc4d..a06bb7ae90 100644
--- a/firmware/controllers/injector_central.h
+++ b/firmware/controllers/injector_central.h
@@ -19,9 +19,8 @@ extern "C"
#endif /* __cplusplus */
void initInjectorCentral(void);
-int isInjectionEnabled(void);
int isInjectorEnabled(int cylinderId);
-void assertCylinderId(int cylinderId, char *msg);
+void assertCylinderId(int cylinderId, const char *msg);
#ifdef __cplusplus
}
diff --git a/firmware/controllers/malfunction_indicator.c b/firmware/controllers/malfunction_indicator.c
index 3b66480900..51582bc4c3 100644
--- a/firmware/controllers/malfunction_indicator.c
+++ b/firmware/controllers/malfunction_indicator.c
@@ -37,7 +37,7 @@
#define MFI_BLINK_SEPARATOR 400
#define MFI_CHECKENGINE_LIGHT 10000
-static WORKING_AREA(mfiThreadStack, UTILITY_THREAD_STACK_SIZE); // declare thread
+static THD_WORKING_AREA(mfiThreadStack, UTILITY_THREAD_STACK_SIZE); // declare thread
static void blink_digits(int digit, int duration) {
for (int iter = 0; iter < digit; iter++) {
diff --git a/firmware/controllers/map_averaging.c b/firmware/controllers/map_averaging.cpp
similarity index 86%
rename from firmware/controllers/map_averaging.c
rename to firmware/controllers/map_averaging.cpp
index 0f30f3bade..ef8c88c8b5 100644
--- a/firmware/controllers/map_averaging.c
+++ b/firmware/controllers/map_averaging.cpp
@@ -1,183 +1,186 @@
-/**
- * @file map_averaging.c
- *
- * @date Dec 11, 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"
-
-#if EFI_MAP_AVERAGING
-
-#include "map_averaging.h"
-#include "trigger_central.h"
-#include "adc_inputs.h"
-#include "map.h"
-#include "engine_state.h"
-#include "engine_configuration.h"
-#include "interpolation.h"
-#include "signal_executor.h"
-
-#if EFI_ANALOG_CHART
-#include "analog_chart.h"
-#endif /* EFI_ANALOG_CHART */
-
-
-#define FAST_MAP_CHART_SKIP_FACTOR 16
-
-static Logging logger;
-
-/**
- * Running counter of measurements per revolution
- */
-static volatile int perRevolutionCounter = 0;
-/**
- * Number of measurements in previous shaft revolution
- */
-static volatile int perRevolution = 0;
-/**
- * Running MAP accumulator
- * v_ for Voltage
- */
-static volatile float v_mapAccumulator = 0;
-/**
- * Running counter of measurements to consider for averaging
- */
-static volatile int mapMeasurementsCounter = 0;
-
-static float currentMaxPressure;
-
-/**
- * v_ for Voltage
- */
-static float v_averagedMapValue;
-
-extern engine_configuration_s *engineConfiguration;
-
-static scheduling_s startTimer[2];
-static scheduling_s endTimer[2];
-
-static void startAveraging(void*arg) {
- chSysLockFromIsr()
- ;
- // with locking we would have a consistent state
- v_mapAccumulator = 0;
- mapMeasurementsCounter = 0;
- chSysUnlockFromIsr()
- ;
-}
-
-/**
- * This method is invoked from ADC callback
- */
-void mapAveragingCallback(adcsample_t value) {
- /* Calculates the average values from the ADC samples.*/
- perRevolutionCounter++;
-
- float voltage = adcToVoltsDivided(value);
- float currentPressure = getMapByVoltage(voltage);
-
- if (engineConfiguration->analogChartMode == AC_MAP)
- if (perRevolutionCounter % FAST_MAP_CHART_SKIP_FACTOR == 0)
- acAddData(getCrankshaftAngle(getTimeNowUs()), currentPressure);
-
- currentMaxPressure = maxF(currentMaxPressure, currentPressure);
-
- chSysLockFromIsr()
- ;
- // with locking we would have a consistent state
-
- v_mapAccumulator += voltage;
- mapMeasurementsCounter++;
- chSysUnlockFromIsr()
- ;
-}
-
-static void endAveraging(void *arg) {
- chSysLockFromIsr()
- ;
- // with locking we would have a consistent state
- v_averagedMapValue = v_mapAccumulator / mapMeasurementsCounter;
- chSysUnlockFromIsr()
- ;
-}
-
-/**
- * Shaft Position callback used to schedule start and end of MAP averaging
- */
-static void shaftPositionCallback(ShaftEvents ckpEventType, int index) {
- // this callback is invoked on interrupt thread
-
- if (index != 0)
- return;
-
- int rpm = getRpm();
- if(!isValidRpm(rpm))
- return;
-
- perRevolution = perRevolutionCounter;
- perRevolutionCounter = 0;
-
- currentMaxPressure = 0;
-
- MAP_sensor_config_s * config = &engineConfiguration->map;
-
- float startAngle = interpolate2d(rpm, config->samplingAngleBins, config->samplingAngle, MAP_ANGLE_SIZE);
- float windowAngle = interpolate2d(rpm, config->samplingWindowBins, config->samplingWindow, MAP_WINDOW_SIZE);
-
- int structIndex = getRevolutionCounter() % 2;
- // todo: schedule this based on closest trigger event, same as ignition works
- scheduleByAngle(&startTimer[structIndex], startAngle, startAveraging, NULL);
- scheduleByAngle(&endTimer[structIndex], startAngle + windowAngle, endAveraging, NULL);
-}
-
-static void showMapStats(void) {
- scheduleMsg(&logger, "per revolution %d", perRevolution);
-}
-
-float getMapVoltage(void) {
- return v_averagedMapValue;
-}
-
-/**
- * because of MAP window averaging, MAP is only available while engine is spinning
- */
-float getMap(void) {
- if (getRpm() == 0)
- return getRawMap(); // maybe return NaN and have a
- return getMapByVoltage(v_averagedMapValue);
-}
-
-void initMapAveraging(void) {
- initLogging(&logger, "Map Averaging");
-
- startTimer[0].name = "map start0";
- startTimer[1].name = "map start1";
- endTimer[0].name = "map end0";
- endTimer[1].name = "map end1";
-
-
- addTriggerEventListener(&shaftPositionCallback, "rpm reporter");
- addConsoleAction("faststat", showMapStats);
-}
-
-#else
-
-float getMap(void) {
- return getRawMap();
-}
-
-#endif /* EFI_MAP_AVERAGING */
+/**
+ * @file map_averaging.cpp
+ *
+ * @date Dec 11, 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 "map.h"
+
+#if EFI_MAP_AVERAGING
+
+#include "map_averaging.h"
+#include "trigger_central.h"
+#include "adc_inputs.h"
+#include "engine_state.h"
+#include "engine_configuration.h"
+#include "interpolation.h"
+#include "signal_executor.h"
+#include "engine.h"
+
+#if EFI_ANALOG_CHART
+#include "analog_chart.h"
+#endif /* EFI_ANALOG_CHART */
+
+
+#define FAST_MAP_CHART_SKIP_FACTOR 16
+
+static Logging logger;
+
+/**
+ * Running counter of measurements per revolution
+ */
+static volatile int perRevolutionCounter = 0;
+/**
+ * Number of measurements in previous shaft revolution
+ */
+static volatile int perRevolution = 0;
+/**
+ * Running MAP accumulator
+ * v_ for Voltage
+ */
+static volatile float v_mapAccumulator = 0;
+/**
+ * Running counter of measurements to consider for averaging
+ */
+static volatile int mapMeasurementsCounter = 0;
+
+/**
+ * v_ for Voltage
+ */
+static float v_averagedMapValue;
+
+extern Engine engine;
+
+extern engine_configuration_s *engineConfiguration;
+
+static scheduling_s startTimer[2];
+static scheduling_s endTimer[2];
+
+static void startAveraging(void*arg) {
+ chSysLockFromIsr()
+ ;
+ // with locking we would have a consistent state
+ v_mapAccumulator = 0;
+ mapMeasurementsCounter = 0;
+ chSysUnlockFromIsr()
+ ;
+}
+
+/**
+ * This method is invoked from ADC callback.
+ * @note This method is invoked OFTEN, this method is a potential bottle-next - the implementation should be
+ * as fast as possible
+ */
+void mapAveragingCallback(adcsample_t value) {
+ /* Calculates the average values from the ADC samples.*/
+ perRevolutionCounter++;
+
+ float voltage = adcToVoltsDivided(value);
+ float currentPressure = getMapByVoltage(voltage);
+
+#if EFI_ANALOG_CHART
+ if (engineConfiguration->analogChartMode == AC_MAP)
+ if (perRevolutionCounter % FAST_MAP_CHART_SKIP_FACTOR == 0)
+ acAddData(getCrankshaftAngle(getTimeNowUs()), currentPressure);
+#endif /* EFI_ANALOG_CHART */
+
+ chSysLockFromIsr()
+ ;
+ // with locking we would have a consistent state
+
+ v_mapAccumulator += voltage;
+ mapMeasurementsCounter++;
+ chSysUnlockFromIsr()
+ ;
+}
+
+static void endAveraging(void *arg) {
+ chSysLockFromIsr()
+ ;
+ // with locking we would have a consistent state
+ v_averagedMapValue = v_mapAccumulator / mapMeasurementsCounter;
+ chSysUnlockFromIsr()
+ ;
+}
+
+/**
+ * Shaft Position callback used to schedule start and end of MAP averaging
+ */
+static void shaftPositionCallback(trigger_event_e ckpEventType, int index, void *arg) {
+ // this callback is invoked on interrupt thread
+
+ if (index != 0)
+ return;
+
+ int rpm = getRpm();
+ if(!isValidRpm(rpm))
+ return;
+
+ perRevolution = perRevolutionCounter;
+ perRevolutionCounter = 0;
+
+ MAP_sensor_config_s * config = &engineConfiguration->map;
+
+ float startAngle = interpolate2d(rpm, config->samplingAngleBins, config->samplingAngle, MAP_ANGLE_SIZE);
+ float windowAngle = interpolate2d(rpm, config->samplingWindowBins, config->samplingWindow, MAP_WINDOW_SIZE);
+
+ int structIndex = getRevolutionCounter() % 2;
+ // todo: schedule this based on closest trigger event, same as ignition works
+ scheduleByAngle(&startTimer[structIndex], startAngle, startAveraging, NULL);
+ scheduleByAngle(&endTimer[structIndex], startAngle + windowAngle, endAveraging, NULL);
+}
+
+static void showMapStats(void) {
+ scheduleMsg(&logger, "per revolution %d", perRevolution);
+}
+
+float getMapVoltage(void) {
+ return v_averagedMapValue;
+}
+
+/**
+ * Because of MAP window averaging, MAP is only available while engine is spinning
+ * @return Manifold Absolute Pressure, in kPa
+ */
+float getMap(void) {
+ if (getRpm() == 0)
+ return getRawMap(); // maybe return NaN in case of stopped engine?
+ return getMapByVoltage(v_averagedMapValue);
+}
+
+void initMapAveraging(void) {
+ initLogging(&logger, "Map Averaging");
+
+ startTimer[0].name = "map start0";
+ startTimer[1].name = "map start1";
+ endTimer[0].name = "map end0";
+ endTimer[1].name = "map end1";
+
+
+ addTriggerEventListener(&shaftPositionCallback, "rpm reporter", NULL);
+ addConsoleAction("faststat", showMapStats);
+}
+
+#else
+
+float getMap(void) {
+ return getRawMap();
+}
+
+#endif /* EFI_MAP_AVERAGING */
diff --git a/firmware/controllers/map_multiplier_thread.c b/firmware/controllers/map_multiplier_thread.cpp
similarity index 91%
rename from firmware/controllers/map_multiplier_thread.c
rename to firmware/controllers/map_multiplier_thread.cpp
index b0067de2ec..159890a80e 100644
--- a/firmware/controllers/map_multiplier_thread.c
+++ b/firmware/controllers/map_multiplier_thread.cpp
@@ -1,83 +1,86 @@
-/*
- * @brief dead code
- *
- *
- * map_multiplier.c
- *
- * @date Jul 23, 2013
- * @author Andrey Belomutskiy, (c) 2012-2014
- */
-
-#include "main.h"
-#include "map_multiplier_thread.h"
-#include "map_adjuster.h"
-#include "rpm_calculator.h"
-#include "main_trigger_callback.h"
-#include "wave_math.h"
-#include "allsensors.h"
-#include "engine_math.h"
-
-extern engine_configuration_s *engineConfiguration;
-
-static Logging logger;
-
-static WORKING_AREA(maThreadStack, UTILITY_THREAD_STACK_SIZE);
-
-static void mapCallback(int rpm, float key, float value) {
- Logging *logging = &logger;
- appendPrintf(logging, "msg%s", DELIMETER);
-
- appendPrintf(logging, "map_adjusted: ");
- appendPrintf(logging, "%d", rpm);
- appendPrintf(logging, " ");
- appendPrintf(logging, "%d", 100 * key);
- appendPrintf(logging, " ");
- appendPrintf(logging, "%d", 100 * value);
-
- appendMsgPostfix(logging);
- scheduleLogging(logging);
-}
-
-static int timeAtNotRunning = 0;
-
-static int isNewState = TRUE;
-
-static void maThread(int param) {
- chRegSetThreadName("map adjustment");
-
- while (TRUE) {
- chThdSleepMilliseconds(100);
-
- systime_t now = chTimeNow();
- if (!isRunning()) {
- timeAtNotRunning = now;
- continue;
- }
-
- int wasNotRunningRecently = overflowDiff(now, timeAtNotRunning) < 60 * CH_FREQUENCY;
- if (!wasNotRunningRecently)
- continue;
- if (isNewState)
- scheduleMsg(&logger, "starting fuel map adjustment at %d", now);
- isNewState = FALSE;
-
- // ideally this should be atomic, but hopefully it's good enough
- int rpm = getRpm();
- float load = getEngineLoad();
- float afr = getAfr();
-
- addAfr(rpm, load, afr);
- int total = runMapAdjustments(mapCallback);
- if (total > 0) {
-// scheduleSimpleMsg(&logger, "map adjusted for maf ", 100 * key);
- }
- }
-}
-
-void initMapAdjusterThread(void) {
- initLogging(&logger, "Map self learning thread");
-
- initMapAdjuster();
-
- chThdCreateStatic(maThreadStack, sizeof(maThreadStack), NORMALPRIO, (tfunc_t)maThread, NULL);
-}
+/*
+ * @brief dead code
+ *
+ *
+ * map_multiplier.cpp
+ *
+ * @date Jul 23, 2013
+ * @author Andrey Belomutskiy, (c) 2012-2014
+ */
+
+#include "main.h"
+#include "map_multiplier_thread.h"
+#include "map_adjuster.h"
+#include "rpm_calculator.h"
+#include "main_trigger_callback.h"
+#include "wave_math.h"
+#include "allsensors.h"
+#include "engine_math.h"
+#include "engine.h"
+
+extern engine_configuration_s *engineConfiguration;
+
+static Logging logger;
+
+extern Engine engine;
+
+static THD_WORKING_AREA(maThreadStack, UTILITY_THREAD_STACK_SIZE);
+
+static void mapCallback(int rpm, float key, float value) {
+ Logging *logging = &logger;
+ appendPrintf(logging, "msg%s", DELIMETER);
+
+ appendPrintf(logging, "map_adjusted: ");
+ appendPrintf(logging, "%d", rpm);
+ appendPrintf(logging, " ");
+ appendPrintf(logging, "%d", 100 * key);
+ appendPrintf(logging, " ");
+ appendPrintf(logging, "%d", 100 * value);
+
+ appendMsgPostfix(logging);
+ scheduleLogging(logging);
+}
+
+static int timeAtNotRunning = 0;
+
+static int isNewState = TRUE;
+
+static void maThread(int param) {
+ chRegSetThreadName("map adjustment");
+
+ while (TRUE) {
+ chThdSleepMilliseconds(100);
+
+ systime_t now = chTimeNow();
+ if (!isRunning()) {
+ timeAtNotRunning = now;
+ continue;
+ }
+
+ int wasNotRunningRecently = overflowDiff(now, timeAtNotRunning) < 60 * CH_FREQUENCY;
+ if (!wasNotRunningRecently)
+ continue;
+ if (isNewState)
+ scheduleMsg(&logger, "starting fuel map adjustment at %d", now);
+ isNewState = FALSE;
+
+ // ideally this should be atomic, but hopefully it's good enough
+ int rpm = getRpm();
+ float load = getEngineLoad();
+ float afr = getAfr();
+
+ addAfr(rpm, load, afr);
+ int total = runMapAdjustments(mapCallback);
+ if (total > 0) {
+// scheduleSimpleMsg(&logger, "map adjusted for maf ", 100 * key);
+ }
+ }
+}
+
+void initMapAdjusterThread(void) {
+ initLogging(&logger, "Map self learning thread");
+
+ initMapAdjuster();
+
+ chThdCreateStatic(maThreadStack, sizeof(maThreadStack), NORMALPRIO, (tfunc_t)maThread, NULL);
+}
diff --git a/firmware/controllers/math/engine_math.cpp b/firmware/controllers/math/engine_math.cpp
index e054aa7c78..9c74451971 100644
--- a/firmware/controllers/math/engine_math.cpp
+++ b/firmware/controllers/math/engine_math.cpp
@@ -19,14 +19,15 @@
* If not, see .
*/
-#include "engine_math.h"
#include "main.h"
+#include "engine_math.h"
#include "engine_configuration.h"
#include "interpolation.h"
#include "allsensors.h"
#include "io_pins.h"
#include "OutputSignalList.h"
#include "trigger_decoder.h"
+#include "event_registry.h"
/*
* default Volumetric Efficiency
@@ -36,42 +37,21 @@
// return interpolate(5000, 1.1, 8000, 1, rpm);
// return interpolate(500, 0.5, 5000, 1.1, rpm);
//}
-//#define K_AT_MIN_RPM_MIN_TPS 0.25
-//#define K_AT_MIN_RPM_MAX_TPS 0.25
-//#define K_AT_MAX_RPM_MIN_TPS 0.25
-//#define K_AT_MAX_RPM_MAX_TPS 0.9
-//
-//#define rpmMin 500
-//#define rpmMax 8000
-//
-//#define tpMin 0
-//#define tpMax 100
-//
-// http://rusefi.com/math/t_charge.html
-// /
-//float getTCharge(int rpm, int tps, float coolantTemp, float airTemp) {
-// float minRpmKcurrentTPS = interpolate(tpMin, K_AT_MIN_RPM_MIN_TPS, tpMax,
-// K_AT_MIN_RPM_MAX_TPS, tps);
-// float maxRpmKcurrentTPS = interpolate(tpMin, K_AT_MAX_RPM_MIN_TPS, tpMax,
-// K_AT_MAX_RPM_MAX_TPS, tps);
-//
-// float Tcharge_coff = interpolate(rpmMin, minRpmKcurrentTPS, rpmMax,
-// maxRpmKcurrentTPS, rpm);
-//
-// float Tcharge = coolantTemp * (1 - Tcharge_coff) + airTemp * Tcharge_coff;
-//
-// return Tcharge;
-//}
-#define MAX_STARTING_FUEL 15
-#define MIN_STARTING_FUEL 8
-
/**
* @return time needed to rotate crankshaft by one degree, in milliseconds.
+ * @deprecated
*/
float getOneDegreeTimeMs(int rpm) {
return 1000.0 * 60 / 360 / rpm;
}
+/**
+ * @return time needed to rotate crankshaft by one degree, in microseconds.
+ */
+float getOneDegreeTimeUs(int rpm) {
+ return 1000000.0 * 60 / 360 / rpm;
+}
+
/**
* @return number of milliseconds in one crankshaft revolution
*/
@@ -136,34 +116,39 @@ int isCrankingRT(engine_configuration_s *engineConfiguration, int rpm) {
OutputSignalList ignitionSignals;
OutputSignalList injectonSignals;
-
static void registerSparkEvent(engine_configuration_s const *engineConfiguration, trigger_shape_s * s,
- ActuatorEventList *list, OutputSignal *actuator, float angleOffset) {
+ IgnitionEventList *list, OutputSignal *actuator, float localAdvance, float dwell) {
- registerActuatorEventExt(engineConfiguration, s, list,
- actuator, angleOffset);
+ IgnitionEvent *event = list->getNextActuatorEvent();
+ if (event == NULL)
+ return; // error already reported
+
+ event->advance = localAdvance;
+
+ registerActuatorEventExt(engineConfiguration, s, &event->actuator, actuator, localAdvance - dwell);
}
-void initializeIgnitionActions(float baseAngle, engine_configuration_s *engineConfiguration,
- engine_configuration2_s *engineConfiguration2, float dwellMs, ActuatorEventList *list) {
- chDbgCheck(engineConfiguration->cylindersCount > 0, "cylindersCount");
+void initializeIgnitionActions(float advance, float dwellAngle, engine_configuration_s *engineConfiguration,
+ engine_configuration2_s *engineConfiguration2, IgnitionEventList *list) {
+
+ efiAssertVoid(engineConfiguration->cylindersCount > 0, "cylindersCount");
ignitionSignals.clear();
- resetEventList(list);
+ list->resetEventList();
switch (engineConfiguration->ignitionMode) {
case IM_ONE_COIL:
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
// todo: extract method
- float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount;
+ float localAdvance = advance + 720.0 * i / engineConfiguration->cylindersCount;
registerSparkEvent(engineConfiguration, &engineConfiguration2->triggerShape, list,
- ignitionSignals.add(SPARKOUT_1_OUTPUT), angle);
+ ignitionSignals.add(SPARKOUT_1_OUTPUT), localAdvance, dwellAngle);
}
break;
case IM_WASTED_SPARK:
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
- float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount;
+ float localAdvance = advance + 720.0 * i / engineConfiguration->cylindersCount;
int wastedIndex = i % (engineConfiguration->cylindersCount / 2);
@@ -171,18 +156,18 @@ void initializeIgnitionActions(float baseAngle, engine_configuration_s *engineCo
io_pin_e ioPin = (io_pin_e) (SPARKOUT_1_OUTPUT + id);
registerSparkEvent(engineConfiguration, &engineConfiguration2->triggerShape, list,
- ignitionSignals.add(ioPin), angle);
+ ignitionSignals.add(ioPin), localAdvance, dwellAngle);
}
break;
case IM_INDIVIDUAL_COILS:
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
- float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount;
+ float localAdvance = advance + 720.0 * i / engineConfiguration->cylindersCount;
io_pin_e pin = (io_pin_e) ((int) SPARKOUT_1_OUTPUT + getCylinderId(engineConfiguration->firingOrder, i) - 1);
- registerSparkEvent(engineConfiguration, &engineConfiguration2->triggerShape, list,
- ignitionSignals.add(pin), angle);
+ registerSparkEvent(engineConfiguration, &engineConfiguration2->triggerShape, list, ignitionSignals.add(pin),
+ localAdvance, dwellAngle);
}
break;
@@ -193,7 +178,7 @@ void initializeIgnitionActions(float baseAngle, engine_configuration_s *engineCo
void addFuelEvents(engine_configuration_s const *e, engine_configuration2_s *engineConfiguration2,
ActuatorEventList *list, injection_mode_e mode) {
- resetEventList(list);
+ list->resetEventList();
trigger_shape_s *s = &engineConfiguration2->triggerShape;
@@ -204,7 +189,7 @@ void addFuelEvents(engine_configuration_s const *e, engine_configuration2_s *eng
for (int i = 0; i < e->cylindersCount; i++) {
io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + getCylinderId(e->firingOrder, i) - 1);
float angle = baseAngle + i * 720.0 / e->cylindersCount;
- registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle);
+ registerActuatorEventExt(e, s, list->getNextActuatorEvent(), injectonSignals.add(pin), angle);
}
break;
case IM_SIMULTANEOUS:
@@ -213,7 +198,7 @@ void addFuelEvents(engine_configuration_s const *e, engine_configuration2_s *eng
for (int j = 0; j < e->cylindersCount; j++) {
io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + j);
- registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle);
+ registerActuatorEventExt(e, s, list->getNextActuatorEvent(), injectonSignals.add(pin), angle);
}
}
break;
@@ -221,7 +206,7 @@ void addFuelEvents(engine_configuration_s const *e, engine_configuration2_s *eng
for (int i = 0; i < e->cylindersCount; i++) {
io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + (i % 2));
float angle = baseAngle + i * 720.0 / e->cylindersCount;
- registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle);
+ registerActuatorEventExt(e, s, list->getNextActuatorEvent(), injectonSignals.add(pin), angle);
}
break;
default:
@@ -238,53 +223,76 @@ float getSparkDwellMsT(engine_configuration_s *engineConfiguration, int rpm) {
float angle = engineConfiguration->crankingChargeAngle;
return getOneDegreeTimeMs(rpm) * angle;
}
- efiAssert(!cisnan(rpm), "invalid rpm");
+ efiAssert(!cisnan(rpm), "invalid rpm", NAN);
return interpolate2d(rpm, engineConfiguration->sparkDwellBins, engineConfiguration->sparkDwell, DWELL_CURVE_SIZE);
}
-void registerActuatorEventExt(engine_configuration_s const *engineConfiguration, trigger_shape_s * s,
- ActuatorEventList *list, OutputSignal *actuator, float angleOffset) {
- chDbgCheck(s->getSize() > 0, "uninitialized trigger_shape_s");
+/**
+ * Trigger event count equals engine cycle event count if we have a cam sensor.
+ * Two trigger cycles make one engine cycle in case of a four stroke engine If we only have a cranksensor.
+ */
+int getEngineCycleEventCount(engine_configuration_s const *engineConfiguration, trigger_shape_s * s) {
+ return getOperationMode(engineConfiguration) == FOUR_STROKE_CAM_SENSOR ? s->getSize() : 2 * s->getSize();
+}
+
+void findTriggerPosition(engine_configuration_s const *engineConfiguration, trigger_shape_s * s,
+ event_trigger_position_s *position, float angleOffset) {
angleOffset = fixAngle(angleOffset + engineConfiguration->globalTriggerAngleOffset);
- int triggerIndexOfZeroEvent = s->triggerShapeSynchPointIndex;
-
// todo: migrate to crankAngleRange?
- float firstAngle = s->wave.getSwitchTime(triggerIndexOfZeroEvent) * 720;
+ float firstAngle = s->wave.getAngle(s->triggerShapeSynchPointIndex, engineConfiguration, s);
+
+ int engineCycleEventCount = getEngineCycleEventCount(engineConfiguration, s);
// let's find the last trigger angle which is less or equal to the desired angle
int i;
- for (i = 0; i < s->getSize() - 1; i++) {
+ for (i = 0; i < engineCycleEventCount - 1; i++) {
// todo: we need binary search here
float angle = fixAngle(
- s->wave.getSwitchTime((triggerIndexOfZeroEvent + i + 1) % s->getSize()) * 720 - firstAngle);
+ s->wave.getAngle((s->triggerShapeSynchPointIndex + i + 1) % engineCycleEventCount, engineConfiguration, s)
+ - firstAngle);
if (angle > angleOffset)
break;
}
// explicit check for zero to avoid issues where logical zero is not exactly zero due to float nature
- float angle =
- i == 0 ?
- 0 :
- fixAngle(s->wave.getSwitchTime((triggerIndexOfZeroEvent + i) % s->getSize()) * 720 - firstAngle);
+ float eventAngle;
+ if (i == 0) {
+ eventAngle = 0;
+ } else {
+ eventAngle = fixAngle(
+ s->wave.getAngle((s->triggerShapeSynchPointIndex + i) % engineCycleEventCount, engineConfiguration, s)
+ - firstAngle);
+ }
- if (angleOffset < angle) {
- firmwareError("angle constraint violation in registerActuatorEventExt(): %f/%f", angleOffset, angle);
+ if (angleOffset < eventAngle) {
+ firmwareError("angle constraint violation in registerActuatorEventExt(): %f/%f", angleOffset, eventAngle);
return;
}
- registerActuatorEvent(list, i, actuator, angleOffset - angle);
+ position->eventIndex = i;
+ position->eventAngle = eventAngle;
+ position->angleOffset = angleOffset - eventAngle;
}
-//float getTriggerEventAngle(int triggerEventIndex) {
-// return 0;
-//}
+void registerActuatorEventExt(engine_configuration_s const *engineConfiguration, trigger_shape_s * s, ActuatorEvent *e,
+ OutputSignal *actuator, float angleOffset) {
+ efiAssertVoid(s->getSize() > 0, "uninitialized trigger_shape_s");
+
+ if (e == NULL)
+ return; // error already reported
+ e->actuator = actuator;
+
+ findTriggerPosition(engineConfiguration, s, &e->position, angleOffset);
+}
static int order_1_THEN_3_THEN_4_THEN2[] = { 1, 3, 4, 2 };
static int order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[] = { 1, 5, 3, 6, 2, 4 };
+static int order_1_8_4_3_6_5_7_2[] = {1, 8, 4, 3, 6, 5, 7, 2};
+
/**
* @param index from zero to cylindersCount - 1
* @return cylinderId from one to cylindersCount
@@ -298,6 +306,8 @@ int getCylinderId(firing_order_e firingOrder, int index) {
return order_1_THEN_3_THEN_4_THEN2[index];
case FO_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4:
return order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[index];
+ case FO_1_8_4_3_6_5_7_2:
+ return order_1_8_4_3_6_5_7_2[index];
default:
firmwareError("getCylinderId not supported for %d", firingOrder);
@@ -320,8 +330,13 @@ void prepareOutputSignals(engine_configuration_s *engineConfiguration, engine_co
}
void setTableBin(float array[], int size, float l, float r) {
- for (int i = 0; i < size; i++)
- array[i] = interpolate(0, l, size - 1, r, i);
+ for (int i = 0; i < size; i++) {
+ float value = interpolate(0, l, size - 1, r, i);
+ /**
+ * rounded values look nicer, also we want to avoid precision mismatch with Tuner Studio
+ */
+ array[i] = efiRound(value, 0.01);
+ }
}
void setFuelRpmBin(engine_configuration_s *engineConfiguration, float l, float r) {
@@ -339,3 +354,7 @@ void setTimingRpmBin(engine_configuration_s *engineConfiguration, float l, float
void setTimingLoadBin(engine_configuration_s *engineConfiguration, float l, float r) {
setTableBin(engineConfiguration->ignitionLoadBins, IGN_LOAD_COUNT, l, r);
}
+
+int isInjectionEnabled(engine_configuration2_s const *engineConfiguration2) {
+ return engineConfiguration2->isInjectionEnabledFlag;
+}
diff --git a/firmware/controllers/math/engine_math.h b/firmware/controllers/math/engine_math.h
index 0117fa45f2..abc5ac50cb 100644
--- a/firmware/controllers/math/engine_math.h
+++ b/firmware/controllers/math/engine_math.h
@@ -10,6 +10,54 @@
#include "engine_configuration.h"
+#ifdef __cplusplus
+#include "ec2.h"
+#include "trigger_structure.h"
+void findTriggerPosition(engine_configuration_s const *engineConfiguration, trigger_shape_s * s,
+ event_trigger_position_s *position, float angleOffset);
+
+int isInjectionEnabled(engine_configuration2_s const *engineConfiguration2);
+
+// 'random' value to be sure we are not treating any non-zero trash as TRUE
+#define MAGIC_TRUE_VALUE 153351512
+
+template
+class Map3D {
+public:
+ void init(float table[RPM_BIN_SIZE][LOAD_BIN_SIZE]);
+ float getValue(float x, float xBin[], float y, float yBin[]);
+ void setAll(float value);
+private:
+ float *pointers[LOAD_BIN_SIZE];
+ int initialized;
+};
+
+template
+void Map3D::init(float table[RPM_BIN_SIZE][LOAD_BIN_SIZE]) {
+ for (int k = 0; k < LOAD_BIN_SIZE; k++)
+ pointers[k] = table[k];
+ initialized = MAGIC_TRUE_VALUE;
+}
+
+template
+float Map3D::getValue(float x, float xBin[], float y, float yBin[]) {
+ efiAssert(initialized == MAGIC_TRUE_VALUE, "fuel map initialized", NAN);
+ return interpolate3d(x, xBin, LOAD_BIN_SIZE, y, yBin, RPM_BIN_SIZE, pointers);
+}
+
+template
+void Map3D::setAll(float value) {
+ for (int l = 0; l < LOAD_BIN_SIZE; l++) {
+ for (int r = 0; r < RPM_BIN_SIZE; r++) {
+ pointers[l][r] = value;
+ }
+ }
+}
+
+typedef Map3D<16, 16> Map3D1616;
+
+#endif
+
#ifdef __cplusplus
extern "C"
{
@@ -18,9 +66,9 @@ extern "C"
//float getDefaultVE(int rpm);
float getDefaultFuel(int rpm, float map);
-//float getTCharge(int rpm, int tps, float coolantTemp, float airTemp);
float getOneDegreeTimeMs(int rpm);
+float getOneDegreeTimeUs(int rpm);
float getCrankshaftRevolutionTimeMs(int rpm);
int isCrankingRT(engine_configuration_s *engineConfiguration, int rpm);
@@ -35,6 +83,7 @@ float getEngineLoadT(engine_configuration_s *engineConfiguration);
float getSparkDwellMsT(engine_configuration_s *engineConfiguration, int rpm);
#define getSparkDwellMs(rpm) getSparkDwellMsT(engineConfiguration, rpm)
+
int getCylinderId(firing_order_e firingOrder, int index);
void setTableBin(float array[], int size, float l, float r);
diff --git a/firmware/controllers/math/math.mk b/firmware/controllers/math/math.mk
index 66e9d2a71f..f30dad12ea 100644
--- a/firmware/controllers/math/math.mk
+++ b/firmware/controllers/math/math.mk
@@ -1,4 +1,5 @@
CONTROLLERS_MATH_SRC =
-CONTROLLERS_MATH_SRC_CPP = $(PROJECT_DIR)/controllers/math/engine_math.cpp
+CONTROLLERS_MATH_SRC_CPP = $(PROJECT_DIR)/controllers/math/engine_math.cpp \
+ $(PROJECT_DIR)/controllers/math/speed_density.cpp
diff --git a/firmware/controllers/math/speed_density.cpp b/firmware/controllers/math/speed_density.cpp
new file mode 100644
index 0000000000..53f2d51b3d
--- /dev/null
+++ b/firmware/controllers/math/speed_density.cpp
@@ -0,0 +1,78 @@
+/**
+ * @file speed_density.cpp
+ *
+ * @date May 29, 2014
+ * @author Andrey Belomutskiy, (c) 2012-2014
+ */
+
+#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"
+
+#define K_AT_MIN_RPM_MIN_TPS 0.25
+#define K_AT_MIN_RPM_MAX_TPS 0.25
+#define K_AT_MAX_RPM_MIN_TPS 0.25
+#define K_AT_MAX_RPM_MAX_TPS 0.9
+
+#define rpmMin 500
+#define rpmMax 8000
+
+static Map3D1616 veMap;
+static Map3D1616 afrMap;
+
+#define tpMin 0
+#define tpMax 100
+// http://rusefi.com/math/t_charge.html
+float getTCharge(int rpm, int tps, float coolantTemp, float airTemp) {
+ float minRpmKcurrentTPS = interpolate(tpMin, K_AT_MIN_RPM_MIN_TPS, tpMax,
+ K_AT_MIN_RPM_MAX_TPS, tps);
+ float maxRpmKcurrentTPS = interpolate(tpMin, K_AT_MAX_RPM_MIN_TPS, tpMax,
+ K_AT_MAX_RPM_MAX_TPS, tps);
+
+ float Tcharge_coff = interpolate(rpmMin, minRpmKcurrentTPS, rpmMax, maxRpmKcurrentTPS, rpm);
+
+ float Tcharge = coolantTemp * (1 - Tcharge_coff) + airTemp * Tcharge_coff;
+
+ return Tcharge;
+}
+
+/**
+ * is J/g*K
+ */
+#define GAS_R 0.28705
+
+float sdMath(engine_configuration_s *engineConfiguration, float VE, float MAP, float AFR, float temp) {
+ float injectorFlowRate = cc_minute_to_gramm_second(engineConfiguration->injectorFlow);
+ float Vol = engineConfiguration->displacement / engineConfiguration->cylindersCount;
+ return (Vol * VE * MAP) / (AFR * injectorFlowRate * GAS_R * temp);
+}
+
+float getSpeedDensityFuel(Engine *engine) {
+ int rpm = engine->rpmCalculator->rpm();
+
+ float tps = getTPS();
+ float coolantC = getCoolantTemperature();
+ float intakeC = getIntakeAirTemperature();
+ float tChargeK = convertCelciusToKelvin(getTCharge(rpm, tps, coolantC, intakeC));
+ float MAP = getMap();
+ float VE = 0.8;//veMap.getValue(rpm)
+ float AFR = 14.7;
+
+ return sdMath(engine->engineConfiguration, VE, MAP, AFR, tChargeK);
+
+}
+
+void setDetaultVETable(engine_configuration_s *engineConfiguration) {
+ setTableBin(engineConfiguration->veRpmBins, FUEL_RPM_COUNT, 800, 7000);
+
+ veMap.setAll(0.8);
+}
+
+void initSpeedDensity(configuration_s *config) {
+ veMap.init(config->engineConfiguration->veTable);
+ afrMap.init(config->engineConfiguration->afrTable);
+}
diff --git a/firmware/controllers/math/speed_density.h b/firmware/controllers/math/speed_density.h
new file mode 100644
index 0000000000..8ee631bc85
--- /dev/null
+++ b/firmware/controllers/math/speed_density.h
@@ -0,0 +1,23 @@
+/**
+ * @file speed_density.h
+ *
+ * @date May 29, 2014
+ * @author Andrey Belomutskiy, (c) 2012-2014
+ */
+#ifndef SPEED_DENSITY_H_
+#define SPEED_DENSITY_H_
+
+#include "engine_configuration.h"
+#include "ec2.h"
+
+float getTCharge(int rpm, int tps, float coolantTemp, float airTemp);
+void setDetaultVETable(engine_configuration_s *engineConfiguration);
+float sdMath(engine_configuration_s *engineConfiguration, float VE, float MAP, float AFR, float temp);
+
+#define gramm_second_to_cc_minute(gs) ((gs) / 0.0119997981)
+
+#define cc_minute_to_gramm_second(ccm) ((ccm) * 0.0119997981)
+
+void initSpeedDensity(configuration_s *config);
+
+#endif /* SPEED_DENSITY_H_ */
diff --git a/firmware/controllers/sensors/ego.c b/firmware/controllers/sensors/ego.cpp
similarity index 96%
rename from firmware/controllers/sensors/ego.c
rename to firmware/controllers/sensors/ego.cpp
index ec499784ea..224d01a421 100644
--- a/firmware/controllers/sensors/ego.c
+++ b/firmware/controllers/sensors/ego.cpp
@@ -1,15 +1,15 @@
-#include "main.h"
-#include "engine_configuration.h"
-#include "interpolation.h"
-#include "boards.h"
-#include "adc_inputs.h"
-
-extern engine_configuration_s *engineConfiguration;
-
-float getAfr(void) {
- afr_sensor_s * sensor = &engineConfiguration->afrSensor;
-
- float volts = getVoltageDivided(sensor->afrAdcChannel);
-
- return interpolate(sensor->v1, sensor->value1, sensor->v2, sensor->value2, volts);
-}
+#include "main.h"
+#include "engine_configuration.h"
+#include "interpolation.h"
+#include "boards.h"
+#include "adc_inputs.h"
+
+extern engine_configuration_s *engineConfiguration;
+
+float getAfr(void) {
+ afr_sensor_s * sensor = &engineConfiguration->afrSensor;
+
+ float volts = getVoltageDivided(sensor->afrAdcChannel);
+
+ return interpolate(sensor->v1, sensor->value1, sensor->v2, sensor->value2, volts);
+}
diff --git a/firmware/controllers/sensors/ego.h b/firmware/controllers/sensors/ego.h
index 40b3e1028b..a49b45986e 100644
--- a/firmware/controllers/sensors/ego.h
+++ b/firmware/controllers/sensors/ego.h
@@ -12,15 +12,6 @@
#include "main.h"
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
float getAfr(void);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
#endif
diff --git a/firmware/controllers/sensors/map.c b/firmware/controllers/sensors/map.cpp
similarity index 79%
rename from firmware/controllers/sensors/map.c
rename to firmware/controllers/sensors/map.cpp
index cb0b52bc21..c1199c6c83 100644
--- a/firmware/controllers/sensors/map.c
+++ b/firmware/controllers/sensors/map.cpp
@@ -1,58 +1,58 @@
-#include "main.h"
-#include "boards.h"
-#include "engine_configuration.h"
-#include "engine_math.h"
-#include "adc_inputs.h"
-#include "interpolation.h"
-#include "error_handling.h"
-#include "map.h"
-
-extern engine_configuration_s * engineConfiguration;
-
-/**
- * @brief MAP value decoded for a 1.83 Honda sensor
- * -6.64kPa at zero volts
- * 182.78kPa at 5 volts
- *
- * @returns kPa value
- */
-float getMAPValueHonda_Denso183(float voltage) {
- return interpolate(0, -6.64, 5, 182.78, voltage);
-}
-
-float getMAPValueMPX_4250(float voltage) {
- return interpolate(0, 8, 5, 260, voltage);
-}
-
-float decodePressure(float voltage, air_pressure_sensor_config_s * config) {
- switch (config->sensorType) {
- case MT_CUSTOM:
- return interpolate(0, config->Min, 5, config->Max, voltage);
- case MT_DENSO183:
- return getMAPValueHonda_Denso183(voltage);
- case MT_MPX4250:
- return getMAPValueMPX_4250(voltage);
- default:
- firmwareError("Unknown MAP type: %d", config->sensorType);
- return NAN;
- }
-}
-
-/**
- * @brief MAP value decoded according to current settings
- * @returns kPa value
- */
-float getMapByVoltage(float voltage) {
- air_pressure_sensor_config_s * config = &engineConfiguration->map.sensor;
- return decodePressure(voltage, config);
-}
-
-float getRawMap(void) {
- float voltage = getVoltageDivided(engineConfiguration->map.sensor.hwChannel);
- return getMapByVoltage(voltage);
-}
-
-float getBaroPressure(void) {
- float voltage = getVoltageDivided(engineConfiguration->baroSensor.hwChannel);
- return decodePressure(voltage, &engineConfiguration->baroSensor);
-}
+#include "main.h"
+#include "boards.h"
+#include "engine_configuration.h"
+#include "engine_math.h"
+#include "adc_inputs.h"
+#include "interpolation.h"
+#include "error_handling.h"
+#include "map.h"
+
+extern engine_configuration_s * engineConfiguration;
+
+/**
+ * @brief MAP value decoded for a 1.83 Honda sensor
+ * -6.64kPa at zero volts
+ * 182.78kPa at 5 volts
+ *
+ * @returns kPa value
+ */
+static FastInterpolation denso183(0, -6.64, 5, 182.78);
+
+static FastInterpolation mpx4250(0, 8, 5, 260);
+
+float decodePressure(float voltage, air_pressure_sensor_config_s * config) {
+ switch (config->sensorType) {
+ case MT_CUSTOM:
+ // todo: introduce 'FastInterpolation customMap'
+ return interpolate(0, config->Min, 5, config->Max, voltage);
+ case MT_DENSO183:
+ return denso183.getValue(voltage);
+ case MT_MPX4250:
+ return mpx4250.getValue(voltage);
+ default:
+ firmwareError("Unknown MAP type: %d", config->sensorType);
+ return NAN;
+ }
+}
+
+/**
+ * @brief MAP value decoded according to current settings
+ * @returns kPa value
+ */
+float getMapByVoltage(float voltage) {
+ air_pressure_sensor_config_s * config = &engineConfiguration->map.sensor;
+ return decodePressure(voltage, config);
+}
+
+/**
+ * @return Manifold Absolute Pressure, in kPa
+ */
+float getRawMap(void) {
+ float voltage = getVoltageDivided(engineConfiguration->map.sensor.hwChannel);
+ return getMapByVoltage(voltage);
+}
+
+float getBaroPressure(void) {
+ float voltage = getVoltageDivided(engineConfiguration->baroSensor.hwChannel);
+ return decodePressure(voltage, &engineConfiguration->baroSensor);
+}
diff --git a/firmware/controllers/sensors/map.h b/firmware/controllers/sensors/map.h
index ed6a3cfd63..098bb34680 100644
--- a/firmware/controllers/sensors/map.h
+++ b/firmware/controllers/sensors/map.h
@@ -6,6 +6,8 @@ extern "C"
{
#endif /* __cplusplus */
+#include "sensor_types.h"
+
/**
* @return Raw MAP sensor value right now
*/
@@ -17,8 +19,7 @@ float getBaroPressure(void);
float getMap(void);
float getMapVoltage(void);
float getMapByVoltage(float voltage);
-float getMAPValueHonda_Denso183(float volts);
-float getMAPValueMPX_4250(float volts);
+float decodePressure(float voltage, air_pressure_sensor_config_s * config);
#ifdef __cplusplus
}
diff --git a/firmware/controllers/sensors/sensor_types.h b/firmware/controllers/sensors/sensor_types.h
index 998d7874be..95ae5be362 100644
--- a/firmware/controllers/sensors/sensor_types.h
+++ b/firmware/controllers/sensors/sensor_types.h
@@ -14,6 +14,9 @@
#include "rusefi_enums.h"
typedef struct {
+ /**
+ * kPa value at zero volts
+ */
float Min;
float Max;
air_pressure_sensor_type_e sensorType;
diff --git a/firmware/controllers/sensors/sensors.mk b/firmware/controllers/sensors/sensors.mk
index 79933a8c74..fb2e495b0c 100644
--- a/firmware/controllers/sensors/sensors.mk
+++ b/firmware/controllers/sensors/sensors.mk
@@ -1,9 +1,9 @@
-CONTROLLERS_SENSORS_SRC = $(PROJECT_DIR)/controllers/sensors/tps.c \
- $(PROJECT_DIR)/controllers/sensors/allsensors.c \
+CONTROLLERS_SENSORS_SRC = $(PROJECT_DIR)/controllers/sensors/allsensors.c \
$(PROJECT_DIR)/controllers/sensors/maf.c \
- $(PROJECT_DIR)/controllers/sensors/map.c \
- $(PROJECT_DIR)/controllers/sensors/ego.c \
$(PROJECT_DIR)/controllers/sensors/voltage.c
-CONTROLLERS_SENSORS_SRC_CPP = $(PROJECT_DIR)/controllers/sensors/thermistors.cpp
+CONTROLLERS_SENSORS_SRC_CPP = $(PROJECT_DIR)/controllers/sensors/thermistors.cpp \
+ $(PROJECT_DIR)/controllers/sensors/map.cpp \
+ $(PROJECT_DIR)/controllers/sensors/tps.cpp \
+ $(PROJECT_DIR)/controllers/sensors/ego.cpp
diff --git a/firmware/controllers/sensors/thermistors.cpp b/firmware/controllers/sensors/thermistors.cpp
index de3bf2bddf..fe99f85901 100644
--- a/firmware/controllers/sensors/thermistors.cpp
+++ b/firmware/controllers/sensors/thermistors.cpp
@@ -52,7 +52,7 @@ float convertKelvinToC(float tempK) {
return tempK - KELV;
}
-float convertCelciustoKelvin(float tempC) {
+float convertCelciusToKelvin(float tempC) {
return tempC + KELV;
}
@@ -81,7 +81,7 @@ float getKelvinTemperature(float resistance, ThermistorConf *thermistor) {
float getResistance(Thermistor *thermistor) {
float voltage = getVoltageDivided(thermistor->channel);
- chDbgCheck(thermistor->config != NULL, "config is null");
+ efiAssert(thermistor->config != NULL, "config is null", NAN);
float resistance = getR2InVoltageDividor(voltage, _5_VOLTS, thermistor->config->bias_resistor);
return resistance;
}
diff --git a/firmware/controllers/sensors/thermistors.h b/firmware/controllers/sensors/thermistors.h
index a6b81995ca..9bbc2f99ef 100644
--- a/firmware/controllers/sensors/thermistors.h
+++ b/firmware/controllers/sensors/thermistors.h
@@ -29,7 +29,7 @@ float getTempK(float resistance);
* converts Kelvin temperature into Celcius temperature
*/
float convertKelvinToCelcius(float tempK);
-float convertCelciustoKelvin(float tempC);
+float convertCelciusToKelvin(float tempC);
float convertCelciustoF(float tempC);
float convertFtoCelcius(float tempF);
diff --git a/firmware/controllers/sensors/tps.c b/firmware/controllers/sensors/tps.cpp
similarity index 93%
rename from firmware/controllers/sensors/tps.c
rename to firmware/controllers/sensors/tps.cpp
index f8a87e130d..4371584c6d 100644
--- a/firmware/controllers/sensors/tps.c
+++ b/firmware/controllers/sensors/tps.cpp
@@ -1,109 +1,109 @@
-#include "main.h"
-#include "boards.h"
-#include "tps.h"
-#include "engine_configuration.h"
-#include "interpolation.h"
-#include "adc_inputs.h"
-#include "wave_math.h"
-
-extern engine_configuration_s *engineConfiguration;
-
-/**
- * We are using one instance for read and another for modification, this is how we get lock-free thread-safety
- *
- */
-static tps_roc_s states[2];
-
-static volatile int tpsRocIndex = 0;
-
-/**
- * this method is lock-free thread-safe if invoked only from one thread
- */
-void saveTpsState(time_t now, float curValue) {
- int tpsNextIndex = (tpsRocIndex + 1) % 2;
- tps_roc_s *cur = &states[tpsRocIndex];
- tps_roc_s *next = &states[tpsNextIndex];
-
- next->prevTime = cur->curTime;
- next->prevValue = cur->curValue;
- next->curTime = now;
- next->curValue = curValue;
-
- int diffSysticks = overflowDiff(now, cur->curTime);
- float diffSeconds = diffSysticks * 1.0 / CH_FREQUENCY;
- next->rateOfChange = (curValue - cur->curValue) / diffSeconds;
-
- // here we update volatile index
- tpsRocIndex = tpsNextIndex;
-}
-
-/**
- * this read-only method is lock-free thread-safe
- */
-float getTpsRateOfChange(void) {
- return states[tpsRocIndex].rateOfChange;
-}
-
-/*
- * Return current TPS position based on configured ADC levels, and adc
- *
- * */
-float getTpsValue(int adc) {
- if (adc < engineConfiguration->tpsMin)
- return 0;
- if (adc > engineConfiguration->tpsMax)
- return 100;
- // todo: double comparison using EPS
- if (engineConfiguration->tpsMin == engineConfiguration->tpsMax) {
- firmwareError("Invalid TPS configuration: same value");
- return 0;
- }
- return interpolate(engineConfiguration->tpsMin, 0, engineConfiguration->tpsMax, 100, adc);
-}
-
-/*
- * Return voltage on TPS AND channel
- * */
-float getTPSVoltage(void) {
- return getVoltageDivided(engineConfiguration->tpsAdcChannel);
-}
-
-/*
- * Return TPS ADC readings.
- * We need ADC value because TunerStudio has a nice TPS configuration wizard, and this wizard
- * wants a TPS value.
- */
-int getTPS10bitAdc(void) {
- int adc = getAdcValue(engineConfiguration->tpsAdcChannel);
- return (int) adc / 4; // Only for TunerStudio compatibility. Max TS adc value in 1023
-}
-
-/**
- * @brief Position on physical primary TPS
- */
-static float getPrimatyRawTPS(void) {
- // blue, 1st board
- /* PA7 - blue TP */
- float tpsValue = getTpsValue(getTPS10bitAdc());
- return tpsValue;
-}
-
-// todo: static float getSecondaryRawTPS
-
-/*
- * In case of dual TPS this function would return logical TPS position
- *
- * @return Current TPS position. 0 means idle and 100 means Wide Open Throttle
- */
-float getTPS(void) {
- // todo: if (config->isDualTps)
- // todo: blah blah
- // todo: if two TPS do not match - show OBD code via malfunction_central.c
-
- return getPrimatyRawTPS();
-}
-
-int convertVoltageTo10bitADC(float voltage) {
- // divided by 2 because of voltage divider, then converted into 10bit ADC value (TunerStudio format)
- return (int)(voltage / 2 * 1024 / 3.3);
-}
+#include "main.h"
+#include "boards.h"
+#include "tps.h"
+#include "engine_configuration.h"
+#include "interpolation.h"
+#include "adc_inputs.h"
+#include "wave_math.h"
+
+extern engine_configuration_s *engineConfiguration;
+
+/**
+ * We are using one instance for read and another for modification, this is how we get lock-free thread-safety
+ *
+ */
+static tps_roc_s states[2];
+
+static volatile int tpsRocIndex = 0;
+
+/**
+ * this method is lock-free thread-safe if invoked only from one thread
+ */
+void saveTpsState(time_t now, float curValue) {
+ int tpsNextIndex = (tpsRocIndex + 1) % 2;
+ tps_roc_s *cur = &states[tpsRocIndex];
+ tps_roc_s *next = &states[tpsNextIndex];
+
+ next->prevTime = cur->curTime;
+ next->prevValue = cur->curValue;
+ next->curTime = now;
+ next->curValue = curValue;
+
+ int diffSysticks = overflowDiff(now, cur->curTime);
+ float diffSeconds = diffSysticks * 1.0 / CH_FREQUENCY;
+ next->rateOfChange = (curValue - cur->curValue) / diffSeconds;
+
+ // here we update volatile index
+ tpsRocIndex = tpsNextIndex;
+}
+
+/**
+ * this read-only method is lock-free thread-safe
+ */
+float getTpsRateOfChange(void) {
+ return states[tpsRocIndex].rateOfChange;
+}
+
+/*
+ * Return current TPS position based on configured ADC levels, and adc
+ *
+ * */
+float getTpsValue(int adc) {
+ if (adc < engineConfiguration->tpsMin)
+ return 0;
+ if (adc > engineConfiguration->tpsMax)
+ return 100;
+ // todo: double comparison using EPS
+ if (engineConfiguration->tpsMin == engineConfiguration->tpsMax) {
+ firmwareError("Invalid TPS configuration: same value");
+ return 0;
+ }
+ return interpolate(engineConfiguration->tpsMin, 0, engineConfiguration->tpsMax, 100, adc);
+}
+
+/*
+ * Return voltage on TPS AND channel
+ * */
+float getTPSVoltage(void) {
+ return getVoltageDivided(engineConfiguration->tpsAdcChannel);
+}
+
+/*
+ * Return TPS ADC readings.
+ * We need ADC value because TunerStudio has a nice TPS configuration wizard, and this wizard
+ * wants a TPS value.
+ */
+int getTPS10bitAdc(void) {
+ int adc = getAdcValue(engineConfiguration->tpsAdcChannel);
+ return (int) adc / 4; // Only for TunerStudio compatibility. Max TS adc value in 1023
+}
+
+/**
+ * @brief Position on physical primary TPS
+ */
+static float getPrimatyRawTPS(void) {
+ // blue, 1st board
+ /* PA7 - blue TP */
+ float tpsValue = getTpsValue(getTPS10bitAdc());
+ return tpsValue;
+}
+
+// todo: static float getSecondaryRawTPS
+
+/*
+ * In case of dual TPS this function would return logical TPS position
+ *
+ * @return Current TPS position, percent of WOT. 0 means idle and 100 means Wide Open Throttle
+ */
+float getTPS(void) {
+ // todo: if (config->isDualTps)
+ // todo: blah blah
+ // todo: if two TPS do not match - show OBD code via malfunction_central.c
+
+ return getPrimatyRawTPS();
+}
+
+int convertVoltageTo10bitADC(float voltage) {
+ // divided by 2 because of voltage divider, then converted into 10bit ADC value (TunerStudio format)
+ return (int)(voltage / 2 * 1024 / 3.3);
+}
diff --git a/firmware/controllers/sensors/tps.h b/firmware/controllers/sensors/tps.h
index cb83094512..e414a78fa1 100644
--- a/firmware/controllers/sensors/tps.h
+++ b/firmware/controllers/sensors/tps.h
@@ -12,11 +12,6 @@
#include "global.h"
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
float getTPS(void);
int convertVoltageTo10bitADC(float voltage);
int getTPS10bitAdc(void);
@@ -39,8 +34,4 @@ typedef struct {
void saveTpsState(time_t now, float curValue);
float getTpsRateOfChange(void);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
#endif
diff --git a/firmware/controllers/settings.cpp b/firmware/controllers/settings.cpp
index 9159472f8b..1f47b43277 100644
--- a/firmware/controllers/settings.cpp
+++ b/firmware/controllers/settings.cpp
@@ -1,5 +1,5 @@
/**
- * @file settings.c
+ * @file settings.cpp
* @brief This file is about configuring engine via the human-readable protocol
*
* @date Dec 30, 2012
@@ -18,6 +18,7 @@
#include "tps.h"
#include "ec2.h"
#include "map.h"
+#include "trigger_decoder.h"
#if EFI_PROD_CODE
#include "rusefi.h"
@@ -91,11 +92,32 @@ const char* getConfigurationName(engine_configuration_s *engineConfiguration) {
return "Ford Escort GT";
case CITROEN_TU3JP:
return "Citroen TU3JP";
+ case ROVER_V8:
+ return "Rover v8";
default:
return NULL;
}
}
+static const char * pinModeToString(pin_output_mode_e mode) {
+ switch (mode) {
+ case OM_DEFAULT:
+ return "default";
+ case OM_INVERTED:
+ return "inverted";
+ case OM_OPENDRAIN:
+ return "open drain";
+ case OM_OPENDRAIN_INVERTED:
+ return "open drain inverted";
+ default:
+ return "unexpected";
+ }
+}
+
+static const char * boolToString(bool_t value) {
+ return value ? "Yes" : "No";
+}
+
/**
* @brief Prints current engine configuration to human-readable console.
*/
@@ -147,38 +169,41 @@ void printConfiguration(engine_configuration_s *engineConfiguration, engine_conf
// scheduleMsg(&logger, "crankingRpm: %d", engineConfiguration->crankingSettings.crankingRpm);
- scheduleMsg(&logger, "idlePinMode: %d", boardConfiguration->idleValvePinMode);
- scheduleMsg(&logger, "malfunctionIndicatorPinMode: %d", boardConfiguration->malfunctionIndicatorPinMode);
+ scheduleMsg(&logger, "idlePinMode: %s", pinModeToString(boardConfiguration->idleValvePinMode));
+ scheduleMsg(&logger, "malfunctionIndicatorPinMode: %s", pinModeToString(boardConfiguration->malfunctionIndicatorPinMode));
scheduleMsg(&logger, "analogInputDividerCoefficient: %f", engineConfiguration->analogInputDividerCoefficient);
- scheduleMsg(&logger, "needSecondTriggerInput: %d", engineConfiguration->needSecondTriggerInput);
+ scheduleMsg(&logger, "needSecondTriggerInput: %s", boolToString(engineConfiguration->needSecondTriggerInput));
#if EFI_PROD_CODE
scheduleMsg(&logger, "idleValvePin: %s", hwPortname(boardConfiguration->idleValvePin));
- scheduleMsg(&logger, "fuelPumpPin: mode %d @ %s", boardConfiguration->fuelPumpPinMode,
+ scheduleMsg(&logger, "fuelPumpPin: mode %s @ %s", pinModeToString(boardConfiguration->fuelPumpPinMode),
hwPortname(boardConfiguration->fuelPumpPin));
- scheduleMsg(&logger, "injectionPins: mode %d", boardConfiguration->injectionPinMode);
+ scheduleMsg(&logger, "injectionPins: mode %s", pinModeToString(boardConfiguration->injectionPinMode));
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
brain_pin_e brainPin = boardConfiguration->injectionPins[i];
scheduleMsg(&logger, "injection %d @ %s", i, hwPortname(brainPin));
}
- scheduleMsg(&logger, "ignitionPins: mode %d", boardConfiguration->ignitionPinMode);
+ scheduleMsg(&logger, "ignitionPins: mode %s", pinModeToString(boardConfiguration->ignitionPinMode));
// todo: calculate coils count based on ignition mode
for (int i = 0; i < 4; i++) {
brain_pin_e brainPin = boardConfiguration->ignitionPins[i];
scheduleMsg(&logger, "ignition %d @ %s", i, hwPortname(brainPin));
}
- scheduleMsg(&logger, "primary trigger simulator: %s %d", hwPortname(boardConfiguration->triggerSimulatorPins[0]),
- boardConfiguration->triggerSimulatorPinModes[0]);
- scheduleMsg(&logger, "secondary trigger simulator: %s %d", hwPortname(boardConfiguration->triggerSimulatorPins[1]),
- boardConfiguration->triggerSimulatorPinModes[1]);
+ scheduleMsg(&logger, "primary trigger simulator: %s %s", hwPortname(boardConfiguration->triggerSimulatorPins[0]),
+ pinModeToString(boardConfiguration->triggerSimulatorPinModes[0]));
+ scheduleMsg(&logger, "secondary trigger simulator: %s %s", hwPortname(boardConfiguration->triggerSimulatorPins[1]),
+ pinModeToString(boardConfiguration->triggerSimulatorPinModes[1]));
+
+ scheduleMsg(&logger, "primary trigger input: %s", hwPortname(boardConfiguration->primaryTriggerInputPin));
+
#endif /* EFI_PROD_CODE */
- scheduleMsg(&logger, "isInjectionEnabledFlag %d", engineConfiguration2->isInjectionEnabledFlag);
+ scheduleMsg(&logger, "isInjectionEnabledFlag %s", boolToString(engineConfiguration2->isInjectionEnabledFlag));
// appendPrintf(&logger, DELIMETER);
// scheduleLogging(&logger);
@@ -197,8 +222,9 @@ static void setTimingMode(int value) {
}
void setEngineType(int value) {
- engineConfiguration->engineType = (engine_type_e)value;
- resetConfigurationExt(&logger, (engine_type_e) value, engineConfiguration, engineConfiguration2, boardConfiguration);
+ engineConfiguration->engineType = (engine_type_e) value;
+ resetConfigurationExt(&logger, (engine_type_e) value, engineConfiguration, engineConfiguration2,
+ boardConfiguration);
#if EFI_INTERNAL_FLASH
writeToFlash();
// scheduleReset();
@@ -259,9 +285,9 @@ static void setRpmMultiplier(int value) {
doPrintConfiguration();
}
-static uint8_t pinNameBuffer[16];
+static char pinNameBuffer[16];
-static void printThermistor(char *msg, Thermistor *thermistor) {
+static void printThermistor(const char *msg, Thermistor *thermistor) {
int adcChannel = thermistor->channel;
float voltage = getVoltageDivided(adcChannel);
float r = getResistance(thermistor);
@@ -278,17 +304,15 @@ static void printThermistor(char *msg, Thermistor *thermistor) {
static void printMAPInfo(void) {
#if EFI_PROD_CODE
- scheduleMsg(&logger, "map type=%d raw=%f MAP=%f", engineConfiguration->map.sensor.sensorType, getRawMap(), getMap());
+ scheduleMsg(&logger, "map type=%d raw=%f MAP=%f", engineConfiguration->map.sensor.sensorType, getRawMap(),
+ getMap());
if (engineConfiguration->map.sensor.sensorType == MT_CUSTOM) {
- scheduleMsg(&logger, "min=%f max=%f", engineConfiguration->map.sensor.Min,
- engineConfiguration->map.sensor.Max);
+ scheduleMsg(&logger, "min=%f max=%f", engineConfiguration->map.sensor.Min, engineConfiguration->map.sensor.Max);
}
-
scheduleMsg(&logger, "baro type=%d value=%f", engineConfiguration->baroSensor.sensorType, getBaroPressure());
if (engineConfiguration->baroSensor.sensorType == MT_CUSTOM) {
- scheduleMsg(&logger, "min=%f max=%f", engineConfiguration->baroSensor.Min,
- engineConfiguration->baroSensor.Max);
+ scheduleMsg(&logger, "min=%f max=%f", engineConfiguration->baroSensor.Min, engineConfiguration->baroSensor.Max);
}
#endif
}
@@ -388,6 +412,13 @@ static void setIgnitionMode(int value) {
doPrintConfiguration();
}
+static void setTotalToothCount(int value) {
+ engineConfiguration->triggerConfig.totalToothCount = value;
+ initializeTriggerShape(&logger, engineConfiguration, engineConfiguration2);
+ incrementGlobalConfigurationVersion();
+ doPrintConfiguration();
+}
+
static void setCrankingChargeAngle(float value) {
engineConfiguration->crankingChargeAngle = value;
incrementGlobalConfigurationVersion();
@@ -500,5 +531,6 @@ void initSettings(void) {
addConsoleAction("enable_injection", enableInjection);
addConsoleAction("disable_injection", disableInjection);
+ addConsoleActionI("set_total_tooth_count", setTotalToothCount);
}
diff --git a/firmware/controllers/settings.h b/firmware/controllers/settings.h
index 9454133ae1..b255727362 100644
--- a/firmware/controllers/settings.h
+++ b/firmware/controllers/settings.h
@@ -17,9 +17,6 @@ extern "C"
void initSettings(void);
void pokeControl(void);
void setEngineType(int value);
-int getInjectionPeriod(void);
-int getInjectionOffset(void);
-int getInjectionDivider(void);
#ifdef __cplusplus
}
diff --git a/firmware/controllers/system/SingleTimerExecutor.cpp b/firmware/controllers/system/SingleTimerExecutor.cpp
index 2022a14ad9..3ec65e8b8d 100644
--- a/firmware/controllers/system/SingleTimerExecutor.cpp
+++ b/firmware/controllers/system/SingleTimerExecutor.cpp
@@ -4,7 +4,10 @@
* This class combines the powers of a 1MHz hardware timer from microsecond_timer.c
* and pending events queue event_queue.cpp
*
- * todo: add thread safety
+ * As of version 2.6.x, ChibiOS tick-based kernel is not capable of scheduling events
+ * with the level of precision we need, and realistically it should not.
+ *
+ * http://sourceforge.net/p/rusefi/tickets/24/
*
* @date: Apr 18, 2014
* @author Andrey Belomutskiy, (c) 2012-2014
@@ -78,7 +81,7 @@ void Executor::doExecute(uint64_t nowUs) {
* Let's set up the timer for the next execution
*/
uint64_t nextEventTime = queue.getNextEventTime(nowUs);
- efiAssert(nextEventTime > nowUs, "setTimer constraint");
+ efiAssertVoid(nextEventTime > nowUs, "setTimer constraint");
if (nextEventTime == EMPTY_QUEUE)
return; // no pending events in the queue
setHardwareUsTimer(nextEventTime - nowUs);
@@ -94,11 +97,7 @@ void Executor::doExecute(uint64_t nowUs) {
* @param [in] dwell the number of ticks of output duration.
*/
void scheduleTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) {
- efiAssert(delayUs >= 0, "delayUs"); // todo: remove this line?
- if (delayUs < 0) {
- firmwareError("Negative delayUs");
- return;
- }
+ efiAssertVoid(delayUs >= 0, "Negative delayUs");
if (delayUs == 0) {
callback(param);
return;
diff --git a/firmware/controllers/system/event_queue.cpp b/firmware/controllers/system/event_queue.cpp
index 47b333bf18..f62204c347 100644
--- a/firmware/controllers/system/event_queue.cpp
+++ b/firmware/controllers/system/event_queue.cpp
@@ -13,32 +13,13 @@
#include "event_queue.h"
#include "efitime.h"
-#include "utlist.h"
-
-#if EFI_SIGNAL_EXECUTOR_ONE_TIMER
-
-#define QUEUE_LENGTH_LIMIT 1000
EventQueue::EventQueue() {
head = NULL;
}
bool_t EventQueue::checkIfPending(scheduling_s *scheduling) {
- // this code is just to validate state, no functional load
- scheduling_s * current;
- int counter = 0;
- LL_FOREACH(head, current)
- {
- if (++counter > QUEUE_LENGTH_LIMIT) {
- firmwareError("Looped queue?");
- return FALSE;
- }
- if (current == scheduling) {
- warning(OBD_PCM_Processor_Fault, "re-adding element into event_queue: [%s]", scheduling->name);
- return TRUE;
- }
- }
- return FALSE;
+ return assertNotInList(head, scheduling);
}
void EventQueue::insertTask(scheduling_s *scheduling, uint64_t nowUs, int delayUs, schfunc_t callback, void *param) {
@@ -54,7 +35,6 @@ void EventQueue::insertTask(scheduling_s *scheduling, uint64_t nowUs, int delayU
scheduling->callback = callback;
scheduling->param = param;
-
LL_PREPEND(head, scheduling);
}
@@ -77,7 +57,7 @@ uint64_t EventQueue::getNextEventTime(uint64_t nowUs) {
firmwareError("Is this list looped #2?");
return EMPTY_QUEUE;
}
- efiAssert(current->momentUs > nowUs, "executeAll should have been called");
+ efiAssert(current->momentUs > nowUs, "executeAll should have been called", EMPTY_QUEUE);
if (current->momentUs < result)
result = current->momentUs;
}
@@ -122,9 +102,18 @@ int EventQueue::size(void) {
return result;
}
+scheduling_s *EventQueue::getForUnitText(int index) {
+ scheduling_s * current;
+
+ LL_FOREACH(head, current)
+ {
+ if (index == 0)
+ return current;
+ index--;
+ }
+ return NULL;
+}
+
void EventQueue::clear(void) {
head = NULL;
}
-
-#endif /* EFI_SIGNAL_EXECUTOR_ONE_TIMER */
-
diff --git a/firmware/controllers/system/event_queue.h b/firmware/controllers/system/event_queue.h
index 652955e19d..398b0a832b 100644
--- a/firmware/controllers/system/event_queue.h
+++ b/firmware/controllers/system/event_queue.h
@@ -6,12 +6,34 @@
*/
#include "scheduler.h"
+#include "utlist.h"
#ifndef EVENT_SCHEDULER_H_
#define EVENT_SCHEDULER_H_
#define EMPTY_QUEUE 0x0FFFFFFFFFFFFFFFLL
+#define QUEUE_LENGTH_LIMIT 1000
+
+template
+bool_t assertNotInList(T *head, T*element) {
+ // this code is just to validate state, no functional load
+ T * current;
+ int counter = 0;
+ LL_FOREACH(head, current)
+ {
+ if (++counter > QUEUE_LENGTH_LIMIT) {
+ firmwareError("Looped queue?");
+ return FALSE;
+ }
+ if (current == element) {
+ warning(OBD_PCM_Processor_Fault, "re-adding element into event_queue: [%s]", element->name);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
class EventQueue {
public:
EventQueue();
@@ -24,6 +46,7 @@ public:
uint64_t getNextEventTime(uint64_t nowUs);
void clear(void);
int size(void);
+ scheduling_s *getForUnitText(int index);
private:
bool_t checkIfPending(scheduling_s *scheduling);
scheduling_s *head;
diff --git a/firmware/controllers/system/pwm_generator_logic.cpp b/firmware/controllers/system/pwm_generator_logic.cpp
index bc43619a6e..8d62c6a739 100644
--- a/firmware/controllers/system/pwm_generator_logic.cpp
+++ b/firmware/controllers/system/pwm_generator_logic.cpp
@@ -1,5 +1,5 @@
/**
- * @file pwm_generator_logic.c
+ * @file pwm_generator_logic.cpp
*
* This PWM implementation keep track of when it would be the next time to toggle the signal.
* It constantly sets timer to that next toggle time, then sets the timer again from the callback, and so on.
@@ -8,15 +8,43 @@
* @author Andrey Belomutskiy, (c) 2012-2014
*/
+#include "main.h"
#include "pwm_generator_logic.h"
-PwmConfig::PwmConfig(float *st, single_wave_s *waves) :
- multiWave(st, waves) {
+/**
+ * We need to limit the number of iterations in order to avoid precision loss while calculating
+ * next toggle time
+ */
+#define ITERATION_LIMIT 10000
+
+SimplePwm::SimplePwm() {
+ wave.init(pinStates);
+ sr[0] = wave;
+ init(_switchTimes, sr);
+}
+
+PwmConfig::PwmConfig() {
scheduling.name = "PwmConfig";
}
+PwmConfig::PwmConfig(float *st, single_wave_s *waves) {
+ multiWave.init(st, waves);
+ scheduling.name = "PwmConfig";
+}
+
+void PwmConfig::init(float *st, single_wave_s *waves) {
+ multiWave.init(st, waves);
+}
+
+/**
+ * @param dutyCycle value between 0 and 1
+ */
+void SimplePwm::setSimplePwmDutyCycle(float dutyCycle) {
+ multiWave.setSwitchTime(0, dutyCycle);
+}
+
static uint64_t getNextSwitchTimeUs(PwmConfig *state) {
- chDbgAssert(state->safe.phaseIndex < PWM_PHASE_MAX_COUNT, "phaseIndex range", NULL);
+ efiAssert(state->safe.phaseIndex < PWM_PHASE_MAX_COUNT, "phaseIndex range", 0);
int iteration = state->safe.iteration;
float switchTime = state->multiWave.switchTimes[state->safe.phaseIndex];
float periodMs = state->safe.periodMs;
@@ -24,6 +52,10 @@ static uint64_t getNextSwitchTimeUs(PwmConfig *state) {
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
+ */
uint64_t timeToSwitchUs = (iteration + switchTime) * periodMs * 1000;
#if DEBUG_PWM
@@ -50,8 +82,8 @@ static uint64_t togglePwmState(PwmConfig *state) {
}
if (state->cycleCallback != NULL)
state->cycleCallback(state);
- chDbgAssert(state->periodMs != 0, "period not initialized", NULL);
- if (state->safe.periodMs != state->periodMs) {
+ 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
*/
@@ -65,7 +97,7 @@ static uint64_t togglePwmState(PwmConfig *state) {
}
state->stateChangeCallback(state,
- state->safe.phaseIndex == 0 ? state->multiWave.phaseCount - 1 : state->safe.phaseIndex - 1);
+ state->safe.phaseIndex == 0 ? state->phaseCount - 1 : state->safe.phaseIndex - 1);
uint64_t nextSwitchTimeUs = getNextSwitchTimeUs(state);
#if DEBUG_PWM
@@ -79,7 +111,7 @@ static uint64_t togglePwmState(PwmConfig *state) {
}
state->safe.phaseIndex++;
- if (state->safe.phaseIndex == state->multiWave.phaseCount) {
+ if (state->safe.phaseIndex == state->phaseCount) {
state->safe.phaseIndex = 0; // restart
state->safe.iteration++;
}
@@ -96,7 +128,7 @@ static void timerCallback(PwmConfig *state) {
* into our own permanent storage, right?
*/
void copyPwmParameters(PwmConfig *state, int phaseCount, float *switchTimes, int waveCount, int **pinStates) {
- state->multiWave.phaseCount = phaseCount;
+ state->phaseCount = phaseCount;
for (int phaseIndex = 0; phaseIndex < phaseCount; phaseIndex++) {
state->multiWave.switchTimes[phaseIndex] = switchTimes[phaseIndex];
@@ -112,7 +144,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) {
- chDbgCheck(state->periodMs != 0, "period is not initialized");
+ efiAssertVoid(state->periodMs != 0, "period is not initialized");
if (phaseCount == 0) {
firmwareError("signal length cannot be zero");
return;
@@ -125,7 +157,7 @@ void weComplexInit(const char *msg, PwmConfig *state, int phaseCount, float *swi
firmwareError("last switch time has to be 1");
return;
}
- chDbgCheck(waveCount > 0, "waveCount should be positive");
+ efiAssertVoid(waveCount > 0, "waveCount should be positive");
checkSwitchTimes2(phaseCount, switchTimes);
state->multiWave.waveCount = waveCount;
diff --git a/firmware/controllers/system/pwm_generator_logic.h b/firmware/controllers/system/pwm_generator_logic.h
index f7efaea2cb..0ca582e7db 100644
--- a/firmware/controllers/system/pwm_generator_logic.h
+++ b/firmware/controllers/system/pwm_generator_logic.h
@@ -35,13 +35,14 @@ class PwmConfig;
typedef void (pwm_cycle_callback)(PwmConfig *state);
typedef void (pwm_gen_callback)(PwmConfig *state, int stateIndex);
-
/**
* @brief Multi-channel software PWM output configuration
*/
class PwmConfig {
public:
+ PwmConfig();
PwmConfig(float *switchTimes, single_wave_s *waves);
+ void init(float *switchTimes, single_wave_s *waves);
io_pin_e outputPins[PWM_PHASE_MAX_WAVE_PER_PWM];
multi_wave_s multiWave;
const char *name;
@@ -54,6 +55,10 @@ public:
scheduling_s scheduling;
pwm_config_safe_state_s safe;
+ /**
+ * Number of events in the cycle
+ */
+ int phaseCount;
/**
* this callback is invoked before each wave generation cycle
@@ -66,6 +71,17 @@ public:
pwm_gen_callback *stateChangeCallback;
};
+
+class SimplePwm : public PwmConfig {
+public:
+ SimplePwm();
+ void setSimplePwmDutyCycle(float dutyCycle);
+ int pinStates[2];
+ single_wave_s wave;
+ single_wave_s sr[1];
+ float _switchTimes[2];
+};
+
#ifdef __cplusplus
extern "C"
{
diff --git a/firmware/controllers/system/scheduler.h b/firmware/controllers/system/scheduler.h
index a1cd0303cb..992795ba4e 100644
--- a/firmware/controllers/system/scheduler.h
+++ b/firmware/controllers/system/scheduler.h
@@ -17,13 +17,11 @@ struct scheduling_struct {
VirtualTimer timer;
#endif /* EFI_SIGNAL_EXECUTOR_SLEEP */
-#if EFI_SIGNAL_EXECUTOR_ONE_TIMER
volatile uint64_t momentUs;
schfunc_t callback;
void *param;
scheduling_s *next;
-#endif
- char *name;
+ const char *name;
};
#ifdef __cplusplus
diff --git a/firmware/controllers/trigger/main_trigger_callback.cpp b/firmware/controllers/trigger/main_trigger_callback.cpp
index a295d7a78b..f9578f46fe 100644
--- a/firmware/controllers/trigger/main_trigger_callback.cpp
+++ b/firmware/controllers/trigger/main_trigger_callback.cpp
@@ -23,17 +23,22 @@
#include "main.h"
+#if !EFI_PROD_CODE && !EFI_SIMULATOR
+
+#define chThdSelf() 0
+#define getRemainingStack(x) (999999)
+
+#endif
+
#if EFI_ENGINE_CONTROL
#include "main_trigger_callback.h"
#include "ec2.h"
-extern "C" {
+#include "engine_math.h"
#include "trigger_central.h"
#include "rpm_calculator.h"
#include "signal_executor.h"
-#include "eficonsole.h"
-#include "engine_math.h"
#include "engine_configuration.h"
#include "interpolation.h"
#include "advance_map.h"
@@ -42,36 +47,46 @@ extern "C" {
#include "histogram.h"
#include "fuel_math.h"
#include "histogram.h"
+#if EFI_PROD_CODE
#include "rfiutil.h"
+#endif /* EFI_HISTOGRAMS */
#include "LocalVersionHolder.h"
+#include "event_queue.h"
+#include "engine.h"
static LocalVersionHolder localVersion;
-int isInjectionEnabled(void);
+extern Engine engine;
-}
+static MainTriggerCallback mainTriggerCallbackInstance;
-extern engine_configuration_s *engineConfiguration;
-extern engine_configuration2_s *engineConfiguration2;
+/**
+ * That's the list of pending spark firing events
+ */
+static IgnitionEvent *iHead = NULL;
+
+/**
+ * In order to archive higher event precision, we are using a hybrid approach
+ * where we are scheduling events based on the closest trigger event with a time offset.
+ *
+ * This queue is using global trigger event index as 'time'
+ */
+static EventQueue triggerEventsQueue;
static cyclic_buffer ignitionErrorDetection;
static Logging logger;
-/**
- * this field is accessed only from shaft sensor event handler.
- * This is not a method variable just to save us from stack overflow.
- */
-static ActuatorEventList events;
-
-static void handleFuelInjectionEvent(ActuatorEvent *event, int rpm) {
- float fuelMs = getFuelMs(rpm) * engineConfiguration->globalFuelCorrection;
+static void handleFuelInjectionEvent(MainTriggerCallback *mainTriggerCallback, ActuatorEvent *event, int rpm) {
+ float fuelMs = getFuelMs(rpm) * mainTriggerCallback->engineConfiguration->globalFuelCorrection;
if (fuelMs < 0) {
+#if EFI_PROD_CODE
scheduleMsg(&logger, "ERROR: negative injectionPeriod %f", fuelMs);
+#endif
return;
}
- float delay = getOneDegreeTimeMs(rpm) * event->angleOffset;
+ float delay = getOneDegreeTimeMs(rpm) * event->position.angleOffset;
// if (isCranking())
// scheduleMsg(&logger, "crankingFuel=%f for CLT=%fC", fuelMs, getCoolantTemperature());
@@ -79,10 +94,12 @@ static void handleFuelInjectionEvent(ActuatorEvent *event, int rpm) {
scheduleOutput(event->actuator, delay, fuelMs);
}
-static void handleFuel(int eventIndex, int rpm) {
- if (!isInjectionEnabled())
+static void handleFuel(MainTriggerCallback *mainTriggerCallback, int eventIndex, int rpm) {
+ if (!isInjectionEnabled(mainTriggerCallback->engineConfiguration2))
return;
- chDbgCheck(eventIndex < engineConfiguration2->triggerShape.shaftPositionEventCount, "event index");
+ efiAssertVoid(getRemainingStack(chThdSelf()) > 16, "stack#3");
+ efiAssertVoid(eventIndex < 2 * mainTriggerCallback->engineConfiguration2->triggerShape.shaftPositionEventCount,
+ "event index");
/**
* Ignition events are defined by addFuelEvents() according to selected
@@ -90,39 +107,88 @@ static void handleFuel(int eventIndex, int rpm) {
*/
ActuatorEventList *source =
isCranking() ?
- &engineConfiguration2->engineEventConfiguration.crankingInjectionEvents :
- &engineConfiguration2->engineEventConfiguration.injectionEvents;
- findEvents(eventIndex, source, &events);
+ &mainTriggerCallback->engineConfiguration2->engineEventConfiguration.crankingInjectionEvents :
+ &mainTriggerCallback->engineConfiguration2->engineEventConfiguration.injectionEvents;
- if (events.size == 0)
- return;
-
- for (int i = 0; i < events.size; i++) {
- ActuatorEvent *event = &events.events[i];
- handleFuelInjectionEvent(event, rpm);
+ for (int i = 0; i < source->size; i++) {
+ ActuatorEvent *event = &source->events[i];
+ if (event->position.eventIndex != eventIndex)
+ continue;
+ handleFuelInjectionEvent(mainTriggerCallback, event, rpm);
}
}
-static void handleSparkEvent(ActuatorEvent *event, int rpm) {
- float dwellMs = getSparkDwellMs(rpm);
+static void handleSparkEvent(MainTriggerCallback *mainTriggerCallback, int eventIndex, IgnitionEvent *iEvent, int rpm) {
+ engine_configuration_s *engineConfiguration = mainTriggerCallback->engineConfiguration;
+ engine_configuration2_s *engineConfiguration2 = mainTriggerCallback->engineConfiguration2;
+
+ float dwellMs = getSparkDwellMsT(engineConfiguration, rpm);
if (cisnan(dwellMs) || dwellMs < 0) {
firmwareError("invalid dwell: %f at %d", dwellMs, rpm);
return;
}
- float sparkDelay = getOneDegreeTimeMs(rpm) * event->angleOffset;
+ ActuatorEvent *event = &iEvent->actuator;
+ float sparkDelay = getOneDegreeTimeMs(rpm) * event->position.angleOffset;
int isIgnitionError = sparkDelay < 0;
ignitionErrorDetection.add(isIgnitionError);
if (isIgnitionError) {
+#if EFI_PROD_CODE
scheduleMsg(&logger, "Negative spark delay=%f", sparkDelay);
+#endif
sparkDelay = 0;
- //return;
+ return;
}
- scheduleOutput(event->actuator, sparkDelay, dwellMs);
+ OutputSignal *signal = event->actuator;
+ //scheduleOutput(event->actuator, sparkDelay, dwellMs);
+
+ if (cisnan(dwellMs)) {
+ firmwareError("NaN in scheduleOutput", dwellMs);
+ return;
+ }
+
+ /**
+ * We are alternating two event lists in order to avoid a potential issue around revolution boundary
+ * when an event is scheduled within the next revolution.
+ */
+ scheduling_s * sUp = &signal->signalTimerUp[0];
+ scheduling_s * sDown = &signal->signalTimerDown[0];
+
+ /**
+ * 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 *) signal);
+ /**
+ * Spark event is often happening during a later trigger event timeframe
+ * TODO: improve precision
+ */
+
+ findTriggerPosition(engineConfiguration, &engineConfiguration2->triggerShape, &iEvent->sparkPosition,
+ iEvent->advance);
+
+ if (iEvent->sparkPosition.eventIndex == eventIndex) {
+ /**
+ * Spark should be fired before the next trigger event - time-based delay is best precision possible
+ */
+ float timeTillIgnitionUs = getOneDegreeTimeUs(rpm) * iEvent->sparkPosition.angleOffset;
+
+ scheduleTask(sDown, (int) timeTillIgnitionUs, (schfunc_t) &turnPinLow, (void*) signal);
+ } else {
+ /**
+ * Spark should be scheduled in relation to some future trigger event, this way we get better firing precision
+ */
+ bool isPending = assertNotInList(iHead, iEvent);
+ if (isPending)
+ return;
+
+ LL_APPEND(iHead, iEvent);
+
+ //scheduleTask(sDown, (int) MS2US(sparkDelay + dwellMs), (schfunc_t) &turnPinLow, (void*) signal);
+ }
}
-static void handleSpark(int eventIndex, int rpm, ActuatorEventList *list) {
+static void handleSpark(MainTriggerCallback *mainTriggerCallback, int eventIndex, int rpm, IgnitionEventList *list) {
if (!isValidRpm(rpm))
return; // this might happen for instance in case of a single trigger event after a pause
@@ -130,30 +196,49 @@ static void handleSpark(int eventIndex, int rpm, ActuatorEventList *list) {
* Ignition schedule is defined once per revolution
* See initializeIgnitionActions()
*/
- findEvents(eventIndex, list, &events);
- if (events.size == 0)
- return;
+
+ IgnitionEvent *current, *tmp;
+
+ LL_FOREACH_SAFE(iHead, current, tmp)
+ {
+ if (current->sparkPosition.eventIndex == eventIndex) {
+ // time to fire a spark which was scheduled previously
+ LL_DELETE(iHead, current);
+
+ ActuatorEvent *event = ¤t->actuator;
+ OutputSignal *signal = event->actuator;
+ scheduling_s * sDown = &signal->signalTimerDown[0];
+
+ float timeTillIgnitionUs = getOneDegreeTimeUs(rpm) * current->sparkPosition.angleOffset;
+ scheduleTask(sDown, (int) timeTillIgnitionUs, (schfunc_t) &turnPinLow, (void*) signal);
+ }
+ }
// scheduleSimpleMsg(&logger, "eventId spark ", eventIndex);
-
- for (int i = 0; i < events.size; i++) {
- ActuatorEvent *event = &events.events[i];
- handleSparkEvent(event, rpm);
+ for (int i = 0; i < list->size; i++) {
+ IgnitionEvent *event = &list->events[i];
+ if (event->actuator.position.eventIndex != eventIndex)
+ continue;
+ handleSparkEvent(mainTriggerCallback, eventIndex, event, rpm);
}
}
static histogram_s mainLoopHisto;
void showMainHistogram(void) {
+#if EFI_PROD_CODE
printHistogram(&logger, &mainLoopHisto);
+#endif
}
/**
* This is the main trigger event handler.
* Both injection and ignition are controlled from this method.
*/
-static void onTriggerEvent(ShaftEvents ckpSignalType, int eventIndex) {
- chDbgCheck(eventIndex < engineConfiguration2->triggerShape.shaftPositionEventCount, "event index");
+void onTriggerEvent(trigger_event_e ckpSignalType, int eventIndex, MainTriggerCallback *mainTriggerCallback) {
+ efiAssertVoid(eventIndex < 2 * mainTriggerCallback->engineConfiguration2->triggerShape.shaftPositionEventCount,
+ "event index");
+ efiAssertVoid(getRemainingStack(chThdSelf()) > 16, "stack#3");
int rpm = getRpm();
if (rpm == 0) {
@@ -165,18 +250,20 @@ static void onTriggerEvent(ShaftEvents ckpSignalType, int eventIndex) {
warning(OBD_Camshaft_Position_Sensor_Circuit_Range_Performance, "noisy trigger");
return;
}
- if (rpm > engineConfiguration->rpmHardLimit) {
+ if (rpm > mainTriggerCallback->engineConfiguration->rpmHardLimit) {
warning(OBD_PCM_Processor_Fault, "skipping stroke due to rpm=%d", rpm);
return;
}
+#if EFI_HISTOGRAMS && EFI_PROD_CODE
int beforeCallback = hal_lld_get_counter_value();
+#endif
int revolutionIndex = getRevolutionCounter() % 2;
if (eventIndex == 0) {
if (localVersion.isOld())
- prepareOutputSignals(engineConfiguration, engineConfiguration2);
+ prepareOutputSignals(mainTriggerCallback->engineConfiguration, mainTriggerCallback->engineConfiguration2);
/**
* TODO: warning. there is a bit of a hack here, todo: improve.
@@ -190,24 +277,29 @@ static void onTriggerEvent(ShaftEvents ckpSignalType, int eventIndex) {
* Within one engine cycle all cylinders are fired with same timing advance.
* todo: one day we can control cylinders individually
*/
- float dwellMs = getSparkDwellMs(rpm);
+ float dwellMs = getSparkDwellMsT(mainTriggerCallback->engineConfiguration, rpm);
if (cisnan(dwellMs) || dwellMs < 0) {
firmwareError("invalid dwell: %f at %d", dwellMs, rpm);
return;
}
- float advance = getAdvance(rpm, getEngineLoad());
+ float advance = getAdvance(rpm, getEngineLoadT(mainTriggerCallback->engineConfiguration));
float dwellAngle = dwellMs / getOneDegreeTimeMs(rpm);
- initializeIgnitionActions(advance - dwellAngle, engineConfiguration, engineConfiguration2, dwellMs, &engineConfiguration2->engineEventConfiguration.ignitionEvents[revolutionIndex]);
+ initializeIgnitionActions(advance, dwellAngle, mainTriggerCallback->engineConfiguration,
+ mainTriggerCallback->engineConfiguration2,
+ &mainTriggerCallback->engineConfiguration2->engineEventConfiguration.ignitionEvents[revolutionIndex]);
}
- handleFuel(eventIndex, rpm);
- handleSpark(eventIndex, rpm, &engineConfiguration2->engineEventConfiguration.ignitionEvents[revolutionIndex]);
-#if EFI_HISTOGRAMS
+ triggerEventsQueue.executeAll(getCrankEventCounter());
+
+ handleFuel(mainTriggerCallback, eventIndex, rpm);
+ handleSpark(mainTriggerCallback, eventIndex, rpm,
+ &mainTriggerCallback->engineConfiguration2->engineEventConfiguration.ignitionEvents[revolutionIndex]);
+#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 */
}
@@ -218,25 +310,37 @@ static void showTriggerHistogram(void) {
static void showMainInfo(void) {
int rpm = getRpm();
- float el = getEngineLoad();
+ float el = getEngineLoadT(mainTriggerCallbackInstance.engineConfiguration);
+#if EFI_PROD_CODE
scheduleMsg(&logger, "rpm %d engine_load %f", rpm, el);
scheduleMsg(&logger, "fuel %fms timing %f", getFuelMs(rpm), getAdvance(rpm, el));
+#endif
}
-void initMainEventListener() {
+void MainTriggerCallback::init(engine_configuration_s *engineConfiguration,
+ engine_configuration2_s *engineConfiguration2) {
+ this->engineConfiguration = engineConfiguration;
+ this->engineConfiguration2 = engineConfiguration2;
+}
+
+void initMainEventListener(engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2) {
+ mainTriggerCallbackInstance.init(engineConfiguration, engineConfiguration2);
+
+#if EFI_PROD_CODE
addConsoleAction("performanceinfo", showTriggerHistogram);
addConsoleAction("maininfo", showMainInfo);
initLogging(&logger, "main event handler");
printMsg(&logger, "initMainLoop: %d", currentTimeMillis());
+ if (!isInjectionEnabled(mainTriggerCallbackInstance.engineConfiguration2))
+ printMsg(&logger, "!!!!!!!!!!!!!!!!!!! injection disabled");
+#endif
+
#if EFI_HISTOGRAMS
initHistogram(&mainLoopHisto, "main callback");
#endif /* EFI_HISTOGRAMS */
- if (!isInjectionEnabled())
- printMsg(&logger, "!!!!!!!!!!!!!!!!!!! injection disabled");
-
- addTriggerEventListener(&onTriggerEvent, "main loop");
+ addTriggerEventListener((ShaftPositionListener) &onTriggerEvent, "main loop", &mainTriggerCallbackInstance);
}
int isIgnitionTimingError(void) {
diff --git a/firmware/controllers/trigger/rpm_calculator.cpp b/firmware/controllers/trigger/rpm_calculator.cpp
index 62ae09c22f..edab831516 100644
--- a/firmware/controllers/trigger/rpm_calculator.cpp
+++ b/firmware/controllers/trigger/rpm_calculator.cpp
@@ -12,25 +12,29 @@
#include "main.h"
-#if EFI_SHAFT_POSITION_INPUT
-
#include "rpm_calculator.h"
-#include "trigger_central.h"
-#include "engine_configuration.h"
-#include "ec2.h"
-#include "engine_math.h"
-#include "rfiutil.h"
-
-#if EFI_ANALOG_CHART
-#include "analog_chart.h"
-#endif /* EFI_PROD_CODE */
#if EFI_WAVE_CHART
#include "wave_chart.h"
extern WaveChart waveChart;
#endif /* EFI_WAVE_CHART */
-static rpm_s rpmState;
+#if EFI_SHAFT_POSITION_INPUT
+
+#include "trigger_central.h"
+#include "engine_configuration.h"
+#include "ec2.h"
+#include "engine_math.h"
+#if EFI_PROD_CODE
+#include "rfiutil.h"
+#include "engine.h"
+#endif
+
+#if EFI_ANALOG_CHART
+#include "analog_chart.h"
+#endif /* EFI_PROD_CODE */
+
+static RpmCalculator rpmState;
#define UNREALISTIC_RPM 30000
@@ -39,17 +43,33 @@ static rpm_s rpmState;
extern engine_configuration_s *engineConfiguration;
extern engine_configuration2_s *engineConfiguration2;
+#if EFI_PROD_CODE || EFI_SIMULATOR
static Logging logger;
+extern Engine engine;
+#endif
+
+RpmCalculator::RpmCalculator() {
+ rpmValue = 0;
+
+ // we need this initial to have not_running at first invocation
+ lastRpmEventTimeUs = (uint64_t) -10 * US_PER_SECOND;
+}
/**
* @return true if there was a full shaft revolution within the last second
*/
-bool_t isRunning(void) {
+bool RpmCalculator::isRunning(void) {
uint64_t nowUs = getTimeNowUs();
- return nowUs - rpmState.lastRpmEventTimeUs < US_PER_SECOND;
+ return nowUs - lastRpmEventTimeUs < US_PER_SECOND;
}
-bool_t isValidRpm(int rpm) {
+int RpmCalculator::rpm(void) {
+ if (!this->isRunning())
+ return 0;
+ return rpmValue;
+}
+
+bool isValidRpm(int rpm) {
return rpm > 0 && rpm < UNREALISTIC_RPM;
}
@@ -57,18 +77,19 @@ uint64_t getLastRpmEventTime(void) {
return rpmState.lastRpmEventTimeUs;
}
-bool_t isCranking(void) {
+#if EFI_PROD_CODE || EFI_SIMULATOR
+bool isCranking(void) {
int rpm = getRpm();
return isCrankingR(rpm);
}
+#endif
/**
* @return -1 in case of isNoisySignal(), current RPM otherwise
*/
-int getRpm() {
- if (!isRunning())
- return 0;
- return rpmState.rpm;
+int getRpmE(Engine *engine) {
+ efiAssert(engine->rpmCalculator!=NULL, "rpmCalculator not assigned", -1);
+ return engine->rpmCalculator->rpm();
}
/**
@@ -77,7 +98,7 @@ int getRpm() {
float getCrankshaftAngle(uint64_t timeUs) {
uint64_t timeSinceZeroAngle = timeUs - rpmState.lastRpmEventTimeUs;
- float cRevolutionTimeMs = getCrankshaftRevolutionTimeMs(rpmState.rpm);
+ float cRevolutionTimeMs = getCrankshaftRevolutionTimeMs(rpmState.rpm());
return 360.0 * timeSinceZeroAngle / cRevolutionTimeMs / 1000;
}
@@ -92,18 +113,16 @@ int getRevolutionCounter(void) {
*
* @return TRUE if noise is detected
*/
-static int isNoisySignal(rpm_s * rpmState, uint64_t nowUs) {
+static int isNoisySignal(RpmCalculator * rpmState, uint64_t nowUs) {
uint64_t diff = nowUs - rpmState->lastRpmEventTimeUs;
- return diff < 1000; // that's 1ms
+ /**
+ * 60/2 wheel at 8000 rpm
+ * 60000000us / 8000 / 120 = 62us
+ */
+ return diff < 40; // that's 40us
}
-static uint8_t shaft_signal_msg_index[15];
-
-void addWaveChartEvent(const char *name, const char * msg, const char *msg2) {
-#if EFI_WAVE_CHART
- addWaveChartEvent3(&waveChart, name, msg, msg2);
-#endif /* EFI_WAVE_CHART */
-}
+static char shaft_signal_msg_index[15];
/**
* @brief Shaft position callback used by RPM calculation logic.
@@ -112,7 +131,7 @@ void addWaveChartEvent(const char *name, const char * msg, const char *msg2) {
* updated here.
* This callback is invoked on interrupt thread.
*/
-static void shaftPositionCallback(ShaftEvents ckpSignalType, int index) {
+void shaftPositionCallback(trigger_event_e ckpSignalType, int index, RpmCalculator *rpmState) {
itoa10(&shaft_signal_msg_index[1], index);
if (ckpSignalType == SHAFT_PRIMARY_UP) {
addWaveChartEvent("crank", "up", (char*) shaft_signal_msg_index);
@@ -131,28 +150,30 @@ static void shaftPositionCallback(ShaftEvents ckpSignalType, int index) {
#endif
return;
}
- rpmState.revolutionCounter++;
+ rpmState->revolutionCounter++;
uint64_t nowUs = getTimeNowUs();
- int hadRpmRecently = isRunning();
+ bool_t hadRpmRecently = rpmState->isRunning();
if (hadRpmRecently) {
- if (isNoisySignal(&rpmState, nowUs)) {
+ if (isNoisySignal(rpmState, nowUs)) {
// unexpected state. Noise?
- rpmState.rpm = NOISY_RPM;
+ rpmState->rpmValue = NOISY_RPM;
} else {
- uint64_t diff = nowUs - rpmState.lastRpmEventTimeUs;
+ uint64_t diff = nowUs - rpmState->lastRpmEventTimeUs;
// 60000 because per minute
// * 2 because each revolution of crankshaft consists of two camshaft revolutions
- // / 4 because each cylinder sends a signal
// need to measure time from the previous non-skipped event
+ /**
+ * Four stroke cycle is two crankshaft revolutions
+ */
- int rpm = (int) (60 * US_PER_SECOND / engineConfiguration->rpmMultiplier / diff);
- rpmState.rpm = rpm > UNREALISTIC_RPM ? NOISY_RPM : rpm;
+ int rpm = (int) (60 * US_PER_SECOND * 2 / diff);
+ rpmState->rpmValue = rpm > UNREALISTIC_RPM ? NOISY_RPM : rpm;
}
}
- rpmState.lastRpmEventTimeUs = nowUs;
+ rpmState->lastRpmEventTimeUs = nowUs;
#if EFI_ANALOG_CHART
if (engineConfiguration->analogChartMode == AC_TRIGGER)
acAddData(getCrankshaftAngle(nowUs), index);
@@ -161,37 +182,38 @@ static void shaftPositionCallback(ShaftEvents ckpSignalType, int index) {
static scheduling_s tdcScheduler[2];
-static uint8_t rpmBuffer[10];
+static char rpmBuffer[10];
+#if EFI_PROD_CODE || EFI_SIMULATOR
static void onTdcCallback(void) {
itoa10(rpmBuffer, getRpm());
addWaveChartEvent(TOP_DEAD_CENTER_MESSAGE, (char*) rpmBuffer, "");
}
-static void tdcMarkCallback(ShaftEvents ckpSignalType, int index) {
+static void tdcMarkCallback(trigger_event_e ckpSignalType, int index, void *arg) {
if (index == 0) {
int index = getRevolutionCounter() % 2;
scheduleByAngle(&tdcScheduler[index], engineConfiguration->globalTriggerAngleOffset, (schfunc_t) onTdcCallback, NULL);
}
}
+#endif
void initRpmCalculator(void) {
+#if EFI_PROD_CODE || EFI_SIMULATOR
initLogging(&logger, "rpm calc");
+ engine.rpmCalculator = &rpmState;
tdcScheduler[0].name = "tdc0";
tdcScheduler[1].name = "tdc1";
+ addTriggerEventListener(&tdcMarkCallback, "chart TDC mark", NULL);
+#endif
strcpy((char*) shaft_signal_msg_index, "_");
- rpmState.rpm = 0;
-
- // we need this initial to have not_running at first invocation
- rpmState.lastRpmEventTimeUs = (uint64_t) -10 * US_PER_SECOND;
-
- addTriggerEventListener(&shaftPositionCallback, "rpm reporter");
- addTriggerEventListener(&tdcMarkCallback, "chart TDC mark");
+ addTriggerEventListener((ShaftPositionListener)&shaftPositionCallback, "rpm reporter", &rpmState);
}
+#if EFI_PROD_CODE || EFI_SIMULATOR
/**
* Schedules a callback 'angle' degree of crankshaft from now.
* The callback would be executed once after the duration of time which
@@ -211,5 +233,12 @@ void scheduleByAngle(scheduling_s *timer, float angle, schfunc_t callback, void
}
scheduleTask(timer, (int)MS2US(delayMs), callback, param);
}
+#endif
#endif /* EFI_SHAFT_POSITION_INPUT */
+
+void addWaveChartEvent(const char *name, const char * msg, const char *msg2) {
+#if EFI_WAVE_CHART
+ addWaveChartEvent3(&waveChart, name, msg, msg2);
+#endif /* EFI_WAVE_CHART */
+}
diff --git a/firmware/controllers/trigger/rpm_calculator.h b/firmware/controllers/trigger/rpm_calculator.h
index 89f323ae4a..275366fd3d 100644
--- a/firmware/controllers/trigger/rpm_calculator.h
+++ b/firmware/controllers/trigger/rpm_calculator.h
@@ -13,15 +13,31 @@
#define NOISY_RPM -1
-typedef struct {
- volatile int rpm;
+#ifdef __cplusplus
+#include "engine.h"
+
+class RpmCalculator {
+public:
+ RpmCalculator();
+ int rpm(void);
+ volatile int rpmValue;
volatile uint64_t lastRpmEventTimeUs;
/**
* This counter is incremented with each revolution of one of the shafts. Could be
* crankshaft could be camshaft.
*/
volatile int revolutionCounter;
-} rpm_s;
+ bool isRunning(void);
+};
+
+#define getRpm() getRpmE(&engine)
+
+/**
+ * @brief Current RPM
+ */
+int getRpmE(Engine *engine);
+void shaftPositionCallback(trigger_event_e ckpSignalType, int index, RpmCalculator *rpmState);
+#endif
#ifdef __cplusplus
extern "C"
@@ -32,17 +48,13 @@ extern "C"
* @brief Initialize RPM calculator
*/
void initRpmCalculator(void);
-/**
- * @brief Current RPM
- */
-int getRpm(void);
-bool_t isCranking(void);
+bool isCranking(void);
uint64_t getLastRpmEventTime(void);
int getRevolutionCounter(void);
float getCrankshaftAngle(uint64_t timeUs);
-bool_t isRunning(void);
-bool_t isValidRpm(int rpm);
+bool isRunning(void);
+bool isValidRpm(int rpm);
void addWaveChartEvent(const char *name, const char *msg, const char *msg2);
#ifdef __cplusplus
diff --git a/firmware/controllers/trigger/trigger_bmw.cpp b/firmware/controllers/trigger/trigger_bmw.cpp
index 73d5d46999..fee005e3b1 100644
--- a/firmware/controllers/trigger/trigger_bmw.cpp
+++ b/firmware/controllers/trigger/trigger_bmw.cpp
@@ -7,31 +7,62 @@
#include "trigger_bmw.h"
+static inline float addPair(trigger_shape_s *s, float a, float w) {
+ s->addEvent(a, T_SECONDARY, TV_HIGH);
+ a += w;
+ s->addEvent(a, T_SECONDARY, TV_LOW);
+ a += w;
+ return a;
+}
+
void configureMiniCooperTriggerShape(engine_configuration_s *engineConfiguration,
engine_configuration2_s *engineConfiguration2) {
trigger_shape_s *s = &engineConfiguration2->triggerShape;
s->reset();
- s->initialState[0] = 1;
+// s->initialState[0] = 1;
- float w = 2.96;
+ float w = 360.0 / 121;
float a = w / 2;
- for (int i = 0; i < 19; i++) {
- a += w;
- s->addEvent(a, T_SECONDARY, TV_HIGH);
- a += w;
- s->addEvent(a, T_SECONDARY, TV_LOW);
- }
- a += 3 * w;
- s->addEvent(a, T_SECONDARY, TV_HIGH);
- a += 2 * w;
s->addEvent(a, T_SECONDARY, TV_LOW);
+ a += w;
+ for (int i = 0; i <= 22; i++)
+ a = addPair(s, a, w);
+ a += 3 * w;
- s->addEvent(376.4444444, T_PRIMARY, TV_LOW);
- s->addEvent(720, T_PRIMARY, TV_HIGH);
+ float firstGapAngle = a;
+
+ s->addEvent(a, T_SECONDARY, TV_HIGH);
+ a += 3 * w;
+ s->addEvent(a, T_SECONDARY, TV_LOW);
+ a += w;
+
+ for (int i = 0; i < 36; i++)
+ a = addPair(s, a, w);
+
+ s->addEvent(376, T_PRIMARY, TV_HIGH);
+
+ for (int i = 0; i < 21; i++)
+ a = addPair(s, a, w);
+ a += 3 * w;
+
+ efiAssertVoid(absF(firstGapAngle + 360 - a) < 0.1, "shape constraint");
+
+ s->addEvent(a, T_SECONDARY, TV_HIGH);
+ a += 3 * w;
+ s->addEvent(a, T_SECONDARY, TV_LOW);
+ a += w;
+
+ for (int i = 0; i < 33; i++)
+ a = addPair(s, a, w);
+
+ efiAssertVoid(absF(720 - w / 2 - a) < 0.1, "shape constraint");
+ s->addEvent(a, T_SECONDARY, TV_HIGH);
+
+ s->addEvent(720, T_PRIMARY, TV_LOW);
s->shaftPositionEventCount = s->getSize();
/**
diff --git a/firmware/controllers/trigger/trigger_central.cpp b/firmware/controllers/trigger/trigger_central.cpp
index dfe3f39183..c11d787ac0 100644
--- a/firmware/controllers/trigger/trigger_central.cpp
+++ b/firmware/controllers/trigger/trigger_central.cpp
@@ -1,5 +1,5 @@
/*
- * @file trigger_central.c
+ * @file trigger_central.cpp
*
* @date Feb 23, 2014
* @author Andrey Belomutskiy, (c) 2012-2014
@@ -10,34 +10,34 @@
#include "trigger_decoder.h"
#include "main_trigger_callback.h"
#include "engine_configuration.h"
-#include "histogram.h"
-#include "rfiutil.h"
#include "listener_array.h"
#include "wave_math.h"
#include "data_buffer.h"
-
-#if defined __GNUC__
-static histogram_s triggerCallback __attribute__((section(".ccm")));
-#else
-static histogram_s triggerCallback;
+#include "histogram.h"
+#if EFI_PROD_CODE
+#include "rfiutil.h"
#endif
-extern engine_configuration_s *engineConfiguration;
-extern engine_configuration2_s *engineConfiguration2;
+static histogram_s triggerCallback;
// we need this initial to have not_running at first invocation
static volatile uint64_t previousShaftEventTime = (efitimems_t) -10 * US_PER_SECOND;
-static IntListenerArray triggerListeneres;
+TriggerCentral triggerCentral;
static Logging logging;
-static trigger_state_s triggerState;
+uint64_t getCrankEventCounter() {
+ return triggerCentral.triggerState.getTotalEventCounter();
+}
-static volatile int shaftEventCounter;
+uint64_t getStartOfRevolutionIndex() {
+ return triggerCentral.triggerState.getStartOfRevolutionIndex();
+}
-int getCrankEventCounter() {
- return shaftEventCounter;
+void TriggerCentral::addEventListener(ShaftPositionListener listener, const char *name, void *arg) {
+ print("registerCkpListener: %s\r\n", name);
+ registerCallback(&triggerListeneres, (IntListener) listener, arg);
}
/**
@@ -46,26 +46,36 @@ int getCrankEventCounter() {
* Trigger event listener would be invoked on each trigger event. For example, for a 60/2 wheel
* that would be 116 events: 58 SHAFT_PRIMARY_UP and 58 SHAFT_PRIMARY_DOWN events.
*/
-void addTriggerEventListener(ShaftPositionListener handler, const char *name) {
- print("registerCkpListener: %s\r\n", name);
- registerCallback(&triggerListeneres, (IntListener) handler, NULL);
+void addTriggerEventListener(ShaftPositionListener listener, const char *name, void *arg) {
+ triggerCentral.addEventListener(listener, name, arg);
}
-#define HW_EVENT_TYPES 4
+#if EFI_PROD_CODE || EFI_SIMULATOR
+extern configuration_s *configuration;
-static int hwEventCounters[HW_EVENT_TYPES];
+void hwHandleShaftSignal(trigger_event_e signal) {
+ triggerCentral.handleShaftSignal(configuration, signal, getTimeNowUs());
+}
+#endif /* EFI_PROD_CODE */
-void hwHandleShaftSignal(ShaftEvents signal) {
- chDbgCheck(engineConfiguration!=NULL, "engineConfiguration");
- chDbgCheck(engineConfiguration2!=NULL, "engineConfiguration2");
+TriggerCentral::TriggerCentral() {
+ memset(hwEventCounters, 0, sizeof(hwEventCounters));
+ clearCallbacks(&triggerListeneres);
+}
+void TriggerCentral::handleShaftSignal(configuration_s *configuration, trigger_event_e signal, uint64_t nowUs) {
+ efiAssertVoid(configuration!=NULL, "configuration");
+
+ efiAssertVoid(configuration->engineConfiguration!=NULL, "engineConfiguration");
+ efiAssertVoid(configuration->engineConfiguration2!=NULL, "engineConfiguration2");
+
+#if EFI_HISTOGRAMS && EFI_PROD_CODE
int beforeCallback = hal_lld_get_counter_value();
+#endif
int eventIndex = (int) signal;
- chDbgCheck(eventIndex >= 0 && eventIndex < HW_EVENT_TYPES, "signal type");
+ efiAssertVoid(eventIndex >= 0 && eventIndex < HW_EVENT_TYPES, "signal type");
hwEventCounters[eventIndex]++;
- uint64_t nowUs = getTimeNowUs();
-
if (nowUs - previousShaftEventTime > US_PER_SECOND) {
/**
* We are here if there is a time gap between now and previous shaft event - that means the engine is not runnig.
@@ -74,52 +84,69 @@ void hwHandleShaftSignal(ShaftEvents signal) {
triggerState.shaft_is_synchronized = FALSE;
}
previousShaftEventTime = nowUs;
- // this is not atomic, but it's fine here
- shaftEventCounter++;
+
+ trigger_shape_s * triggerShape = &configuration->engineConfiguration2->triggerShape;
/**
* This invocation changes the state of
*/
- processTriggerEvent(&triggerState, &engineConfiguration2->triggerShape, &engineConfiguration->triggerConfig, signal,
- nowUs);
+ triggerState.processTriggerEvent(triggerShape, &configuration->engineConfiguration->triggerConfig, signal, nowUs);
if (!triggerState.shaft_is_synchronized)
return; // we should not propagate event if we do not know where we are
- if (triggerState.current_index >= engineConfiguration2->triggerShape.shaftPositionEventCount) {
- int f = warning(OBD_PCM_Processor_Fault, "unexpected eventIndex=%d", triggerState.current_index);
- if(!f) {
+ if (triggerState.getCurrentIndex() >= configuration->engineConfiguration2->triggerShape.shaftPositionEventCount) {
+ int f = warning(OBD_PCM_Processor_Fault, "unexpected eventIndex=%d", triggerState.getCurrentIndex());
+ if (!f) {
+#if EFI_PROD_CODE
+ // this temporary code is about trigger noise debugging
for (int i = 0; i < HW_EVENT_TYPES; i++)
scheduleMsg(&logging, "event type: %d count=%d", i, hwEventCounters[i]);
+#endif
}
} else {
+ /**
+ * If we only have a crank position sensor, here we are extending crank revolutions with a 360 degree
+ * cycle into a four stroke, 720 degrees cycle. TODO
+ */
+ int triggerIndexForListeners;
+ if( getOperationMode(configuration->engineConfiguration) == FOUR_STROKE_CAM_SENSOR) {
+ // That's easy - trigger cycle matches engine cycle
+ triggerIndexForListeners = triggerState.getCurrentIndex();
+ } else {
+ bool isEven = (triggerState.getTotalRevolutionCounter() & 1) == 0;
+
+ triggerIndexForListeners = triggerState.getCurrentIndex() + (isEven ? 0 : triggerShape->getSize());
+ }
+
/**
* Here we invoke all the listeners - the main engine control logic is inside these listeners
*/
- invokeIntIntCallbacks(&triggerListeneres, signal, triggerState.current_index);
+ invokeIntIntVoidCallbacks(&triggerListeneres, signal, triggerIndexForListeners);
}
+#if EFI_HISTOGRAMS && EFI_PROD_CODE
int afterCallback = hal_lld_get_counter_value();
-#if EFI_HISTOGRAMS
int diff = afterCallback - beforeCallback;
// this counter is only 32 bits so it overflows every minute, let's ignore the value in case of the overflow for simplicity
if (diff > 0)
- hsAdd(&triggerCallback, diff);
+ hsAdd(&triggerCallback, diff);
#endif /* EFI_HISTOGRAMS */
}
void printAllCallbacksHistogram(void) {
+#if EFI_PROD_CODE
printHistogram(&logging, &triggerCallback);
+#endif
}
void initTriggerCentral(void) {
+#if EFI_PROD_CODE
initLogging(&logging, "ShaftPosition");
-
- memset(hwEventCounters, 0, sizeof(hwEventCounters));
+#endif
#if EFI_HISTOGRAMS
initHistogram(&triggerCallback, "all callbacks");
#endif /* EFI_HISTOGRAMS */
initTriggerDecoder();
- clearTriggerState(&triggerState);
}
diff --git a/firmware/controllers/trigger/trigger_central.h b/firmware/controllers/trigger/trigger_central.h
index 9d55958acd..7f81372920 100644
--- a/firmware/controllers/trigger/trigger_central.h
+++ b/firmware/controllers/trigger/trigger_central.h
@@ -9,17 +9,35 @@
#define TRIGGER_CENTRAL_H_
#include "rusefi_enums.h"
+#include "listener_array.h"
-typedef void (*ShaftPositionListener)(ShaftEvents signal, int index);
+typedef void (*ShaftPositionListener)(trigger_event_e signal, int index, void *arg);
#ifdef __cplusplus
-extern "C"
-{
+#include "ec2.h"
+
+#define HW_EVENT_TYPES 4
+
+class TriggerCentral {
+public:
+ TriggerCentral();
+ void addEventListener(ShaftPositionListener handler, const char *name, void *arg);
+ void handleShaftSignal(configuration_s *configuration, trigger_event_e signal, uint64_t nowUs);
+ TriggerState triggerState;
+private:
+ IntListenerArray triggerListeneres;
+ int hwEventCounters[HW_EVENT_TYPES];
+};
+#endif
+
+#ifdef __cplusplus
+extern "C" {
#endif /* __cplusplus */
-void addTriggerEventListener(ShaftPositionListener handler, const char *name);
-int getCrankEventCounter(void);
+void addTriggerEventListener(ShaftPositionListener handler, const char *name, void *arg);
+uint64_t getCrankEventCounter(void);
+uint64_t getStartOfRevolutionIndex(void);
int isSignalDecoderError(void);
-void hwHandleShaftSignal(ShaftEvents signal);
+void hwHandleShaftSignal(trigger_event_e signal);
void initTriggerCentral(void);
void printAllCallbacksHistogram(void);
diff --git a/firmware/controllers/trigger/trigger_decoder.cpp b/firmware/controllers/trigger/trigger_decoder.cpp
index c9c07c2212..3c4ae8b02b 100644
--- a/firmware/controllers/trigger/trigger_decoder.cpp
+++ b/firmware/controllers/trigger/trigger_decoder.cpp
@@ -45,7 +45,7 @@ int isTriggerDecoderError(void) {
return errorDetection.sum(6) > 4;
}
-static inline int isSynchronizationGap(trigger_state_s const *shaftPositionState, trigger_shape_s const *triggerShape,
+static inline int isSynchronizationGap(TriggerState const *shaftPositionState, trigger_shape_s const *triggerShape,
trigger_config_s const *triggerConfig, const int currentDuration) {
if (!triggerConfig->isSynchronizationNeeded)
return FALSE;
@@ -54,8 +54,8 @@ static inline int isSynchronizationGap(trigger_state_s const *shaftPositionState
&& currentDuration < shaftPositionState->toothed_previous_duration * triggerConfig->syncRatioTo;
}
-static inline int noSynchronizationResetNeeded(trigger_state_s const *shaftPositionState,
- trigger_shape_s const *triggerShape, trigger_config_s const*triggerConfig) {
+static inline int noSynchronizationResetNeeded(TriggerState *shaftPositionState, trigger_shape_s const *triggerShape,
+ trigger_config_s const*triggerConfig) {
if (triggerConfig->isSynchronizationNeeded)
return FALSE;
if (!shaftPositionState->shaft_is_synchronized)
@@ -63,14 +63,14 @@ static inline int noSynchronizationResetNeeded(trigger_state_s const *shaftPosit
/**
* in case of noise the counter could be above the expected number of events
*/
- return shaftPositionState->current_index >= triggerShape->shaftPositionEventCount - 1;
+ return shaftPositionState->getCurrentIndex() >= triggerShape->shaftPositionEventCount - 1;
}
/**
* @brief This method changes the state of trigger_state_s data structure according to the trigger event
*/
-void processTriggerEvent(trigger_state_s *shaftPositionState, trigger_shape_s const *triggerShape,
- trigger_config_s const *triggerConfig, ShaftEvents signal, uint64_t nowUs) {
+void TriggerState::processTriggerEvent(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);
@@ -79,12 +79,12 @@ void processTriggerEvent(trigger_state_s *shaftPositionState, trigger_shape_s co
/**
* For less important events we simply increment the index.
*/
- shaftPositionState->current_index++;
+ nextTriggerEvent();
return;
}
- int64_t currentDuration = nowUs - shaftPositionState->toothed_previous_time;
- chDbgCheck(currentDuration >= 0, "negative duration?");
+ int64_t currentDuration = nowUs - toothed_previous_time;
+ efiAssertVoid(currentDuration >= 0, "negative duration?");
// todo: skip a number of signal from the beginning
@@ -92,36 +92,35 @@ void processTriggerEvent(trigger_state_s *shaftPositionState, trigger_shape_s co
// scheduleMsg(&logger, "from %f to %f %d %d", triggerConfig->syncRatioFrom, triggerConfig->syncRatioTo, currentDuration, shaftPositionState->toothed_previous_duration);
// scheduleMsg(&logger, "ratio %f", 1.0 * currentDuration/ shaftPositionState->toothed_previous_duration);
#else
- if (shaftPositionState->toothed_previous_duration != 0) {
+ if (toothed_previous_duration != 0) {
// printf("ratio %f: cur=%d pref=%d\r\n", 1.0 * currentDuration / shaftPositionState->toothed_previous_duration,
// currentDuration, shaftPositionState->toothed_previous_duration);
}
#endif
- if (noSynchronizationResetNeeded(shaftPositionState, triggerShape, triggerConfig)
- || isSynchronizationGap(shaftPositionState, triggerShape, triggerConfig, currentDuration)) {
+ if (noSynchronizationResetNeeded(this, triggerShape, triggerConfig)
+ || isSynchronizationGap(this, triggerShape, triggerConfig, currentDuration)) {
/**
* We can check if things are fine by comparing the number of events in a cycle with the expected number of event.
*/
- int isDecodingError = shaftPositionState->current_index != triggerShape->shaftPositionEventCount - 1;
+ int isDecodingError = getCurrentIndex() != triggerShape->shaftPositionEventCount - 1;
errorDetection.add(isDecodingError);
if (isTriggerDecoderError())
warning(OBD_PCM_Processor_Fault, "trigger decoding issue");
- shaftPositionState->shaft_is_synchronized = TRUE;
- shaftPositionState->current_index = 0;
+ shaft_is_synchronized = TRUE;
+ nextRevolution(triggerShape->shaftPositionEventCount);
} else {
- shaftPositionState->current_index++;
+ nextTriggerEvent();
}
- shaftPositionState->toothed_previous_duration = currentDuration;
- shaftPositionState->toothed_previous_time = nowUs;
-
+ toothed_previous_duration = currentDuration;
+ toothed_previous_time = nowUs;
}
static void initializeSkippedToothTriggerShape(trigger_shape_s *s, int totalTeethCount, int skippedCount) {
- efiAssert(s != NULL, "trigger_shape_s is NULL");
+ efiAssertVoid(s != NULL, "trigger_shape_s is NULL");
s->reset();
float toothWidth = 0.5;
@@ -140,6 +139,8 @@ static void initializeSkippedToothTriggerShape(trigger_shape_s *s, int totalTeet
void initializeSkippedToothTriggerShapeExt(engine_configuration2_s *engineConfiguration2, int totalTeethCount,
int skippedCount) {
+ efiAssertVoid(totalTeethCount > 0, "totalTeethCount is zero");
+
trigger_shape_s *s = &engineConfiguration2->triggerShape;
initializeSkippedToothTriggerShape(s, totalTeethCount, skippedCount);
@@ -208,43 +209,49 @@ void initializeTriggerShape(Logging *logger, engine_configuration_s *engineConfi
firmwareError("initializeTriggerShape() not implemented: %d", tt->triggerType);
;
}
+ if (engineConfiguration2->triggerShape.shaftPositionEventCount != engineConfiguration2->triggerShape.getSize())
+ firmwareError("trigger size or shaftPositionEventCount?");
}
+/**
+ * Trigger shape is defined in a way which is convenient for trigger shape definition
+ * On the other hand, trigger decoder indexing begins from synchronization event.
+ *
+ * This function finds the index of synchronization event within trigger_shape_s
+ */
int findTriggerZeroEventIndex(trigger_shape_s * shape, trigger_config_s const*triggerConfig) {
- trigger_state_s state;
- clearTriggerState(&state);
+ TriggerState state;
errorDetection.clear();
int primaryWheelState = FALSE;
int secondaryWheelState = FALSE;
- for (int i = 0; i < 100; i++) {
+ for (int i = 0; i < 4 * PWM_PHASE_MAX_COUNT; i++) {
int stateIndex = i % shape->getSize();
int loopIndex = i / shape->getSize();
- int time = (int)(10000 * (loopIndex + shape->wave.getSwitchTime(stateIndex)));
+ int time = (int) (10000 * (loopIndex + shape->wave.getSwitchTime(stateIndex)));
int newPrimaryWheelState = shape->wave.getChannelState(0, stateIndex);
int newSecondaryWheelState = shape->wave.getChannelState(1, stateIndex);
if (primaryWheelState != newPrimaryWheelState) {
primaryWheelState = newPrimaryWheelState;
- ShaftEvents s = primaryWheelState ? SHAFT_PRIMARY_UP : SHAFT_PRIMARY_DOWN;
- processTriggerEvent(&state, shape, triggerConfig, s, time);
+ trigger_event_e s = primaryWheelState ? SHAFT_PRIMARY_UP : SHAFT_PRIMARY_DOWN;
+ state.processTriggerEvent(shape, triggerConfig, s, time);
}
if (secondaryWheelState != newSecondaryWheelState) {
secondaryWheelState = newSecondaryWheelState;
- ShaftEvents s = secondaryWheelState ? SHAFT_SECONDARY_UP : SHAFT_SECONDARY_DOWN;
- processTriggerEvent(&state, shape, triggerConfig, s, time);
+ trigger_event_e s = secondaryWheelState ? SHAFT_SECONDARY_UP : SHAFT_SECONDARY_DOWN;
+ state.processTriggerEvent(shape, triggerConfig, s, time);
}
- if (state.shaft_is_synchronized) {
+ if (state.shaft_is_synchronized)
return stateIndex;
- }
}
firmwareError("findTriggerZeroEventIndex() failed");
return -1;
diff --git a/firmware/controllers/trigger/trigger_decoder.h b/firmware/controllers/trigger/trigger_decoder.h
index c46dab305e..2887b9cd06 100644
--- a/firmware/controllers/trigger/trigger_decoder.h
+++ b/firmware/controllers/trigger/trigger_decoder.h
@@ -10,18 +10,16 @@
#include
+#include "trigger_structure.h"
+#include "engine_configuration.h"
+#include "ec2.h"
+
#ifdef __cplusplus
extern "C"
{
#endif
-#include "trigger_structure.h"
-#include "engine_configuration.h"
-#include "ec2.h"
-
-
int isTriggerDecoderError(void);
-void processTriggerEvent(trigger_state_s *shaftPositionState, trigger_shape_s const*triggerShape, trigger_config_s const*triggerConfig, ShaftEvents signal, uint64_t nowUs);
void initializeSkippedToothTriggerShapeExt(engine_configuration2_s *engineConfiguration2, int totalTeethCount, int skippedCount);
void initializeTriggerShape(Logging *logger, engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2);
int findTriggerZeroEventIndex(trigger_shape_s * shape, trigger_config_s const*triggerConfig);
diff --git a/firmware/controllers/trigger/trigger_structure.cpp b/firmware/controllers/trigger/trigger_structure.cpp
index 8354e659b2..a6106e3a14 100644
--- a/firmware/controllers/trigger/trigger_structure.cpp
+++ b/firmware/controllers/trigger/trigger_structure.cpp
@@ -1,5 +1,5 @@
/**
- * @file trigger_structure.c
+ * @file trigger_structure.cpp
*
* @date Jan 20, 2014
* @author Andrey Belomutskiy, (c) 2012-2014
@@ -20,6 +20,7 @@
#include "main.h"
#include "trigger_structure.h"
+#include "error_handling.h"
trigger_shape_helper::trigger_shape_helper() {
waves[0].init(pinStates0);
@@ -43,6 +44,7 @@ void trigger_shape_s::reset() {
memset(initialState, 0, sizeof(initialState));
memset(switchTimes, 0, sizeof(switchTimes));
wave.reset();
+ previousAngle = 0;
}
int multi_wave_s::getChannelState(int channelIndex, int phaseIndex) const {
@@ -53,15 +55,49 @@ void multi_wave_s::setSwitchTime(int index, float value) {
switchTimes[index] = value;
}
-void clearTriggerState(trigger_state_s *state) {
- state->shaft_is_synchronized = FALSE;
- state->toothed_previous_time = 0;
- state->toothed_previous_duration = 0;
- state->current_index = 0;
+TriggerState::TriggerState() {
+ clear();
+ totalEventCountBase = 0;
+}
+
+int TriggerState::getCurrentIndex() {
+ return current_index;
+}
+
+uint64_t TriggerState::getStartOfRevolutionIndex() {
+ return totalEventCountBase;
+}
+
+uint64_t TriggerState::getTotalEventCounter() {
+ return totalEventCountBase + current_index;
+}
+
+void TriggerState::nextRevolution(int triggerEventCount) {
+ current_index = 0;
+ totalRevolutionCounter ++;
+ totalEventCountBase += triggerEventCount;
+}
+
+int TriggerState::getTotalRevolutionCounter() {
+ return totalRevolutionCounter;
+}
+
+void TriggerState::nextTriggerEvent() {
+ current_index++;
+}
+
+void TriggerState::clear() {
+ shaft_is_synchronized = FALSE;
+ toothed_previous_time = 0;
+ toothed_previous_duration = 0;
+ current_index = 0;
+ totalRevolutionCounter = 0;
}
void trigger_shape_s::addEvent(float angle, trigger_wheel_e waveIndex, trigger_value_e state) {
angle /= 720;
+ efiAssertVoid(angle > previousAngle, "invalid angle order");
+ previousAngle = angle;
if (size == 0) {
size = 1;
for (int i = 0; i < PWM_PHASE_MAX_WAVE_PER_PWM; i++) {
diff --git a/firmware/controllers/trigger/trigger_structure.h b/firmware/controllers/trigger/trigger_structure.h
index 95a2db0960..fa226a2205 100644
--- a/firmware/controllers/trigger/trigger_structure.h
+++ b/firmware/controllers/trigger/trigger_structure.h
@@ -12,19 +12,38 @@
#include "rusefi_enums.h"
#include "EfiWave.h"
+#include "engine_configuration.h"
+
+class trigger_shape_s;
+
+class TriggerState {
+public:
+ TriggerState();
+ int getCurrentIndex();
+ int getTotalRevolutionCounter();
+ uint64_t getTotalEventCounter();
+ uint64_t getStartOfRevolutionIndex();
+ void nextRevolution(int triggerEventCount);
+ void nextTriggerEvent();
+ void processTriggerEvent(trigger_shape_s const*triggerShape, trigger_config_s const*triggerConfig, trigger_event_e signal, uint64_t nowUs);
+
-typedef struct {
/**
* TRUE if we know where we are
*/
unsigned char shaft_is_synchronized;
- int current_index;
-
uint64_t toothed_previous_duration;
uint64_t toothed_previous_time;
-
-} trigger_state_s;
+private:
+ void clear();
+ /**
+ * index within trigger revolution, from 0 to trigger event count
+ */
+ int current_index;
+ uint64_t totalEventCountBase;
+ int totalRevolutionCounter;
+};
typedef enum {
TV_LOW = 0,
@@ -67,9 +86,14 @@ public:
// tood: maybe even automate this flag calculation?
int initialState[PWM_PHASE_MAX_WAVE_PER_PWM];
+ /**
+ * index of synchronization event within trigger_shape_s
+ * See findTriggerZeroEventIndex()
+ */
int triggerShapeSynchPointIndex;
private:
float switchTimes[PWM_PHASE_MAX_COUNT];
+ float previousAngle;
};
#ifdef __cplusplus
@@ -77,7 +101,6 @@ extern "C"
{
#endif /* __cplusplus */
-void clearTriggerState(trigger_state_s *state);
void triggerAddEvent(trigger_shape_s *trigger, float angle, trigger_wheel_e waveIndex, trigger_value_e state);
#ifdef __cplusplus
diff --git a/firmware/dump_debug.bat b/firmware/dump_debug.bat
new file mode 100644
index 0000000000..d19e8cd431
--- /dev/null
+++ b/firmware/dump_debug.bat
@@ -0,0 +1 @@
+arm-none-eabi-objdump -S debug/rusefi.elf > debug.dump
\ No newline at end of file
diff --git a/firmware/dump_release.bat b/firmware/dump_release.bat
new file mode 100644
index 0000000000..ab2ea8ffc7
--- /dev/null
+++ b/firmware/dump_release.bat
@@ -0,0 +1 @@
+arm-none-eabi-objdump -S release/rusefi.elf > release.dump
\ No newline at end of file
diff --git a/firmware/emulation/emulation.mk b/firmware/emulation/emulation.mk
index 10da7bf43f..cde4721375 100644
--- a/firmware/emulation/emulation.mk
+++ b/firmware/emulation/emulation.mk
@@ -1,6 +1,8 @@
EMULATIONSRC = emulation/hw_layer/poten.c \
emulation/analog_chart.c \
+ emulation/test/test.c \
+ emulation/test/testbmk.c \
emulation/wave_analyzer.c
EMULATIONSRC_CPP = emulation/trigger_emulator.cpp \
diff --git a/firmware/emulation/engine_emulator.cpp b/firmware/emulation/engine_emulator.cpp
index 2f4e32a673..6d0bdd1319 100644
--- a/firmware/emulation/engine_emulator.cpp
+++ b/firmware/emulation/engine_emulator.cpp
@@ -17,12 +17,11 @@ extern "C" {
#include "fuel_math.h"
#include "pin_repository.h"
#include "poten.h"
-#include "rfi_perftest.h"
}
#include "trigger_emulator.h"
-static WORKING_AREA(eeThreadStack, UTILITY_THREAD_STACK_SIZE);
+static THD_WORKING_AREA(eeThreadStack, UTILITY_THREAD_STACK_SIZE);
#define DIAG_PORT GPIOD
#define DIAG_PIN 0
@@ -62,7 +61,7 @@ static msg_t eeThread(void *arg) {
while (TRUE) {
while (!flag)
- chThdSleepMilliseconds(10);
+ chThdSleepMilliseconds(200);
flag = FALSE;
emulate();
}
@@ -96,9 +95,6 @@ static void initECUstimulator(void) {
void initEngineEmulator(void) {
if (hasFirmwareError())
return;
-#if EFI_PERF_METRICS
- initTimePerfActions();
-#endif
#if EFI_POTENTIOMETER
initPotentiometers();
diff --git a/firmware/emulation/rfi_perftest.cpp b/firmware/emulation/rfi_perftest.cpp
index d286469677..6e6ed4ff17 100644
--- a/firmware/emulation/rfi_perftest.cpp
+++ b/firmware/emulation/rfi_perftest.cpp
@@ -1,5 +1,5 @@
/**
- * @file rdi_perftest.c
+ * @file rfi_perftest.cpp
*
* @date Nov 30, 2012
* @author Andrey Belomutskiy, (c) 2012-2014
@@ -9,19 +9,17 @@
#include "rfi_perftest.h"
#include "fuel_math.h"
-//#include "rfirtc.h"
+#include "test.h"
#include "eficonsole.h"
#include "time.h"
#include "engine_math.h"
#include "gpio_helper.h"
#include "efilib2.h"
+#include "console_io.h"
-//#define TEST_PORT GPIOB
-//#define TEST_PIN 6
+#if EFI_PERF_METRICS
-//static OutputPin testOutput;
-
-Logging logger;
+static Logging logger;
static void testSystemCalls(const int count) {
time_t start, time;
@@ -201,7 +199,7 @@ static int rtcStartTime;
static void timeInfo(void) {
scheduleMsg(&logger, "chTimeNow as seconds = %d", getTimeNowSeconds());
- scheduleMsg(&logger, "hal seconds = %d", halTime.get() / 168000000LL);
+ scheduleMsg(&logger, "hal seconds = %d", halTime.get(hal_lld_get_counter_value(), false) / 168000000LL);
#if EFI_RTC
int unix = rtcGetTimeUnixSec(&RTCD1) - rtcStartTime;
@@ -209,6 +207,26 @@ static void timeInfo(void) {
#endif
}
+static void runChibioTest(void) {
+ print("EFI_SHAFT_POSITION_INPUT=%d\r\n", EFI_SHAFT_POSITION_INPUT);
+ print("EFI_EMULATE_POSITION_SENSORS=%d\r\n", EFI_EMULATE_POSITION_SENSORS);
+ print("EFI_ANALOG_INPUTS=%d\r\n", EFI_ANALOG_INPUTS);
+ print("EFI_INTERNAL_ADC=%d\r\n", EFI_INTERNAL_ADC);
+ print("EFI_HD44780_LCD=%d\r\n", EFI_HD44780_LCD);
+ print("EFI_MAP_AVERAGING=%d\r\n", EFI_MAP_AVERAGING);
+ print("EFI_WAVE_ANALYZER=%d\r\n", EFI_WAVE_ANALYZER);
+ print("EFI_WAVE_CHART=%d\r\n", EFI_WAVE_CHART);
+ print("EFI_ANALOG_CHART=%d\r\n", EFI_ANALOG_CHART);
+ print("EFI_SHAFT_POSITION_INPUT=%d\r\n", EFI_SHAFT_POSITION_INPUT);
+ print("EFI_ENGINE_CONTROL=%d\r\n", EFI_ENGINE_CONTROL);
+ print("CH_DBG_SYSTEM_STATE_CHECK=%d\r\n", CH_DBG_SYSTEM_STATE_CHECK);
+ print("CH_DBG_ENABLE_CHECKS=%d\r\n", CH_DBG_ENABLE_CHECKS);
+ print("CH_DBG_ENABLE_ASSERTS=%d\r\n", CH_DBG_ENABLE_ASSERTS);
+ print("CH_DBG_ENABLE_STACK_CHECK=%d\r\n", CH_DBG_ENABLE_STACK_CHECK);
+ print("CH_DBG_THREADS_PROFILING=%d\r\n", CH_DBG_THREADS_PROFILING);
+ TestThread(getConsoleChannel());
+}
+
void initTimePerfActions() {
#if EFI_RTC
rtcStartTime = rtcGetTimeUnixSec(&RTCD1);
@@ -220,4 +238,7 @@ void initTimePerfActions() {
addConsoleActionI("perftest", runTests);
addConsoleAction("timeinfo", timeInfo);
+ addConsoleAction("chtest", runChibioTest);
}
+
+#endif /* EFI_PERF_METRICS */
diff --git a/firmware/emulation/test/test.c b/firmware/emulation/test/test.c
new file mode 100644
index 0000000000..4a89322fb0
--- /dev/null
+++ b/firmware/emulation/test/test.c
@@ -0,0 +1,372 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file test.c
+ * @brief Tests support code.
+ *
+ * @addtogroup test
+ * @{
+ */
+
+#include "main.h"
+
+#if EFI_PERF_METRICS
+
+#include "test.h"
+#include "testbmk.h"
+
+/*
+ * Array of all the test patterns.
+ */
+static ROMCONST struct testcase * ROMCONST *patterns[] = {
+ patternbmk,
+ NULL
+};
+
+static bool_t local_fail, global_fail;
+static unsigned failpoint;
+static char tokens_buffer[MAX_TOKENS];
+static char *tokp;
+
+/*
+ * Static working areas, the following areas can be used for threads or
+ * used as temporary buffers.
+ */
+union test_buffers test;
+
+/*
+ * Pointers to the spawned threads.
+ */
+Thread *threads[MAX_THREADS];
+
+/*
+ * Pointers to the working areas.
+ */
+void * ROMCONST wa[5] = {test.wa.T0, test.wa.T1, test.wa.T2,
+ test.wa.T3, test.wa.T4};
+
+/*
+ * Console output.
+ */
+static BaseSequentialStream *chp;
+
+/**
+ * @brief Prints a decimal unsigned number.
+ *
+ * @param[in] n the number to be printed
+ */
+void test_printn(uint32_t n) {
+ char buf[16], *p;
+
+ if (!n)
+ chSequentialStreamPut(chp, '0');
+ else {
+ p = buf;
+ while (n)
+ *p++ = (n % 10) + '0', n /= 10;
+ while (p > buf)
+ chSequentialStreamPut(chp, *--p);
+ }
+}
+
+/**
+ * @brief Prints a line without final end-of-line.
+ *
+ * @param[in] msgp the message
+ */
+void test_print(const char *msgp) {
+
+ while (*msgp)
+ chSequentialStreamPut(chp, *msgp++);
+}
+
+/**
+ * @brief Prints a line.
+ *
+ * @param[in] msgp the message
+ */
+void test_println(const char *msgp) {
+
+ test_print(msgp);
+ chSequentialStreamWrite(chp, (const uint8_t *)"\r\n", 2);
+}
+
+/*
+ * Tokens.
+ */
+static void clear_tokens(void) {
+
+ tokp = tokens_buffer;
+}
+
+static void print_tokens(void) {
+ char *cp = tokens_buffer;
+
+ while (cp < tokp)
+ chSequentialStreamPut(chp, *cp++);
+}
+
+/**
+ * @brief Emits a token into the tokens buffer.
+ *
+ * @param[in] token the token as a char
+ */
+void test_emit_token(char token) {
+
+ chSysLock();
+ *tokp++ = token;
+ chSysUnlock();
+}
+
+/*
+ * Assertions.
+ */
+bool_t _test_fail(unsigned point) {
+
+ local_fail = TRUE;
+ global_fail = TRUE;
+ failpoint = point;
+ return TRUE;
+}
+
+bool_t _test_assert(unsigned point, bool_t condition) {
+
+ if (!condition)
+ return _test_fail(point);
+ return FALSE;
+}
+
+bool_t _test_assert_sequence(unsigned point, char *expected) {
+ char *cp = tokens_buffer;
+ while (cp < tokp) {
+ if (*cp++ != *expected++)
+ return _test_fail(point);
+ }
+ if (*expected)
+ return _test_fail(point);
+ clear_tokens();
+ return FALSE;
+}
+
+bool_t _test_assert_time_window(unsigned point, systime_t start, systime_t end) {
+
+ return _test_assert(point, chTimeIsWithin(start, end));
+}
+
+/*
+ * Threads utils.
+ */
+
+/**
+ * @brief Sets a termination request in all the test-spawned threads.
+ */
+void test_terminate_threads(void) {
+ int i;
+
+ for (i = 0; i < MAX_THREADS; i++)
+ if (threads[i])
+ chThdTerminate(threads[i]);
+}
+
+/**
+ * @brief Waits for the completion of all the test-spawned threads.
+ */
+void test_wait_threads(void) {
+ int i;
+
+ for (i = 0; i < MAX_THREADS; i++)
+ if (threads[i] != NULL) {
+ chThdWait(threads[i]);
+ threads[i] = NULL;
+ }
+}
+
+#if CH_DBG_THREADS_PROFILING
+/**
+ * @brief CPU pulse.
+ * @note The current implementation is not totally reliable.
+ *
+ * @param[in] duration CPU pulse duration in milliseconds
+ */
+void test_cpu_pulse(unsigned duration) {
+ systime_t start, end, now;
+
+ start = chThdSelf()->p_time;
+ end = start + MS2ST(duration);
+ do {
+ now = chThdSelf()->p_time;
+#if defined(SIMULATOR)
+ ChkIntSources();
+#endif
+ }
+ while (end > start ? (now >= start) && (now < end) :
+ (now >= start) || (now < end));
+}
+#endif
+
+/**
+ * @brief Delays execution until next system time tick.
+ *
+ * @return The system time.
+ */
+systime_t test_wait_tick(void) {
+
+ chThdSleep(1);
+ return chTimeNow();
+}
+
+/*
+ * Timer utils.
+ */
+
+/**
+ * @brief Set to @p TRUE when the test timer reaches its deadline.
+ */
+bool_t test_timer_done;
+
+static VirtualTimer vt;
+static void tmr(void *p) {
+ (void)p;
+
+ test_timer_done = TRUE;
+}
+
+/**
+ * @brief Starts the test timer.
+ *
+ * @param[in] ms time in milliseconds
+ */
+void test_start_timer(unsigned ms) {
+
+ systime_t duration = MS2ST(ms);
+ test_timer_done = FALSE;
+ chVTSet(&vt, duration, tmr, NULL);
+}
+
+/*
+ * Test suite execution.
+ */
+static void execute_test(const struct testcase *tcp) {
+ int i;
+
+ /* Initialization */
+ clear_tokens();
+ local_fail = FALSE;
+ for (i = 0; i < MAX_THREADS; i++)
+ threads[i] = NULL;
+
+ if (tcp->setup != NULL)
+ tcp->setup();
+ tcp->execute();
+ if (tcp->teardown != NULL)
+ tcp->teardown();
+
+ test_wait_threads();
+}
+
+static void print_line(void) {
+ unsigned i;
+
+ for (i = 0; i < 76; i++)
+ chSequentialStreamPut(chp, '-');
+ chSequentialStreamWrite(chp, (const uint8_t *)"\r\n", 2);
+}
+
+/**
+ * @brief Test execution thread function.
+ *
+ * @param[in] p pointer to a @p BaseChannel object for test output
+ * @return A failure boolean value.
+ */
+msg_t TestThread(void *p) {
+ int i, j;
+
+ chp = p;
+ test_println("");
+ test_println("*** ChibiOS/RT test suite");
+ test_println("***");
+ test_print("*** Kernel: ");
+ test_println(CH_KERNEL_VERSION);
+ test_print("*** Compiled: ");
+ test_println(__DATE__ " - " __TIME__);
+#ifdef CH_COMPILER_NAME
+ test_print("*** Compiler: ");
+ test_println(CH_COMPILER_NAME);
+#endif
+ test_print("*** Architecture: ");
+ test_println(CH_ARCHITECTURE_NAME);
+#ifdef CH_CORE_VARIANT_NAME
+ test_print("*** Core Variant: ");
+ test_println(CH_CORE_VARIANT_NAME);
+#endif
+#ifdef CH_PORT_INFO
+ test_print("*** Port Info: ");
+ test_println(CH_PORT_INFO);
+#endif
+#ifdef PLATFORM_NAME
+ test_print("*** Platform: ");
+ test_println(PLATFORM_NAME);
+#endif
+#ifdef BOARD_NAME
+ test_print("*** Test Board: ");
+ test_println(BOARD_NAME);
+#endif
+ test_println("");
+
+ global_fail = FALSE;
+ i = 0;
+ while (patterns[i]) {
+ j = 0;
+ while (patterns[i][j]) {
+ print_line();
+ test_print("--- Test Case ");
+ test_printn(i + 1);
+ test_print(".");
+ test_printn(j + 1);
+ test_print(" (");
+ test_print(patterns[i][j]->name);
+ test_println(")");
+#if DELAY_BETWEEN_TESTS > 0
+ chThdSleepMilliseconds(DELAY_BETWEEN_TESTS);
+#endif
+ execute_test(patterns[i][j]);
+ if (local_fail) {
+ test_print("--- Result: FAILURE (#");
+ test_printn(failpoint);
+ test_print(" [");
+ print_tokens();
+ test_println("])");
+ }
+ else
+ test_println("--- Result: SUCCESS");
+ j++;
+ }
+ i++;
+ }
+ print_line();
+ test_println("");
+ test_print("Final result: ");
+ if (global_fail)
+ test_println("FAILURE");
+ else
+ test_println("SUCCESS");
+
+ return (msg_t)global_fail;
+}
+
+/** @} */
+#endif /* EFI_PERF_METRICS */
diff --git a/firmware/emulation/test/test.h b/firmware/emulation/test/test.h
new file mode 100644
index 0000000000..e5778177bd
--- /dev/null
+++ b/firmware/emulation/test/test.h
@@ -0,0 +1,173 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file test.h
+ * @brief Tests support header.
+ *
+ * @addtogroup test
+ * @{
+ */
+
+#ifndef _TEST_H_
+#define _TEST_H_
+
+/**
+ * @brief Delay inserted between test cases.
+ */
+#if !defined(DELAY_BETWEEN_TESTS) || defined(__DOXYGEN__)
+#define DELAY_BETWEEN_TESTS 200
+#endif
+
+/**
+ * @brief If @p TRUE then benchmarks are not included.
+ */
+#if !defined(TEST_NO_BENCHMARKS) || defined(__DOXYGEN__)
+#define TEST_NO_BENCHMARKS FALSE
+#endif
+
+#define MAX_THREADS 5
+#define MAX_TOKENS 16
+
+#if defined(CH_ARCHITECTURE_AVR) || defined(CH_ARCHITECTURE_MSP430)
+#define THREADS_STACK_SIZE 48
+#elif defined(CH_ARCHITECTURE_STM8)
+#define THREADS_STACK_SIZE 64
+#elif defined(CH_ARCHITECTURE_SIMIA32)
+#define THREADS_STACK_SIZE 512
+#else
+#define THREADS_STACK_SIZE 128
+#endif
+#define WA_SIZE THD_WA_SIZE(THREADS_STACK_SIZE)
+
+/**
+ * @brief Structure representing a test case.
+ */
+struct testcase {
+ const char *name; /**< @brief Test case name. */
+ void (*setup)(void); /**< @brief Test case preparation function. */
+ void (*teardown)(void); /**< @brief Test case clean up function. */
+ void (*execute)(void); /**< @brief Test case execution function. */
+};
+
+#ifndef __DOXYGEN__
+union test_buffers {
+ struct {
+ WORKING_AREA(T0, THREADS_STACK_SIZE);
+ WORKING_AREA(T1, THREADS_STACK_SIZE);
+ WORKING_AREA(T2, THREADS_STACK_SIZE);
+ WORKING_AREA(T3, THREADS_STACK_SIZE);
+ WORKING_AREA(T4, THREADS_STACK_SIZE);
+ } wa;
+ uint8_t buffer[WA_SIZE * 5];
+};
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ msg_t TestThread(void *p);
+ void test_printn(uint32_t n);
+ void test_print(const char *msgp);
+ void test_println(const char *msgp);
+ void test_emit_token(char token);
+ bool_t _test_fail(unsigned point);
+ bool_t _test_assert(unsigned point, bool_t condition);
+ bool_t _test_assert_sequence(unsigned point, char *expected);
+ bool_t _test_assert_time_window(unsigned point, systime_t start, systime_t end);
+ void test_terminate_threads(void);
+ void test_wait_threads(void);
+ systime_t test_wait_tick(void);
+ void test_start_timer(unsigned ms);
+#if CH_DBG_THREADS_PROFILING
+ void test_cpu_pulse(unsigned duration);
+#endif
+#if defined(WIN32)
+ void ChkIntSources(void);
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @brief Test failure enforcement.
+ */
+#define test_fail(point) { \
+ _test_fail(point); \
+ return; \
+}
+
+/**
+ * @brief Test assertion.
+ *
+ * @param[in] point numeric assertion identifier
+ * @param[in] condition a boolean expression that must be verified to be true
+ * @param[in] msg failure message
+ */
+#define test_assert(point, condition, msg) { \
+ if (_test_assert(point, condition)) \
+ return; \
+}
+
+/**
+ * @brief Test assertion with lock.
+ *
+ * @param[in] point numeric assertion identifier
+ * @param[in] condition a boolean expression that must be verified to be true
+ * @param[in] msg failure message
+ */
+#define test_assert_lock(point, condition, msg) { \
+ chSysLock(); \
+ if (_test_assert(point, condition)) { \
+ chSysUnlock(); \
+ return; \
+ } \
+ chSysUnlock(); \
+}
+
+/**
+ * @brief Test sequence assertion.
+ *
+ * @param[in] point numeric assertion identifier
+ * @param[in] expected string to be matched with the tokens buffer
+ */
+#define test_assert_sequence(point, expected) { \
+ if (_test_assert_sequence(point, expected)) \
+ return; \
+}
+
+/**
+ * @brief Test time window assertion.
+ *
+ * @param[in] point numeric assertion identifier
+ * @param[in] start initial time in the window (included)
+ * @param[in] end final time in the window (not included)
+ */
+#define test_assert_time_window(point, start, end) { \
+ if (_test_assert_time_window(point, start, end)) \
+ return; \
+}
+
+#if !defined(__DOXYGEN__)
+extern Thread *threads[MAX_THREADS];
+extern union test_buffers test;
+extern void * ROMCONST wa[];
+extern bool_t test_timer_done;
+#endif
+
+#endif /* _TEST_H_ */
+
+/** @} */
diff --git a/firmware/emulation/test/testbmk.c b/firmware/emulation/test/testbmk.c
new file mode 100644
index 0000000000..069dff0590
--- /dev/null
+++ b/firmware/emulation/test/testbmk.c
@@ -0,0 +1,724 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "main.h"
+
+#if EFI_PERF_METRICS
+#include "test.h"
+
+/**
+ * @page test_benchmarks Kernel Benchmarks
+ *
+ * File: @ref testbmk.c
+ *
+ *
Description
+ * This module implements a series of system benchmarks. The benchmarks are
+ * useful as a stress test and as a reference when comparing ChibiOS/RT
+ * with similar systems.
+ *
+ *
Objective
+ * Objective of the test module is to provide a performance index for the
+ * most critical system subsystems. The performance numbers allow to
+ * discover performance regressions between successive ChibiOS/RT releases.
+ *
+ *
+ * A message server thread is created with a lower priority than the client
+ * thread, the messages throughput per second is measured and the result
+ * printed in the output log.
+ */
+
+static void bmk1_execute(void) {
+ uint32_t n;
+
+ threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()-1, thread1, NULL);
+ n = msg_loop_test(threads[0]);
+ test_wait_threads();
+ test_print("--- Score : ");
+ test_printn(n);
+ test_print(" msgs/S, ");
+ test_printn(n << 1);
+ test_println(" ctxswc/S");
+}
+
+ROMCONST struct testcase testbmk1 = {
+ "Benchmark, messages #1",
+ NULL,
+ NULL,
+ bmk1_execute
+};
+
+/**
+ * @page test_benchmarks_002 Messages performance #2
+ *
+ *
Description
+ * A message server thread is created with an higher priority than the client
+ * thread, the messages throughput per second is measured and the result
+ * printed in the output log.
+ */
+
+static void bmk2_execute(void) {
+ uint32_t n;
+
+ threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()+1, thread1, NULL);
+ n = msg_loop_test(threads[0]);
+ test_wait_threads();
+ test_print("--- Score : ");
+ test_printn(n);
+ test_print(" msgs/S, ");
+ test_printn(n << 1);
+ test_println(" ctxswc/S");
+}
+
+ROMCONST struct testcase testbmk2 = {
+ "Benchmark, messages #2",
+ NULL,
+ NULL,
+ bmk2_execute
+};
+
+static msg_t thread2(void *p) {
+
+ return (msg_t)p;
+}
+
+/**
+ * @page test_benchmarks_003 Messages performance #3
+ *
+ *
Description
+ * A message server thread is created with an higher priority than the client
+ * thread, four lower priority threads crowd the ready list, the messages
+ * throughput per second is measured while the ready list and the result
+ * printed in the output log.
+ */
+
+static void bmk3_execute(void) {
+ uint32_t n;
+
+ threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()+1, thread1, NULL);
+ threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriority()-2, thread2, NULL);
+ threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriority()-3, thread2, NULL);
+ threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriority()-4, thread2, NULL);
+ threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriority()-5, thread2, NULL);
+ n = msg_loop_test(threads[0]);
+ test_wait_threads();
+ test_print("--- Score : ");
+ test_printn(n);
+ test_print(" msgs/S, ");
+ test_printn(n << 1);
+ test_println(" ctxswc/S");
+}
+
+ROMCONST struct testcase testbmk3 = {
+ "Benchmark, messages #3",
+ NULL,
+ NULL,
+ bmk3_execute
+};
+
+/**
+ * @page test_benchmarks_004 Context Switch performance
+ *
+ *
Description
+ * A thread is created that just performs a @p chSchGoSleepS() into a loop,
+ * the thread is awakened as fast is possible by the tester thread.
+ * The Context Switch performance is calculated by measuring the number of
+ * iterations after a second of continuous operations.
+ */
+
+msg_t thread4(void *p) {
+ msg_t msg;
+ Thread *self = chThdSelf();
+
+ (void)p;
+ chSysLock();
+ do {
+ chSchGoSleepS(THD_STATE_SUSPENDED);
+ msg = self->p_u.rdymsg;
+ } while (msg == RDY_OK);
+ chSysUnlock();
+ return 0;
+}
+
+static void bmk4_execute(void) {
+ Thread *tp;
+ uint32_t n;
+
+ tp = threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()+1, thread4, NULL);
+ n = 0;
+ test_wait_tick();
+ test_start_timer(1000);
+ do {
+ chSysLock();
+ chSchWakeupS(tp, RDY_OK);
+ chSchWakeupS(tp, RDY_OK);
+ chSchWakeupS(tp, RDY_OK);
+ chSchWakeupS(tp, RDY_OK);
+ chSysUnlock();
+ n += 4;
+#if defined(SIMULATOR)
+ ChkIntSources();
+#endif
+ } while (!test_timer_done);
+ chSysLock();
+ chSchWakeupS(tp, RDY_TIMEOUT);
+ chSysUnlock();
+
+ test_wait_threads();
+ test_print("--- Score : ");
+ test_printn(n * 2);
+ test_println(" ctxswc/S");
+}
+
+ROMCONST struct testcase testbmk4 = {
+ "Benchmark, context switch",
+ NULL,
+ NULL,
+ bmk4_execute
+};
+
+/**
+ * @page test_benchmarks_005 Threads performance, full cycle
+ *
+ *
Description
+ * Threads are continuously created and terminated into a loop. A full
+ * @p chThdCreateStatic() / @p chThdExit() / @p chThdWait() cycle is performed
+ * in each iteration.
+ * The performance is calculated by measuring the number of iterations after
+ * a second of continuous operations.
+ */
+
+static void bmk5_execute(void) {
+
+ uint32_t n = 0;
+ void *wap = wa[0];
+ tprio_t prio = chThdGetPriority() - 1;
+ test_wait_tick();
+ test_start_timer(1000);
+ do {
+ chThdWait(chThdCreateStatic(wap, WA_SIZE, prio, thread2, NULL));
+ n++;
+#if defined(SIMULATOR)
+ ChkIntSources();
+#endif
+ } while (!test_timer_done);
+ test_print("--- Score : ");
+ test_printn(n);
+ test_println(" threads/S");
+}
+
+ROMCONST struct testcase testbmk5 = {
+ "Benchmark, threads, full cycle",
+ NULL,
+ NULL,
+ bmk5_execute
+};
+
+/**
+ * @page test_benchmarks_006 Threads performance, create/exit only
+ *
+ *
Description
+ * Threads are continuously created and terminated into a loop. A partial
+ * @p chThdCreateStatic() / @p chThdExit() cycle is performed in each
+ * iteration, the @p chThdWait() is not necessary because the thread is
+ * created at an higher priority so there is no need to wait for it to
+ * terminate.
+ * The performance is calculated by measuring the number of iterations after
+ * a second of continuous operations.
+ */
+
+static void bmk6_execute(void) {
+
+ uint32_t n = 0;
+ void *wap = wa[0];
+ tprio_t prio = chThdGetPriority() + 1;
+ test_wait_tick();
+ test_start_timer(1000);
+ do {
+ chThdCreateStatic(wap, WA_SIZE, prio, thread2, NULL);
+ n++;
+#if defined(SIMULATOR)
+ ChkIntSources();
+#endif
+ } while (!test_timer_done);
+ test_print("--- Score : ");
+ test_printn(n);
+ test_println(" threads/S");
+}
+
+ROMCONST struct testcase testbmk6 = {
+ "Benchmark, threads, create only",
+ NULL,
+ NULL,
+ bmk6_execute
+};
+
+/**
+ * @page test_benchmarks_007 Mass reschedule performance
+ *
+ *
Description
+ * Five threads are created and atomically rescheduled by resetting the
+ * semaphore where they are waiting on. The operation is performed into a
+ * continuous loop.
+ * The performance is calculated by measuring the number of iterations after
+ * a second of continuous operations.
+ */
+
+static msg_t thread3(void *p) {
+
+ (void)p;
+ while (!chThdShouldTerminate())
+ chSemWait(&sem1);
+ return 0;
+}
+
+static void bmk7_setup(void) {
+
+ chSemInit(&sem1, 0);
+}
+
+static void bmk7_execute(void) {
+ uint32_t n;
+
+ threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()+5, thread3, NULL);
+ threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriority()+4, thread3, NULL);
+ threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriority()+3, thread3, NULL);
+ threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriority()+2, thread3, NULL);
+ threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriority()+1, thread3, NULL);
+
+ n = 0;
+ test_wait_tick();
+ test_start_timer(1000);
+ do {
+ chSemReset(&sem1, 0);
+ n++;
+#if defined(SIMULATOR)
+ ChkIntSources();
+#endif
+ } while (!test_timer_done);
+ test_terminate_threads();
+ chSemReset(&sem1, 0);
+ test_wait_threads();
+
+ test_print("--- Score : ");
+ test_printn(n);
+ test_print(" reschedules/S, ");
+ test_printn(n * 6);
+ test_println(" ctxswc/S");
+}
+
+ROMCONST struct testcase testbmk7 = {
+ "Benchmark, mass reschedule, 5 threads",
+ bmk7_setup,
+ NULL,
+ bmk7_execute
+};
+
+/**
+ * @page test_benchmarks_008 I/O Round-Robin voluntary reschedule.
+ *
+ *
Description
+ * Five threads are created at equal priority, each thread just increases a
+ * variable and yields.
+ * The performance is calculated by measuring the number of iterations after
+ * a second of continuous operations.
+ */
+
+static msg_t thread8(void *p) {
+
+ do {
+ chThdYield();
+ chThdYield();
+ chThdYield();
+ chThdYield();
+ (*(uint32_t *)p) += 4;
+#if defined(SIMULATOR)
+ ChkIntSources();
+#endif
+ } while(!chThdShouldTerminate());
+ return 0;
+}
+
+static void bmk8_execute(void) {
+ uint32_t n;
+
+ n = 0;
+ test_wait_tick();
+
+ threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()-1, thread8, (void *)&n);
+ threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriority()-1, thread8, (void *)&n);
+ threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriority()-1, thread8, (void *)&n);
+ threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriority()-1, thread8, (void *)&n);
+ threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriority()-1, thread8, (void *)&n);
+
+ chThdSleepSeconds(1);
+ test_terminate_threads();
+ test_wait_threads();
+
+ test_print("--- Score : ");
+ test_printn(n);
+ test_println(" ctxswc/S");
+}
+
+ROMCONST struct testcase testbmk8 = {
+ "Benchmark, round robin context switching",
+ NULL,
+ NULL,
+ bmk8_execute
+};
+
+#if CH_USE_QUEUES || defined(__DOXYGEN__)
+/**
+ * @page test_benchmarks_009 I/O Queues throughput
+ *
+ *
Description
+ * Four bytes are written and then read from an @p InputQueue into a continuous
+ * loop.
+ * The performance is calculated by measuring the number of iterations after
+ * a second of continuous operations.
+ */
+
+static void bmk9_execute(void) {
+ uint32_t n;
+ static uint8_t ib[16];
+ static InputQueue iq;
+
+ chIQInit(&iq, ib, sizeof(ib), NULL, NULL);
+ n = 0;
+ test_wait_tick();
+ test_start_timer(1000);
+ do {
+ chSysLock();
+ chIQPutI(&iq, 0);
+ chIQPutI(&iq, 1);
+ chIQPutI(&iq, 2);
+ chIQPutI(&iq, 3);
+ chSysUnlock();
+ (void)chIQGet(&iq);
+ (void)chIQGet(&iq);
+ (void)chIQGet(&iq);
+ (void)chIQGet(&iq);
+ n++;
+#if defined(SIMULATOR)
+ ChkIntSources();
+#endif
+ } while (!test_timer_done);
+ test_print("--- Score : ");
+ test_printn(n * 4);
+ test_println(" bytes/S");
+}
+
+ROMCONST struct testcase testbmk9 = {
+ "Benchmark, I/O Queues throughput",
+ NULL,
+ NULL,
+ bmk9_execute
+};
+#endif /* CH_USE_QUEUES */
+
+/**
+ * @page test_benchmarks_010 Virtual Timers set/reset performance
+ *
+ *
Description
+ * A virtual timer is set and immediately reset into a continuous loop.
+ * The performance is calculated by measuring the number of iterations after
+ * a second of continuous operations.
+ */
+
+static void tmo(void *param) {(void)param;}
+
+static void bmk10_execute(void) {
+ static VirtualTimer vt1, vt2;
+ uint32_t n = 0;
+
+ test_wait_tick();
+ test_start_timer(1000);
+ do {
+ chSysLock();
+ chVTSetI(&vt1, 1, tmo, NULL);
+ chVTSetI(&vt2, 10000, tmo, NULL);
+ chVTResetI(&vt1);
+ chVTResetI(&vt2);
+ chSysUnlock();
+ n++;
+#if defined(SIMULATOR)
+ ChkIntSources();
+#endif
+ } while (!test_timer_done);
+ test_print("--- Score : ");
+ test_printn(n * 2);
+ test_println(" timers/S");
+}
+
+ROMCONST struct testcase testbmk10 = {
+ "Benchmark, virtual timers set/reset",
+ NULL,
+ NULL,
+ bmk10_execute
+};
+
+#if CH_USE_SEMAPHORES || defined(__DOXYGEN__)
+/**
+ * @page test_benchmarks_011 Semaphores wait/signal performance
+ *
+ *
Description
+ * A counting semaphore is taken/released into a continuous loop, no Context
+ * Switch happens because the counter is always non negative.
+ * The performance is calculated by measuring the number of iterations after
+ * a second of continuous operations.
+ */
+
+static void bmk11_setup(void) {
+
+ chSemInit(&sem1, 1);
+}
+
+static void bmk11_execute(void) {
+ uint32_t n = 0;
+
+ test_wait_tick();
+ test_start_timer(1000);
+ do {
+ chSemWait(&sem1);
+ chSemSignal(&sem1);
+ chSemWait(&sem1);
+ chSemSignal(&sem1);
+ chSemWait(&sem1);
+ chSemSignal(&sem1);
+ chSemWait(&sem1);
+ chSemSignal(&sem1);
+ n++;
+#if defined(SIMULATOR)
+ ChkIntSources();
+#endif
+ } while (!test_timer_done);
+ test_print("--- Score : ");
+ test_printn(n * 4);
+ test_println(" wait+signal/S");
+}
+
+ROMCONST struct testcase testbmk11 = {
+ "Benchmark, semaphores wait/signal",
+ bmk11_setup,
+ NULL,
+ bmk11_execute
+};
+#endif /* CH_USE_SEMAPHORES */
+
+#if CH_USE_MUTEXES || defined(__DOXYGEN__)
+/**
+ * @page test_benchmarks_012 Mutexes lock/unlock performance
+ *
+ *
Description
+ * A mutex is locked/unlocked into a continuous loop, no Context Switch happens
+ * because there are no other threads asking for the mutex.
+ * The performance is calculated by measuring the number of iterations after
+ * a second of continuous operations.
+ */
+
+static void bmk12_setup(void) {
+
+ chMtxInit(&mtx1);
+}
+
+static void bmk12_execute(void) {
+ uint32_t n = 0;
+
+ test_wait_tick();
+ test_start_timer(1000);
+ do {
+ chMtxLock(&mtx1);
+ chMtxUnlock();
+ chMtxLock(&mtx1);
+ chMtxUnlock();
+ chMtxLock(&mtx1);
+ chMtxUnlock();
+ chMtxLock(&mtx1);
+ chMtxUnlock();
+ n++;
+#if defined(SIMULATOR)
+ ChkIntSources();
+#endif
+ } while (!test_timer_done);
+ test_print("--- Score : ");
+ test_printn(n * 4);
+ test_println(" lock+unlock/S");
+}
+
+ROMCONST struct testcase testbmk12 = {
+ "Benchmark, mutexes lock/unlock",
+ bmk12_setup,
+ NULL,
+ bmk12_execute
+};
+#endif
+
+/**
+ * @page test_benchmarks_013 RAM Footprint
+ *
+ *