2016-06-24 16:02:45 -07:00
|
|
|
/*
|
|
|
|
* @file CJ125.cpp
|
|
|
|
*
|
|
|
|
* @date: Jun 24, 2016
|
2018-01-20 17:55:31 -08:00
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2018
|
2016-06-24 16:02:45 -07:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-07-17 00:01:48 -07:00
|
|
|
#include "engine.h"
|
2016-07-16 23:03:46 -07:00
|
|
|
#include "CJ125.h"
|
2016-07-17 00:01:48 -07:00
|
|
|
#include "pwm_generator.h"
|
2018-01-29 11:26:42 -08:00
|
|
|
#include "rpm_calculator.h"
|
2016-07-17 00:01:48 -07:00
|
|
|
#include "pid.h"
|
|
|
|
|
2016-07-25 20:03:45 -07:00
|
|
|
#if EFI_CJ125 || defined(__DOXYGEN__)
|
|
|
|
|
2018-01-29 11:38:58 -08:00
|
|
|
// looks like 3v range should be enough, divider not needed
|
|
|
|
#define EFI_CJ125_DIRECTLY_CONNECTED_UR TRUE
|
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
#include "adc_inputs.h"
|
|
|
|
|
2018-09-25 12:45:17 -07:00
|
|
|
//#define CJ125_DEBUG
|
2018-01-29 11:26:42 -08:00
|
|
|
//#define CJ125_DEBUG_SPI
|
2016-08-01 19:04:24 -07:00
|
|
|
|
2016-07-17 00:01:48 -07:00
|
|
|
EXTERN_ENGINE;
|
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
|
2019-01-03 04:22:21 -08:00
|
|
|
#include "hardware.h"
|
|
|
|
#include "backup_ram.h"
|
|
|
|
#include "pin_repository.h"
|
2018-01-29 11:26:42 -08:00
|
|
|
extern TunerStudioOutputChannels tsOutputChannels;
|
|
|
|
#endif
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
struct CJ125_state {
|
|
|
|
efitick_t startHeatingNt;
|
|
|
|
efitick_t prevNt;
|
|
|
|
};
|
|
|
|
|
2019-01-09 05:50:51 -08:00
|
|
|
static SimplePwm wboHeaterControl("wbo");
|
2018-01-29 11:26:42 -08:00
|
|
|
static OutputPin wboHeaterPin;
|
2016-07-17 00:01:48 -07:00
|
|
|
static OutputPin cj125Cs;
|
2016-07-26 19:01:50 -07:00
|
|
|
static Logging *logger;
|
2018-01-29 11:26:42 -08:00
|
|
|
static unsigned char tx_buff[2];
|
2016-07-30 19:02:52 -07:00
|
|
|
static unsigned char rx_buff[1];
|
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static pid_s heaterPidConfig;
|
|
|
|
static Pid heaterPid(&heaterPidConfig);
|
|
|
|
static float heaterDuty = 0.0f;
|
|
|
|
|
2019-01-03 05:26:42 -08:00
|
|
|
// todo: only define this variable in EIF_PROD
|
|
|
|
static CJ125 globalInstance;
|
|
|
|
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
|
|
|
|
|
2016-07-25 19:01:42 -07:00
|
|
|
static THD_WORKING_AREA(cjThreadStack, UTILITY_THREAD_STACK_SIZE);
|
|
|
|
|
2016-07-17 00:01:48 -07:00
|
|
|
static SPIDriver *driver;
|
|
|
|
|
2018-11-03 08:44:57 -07:00
|
|
|
#if EFI_PROD_CODE
|
2016-07-17 00:01:48 -07:00
|
|
|
static SPIConfig cj125spicfg = { NULL,
|
2018-01-29 11:26:42 -08:00
|
|
|
/* HW dependent part.*/
|
|
|
|
NULL, 0, SPI_CR1_MSTR | SPI_CR1_CPHA | SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR_2 };
|
2019-01-03 05:26:42 -08:00
|
|
|
#endif /* EFI_PROD_CODE */
|
2018-01-29 11:26:42 -08:00
|
|
|
|
2019-01-03 05:26:42 -08:00
|
|
|
#endif /* EFI_UNIT_TEST */
|
|
|
|
|
|
|
|
CJ125::CJ125() {
|
|
|
|
state = CJ125_IDLE;
|
|
|
|
errorCode = CJ125_ERROR_NONE;
|
|
|
|
}
|
2019-01-03 04:22:21 -08:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
// Chip diagnostics register contents
|
|
|
|
static volatile int diag = 0;
|
|
|
|
|
|
|
|
// Current values
|
2019-01-03 05:26:42 -08:00
|
|
|
static volatile float vUa = 0.0f;
|
|
|
|
static volatile float vUr = 0.0f;
|
2018-01-29 11:26:42 -08:00
|
|
|
// Calibration values
|
|
|
|
static volatile float vUaCal = 0.0f, vUrCal = 0.0f;
|
|
|
|
|
|
|
|
static volatile int lastSlowAdcCounter = 0;
|
|
|
|
|
|
|
|
static volatile cj125_mode_e mode = CJ125_MODE_NONE;
|
|
|
|
// Amplification coefficient, needed by cjGetAfr()
|
|
|
|
static volatile float amplCoeff = 0.0f;
|
|
|
|
// Calculated Lambda-value
|
|
|
|
static volatile float lambda = 1.0f;
|
|
|
|
|
|
|
|
// LSU conversion tables. See cj125_sensor_type_e
|
|
|
|
// For LSU4.2, See http://www.bosch-motorsport.com/media/catalog_resources/Lambda_Sensor_LSU_42_Datasheet_51_en_2779111435pdf.pdf
|
|
|
|
// See LSU4.9, See http://www.bosch-motorsport.com/media/catalog_resources/Lambda_Sensor_LSU_49_Datasheet_51_en_2779147659pdf.pdf
|
2018-06-23 13:24:59 -07:00
|
|
|
static const int CJ125_LSU_CURVE_SIZE = 25;
|
2018-01-29 11:26:42 -08:00
|
|
|
// This is a number of bins for each sensor type (should be < CJ125_LSU_CURVE_SIZE)
|
|
|
|
static const float cjLSUTableSize[2] = {
|
2018-06-23 13:24:59 -07:00
|
|
|
9, 24,
|
2018-01-29 11:26:42 -08:00
|
|
|
};
|
|
|
|
// Pump current, mA
|
|
|
|
static const float cjLSUBins[2][CJ125_LSU_CURVE_SIZE] = { {
|
|
|
|
// LSU 4.2
|
|
|
|
-1.85f, -1.08f, -0.76f, -0.47f, 0.0f, 0.34f, 0.68f, 0.95f, 1.4f }, {
|
|
|
|
// LSU 4.9
|
2018-06-23 13:24:59 -07:00
|
|
|
-2.0f, -1.602f, -1.243f, -0.927f, -0.8f, -0.652f, -0.405f, -0.183f, -0.106f, -0.04f, 0, 0.015f, 0.097f, 0.193f, 0.250f, 0.329f, 0.671f, 0.938f, 1.150f, 1.385f, 1.700f, 2.000f, 2.150f, 2.250f },
|
2018-01-29 11:26:42 -08:00
|
|
|
};
|
|
|
|
// Lambda value
|
|
|
|
static const float cjLSULambda[2][CJ125_LSU_CURVE_SIZE] = { {
|
|
|
|
// LSU 4.2
|
|
|
|
0.7f, 0.8f, 0.85f, 0.9f, 1.009f, 1.18f, 1.43f, 1.7f, 2.42f }, {
|
|
|
|
// LSU 4.9
|
2018-06-23 13:24:59 -07:00
|
|
|
0.65f, 0.7f, 0.75f, 0.8f, 0.822f, 0.85f, 0.9f, 0.95f, 0.97f, 0.99f, 1.003f, 1.01f, 1.05f, 1.1f, 1.132f, 1.179f, 1.429f, 1.701f, 1.990f, 2.434f, 3.413f, 5.391f, 7.506f, 10.119f },
|
2018-01-29 11:26:42 -08:00
|
|
|
};
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static int cjReadRegister(unsigned char regAddr) {
|
2019-01-03 04:22:21 -08:00
|
|
|
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
|
2018-01-29 11:26:42 -08:00
|
|
|
spiSelect(driver);
|
|
|
|
tx_buff[0] = regAddr;
|
|
|
|
spiSend(driver, 1, tx_buff);
|
|
|
|
// safety?
|
|
|
|
chThdSleepMilliseconds(10);
|
|
|
|
|
|
|
|
rx_buff[0] = 0;
|
|
|
|
spiReceive(driver, 1, rx_buff);
|
|
|
|
spiUnselect(driver);
|
2016-07-25 19:01:42 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
#ifdef CJ125_DEBUG_SPI
|
|
|
|
scheduleMsg(logger, "cjReadRegister: addr=%d answer=%d", regAddr, rx_buff[0]);
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif /* CJ125_DEBUG_SPI */
|
2018-01-29 11:26:42 -08:00
|
|
|
return rx_buff[0];
|
2019-01-03 04:22:21 -08:00
|
|
|
#else /* EFI_UNIT_TEST */
|
|
|
|
return 0;
|
|
|
|
#endif /* EFI_UNIT_TEST */
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
2016-07-25 19:01:42 -07:00
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static void cjWriteRegister(unsigned char regAddr, unsigned char regValue) {
|
2019-01-03 04:22:21 -08:00
|
|
|
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
|
2018-01-29 11:26:42 -08:00
|
|
|
#ifdef CJ125_DEBUG_SPI
|
|
|
|
scheduleMsg(logger, "cjWriteRegister: addr=%d value=%d", regAddr, regValue);
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif /* CJ125_DEBUG_SPI */
|
2018-01-29 11:26:42 -08:00
|
|
|
spiSelect(driver);
|
|
|
|
tx_buff[0] = regAddr;
|
|
|
|
tx_buff[1] = regValue;
|
|
|
|
spiSend(driver, 2, tx_buff);
|
|
|
|
spiUnselect(driver);
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif /* EFI_UNIT_TEST */
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
2016-07-25 19:01:42 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static float getUr() {
|
2019-01-03 04:22:21 -08:00
|
|
|
#if ! EFI_UNIT_TEST
|
2018-01-29 11:26:42 -08:00
|
|
|
if (CONFIG(cj125ur) != EFI_ADC_NONE) {
|
2018-11-03 08:44:57 -07:00
|
|
|
#if EFI_PROD_CODE
|
2018-01-29 11:26:42 -08:00
|
|
|
#ifdef EFI_CJ125_DIRECTLY_CONNECTED_UR
|
|
|
|
// in case of directly connected Ur signal from CJ125 to the ADC pin of MCU
|
|
|
|
return getVoltage("cj125ur", CONFIG(cj125ur));
|
|
|
|
#else
|
|
|
|
// if a standard voltage division scheme with OpAmp is used
|
|
|
|
return getVoltageDivided("cj125ur", CONFIG(cj125ur));
|
2018-11-03 08:44:57 -07:00
|
|
|
#endif /* EFI_CJ125_DIRECTLY_CONNECTED_UR */
|
|
|
|
#endif /* EFI_PROD_CODE */
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
return 0.0f;
|
2019-01-03 04:22:21 -08:00
|
|
|
#else /* EFI_UNIT_TEST */
|
|
|
|
return 0;
|
|
|
|
#endif /* EFI_UNIT_TEST */
|
2016-07-25 19:01:42 -07:00
|
|
|
}
|
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static float getUa() {
|
2019-01-03 04:22:21 -08:00
|
|
|
#if ! EFI_UNIT_TEST
|
2018-06-17 12:07:42 -07:00
|
|
|
if (CONFIG(cj125ua) != EFI_ADC_NONE) {
|
2018-11-03 08:44:57 -07:00
|
|
|
#if EFI_PROD_CODE
|
2018-06-17 12:07:42 -07:00
|
|
|
if (engineConfiguration->cj125isUaDivided) {
|
|
|
|
return getVoltageDivided("cj125ua", CONFIG(cj125ua));
|
|
|
|
} else {
|
|
|
|
return getVoltage("cj125ua", CONFIG(cj125ua));
|
|
|
|
}
|
2018-11-03 08:44:57 -07:00
|
|
|
#endif /* EFI_PROD_CODE */
|
2018-06-17 12:07:42 -07:00
|
|
|
}
|
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
return 0.0f;
|
2019-01-03 04:22:21 -08:00
|
|
|
#else /* EFI_UNIT_TEST */
|
|
|
|
return 0;
|
|
|
|
#endif /* EFI_UNIT_TEST */
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
2016-07-25 19:01:42 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static float getVoltageFrom16bit(uint32_t stored) {
|
|
|
|
return ((float)stored) / CJ125_VOLTAGE_TO_16BIT_FACTOR;
|
|
|
|
}
|
2016-07-30 19:02:52 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static uint32_t get16bitFromVoltage(float v) {
|
|
|
|
return (uint32_t)(v * CJ125_VOLTAGE_TO_16BIT_FACTOR);
|
|
|
|
}
|
2016-07-30 19:02:52 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static void cjPrintData(void) {
|
|
|
|
#ifdef CJ125_DEBUG
|
|
|
|
scheduleMsg(logger, "cj125: state=%d diag=0x%x (vUa=%.3f vUr=%.3f) (vUaCal=%.3f vUrCal=%.3f)", state, diag, vUa, vUr, vUaCal, vUrCal);
|
|
|
|
#endif
|
|
|
|
}
|
2016-07-30 19:02:52 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static void cjPrintErrorCode(cj125_error_e errCode) {
|
|
|
|
const char *errString = nullptr;
|
|
|
|
switch (errCode) {
|
|
|
|
case CJ125_ERROR_HEATER_MALFUNCTION:
|
|
|
|
errString = "Heater malfunction (Too long preheat)";
|
|
|
|
break;
|
|
|
|
case CJ125_ERROR_OVERHEAT:
|
|
|
|
errString = "Sensor overheating";
|
|
|
|
break;
|
|
|
|
case CJ125_ERROR_NONE:
|
|
|
|
errString = "N/A";
|
|
|
|
break;
|
2018-07-22 11:17:51 -07:00
|
|
|
case CJ125_ERROR_WRONG_IDENT:
|
|
|
|
errString = "W_IDENT";
|
|
|
|
break;
|
|
|
|
case CJ125_ERROR_WRONG_INIT:
|
|
|
|
errString = "W_INIT";
|
|
|
|
break;
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
scheduleMsg(logger, "cj125 ERROR: %s.", errString);
|
|
|
|
}
|
2016-07-30 19:02:52 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static void cjSetMode(cj125_mode_e m) {
|
|
|
|
if (mode == m)
|
|
|
|
return;
|
|
|
|
switch (m) {
|
|
|
|
case CJ125_MODE_NORMAL_8:
|
|
|
|
cjWriteRegister(INIT_REG1_WR, CJ125_INIT1_NORMAL_8);
|
|
|
|
amplCoeff = 1.0f / 8.0f;
|
|
|
|
break;
|
|
|
|
case CJ125_MODE_NORMAL_17:
|
|
|
|
cjWriteRegister(INIT_REG1_WR, CJ125_INIT1_NORMAL_17);
|
|
|
|
amplCoeff = 1.0f / 17.0f;
|
|
|
|
break;
|
|
|
|
case CJ125_MODE_CALIBRATION:
|
|
|
|
cjWriteRegister(INIT_REG1_WR, CJ125_INIT1_CALBRT);
|
|
|
|
amplCoeff = 0.0f;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
mode = m;
|
|
|
|
}
|
2016-07-30 19:02:52 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static void cjIdentify(void) {
|
|
|
|
// read Ident register
|
|
|
|
int ident = cjReadRegister(IDENT_REG_RD) & CJ125_IDENT_MASK;
|
|
|
|
|
|
|
|
// set initial registers
|
|
|
|
cjWriteRegister(INIT_REG1_WR, CJ125_INIT1_NORMAL_17);
|
|
|
|
cjWriteRegister(INIT_REG2_WR, CJ125_INIT2_DIAG);
|
|
|
|
// check if regs are ok
|
|
|
|
int init1 = cjReadRegister(INIT_REG1_RD);
|
|
|
|
int init2 = cjReadRegister(INIT_REG2_RD);
|
|
|
|
|
|
|
|
diag = cjReadRegister(DIAG_REG_RD);
|
|
|
|
scheduleMsg(logger, "cj125: Check ident=0x%x diag=0x%x init1=0x%x init2=0x%x", ident, diag, init1, init2);
|
|
|
|
if (ident != CJ125_IDENT) {
|
|
|
|
scheduleMsg(logger, "cj125: Error! Wrong ident! Cannot communicate with CJ125!");
|
|
|
|
}
|
|
|
|
if (init1 != CJ125_INIT1_NORMAL_17 || init2 != CJ125_INIT2_DIAG) {
|
|
|
|
scheduleMsg(logger, "cj125: Error! Cannot set init registers! Cannot communicate with CJ125!");
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
if (diag != CJ125_DIAG_NORM) {
|
|
|
|
scheduleMsg(logger, "cj125: Diag error!");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2016-07-30 19:02:52 -07:00
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
static void cjUpdateAnalogValues() {
|
|
|
|
#if EFI_PROD_CODE
|
|
|
|
// todo: some solution for testing
|
2018-01-29 11:26:42 -08:00
|
|
|
waitForSlowAdc(lastSlowAdcCounter);
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif
|
|
|
|
vUr = getUr();
|
2018-01-29 11:26:42 -08:00
|
|
|
vUa = getUa();
|
2019-01-03 04:22:21 -08:00
|
|
|
#if EFI_PROD_CODE
|
|
|
|
// todo: some solution for testing
|
2018-01-29 11:26:42 -08:00
|
|
|
lastSlowAdcCounter = getSlowAdcCounter();
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif
|
2016-07-25 19:01:42 -07:00
|
|
|
}
|
2016-06-24 16:02:45 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static void cjCalibrate(void) {
|
|
|
|
cjIdentify();
|
2016-07-17 00:01:48 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
scheduleMsg(logger, "cj125: Starting calibration...");
|
|
|
|
cjSetMode(CJ125_MODE_CALIBRATION);
|
|
|
|
int init1 = cjReadRegister(INIT_REG1_RD);
|
|
|
|
// check if our command has been accepted
|
|
|
|
if (init1 != CJ125_INIT1_CALBRT) {
|
|
|
|
scheduleMsg(logger, "cj125: Calibration error (init1=0x%02x)! Failed!", init1);
|
|
|
|
cjSetMode(CJ125_MODE_NORMAL_17);
|
2016-07-17 00:01:48 -07:00
|
|
|
return;
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
2019-01-03 04:22:21 -08:00
|
|
|
#if EFI_PROD_CODE
|
|
|
|
// todo: some testing solution
|
2018-01-29 11:26:42 -08:00
|
|
|
// wait for the start of the calibration
|
|
|
|
chThdSleepMilliseconds(CJ125_CALIBRATION_DELAY);
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif
|
2018-01-29 11:26:42 -08:00
|
|
|
vUaCal = 0.0f;
|
|
|
|
vUrCal = 0.0f;
|
|
|
|
// wait for some more ADC samples
|
|
|
|
for (int i = 0; i < CJ125_CALIBRATE_NUM_SAMPLES; i++) {
|
|
|
|
cjUpdateAnalogValues();
|
|
|
|
cjPrintData();
|
2018-06-23 14:10:59 -07:00
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
#if EFI_TUNER_STUDIO || defined(__DOXYGEN__)
|
2018-06-23 14:10:59 -07:00
|
|
|
if (engineConfiguration->debugMode == DBG_CJ125) {
|
|
|
|
cjPostState(&tsOutputChannels);
|
|
|
|
}
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif /* EFI_TUNER_STUDIO */
|
2018-06-23 14:10:59 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
vUaCal += vUa;
|
|
|
|
vUrCal += vUr;
|
|
|
|
}
|
|
|
|
// find average
|
|
|
|
vUaCal /= (float)CJ125_CALIBRATE_NUM_SAMPLES;
|
|
|
|
vUrCal /= (float)CJ125_CALIBRATE_NUM_SAMPLES;
|
|
|
|
// restore normal mode
|
|
|
|
cjSetMode(CJ125_MODE_NORMAL_17);
|
2019-01-03 04:22:21 -08:00
|
|
|
#if EFI_PROD_CODE
|
|
|
|
// todo: testing solution
|
2018-01-29 11:26:42 -08:00
|
|
|
chThdSleepMilliseconds(CJ125_CALIBRATION_DELAY);
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif
|
2018-01-29 11:26:42 -08:00
|
|
|
// check if everything is ok
|
|
|
|
diag = cjReadRegister(DIAG_REG_RD);
|
|
|
|
cjUpdateAnalogValues();
|
|
|
|
cjPrintData();
|
|
|
|
|
|
|
|
// store new calibration data
|
|
|
|
uint32_t storedLambda = get16bitFromVoltage(vUaCal);
|
|
|
|
uint32_t storedHeater = get16bitFromVoltage(vUrCal);
|
|
|
|
scheduleMsg(logger, "cj125: Done! Saving calibration data (%d %d).", storedLambda, storedHeater);
|
2018-11-03 10:17:58 -07:00
|
|
|
#if EFI_PROD_CODE
|
2018-01-29 11:26:42 -08:00
|
|
|
backupRamSave(BACKUP_CJ125_CALIBRATION_LAMBDA, storedLambda);
|
|
|
|
backupRamSave(BACKUP_CJ125_CALIBRATION_HEATER, storedHeater);
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif /* EFI_PROD_CODE */
|
2018-01-29 11:26:42 -08:00
|
|
|
|
2019-01-03 05:26:42 -08:00
|
|
|
globalInstance.state = CJ125_IDLE;
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
static void cjStart(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2018-01-29 11:26:42 -08:00
|
|
|
if (!boardConfiguration->isCJ125Enabled) {
|
|
|
|
scheduleMsg(logger, "cj125 is disabled.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cjIdentify();
|
|
|
|
|
|
|
|
// Load calibration values
|
2018-11-03 10:45:36 -07:00
|
|
|
#if EFI_PROD_CODE
|
2018-01-29 11:26:42 -08:00
|
|
|
uint32_t storedLambda = backupRamLoad(BACKUP_CJ125_CALIBRATION_LAMBDA);
|
|
|
|
uint32_t storedHeater = backupRamLoad(BACKUP_CJ125_CALIBRATION_HEATER);
|
2018-11-03 10:45:36 -07:00
|
|
|
#else
|
|
|
|
uint32_t storedLambda = 0;
|
|
|
|
uint32_t storedHeater = 0;
|
|
|
|
#endif
|
2018-01-29 11:26:42 -08:00
|
|
|
// if no calibration, try to calibrate now and store new values
|
|
|
|
if (storedLambda == 0 || storedHeater == 0) {
|
|
|
|
cjCalibrate();
|
|
|
|
} else {
|
|
|
|
scheduleMsg(logger, "cj125: Loading stored calibration data (%d %d)", storedLambda, storedHeater);
|
|
|
|
vUaCal = getVoltageFrom16bit(storedLambda);
|
|
|
|
vUrCal = getVoltageFrom16bit(storedHeater);
|
|
|
|
// Start normal measurement mode
|
|
|
|
cjSetMode(CJ125_MODE_NORMAL_17);
|
|
|
|
}
|
|
|
|
cjPrintData();
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
#if EFI_PROD_CODE
|
|
|
|
// todo: testig solution
|
2018-01-29 11:26:42 -08:00
|
|
|
lastSlowAdcCounter = getSlowAdcCounter();
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
static void cjSetHeater(float value DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2018-01-29 11:26:42 -08:00
|
|
|
// limit duty cycle for sensor safety
|
|
|
|
float maxDuty = (engine->sensors.vBatt > CJ125_HEATER_LIMITING_VOLTAGE) ? CJ125_HEATER_LIMITING_RATE : 1.0f;
|
|
|
|
heaterDuty = (value < CJ125_HEATER_MIN_DUTY) ? 0.0f : minF(maxF(value, 0.0f), maxDuty);
|
|
|
|
#ifdef CJ125_DEBUG
|
|
|
|
scheduleMsg(logger, "cjSetHeater: %.2f", heaterDuty);
|
|
|
|
#endif
|
|
|
|
// a little trick to disable PWM if needed.
|
|
|
|
// todo: this should be moved to wboHeaterControl.setPwmDutyCycle()
|
|
|
|
wboHeaterControl.setFrequency(heaterDuty == 0.0f ? NAN : CJ125_HEATER_PWM_FREQ);
|
|
|
|
wboHeaterControl.setSimplePwmDutyCycle(heaterDuty);
|
|
|
|
// This fixes pwm sticking to the last pin state
|
|
|
|
if (heaterDuty == 0.0f)
|
|
|
|
wboHeaterPin.setValue(false);
|
|
|
|
}
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
static void cjSetIdleHeater(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2018-01-29 11:26:42 -08:00
|
|
|
// small preheat for faster start & moisture anti-shock therapy for the sensor
|
2019-01-03 04:22:21 -08:00
|
|
|
cjSetHeater(CJ125_HEATER_IDLE_RATE PASS_ENGINE_PARAMETER_SUFFIX);
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
2016-07-17 00:01:48 -07:00
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
static void cjStartHeaterControl(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2018-01-29 11:26:42 -08:00
|
|
|
if (boardConfiguration->wboHeaterPin != GPIO_UNASSIGNED) {
|
|
|
|
scheduleMsg(logger, "cj125: Starting heater control");
|
|
|
|
// todo: use custom pin state method, turn pin off while not running
|
2019-01-09 05:50:51 -08:00
|
|
|
startSimplePwmExt(&wboHeaterControl, "wboHeaterPin",
|
|
|
|
&engine->executor,
|
|
|
|
boardConfiguration->wboHeaterPin,
|
2018-01-29 11:26:42 -08:00
|
|
|
&wboHeaterPin, CJ125_HEATER_PWM_FREQ, 0.0f, applyPinState);
|
2019-01-03 04:22:21 -08:00
|
|
|
cjSetIdleHeater(PASS_ENGINE_PARAMETER_SIGNATURE);
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 05:26:42 -08:00
|
|
|
void CJ125::cjSetError(cj125_error_e errCode DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2018-01-29 11:26:42 -08:00
|
|
|
errorCode = errCode;
|
|
|
|
state = CJ125_ERROR;
|
|
|
|
cjPrintErrorCode(errorCode);
|
|
|
|
// This is for safety:
|
|
|
|
scheduleMsg(logger, "cj125: Controller Shutdown!");
|
2019-01-03 04:22:21 -08:00
|
|
|
cjSetHeater(0 PASS_ENGINE_PARAMETER_SUFFIX);
|
2018-01-29 11:26:42 -08:00
|
|
|
// Software-reset of CJ125
|
|
|
|
cjWriteRegister(INIT_REG2_WR, CJ125_INIT2_RESET);
|
|
|
|
}
|
|
|
|
|
2019-01-03 05:26:42 -08:00
|
|
|
bool CJ125::cjIsWorkingState(void) {
|
2018-01-29 11:26:42 -08:00
|
|
|
return state != CJ125_ERROR && state != CJ125_INIT && state != CJ125_IDLE;
|
|
|
|
}
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
static void cjInitPid(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2018-08-08 17:42:55 -07:00
|
|
|
if(engineConfiguration->cj125isLsu49) {
|
|
|
|
heaterPidConfig.pFactor = CJ125_PID_LSU49_P;
|
|
|
|
heaterPidConfig.iFactor = CJ125_PID_LSU49_I;
|
|
|
|
} else {
|
|
|
|
heaterPidConfig.pFactor = CJ125_PID_LSU42_P;
|
|
|
|
heaterPidConfig.iFactor = CJ125_PID_LSU42_I;
|
|
|
|
}
|
2018-01-29 11:26:42 -08:00
|
|
|
heaterPidConfig.dFactor = 0.0f;
|
|
|
|
heaterPidConfig.minValue = 0;
|
|
|
|
heaterPidConfig.maxValue = 1;
|
|
|
|
heaterPidConfig.offset = 0;
|
|
|
|
// todo: period?
|
|
|
|
heaterPidConfig.period = 1.0f;
|
|
|
|
heaterPid.reset();
|
|
|
|
}
|
|
|
|
|
2018-06-17 12:07:42 -07:00
|
|
|
// engineConfiguration->spi2SckMode = PAL_STM32_OTYPE_OPENDRAIN; // 4
|
|
|
|
// engineConfiguration->spi2MosiMode = PAL_STM32_OTYPE_OPENDRAIN; // 4
|
|
|
|
// engineConfiguration->spi2MisoMode = PAL_STM32_PUDR_PULLUP; // 32
|
|
|
|
// boardConfiguration->cj125CsPin = GPIOA_15;
|
|
|
|
// engineConfiguration->cj125CsPinMode = OM_OPENDRAIN;
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
void cj125defaultPinout(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2018-06-17 16:14:46 -07:00
|
|
|
engineConfiguration->cj125ua = EFI_ADC_13; // PC3
|
|
|
|
engineConfiguration->cj125ur = EFI_ADC_4; // PA4
|
2018-06-17 12:07:42 -07:00
|
|
|
boardConfiguration->wboHeaterPin = GPIOC_13;
|
|
|
|
|
|
|
|
boardConfiguration->isCJ125Enabled = false;
|
|
|
|
|
|
|
|
boardConfiguration->spi2mosiPin = GPIOB_15;
|
|
|
|
boardConfiguration->spi2misoPin = GPIOB_14;
|
|
|
|
boardConfiguration->spi2sckPin = GPIOB_13;
|
|
|
|
|
|
|
|
boardConfiguration->cj125CsPin = GPIOB_0;
|
|
|
|
boardConfiguration->isCJ125Enabled = true;
|
|
|
|
boardConfiguration->is_enabled_spi_2 = true;
|
|
|
|
}
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
static void cjStartSpi(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2017-04-21 15:11:36 -07:00
|
|
|
cj125Cs.initPin("cj125 CS", boardConfiguration->cj125CsPin,
|
2016-07-17 00:01:48 -07:00
|
|
|
&engineConfiguration->cj125CsPinMode);
|
|
|
|
|
2018-11-03 08:44:57 -07:00
|
|
|
#if EFI_PROD_CODE
|
|
|
|
cj125spicfg.ssport = getHwPort("cj125", boardConfiguration->cj125CsPin);
|
|
|
|
cj125spicfg.sspad = getHwPin("cj125", boardConfiguration->cj125CsPin);
|
|
|
|
driver = getSpiDevice(engineConfiguration->cj125SpiDevice);
|
2018-01-29 11:26:42 -08:00
|
|
|
scheduleMsg(logger, "cj125: Starting SPI driver");
|
|
|
|
spiStart(driver, &cj125spicfg);
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif /* EFI_PROD_CODE */
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
2016-07-17 00:01:48 -07:00
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
/**
|
|
|
|
* @return true if currently in IDLE or ERROR state
|
|
|
|
*/
|
2019-01-03 05:26:42 -08:00
|
|
|
static bool cj125periodic(CJ125 *instance, CJ125_state *anotherState DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2019-01-03 04:22:21 -08:00
|
|
|
{
|
|
|
|
efitick_t nowNt = getTimeNowNt();
|
2018-01-29 11:26:42 -08:00
|
|
|
bool isStopped = engine->rpmCalculator.isStopped(PASS_ENGINE_PARAMETER_SIGNATURE);
|
|
|
|
|
|
|
|
cjUpdateAnalogValues();
|
|
|
|
|
|
|
|
// If the controller is disabled
|
2019-01-03 05:26:42 -08:00
|
|
|
if (instance->state == CJ125_IDLE || instance->state == CJ125_ERROR) {
|
2019-01-03 04:22:21 -08:00
|
|
|
return true;
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
|
2019-01-03 05:26:42 -08:00
|
|
|
if (instance->state == CJ125_CALIBRATION) {
|
2018-01-29 11:26:42 -08:00
|
|
|
cjCalibrate();
|
|
|
|
// Start normal operation
|
2019-01-03 05:26:42 -08:00
|
|
|
instance->state = CJ125_INIT;
|
2018-01-29 11:26:42 -08:00
|
|
|
cjSetMode(CJ125_MODE_NORMAL_17);
|
|
|
|
}
|
|
|
|
|
|
|
|
diag = cjReadRegister(DIAG_REG_RD);
|
|
|
|
|
|
|
|
// check heater state
|
|
|
|
if (vUr > CJ125_UR_PREHEAT_THR || heaterDuty < CJ125_PREHEAT_MIN_DUTY) {
|
|
|
|
// Check if RPM>0 and it's time to start pre-heating
|
2019-01-03 05:26:42 -08:00
|
|
|
if (instance->state == CJ125_INIT && !isStopped) {
|
2018-01-29 11:26:42 -08:00
|
|
|
// start preheating
|
2019-01-03 05:26:42 -08:00
|
|
|
instance->state = CJ125_PREHEAT;
|
2019-01-03 04:22:21 -08:00
|
|
|
anotherState->startHeatingNt = anotherState->prevNt = getTimeNowNt();
|
2018-01-29 11:26:42 -08:00
|
|
|
cjSetMode(CJ125_MODE_NORMAL_17);
|
|
|
|
}
|
|
|
|
} else if (vUr > CJ125_UR_GOOD_THR) {
|
2019-01-03 05:26:42 -08:00
|
|
|
instance->state = CJ125_HEAT_UP;
|
2018-01-29 11:26:42 -08:00
|
|
|
} else if (vUr < CJ125_UR_OVERHEAT_THR) {
|
2019-01-03 05:26:42 -08:00
|
|
|
instance->state = CJ125_OVERHEAT;
|
2018-01-29 11:26:42 -08:00
|
|
|
} else {
|
|
|
|
// This indicates that the heater temperature is optimal for UA measurement
|
2019-01-03 05:26:42 -08:00
|
|
|
instance->state = CJ125_READY;
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
|
2019-01-03 05:26:42 -08:00
|
|
|
if (isStopped && instance->cjIsWorkingState()) {
|
|
|
|
instance->state = CJ125_INIT;
|
2019-01-03 04:22:21 -08:00
|
|
|
cjSetIdleHeater(PASS_ENGINE_PARAMETER_SIGNATURE);
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// Change amplification if AFR gets lean/rich for better accuracy
|
|
|
|
cjSetMode(lambda > 1.0f ? CJ125_MODE_NORMAL_17 : CJ125_MODE_NORMAL_8);
|
|
|
|
#endif
|
|
|
|
|
2019-01-03 05:26:42 -08:00
|
|
|
switch (instance->state) {
|
2018-01-29 11:26:42 -08:00
|
|
|
case CJ125_PREHEAT:
|
|
|
|
// use constant-speed startup heat-up
|
2019-01-03 04:22:21 -08:00
|
|
|
if (nowNt - anotherState->prevNt >= CJ125_HEATER_PREHEAT_PERIOD) {
|
|
|
|
float periodSecs = (float)(nowNt - anotherState->prevNt) / US2NT(US_PER_SECOND_LL);
|
2018-01-29 11:26:42 -08:00
|
|
|
// maintain speed at ~0.4V/sec
|
|
|
|
float preheatDuty = heaterDuty + periodSecs * CJ125_HEATER_PREHEAT_RATE;
|
2019-01-03 04:22:21 -08:00
|
|
|
cjSetHeater(preheatDuty PASS_ENGINE_PARAMETER_SUFFIX);
|
2018-01-29 11:26:42 -08:00
|
|
|
// If we are heating too long, and there's still no result, then something is wrong...
|
2019-01-03 04:22:21 -08:00
|
|
|
if (nowNt - anotherState->startHeatingNt > US2NT(US_PER_SECOND_LL) * CJ125_PREHEAT_TIMEOUT) {
|
2019-01-03 05:26:42 -08:00
|
|
|
instance->cjSetError(CJ125_ERROR_HEATER_MALFUNCTION PASS_ENGINE_PARAMETER_SUFFIX);
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
cjPrintData();
|
2019-01-03 04:22:21 -08:00
|
|
|
anotherState->prevNt = nowNt;
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CJ125_HEAT_UP:
|
|
|
|
case CJ125_READY:
|
|
|
|
// use PID for normal heater control
|
2019-01-03 04:22:21 -08:00
|
|
|
if (nowNt - anotherState->prevNt >= CJ125_HEATER_CONTROL_PERIOD) {
|
2018-05-27 17:47:40 -07:00
|
|
|
/* PID doesn't care about the target or the input, it knows only the
|
|
|
|
* error value as the difference of (target - input). and if we swap them we'll just get a sign inversion. If target=vUrCal, and input=vUr, then error=vUrCal-vUr, i.e. if vUr<vUrCal then the error will cause the heater to increase it's duty cycle. But it's not exactly what we want! Lesser vUr means HOTTER cell. That's why we even have this safety check for overheating: (vUr < CJ125_UR_OVERHEAT_THR)...
|
|
|
|
* So the simple trick is to inverse the error by swapping the target and input values.
|
|
|
|
*/
|
2018-01-29 11:26:42 -08:00
|
|
|
float duty = heaterPid.getValue(vUr, vUrCal);
|
|
|
|
heaterPid.showPidStatus(logger, "cj");
|
2019-01-03 04:22:21 -08:00
|
|
|
cjSetHeater(duty PASS_ENGINE_PARAMETER_SUFFIX);
|
2018-01-29 11:26:42 -08:00
|
|
|
cjPrintData();
|
2019-01-03 04:22:21 -08:00
|
|
|
anotherState->prevNt = nowNt;
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CJ125_OVERHEAT:
|
2019-01-03 04:22:21 -08:00
|
|
|
if (nowNt - anotherState->prevNt >= CJ125_HEATER_OVERHEAT_PERIOD) {
|
2019-01-03 05:26:42 -08:00
|
|
|
instance->cjSetError(CJ125_ERROR_OVERHEAT PASS_ENGINE_PARAMETER_SUFFIX);
|
2019-01-03 04:22:21 -08:00
|
|
|
anotherState->prevNt = nowNt;
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
;
|
|
|
|
}
|
2019-01-03 04:22:21 -08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
|
2018-01-29 11:26:42 -08:00
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
static CJ125_state globalStateInstance;
|
|
|
|
|
|
|
|
static msg_t cjThread(void)
|
|
|
|
{
|
|
|
|
chRegSetThreadName("cj125");
|
|
|
|
|
|
|
|
chThdSleepMilliseconds(500);
|
|
|
|
|
|
|
|
globalStateInstance.startHeatingNt = 0;
|
|
|
|
globalStateInstance.prevNt = getTimeNowNt();
|
|
|
|
while (1) {
|
2019-01-03 05:26:42 -08:00
|
|
|
bool needIdleSleep = cj125periodic(&globalInstance, &globalStateInstance PASS_ENGINE_PARAMETER_SUFFIX);
|
2019-01-03 04:22:21 -08:00
|
|
|
chThdSleepMilliseconds(needIdleSleep ? CJ125_IDLE_TICK_DELAY : CJ125_TICK_DELAY);
|
2016-07-17 00:01:48 -07:00
|
|
|
}
|
2018-01-29 11:26:42 -08:00
|
|
|
return -1;
|
|
|
|
}
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif /* EFI_UNIT_TEST */
|
2016-07-25 19:01:42 -07:00
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
|
2018-01-29 11:26:42 -08:00
|
|
|
static bool cjCheckConfig(void) {
|
|
|
|
if (!boardConfiguration->isCJ125Enabled) {
|
|
|
|
scheduleMsg(logger, "cj125 is disabled. Failed!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2016-07-26 19:01:50 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static void cjStartCalibration(void) {
|
|
|
|
if (!cjCheckConfig())
|
|
|
|
return;
|
2019-01-03 05:26:42 -08:00
|
|
|
if (globalInstance.cjIsWorkingState()) {
|
2018-01-29 11:26:42 -08:00
|
|
|
// todo: change this later for the normal thread operation (auto pre-heating)
|
|
|
|
scheduleMsg(logger, "cj125: Cannot start calibration. Please restart the board and make sure that your sensor is not heating");
|
|
|
|
return;
|
|
|
|
}
|
2019-01-03 05:26:42 -08:00
|
|
|
globalInstance.state = CJ125_CALIBRATION;
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
2016-07-25 19:01:42 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static void cjStartTest(void) {
|
|
|
|
if (!cjCheckConfig())
|
|
|
|
return;
|
2019-01-03 05:26:42 -08:00
|
|
|
globalInstance.state = CJ125_INIT;
|
2018-01-29 11:26:42 -08:00
|
|
|
}
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif /* EFI_UNIT_TEST */
|
|
|
|
|
2016-07-25 19:01:42 -07:00
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
#ifdef CJ125_DEBUG
|
|
|
|
static void cjSetInit1(int v) {
|
|
|
|
cjWriteRegister(INIT_REG1_WR, v & 0xff);
|
|
|
|
v = cjReadRegister(INIT_REG1_RD);
|
|
|
|
scheduleMsg(logger, "cj125 INIT_REG1=0x%02x.", v);
|
2016-06-24 16:02:45 -07:00
|
|
|
}
|
|
|
|
|
2018-01-29 11:26:42 -08:00
|
|
|
static void cjSetInit2(int v) {
|
|
|
|
cjWriteRegister(INIT_REG2_WR, v & 0xff);
|
|
|
|
v = cjReadRegister(INIT_REG2_RD);
|
|
|
|
scheduleMsg(logger, "cj125 INIT_REG2=0x%02x.", v);
|
2018-01-28 10:04:19 -08:00
|
|
|
}
|
2018-01-29 11:26:42 -08:00
|
|
|
#endif /* CJ125_DEBUG */
|
2018-01-28 10:04:19 -08:00
|
|
|
|
|
|
|
float cjGetAfr(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2018-08-08 17:42:55 -07:00
|
|
|
cj125_sensor_type_e sensorType;
|
|
|
|
if (engineConfiguration->cj125isLsu49) {
|
|
|
|
sensorType = CJ125_LSU_49;
|
|
|
|
} else {
|
|
|
|
sensorType = CJ125_LSU_42;
|
|
|
|
}
|
2018-01-29 11:26:42 -08:00
|
|
|
|
|
|
|
// See CJ125 datasheet, page 6
|
|
|
|
float pumpCurrent = (vUa - vUaCal) * amplCoeff * (CJ125_PUMP_CURRENT_FACTOR / CJ125_PUMP_SHUNT_RESISTOR);
|
|
|
|
lambda = interpolate2d("cj125Lsu", pumpCurrent, (float *)cjLSUBins[sensorType], (float *)cjLSULambda[sensorType], cjLSUTableSize[sensorType]);
|
|
|
|
// todo: make configurable stoich ratio
|
|
|
|
return lambda * CJ125_STOICH_RATIO;
|
2018-01-28 10:04:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cjHasAfrSensor(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2018-01-29 11:26:42 -08:00
|
|
|
if (!boardConfiguration->isCJ125Enabled)
|
|
|
|
return false;
|
2019-01-03 05:26:42 -08:00
|
|
|
#if ! EFI_UNIT_TEST
|
2018-01-29 11:26:42 -08:00
|
|
|
// check if controller is functioning
|
2019-01-03 05:26:42 -08:00
|
|
|
if (!globalInstance.cjIsWorkingState())
|
2018-01-29 11:26:42 -08:00
|
|
|
return false;
|
2019-01-03 05:26:42 -08:00
|
|
|
#endif /* EFI_UNIT_TEST */
|
2018-01-29 11:26:42 -08:00
|
|
|
// check if amplification is turned on
|
|
|
|
if (amplCoeff == 0.0f)
|
|
|
|
return false;
|
|
|
|
// check if UA calibration value is valid
|
|
|
|
if (vUaCal < CJ125_UACAL_MIN || vUaCal > CJ125_UACAL_MAX)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-03 10:27:48 -07:00
|
|
|
#if EFI_TUNER_STUDIO || defined(__DOXYGEN__)
|
2018-06-17 16:14:46 -07:00
|
|
|
// used by DBG_CJ125
|
2018-01-29 11:26:42 -08:00
|
|
|
void cjPostState(TunerStudioOutputChannels *tsOutputChannels) {
|
|
|
|
tsOutputChannels->debugFloatField1 = heaterDuty;
|
|
|
|
tsOutputChannels->debugFloatField2 = heaterPid.getIntegration();
|
|
|
|
tsOutputChannels->debugFloatField3 = heaterPid.getPrevError();
|
|
|
|
tsOutputChannels->debugFloatField4 = vUa;
|
|
|
|
tsOutputChannels->debugFloatField5 = vUr;
|
|
|
|
tsOutputChannels->debugFloatField6 = vUaCal;
|
|
|
|
tsOutputChannels->debugFloatField7 = vUrCal;
|
2019-01-03 05:26:42 -08:00
|
|
|
tsOutputChannels->debugIntField1 = globalInstance.state;
|
2018-01-29 11:26:42 -08:00
|
|
|
tsOutputChannels->debugIntField2 = diag;
|
|
|
|
}
|
2018-11-03 10:27:48 -07:00
|
|
|
#endif /* EFI_TUNER_STUDIO */
|
2018-01-29 11:26:42 -08:00
|
|
|
|
2018-11-03 10:38:02 -07:00
|
|
|
void initCJ125(Logging *sharedLogger DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2018-01-29 11:26:42 -08:00
|
|
|
logger = sharedLogger;
|
|
|
|
|
|
|
|
if (!boardConfiguration->isCJ125Enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (CONFIG(cj125ur) == EFI_ADC_NONE || CONFIG(cj125ua) == EFI_ADC_NONE) {
|
|
|
|
scheduleMsg(logger, "cj125 init error! cj125ur and cj125ua are required.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (boardConfiguration->wboHeaterPin == GPIO_UNASSIGNED) {
|
|
|
|
scheduleMsg(logger, "cj125 init error! wboHeaterPin is required.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
cjInitPid(PASS_ENGINE_PARAMETER_SIGNATURE);
|
|
|
|
cjStartSpi(PASS_ENGINE_PARAMETER_SIGNATURE);
|
|
|
|
cjStartHeaterControl(PASS_ENGINE_PARAMETER_SIGNATURE);
|
|
|
|
cjStart(PASS_ENGINE_PARAMETER_SIGNATURE);
|
2018-01-29 11:26:42 -08:00
|
|
|
|
|
|
|
#if 1
|
2019-01-03 05:26:42 -08:00
|
|
|
globalInstance.state = CJ125_INIT;
|
2018-01-29 11:26:42 -08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CJ125_DEBUG
|
|
|
|
addConsoleActionF("cj125_heater", cjSetHeater);
|
|
|
|
addConsoleActionI("cj125_set_init1", cjSetInit1);
|
|
|
|
addConsoleActionI("cj125_set_init2", cjSetInit2);
|
|
|
|
#endif /* CJ125_DEBUG */
|
|
|
|
|
2019-01-03 04:22:21 -08:00
|
|
|
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
|
|
|
|
addConsoleAction("cj125", cjStartTest);
|
|
|
|
addConsoleAction("cj125_calibrate", cjStartCalibration);
|
|
|
|
|
2018-12-27 06:40:40 -08:00
|
|
|
chThdCreateStatic(cjThreadStack, sizeof(cjThreadStack), LOWPRIO, (tfunc_t)(void*) cjThread, NULL);
|
2019-01-03 04:22:21 -08:00
|
|
|
#endif /* EFI_UNIT_TEST */
|
2018-01-28 10:04:19 -08:00
|
|
|
}
|
|
|
|
|
2016-07-25 20:03:45 -07:00
|
|
|
#endif /* EFI_CJ125 */
|