From ab412bec0fafb0a9b0e9c3f752ea3e5255c1409f Mon Sep 17 00:00:00 2001 From: Andrey B Date: Fri, 25 Apr 2014 22:50:40 -0500 Subject: [PATCH] ? --- config/boards/arro_board.h | 283 +++++++++++++++ config/engines/snow_blower.c | 14 + console/tunerstudio/tunerstudio.c | 276 ++++++++++++++ controllers/algo/event_queue.cpp | 89 +++++ controllers/algo/event_queue.h | 30 ++ controllers/algo/signal_executor.c | 163 +++++++++ controllers/algo/signal_executor.h | 111 ++++++ .../algo/signal_executor_single_timer_algo.c | 74 ++++ efitime.h | 61 ++++ engine_math.cpp | 340 ++++++++++++++++++ engine_math.h | 62 ++++ firmware/controllers/algo/fuel_math.c | 155 -------- firmware/controllers/math/engine_math.c | 231 ------------ firmware/hw_layer/lcd/lcd_2x16.c | 212 ----------- firmware/hw_layer/lcd/lcd_2x16.h | 17 - wave_chart.c | 146 ++++++++ 16 files changed, 1649 insertions(+), 615 deletions(-) create mode 100644 config/boards/arro_board.h create mode 100644 config/engines/snow_blower.c create mode 100644 console/tunerstudio/tunerstudio.c create mode 100644 controllers/algo/event_queue.cpp create mode 100644 controllers/algo/event_queue.h create mode 100644 controllers/algo/signal_executor.c create mode 100644 controllers/algo/signal_executor.h create mode 100644 controllers/algo/signal_executor_single_timer_algo.c create mode 100644 efitime.h create mode 100644 engine_math.cpp create mode 100644 engine_math.h delete mode 100644 firmware/controllers/algo/fuel_math.c delete mode 100644 firmware/controllers/math/engine_math.c delete mode 100644 firmware/hw_layer/lcd/lcd_2x16.c delete mode 100644 firmware/hw_layer/lcd/lcd_2x16.h create mode 100644 wave_chart.c diff --git a/config/boards/arro_board.h b/config/boards/arro_board.h new file mode 100644 index 0000000000..52bd07b48a --- /dev/null +++ b/config/boards/arro_board.h @@ -0,0 +1,283 @@ +/** + * @file arro_board.h + * + * This file contents a configuration of default ecu board. Pinout and other. + * TODO: most of the pins should get configurable + * + * + * @date Nov 14, 2013 + * @author Andrey Belomutskiy, (c) 2012-2014 + * @author frig + * + * 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 . + */ +#ifndef ARRO_BOARD_H_ +#define ARRO_BOARD_H_ + + +#define STM32_ICU_USE_TIM1 TRUE // wave input +#define STM32_ICU_USE_TIM2 TRUE // primary position sensor +#define STM32_ICU_USE_TIM3 TRUE // secondary position sensor +#define STM32_ICU_USE_TIM4 FALSE +#define STM32_ICU_USE_TIM5 FALSE +#define STM32_ICU_USE_TIM8 FALSE +#define STM32_ICU_USE_TIM9 TRUE // wave input + +// todo: switch to continues ADC conversion for slow ADC? +#define EFI_INTERNAL_SLOW_ADC_PWM &PWMD8 +// todo: switch to continues ADC conversion for fast ADC? +#define EFI_INTERNAL_FAST_ADC_PWM &PWMD4 + + +#define STM32_PWM_USE_TIM1 FALSE +#define STM32_PWM_USE_TIM2 FALSE +#define STM32_PWM_USE_TIM3 FALSE +// +#define STM32_PWM_USE_TIM4 TRUE // fast adc +#define STM32_PWM_USE_TIM5 FALSE +#define STM32_PWM_USE_TIM8 TRUE // slow adc +#define STM32_PWM_USE_TIM9 FALSE + +#define STM32_SPI_USE_SPI1 FALSE +#define STM32_SPI_USE_SPI2 FALSE // external ADC +#define STM32_SPI_USE_SPI3 TRUE // potentiometer + +#define STM32_CAN_USE_CAN1 TRUE +#define STM32_CAN_USE_CAN2 TRUE + +#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5) +#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6) + +#define EFI_CAN_DEVICE CAND2 +#define EFI_CAN_RX_PORT GPIOB +#define EFI_CAN_RX_PIN 12 +#define EFI_CAN_RX_AF 9 +#define EFI_CAN_TX_PORT GPIOB +#define EFI_CAN_TX_PIN 6 +#define EFI_CAN_TX_AF 9 + +//#define EFI_CAN_DEVICE CAND1 +//#define EFI_CAN_RX_PORT GPIOB +//#define EFI_CAN_RX_PIN 8 +//#define EFI_CAN_RX_AF 9 +//#define EFI_CAN_TX_PORT GPIOB +//#define EFI_CAN_TX_PIN 9 +//#define EFI_CAN_TX_AF 9 + +/** + * This section is for bottom-left corner SPI + */ +//#define SPI_CS1_PORT GPIOE +//#define SPI_CS1_PIN 13 +//#define SPI_CS2_PORT GPIOE +//#define SPI_CS2_PIN 14 +//#define SPI_CS3_PORT GPIOE +//#define SPI_CS3_PIN 15 +//#define SPI_CS4_PORT GPIOD +//#define SPI_CS4_PIN 10 +//#define SPI_SD_MODULE_PORT GPIOD +//#define SPI_SD_MODULE_PIN 11 +#define EFI_SPI2_SCK_PORT GPIOB +#define EFI_SPI2_SCK_PIN 13 +#define EFI_SPI2_MISO_PORT GPIOB +#define EFI_SPI2_MISO_PIN 14 +#define EFI_SPI2_MOSI_PORT GPIOB +#define EFI_SPI2_MOSI_PIN 15 +#define EFI_SPI2_AF 5 + +/** + * This section is for right-side center SPI + */ +#define SPI_CS1_PORT GPIOD +#define SPI_CS1_PIN 7 +// this is pointing into the sky for now - conflict with I2C +#define SPI_CS2_PORT GPIOH +// this is pointing into the sky for now - conflict with I2C +#define SPI_CS2_PIN 0 +#define SPI_CS3_PORT GPIOD +#define SPI_CS3_PIN 5 +#define SPI_CS4_PORT GPIOD +#define SPI_CS4_PIN 3 +#define SPI_SD_MODULE_PORT GPIOD +#define SPI_SD_MODULE_PIN 4 +#define EFI_SPI3_SCK_PORT GPIOB +#define EFI_SPI3_SCK_PIN 3 +#define EFI_SPI3_MISO_PORT GPIOB +#define EFI_SPI3_MISO_PIN 4 +#define EFI_SPI3_MOSI_PORT GPIOB +#define EFI_SPI3_MOSI_PIN 5 +#define EFI_SPI3_AF 6 +#define MMC_CARD_SPI SPID3 + +#define EFI_I2C_SCL_PORT GPIOB +#define EFI_I2C_SCL_PIN 6 +#define EFI_I2C_SDA_PORT GPIOB +#define EFI_I2C_SDA_PIN 7 +#define EFI_I2C_AF 4 + +#define EFI_ADC_SLOW_CHANNELS_COUNT 10 + +#define EFI_USE_ADC_CHANNEL_IN0 TRUE +#define EFI_USE_ADC_CHANNEL_IN1 TRUE +#define EFI_USE_ADC_CHANNEL_IN2 TRUE +#define EFI_USE_ADC_CHANNEL_IN3 TRUE +#define EFI_USE_ADC_CHANNEL_IN4 TRUE + +#define EFI_USE_ADC_CHANNEL_IN6 TRUE +#define EFI_USE_ADC_CHANNEL_IN7 TRUE + +#define EFI_USE_ADC_CHANNEL_IN11 TRUE +#define EFI_USE_ADC_CHANNEL_IN12 TRUE +#define EFI_USE_ADC_CHANNEL_IN13 TRUE +#define EFI_USE_ADC_CHANNEL_IN14 FALSE +#define EFI_USE_ADC_CHANNEL_IN15 FALSE + +/** + * Patched version of ChibiOS/RT support extra details in the system error messages + */ +#define EFI_CUSTOM_PANIC_METHOD TRUE + +/* + * 10 channel board is (from left to right): + * ADC 15 PC5 TPS + * ADC 14 PC4 MAP + * ADC 7 PA7 IAT + * ADC 6 PA6 CLT + * ADC 5 PA5 TIM2_CH1 + * ADC 4 PA4 + * ADC 3 PA3 + * ADC 2 PA2 + * ADC 1 PA1 vBatt + * ADC 0 PA0 MAF + */ + +#define ADC_LOGIC_TPS_2 ADC_CHANNEL_IN0 + +#define ADC_CHANNEL_VREF ADC_CHANNEL_IN14 + + +/** + * currently ChibiOS uses only first and second channels of each timer for input capture + * + * So, our options are: + * + * TIM2_CH1 + * PA5 + * + * TIM4_CH1 + * PB6 + * PD12 + * + * TIM9_CH1 + * PE5 + */ + + +/** + * Primary shaft position input + * TODO: ? rename to PRIMARY_TRIGGER? + */ + +#define PRIMARY_SHAFT_POSITION_INPUT_DRIVER ICUD3 +#define PRIMARY_SHAFT_POSITION_INPUT_PORT GPIOC +#define PRIMARY_SHAFT_POSITION_INPUT_PIN 6 +#define PRIMARY_SHAFT_POSITION_INPUT_CHANNEL ICU_CHANNEL_1 + +/** + * Secondary shaft position input + * TODO: ? rename to SECONDARY_TRIGGER? * + */ +#define SECONDARY_SHAFT_POSITION_INPUT_DRIVER ICUD2 +#define SECONDARY_SHAFT_POSITION_INPUT_PORT GPIOA +#define SECONDARY_SHAFT_POSITION_INPUT_PIN 5 +#define SECONDARY_SHAFT_POSITION_INPUT_CHANNEL ICU_CHANNEL_1 + +/* Logic analyzer */ +#define LOGIC_ANALYZER_1_DRIVER ICUD1 +#define LOGIC_ANALYZER_1_PORT GPIOA +#define LOGIC_ANALYZER_1_PIN 8 + +#define LOGIC_ANALYZER_2_DRIVER ICUD9 +#define LOGIC_ANALYZER_2_PORT GPIOE +#define LOGIC_ANALYZER_2_PIN 7 + +//#define ETB_CONTROL_LINE_1_PORT GPIOE +//#define ETB_CONTROL_LINE_1_PIN 0 +// +//#define ETB_CONTROL_LINE_2_PORT GPIOB +//#define ETB_CONTROL_LINE_2_PIN 8 + +//#define CONSOLE_PORT GPIOB +//#define CONSOLE_TX_PIN 10 +//#define CONSOLE_RX_PIN 11 + +/** + * Here we define the pinout for the human-readable protocol via UART, TunerStudio pinout is defined separately + */ +//#define EFI_CONSOLE_TX_PORT GPIOD +//#define EFI_CONSOLE_TX_PIN 8 +//#define EFI_CONSOLE_RX_PORT GPIOD +//#define EFI_CONSOLE_RX_PIN 9 +//#define EFI_CONSOLE_AF 7 + +#define EFI_CONSOLE_UART_DEVICE (&SD3) + +#define EFI_CONSOLE_TX_PORT GPIOC +#define EFI_CONSOLE_TX_PIN 10 +#define EFI_CONSOLE_RX_PORT GPIOC +#define EFI_CONSOLE_RX_PIN 11 +#define EFI_CONSOLE_AF 7 + +//#define TS_SERIAL_TX_PORT GPIOD +//#define TS_SERIAL_TX_PIN 8 +//#define TS_SERIAL_RX_PORT GPIOD +//#define TS_SERIAL_RX_PIN 9 +//#define TS_SERIAL_AF 7 + +#define TS_SERIAL_TX_PORT GPIOC +#define TS_SERIAL_TX_PIN 10 +#define TS_SERIAL_RX_PORT GPIOC +#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_RUNNING_STATUS_PORT GPIOD +#define LED_RUNNING_STATUS_PIN GPIOD_LED4 + +#define LED_ERROR_PORT GPIOD +#define LED_ERROR_PIN GPIOD_LED5 + +#define LED_COMMUNICATION_PORT GPIOD +#define LED_COMMUNICATION_PIN GPIOD_LED6 + +#define EFI_SIGNAL_EXECUTOR_SLEEP FALSE +#define EFI_SIGNAL_EXECUTOR_SINGLE_TIMER FALSE +#define EFI_SIGNAL_EXECUTOR_ONE_TIMER TRUE +#define EFI_SIGNAL_EXECUTOR_HW_TIMER FALSE + +//#define EFI_SIGNAL_EXECUTOR_SLEEP FALSE +//#define EFI_SIGNAL_EXECUTOR_SINGLE_TIMER TRUE + + +// USART1 -> check defined STM32_SERIAL_USE_USART1 +// For GPS we have USART1. We can start with PB7 USART1_RX and PB6 USART1_TX +#define GPS_SERIAL_DEVICE &SD1 +#define GPS_SERIAL_SPEED 38400 +#define GPS_PORT GPIOB +#define GPS_SERIAL_TX_PIN 6 +#define GPS_SERIAL_RX_PIN 7 + +#endif /*ARRO_BOARD_H_*/ diff --git a/config/engines/snow_blower.c b/config/engines/snow_blower.c new file mode 100644 index 0000000000..c09ccfc50c --- /dev/null +++ b/config/engines/snow_blower.c @@ -0,0 +1,14 @@ +/** + * @file snow_blower.c + * @brief Default configuration of a single-cylinder engine + * + * @date Sep 9, 2013 + * @author Andrey Belomutskiy, (c) 2012-2014 + */ + +#include "main.h" + +#if EFI_ENGINE_SNOW_BLOWER + + +#endif diff --git a/console/tunerstudio/tunerstudio.c b/console/tunerstudio/tunerstudio.c new file mode 100644 index 0000000000..6a09eb32fb --- /dev/null +++ b/console/tunerstudio/tunerstudio.c @@ -0,0 +1,276 @@ +/** + * @file tunerstudio.c + * @brief Integration with EFI Analytics Tuner Studio software + * + * todo: merge this file with tunerstudio_algo.c? + * + * @date Aug 26, 2013 + * @author Andrey Belomutskiy, (c) 2012-2014 + * + * This file is part of rusEfi - see http://rusefi.com + * + * rusEfi is free software; you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + * + */ + +#include "main.h" + +#include "engine_state.h" +#include "tunerstudio.h" +#include "pin_repository.h" + +#include "main_trigger_callback.h" +#include "flash_main.h" +#include "usbconsole.h" +#include "map_averaging.h" + +#include "tunerstudio_algo.h" +#include "tunerstudio_configuration.h" +#include "malfunction_central.h" +#include "wave_math.h" + +#if EFI_TUNER_STUDIO + +static Logging logger; + +extern engine_configuration_s *engineConfiguration; +extern board_configuration_s *boardConfiguration; +extern persistent_config_s configWorkingCopy; +extern FlashState flashState; + +extern SerialUSBDriver SDU1; +#define CONSOLE_DEVICE &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 WORKING_AREA(TS_WORKING_AREA, UTILITY_THREAD_STACK_SIZE); + +static int tsCounter = 0; +static int writeCounter = 0; + +static short pageId; + +static TunerStudioWriteRequest writeRequest; + +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 */ + 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 B counter=%d", tsState.burnCommandCounter); + scheduleMsg(&logger, "TunerStudio W counter=%d", writeCounter); + 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); +} + +void tunerStudioDebug(char *msg) { +#if EFI_TUNER_STUDIO_VERBOSE + scheduleMsg(&logger, "%s", msg); + printStats(); +#endif +} + +char *getWorkingPageAddr(int pageIndex) { + switch (pageIndex) { + case 0: + return (char*) &configWorkingCopy.engineConfiguration; + case 1: + return (char*) &configWorkingCopy.boardConfiguration; + } + return NULL; +} + +int getTunerStudioPageSize(int pageIndex) { + switch (pageIndex) { + case 0: + return sizeof(configWorkingCopy.engineConfiguration); + case 1: + return sizeof(configWorkingCopy.boardConfiguration); + } + return 0; + +} + +/** + * 'Write' command receives a single value at a given offset + */ +void handleValueWriteCommand(void) { + writeCounter++; + + //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); +// 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]; +// + + efitimems_t nowMs = currentTimeMillis(); + if (nowMs - previousWriteReportMs > 5) { + previousWriteReportMs = nowMs; +// scheduleMsg(&logger, "page %d offset %d: value=%d", pageId, writeRequest.offset, writeRequest.value); + } + + getWorkingPageAddr(pageId)[writeRequest.offset] = writeRequest.value; + +// scheduleMsg(&logger, "va=%d", configWorkingCopy.boardConfiguration.idleValvePin); +} + +void handlePageReadCommand(void) { + tsState.readPageCommandsCounter++; + tunerStudioDebug("got C (Constants)"); + 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", pageId); +#endif + + tunerStudioWriteData((const uint8_t *) getWorkingPageAddr(pageId), getTunerStudioPageSize(pageId)); +} + + +/** + * 'Burn' command is a command to commit the changes + */ +void handleBurnCommand(void) { + tsState.burnCommandCounter++; + + tunerStudioDebug("got B (Burn)"); + + 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); +#endif + + // todo: how about some multi-threading? + memcpy(&flashState.persistentConfiguration, &configWorkingCopy, sizeof(persistent_config_s)); + + scheduleMsg(&logger, "va1=%d", configWorkingCopy.boardConfiguration.idleValvePin); + scheduleMsg(&logger, "va2=%d", flashState.persistentConfiguration.boardConfiguration.idleValvePin); + + writeToFlash(); + incrementGlobalConfigurationVersion(); +} + +static msg_t tsThreadEntryPoint(void *arg) { + (void) arg; + chRegSetThreadName("tunerstudio thread"); + + int wasReady = FALSE; + while (true) { + int isReady = ts_serail_ready(); + if (!isReady) { + chThdSleepMilliseconds(10); + wasReady = FALSE; + continue; + } + if (!wasReady) { + wasReady = TRUE; +// scheduleSimpleMsg(&logger, "ts channel is now ready ", hTimeNow()); + } + + 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++; + } +#if defined __GNUC__ + return 0; +#endif +} + +extern engine_configuration_s *engineConfiguration; + +void syncTunerStudioCopy(void) { + memcpy(&configWorkingCopy, &flashState.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)); + + sdStart(TS_SERIAL_DEVICE, &tsSerialConfig); +#endif + + syncTunerStudioCopy(); + + addConsoleAction("tsinfo", printStats); + + 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 = getAtmosphericPressure(); + tsOutputChannels.manifold_air_pressure = getMap(); + tsOutputChannels.checkEngine = hasErrorCodes(); +} + +#endif /* EFI_TUNER_STUDIO */ diff --git a/controllers/algo/event_queue.cpp b/controllers/algo/event_queue.cpp new file mode 100644 index 0000000000..6dfca9306c --- /dev/null +++ b/controllers/algo/event_queue.cpp @@ -0,0 +1,89 @@ +/** + * @file event_queue.cpp + * This is a data structure which keeps track of all pending events + * Implemented as a linked list, which is fine since the number of + * pending events is pretty low + * todo: MAYBE migrate to a better data structure, but that's low priority + * + * this data structure is NOT thread safe + * + * @date Apr 17, 2014 + * @author Andrey Belomutskiy, (c) 2012-2014 + */ + +#include "event_queue.h" +#include "efitime.h" +#include "utlist.h" + +EventQueue::EventQueue() { + head = NULL; +} + +void EventQueue::insertTask(scheduling_s *scheduling, uint64_t nowUs, int delayUs, schfunc_t callback, void *param) { + if (callback == NULL) + firmwareError("NULL callback"); + uint64_t time = nowUs + delayUs; + + scheduling->momentUs = time; +#if EFI_SIGNAL_EXECUTOR_ONE_TIMER + scheduling->callback = callback; + scheduling->param = param; +#endif + + scheduling_s * elt; + LL_FOREACH(head, elt) + { + if (elt == scheduling) { + firmwareError("re-adding element"); + return; + } + } + + LL_PREPEND(head, scheduling); +} + +void EventQueue::insertTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) { + insertTask(scheduling, getTimeNowUs(), delayUs, callback, param); +} + +/** + * Get the timestamp of the soonest pending action + */ +uint64_t EventQueue::getNextEventTime(uint64_t nowUs) { + scheduling_s * elt; + // this is a large value which is expected to be larger than any real time + uint64_t result = EMPTY_QUEUE; + + LL_FOREACH(head, elt) + { + if (elt->momentUs <= nowUs) { + // todo: I am not so sure about this branch + continue; + } + if (elt->momentUs < result) + result = elt->momentUs; + } + return result; +} + +/** + * Invoke all pending actions prior to specified timestamp + */ +void EventQueue::executeAll(uint64_t now) { + scheduling_s * elt, *tmp; + +// here we need safe iteration because we are removing elements + LL_FOREACH_SAFE(head, elt, tmp) + { + if (elt->momentUs <= now) { + LL_DELETE(head, elt); +#if EFI_SIGNAL_EXECUTOR_ONE_TIMER + elt->callback(elt->param); +#endif /* EFI_SIGNAL_EXECUTOR_ONE_TIMER */ + } + } +} + +void EventQueue::clear(void) { + head = NULL; +} diff --git a/controllers/algo/event_queue.h b/controllers/algo/event_queue.h new file mode 100644 index 0000000000..3f307c0e31 --- /dev/null +++ b/controllers/algo/event_queue.h @@ -0,0 +1,30 @@ +/** + * @file event_queue.h + * + * @date Apr 17, 2014 + * @author Andrey Belomutskiy, (c) 2012-2014 + */ + +#include "signal_executor.h" + +#ifndef EVENT_SCHEDULER_H_ +#define EVENT_SCHEDULER_H_ + +#define EMPTY_QUEUE 0x0FFFFFFFFFFFFFFFLL + +class EventQueue { +public: + EventQueue(); + + void insertTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param); + void insertTask(scheduling_s *scheduling, uint64_t nowUs, int delayUs, schfunc_t callback, void *param); + + void executeAll(uint64_t now); + + uint64_t getNextEventTime(uint64_t nowUs); + void clear(void); +private: + scheduling_s *head; +}; + +#endif /* EVENT_SCHEDULER_H_ */ diff --git a/controllers/algo/signal_executor.c b/controllers/algo/signal_executor.c new file mode 100644 index 0000000000..0fe0845b04 --- /dev/null +++ b/controllers/algo/signal_executor.c @@ -0,0 +1,163 @@ +/** + * @file signal_executor.c + * + * todo: we should split this file into two: + * one for pure scheduling and another one for signal output which would + * use the scheduling + * + * @date Dec 4, 2013 + * @author Andrey Belomutskiy, (c) 2012-2014 + * + * This file is part of rusEfi - see http://rusefi.com + * + * rusEfi is free software; you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "main.h" +#include "signal_executor.h" + +#if EFI_WAVE_CHART +#include "rpm_calculator.h" +#endif + +#if EFI_WAVE_ANALYZER + +/** + * Signal executors feed digital events right into WaveChart used by Sniffer tab of Dev Console + */ +#include "wave_analyzer.h" + +#endif /* EFI_WAVE_ANALYZER */ + +#if EFI_PROD_CODE || EFI_SIMULATOR + static Logging logger; +#endif + +void initSignalExecutor(void) { +#if EFI_PROD_CODE || EFI_SIMULATOR + initLogging(&logger, "s exec"); +#endif + initSignalExecutorImpl(); +} + +void initOutputSignalBase(OutputSignal *signal) { + signal->status = IDLE; +// signal->last_scheduling_time = 0; + signal->initialized = TRUE; +} + +static void turnHigh(OutputSignal *signal) { +#if EFI_DEFAILED_LOGGING +// signal->hi_time = hTimeNow(); +#endif /* EFI_DEFAILED_LOGGING */ + io_pin_e pin = signal->io_pin; + // turn the output level ACTIVE + // todo: this XOR should go inside the setOutputPinValue method + setOutputPinValue(pin, TRUE); + // sleep for the needed duration + +#if EFI_PROD_CODE || EFI_SIMULATOR + if( + pin == SPARKOUT_1_OUTPUT || + pin == SPARKOUT_3_OUTPUT) { +// time_t now = hTimeNow(); +// float an = getCrankshaftAngle(now); +// scheduleMsg(&logger, "spark up%d %d", pin, now); +// scheduleMsg(&logger, "spark angle %d %f", (int)an, an); + } +#endif + +#if EFI_WAVE_CHART + addWaveChartEvent(signal->name, "up", ""); +#endif /* EFI_WAVE_ANALYZER */ +} + +static void turnLow(OutputSignal *signal) { + // turn off the output + // todo: this XOR should go inside the setOutputPinValue method + setOutputPinValue(signal->io_pin, FALSE); + +#if EFI_DEFAILED_LOGGING + systime_t after = hTimeNow(); + debugInt(&signal->logging, "a_time", after - signal->hi_time); + scheduleLogging(&signal->logging); +#endif /* EFI_DEFAILED_LOGGING */ + +#if EFI_WAVE_CHART + addWaveChartEvent(signal->name, "down", ""); +#endif /* EFI_WAVE_ANALYZER */ +} + +/** + * + * @param delay the number of ticks before the output signal + * immediate output if delay is zero + * @param dwell the number of ticks of output duration + * + */ + +int getRevolutionCounter(void); + +void scheduleOutput(OutputSignal *signal, float delayMs, float durationMs) { + if (durationMs < 0) { + firmwareError("duration cannot be negative: %d", durationMs); + return; + } + + scheduleOutputBase(signal, delayMs, durationMs); + + int index = getRevolutionCounter() % 2; + scheduling_s * sUp = &signal->signalTimerUp[index]; + scheduling_s * sDown = &signal->signalTimerDown[index]; + + scheduleTask(sUp, MS2US(delayMs), (schfunc_t) &turnHigh, (void *) signal); + scheduleTask(sDown, MS2US(delayMs + durationMs), (schfunc_t) &turnLow, (void*)signal); + +// signal->last_scheduling_time = now; +} + +void scheduleOutputBase(OutputSignal *signal, float delayMs, float durationMs) { + /** + * it's better to check for the exact 'TRUE' value since otherwise + * we would accept any memory garbage + */ + chDbgCheck(signal->initialized == TRUE, "Signal not initialized"); +// signal->offset = offset; +// signal->duration = duration; +} + + +char *getPinName(io_pin_e io_pin) { + switch (io_pin) { + case SPARKOUT_1_OUTPUT: + return "Spark 1"; + case SPARKOUT_2_OUTPUT: + return "Spark 2"; + case SPARKOUT_3_OUTPUT: + return "Spark 3"; + case SPARKOUT_4_OUTPUT: + return "Spark 4"; + + case INJECTOR_1_OUTPUT: + return "Injector 1"; + case INJECTOR_2_OUTPUT: + return "Injector 2"; + case INJECTOR_3_OUTPUT: + return "Injector 3"; + case INJECTOR_4_OUTPUT: + return "Injector 4"; + case INJECTOR_5_OUTPUT: + return "Injector 5"; + default: + return "No name"; + } +} diff --git a/controllers/algo/signal_executor.h b/controllers/algo/signal_executor.h new file mode 100644 index 0000000000..659848da90 --- /dev/null +++ b/controllers/algo/signal_executor.h @@ -0,0 +1,111 @@ +/** + * @file signal_executor.h + * @brief Asynchronous output signal header + * + * @date Feb 10, 2013 + * @author Andrey Belomutskiy, (c) 2012-2014 + */ + +#ifndef SPARKOUT_H_ +#define SPARKOUT_H_ + +#include "rusefi_enums.h" +#include "global.h" +#include "efifeatures.h" +#include "io_pins.h" + +#if EFI_PROD_CODE +#include "datalogging.h" +#endif /* EFI_PROD_CODE */ + +#if EFI_SIGNAL_EXECUTOR_SLEEP +#include "signal_executor_sleep.h" +#endif /* EFI_SIGNAL_EXECUTOR_SLEEP */ + +#if EFI_SIGNAL_EXECUTOR_SINGLE_TIMER +#include "signal_executor_single_timer.h" +#endif /* EFI_SIGNAL_EXECUTOR_SINGLE_TIMER */ + +typedef void (*schfunc_t)(void *); + +typedef struct scheduling_struct scheduling_s; +struct scheduling_struct { + //int initialized; +#if EFI_SIGNAL_EXECUTOR_SLEEP + VirtualTimer timer; +#endif /* EFI_SIGNAL_EXECUTOR_SLEEP */ +#if EFI_SIGNAL_EXECUTOR_SINGLE_TIMER + volatile time_t moment; +#endif /* EFI_SIGNAL_EXECUTOR_SINGLE_TIMER */ + + volatile uint64_t momentUs; +#if EFI_SIGNAL_EXECUTOR_ONE_TIMER + schfunc_t callback; + void *param; +#endif + + scheduling_s *next; +}; + +typedef enum { + IDLE = 0, ACTIVE +} executor_status_t; + +/** + * @brief Asynchronous output signal data structure + */ +typedef struct OutputSignal_struct OutputSignal; +struct OutputSignal_struct { + /** + * name of this signal + */ + char *name; + io_pin_e io_pin; +#if 0 // depricated + // time in system ticks + volatile int offset; + // time in system ticks + volatile int duration; +#endif + int initialized; + +// time_t last_scheduling_time; +// time_t hi_time; + + /** + * We are alternating instances so that events which extend into next revolution are not overriden while + * scheduling next revolution events + */ + scheduling_s signalTimerUp[2]; + scheduling_s signalTimerDown[2]; + + executor_status_t status; + +#if EFI_SIGNAL_EXECUTOR_HW_TIMER + // todo +#endif + + OutputSignal *next; +}; + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +void initOutputSignal(OutputSignal *signal, io_pin_e ioPin); +void scheduleOutput(OutputSignal *signal, float delayMs, float durationMs); +void initOutputSignalBase(OutputSignal *signal); +void scheduleOutputBase(OutputSignal *signal, float delayMs, float durationMs); + +void initSignalExecutor(void); +void initSignalExecutorImpl(void); +void scheduleTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param); +void scheduleByAngle(scheduling_s *timer, float angle, schfunc_t callback, void *param); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SPARKOUT_H_ */ diff --git a/controllers/algo/signal_executor_single_timer_algo.c b/controllers/algo/signal_executor_single_timer_algo.c new file mode 100644 index 0000000000..73893445dd --- /dev/null +++ b/controllers/algo/signal_executor_single_timer_algo.c @@ -0,0 +1,74 @@ +/** + * @file signal_executor_single_timer_algo.c + * + * @date Nov 28, 2013 + * @author Andrey Belomutskiy, (c) 2012-2014 + * + * + * This file is part of rusEfi - see http://rusefi.com + * + * rusEfi is free software; you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "signal_executor.h" +#include "signal_executor_single_timer_algo.h" +#include "main.h" +#include "utlist.h" +#include "io_pins.h" + +#if EFI_WAVE_ANALYZER +#include "wave_analyzer.h" +#include "wave_chart.h" +extern WaveChart waveChart; +#endif + +#if EFI_SIGNAL_EXECUTOR_SINGLE_TIMER +/** + * @brief Output list + * + * List of all active output signals + * This is actually the head of the list. + * When the list is empty (initial state) the head of the list should be NULL. + * This is by design. + */ +OutputSignal *st_output_list = NULL; + +inline void registerSignal(OutputSignal *signal) { + LL_APPEND(st_output_list, signal); +} + +void setOutputPinValue(io_pin_e pin, int value); + +/** + * @return time of next event within for this signal + * @todo Find better name. + */ +inline time_t toggleSignalIfNeeded(OutputSignal *out, time_t now) { +// chDbgCheck(out!=NULL, "out is NULL"); +// chDbgCheck(out->io_pin < IO_PIN_COUNT, "pin assertion"); + time_t last = out->last_scheduling_time; + //estimated = last + out->timing[out->status]; + time_t estimated = last + GET_DURATION(out); + if (now >= estimated) { + out->status ^= 1; /* toggle status */ + //setOutputPinValue(out->io_pin, out->status); /* Toggle output */ + palWritePad(GPIOE, 5, out->status); +#if EFI_WAVE_ANALYZER +// addWaveChartEvent(out->name, out->status ? "up" : "down", ""); +#endif /* EFI_WAVE_ANALYZER */ + +// out->last_scheduling_time = now; /* store last update */ + estimated = now + GET_DURATION(out); /* update estimation */ + } + return estimated - now; +} +#endif /* EFI_SIGNAL_EXECUTOR_SINGLE_TIMER */ diff --git a/efitime.h b/efitime.h new file mode 100644 index 0000000000..d39a9aa34c --- /dev/null +++ b/efitime.h @@ -0,0 +1,61 @@ +/** + * @file efitime.h + * + * By the way, there are 86400000 milliseconds in a day + * + * @date Apr 14, 2014 + * @author Andrey Belomutskiy, (c) 2012-2014 + */ + +#ifndef EFITIME_H_ +#define EFITIME_H_ + +#include +#include "efifeatures.h" + +/** + * integer time in milliseconds + * 32 bit 4B / 1000 = 4M seconds = 1111.11 hours = 46 days. + * Please restart your ECU every 46 days? :) + */ +typedef uint32_t efitimems_t; + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#define US_PER_SECOND 1000000 + +#define MS2US(MS_TIME) ((MS_TIME) * 1000) + +#define US_TO_TI_TEMP 10 + +// todo: implement a function to work with times considering counter overflow +#define overflowDiff(now, time) ((now) - (time)) + +/** + * 64-bit counter of microseconds (1/1 000 000 of a second) since MCU reset + * + * By using 64 bit, we can achive a very precise timestamp which does not overflow. + * The primary implementation counts the number of CPU cycles from MCU reset. + */ +uint64_t getTimeNowUs(void); + +uint64_t getHalTimer(void); + +/** + * @brief Returns the number of milliseconds since the board initialization. + */ +efitimems_t currentTimeMillis(void); + +/** + * @brief Current system time in seconds. + */ +int getTimeNowSeconds(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* EFITIME_H_ */ diff --git a/engine_math.cpp b/engine_math.cpp new file mode 100644 index 0000000000..9e8ef9767e --- /dev/null +++ b/engine_math.cpp @@ -0,0 +1,340 @@ +/** + * @file engine_math.cpp + * @brief + * + * @date Jul 13, 2013 + * @author Andrey Belomutskiy, (c) 2012-2014 + * + * This file is part of rusEfi - see http://rusefi.com + * + * rusEfi is free software; you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "engine_math.h" +#include "main.h" +#include "engine_configuration.h" +#include "interpolation.h" +#include "allsensors.h" +#include "io_pins.h" +#include "OutputSignalList.h" +#include "trigger_decoder.h" + +/* + * default Volumetric Efficiency + */ +//float getDefaultVE(int rpm) { +// if (rpm > 5000) +// return interpolate(5000, 1.1, 8000, 1, rpm); +// return interpolate(500, 0.5, 5000, 1.1, rpm); +//} +//#define K_AT_MIN_RPM_MIN_TPS 0.25 +//#define K_AT_MIN_RPM_MAX_TPS 0.25 +//#define K_AT_MAX_RPM_MIN_TPS 0.25 +//#define K_AT_MAX_RPM_MAX_TPS 0.9 +// +//#define rpmMin 500 +//#define rpmMax 8000 +// +//#define tpMin 0 +//#define tpMax 100 +// +// http://rusefi.com/math/t_charge.html +// / +//float getTCharge(int rpm, int tps, float coolantTemp, float airTemp) { +// float minRpmKcurrentTPS = interpolate(tpMin, K_AT_MIN_RPM_MIN_TPS, tpMax, +// K_AT_MIN_RPM_MAX_TPS, tps); +// float maxRpmKcurrentTPS = interpolate(tpMin, K_AT_MAX_RPM_MIN_TPS, tpMax, +// K_AT_MAX_RPM_MAX_TPS, tps); +// +// float Tcharge_coff = interpolate(rpmMin, minRpmKcurrentTPS, rpmMax, +// maxRpmKcurrentTPS, rpm); +// +// float Tcharge = coolantTemp * (1 - Tcharge_coff) + airTemp * Tcharge_coff; +// +// return Tcharge; +//} +#define MAX_STARTING_FUEL 15 +#define MIN_STARTING_FUEL 8 + +/** + * @return time needed to rotate crankshaft by one degree, in milliseconds. + */ +float getOneDegreeTimeMs(int rpm) { + return 1000.0 * 60 / 360 / rpm; +} + +/** + * @return number of milliseconds in one crankshaft revolution + */ +float getCrankshaftRevolutionTimeMs(int rpm) { + return 360 * getOneDegreeTimeMs(rpm); +} + +/** + * @brief Shifts angle into the [0..720) range + * TODO: should be 'crankAngleRange' range? + */ +float fixAngle(float angle) { + // I guess this implementation would be faster than 'angle % 720' + while (angle < 0) + angle += 720; + while (angle > 720) + angle -= 720; + return angle; +} + +/** + * @brief Returns engine load according to selected engine_load_mode + * + */ +float getEngineLoadT(engine_configuration_s *engineConfiguration) { + switch (engineConfiguration->engineLoadMode) { + case LM_MAF: + return getMaf(); + case LM_MAP: + return getMap(); + case LM_TPS: + return getTPS(); + case LM_SPEED_DENSITY: + // TODO: real implementation + return getMap(); + default: + firmwareError("Unexpected engine load parameter: %d", engineConfiguration->engineLoadMode); + return -1; + } +} + +void setSingleCoilDwell(engine_configuration_s *engineConfiguration) { + for (int i = 0; i < DWELL_CURVE_SIZE; i++) { + engineConfiguration->sparkDwellBins[i] = 0; + engineConfiguration->sparkDwell[i] = -1; + } + + engineConfiguration->sparkDwellBins[5] = 1; + engineConfiguration->sparkDwell[5] = 4; + + engineConfiguration->sparkDwellBins[6] = 4500; + engineConfiguration->sparkDwell[6] = 4; + + engineConfiguration->sparkDwellBins[7] = 12500; + engineConfiguration->sparkDwell[7] = 0; +} + +int isCrankingRT(engine_configuration_s *engineConfiguration, int rpm) { + return rpm > 0 && rpm < engineConfiguration->crankingSettings.crankingRpm; +} + +OutputSignalList ignitionSignals; +OutputSignalList injectonSignals; + +void initializeIgnitionActions(float baseAngle, engine_configuration_s *engineConfiguration, + engine_configuration2_s *engineConfiguration2) { + chDbgCheck(engineConfiguration->cylindersCount > 0, "cylindersCount"); + ignitionSignals.clear(); + + EventHandlerConfiguration *config = &engineConfiguration2->engineEventConfiguration; + resetEventList(&config->ignitionEvents); + + switch (engineConfiguration->ignitionMode) { + case IM_ONE_COIL: + for (int i = 0; i < engineConfiguration->cylindersCount; i++) { + // todo: extract method + float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount; + + registerActuatorEventExt(engineConfiguration, &engineConfiguration2->triggerShape, &config->ignitionEvents, + ignitionSignals.add(SPARKOUT_1_OUTPUT), angle); + } + break; + case IM_WASTED_SPARK: + for (int i = 0; i < engineConfiguration->cylindersCount; i++) { + float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount; + + int wastedIndex = i % (engineConfiguration->cylindersCount / 2); + + int id = (getCylinderId(engineConfiguration->firingOrder, wastedIndex) - 1); + io_pin_e ioPin = (io_pin_e) (SPARKOUT_1_OUTPUT + id); + + registerActuatorEventExt(engineConfiguration, &engineConfiguration2->triggerShape, &config->ignitionEvents, + ignitionSignals.add(ioPin), angle); + + } + + break; + case IM_INDIVIDUAL_COILS: + for (int i = 0; i < engineConfiguration->cylindersCount; i++) { + float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount; + + io_pin_e pin = (io_pin_e) ((int) SPARKOUT_1_OUTPUT + getCylinderId(engineConfiguration->firingOrder, i) - 1); + registerActuatorEventExt(engineConfiguration, &engineConfiguration2->triggerShape, &config->ignitionEvents, + ignitionSignals.add(pin), angle); + } + break; + + default: + firmwareError("unsupported ignitionMode %d in initializeIgnitionActions()", engineConfiguration->ignitionMode); + } +} + +void addFuelEvents(engine_configuration_s const *e, engine_configuration2_s *engineConfiguration2, + ActuatorEventList *list, injection_mode_e mode) { + resetEventList(list); + + trigger_shape_s *s = &engineConfiguration2->triggerShape; + + float baseAngle = e->globalTriggerAngleOffset + e->injectionOffset; + + switch (mode) { + case IM_SEQUENTIAL: + for (int i = 0; i < e->cylindersCount; i++) { + io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + getCylinderId(e->firingOrder, i) - 1); + float angle = baseAngle + i * 720.0 / e->cylindersCount; + registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle); + } + break; + case IM_SIMULTANEOUS: + for (int i = 0; i < e->cylindersCount; i++) { + float angle = baseAngle + i * 720.0 / e->cylindersCount; + + for (int j = 0; j < e->cylindersCount; j++) { + io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + j); + registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle); + } + } + break; + case IM_BATCH: + for (int i = 0; i < e->cylindersCount; i++) { + io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + (i % 2)); + float angle = baseAngle + i * 720.0 / e->cylindersCount; + registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle); + } + break; + default: + firmwareError("Unexpected injection mode %d", mode); + } +} + +/** + * @return Spark dwell time, in milliseconds. + */ +float getSparkDwellMsT(engine_configuration_s *engineConfiguration, int rpm) { + if (isCrankingR(rpm)) { + // technically this could be implemented via interpolate2d + float angle = engineConfiguration->crankingChargeAngle; + return getOneDegreeTimeMs(rpm) * angle; + } + + if (rpm > engineConfiguration->rpmHardLimit) { + // technically this could be implemented via interpolate2d by setting everything above rpmHardLimit to zero + warning(OBD_PCM_Processor_Fault, "skipping spark due to rpm=%d", rpm); + return 0; + } + + return interpolate2d(rpm, engineConfiguration->sparkDwellBins, engineConfiguration->sparkDwell, DWELL_CURVE_SIZE); +} + +void registerActuatorEventExt(engine_configuration_s const *engineConfiguration, trigger_shape_s * s, + ActuatorEventList *list, OutputSignal *actuator, float angleOffset) { + chDbgCheck(s->size > 0, "uninitialized trigger_shape_s"); + + angleOffset = fixAngle(angleOffset + engineConfiguration->globalTriggerAngleOffset); + + int triggerIndexOfZeroEvent = s->triggerShapeSynchPointIndex; + + // todo: migrate to crankAngleRange? + float firstAngle = s->wave.switchTimes[triggerIndexOfZeroEvent] * 720; + + // let's find the last trigger angle which is less or equal to the desired angle + int i; + for (i = 0; i < s->size - 1; i++) { + // todo: we need binary search here + float angle = fixAngle(s->wave.switchTimes[(triggerIndexOfZeroEvent + i + 1) % s->size] * 720 - firstAngle); + if (angle > angleOffset) + break; + } + // explicit check for zero to avoid issues where logical zero is not exactly zero due to float nature + float angle = + i == 0 ? 0 : fixAngle(s->wave.switchTimes[(triggerIndexOfZeroEvent + i) % s->size] * 720 - firstAngle); + + chDbgCheck(angleOffset >= angle, "angle constraint violation in registerActuatorEventExt()"); + + registerActuatorEvent(list, i, actuator, angleOffset - angle); +} + +//float getTriggerEventAngle(int triggerEventIndex) { +// return 0; +//} + +/** + * there is some BS related to isnan in MinGW, so let's have all the issues in one place + */ +int cisnan(float f) { + return *(((int*) (&f))) == 0x7FC00000; +} + +static int order_1_THEN_3_THEN_4_THEN2[] = { 1, 3, 4, 2 }; + +static int order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[] = { 1, 5, 3, 6, 2, 4 }; + +/** + * @param index from zero to cylindersCount - 1 + * @return cylinderId from one to cylindersCount + */ +int getCylinderId(firing_order_e firingOrder, int index) { + + switch (firingOrder) { + case FO_ONE_CYLINDER: + return 1; + case FO_1_THEN_3_THEN_4_THEN2: + return order_1_THEN_3_THEN_4_THEN2[index]; + case FO_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4: + return order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[index]; + + default: + firmwareError("getCylinderId not supported for %d", firingOrder); + } + return -1; +} + +void prepareOutputSignals(engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2) { + + // todo: move this reset into decoder + engineConfiguration2->triggerShape.triggerShapeSynchPointIndex = findTriggerZeroEventIndex( + &engineConfiguration2->triggerShape, &engineConfiguration->triggerConfig); + + injectonSignals.clear(); + EventHandlerConfiguration *config = &engineConfiguration2->engineEventConfiguration; + addFuelEvents(engineConfiguration, engineConfiguration2, &config->crankingInjectionEvents, + engineConfiguration->crankingInjectionMode); + addFuelEvents(engineConfiguration, engineConfiguration2, &config->injectionEvents, + engineConfiguration->injectionMode); +} + +void setTableBin(float array[], int size, float l, float r) { + for (int i = 0; i < size; i++) + array[i] = interpolate(0, l, size - 1, r, i); +} + +void setFuelRpmBin(engine_configuration_s *engineConfiguration, float l, float r) { + setTableBin(engineConfiguration->fuelRpmBins, FUEL_RPM_COUNT, l, r); +} + +void setFuelLoadBin(engine_configuration_s *engineConfiguration, float l, float r) { + setTableBin(engineConfiguration->fuelLoadBins, FUEL_LOAD_COUNT, l, r); +} + +void setTimingRpmBin(engine_configuration_s *engineConfiguration, float l, float r) { + setTableBin(engineConfiguration->ignitionRpmBins, IGN_RPM_COUNT, l, r); +} + +void setTimingLoadBin(engine_configuration_s *engineConfiguration, float l, float r) { + setTableBin(engineConfiguration->ignitionLoadBins, IGN_LOAD_COUNT, l, r); +} diff --git a/engine_math.h b/engine_math.h new file mode 100644 index 0000000000..dfc886270a --- /dev/null +++ b/engine_math.h @@ -0,0 +1,62 @@ +/** + * @file engine_math.h + * + * @date Jul 13, 2013 + * @author Andrey Belomutskiy, (c) 2012-2014 + */ + +#ifndef ENGINE_MATH_H_ +#define ENGINE_MATH_H_ + +#include "engine_configuration.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +int cisnan(float f); + +//float getDefaultVE(int rpm); + +float getDefaultFuel(int rpm, float map); +//float getTCharge(int rpm, int tps, float coolantTemp, float airTemp); + +float getOneDegreeTimeMs(int rpm); +float getCrankshaftRevolutionTimeMs(int rpm); + +int isCrankingRT(engine_configuration_s *engineConfiguration, int rpm); +#define isCrankingR(rpm) isCrankingRT(engineConfiguration, rpm) + +float fixAngle(float angle); +float getTriggerEventAngle(int triggerEventIndex); + +float getEngineLoadT(engine_configuration_s *engineConfiguration); +#define getEngineLoad() getEngineLoadT(engineConfiguration) + +void initializeIgnitionActions(float baseAngle, engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2); +void addFuelEvents(engine_configuration_s const *e, engine_configuration2_s *engineConfiguration2, ActuatorEventList *list, injection_mode_e mode); + +float getSparkDwellMsT(engine_configuration_s *engineConfiguration, int rpm); +#define getSparkDwellMs(rpm) getSparkDwellMsT(engineConfiguration, rpm) + +void registerActuatorEventExt(engine_configuration_s const *engineConfiguration, trigger_shape_s * s, ActuatorEventList *list, OutputSignal *actuator, float angleOffset); + +int getCylinderId(firing_order_e firingOrder, int index); +void prepareOutputSignals(engine_configuration_s *engineConfiguration, + engine_configuration2_s *engineConfiguration2); + +void setTableBin(float array[], int size, float l, float r); +void setFuelRpmBin(engine_configuration_s *engineConfiguration, float l, float r); +void setFuelLoadBin(engine_configuration_s *engineConfiguration, float l, float r); +void setTimingRpmBin(engine_configuration_s *engineConfiguration, float l, float r); +void setTimingLoadBin(engine_configuration_s *engineConfiguration, float l, float r); + +void setSingleCoilDwell(engine_configuration_s *engineConfiguration); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ENGINE_MATH_H_ */ diff --git a/firmware/controllers/algo/fuel_math.c b/firmware/controllers/algo/fuel_math.c deleted file mode 100644 index 1ffd4f1711..0000000000 --- a/firmware/controllers/algo/fuel_math.c +++ /dev/null @@ -1,155 +0,0 @@ -/** - * @file fuel_math.c - * @brief Fuel amount calculation logic - * - * While engine running, fuel amount is an interpolated value from the fuel map by getRpm() and getEngineLoad() - * On top of the value from the fuel map we also apply - *
1) getInjectorLag() correction to account for fuel injector lag - *
2) getCltCorrection() for warm-up - *
3) getIatCorrection() to account for cold weather - * - * getCrankingFuel() depents only on getCoolantTemperature() - * - * - * @date May 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 "fuel_math.h" -#include "interpolation.h" -#include "engine_configuration.h" -#include "allsensors.h" -#include "engine_math.h" - -static float *fuel_ptrs[FUEL_LOAD_COUNT]; -static int initialized = FALSE; -extern engine_configuration_s *engineConfiguration; - -/** - * @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; -} - -/** - * @brief Engine warm-up fuel correction. - */ -float getCltCorrection(float clt) { - if (cisnan(clt)) - return 1; // this error should be already reported somewhere else, let's just handle it - return interpolate2d(clt, engineConfiguration->cltFuelCorrBins, engineConfiguration->cltFuelCorr, CLT_CURVE_SIZE); -} - -float getIatCorrection(float iat) { - if (cisnan(iat)) - return 1; // this error should be already reported somewhere else, let's just handle it - return interpolate2d(iat, engineConfiguration->iatFuelCorrBins, engineConfiguration->iatFuelCorr, IAT_CURVE_SIZE); -} - -/** - * @brief Injector lag correction - * @param vBatt Battery voltage. - * @return Time in ms for injection opening time based on current battery voltage - */ -float getInjectorLag(float vBatt) { - if (cisnan(vBatt)) { - warning("vBatt=%f", vBatt); - return 0; - } - float vBattCorrection = interpolate2d(vBatt, engineConfiguration->battInjectorLagCorrBins, - engineConfiguration->battInjectorLagCorr, VBAT_INJECTOR_CURVE_SIZE); - return engineConfiguration->injectorLag + vBattCorrection; -} - -float getBaseFuel(int rpm, float engineLoad) { - chDbgCheck(initialized, "fuel map initialized"); - return interpolate3d(engineLoad, engineConfiguration->fuelLoadBins, FUEL_LOAD_COUNT, rpm, engineConfiguration->fuelRpmBins, - FUEL_RPM_COUNT, fuel_ptrs); -} - -float getCrankingFuel(void) { - return getStartingFuel(getCoolantTemperature()); -} - -int isCranking(void); - -/** - * @returns Length of fuel injection, in milliseconds - */ -float getFuelMs(int rpm) { - if (isCranking()) { - return getCrankingFuel(); - } else { - float fuel = getRunningFuel(rpm, getEngineLoad()); - return fuel; - } -} - -float getRunningFuel(int rpm, float engineLoad) { - float baseFuel = getBaseFuel(rpm, engineLoad); - - float iatCorrection = getIatCorrection(getIntakeAirTemperature()); - float cltCorrection = getCltCorrection(getCoolantTemperature()); - float injectorLag = getInjectorLag(getVBatt()); - - return baseFuel * cltCorrection * iatCorrection + injectorLag; -} - -float getStartingFuel(float coolantTemperature) { - // these magic constants are in Celsius - if (cisnan(coolantTemperature) - || coolantTemperature - < engineConfiguration->crankingSettings.coolantTempMinC) - return engineConfiguration->crankingSettings.fuelAtMinTempMs; - if (coolantTemperature - > engineConfiguration->crankingSettings.coolantTempMaxC) - return engineConfiguration->crankingSettings.fuelAtMaxTempMs; - return interpolate(engineConfiguration->crankingSettings.coolantTempMinC, - engineConfiguration->crankingSettings.fuelAtMinTempMs, - engineConfiguration->crankingSettings.coolantTempMaxC, - engineConfiguration->crankingSettings.fuelAtMaxTempMs, - coolantTemperature); -} - -/** - * @return 0 for OM_DEFAULT and OM_OPENDRAIN - */ - -inline static int getElectricalValue0(pin_output_mode_e mode) { - return mode == OM_INVERTED || mode == OM_OPENDRAIN_INVERTED; -} - -/** - * @return 1 for OM_DEFAULT and OM_OPENDRAIN - */ -inline static int getElectricalValue1(pin_output_mode_e mode) { - return mode == OM_DEFAULT || mode == OM_OPENDRAIN; -} - -// 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"); - - return logicalValue ? getElectricalValue1(mode) : getElectricalValue0(mode); -} - diff --git a/firmware/controllers/math/engine_math.c b/firmware/controllers/math/engine_math.c deleted file mode 100644 index 87f838f699..0000000000 --- a/firmware/controllers/math/engine_math.c +++ /dev/null @@ -1,231 +0,0 @@ -/** - * @file engine_math.c - * @brief - * - * @date Jul 13, 2013 - * @author Andrey Belomutskiy, (c) 2012-2014 - * - * This file is part of rusEfi - see http://rusefi.com - * - * rusEfi is free software; you can redistribute it and/or modify it under the terms of - * the GNU General Public License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see . - */ - -//#include - -#include "engine_math.h" -#include "main.h" -#include "engine_configuration.h" -#include "interpolation.h" -#include "allsensors.h" -#include "io_pins.h" - -/* - * default Volumetric Efficiency - */ -//float getDefaultVE(int rpm) { -// if (rpm > 5000) -// return interpolate(5000, 1.1, 8000, 1, rpm); -// return interpolate(500, 0.5, 5000, 1.1, rpm); -//} -//#define K_AT_MIN_RPM_MIN_TPS 0.25 -//#define K_AT_MIN_RPM_MAX_TPS 0.25 -//#define K_AT_MAX_RPM_MIN_TPS 0.25 -//#define K_AT_MAX_RPM_MAX_TPS 0.9 -// -//#define rpmMin 500 -//#define rpmMax 8000 -// -//#define tpMin 0 -//#define tpMax 100 -// -// http://rusefi.com/math/t_charge.html -// / -//float getTCharge(int rpm, int tps, float coolantTemp, float airTemp) { -// float minRpmKcurrentTPS = interpolate(tpMin, K_AT_MIN_RPM_MIN_TPS, tpMax, -// K_AT_MIN_RPM_MAX_TPS, tps); -// float maxRpmKcurrentTPS = interpolate(tpMin, K_AT_MAX_RPM_MIN_TPS, tpMax, -// K_AT_MAX_RPM_MAX_TPS, tps); -// -// float Tcharge_coff = interpolate(rpmMin, minRpmKcurrentTPS, rpmMax, -// maxRpmKcurrentTPS, rpm); -// -// float Tcharge = coolantTemp * (1 - Tcharge_coff) + airTemp * Tcharge_coff; -// -// return Tcharge; -//} -#define MAX_STARTING_FUEL 15 -#define MIN_STARTING_FUEL 8 - -/** - * @return time needed to rotate crankshaft by one degree, in milliseconds. - */ -float getOneDegreeTimeMs(int rpm) { - return 1000.0 * 60 / 360 / rpm; -} - -/** - * @return time needed to rotate crankshaft by one degree, in systicks - */ -float getOneDegreeTime(int rpm) { - return getOneDegreeTimeMs(rpm) * TICKS_IN_MS; -} - -/** - * @return number of system it needed for one crankshaft revolution - */ -float getCrankshaftRevolutionTime(int rpm) { - return 360 * getOneDegreeTime(rpm); -} - -/** - * @brief Shifts angle into the [0..720) range - * TODO: should be 'crankAngleRange' range? - */ -float fixAngle(float angle) { - // I guess this implementation would be faster than 'angle % 720' - while (angle < 0) - angle += 720; - while (angle > 720) - angle -= 720; - return angle; -} - -/** - * @brief Returns engine load according to selected engine_load_mode - * - */ -float getEngineLoadT(engine_configuration_s *engineConfiguration) { - switch (engineConfiguration->engineLoadMode) { - case LM_MAF: - return getMaf(); - case LM_MAP: - return getMap(); - case LM_TPS: - return getTPS(); - case LM_SPEED_DENSITY: - // TODO: real implementation - return getMap(); - default: - fatal("Unexpected engine load parameter"); - return -1; - } -} - -int isCrankingRT(engine_configuration_s *engineConfiguration, int rpm) { - return rpm > 0 && rpm < engineConfiguration->crankingSettings.crankingRpm; -} - -void initializeIgnitionActions(engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2) { - EventHandlerConfiguration *config = &engineConfiguration2->engineEventConfiguration; - resetEventList(&config->ignitionEvents); - chDbgCheck(engineConfiguration->cylindersCount > 0, "cylindersCount"); - - int x = 13; //todo - - if (engineConfiguration->ignitionMode == IM_ONE_COIL) { - - for (int i = 0; i < engineConfiguration->cylindersCount; i++) { - float angle = x + 720.0 * i / engineConfiguration->cylindersCount; - - registerActuatorEventExt(engineConfiguration, - &engineConfiguration2->triggerShape, - &config->ignitionEvents, addOutputSignal(SPARKOUT_1_OUTPUT), angle); - } - - } else - fatal("unfinished initializeIgnitionActions"); - -} - -void addFuelEvents(engine_configuration_s *e, trigger_shape_s * s, EventHandlerConfiguration *config) { - resetEventList(&config->injectionEvents); - - 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 = e->injectionOffset + i * 720.0 / e->cylindersCount; - registerActuatorEventExt(e, s, &config->injectionEvents, addOutputSignal(pin), angle); - } -} - - -/** - * @return Spark dwell time, in milliseconds. - */ -float getSparkDwellMsT(engine_configuration_s *engineConfiguration, int rpm) { - if (isCrankingR(rpm)) { - // technically this could be implemented via interpolate2d - float angle = engineConfiguration->crankingChargeAngle; - return getOneDegreeTimeMs(rpm) * angle; - } - - if (rpm > engineConfiguration->rpmHardLimit) { - // technically this could be implemented via interpolate2d by setting everything above rpmHardLimit to zero - warning("skipping spark due to rpm=%d", rpm); - return 0; - } - - return interpolate2d(rpm, engineConfiguration->sparkDwellBins, engineConfiguration->sparkDwell, DWELL_CURVE_SIZE); -} - -void registerActuatorEventExt(engine_configuration_s *engineConfiguration, trigger_shape_s * s, ActuatorEventList *list, OutputSignal *actuator, float angleOffset) { - chDbgCheck(s->size > 0, "uninitialized trigger_shape_s"); - - angleOffset = fixAngle(angleOffset + engineConfiguration->globalTriggerAngleOffset); - - // todo: migrate to crankAngleRange? - float firstAngle = s->wave.switchTimes[0] * 720; - - // let's find the last trigger angle which is less or equal to the desired angle - int i; - for (i = 0; i < s->size - 1; i++) { - // todo: we need binary search here - float angle = s->wave.switchTimes[i + 1] * 720 - firstAngle; - if (angle > angleOffset) - break; - } - float angle = s->wave.switchTimes[i] * 720 - firstAngle; - - registerActuatorEvent(list, i, actuator, angleOffset - angle); -} - - -//float getTriggerEventAngle(int triggerEventIndex) { -// return 0; -//} - -/** - * there is some BS related to isnan in MinGW, so let's have all the issues in one place - */ -int cisnan(float f) { - return *(((int*)(&f))) == 0x7FC00000; -} - -static int order_1_THEN_3_THEN_4_THEN2[] = {1, 3, 4, 2}; - -/** - * @param index from zero to cylindersCount - 1 - * @return cylinderId from one to cylindersCount - */ -int getCylinderId(firing_order_e firingOrder, int index) { - - switch(firingOrder) { - case FO_ONE_CYLINDER: - return 1; - case FO_1_THEN_3_THEN_4_THEN2: - return order_1_THEN_3_THEN_4_THEN2[index]; - - default: - firmwareError("getCylinderId not supported for %d", firingOrder); - } - return -1; -} - diff --git a/firmware/hw_layer/lcd/lcd_2x16.c b/firmware/hw_layer/lcd/lcd_2x16.c deleted file mode 100644 index 319511985b..0000000000 --- a/firmware/hw_layer/lcd/lcd_2x16.c +++ /dev/null @@ -1,212 +0,0 @@ -/** - * @file lcd_2x16.c - * @brief HD44780 character display driver - * - * - * http://forum.chibios.org/phpbb/viewtopic.php?f=16&t=1584 - * @date 13.12.2013 - * @author shilow - */ - -#include "main.h" - -#include "lcd_2x16.h" -#include "pin_repository.h" -#include "string.h" - -#include "engine_configuration.h" - -extern engine_configuration_s *engineConfiguration; - -static Logging logger; - -enum { - LCD_2X16_RESET = 0x30, LCD_2X16_4_BIT_BUS = 0x20, -// LCD_2X16_8_BIT_BUS = 0x30, -// LCD_2X16_LINE_ONE = 0x20, -// LCD_2X16_LINES_TWO = 0x28, -// LCD_2X16_FONT_5X8 = 0x20, -// LCD_2X16_FONT_5X10 = 0x24, -// LCD_2X16_DISPLAY_CLEAR = 0x01, -// LCD_2X16_DISPLAY_HOME = 0x02, -// LCD_2X16_DISPLAY_ON = 0x0C, -// LCD_2X16_DISPLAY_RIGHT = 0x1C, -// LCD_2X16_DISPLAY_LEFT = 0x18, -// LCD_2X16_DISPLAY_SHIFT = 0x05, -// LCD_2X16_CURSOR_ON = 0x0A, -// LCD_2X16_CURSOR_BLINK = 0x09, -// LCD_2X16_CURSOR_RIGHT = 0x14, -// LCD_2X16_CURSOR_LEFT = 0x10, -// LCD_2X16_SHIFT_RIGHT = 0x06, -// LCD_2X16_SHIFT_LEFT = 0x04, -// LCD_2X16_CGRAM_ADDR = 0x40, - LCD_2X16_DDRAM_ADDR = 0x80, -// LCD_2X16_BUSY_FLAG = 0x80, -// LCD_2X16_COMMAND = 0x01, -// LCD_2X16_DATA = 0x00, -} lcd_2x16_command; - -// http://web.alfredstate.edu/weimandn/lcd/lcd_addressing/lcd_addressing_index.html -static const int lineStart[] = { 0, 0x40, 0x14, 0x54 }; - -static int BUSY_WAIT_DELAY = FALSE; -static int currentRow = 0; - -static void lcdSleep(int period) { - if (BUSY_WAIT_DELAY) { - // this mode is useful for displaying messages to report OS fatal issues - - int ticks = 168000000 / 1000000 * period; - int a = 0; - for (int i = 0; i < ticks; i++) - a += i; - // the purpose of this code is to fool the compiler so that the loop is not optimized away - chDbgCheck(a != 0, "true"); - - } else { - chThdSleepMicroseconds(period); - } -} - -static char txbuf[1]; -#define LCD_PORT_EXP_ADDR 0x20 - -//----------------------------------------------------------------------------- -static void lcd_HD44780_write(uint8_t data) { - if (engineConfiguration->displayMode == DM_HD44780) { - palWritePad(HD44780_PORT_DB7, HD44780_PIN_DB7, data & 0x80 ? 1 : 0); - palWritePad(HD44780_PORT_DB6, HD44780_PIN_DB6, data & 0x40 ? 1 : 0); - palWritePad(HD44780_PORT_DB5, HD44780_PIN_DB5, data & 0x20 ? 1 : 0); - palWritePad(HD44780_PORT_DB4, HD44780_PIN_DB4, data & 0x10 ? 1 : 0); - - palSetPad(HD44780_PORT_E, HD44780_PIN_E); // En high - lcdSleep(10); // enable pulse must be >450ns - palClearPad(HD44780_PORT_E, HD44780_PIN_E); // En low - lcdSleep(40); // commands need > 37us to settle - } else { - - // LCD D4_pin -> P4 - // LCD D5_pin -> P5 - // LCD D6_pin -> P6 - // LCD D7_pin -> P7 - // LCD Pin RS -> P0 - // LCD Pin RW -> P1 - // LCD Pin E -> P2 - - i2cAcquireBus(&I2CD1); - - txbuf[0] = 4; - i2cMasterTransmit(&I2CD1, LCD_PORT_EXP_ADDR, txbuf, 1, NULL, 0); - lcdSleep(10); // enable pulse must be >450ns - - txbuf[0] = 0; - i2cMasterTransmit(&I2CD1, LCD_PORT_EXP_ADDR, txbuf, 1, NULL, 0); - - i2cReleaseBus(&I2CD1); - - } -} - -//----------------------------------------------------------------------------- -void lcd_2x16_write_command(uint8_t data) { - palClearPad(HD44780_PORT_RS, HD44780_PIN_RS); - - lcd_HD44780_write(data); - lcd_HD44780_write(data << 4); -} - -//----------------------------------------------------------------------------- -void lcd_2x16_write_data(uint8_t data) { - palSetPad(HD44780_PORT_RS, HD44780_PIN_RS); - - lcd_HD44780_write(data); - lcd_HD44780_write(data << 4); - - palClearPad(HD44780_PORT_RS, HD44780_PIN_RS); -} - -//----------------------------------------------------------------------------- -void lcd_HD44780_set_position(uint8_t row, uint8_t column) { - chDbgCheck(row <= engineConfiguration->HD44780height, "invalid row"); - currentRow = row; - lcd_2x16_write_command(LCD_2X16_DDRAM_ADDR + lineStart[row] + column); -} - -void lcd_HD44780_print_char(char data) { - if (data == '\n') { - lcd_HD44780_set_position(++currentRow, 0); - } else { - lcd_2x16_write_data(data); - } -} - -void lcd_HD44780_print_string(char* string) { - while (*string != 0x00) - lcd_HD44780_print_char(*string++); -} - -static void lcdInfo(void) { - scheduleMsg(&logger, "HD44780 RS=%s%d E=%s%d", portname(HD44780_PORT_RS), HD44780_PIN_RS, portname(HD44780_PORT_E), HD44780_PIN_E); - scheduleMsg(&logger, "HD44780 D4=%s%d D5=%s%d", portname(HD44780_PORT_DB4), HD44780_PIN_DB4, portname(HD44780_PORT_DB5), HD44780_PIN_DB5); - scheduleMsg(&logger, "HD44780 D6=%s%d D7=%s%d", portname(HD44780_PORT_DB6), HD44780_PIN_DB6, portname(HD44780_PORT_DB7), HD44780_PIN_DB7); -} - -void lcd_HD44780_init(void) { - initLogging(&logger, "HD44780 driver"); - - addConsoleAction("lcdinfo", lcdInfo); - - - if (engineConfiguration->displayMode == DM_HD44780) { - mySetPadMode("lcd RS", HD44780_PORT_RS, HD44780_PIN_RS, PAL_MODE_OUTPUT_PUSHPULL); - mySetPadMode("lcd E", HD44780_PORT_E, HD44780_PIN_E, PAL_MODE_OUTPUT_PUSHPULL); - mySetPadMode("lcd DB4", HD44780_PORT_DB4, HD44780_PIN_DB4, PAL_MODE_OUTPUT_PUSHPULL); - mySetPadMode("lcd DB6", HD44780_PORT_DB5, HD44780_PIN_DB5, PAL_MODE_OUTPUT_PUSHPULL); - mySetPadMode("lcd DB7", HD44780_PORT_DB6, HD44780_PIN_DB6, PAL_MODE_OUTPUT_PUSHPULL); - mySetPadMode("lcd DB8", HD44780_PORT_DB7, HD44780_PIN_DB7, PAL_MODE_OUTPUT_PUSHPULL); - - palWritePad(HD44780_PORT_RS, HD44780_PIN_RS, 0); - palWritePad(HD44780_PORT_E, HD44780_PIN_E, 0); - palWritePad(HD44780_PORT_DB4, HD44780_PIN_DB4, 0); - palWritePad(HD44780_PORT_DB5, HD44780_PIN_DB5, 0); - palWritePad(HD44780_PORT_DB6, HD44780_PIN_DB6, 0); - palWritePad(HD44780_PORT_DB7, HD44780_PIN_DB7, 0); - } - - // LCD needs some time to wake up - chThdSleepMilliseconds(50); - - lcd_HD44780_write(LCD_2X16_RESET); - chThdSleepMilliseconds(1); - - lcd_HD44780_write(0x30); - - lcd_HD44780_write(LCD_2X16_4_BIT_BUS); // 4 bit, 2 line - chThdSleepMicroseconds(40); - - lcd_HD44780_write(LCD_2X16_4_BIT_BUS); // 4 bit, 2 line - lcd_HD44780_write(0x80); - chThdSleepMicroseconds(40); - - lcd_HD44780_write(0x00); // display and cursor control - lcd_HD44780_write(0xC0); - chThdSleepMicroseconds(40); - - lcd_HD44780_write(0x00); // display clear - lcd_HD44780_write(0x01); - chThdSleepMilliseconds(2); - - lcd_HD44780_write(0x00); // entry mode set - lcd_HD44780_write(0x60); - - lcd_HD44780_set_position(0, 0); - lcd_HD44780_print_string("rusefi here\n"); - lcd_HD44780_print_string(__DATE__); -} - -void lcdShowFatalMessage(char *message) { - BUSY_WAIT_DELAY = TRUE; - lcd_HD44780_set_position(0, 0); - lcd_HD44780_print_string("fatal\n"); - lcd_HD44780_print_string(message); -} diff --git a/firmware/hw_layer/lcd/lcd_2x16.h b/firmware/hw_layer/lcd/lcd_2x16.h deleted file mode 100644 index 6a49f621a2..0000000000 --- a/firmware/hw_layer/lcd/lcd_2x16.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * @file lcd_2x16.h - * @date 13.12.2013 - */ - -#ifndef LCD_2X16_H_ -#define LCD_2X16_H_ - -extern void lcd_HD44780_init(void); -extern void lcd_HD44780_set_position(uint8_t row, uint8_t column); -extern void lcd_HD44780_print_char(char data); -extern void lcd_HD44780_print_string(char *string); -extern const uint8_t lcd_2x16_decode[]; - -void lcdShowFatalMessage(char *message); - -#endif /* LCD_2X16_H_ */ diff --git a/wave_chart.c b/wave_chart.c new file mode 100644 index 0000000000..8dc9a69efc --- /dev/null +++ b/wave_chart.c @@ -0,0 +1,146 @@ +/** + * @file wave_chart.c + * @brief Dev console wave sniffer logic + * + * Here we have our own build-in logic analyzer. The data we aggregate here is sent to the + * java UI Dev Console so that it can be displayed nicely in the Sniffer tab. + * + * Both external events (see wave_analyzer.c) and internal (see signal executors) are supported + * + * @date Jun 23, 2013 + * @author Andrey Belomutskiy, (c) 2012-2014 + * + * This file is part of rusEfi - see http://rusefi.com + * + * rusEfi is free software; you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "wave_chart.h" +#include "main.h" + +#if EFI_WAVE_CHART + +#include "eficonsole.h" +#include "status_loop.h" + + +#define CHART_DELIMETER "!" + +/** + * This is the number of events in the digital chart which would be displayed + * on the 'digital sniffer' pane + */ +#if EFI_PROD_CODE +static volatile int chartSize = 100; +#else +// need more events for automated test +static volatile int chartSize = 200; +#endif + +static int isChartActive = TRUE; +//static int isChartActive = FALSE; + +//#define DEBUG_WAVE 1 + +#if DEBUG_WAVE +static Logging debugLogging; +#endif + +static Logging logger; + +void resetWaveChart(WaveChart *chart) { +#if DEBUG_WAVE + scheduleSimpleMsg(&debugLogging, "reset while at ", chart->counter); +#endif + resetLogging(&chart->logging); + chart->counter = 0; + appendPrintf(&chart->logging, "wave_chart%s", DELIMETER); +} + +static char LOGGING_BUFFER[5000] __attribute__((section(".ccm"))); + +static void printStatus(void) { + scheduleIntValue(&logger, "chart", isChartActive); + scheduleIntValue(&logger, "chartsize", chartSize); +} + +static void setChartActive(int value) { + isChartActive = value; + printStatus(); +} + +void setChartSize(int newSize) { + if (newSize < 5) + return; + chartSize = newSize; + printStatus(); +} + +void publishChartIfFull(WaveChart *chart) { + if (isWaveChartFull(chart)) { + publishChart(chart); + resetWaveChart(chart); + } +} + +int isWaveChartFull(WaveChart *chart) { + return chart->counter >= chartSize; +} + +void publishChart(WaveChart *chart) { + appendPrintf(&chart->logging, DELIMETER); +#if DEBUG_WAVE + Logging *l = &chart->logging; + scheduleSimpleMsg(&debugLogging, "IT'S TIME", strlen(l->buffer)); +#endif + if (isChartActive && getFullLog()) + scheduleLogging(&chart->logging); +} + +/** + * @brief Register a change in sniffed signal + */ +void addWaveChartEvent3(WaveChart *chart, char *name, char * msg, char * msg2) { + chDbgCheck(chart->isInitialized, "chart not initialized"); +#if DEBUG_WAVE + scheduleSimpleMsg(&debugLogging, "current", chart->counter); +#endif + if (isWaveChartFull(chart)) + return; + lockOutputBuffer(); // we have multiple threads writing to the same output buffer + appendPrintf(&chart->logging, "%s%s%s%s", name, CHART_DELIMETER, msg, CHART_DELIMETER); + int time100 = getTimeNowUs() / 10; + appendPrintf(&chart->logging, "%d%s%s", time100, msg2, CHART_DELIMETER); + chart->counter++; + unlockOutputBuffer(); +} + +void initWaveChart(WaveChart *chart) { + initLogging(&logger, "wave info"); + + if (!isChartActive) + printMsg(&logger, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! chart disabled"); + + printStatus(); + + initLoggingExt(&chart->logging, "wave chart", LOGGING_BUFFER, sizeof(LOGGING_BUFFER)); + chart->isInitialized = TRUE; +#if DEBUG_WAVE + initLoggingExt(&debugLogging, "wave chart debug", &debugLogging.DEFAULT_BUFFER, sizeof(debugLogging.DEFAULT_BUFFER)); +#endif + + resetWaveChart(chart); + addConsoleActionI("chartsize", setChartSize); + addConsoleActionI("chart", setChartActive); +} + +#endif /* EFI_WAVE_CHART */