rusefi-1/firmware/rusefi.cpp

283 lines
10 KiB
C++
Raw Normal View History

2014-08-29 07:52:33 -07:00
/**
* @file rusefi.cpp
* @brief Initialization code and main status reporting look
*
* @date Dec 25, 2013
2015-01-12 15:04:10 -08:00
* @author Andrey Belomutskiy, (c) 2012-2015
2014-08-29 07:52:33 -07:00
*/
/**
* @mainpage
*
* @section sec_into
*
* rusEfi is implemented based on the idea that with modern 100+ MHz microprocessors the relatively
* undemanding task of internal combustion engine control could be implemented in a high-level, processor-independent
* (to some extent) manner. Thus the key concepts of rusEfi: dependency on high-level hardware abstraction layer, software-based PWM etc.
*
* @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.
*
2014-10-21 04:02:54 -07:00
* @section config Persistent Configuration
* engine_configuration_s structure is kept in the internal flash memory, it has all the settings. Currently rusefi.ini has a direct mapping of this structure.
*
* Please note that due to TunerStudio protocol it's important to have the total structure size in synch between the firmware and TS .ini file -
* just to make sure that this is not forgotten the size of the structure is hard-coded as PAGE_0_SIZE constant. There is always some 'unused' fields added in advance so that
* one can add some fields without the pain of increasing the total config page size.
* <br>See flash_main.cpp
*
2014-08-29 07:52:33 -07:00
*
* @section sec_fuel_injection Fuel Injection
*
*
2014-10-21 04:02:54 -07:00
* @sectuion sec_misc Misc
2014-08-29 07:52:33 -07:00
*
* <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.cpp for details on how getRpm() is calculated
*
*/
#include "main.h"
#include "trigger_structure.h"
#include "hardware.h"
#include "engine_controller.h"
2014-09-27 15:03:08 -07:00
#include "efiGpio.h"
2014-08-29 07:52:33 -07:00
#include "global.h"
#include "rfi_perftest.h"
#include "rusefi.h"
2014-11-15 09:03:07 -08:00
#include "memstreams.h"
2014-08-29 07:52:33 -07:00
#include "eficonsole.h"
#include "status_loop.h"
#include "pin_repository.h"
#if EFI_HD44780_LCD
#include "lcd_HD44780.h"
#endif /* EFI_HD44780_LCD */
2014-11-15 09:03:07 -08:00
#if EFI_ENGINE_EMULATOR || defined(__DOXYGEN__)
2014-08-29 07:52:33 -07:00
#include "engine_emulator.h"
#endif /* EFI_ENGINE_EMULATOR */
2015-02-09 09:05:46 -08:00
static LoggingWithStorage sharedLogger("main");
2014-08-29 07:52:33 -07:00
2015-01-24 13:04:21 -08:00
bool_t main_loop_started = false;
2014-08-29 07:52:33 -07:00
static MemoryStream firmwareErrorMessageStream;
2015-01-20 19:04:09 -08:00
static char panicMessage[200];
2014-08-29 07:52:33 -07:00
uint8_t errorMessageBuffer[200];
2014-11-24 09:03:09 -08:00
bool hasFirmwareErrorFlag = false;
2015-01-20 19:04:09 -08:00
static virtual_timer_t resetTimer;
2014-11-19 06:03:54 -08:00
EXTERN_ENGINE
;
2014-08-29 07:52:33 -07:00
char *getFirmwareError(void) {
2014-11-19 06:03:54 -08:00
return (char*) errorMessageBuffer;
2014-08-29 07:52:33 -07:00
}
2015-02-17 18:06:10 -08:00
// todo: move this into a hw-specific file
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.
*/
static void scheduleReboot(void) {
scheduleMsg(&sharedLogger, "Rebooting in 5 seconds...");
lockAnyContext();
chVTSetI(&resetTimer, 5 * CH_FREQUENCY, (vtfunc_t) rebootNow, NULL);
unlockAnyContext();
}
2015-02-15 11:06:59 -08:00
void swo_init()
{
// todo: make SWO work
// uint32_t SWOSpeed = 2000000; //2000kbps, default for ST-LINK
// // todo: use a macro to access clock speed
// uint32_t SWOPrescaler = (168000000 / SWOSpeed) - 1; // SWOSpeed in Hz, note that F_CPU is expected to be 96000000 in this case
// CoreDebug->DEMCR = CoreDebug_DEMCR_TRCENA_Msk;
// *((volatile unsigned *)(ITM_BASE + 0x400F0)) = 0x00000002; // "Selected PIN Protocol Register": Select which protocol to use for trace output (2: SWO)
// *((volatile unsigned *)(ITM_BASE + 0x40010)) = SWOPrescaler; // "Async Clock Prescaler Register". Scale the baud rate of the asynchronous output
// *((volatile unsigned *)(ITM_BASE + 0x00FB0)) = 0xC5ACCE55; // ITM Lock Access Register, C5ACCE55 enables more write access to Control Register 0xE00 :: 0xFFC
// ITM->TCR = ITM_TCR_TraceBusID_Msk | ITM_TCR_SWOENA_Msk | ITM_TCR_SYNCENA_Msk | ITM_TCR_ITMENA_Msk; // ITM Trace Control Register
// ITM->TPR = ITM_TPR_PRIVMASK_Msk; // ITM Trace Privilege Register
// ITM->TER = 0x00000001; // ITM Trace Enable Register. Enabled tracing on stimulus ports. One bit per stimulus port.
// *((volatile unsigned *)(ITM_BASE + 0x01000)) = 0x400003FE; // DWT_CTRL
// *((volatile unsigned *)(ITM_BASE + 0x40304)) = 0x00000100; // Formatter and Flush Control Register
}
2014-08-29 07:52:33 -07:00
void runRusEfi(void) {
msObjectInit(&firmwareErrorMessageStream, errorMessageBuffer, sizeof(errorMessageBuffer), 0);
2014-09-08 21:02:56 -07:00
// that's dirty, this assignment should be nicer or in a better spot
2014-11-07 19:04:45 -08:00
engine->engineConfiguration = engineConfiguration;
2015-01-20 15:04:01 -08:00
#if EFI_ENGINE_CONTROL || defined(__DOXYGEN__)
2014-11-07 19:04:45 -08:00
engine->engineConfiguration2 = engineConfiguration2;
2015-01-20 15:04:01 -08:00
#endif
2014-09-08 21:02:56 -07:00
2014-08-29 07:52:33 -07:00
initErrorHandling();
2015-02-15 11:06:59 -08:00
swo_init();
2014-08-29 07:52:33 -07:00
/**
* 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
*/
2015-01-15 13:03:51 -08:00
initializeConsole(&sharedLogger);
2014-08-29 07:52:33 -07:00
2014-11-07 19:04:45 -08:00
engine->init();
2014-09-26 13:03:02 -07:00
2015-02-17 18:06:10 -08:00
addConsoleAction("reboot", scheduleReboot);
2014-08-29 07:52:33 -07:00
/**
* Initialize hardware drivers
*/
2015-01-14 15:04:00 -08:00
initHardware(&sharedLogger, engine);
2014-08-29 07:52:33 -07:00
2014-11-07 19:04:45 -08:00
initStatusLoop(engine);
2014-08-29 07:52:33 -07:00
/**
* Now let's initialize actual engine control logic
* todo: should we initialize some? most? controllers before hardware?
*/
2015-01-14 15:04:00 -08:00
initEngineContoller(&sharedLogger, engine);
2014-08-29 07:52:33 -07:00
2014-11-15 09:03:07 -08:00
#if EFI_PERF_METRICS || defined(__DOXYGEN__)
2015-01-14 15:04:00 -08:00
initTimePerfActions(&sharedLogger);
2014-08-29 07:52:33 -07:00
#endif
2014-11-15 09:03:07 -08:00
#if EFI_ENGINE_EMULATOR || defined(__DOXYGEN__)
2015-01-14 16:03:39 -08:00
initEngineEmulator(&sharedLogger, engine);
2014-08-29 07:52:33 -07:00
#endif
2014-11-07 19:04:45 -08:00
startStatusThreads(engine);
2014-08-29 07:52:33 -07:00
print("Running main loop\r\n");
2014-12-06 20:03:14 -08:00
main_loop_started = true;
2014-08-29 07:52:33 -07:00
/**
* 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) {
2014-11-08 16:03:17 -08:00
efiAssertVoid(getRemainingStack(chThdSelf()) > 128, "stack#1");
2014-08-29 07:52:33 -07:00
2014-11-15 09:03:07 -08:00
#if (EFI_CLI_SUPPORT && !EFI_UART_ECHO_TEST_MODE) || defined(__DOXYGEN__)
2014-08-29 07:52:33 -07:00
// sensor state + all pending messages for our own dev console
2014-11-07 19:04:45 -08:00
updateDevConsoleState(engine);
2014-08-29 07:52:33 -07:00
#endif /* EFI_CLI_SUPPORT */
chThdSleepMilliseconds(boardConfiguration->consoleLoopPeriod);
}
}
void chDbgStackOverflowPanic(Thread *otp) {
strcpy(panicMessage, "stack overflow: ");
#ifdef CH_USE_REGISTRY
strcat(panicMessage, otp->p_name);
#endif
chDbgPanic3(panicMessage, __FILE__, __LINE__);
}
2015-01-13 10:06:16 -08:00
extern engine_pins_s enginePins;
2015-01-07 16:03:45 -08:00
2014-11-08 08:09:38 -08:00
// todo: why is this method here and not in error_handling.c ?
2015-02-02 05:06:40 -08:00
void firmwareError(const char *errorMsg, ...) {
2014-08-29 07:52:33 -07:00
if (hasFirmwareErrorFlag)
return;
2015-02-04 04:06:14 -08:00
ON_FATAL_ERROR();
2014-08-29 07:52:33 -07:00
hasFirmwareErrorFlag = TRUE;
2015-02-02 05:06:40 -08:00
if (indexOf(errorMsg, '%') == -1) {
2014-11-19 06:03:54 -08:00
/**
* in case of simple error message let's reduce stack usage
* because chvprintf might be causing an error
*/
2015-02-02 05:06:40 -08:00
strcpy((char*) errorMessageBuffer, errorMsg);
2014-11-19 06:03:54 -08:00
} else {
firmwareErrorMessageStream.eos = 0; // reset
va_list ap;
2015-02-02 05:06:40 -08:00
va_start(ap, errorMsg);
chvprintf((BaseSequentialStream *) &firmwareErrorMessageStream, errorMsg, ap);
2014-11-19 06:03:54 -08:00
va_end(ap);
firmwareErrorMessageStream.buffer[firmwareErrorMessageStream.eos] = 0; // need to terminate explicitly
}
2014-08-29 07:52:33 -07:00
}
2015-02-11 14:04:02 -08:00
static char UNUSED_RAM_SIZE[9999];
2014-11-22 07:03:14 -08:00
2015-02-11 14:04:02 -08:00
static char UNUSED_CCM_SIZE[4900] CCM_OPTIONAL;
2014-11-22 07:03:14 -08:00
2014-08-29 07:52:33 -07:00
int getRusEfiVersion(void) {
2014-11-22 07:03:14 -08:00
if (UNUSED_RAM_SIZE == 0)
return 1; // this is here to make the compiler happy about the unused array
if (UNUSED_CCM_SIZE == 0)
return 1; // this is here to make the compiler happy about the unused array
2015-02-24 08:09:39 -08:00
return 20150224;
2014-08-29 07:52:33 -07:00
}