rusefi-1/firmware/hw_layer/sensors/cj125.cpp

668 lines
19 KiB
C++
Raw Normal View History

2016-06-24 16:02:45 -07:00
/*
* @file CJ125.cpp
*
2020-04-08 20:14:21 -07:00
* https://github.com/rusefi/rusefi/tree/master/hardware/CJ125_board
*
* https://github.com/rusefi/hw_modular/tree/master/cj125_Module
*
2020-04-30 19:30:37 -07:00
*
* See vag_18_Turbo for test configuration
* set engine_type 102
*
2016-06-24 16:02:45 -07:00
* @date: Jun 24, 2016
2020-01-13 18:57:43 -08:00
* @author Andrey Belomutskiy, (c) 2012-2020
2016-06-24 16:02:45 -07:00
*
*/
#include "pch.h"
#include "cj125.h"
2016-07-17 00:01:48 -07:00
2020-04-13 10:12:55 -07:00
#if EFI_CJ125
2016-07-25 20:03:45 -07:00
2020-04-13 10:12:55 -07:00
#if HAL_USE_SPI
#include "mpu_util.h"
2020-04-13 10:12:55 -07:00
#endif /* HAL_USE_SPI */
//#define CJ125_DEBUG
//#define CJ125_DEBUG_SPI
2016-08-01 19:04:24 -07:00
2019-01-03 04:22:21 -08:00
#include "hardware.h"
#include "backup_ram.h"
2019-01-03 05:26:42 -08:00
static CJ125 globalInstance;
2020-04-13 10:12:55 -07:00
#if ! EFI_UNIT_TEST
2019-07-13 07:32:36 -07:00
static THD_WORKING_AREA(cj125ThreadStack, UTILITY_THREAD_STACK_SIZE);
2020-04-13 10:12:55 -07:00
#endif /* EFI_UNIT_TEST */
#if HAL_USE_SPI
2016-07-25 19:01:42 -07:00
2016-07-17 00:01:48 -07:00
static SPIDriver *driver;
static SPIConfig cj125spicfg = {
.circular = false,
.end_cb = NULL,
.ssport = NULL,
.sspad = 0,
.cr1 =
SPI_CR1_MSTR | SPI_CR1_CPHA |
SPI_CR1_8BIT_MODE,
.cr2 =
SPI_CR2_8BIT_MODE
};
2020-04-13 10:12:55 -07:00
#endif /* HAL_USE_SPI */
2021-03-15 07:23:19 -07:00
static uint32_t lastSlowAdcCounter = 0;
// 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
// Pump current, mA
static constexpr float pumpCurrentLsu42[] = {
// LSU 4.2
-1.85f, -1.08f, -0.76f, -0.47f, 0.0f, 0.34f, 0.68f, 0.95f, 1.4f
};
static constexpr float pumpCurrentLsu49[] = {
// LSU 4.9
-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
};
// Corresponding lambda values for the above pump current
static constexpr float lambdaLsu42[] = {
// LSU 4.2
0.7f, 0.8f, 0.85f, 0.9f, 1.009f, 1.18f, 1.43f, 1.7f, 2.42f
};
static constexpr float lambdaLsu49[] = {
// LSU 4.9
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
};
2019-01-03 04:22:21 -08:00
static uint8_t cjReadRegister(uint8_t regAddr) {
2019-04-12 17:52:51 -07:00
#if ! EFI_UNIT_TEST
spiSelect(driver);
spiPolledExchange(driver, regAddr);
uint8_t result = spiPolledExchange(driver, 0xFF);
spiUnselect(driver);
2016-07-25 19:01:42 -07:00
#ifdef CJ125_DEBUG_SPI
efiPrintf("cjReadRegister: addr=%d answer=%d", regAddr, result);
2019-01-03 04:22:21 -08:00
#endif /* CJ125_DEBUG_SPI */
return result;
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
static void cjWriteRegister(uint8_t regAddr, uint8_t regValue) {
#ifdef CJ125_DEBUG_SPI
efiPrintf("cjWriteRegister: addr=%d value=%d", regAddr, regValue);
2019-01-03 04:22:21 -08:00
#endif /* CJ125_DEBUG_SPI */
2019-03-25 17:30:36 -07:00
// todo: extract 'sendSync' method?
2020-04-13 10:12:55 -07:00
#if HAL_USE_SPI
spiSelect(driver);
spiPolledExchange(driver, regAddr);
spiPolledExchange(driver, regValue);
spiUnselect(driver);
2020-04-13 10:12:55 -07:00
#endif /* HAL_USE_SPI */
}
2016-07-25 19:01:42 -07:00
static float getUr() {
2019-01-03 04:22:21 -08:00
#if ! EFI_UNIT_TEST
if (isAdcChannelValid(engineConfiguration->cj125ur)) {
2018-11-03 08:44:57 -07:00
#if EFI_PROD_CODE
if (!engineConfiguration->cj125isUrDivided) {
// in case of directly connected Ur signal from CJ125 to the ADC pin of MCU
return getVoltage("cj125ur", engineConfiguration->cj125ur);
} else {
// if a standard voltage division scheme with OpAmp is used
return getVoltageDivided("cj125ur", engineConfiguration->cj125ur);
}
2018-11-03 08:44:57 -07:00
#endif /* EFI_PROD_CODE */
}
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
}
static float getUa() {
2019-01-03 04:22:21 -08:00
#if ! EFI_UNIT_TEST
if (isAdcChannelValid(engineConfiguration->cj125ua)) {
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", engineConfiguration->cj125ua);
2018-06-17 12:07:42 -07:00
} else {
return getVoltage("cj125ua", engineConfiguration->cj125ua);
2018-06-17 12:07:42 -07:00
}
2018-11-03 08:44:57 -07:00
#endif /* EFI_PROD_CODE */
2018-06-17 12:07:42 -07: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
static float getVoltageFrom16bit(uint32_t stored) {
return ((float)stored) / CJ125_VOLTAGE_TO_16BIT_FACTOR;
}
2016-07-30 19:02:52 -07:00
static uint32_t get16bitFromVoltage(float v) {
return (uint32_t)(v * CJ125_VOLTAGE_TO_16BIT_FACTOR);
}
2016-07-30 19:02:52 -07:00
2020-05-01 15:52:06 -07:00
static const char * getCjState(cj125_state_e stateCode) {
switch (stateCode) {
case CJ125_INIT:
return "INIT";
case CJ125_IDLE:
return "IDLE";
case CJ125_CALIBRATION:
return "CALIBRATION";
case CJ125_PREHEAT:
return "PREHEAT";
case CJ125_HEAT_UP:
return "HEAT UP";
case CJ125_READY:
return "READY";
case CJ125_OVERHEAT:
return "OVERHEAT";
case CJ125_ERROR:
return "ERROR";
default:
return "UNKNOWN";
2020-04-08 20:14:21 -07:00
}
}
2016-07-30 19:02:52 -07: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;
2019-12-01 22:52:54 -08:00
case CJ125_NO_ERROR:
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;
2019-12-02 06:59:53 -08:00
case CJ125_ERROR_DISABLED:
errString = "DISABLED";
break;
}
efiPrintf("cj125 ERROR: %s.", errString);
}
2016-07-30 19:02:52 -07:00
2020-05-01 15:52:06 -07:00
static void cjPrintState() {
efiPrintf("cj125: state=%s diag=0x%x (current Ua=%.3f Ur=%.3f) (calibration Ua=%.3f Ur=%.3f)",
2020-05-01 15:52:06 -07:00
getCjState(globalInstance.state), globalInstance.diag,
globalInstance.vUa, globalInstance.vUr,
globalInstance.vUaCal, globalInstance.vUrCal);
globalInstance.printDiag();
if (globalInstance.state == CJ125_ERROR) {
cjPrintErrorCode(globalInstance.errorCode);
}
efiPrintf("cj125 P=%f I=%f D=%f",
2020-05-01 15:52:06 -07:00
globalInstance.heaterPidConfig.pFactor,
globalInstance.heaterPidConfig.iFactor,
globalInstance.heaterPidConfig.dFactor);
}
2020-05-01 17:22:49 -07:00
static void cjSetP(float value) {
globalInstance.heaterPidConfig.pFactor = value;
}
static void cjSetI(float value) {
globalInstance.heaterPidConfig.iFactor = value;
}
2020-05-01 15:52:06 -07:00
static void cjInfo() {
cjPrintState();
#if HAL_USE_SPI
printSpiConfig("cj125", engineConfiguration->cj125SpiDevice);
2020-05-01 15:52:06 -07:00
#endif /* HAL_USE_SPI */
}
static void cjPrintData() {
#if ! EFI_UNIT_TEST
if (engineConfiguration->isCJ125Verbose) {
cjPrintState();
}
#endif /* EFI_UNIT_TEST */
}
2019-02-01 22:06:45 -08:00
class RealSpi : public Cj125SpiStream {
public:
uint8_t ReadRegister(uint8_t reg) override {
return cjReadRegister(reg);
}
2019-02-01 22:06:45 -08:00
void WriteRegister(uint8_t regAddr, uint8_t regValue) {
cjWriteRegister(regAddr, regValue);
}
2019-02-01 22:06:45 -08:00
};
static RealSpi spi;
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
waitForSlowAdc(lastSlowAdcCounter);
2020-03-24 16:14:16 -07:00
#endif /* EFI_PROD_CODE */
2019-02-01 22:45:26 -08:00
globalInstance.vUr = getUr();
globalInstance.vUa = getUa();
2019-01-03 04:22:21 -08:00
#if EFI_PROD_CODE
// todo: some solution for testing
lastSlowAdcCounter = getSlowAdcCounter();
2020-03-24 16:14:16 -07:00
#endif /* EFI_PROD_CODE */
2016-07-25 19:01:42 -07:00
}
2016-06-24 16:02:45 -07:00
void CJ125::calibrate() {
cjIdentify();
2016-07-17 00:01:48 -07:00
efiPrintf("cj125: Starting calibration...");
2020-04-08 18:23:49 -07:00
cjSetMode(CJ125_MODE_CALIBRATION);
int init1 = cjReadRegister(INIT_REG1_RD);
// check if our command has been accepted
if (init1 != CJ125_INIT1_CALBRT) {
efiPrintf("cj125: Calibration error (init1=0x%02x)! Failed!", init1);
2020-04-08 18:23:49 -07:00
cjSetMode(CJ125_MODE_NORMAL_17);
2016-07-17 00:01:48 -07:00
return;
}
2019-01-03 04:22:21 -08:00
#if EFI_PROD_CODE
// todo: some testing solution
// wait for the start of the calibration
chThdSleepMilliseconds(CJ125_CALIBRATION_DELAY);
2020-03-24 16:14:16 -07:00
#endif /* EFI_PROD_CODE */
2020-04-08 18:23:49 -07: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();
2019-04-12 17:52:51 -07:00
#if EFI_TUNER_STUDIO
if (engineConfiguration->debugMode == DBG_CJ125) {
cjPostState(&engine->outputChannels);
}
2019-01-03 04:22:21 -08:00
#endif /* EFI_TUNER_STUDIO */
2020-04-08 18:23:49 -07:00
vUaCal += vUa;
vUrCal += vUr;
}
// find average
2020-04-08 18:23:49 -07:00
vUaCal /= (float)CJ125_CALIBRATE_NUM_SAMPLES;
vUrCal /= (float)CJ125_CALIBRATE_NUM_SAMPLES;
// restore normal mode
2020-04-08 18:23:49 -07:00
cjSetMode(CJ125_MODE_NORMAL_17);
2019-01-03 04:22:21 -08:00
#if EFI_PROD_CODE
// todo: testing solution
chThdSleepMilliseconds(CJ125_CALIBRATION_DELAY);
2019-01-03 04:22:21 -08:00
#endif
// check if everything is ok
2020-04-08 18:23:49 -07:00
diag = cjReadRegister(DIAG_REG_RD);
cjUpdateAnalogValues();
cjPrintData();
// store new calibration data
2020-04-08 18:23:49 -07:00
uint32_t storedLambda = get16bitFromVoltage(vUaCal);
uint32_t storedHeater = get16bitFromVoltage(vUrCal);
efiPrintf("cj125: Done! Saving calibration data (%d %d).", storedLambda, storedHeater);
2018-11-03 10:17:58 -07:00
#if EFI_PROD_CODE
backupRamSave(BACKUP_CJ125_CALIBRATION_LAMBDA, storedLambda);
backupRamSave(BACKUP_CJ125_CALIBRATION_HEATER, storedHeater);
2019-01-03 04:22:21 -08:00
#endif /* EFI_PROD_CODE */
2020-04-08 18:23:49 -07:00
state = CJ125_IDLE;
}
static void cjStart() {
if (!engineConfiguration->isCJ125Enabled) {
efiPrintf("cj125 is disabled.");
return;
}
globalInstance.cjIdentify();
// Load calibration values
2018-11-03 10:45:36 -07:00
#if EFI_PROD_CODE
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
// if no calibration, try to calibrate now and store new values
if (storedLambda == 0 || storedHeater == 0) {
2020-05-03 07:41:48 -07:00
/**
* open question if we need special considerations for calibration. Some controllers insist on open air calibration
*/
globalInstance.calibrate();
} else {
efiPrintf("cj125: Loading stored calibration data (%d %d)", storedLambda, storedHeater);
2019-02-01 22:45:26 -08:00
globalInstance.vUaCal = getVoltageFrom16bit(storedLambda);
globalInstance.vUrCal = getVoltageFrom16bit(storedHeater);
// Start normal measurement mode
2019-02-01 22:45:26 -08:00
globalInstance.cjSetMode(CJ125_MODE_NORMAL_17);
}
cjPrintData();
2019-01-03 04:22:21 -08:00
#if EFI_PROD_CODE
// todo: testig solution
lastSlowAdcCounter = getSlowAdcCounter();
2019-01-03 04:22:21 -08:00
#endif
}
void CJ125::setError(cj125_error_e errCode) {
errorCode = errCode;
state = CJ125_ERROR;
cjPrintErrorCode(errorCode);
// This is for safety:
efiPrintf("cj125: Controller Shutdown!");
SetHeater(0);
// Software-reset of CJ125
cjWriteRegister(INIT_REG2_WR, CJ125_INIT2_RESET);
}
static bool cjStartSpi() {
2020-04-13 10:12:55 -07:00
#if HAL_USE_SPI
globalInstance.cj125Cs.initPin("cj125 CS", engineConfiguration->cj125CsPin,
2016-07-17 00:01:48 -07:00
&engineConfiguration->cj125CsPinMode);
2019-01-11 05:33:34 -08:00
// Idle CS pin - SPI CS is high when idle
2019-02-01 20:48:11 -08:00
globalInstance.cj125Cs.setValue(true);
2016-07-17 00:01:48 -07:00
cj125spicfg.cr1 += getSpiPrescaler(_150KHz, engineConfiguration->cj125SpiDevice);
2019-03-25 19:41:31 -07:00
cj125spicfg.ssport = getHwPort("cj125", engineConfiguration->cj125CsPin);
cj125spicfg.sspad = getHwPin("cj125", engineConfiguration->cj125CsPin);
2018-11-03 08:44:57 -07:00
driver = getSpiDevice(engineConfiguration->cj125SpiDevice);
if (driver == NULL) {
// error already reported
2020-04-13 10:12:55 -07:00
return false;
}
efiPrintf("cj125: Starting SPI driver %s", getSpi_device_e(engineConfiguration->cj125SpiDevice));
spiStart(driver, &cj125spicfg);
2020-04-13 10:12:55 -07:00
#endif /* HAL_USE_SPI */
return true;
}
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
*/
static bool cj125periodic(CJ125 *instance) {
2019-01-03 04:22:21 -08:00
{
efitick_t nowNt = getTimeNowNt();
bool isStopped = engine->rpmCalculator.isStopped();
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;
}
2019-01-03 05:26:42 -08:00
if (instance->state == CJ125_CALIBRATION) {
globalInstance.calibrate();
// Start normal operation
2019-01-03 05:26:42 -08:00
instance->state = CJ125_INIT;
2019-02-01 22:45:26 -08:00
globalInstance.cjSetMode(CJ125_MODE_NORMAL_17);
}
2019-02-01 22:06:45 -08:00
globalInstance.diag = cjReadRegister(DIAG_REG_RD);
// check heater state
2019-02-01 22:45:26 -08:00
if (globalInstance.vUr > CJ125_UR_PREHEAT_THR || instance->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) {
// start preheating
2019-01-03 05:26:42 -08:00
instance->state = CJ125_PREHEAT;
2019-02-01 20:16:34 -08:00
instance->startHeatingNt = instance->prevNt = getTimeNowNt();
2019-02-01 22:45:26 -08:00
globalInstance.cjSetMode(CJ125_MODE_NORMAL_17);
}
2019-02-01 22:45:26 -08:00
} else if (instance->vUr > CJ125_UR_GOOD_THR) {
2019-01-03 05:26:42 -08:00
instance->state = CJ125_HEAT_UP;
2019-02-01 22:45:26 -08:00
} else if (instance->vUr < CJ125_UR_OVERHEAT_THR) {
2019-01-03 05:26:42 -08:00
instance->state = CJ125_OVERHEAT;
} else {
// This indicates that the heater temperature is optimal for UA measurement
2019-01-03 05:26:42 -08:00
instance->state = CJ125_READY;
}
2019-02-01 20:16:34 -08:00
if (isStopped && instance->isWorkingState()) {
2019-01-03 05:26:42 -08:00
instance->state = CJ125_INIT;
instance->SetIdleHeater();
}
#if 0
// Change amplification if AFR gets lean/rich for better accuracy
2019-02-01 22:45:26 -08:00
globalInstance.cjSetMode(globalInstance.lambda > 1.0f ? CJ125_MODE_NORMAL_17 : CJ125_MODE_NORMAL_8);
#endif
2019-01-03 05:26:42 -08:00
switch (instance->state) {
case CJ125_PREHEAT:
// use constant-speed startup heat-up
2019-02-01 20:16:34 -08:00
if (nowNt - instance->prevNt >= CJ125_HEATER_PREHEAT_PERIOD) {
float periodSecs = (float)(nowNt - instance->prevNt) / NT_PER_SECOND;
// maintain speed at ~0.4V/sec
2019-02-01 20:16:34 -08:00
float preheatDuty = instance->heaterDuty + periodSecs * CJ125_HEATER_PREHEAT_RATE;
instance->SetHeater(preheatDuty);
// If we are heating too long, and there's still no result, then something is wrong...
if (nowNt - instance->startHeatingNt > NT_PER_SECOND * CJ125_PREHEAT_TIMEOUT) {
instance->setError(CJ125_ERROR_HEATER_MALFUNCTION);
}
cjPrintData();
2019-02-01 20:16:34 -08:00
instance->prevNt = nowNt;
}
break;
case CJ125_HEAT_UP:
case CJ125_READY:
// use PID for normal heater control
2019-02-01 20:16:34 -08:00
if (nowNt - instance->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.
*/
2019-04-25 18:31:33 -07:00
float duty = globalInstance.heaterPid.getOutput(globalInstance.vUr, globalInstance.vUrCal, MS2SEC(CJ125_TICK_DELAY));
instance->SetHeater(duty);
2020-05-01 16:27:26 -07:00
if (engineConfiguration->isCJ125Verbose) {
globalInstance.heaterPid.showPidStatus("cj heater");
2020-05-01 16:27:26 -07:00
cjPrintData();
}
2019-02-01 20:16:34 -08:00
instance->prevNt = nowNt;
}
break;
case CJ125_OVERHEAT:
2019-02-01 20:16:34 -08:00
if (nowNt - instance->prevNt >= CJ125_HEATER_OVERHEAT_PERIOD) {
instance->setError(CJ125_ERROR_OVERHEAT);
2019-02-01 20:16:34 -08:00
instance->prevNt = nowNt;
}
default:
;
}
2019-01-03 04:22:21 -08:00
}
return false;
}
2020-04-13 10:12:55 -07:00
#if ! EFI_UNIT_TEST
static msg_t cjThread()
2019-01-03 04:22:21 -08:00
{
chRegSetThreadName("cj125");
chThdSleepMilliseconds(500);
2019-02-01 20:16:34 -08:00
globalInstance.startHeatingNt = 0;
globalInstance.prevNt = getTimeNowNt();
2019-01-03 04:22:21 -08:00
while (1) {
bool needIdleSleep = cj125periodic(&globalInstance);
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
}
return -1;
}
2016-07-25 19:01:42 -07:00
static bool cjCheckConfig() {
if (!engineConfiguration->isCJ125Enabled) {
efiPrintf("cj125 is disabled. Failed!");
return false;
}
return true;
}
2016-07-26 19:01:50 -07:00
2020-05-01 17:22:49 -07:00
void cjStartCalibration(void) {
if (!cjCheckConfig())
return;
2019-02-01 20:16:34 -08:00
if (globalInstance.isWorkingState()) {
// todo: change this later for the normal thread operation (auto pre-heating)
efiPrintf("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;
}
2016-07-25 19:01:42 -07:00
2020-05-01 16:42:09 -07:00
void cjRestart(void) {
if (!cjCheckConfig())
return;
2019-01-03 05:26:42 -08:00
globalInstance.state = CJ125_INIT;
2020-04-08 20:14:21 -07:00
globalInstance.errorCode = CJ125_NO_ERROR;
cjInfo();
cjStart();
}
2019-01-03 04:22:21 -08:00
#endif /* EFI_UNIT_TEST */
2016-07-25 19:01:42 -07:00
#ifdef CJ125_DEBUG
static void cjSetInit1(int v) {
cjWriteRegister(INIT_REG1_WR, v & 0xff);
v = cjReadRegister(INIT_REG1_RD);
efiPrintf("cj125 INIT_REG1=0x%02x.", v);
2016-06-24 16:02:45 -07:00
}
static void cjSetInit2(int v) {
cjWriteRegister(INIT_REG2_WR, v & 0xff);
v = cjReadRegister(INIT_REG2_RD);
efiPrintf("cj125 INIT_REG2=0x%02x.", v);
}
#endif /* CJ125_DEBUG */
float cjGetAfr() {
// See CJ125 datasheet, page 6
float pumpCurrent = (globalInstance.vUa - globalInstance.vUaCal) * globalInstance.amplCoeff * (CJ125_PUMP_CURRENT_FACTOR / CJ125_PUMP_SHUNT_RESISTOR);
2018-08-08 17:42:55 -07:00
if (engineConfiguration->cj125isLsu49) {
globalInstance.lambda = interpolate2d(pumpCurrent, pumpCurrentLsu49, lambdaLsu49);
2018-08-08 17:42:55 -07:00
} else {
globalInstance.lambda = interpolate2d(pumpCurrent, pumpCurrentLsu42, lambdaLsu42);
2018-08-08 17:42:55 -07:00
}
// todo: make configurable stoich ratio
2019-02-01 22:45:26 -08:00
return globalInstance.lambda * CJ125_STOICH_RATIO;
}
bool cjHasAfrSensor() {
if (!engineConfiguration->isCJ125Enabled)
return false;
2019-02-01 22:45:26 -08:00
return globalInstance.isValidState();
}
2019-04-12 17:52:51 -07:00
#if EFI_TUNER_STUDIO
2018-06-17 16:14:46 -07:00
// used by DBG_CJ125
void cjPostState(TunerStudioOutputChannels *tsOutputChannels) {
2019-02-01 20:16:34 -08:00
tsOutputChannels->debugFloatField1 = globalInstance.heaterDuty;
2019-02-01 22:45:26 -08:00
tsOutputChannels->debugFloatField2 = globalInstance.heaterPid.getIntegration();
tsOutputChannels->debugFloatField3 = globalInstance.heaterPid.getPrevError();
tsOutputChannels->debugFloatField4 = globalInstance.vUa;
tsOutputChannels->debugFloatField5 = globalInstance.vUr;
tsOutputChannels->debugFloatField6 = globalInstance.vUaCal;
tsOutputChannels->debugFloatField7 = globalInstance.vUrCal;
2019-01-03 05:26:42 -08:00
tsOutputChannels->debugIntField1 = globalInstance.state;
2019-02-01 22:06:45 -08:00
tsOutputChannels->debugIntField2 = globalInstance.diag;
}
2018-11-03 10:27:48 -07:00
#endif /* EFI_TUNER_STUDIO */
void initCJ125() {
2019-02-01 22:06:45 -08:00
globalInstance.spi = &spi;
if (!engineConfiguration->isCJ125Enabled) {
2019-12-01 22:52:54 -08:00
globalInstance.errorCode = CJ125_ERROR_DISABLED;
return;
2019-12-01 22:52:54 -08:00
}
if (!isAdcChannelValid(engineConfiguration->cj125ur) || !isAdcChannelValid(engineConfiguration->cj125ua)) {
efiPrintf("cj125 init error! cj125ur and cj125ua are required.");
warning(CUSTOM_CJ125_0, "cj ur ua");
2019-12-01 22:52:54 -08:00
globalInstance.errorCode = CJ125_ERROR_DISABLED;
return;
}
if (!isBrainPinValid(engineConfiguration->wboHeaterPin)) {
efiPrintf("cj125 init error! wboHeaterPin is required.");
warning(CUSTOM_CJ125_1, "cj heater");
2019-12-01 22:52:54 -08:00
globalInstance.errorCode = CJ125_ERROR_DISABLED;
return;
}
globalInstance.cjInitPid();
if (!cjStartSpi())
return;
efiPrintf("cj125: Starting heater control");
globalInstance.StartHeaterControl();
cjStart();
#ifdef CJ125_DEBUG
2019-02-01 20:16:34 -08:00
// addConsoleActionF("cj125_heater", cjConsoleSetHeater);
addConsoleActionI("cj125_set_init1", cjSetInit1);
addConsoleActionI("cj125_set_init2", cjSetInit2);
#endif /* CJ125_DEBUG */
2020-04-13 10:12:55 -07:00
#if ! EFI_UNIT_TEST
2020-04-08 20:14:21 -07:00
addConsoleAction("cj125_info", cjInfo);
2020-05-01 17:22:49 -07:00
addConsoleActionF("cj125_set_p", cjSetP);
addConsoleActionF("cj125_set_i", cjSetI);
2020-04-08 20:14:21 -07:00
addConsoleAction("cj125_restart", cjRestart);
2019-01-03 04:22:21 -08:00
addConsoleAction("cj125_calibrate", cjStartCalibration);
chThdCreateStatic(cj125ThreadStack, sizeof(cj125ThreadStack), PRIO_CJ125, (tfunc_t)(void*) cjThread, NULL);
2020-04-13 10:12:55 -07:00
#endif /* ! EFI_UNIT_TEST */
}
2019-02-01 20:16:34 -08:00
#endif /* EFI_CJ125 && HAL_USE_SPI */
2020-04-08 20:14:21 -07:00
#if EFI_CJ125
// engineConfiguration->spi2SckMode = PAL_STM32_OTYPE_OPENDRAIN; // 4
// engineConfiguration->spi2MosiMode = PAL_STM32_OTYPE_OPENDRAIN; // 4
// engineConfiguration->spi2MisoMode = PAL_STM32_PUDR_PULLUP; // 32
// engineConfiguration->cj125CsPin = Gpio::A15;
2020-04-08 20:14:21 -07:00
// engineConfiguration->cj125CsPinMode = OM_OPENDRAIN;
void cj125defaultPinout() {
2020-04-08 20:14:21 -07:00
engineConfiguration->cj125ua = EFI_ADC_13; // PC3
engineConfiguration->cj125ur = EFI_ADC_4; // PA4
engineConfiguration->wboHeaterPin = Gpio::C13;
2020-04-08 20:14:21 -07:00
engineConfiguration->spi2mosiPin = Gpio::B15;
engineConfiguration->spi2misoPin = Gpio::B14;
engineConfiguration->spi2sckPin = Gpio::B13;
2020-04-08 20:14:21 -07:00
engineConfiguration->cj125CsPin = Gpio::B0;
engineConfiguration->isCJ125Enabled = true;
engineConfiguration->is_enabled_spi_2 = true;
engineConfiguration->cj125SpiDevice = SPI_DEVICE_2;
2020-04-08 20:14:21 -07:00
}
#endif /* EFI_CJ125 */