Support for CJ125 Wideband controller (alpha version) (#560)

This commit is contained in:
andreika-git 2018-01-29 21:26:42 +02:00 committed by rusefi
parent f32c5b18a8
commit f82c56acf4
2 changed files with 614 additions and 86 deletions

View File

@ -1,8 +1,6 @@
/*
* @file CJ125.cpp
*
* Based on Jeff "Turbo SOB" work
*
* @date: Jun 24, 2016
* @author Andrey Belomutskiy, (c) 2012-2018
*
@ -11,96 +9,352 @@
#include "engine.h"
#include "CJ125.h"
#include "pwm_generator.h"
#include "rpm_calculator.h"
#include "pid.h"
#if EFI_CJ125 || defined(__DOXYGEN__)
#include "pin_repository.h"
#include "hardware.h"
#include "adc_inputs.h"
#include "backup_ram.h"
#define CJ125_DEBUG
//#define CJ125_DEBUG_SPI
EXTERN_ENGINE;
static SimplePwm wboHeaderControl;
static OutputPin wboHeaderPin;
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
extern TunerStudioOutputChannels tsOutputChannels;
#endif
static SimplePwm wboHeaterControl;
static OutputPin wboHeaterPin;
static OutputPin cj125Cs;
static Logging *logger;
static unsigned char tx_buff[1];
static unsigned char tx_buff[2];
static unsigned char rx_buff[1];
static pid_s heaterPidConfig;
static Pid heaterPid(&heaterPidConfig);
static float heaterDuty = 0.0f;
static THD_WORKING_AREA(cjThreadStack, UTILITY_THREAD_STACK_SIZE);
static SPIDriver *driver;
static SPIConfig cj125spicfg = { NULL,
/* HW dependent part.*/
NULL, 0,
SPI_CR1_MSTR |
SPI_CR1_CPHA | SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR_2 };
/* HW dependent part.*/
NULL, 0, SPI_CR1_MSTR | SPI_CR1_CPHA | SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR_2 };
/**********************************************************************************
INIT_REG1 - CJ125 Initialization Register 1
00x000x0
||||||||---- VL - Pump Current Sense Amplifier - 0 = x8, 1 = x17
If VL is set to "0" the amplification of Ip is 8.
If VL is "1" the amplification is 17. The higher
amplification is needed for a better resolution
at Lambda > 1, amplification 8 for Lambda < 1.
Note: It seems to make no difference, always set to 1.
|||||||----- Unused
||||||------ LA - Pump Current Sense Amplifier - 0 = measurement, 1 = calibration
If LA is "0" the measurement mode for Ip is active.
The current pump current is displayed on Ua. If LA is
set to "1" the calibration mode is active. The shown
voltage on Ua must been subtracted from the later measurement.
|||||------- Undocumented Bit - Note: CJ125 only seems to work when set to 1.
||||-------- RA - Measurement Current for Ri - 0 = measurement, 1 = calibration
If RA is "0" the measurement mode for Ri is active and
the current resistance of the probe is "displayed" on Ur.
If RA is "1" the calibration mode is active. You get the
optimal voltage for the inner resistance of the probe on Ur.
|||--------- Unused
||---------- PA - Pump Current Control - Set to 0 to be active
|----------- ENABLE/HOLD - Must be set to 1 to enable
***********************************************************************************/
// Used by CJ125 driver state machine
static volatile cj125_state_e state = CJ125_IDLE;
// Chip diagnostics register contents
static volatile int diag = 0;
// Last Error code
static volatile cj125_error_e errorCode = CJ125_ERROR_NONE;
static cj125_state_e state = CJ125_IDLE;
// Current values
static volatile float vUa = 0.0f, vUr = 0.0f;
// Calibration values
static volatile float vUaCal = 0.0f, vUrCal = 0.0f;
static msg_t cjThread(void)
{
chRegSetThreadName("cj125");
static volatile int lastSlowAdcCounter = 0;
// while(1) {
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;
// }
return -1;
}
// 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
static const int CJ125_LSU_CURVE_SIZE = 16;
// This is a number of bins for each sensor type (should be < CJ125_LSU_CURVE_SIZE)
static const float cjLSUTableSize[2] = {
9, 15,
};
// 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
-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 },
};
// 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
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 },
};
static void cj125test(void) {
// read identity command
static int cjReadRegister(unsigned char regAddr) {
spiSelect(driver);
tx_buff[0] = IDENT_REG_RD;
tx_buff[0] = regAddr;
spiSend(driver, 1, tx_buff);
// safety?
chThdSleepMilliseconds(10);
rx_buff[0] = 0;
spiReceive(driver, 1, rx_buff);
int value = rx_buff[0] & 0xF8;
spiUnselect(driver);
scheduleMsg(logger, "cj125 got %x", value);
#ifdef CJ125_DEBUG_SPI
scheduleMsg(logger, "cjReadRegister: addr=%d answer=%d", regAddr, rx_buff[0]);
#endif
return rx_buff[0];
}
void initCJ125(Logging *sharedLogger) {
logger = sharedLogger;
// still a lot to be done here :)
static void cjWriteRegister(unsigned char regAddr, unsigned char regValue) {
#ifdef CJ125_DEBUG_SPI
scheduleMsg(logger, "cjWriteRegister: addr=%d value=%d", regAddr, regValue);
#endif
spiSelect(driver);
tx_buff[0] = regAddr;
tx_buff[1] = regValue;
spiSend(driver, 2, tx_buff);
spiUnselect(driver);
}
if (!boardConfiguration->isCJ125Enabled)
static float getUr() {
if (CONFIG(cj125ur) != EFI_ADC_NONE) {
#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));
#endif
}
return 0.0f;
}
static float getUa() {
if (CONFIG(cj125ua) != EFI_ADC_NONE)
return getVoltageDivided("cj125ua", CONFIG(cj125ua));
return 0.0f;
}
static float getVoltageFrom16bit(uint32_t stored) {
return ((float)stored) / CJ125_VOLTAGE_TO_16BIT_FACTOR;
}
static uint32_t get16bitFromVoltage(float v) {
return (uint32_t)(v * CJ125_VOLTAGE_TO_16BIT_FACTOR);
}
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
}
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;
}
scheduleMsg(logger, "cj125 ERROR: %s.", errString);
}
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;
}
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
}
static void cjUpdateAnalogValues(void) {
waitForSlowAdc(lastSlowAdcCounter);
vUr = getUr();
vUa = getUa();
lastSlowAdcCounter = getSlowAdcCounter();
}
static void cjCalibrate(void) {
cjIdentify();
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);
return;
}
// wait for the start of the calibration
chThdSleepMilliseconds(CJ125_CALIBRATION_DELAY);
vUaCal = 0.0f;
vUrCal = 0.0f;
// wait for some more ADC samples
for (int i = 0; i < CJ125_CALIBRATE_NUM_SAMPLES; i++) {
cjUpdateAnalogValues();
cjPrintData();
cjPostState(&tsOutputChannels);
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);
chThdSleepMilliseconds(CJ125_CALIBRATION_DELAY);
// 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);
backupRamSave(BACKUP_CJ125_CALIBRATION_LAMBDA, storedLambda);
backupRamSave(BACKUP_CJ125_CALIBRATION_HEATER, storedHeater);
state = CJ125_IDLE;
}
static void cjStart(void) {
if (!boardConfiguration->isCJ125Enabled) {
scheduleMsg(logger, "cj125 is disabled.");
return;
}
cjIdentify();
// Load calibration values
uint32_t storedLambda = backupRamLoad(BACKUP_CJ125_CALIBRATION_LAMBDA);
uint32_t storedHeater = backupRamLoad(BACKUP_CJ125_CALIBRATION_HEATER);
// 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();
lastSlowAdcCounter = getSlowAdcCounter();
}
static void cjSetHeater(float value) {
// 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);
}
static void cjSetIdleHeater(void) {
// small preheat for faster start & moisture anti-shock therapy for the sensor
cjSetHeater(CJ125_HEATER_IDLE_RATE);
}
static void cjStartHeaterControl(void) {
if (boardConfiguration->wboHeaterPin != GPIO_UNASSIGNED) {
scheduleMsg(logger, "cj125: Starting heater control");
// todo: use custom pin state method, turn pin off while not running
startSimplePwmExt(&wboHeaterControl, "wboHeaterPin", boardConfiguration->wboHeaterPin,
&wboHeaterPin, CJ125_HEATER_PWM_FREQ, 0.0f, applyPinState);
cjSetIdleHeater();
}
}
static void cjSetError(cj125_error_e errCode) {
errorCode = errCode;
state = CJ125_ERROR;
cjPrintErrorCode(errorCode);
// This is for safety:
scheduleMsg(logger, "cj125: Controller Shutdown!");
cjSetHeater(0);
// Software-reset of CJ125
cjWriteRegister(INIT_REG2_WR, CJ125_INIT2_RESET);
}
static bool cjIsWorkingState(void) {
return state != CJ125_ERROR && state != CJ125_INIT && state != CJ125_IDLE;
}
static void cjInitPid(void) {
// todo: these values are valid only for LSU 4.2
heaterPidConfig.pFactor = CJ125_PID_LSU42_P;
heaterPidConfig.iFactor = CJ125_PID_LSU42_I;
heaterPidConfig.dFactor = 0.0f;
heaterPidConfig.minValue = 0;
heaterPidConfig.maxValue = 1;
heaterPidConfig.offset = 0;
// todo: period?
heaterPidConfig.period = 1.0f;
heaterPid.reset();
}
static void cjStartSpi(void) {
cj125spicfg.ssport = getHwPort("cj125", boardConfiguration->cj125CsPin);
cj125spicfg.sspad = getHwPin("cj125", boardConfiguration->cj125CsPin);
@ -109,33 +363,222 @@ void initCJ125(Logging *sharedLogger) {
cj125Cs.initPin("cj125 CS", boardConfiguration->cj125CsPin,
&engineConfiguration->cj125CsPinMode);
if (boardConfiguration->wboHeaterPin != GPIO_UNASSIGNED) {
// todo: use custom pin state method, turn pin off while not running
startSimplePwmExt(&wboHeaderControl, "heater control", boardConfiguration->wboHeaterPin,
&wboHeaderPin,
300, 0.1, applyPinState);
}
scheduleMsg(logger, "Starting cj125 spi driver");
scheduleMsg(logger, "cj125: Starting SPI driver");
spiStart(driver, &cj125spicfg);
addConsoleAction("cj125", cj125test);
chThdCreateStatic(cjThreadStack, sizeof(cjThreadStack), LOWPRIO, (tfunc_t) cjThread, NULL);
}
void cjPostState(TunerStudioOutputChannels *tsOutputChannels) {
static msg_t cjThread(void)
{
chRegSetThreadName("cj125");
chThdSleepMilliseconds(500);
efitick_t startHeatingNt = 0;
efitick_t prevNt = getTimeNowNt();
while(1) {
efitick_t nowNt = getTimeNowNt();
bool isStopped = engine->rpmCalculator.isStopped(PASS_ENGINE_PARAMETER_SIGNATURE);
cjUpdateAnalogValues();
// If the controller is disabled
if (state == CJ125_IDLE || state == CJ125_ERROR) {
chThdSleepMilliseconds(CJ125_IDLE_TICK_DELAY);
continue;
}
if (state == CJ125_CALIBRATION) {
cjCalibrate();
// Start normal operation
state = CJ125_INIT;
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
if (state == CJ125_INIT && !isStopped) {
// start preheating
state = CJ125_PREHEAT;
startHeatingNt = prevNt = getTimeNowNt();
cjSetMode(CJ125_MODE_NORMAL_17);
}
} else if (vUr > CJ125_UR_GOOD_THR) {
state = CJ125_HEAT_UP;
} else if (vUr < CJ125_UR_OVERHEAT_THR) {
state = CJ125_OVERHEAT;
} else {
// This indicates that the heater temperature is optimal for UA measurement
state = CJ125_READY;
}
if (isStopped && cjIsWorkingState()) {
state = CJ125_INIT;
cjSetIdleHeater();
}
#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
// Update console output variables
cjPostState(&tsOutputChannels);
switch (state) {
case CJ125_PREHEAT:
// use constant-speed startup heat-up
if (nowNt - prevNt >= CJ125_HEATER_PREHEAT_PERIOD) {
float periodSecs = (float)(nowNt - prevNt) / US2NT(US_PER_SECOND_LL);
// maintain speed at ~0.4V/sec
float preheatDuty = heaterDuty + periodSecs * CJ125_HEATER_PREHEAT_RATE;
cjSetHeater(preheatDuty);
// If we are heating too long, and there's still no result, then something is wrong...
if (nowNt - startHeatingNt > US2NT(US_PER_SECOND_LL) * CJ125_PREHEAT_TIMEOUT) {
cjSetError(CJ125_ERROR_HEATER_MALFUNCTION);
}
cjPrintData();
prevNt = nowNt;
}
break;
case CJ125_HEAT_UP:
case CJ125_READY:
// use PID for normal heater control
if (nowNt - prevNt >= CJ125_HEATER_CONTROL_PERIOD) {
float duty = heaterPid.getValue(vUr, vUrCal);
heaterPid.showPidStatus(logger, "cj");
cjSetHeater(duty);
cjPrintData();
prevNt = nowNt;
}
break;
case CJ125_OVERHEAT:
if (nowNt - prevNt >= CJ125_HEATER_OVERHEAT_PERIOD) {
cjSetError(CJ125_ERROR_OVERHEAT);
prevNt = nowNt;
}
default:
;
}
chThdSleepMilliseconds(CJ125_TICK_DELAY);
}
return -1;
}
static bool cjCheckConfig(void) {
if (!boardConfiguration->isCJ125Enabled) {
scheduleMsg(logger, "cj125 is disabled. Failed!");
return false;
}
return true;
}
static void cjStartCalibration(void) {
if (!cjCheckConfig())
return;
if (state != CJ125_IDLE) {
// 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;
}
state = CJ125_CALIBRATION;
}
static void cjStartTest(void) {
if (!cjCheckConfig())
return;
state = CJ125_INIT;
}
#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);
}
static void cjSetInit2(int v) {
cjWriteRegister(INIT_REG2_WR, v & 0xff);
v = cjReadRegister(INIT_REG2_RD);
scheduleMsg(logger, "cj125 INIT_REG2=0x%02x.", v);
}
#endif /* CJ125_DEBUG */
float cjGetAfr(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
return 0.0f;
// todo: make configurable sensor LSU type
cj125_sensor_type_e sensorType = CJ125_LSU_42;
// 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;
}
bool cjHasAfrSensor(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
return false;
if (!boardConfiguration->isCJ125Enabled)
return false;
// check if controller is functioning
if (!cjIsWorkingState())
return false;
// 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;
}
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;
tsOutputChannels->debugIntField1 = state;
tsOutputChannels->debugIntField2 = diag;
}
void initCJ125(Logging *sharedLogger) {
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;
}
cjInitPid();
cjStartSpi();
cjStartHeaterControl();
cjStart();
#if 1
state = CJ125_INIT;
#endif
addConsoleAction("cj125", cjStartTest);
addConsoleAction("cj125_calibrate", cjStartCalibration);
#ifdef CJ125_DEBUG
addConsoleActionF("cj125_heater", cjSetHeater);
addConsoleActionI("cj125_set_init1", cjSetInit1);
addConsoleActionI("cj125_set_init2", cjSetInit2);
#endif /* CJ125_DEBUG */
chThdCreateStatic(cjThreadStack, sizeof(cjThreadStack), LOWPRIO, (tfunc_t) cjThread, NULL);
}
#endif /* EFI_CJ125 */

View File

@ -2,18 +2,108 @@
* @file CJ125.h
*
* @date: Jul 17, 2016
* @author Andrey Belomutskiy, (c) 2012-2017
* @author Andrey Belomutskiy, (c) 2012-2018
*/
#ifndef HW_LAYER_SENSORS_CJ125_H_
#define HW_LAYER_SENSORS_CJ125_H_
#define IDENT_REG_RD 0x48 // Read Identity Register, decimal 72
#define INIT_REG1_WR 0x56 // Write To Initialization Register 1, decimal 86
#define CJ125_NORMAL 0x89 // 137 decimal
#define CJ125_CALBRT 0x9D // 157 decimal
// CJ125 SPI Registers
#define IDENT_REG_RD 0x48 // Read Identity Register
#define INIT_REG1_WR 0x56 // Write To Initialization Register 1
#define INIT_REG2_WR 0x5A // Write To Initialization Register 2
#define INIT_REG1_RD 0x6C // Read Initialization Register 1
#define DIAG_REG_RD 0x78 // Read Diagnostics Register
#define INIT_REG2_RD 0x7E // Read Initialization Register 2
#define CJ125_IDENTT 0x60
#define CJ125_INIT1_NORMAL_8 0x88 // 0b10001000 (Normal mode, Amplification 8)
#define CJ125_INIT1_NORMAL_17 0x89 // 0b10001001 (Normal mode, Amplification 17)
#define CJ125_INIT1_CALBRT 0x9D // 0b10011101 (Calibration mode, LA=1, RA=1)
#define CJ125_INIT2_NORMAL 0x00 // 0b00000000, (Normal mode)
#define CJ125_INIT2_DIAG 0x10 // 0b00010000, (Extended diagnostics mode, SET_DIA_Q=1)
#define CJ125_INIT2_RESET 0x40 // 0b01000000, SRESET=1
#define CJ125_DIAG_NORM 0xFF // no errors
#define CJ125_IDENT 0x60
#define CJ125_IDENT_MASK 0xF8
#define CJ125_CALIBRATION_DELAY 1000 // 1 sec
#define CJ125_TICK_DELAY 20 // 20 ms
#define CJ125_IDLE_TICK_DELAY 1000 // 1 sec
// Heater params for Idle(cold), Preheating and Control stages
// See http://www.waltech.com/wideband-files/boschsensordatasheet.htm
#define CJ125_HEATER_IDLE_RATE 0.15f // for a very cold sensor (presumably), we allow 15% duty max.
#define CJ125_HEATER_PREHEAT_PERIOD 300 // 300 ms
#define CJ125_HEATER_CONTROL_PERIOD 180 // 180 ms
#define CJ125_HEATER_OVERHEAT_PERIOD 500 // 500 ms
#define CJ125_HEATER_PWM_FREQ 100 // 100 Hz
#define CJ125_HEATER_PREHEAT_RATE (0.4f/14.0f) // Assuming that dutycycle=1.0 equals to 14V, and max.allowed heater rate is 0.4V/sec
#define CJ125_HEATER_LIMITING_VOLTAGE 12.0f // Do not allow more than 90% heating for high battery voltage (>12V).
#define CJ125_HEATER_LIMITING_RATE 0.92f // This prevents sensor overheating.
#define CJ125_CALIBRATE_NUM_SAMPLES 10
#define CJ125_UR_PREHEAT_THR 2.0f // Ur > 2.0 Volts is too cold to control with PID
#define CJ125_UR_OVERHEAT_THR 0.5f // Ur < 0.5 Volts is overheat
#define CJ125_UR_GOOD_THR 1.4f
#define CJ125_UACAL_MIN 1.0f // Calibration UA values range
#define CJ125_UACAL_MAX 2.0f
#define CJ125_HEATER_MIN_DUTY 0.1f
#define CJ125_PREHEAT_MIN_DUTY 0.9f
//#define CJ125_PREHEAT_TIMEOUT 90 // 90 secs
#define CJ125_PREHEAT_TIMEOUT 300
#define CJ125_VOLTAGE_TO_16BIT_FACTOR 4096.0f
#define CJ125_PUMP_SHUNT_RESISTOR 61.9f
#define CJ125_STOICH_RATIO 14.7f
#define CJ125_PUMP_CURRENT_FACTOR 1000.0f
// Some experimental magic values for heater PID regulator
#define CJ125_PID_LSU42_P (80.0f / 16.0f * 5.0f / 1024.0f)
#define CJ125_PID_LSU42_I (25.0f / 16.0f * 5.0f / 1024.0f)
// Returned if there's no valid measurement
#define CJ125_AFR_NAN 0.0f
typedef enum {
CJ125_IDLE,
CJ125_INIT,
CJ125_CALIBRATION,
CJ125_PREHEAT,
CJ125_HEAT_UP,
CJ125_READY,
CJ125_OVERHEAT,
CJ125_ERROR,
} cj125_state_e;
typedef enum {
CJ125_ERROR_NONE,
CJ125_ERROR_HEATER_MALFUNCTION,
CJ125_ERROR_OVERHEAT,
} cj125_error_e;
typedef enum {
CJ125_MODE_NONE,
CJ125_MODE_NORMAL_8,
CJ125_MODE_NORMAL_17,
CJ125_MODE_CALIBRATION,
} cj125_mode_e;
typedef enum {
CJ125_LSU_42 = 0,
CJ125_LSU_49 = 1,
} cj125_sensor_type_e;
void initCJ125(Logging *shared);
@ -21,9 +111,4 @@ void cjPostState(TunerStudioOutputChannels *tsOutputChannels);
float cjGetAfr(DECLARE_ENGINE_PARAMETER_SIGNATURE);
bool cjHasAfrSensor(DECLARE_ENGINE_PARAMETER_SIGNATURE);
typedef enum {
CJ125_IDLE = 0
} cj125_state_e;
#endif /* HW_LAYER_SENSORS_CJ125_H_ */