This commit is contained in:
Andrey B 2014-04-26 09:00:30 -05:00
parent d55a41c1b6
commit 8253ccca7a
17 changed files with 20 additions and 3802 deletions

2317
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@ -1,283 +0,0 @@
/**
* @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_*/

View File

@ -1,14 +0,0 @@
/**
* @file snow_blower.c
* @brief Default configuration of a single-cylinder engine
*
* @date Sep 9, 2013
* @author Andrey Belomutskiy, (c) 2012-2014
*/
#include "main.h"
#if EFI_ENGINE_SNOW_BLOWER
#endif

View File

@ -1,276 +0,0 @@
/**
* @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 */

View File

@ -1,89 +0,0 @@
/**
* @file event_queue.cpp
* This is a data structure which keeps track of all pending events
* Implemented as a linked list, which is fine since the number of
* pending events is pretty low
* todo: MAYBE migrate to a better data structure, but that's low priority
*
* this data structure is NOT thread safe
*
* @date Apr 17, 2014
* @author Andrey Belomutskiy, (c) 2012-2014
*/
#include "event_queue.h"
#include "efitime.h"
#include "utlist.h"
EventQueue::EventQueue() {
head = NULL;
}
void EventQueue::insertTask(scheduling_s *scheduling, uint64_t nowUs, int delayUs, schfunc_t callback, void *param) {
if (callback == NULL)
firmwareError("NULL callback");
uint64_t time = nowUs + delayUs;
scheduling->momentUs = time;
#if EFI_SIGNAL_EXECUTOR_ONE_TIMER
scheduling->callback = callback;
scheduling->param = param;
#endif
scheduling_s * elt;
LL_FOREACH(head, elt)
{
if (elt == scheduling) {
firmwareError("re-adding element");
return;
}
}
LL_PREPEND(head, scheduling);
}
void EventQueue::insertTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) {
insertTask(scheduling, getTimeNowUs(), delayUs, callback, param);
}
/**
* Get the timestamp of the soonest pending action
*/
uint64_t EventQueue::getNextEventTime(uint64_t nowUs) {
scheduling_s * elt;
// this is a large value which is expected to be larger than any real time
uint64_t result = EMPTY_QUEUE;
LL_FOREACH(head, elt)
{
if (elt->momentUs <= nowUs) {
// todo: I am not so sure about this branch
continue;
}
if (elt->momentUs < result)
result = elt->momentUs;
}
return result;
}
/**
* Invoke all pending actions prior to specified timestamp
*/
void EventQueue::executeAll(uint64_t now) {
scheduling_s * elt, *tmp;
// here we need safe iteration because we are removing elements
LL_FOREACH_SAFE(head, elt, tmp)
{
if (elt->momentUs <= now) {
LL_DELETE(head, elt);
#if EFI_SIGNAL_EXECUTOR_ONE_TIMER
elt->callback(elt->param);
#endif /* EFI_SIGNAL_EXECUTOR_ONE_TIMER */
}
}
}
void EventQueue::clear(void) {
head = NULL;
}

View File

@ -1,30 +0,0 @@
/**
* @file event_queue.h
*
* @date Apr 17, 2014
* @author Andrey Belomutskiy, (c) 2012-2014
*/
#include "signal_executor.h"
#ifndef EVENT_SCHEDULER_H_
#define EVENT_SCHEDULER_H_
#define EMPTY_QUEUE 0x0FFFFFFFFFFFFFFFLL
class EventQueue {
public:
EventQueue();
void insertTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param);
void insertTask(scheduling_s *scheduling, uint64_t nowUs, int delayUs, schfunc_t callback, void *param);
void executeAll(uint64_t now);
uint64_t getNextEventTime(uint64_t nowUs);
void clear(void);
private:
scheduling_s *head;
};
#endif /* EVENT_SCHEDULER_H_ */

View File

@ -1,163 +0,0 @@
/**
* @file signal_executor.c
*
* todo: we should split this file into two:
* one for pure scheduling and another one for signal output which would
* use the scheduling
*
* @date Dec 4, 2013
* @author Andrey Belomutskiy, (c) 2012-2014
*
* This file is part of rusEfi - see http://rusefi.com
*
* rusEfi is free software; you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "main.h"
#include "signal_executor.h"
#if EFI_WAVE_CHART
#include "rpm_calculator.h"
#endif
#if EFI_WAVE_ANALYZER
/**
* Signal executors feed digital events right into WaveChart used by Sniffer tab of Dev Console
*/
#include "wave_analyzer.h"
#endif /* EFI_WAVE_ANALYZER */
#if EFI_PROD_CODE || EFI_SIMULATOR
static Logging logger;
#endif
void initSignalExecutor(void) {
#if EFI_PROD_CODE || EFI_SIMULATOR
initLogging(&logger, "s exec");
#endif
initSignalExecutorImpl();
}
void initOutputSignalBase(OutputSignal *signal) {
signal->status = IDLE;
// signal->last_scheduling_time = 0;
signal->initialized = TRUE;
}
static void turnHigh(OutputSignal *signal) {
#if EFI_DEFAILED_LOGGING
// signal->hi_time = hTimeNow();
#endif /* EFI_DEFAILED_LOGGING */
io_pin_e pin = signal->io_pin;
// turn the output level ACTIVE
// todo: this XOR should go inside the setOutputPinValue method
setOutputPinValue(pin, TRUE);
// sleep for the needed duration
#if EFI_PROD_CODE || EFI_SIMULATOR
if(
pin == SPARKOUT_1_OUTPUT ||
pin == SPARKOUT_3_OUTPUT) {
// time_t now = hTimeNow();
// float an = getCrankshaftAngle(now);
// scheduleMsg(&logger, "spark up%d %d", pin, now);
// scheduleMsg(&logger, "spark angle %d %f", (int)an, an);
}
#endif
#if EFI_WAVE_CHART
addWaveChartEvent(signal->name, "up", "");
#endif /* EFI_WAVE_ANALYZER */
}
static void turnLow(OutputSignal *signal) {
// turn off the output
// todo: this XOR should go inside the setOutputPinValue method
setOutputPinValue(signal->io_pin, FALSE);
#if EFI_DEFAILED_LOGGING
systime_t after = hTimeNow();
debugInt(&signal->logging, "a_time", after - signal->hi_time);
scheduleLogging(&signal->logging);
#endif /* EFI_DEFAILED_LOGGING */
#if EFI_WAVE_CHART
addWaveChartEvent(signal->name, "down", "");
#endif /* EFI_WAVE_ANALYZER */
}
/**
*
* @param delay the number of ticks before the output signal
* immediate output if delay is zero
* @param dwell the number of ticks of output duration
*
*/
int getRevolutionCounter(void);
void scheduleOutput(OutputSignal *signal, float delayMs, float durationMs) {
if (durationMs < 0) {
firmwareError("duration cannot be negative: %d", durationMs);
return;
}
scheduleOutputBase(signal, delayMs, durationMs);
int index = getRevolutionCounter() % 2;
scheduling_s * sUp = &signal->signalTimerUp[index];
scheduling_s * sDown = &signal->signalTimerDown[index];
scheduleTask(sUp, MS2US(delayMs), (schfunc_t) &turnHigh, (void *) signal);
scheduleTask(sDown, MS2US(delayMs + durationMs), (schfunc_t) &turnLow, (void*)signal);
// signal->last_scheduling_time = now;
}
void scheduleOutputBase(OutputSignal *signal, float delayMs, float durationMs) {
/**
* it's better to check for the exact 'TRUE' value since otherwise
* we would accept any memory garbage
*/
chDbgCheck(signal->initialized == TRUE, "Signal not initialized");
// signal->offset = offset;
// signal->duration = duration;
}
char *getPinName(io_pin_e io_pin) {
switch (io_pin) {
case SPARKOUT_1_OUTPUT:
return "Spark 1";
case SPARKOUT_2_OUTPUT:
return "Spark 2";
case SPARKOUT_3_OUTPUT:
return "Spark 3";
case SPARKOUT_4_OUTPUT:
return "Spark 4";
case INJECTOR_1_OUTPUT:
return "Injector 1";
case INJECTOR_2_OUTPUT:
return "Injector 2";
case INJECTOR_3_OUTPUT:
return "Injector 3";
case INJECTOR_4_OUTPUT:
return "Injector 4";
case INJECTOR_5_OUTPUT:
return "Injector 5";
default:
return "No name";
}
}

View File

@ -1,111 +0,0 @@
/**
* @file signal_executor.h
* @brief Asynchronous output signal header
*
* @date Feb 10, 2013
* @author Andrey Belomutskiy, (c) 2012-2014
*/
#ifndef SPARKOUT_H_
#define SPARKOUT_H_
#include "rusefi_enums.h"
#include "global.h"
#include "efifeatures.h"
#include "io_pins.h"
#if EFI_PROD_CODE
#include "datalogging.h"
#endif /* EFI_PROD_CODE */
#if EFI_SIGNAL_EXECUTOR_SLEEP
#include "signal_executor_sleep.h"
#endif /* EFI_SIGNAL_EXECUTOR_SLEEP */
#if EFI_SIGNAL_EXECUTOR_SINGLE_TIMER
#include "signal_executor_single_timer.h"
#endif /* EFI_SIGNAL_EXECUTOR_SINGLE_TIMER */
typedef void (*schfunc_t)(void *);
typedef struct scheduling_struct scheduling_s;
struct scheduling_struct {
//int initialized;
#if EFI_SIGNAL_EXECUTOR_SLEEP
VirtualTimer timer;
#endif /* EFI_SIGNAL_EXECUTOR_SLEEP */
#if EFI_SIGNAL_EXECUTOR_SINGLE_TIMER
volatile time_t moment;
#endif /* EFI_SIGNAL_EXECUTOR_SINGLE_TIMER */
volatile uint64_t momentUs;
#if EFI_SIGNAL_EXECUTOR_ONE_TIMER
schfunc_t callback;
void *param;
#endif
scheduling_s *next;
};
typedef enum {
IDLE = 0, ACTIVE
} executor_status_t;
/**
* @brief Asynchronous output signal data structure
*/
typedef struct OutputSignal_struct OutputSignal;
struct OutputSignal_struct {
/**
* name of this signal
*/
char *name;
io_pin_e io_pin;
#if 0 // depricated
// time in system ticks
volatile int offset;
// time in system ticks
volatile int duration;
#endif
int initialized;
// time_t last_scheduling_time;
// time_t hi_time;
/**
* We are alternating instances so that events which extend into next revolution are not overriden while
* scheduling next revolution events
*/
scheduling_s signalTimerUp[2];
scheduling_s signalTimerDown[2];
executor_status_t status;
#if EFI_SIGNAL_EXECUTOR_HW_TIMER
// todo
#endif
OutputSignal *next;
};
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
void initOutputSignal(OutputSignal *signal, io_pin_e ioPin);
void scheduleOutput(OutputSignal *signal, float delayMs, float durationMs);
void initOutputSignalBase(OutputSignal *signal);
void scheduleOutputBase(OutputSignal *signal, float delayMs, float durationMs);
void initSignalExecutor(void);
void initSignalExecutorImpl(void);
void scheduleTask(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param);
void scheduleByAngle(scheduling_s *timer, float angle, schfunc_t callback, void *param);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SPARKOUT_H_ */

View File

@ -1,74 +0,0 @@
/**
* @file signal_executor_single_timer_algo.c
*
* @date Nov 28, 2013
* @author Andrey Belomutskiy, (c) 2012-2014
*
*
* This file is part of rusEfi - see http://rusefi.com
*
* rusEfi is free software; you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "signal_executor.h"
#include "signal_executor_single_timer_algo.h"
#include "main.h"
#include "utlist.h"
#include "io_pins.h"
#if EFI_WAVE_ANALYZER
#include "wave_analyzer.h"
#include "wave_chart.h"
extern WaveChart waveChart;
#endif
#if EFI_SIGNAL_EXECUTOR_SINGLE_TIMER
/**
* @brief Output list
*
* List of all active output signals
* This is actually the head of the list.
* When the list is empty (initial state) the head of the list should be NULL.
* This is by design.
*/
OutputSignal *st_output_list = NULL;
inline void registerSignal(OutputSignal *signal) {
LL_APPEND(st_output_list, signal);
}
void setOutputPinValue(io_pin_e pin, int value);
/**
* @return time of next event within for this signal
* @todo Find better name.
*/
inline time_t toggleSignalIfNeeded(OutputSignal *out, time_t now) {
// chDbgCheck(out!=NULL, "out is NULL");
// chDbgCheck(out->io_pin < IO_PIN_COUNT, "pin assertion");
time_t last = out->last_scheduling_time;
//estimated = last + out->timing[out->status];
time_t estimated = last + GET_DURATION(out);
if (now >= estimated) {
out->status ^= 1; /* toggle status */
//setOutputPinValue(out->io_pin, out->status); /* Toggle output */
palWritePad(GPIOE, 5, out->status);
#if EFI_WAVE_ANALYZER
// addWaveChartEvent(out->name, out->status ? "up" : "down", "");
#endif /* EFI_WAVE_ANALYZER */
// out->last_scheduling_time = now; /* store last update */
estimated = now + GET_DURATION(out); /* update estimation */
}
return estimated - now;
}
#endif /* EFI_SIGNAL_EXECUTOR_SINGLE_TIMER */

View File

@ -1,61 +0,0 @@
/**
* @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_ */

View File

@ -1,7 +0,0 @@
svn up
call generate_docs
rem http://www.ncftp.com/download/
cd ../doxygen
ncftpput -u u71977750-docs -p docspass rusefi.com /html html/*

View File

@ -1,33 +0,0 @@
/**
* @file main.cpp
* @brief C++ main entry point
*
* @date Nov 29, 2012
* @author Andrey Belomutskiy, (c) 2012-2014
* http://rusefi.com/
*/
extern "C"
{
#include "global.h"
}
#include "main.h"
extern "C"
{
#include "rusefi.h"
}
int main(void) {
/*
* ChibiOS/RT initialization
*/
halInit();
chSysInit();
// looks like this holds a random value on start? Let's set a nice clean zero
DWT_CYCCNT = 0;
runRusEfi();
return 0;
}

61
main.h
View File

@ -1,61 +0,0 @@
/**
* @file main.h
*
* @date Nov 29, 2012
* @author Andrey Belomutskiy, (c) 2012-2014
*/
#pragma once
#ifndef MAIN_H_
#define MAIN_H_
#include <math.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
#include "global.h"
#include "eficonsole.h"
#include "eficonsole_logic.h"
#include "efilib.h"
#include "rusefi.h"
#include "efifeatures.h"
#include "efitime.h"
#include "engines.h"
#include "datalogging.h"
#include "chprintf.h"
#include "stm32f4xx_specific.h"
#ifdef __cplusplus
}
#endif /* __cplusplus */
// todo: access some existing configuration field
#define CORE_CLOCK 168000000
/**
* number of SysClock ticks in one ms
*/
#define TICKS_IN_MS (CH_FREQUENCY / 1000)
#define Delay(ms) chThdSleepMilliseconds(ms)
#ifdef __cplusplus
extern "C"
{
#endif
void updateHD44780lcd(void);
int systicks2ms(int systicks);
int lockAnyContext(void);
void unlockAnyContext(void);
#ifdef __cplusplus
}
#endif
#endif /* MAIN_H_ */

20
readme.txt Normal file
View File

@ -0,0 +1,20 @@
*****************************************************************************
*** Releases ***
*****************************************************************************
03/26/2014 r2413 C++, refactoring & improvements
02/23/2013 r1777 trigger is now configurable via TunerStudio
01/30/2014 r1309 true trigger angles, VBatt signal
01/12/2014 r1007 refactoring, refactoring & refactoring
12/19/2013 r605 ignition control & a little bit of CAN bus
11/08/2013 r100 Tuner Studio fuel map tuning, fuel pump control
10/14/2013 r39 USB serial bug, missing IAR files, self-containted Makefile
10/13/2013 r33 IAR project file
10/04/2013 r26 Patched ChibiOS/RT 2.6.1 sources are now included
09/23/2013 r20 Tuner Studio integration, configuraton persistence
08/30/2013 r14 initial documentation & refactoring. tunerstudio integration
08/03/2013 r13 wideband O2 input, better idling algorithm, serial-over-USB
07/05/2013 r10 Second CKP, sequential injection.
06/19/2013 r9 Initial version - batch injection & ignition with advance table lookup.

View File

@ -1,278 +0,0 @@
/**
* @file rusefi.c
* @brief Initialization code and main status reporting look
*
* @date Dec 25, 2013
* @author Andrey Belomutskiy, (c) 2012-2014
*/
/**
* @mainpage
*
* @section sec_main Brief overview
*
* rusEfi runs on crankshaft or camshaft ('trigger') position sensor events.
* Once per crankshaft revolution we evaluate the amount of needed fuel and
* the spark timing. Once we have decided on the parameters for this revolution
* we schedule all the actions to be triggered by the closest trigger event.
*
* We also have some utility threads like idle control thread and communication threads.
*
*
*
* @section sec_trigger Trigger Decoding
*
* Our primary trigger decoder is based on the idea of synchronizing the primary shaft signal and simply counting events on
* the secondary signal. A typical scenario would be when camshaft positions sensor is the primary signal and crankshaft is secondary,
* but sometimes there would be two signals generated by two camshaft sensors.
* Another scenario is when we only have crankshaft position sensor, this would make it the primary signal and there would be no secondary signal.
*
* There is no software filtering so the signals are expected to be valid. TODO: in reality we are still catching engine stop noise as unrealisticly high RPM.
*
* The decoder is configured to act either on the primary signal rise or on the primary signal fall. It then compares the duration
* of time from the previous signal to the duration of time from the signal before previous, and if the ratio falls into the configurable
* range between 'syncRatioFrom' and 'syncRatioTo' this is assumed to be the synchronizing event.
*
* For instance, for a 36/1 skipped tooth wheel the ratio range for synchronization is from 1.5 to 3
*
* Some triggers do not require synchronization, this case we just count signals.
* A single tooth primary signal would be a typical example when synchronization is not needed.
*
*
*
*
*
* @section sec_scheduler Event Scheduler
*
* It is a general agreement to measure all angles in crankshaft angles. In a four stroke
* engine, a full cycle consists of two revolutions of the crankshaft, so all the angles are
* running between 0 and 720 degrees.
*
* Ignition timing is a great example of a process which highlights the need of a hybrid
* approach to event scheduling.
* The most important part of controlling ignition
* is firing up the spark at the right moment - so, for this job we need 'angle-based' timing,
* for example we would need to fire up the spark at 700 degrees. Before we can fire up the spark
* at 700 degrees, we need to charge the ignition coil, for example this dwell time is 4ms - that
* means we need to turn on the coil at '4 ms before 700 degrees'. Let's assume that the engine is
* current at 600 RPM - that means 360 degrees would take 100ms so 4ms is 14.4 degrees at current RPM which
* means we need to start charting the coil at 685.6 degrees.
*
* The position sensors at our disposal are not providing us the current position at any moment of time -
* all we've got is a set of events which are happening at the knows positions. For instance, let's assume that
* our sensor sends as an event at 0 degrees, at 90 degrees, at 600 degrees and and 690 degrees.
*
* So, for this particular sensor the most precise scheduling would be possible if we schedule coil charting
* as '85.6 degrees after the 600 degrees position sensor event', and spark firing as
* '10 degrees after the 690 position sensor event'. Considering current RPM, we calculate that '10 degress after' is
* 2.777ms, so we schedule spark firing at '2.777ms after the 690 position sensor event', thus combining trigger events
* with time-based offset.
*
*
* @section sec_fuel_injection Fuel Injection
*
*
* @sectuion sec_misc
*
* <BR>See main_trigger_callback.cpp for main trigger event handler
* <BR>See fuel_math.cpp for details on fuel amount logic
* <BR>See rpm_calculator.c for details on how getRpm() is calculated
*
*/
extern "C" {
#include "global.h"
#include "main.h"
#include "rusefi.h"
#include "eficonsole.h"
#include "hardware.h"
#include "engine_controller.h"
#include "lcd_HD44780.h"
#include "status_loop.h"
#include "pin_repository.h"
#include "status_loop.h"
#include "memstreams.h"
}
#if EFI_ENGINE_EMULATOR
#include "engine_emulator.h"
#endif /* EFI_ENGINE_EMULATOR */
static Logging logging;
int main_loop_started = FALSE;
static MemoryStream errorMessageStream;
uint8_t errorMessageBuffer[200];
bool hasFirmwareError = FALSE;
void runRusEfi(void) {
msObjectInit(&errorMessageStream, errorMessageBuffer, sizeof(errorMessageBuffer), 0);
initErrorHandling();
/**
* First data structure keeps track of which hardware I/O pins are used by whom
*/
initPinRepository();
/**
* Next we should initialize serial port console, it's important to know what's going on
*/
initializeConsole();
initLogging(&logging, "main");
addConsoleAction("reset", scheduleReset);
/**
* Initialize hardware drivers
*/
initHardware();
initStatusLoop();
/**
* Now let's initialize actual engine control logic
* todo: should we initialize some? most? controllers before hardware?
*/
initEngineContoller();
#if EFI_ENGINE_EMULATOR
initEngineEmulator();
#endif
startStatusThreads();
print("Running main loop\r\n");
main_loop_started = TRUE;
/**
* This loop is the closes we have to 'main loop' - but here we only publish the status. The main logic of engine
* control is around main_trigger_callback
*/
while (TRUE) {
#if EFI_CLI_SUPPORT
// sensor state + all pending messages for our own dev console
updateDevConsoleState();
#endif /* EFI_CLI_SUPPORT */
chThdSleepMilliseconds(5);
}
}
int systicks2ms(int systicks) {
return systicks / TICKS_IN_MS;
}
static VirtualTimer resetTimer;
static void rebootNow(void) {
NVIC_SystemReset();
}
/**
* Some configuration changes require full firmware reset.
* Once day we will write graceful shutdown, but that would be one day.
*/
void scheduleReset(void) {
scheduleMsg(&logging, "Rebooting in 5 seconds...");
lockAnyContext();
chVTSetI(&resetTimer, 5 * CH_FREQUENCY, (vtfunc_t) rebootNow, NULL);
unlockAnyContext();
}
extern "C" {
void onFatalError(const char *msg, char * file, int line);
}
void onFatalError(const char *msg, char * file, int line) {
onDbgPanic();
lcdShowFatalMessage((char *) msg);
if (!main_loop_started) {
print("fatal %s %s:%d\r\n", msg, file, line);
chThdSleepSeconds(1);
chSysHalt();
}
}
void DebugMonitorVector(void) {
chDbgPanic3("DebugMonitorVector", __FILE__, __LINE__);
while (TRUE)
;
}
void UsageFaultVector(void) {
chDbgPanic3("UsageFaultVector", __FILE__, __LINE__);
while (TRUE)
;
}
void BusFaultVector(void) {
chDbgPanic3("BusFaultVector", __FILE__, __LINE__);
while (TRUE)
;
}
void HardFaultVector(void) {
chDbgPanic3("HardFaultVector", __FILE__, __LINE__);
while (TRUE)
;
}
extern int main_loop_started;
int hasFatalError(void);
void onFatalError(const char *msg, char * file, int line);
char *dbg_panic_file;
int dbg_panic_line;
extern "C" {
void chDbgPanic3(const char *msg, char * file, int line);
}
void chDbgPanic3(const char *msg, char * file, int line) {
if (hasFatalError())
return;
dbg_panic_file = file;
dbg_panic_line = line;
dbg_panic_msg = msg;
onFatalError(dbg_panic_msg, dbg_panic_file, dbg_panic_line);
}
static char panicMessage[200];
void chDbgStackOverflowPanic(Thread *otp) {
strcpy(panicMessage, "stack overflow: ");
#ifdef CH_USE_REGISTRY
strcat(panicMessage, otp->p_name);
#endif
chDbgPanic3(panicMessage, __FILE__, __LINE__);
}
void firmwareError(const char *fmt, ...) {
if (hasFirmwareError)
return;
hasFirmwareError = TRUE;
errorMessageStream.eos = 0; // reset
va_list ap;
va_start(ap, fmt);
chvprintf((BaseSequentialStream *) &errorMessageStream, fmt, ap);
va_end(ap);
errorMessageStream.buffer[errorMessageStream.eos] = 0; // need to terminate explicitly
}
int getRusEfiVersion(void) {
return 20140424;
}

View File

@ -1,4 +0,0 @@
// This file was generated by Version2Header
#ifndef SVN_VERSION
#define SVN_VERSION 2853
#endif

View File

@ -1 +0,0 @@
java -jar ../java_tools/version2header.jar