This commit is contained in:
parent
237443fffb
commit
2ab26f8aaf
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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_*/
|
|
@ -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
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 */
|
|
@ -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;
|
||||
}
|
|
@ -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_ */
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "signal_executor.h"
|
||||
|
||||
#if EFI_WAVE_CHART
|
||||
#include "rpm_calculator.h"
|
||||
#endif
|
||||
|
||||
#if EFI_WAVE_ANALYZER
|
||||
|
||||
/**
|
||||
* Signal executors feed digital events right into WaveChart used by Sniffer tab of Dev Console
|
||||
*/
|
||||
#include "wave_analyzer.h"
|
||||
|
||||
#endif /* EFI_WAVE_ANALYZER */
|
||||
|
||||
#if EFI_PROD_CODE || EFI_SIMULATOR
|
||||
static Logging logger;
|
||||
#endif
|
||||
|
||||
void initSignalExecutor(void) {
|
||||
#if EFI_PROD_CODE || EFI_SIMULATOR
|
||||
initLogging(&logger, "s exec");
|
||||
#endif
|
||||
initSignalExecutorImpl();
|
||||
}
|
||||
|
||||
void initOutputSignalBase(OutputSignal *signal) {
|
||||
signal->status = IDLE;
|
||||
// signal->last_scheduling_time = 0;
|
||||
signal->initialized = TRUE;
|
||||
}
|
||||
|
||||
static void turnHigh(OutputSignal *signal) {
|
||||
#if EFI_DEFAILED_LOGGING
|
||||
// signal->hi_time = hTimeNow();
|
||||
#endif /* EFI_DEFAILED_LOGGING */
|
||||
io_pin_e pin = signal->io_pin;
|
||||
// turn the output level ACTIVE
|
||||
// todo: this XOR should go inside the setOutputPinValue method
|
||||
setOutputPinValue(pin, TRUE);
|
||||
// sleep for the needed duration
|
||||
|
||||
#if EFI_PROD_CODE || EFI_SIMULATOR
|
||||
if(
|
||||
pin == SPARKOUT_1_OUTPUT ||
|
||||
pin == SPARKOUT_3_OUTPUT) {
|
||||
// time_t now = hTimeNow();
|
||||
// float an = getCrankshaftAngle(now);
|
||||
// scheduleMsg(&logger, "spark up%d %d", pin, now);
|
||||
// scheduleMsg(&logger, "spark angle %d %f", (int)an, an);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if EFI_WAVE_CHART
|
||||
addWaveChartEvent(signal->name, "up", "");
|
||||
#endif /* EFI_WAVE_ANALYZER */
|
||||
}
|
||||
|
||||
static void turnLow(OutputSignal *signal) {
|
||||
// turn off the output
|
||||
// todo: this XOR should go inside the setOutputPinValue method
|
||||
setOutputPinValue(signal->io_pin, FALSE);
|
||||
|
||||
#if EFI_DEFAILED_LOGGING
|
||||
systime_t after = hTimeNow();
|
||||
debugInt(&signal->logging, "a_time", after - signal->hi_time);
|
||||
scheduleLogging(&signal->logging);
|
||||
#endif /* EFI_DEFAILED_LOGGING */
|
||||
|
||||
#if EFI_WAVE_CHART
|
||||
addWaveChartEvent(signal->name, "down", "");
|
||||
#endif /* EFI_WAVE_ANALYZER */
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param delay the number of ticks before the output signal
|
||||
* immediate output if delay is zero
|
||||
* @param dwell the number of ticks of output duration
|
||||
*
|
||||
*/
|
||||
|
||||
int getRevolutionCounter(void);
|
||||
|
||||
void scheduleOutput(OutputSignal *signal, float delayMs, float durationMs) {
|
||||
if (durationMs < 0) {
|
||||
firmwareError("duration cannot be negative: %d", durationMs);
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleOutputBase(signal, delayMs, durationMs);
|
||||
|
||||
int index = getRevolutionCounter() % 2;
|
||||
scheduling_s * sUp = &signal->signalTimerUp[index];
|
||||
scheduling_s * sDown = &signal->signalTimerDown[index];
|
||||
|
||||
scheduleTask(sUp, MS2US(delayMs), (schfunc_t) &turnHigh, (void *) signal);
|
||||
scheduleTask(sDown, MS2US(delayMs + durationMs), (schfunc_t) &turnLow, (void*)signal);
|
||||
|
||||
// signal->last_scheduling_time = now;
|
||||
}
|
||||
|
||||
void scheduleOutputBase(OutputSignal *signal, float delayMs, float durationMs) {
|
||||
/**
|
||||
* it's better to check for the exact 'TRUE' value since otherwise
|
||||
* we would accept any memory garbage
|
||||
*/
|
||||
chDbgCheck(signal->initialized == TRUE, "Signal not initialized");
|
||||
// signal->offset = offset;
|
||||
// signal->duration = duration;
|
||||
}
|
||||
|
||||
|
||||
char *getPinName(io_pin_e io_pin) {
|
||||
switch (io_pin) {
|
||||
case SPARKOUT_1_OUTPUT:
|
||||
return "Spark 1";
|
||||
case SPARKOUT_2_OUTPUT:
|
||||
return "Spark 2";
|
||||
case SPARKOUT_3_OUTPUT:
|
||||
return "Spark 3";
|
||||
case SPARKOUT_4_OUTPUT:
|
||||
return "Spark 4";
|
||||
|
||||
case INJECTOR_1_OUTPUT:
|
||||
return "Injector 1";
|
||||
case INJECTOR_2_OUTPUT:
|
||||
return "Injector 2";
|
||||
case INJECTOR_3_OUTPUT:
|
||||
return "Injector 3";
|
||||
case INJECTOR_4_OUTPUT:
|
||||
return "Injector 4";
|
||||
case INJECTOR_5_OUTPUT:
|
||||
return "Injector 5";
|
||||
default:
|
||||
return "No name";
|
||||
}
|
||||
}
|
|
@ -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_ */
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "signal_executor.h"
|
||||
#include "signal_executor_single_timer_algo.h"
|
||||
#include "main.h"
|
||||
#include "utlist.h"
|
||||
#include "io_pins.h"
|
||||
|
||||
#if EFI_WAVE_ANALYZER
|
||||
#include "wave_analyzer.h"
|
||||
#include "wave_chart.h"
|
||||
extern WaveChart waveChart;
|
||||
#endif
|
||||
|
||||
#if EFI_SIGNAL_EXECUTOR_SINGLE_TIMER
|
||||
/**
|
||||
* @brief Output list
|
||||
*
|
||||
* List of all active output signals
|
||||
* This is actually the head of the list.
|
||||
* When the list is empty (initial state) the head of the list should be NULL.
|
||||
* This is by design.
|
||||
*/
|
||||
OutputSignal *st_output_list = NULL;
|
||||
|
||||
inline void registerSignal(OutputSignal *signal) {
|
||||
LL_APPEND(st_output_list, signal);
|
||||
}
|
||||
|
||||
void setOutputPinValue(io_pin_e pin, int value);
|
||||
|
||||
/**
|
||||
* @return time of next event within for this signal
|
||||
* @todo Find better name.
|
||||
*/
|
||||
inline time_t toggleSignalIfNeeded(OutputSignal *out, time_t now) {
|
||||
// chDbgCheck(out!=NULL, "out is NULL");
|
||||
// chDbgCheck(out->io_pin < IO_PIN_COUNT, "pin assertion");
|
||||
time_t last = out->last_scheduling_time;
|
||||
//estimated = last + out->timing[out->status];
|
||||
time_t estimated = last + GET_DURATION(out);
|
||||
if (now >= estimated) {
|
||||
out->status ^= 1; /* toggle status */
|
||||
//setOutputPinValue(out->io_pin, out->status); /* Toggle output */
|
||||
palWritePad(GPIOE, 5, out->status);
|
||||
#if EFI_WAVE_ANALYZER
|
||||
// addWaveChartEvent(out->name, out->status ? "up" : "down", "");
|
||||
#endif /* EFI_WAVE_ANALYZER */
|
||||
|
||||
// out->last_scheduling_time = now; /* store last update */
|
||||
estimated = now + GET_DURATION(out); /* update estimation */
|
||||
}
|
||||
return estimated - now;
|
||||
}
|
||||
#endif /* EFI_SIGNAL_EXECUTOR_SINGLE_TIMER */
|
|
@ -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 <stdint.h>
|
||||
#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_ */
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "engine_math.h"
|
||||
#include "main.h"
|
||||
#include "engine_configuration.h"
|
||||
#include "interpolation.h"
|
||||
#include "allsensors.h"
|
||||
#include "io_pins.h"
|
||||
#include "OutputSignalList.h"
|
||||
#include "trigger_decoder.h"
|
||||
|
||||
/*
|
||||
* default Volumetric Efficiency
|
||||
*/
|
||||
//float getDefaultVE(int rpm) {
|
||||
// if (rpm > 5000)
|
||||
// return interpolate(5000, 1.1, 8000, 1, rpm);
|
||||
// return interpolate(500, 0.5, 5000, 1.1, rpm);
|
||||
//}
|
||||
//#define K_AT_MIN_RPM_MIN_TPS 0.25
|
||||
//#define K_AT_MIN_RPM_MAX_TPS 0.25
|
||||
//#define K_AT_MAX_RPM_MIN_TPS 0.25
|
||||
//#define K_AT_MAX_RPM_MAX_TPS 0.9
|
||||
//
|
||||
//#define rpmMin 500
|
||||
//#define rpmMax 8000
|
||||
//
|
||||
//#define tpMin 0
|
||||
//#define tpMax 100
|
||||
//
|
||||
// http://rusefi.com/math/t_charge.html
|
||||
// /
|
||||
//float getTCharge(int rpm, int tps, float coolantTemp, float airTemp) {
|
||||
// float minRpmKcurrentTPS = interpolate(tpMin, K_AT_MIN_RPM_MIN_TPS, tpMax,
|
||||
// K_AT_MIN_RPM_MAX_TPS, tps);
|
||||
// float maxRpmKcurrentTPS = interpolate(tpMin, K_AT_MAX_RPM_MIN_TPS, tpMax,
|
||||
// K_AT_MAX_RPM_MAX_TPS, tps);
|
||||
//
|
||||
// float Tcharge_coff = interpolate(rpmMin, minRpmKcurrentTPS, rpmMax,
|
||||
// maxRpmKcurrentTPS, rpm);
|
||||
//
|
||||
// float Tcharge = coolantTemp * (1 - Tcharge_coff) + airTemp * Tcharge_coff;
|
||||
//
|
||||
// return Tcharge;
|
||||
//}
|
||||
#define MAX_STARTING_FUEL 15
|
||||
#define MIN_STARTING_FUEL 8
|
||||
|
||||
/**
|
||||
* @return time needed to rotate crankshaft by one degree, in milliseconds.
|
||||
*/
|
||||
float getOneDegreeTimeMs(int rpm) {
|
||||
return 1000.0 * 60 / 360 / rpm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of milliseconds in one crankshaft revolution
|
||||
*/
|
||||
float getCrankshaftRevolutionTimeMs(int rpm) {
|
||||
return 360 * getOneDegreeTimeMs(rpm);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shifts angle into the [0..720) range
|
||||
* TODO: should be 'crankAngleRange' range?
|
||||
*/
|
||||
float fixAngle(float angle) {
|
||||
// I guess this implementation would be faster than 'angle % 720'
|
||||
while (angle < 0)
|
||||
angle += 720;
|
||||
while (angle > 720)
|
||||
angle -= 720;
|
||||
return angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns engine load according to selected engine_load_mode
|
||||
*
|
||||
*/
|
||||
float getEngineLoadT(engine_configuration_s *engineConfiguration) {
|
||||
switch (engineConfiguration->engineLoadMode) {
|
||||
case LM_MAF:
|
||||
return getMaf();
|
||||
case LM_MAP:
|
||||
return getMap();
|
||||
case LM_TPS:
|
||||
return getTPS();
|
||||
case LM_SPEED_DENSITY:
|
||||
// TODO: real implementation
|
||||
return getMap();
|
||||
default:
|
||||
firmwareError("Unexpected engine load parameter: %d", engineConfiguration->engineLoadMode);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void setSingleCoilDwell(engine_configuration_s *engineConfiguration) {
|
||||
for (int i = 0; i < DWELL_CURVE_SIZE; i++) {
|
||||
engineConfiguration->sparkDwellBins[i] = 0;
|
||||
engineConfiguration->sparkDwell[i] = -1;
|
||||
}
|
||||
|
||||
engineConfiguration->sparkDwellBins[5] = 1;
|
||||
engineConfiguration->sparkDwell[5] = 4;
|
||||
|
||||
engineConfiguration->sparkDwellBins[6] = 4500;
|
||||
engineConfiguration->sparkDwell[6] = 4;
|
||||
|
||||
engineConfiguration->sparkDwellBins[7] = 12500;
|
||||
engineConfiguration->sparkDwell[7] = 0;
|
||||
}
|
||||
|
||||
int isCrankingRT(engine_configuration_s *engineConfiguration, int rpm) {
|
||||
return rpm > 0 && rpm < engineConfiguration->crankingSettings.crankingRpm;
|
||||
}
|
||||
|
||||
OutputSignalList ignitionSignals;
|
||||
OutputSignalList injectonSignals;
|
||||
|
||||
void initializeIgnitionActions(float baseAngle, engine_configuration_s *engineConfiguration,
|
||||
engine_configuration2_s *engineConfiguration2) {
|
||||
chDbgCheck(engineConfiguration->cylindersCount > 0, "cylindersCount");
|
||||
ignitionSignals.clear();
|
||||
|
||||
EventHandlerConfiguration *config = &engineConfiguration2->engineEventConfiguration;
|
||||
resetEventList(&config->ignitionEvents);
|
||||
|
||||
switch (engineConfiguration->ignitionMode) {
|
||||
case IM_ONE_COIL:
|
||||
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
|
||||
// todo: extract method
|
||||
float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount;
|
||||
|
||||
registerActuatorEventExt(engineConfiguration, &engineConfiguration2->triggerShape, &config->ignitionEvents,
|
||||
ignitionSignals.add(SPARKOUT_1_OUTPUT), angle);
|
||||
}
|
||||
break;
|
||||
case IM_WASTED_SPARK:
|
||||
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
|
||||
float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount;
|
||||
|
||||
int wastedIndex = i % (engineConfiguration->cylindersCount / 2);
|
||||
|
||||
int id = (getCylinderId(engineConfiguration->firingOrder, wastedIndex) - 1);
|
||||
io_pin_e ioPin = (io_pin_e) (SPARKOUT_1_OUTPUT + id);
|
||||
|
||||
registerActuatorEventExt(engineConfiguration, &engineConfiguration2->triggerShape, &config->ignitionEvents,
|
||||
ignitionSignals.add(ioPin), angle);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
case IM_INDIVIDUAL_COILS:
|
||||
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
|
||||
float angle = baseAngle + 720.0 * i / engineConfiguration->cylindersCount;
|
||||
|
||||
io_pin_e pin = (io_pin_e) ((int) SPARKOUT_1_OUTPUT + getCylinderId(engineConfiguration->firingOrder, i) - 1);
|
||||
registerActuatorEventExt(engineConfiguration, &engineConfiguration2->triggerShape, &config->ignitionEvents,
|
||||
ignitionSignals.add(pin), angle);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
firmwareError("unsupported ignitionMode %d in initializeIgnitionActions()", engineConfiguration->ignitionMode);
|
||||
}
|
||||
}
|
||||
|
||||
void addFuelEvents(engine_configuration_s const *e, engine_configuration2_s *engineConfiguration2,
|
||||
ActuatorEventList *list, injection_mode_e mode) {
|
||||
resetEventList(list);
|
||||
|
||||
trigger_shape_s *s = &engineConfiguration2->triggerShape;
|
||||
|
||||
float baseAngle = e->globalTriggerAngleOffset + e->injectionOffset;
|
||||
|
||||
switch (mode) {
|
||||
case IM_SEQUENTIAL:
|
||||
for (int i = 0; i < e->cylindersCount; i++) {
|
||||
io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + getCylinderId(e->firingOrder, i) - 1);
|
||||
float angle = baseAngle + i * 720.0 / e->cylindersCount;
|
||||
registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle);
|
||||
}
|
||||
break;
|
||||
case IM_SIMULTANEOUS:
|
||||
for (int i = 0; i < e->cylindersCount; i++) {
|
||||
float angle = baseAngle + i * 720.0 / e->cylindersCount;
|
||||
|
||||
for (int j = 0; j < e->cylindersCount; j++) {
|
||||
io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + j);
|
||||
registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IM_BATCH:
|
||||
for (int i = 0; i < e->cylindersCount; i++) {
|
||||
io_pin_e pin = (io_pin_e) ((int) INJECTOR_1_OUTPUT + (i % 2));
|
||||
float angle = baseAngle + i * 720.0 / e->cylindersCount;
|
||||
registerActuatorEventExt(e, s, list, injectonSignals.add(pin), angle);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
firmwareError("Unexpected injection mode %d", mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Spark dwell time, in milliseconds.
|
||||
*/
|
||||
float getSparkDwellMsT(engine_configuration_s *engineConfiguration, int rpm) {
|
||||
if (isCrankingR(rpm)) {
|
||||
// technically this could be implemented via interpolate2d
|
||||
float angle = engineConfiguration->crankingChargeAngle;
|
||||
return getOneDegreeTimeMs(rpm) * angle;
|
||||
}
|
||||
|
||||
if (rpm > engineConfiguration->rpmHardLimit) {
|
||||
// technically this could be implemented via interpolate2d by setting everything above rpmHardLimit to zero
|
||||
warning(OBD_PCM_Processor_Fault, "skipping spark due to rpm=%d", rpm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return interpolate2d(rpm, engineConfiguration->sparkDwellBins, engineConfiguration->sparkDwell, DWELL_CURVE_SIZE);
|
||||
}
|
||||
|
||||
void registerActuatorEventExt(engine_configuration_s const *engineConfiguration, trigger_shape_s * s,
|
||||
ActuatorEventList *list, OutputSignal *actuator, float angleOffset) {
|
||||
chDbgCheck(s->size > 0, "uninitialized trigger_shape_s");
|
||||
|
||||
angleOffset = fixAngle(angleOffset + engineConfiguration->globalTriggerAngleOffset);
|
||||
|
||||
int triggerIndexOfZeroEvent = s->triggerShapeSynchPointIndex;
|
||||
|
||||
// todo: migrate to crankAngleRange?
|
||||
float firstAngle = s->wave.switchTimes[triggerIndexOfZeroEvent] * 720;
|
||||
|
||||
// let's find the last trigger angle which is less or equal to the desired angle
|
||||
int i;
|
||||
for (i = 0; i < s->size - 1; i++) {
|
||||
// todo: we need binary search here
|
||||
float angle = fixAngle(s->wave.switchTimes[(triggerIndexOfZeroEvent + i + 1) % s->size] * 720 - firstAngle);
|
||||
if (angle > angleOffset)
|
||||
break;
|
||||
}
|
||||
// explicit check for zero to avoid issues where logical zero is not exactly zero due to float nature
|
||||
float angle =
|
||||
i == 0 ? 0 : fixAngle(s->wave.switchTimes[(triggerIndexOfZeroEvent + i) % s->size] * 720 - firstAngle);
|
||||
|
||||
chDbgCheck(angleOffset >= angle, "angle constraint violation in registerActuatorEventExt()");
|
||||
|
||||
registerActuatorEvent(list, i, actuator, angleOffset - angle);
|
||||
}
|
||||
|
||||
//float getTriggerEventAngle(int triggerEventIndex) {
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
/**
|
||||
* there is some BS related to isnan in MinGW, so let's have all the issues in one place
|
||||
*/
|
||||
int cisnan(float f) {
|
||||
return *(((int*) (&f))) == 0x7FC00000;
|
||||
}
|
||||
|
||||
static int order_1_THEN_3_THEN_4_THEN2[] = { 1, 3, 4, 2 };
|
||||
|
||||
static int order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[] = { 1, 5, 3, 6, 2, 4 };
|
||||
|
||||
/**
|
||||
* @param index from zero to cylindersCount - 1
|
||||
* @return cylinderId from one to cylindersCount
|
||||
*/
|
||||
int getCylinderId(firing_order_e firingOrder, int index) {
|
||||
|
||||
switch (firingOrder) {
|
||||
case FO_ONE_CYLINDER:
|
||||
return 1;
|
||||
case FO_1_THEN_3_THEN_4_THEN2:
|
||||
return order_1_THEN_3_THEN_4_THEN2[index];
|
||||
case FO_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4:
|
||||
return order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[index];
|
||||
|
||||
default:
|
||||
firmwareError("getCylinderId not supported for %d", firingOrder);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void prepareOutputSignals(engine_configuration_s *engineConfiguration, engine_configuration2_s *engineConfiguration2) {
|
||||
|
||||
// todo: move this reset into decoder
|
||||
engineConfiguration2->triggerShape.triggerShapeSynchPointIndex = findTriggerZeroEventIndex(
|
||||
&engineConfiguration2->triggerShape, &engineConfiguration->triggerConfig);
|
||||
|
||||
injectonSignals.clear();
|
||||
EventHandlerConfiguration *config = &engineConfiguration2->engineEventConfiguration;
|
||||
addFuelEvents(engineConfiguration, engineConfiguration2, &config->crankingInjectionEvents,
|
||||
engineConfiguration->crankingInjectionMode);
|
||||
addFuelEvents(engineConfiguration, engineConfiguration2, &config->injectionEvents,
|
||||
engineConfiguration->injectionMode);
|
||||
}
|
||||
|
||||
void setTableBin(float array[], int size, float l, float r) {
|
||||
for (int i = 0; i < size; i++)
|
||||
array[i] = interpolate(0, l, size - 1, r, i);
|
||||
}
|
||||
|
||||
void setFuelRpmBin(engine_configuration_s *engineConfiguration, float l, float r) {
|
||||
setTableBin(engineConfiguration->fuelRpmBins, FUEL_RPM_COUNT, l, r);
|
||||
}
|
||||
|
||||
void setFuelLoadBin(engine_configuration_s *engineConfiguration, float l, float r) {
|
||||
setTableBin(engineConfiguration->fuelLoadBins, FUEL_LOAD_COUNT, l, r);
|
||||
}
|
||||
|
||||
void setTimingRpmBin(engine_configuration_s *engineConfiguration, float l, float r) {
|
||||
setTableBin(engineConfiguration->ignitionRpmBins, IGN_RPM_COUNT, l, r);
|
||||
}
|
||||
|
||||
void setTimingLoadBin(engine_configuration_s *engineConfiguration, float l, float r) {
|
||||
setTableBin(engineConfiguration->ignitionLoadBins, IGN_LOAD_COUNT, l, r);
|
||||
}
|
|
@ -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_ */
|
|
@ -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
|
||||
* <BR>1) getInjectorLag() correction to account for fuel injector lag
|
||||
* <BR>2) getCltCorrection() for warm-up
|
||||
* <BR>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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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_ */
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "wave_chart.h"
|
||||
#include "main.h"
|
||||
|
||||
#if EFI_WAVE_CHART
|
||||
|
||||
#include "eficonsole.h"
|
||||
#include "status_loop.h"
|
||||
|
||||
|
||||
#define CHART_DELIMETER "!"
|
||||
|
||||
/**
|
||||
* This is the number of events in the digital chart which would be displayed
|
||||
* on the 'digital sniffer' pane
|
||||
*/
|
||||
#if EFI_PROD_CODE
|
||||
static volatile int chartSize = 100;
|
||||
#else
|
||||
// need more events for automated test
|
||||
static volatile int chartSize = 200;
|
||||
#endif
|
||||
|
||||
static int isChartActive = TRUE;
|
||||
//static int isChartActive = FALSE;
|
||||
|
||||
//#define DEBUG_WAVE 1
|
||||
|
||||
#if DEBUG_WAVE
|
||||
static Logging debugLogging;
|
||||
#endif
|
||||
|
||||
static Logging logger;
|
||||
|
||||
void resetWaveChart(WaveChart *chart) {
|
||||
#if DEBUG_WAVE
|
||||
scheduleSimpleMsg(&debugLogging, "reset while at ", chart->counter);
|
||||
#endif
|
||||
resetLogging(&chart->logging);
|
||||
chart->counter = 0;
|
||||
appendPrintf(&chart->logging, "wave_chart%s", DELIMETER);
|
||||
}
|
||||
|
||||
static char LOGGING_BUFFER[5000] __attribute__((section(".ccm")));
|
||||
|
||||
static void printStatus(void) {
|
||||
scheduleIntValue(&logger, "chart", isChartActive);
|
||||
scheduleIntValue(&logger, "chartsize", chartSize);
|
||||
}
|
||||
|
||||
static void setChartActive(int value) {
|
||||
isChartActive = value;
|
||||
printStatus();
|
||||
}
|
||||
|
||||
void setChartSize(int newSize) {
|
||||
if (newSize < 5)
|
||||
return;
|
||||
chartSize = newSize;
|
||||
printStatus();
|
||||
}
|
||||
|
||||
void publishChartIfFull(WaveChart *chart) {
|
||||
if (isWaveChartFull(chart)) {
|
||||
publishChart(chart);
|
||||
resetWaveChart(chart);
|
||||
}
|
||||
}
|
||||
|
||||
int isWaveChartFull(WaveChart *chart) {
|
||||
return chart->counter >= chartSize;
|
||||
}
|
||||
|
||||
void publishChart(WaveChart *chart) {
|
||||
appendPrintf(&chart->logging, DELIMETER);
|
||||
#if DEBUG_WAVE
|
||||
Logging *l = &chart->logging;
|
||||
scheduleSimpleMsg(&debugLogging, "IT'S TIME", strlen(l->buffer));
|
||||
#endif
|
||||
if (isChartActive && getFullLog())
|
||||
scheduleLogging(&chart->logging);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a change in sniffed signal
|
||||
*/
|
||||
void addWaveChartEvent3(WaveChart *chart, char *name, char * msg, char * msg2) {
|
||||
chDbgCheck(chart->isInitialized, "chart not initialized");
|
||||
#if DEBUG_WAVE
|
||||
scheduleSimpleMsg(&debugLogging, "current", chart->counter);
|
||||
#endif
|
||||
if (isWaveChartFull(chart))
|
||||
return;
|
||||
lockOutputBuffer(); // we have multiple threads writing to the same output buffer
|
||||
appendPrintf(&chart->logging, "%s%s%s%s", name, CHART_DELIMETER, msg, CHART_DELIMETER);
|
||||
int time100 = getTimeNowUs() / 10;
|
||||
appendPrintf(&chart->logging, "%d%s%s", time100, msg2, CHART_DELIMETER);
|
||||
chart->counter++;
|
||||
unlockOutputBuffer();
|
||||
}
|
||||
|
||||
void initWaveChart(WaveChart *chart) {
|
||||
initLogging(&logger, "wave info");
|
||||
|
||||
if (!isChartActive)
|
||||
printMsg(&logger, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! chart disabled");
|
||||
|
||||
printStatus();
|
||||
|
||||
initLoggingExt(&chart->logging, "wave chart", LOGGING_BUFFER, sizeof(LOGGING_BUFFER));
|
||||
chart->isInitialized = TRUE;
|
||||
#if DEBUG_WAVE
|
||||
initLoggingExt(&debugLogging, "wave chart debug", &debugLogging.DEFAULT_BUFFER, sizeof(debugLogging.DEFAULT_BUFFER));
|
||||
#endif
|
||||
|
||||
resetWaveChart(chart);
|
||||
addConsoleActionI("chartsize", setChartSize);
|
||||
addConsoleActionI("chart", setChartActive);
|
||||
}
|
||||
|
||||
#endif /* EFI_WAVE_CHART */
|
Loading…
Reference in New Issue