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

563 lines
15 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
* @file HIP9011.cpp
* @brief HIP9011/TPIC8101 driver
*
2017-01-27 09:03:05 -08:00
* Jan 2017 status:
* 1) seems to be kind of working - reacts to parameter changes and does produce variable output
* 2) only one (first) channel is currently used
* 3) engine control does not yet react to knock since very little actual testing - no engine runs with proven knock yet
*
*
2017-01-27 18:04:23 -08:00
* http://rusefi.com/forum/viewtopic.php?f=4&t=400
* http://rusefi.com/forum/viewtopic.php?f=5&t=778
*
2015-07-10 06:01:56 -07:00
* pin1 VDD
* pin2 GND
*
* pin8 Chip Select - CS
2017-01-27 09:03:05 -08:00
* pin11 Slave Data Out - MISO
2015-07-10 06:01:56 -07:00
* pin12 Slave Data In - MOSI
* pin13 SPI clock - SCLK
*
*
* http://www.ti.com/lit/ds/symlink/tpic8101.pdf
* http://www.intersil.com/content/dam/Intersil/documents/hip9/hip9011.pdf
* http://www.intersil.com/content/dam/Intersil/documents/an97/an9770.pdf
* http://e2e.ti.com/cfs-file/__key/telligent-evolution-components-attachments/00-26-01-00-00-42-36-40/TPIC8101-Training.pdf
*
* max SPI frequency: 5MHz max
*
* @date Nov 27, 2013
2020-01-13 18:57:43 -08:00
* @author Andrey Belomutskiy, (c) 2012-2020
2015-07-10 06:01:56 -07:00
* @Spilly
*/
2018-09-16 19:26:57 -07:00
#include "global.h"
2015-07-10 06:01:56 -07:00
#include "engine.h"
#include "settings.h"
#include "hardware.h"
#include "rpm_calculator.h"
#include "trigger_central.h"
#include "hip9011_logic.h"
#include "hip9011.h"
2015-07-10 06:01:56 -07:00
#include "adc_inputs.h"
#include "perf_trace.h"
#include "thread_priority.h"
2015-07-10 06:01:56 -07:00
#include "engine_controller.h"
2018-11-03 10:27:48 -07:00
#if EFI_PROD_CODE
#include "pin_repository.h"
#include "mpu_util.h"
#include "os_util.h"
2018-11-03 10:27:48 -07:00
#endif
2019-04-12 17:52:51 -07:00
#if EFI_HIP_9011
2015-07-10 06:01:56 -07:00
/*==========================================================================*/
/* Local definitions. */
/*==========================================================================*/
/*==========================================================================*/
/* Local variables and types. */
/*==========================================================================*/
static NamedOutputPin intHold(PROTOCOL_HIP_NAME);
static NamedOutputPin Cs(PROTOCOL_HIP_NAME);
2015-07-10 06:01:56 -07:00
class Hip9011Hardware : public Hip9011HardwareInterface {
int sendSyncCommand(uint8_t command, uint8_t *rx_ptr) override;
};
/* TODO: include following stuff in object */
/* wake semaphore */
static semaphore_t wake;
static SPIDriver *spi;
static Hip9011Hardware hardware;
2015-07-10 06:01:56 -07:00
static float hipValueMax = 0;
HIP9011 instance(&hardware);
2019-01-03 21:22:35 -08:00
static scheduling_s endTimer;
2015-07-10 06:01:56 -07:00
static Logging *logger;
// SPI_CR1_BR_1 // 5MHz
// SPI_CR1_CPHA Clock Phase
// todo: nicer method which would mention SPI speed explicitly?
2018-11-03 06:44:34 -07:00
#if EFI_PROD_CODE
static SPIConfig hipSpiCfg = {
.circular = false,
.end_cb = NULL,
.ssport = NULL,
.sspad = 0,
.cr1 =
SPI_CR1_8BIT_MODE |
SPI_CR1_MSTR |
SPI_CR1_CPHA |
//SPI_CR1_BR_1 // 5MHz
SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR_2,
.cr2 =
SPI_CR2_8BIT_MODE
};
2019-01-03 19:53:34 -08:00
#endif /* EFI_PROD_CODE */
2015-07-10 06:01:56 -07:00
/*==========================================================================*/
/* Forward declarations */
/*==========================================================================*/
static void hip_addconsoleActions(void);
/*==========================================================================*/
/* Local functions. */
/*==========================================================================*/
static int checkResponseDefMode(uint8_t tx, uint8_t rx) {
/* in default SPI mode SDO is directly equals the SDI (echo function) */
if (tx == rx) {
return 0;
2015-07-10 15:02:29 -07:00
} else {
return -1;
2015-07-10 15:02:29 -07:00
}
}
static int checkResponseAdvMode(uint8_t tx, uint8_t rx) {
UNUSED(tx); UNUSED(rx);
/* TODO: no check for advanced mode yet */
return 0;
}
int Hip9011Hardware::sendSyncCommand(uint8_t tx, uint8_t *rx_ptr) {
int ret;
uint8_t rx;
/* Acquire ownership of the bus. */
spiAcquireBus(spi);
/* Setup transfer parameters. */
spiStart(spi, &hipSpiCfg);
/* Slave Select assertion. */
spiSelect(spi);
/* Transfer */
rx = spiPolledExchange(spi, tx);
/* Slave Select de-assertion. */
spiUnselect(spi);
/* Ownership release. */
spiReleaseBus(spi);
/* received data */
if (rx_ptr)
*rx_ptr = rx;
/* check response */
if (instance.adv_mode == false)
ret = checkResponseDefMode(tx, rx);
else
ret = checkResponseAdvMode(tx, rx);
/* statistic counters */
if (ret)
instance.invalidResponsesCount++;
else
instance.correctResponsesCount++;
return ret;
}
EXTERN_ENGINE;
2015-07-10 06:01:56 -07:00
static int hip_wake_driver(void)
{
/* Entering a reentrant critical zone.*/
syssts_t sts = chSysGetStatusAndLockX();
chSemSignalI(&wake);
if (!port_is_isr_context()) {
/**
* chSemSignalI above requires rescheduling
* interrupt handlers have implicit rescheduling
*/
chSchRescheduleS();
2015-07-10 06:01:56 -07:00
}
/* Leaving the critical zone.*/
chSysRestoreStatusX(sts);
2015-07-10 06:01:56 -07:00
return 0;
2015-07-10 06:01:56 -07:00
}
static void startIntegration(void *) {
2019-01-03 21:22:35 -08:00
if (instance.state == READY_TO_INTEGRATE) {
2015-07-10 06:01:56 -07:00
/**
* SPI communication is only allowed while not integrating, so we postpone the exchange
* until we are done integrating
*/
2019-01-03 21:22:35 -08:00
instance.state = IS_INTEGRATING;
2017-04-21 16:23:20 -07:00
intHold.setHigh();
2015-07-10 06:01:56 -07:00
}
}
static void endIntegration(void *) {
2015-07-10 06:01:56 -07:00
/**
* isIntegrating could be 'false' if an SPI command was pending thus we did not integrate during this
* engine cycle
*/
2019-01-03 21:22:35 -08:00
if (instance.state == IS_INTEGRATING) {
2017-04-21 16:23:20 -07:00
intHold.setLow();
2019-01-03 21:22:35 -08:00
instance.state = WAITING_FOR_ADC_TO_SKIP;
2015-07-10 06:01:56 -07:00
}
}
/**
* Ignition callback used to start HIP integration and schedule finish
2015-07-10 06:01:56 -07:00
*/
void hip9011_startKnockSampling(uint8_t cylinderNumber, efitick_t nowNt) {
if (!CONFIG(isHip9011Enabled))
2015-07-10 06:01:56 -07:00
return;
/* overrun? */
if (instance.state != READY_TO_INTEGRATE) {
instance.overrun++;
2015-07-10 06:01:56 -07:00
return;
}
if (cylinderNumber == instance.expectedCylinderNumber) {
/* save currect cylinder */
instance.cylinderNumber = cylinderNumber;
startIntegration(NULL);
2015-07-10 06:01:56 -07:00
/* TODO: reference to knockDetectionWindowStart */
scheduleByAngle(&endTimer, nowNt,
engineConfiguration->knockDetectionWindowEnd - engineConfiguration->knockDetectionWindowStart,
&endIntegration);
} else {
/* out of sync */
if (instance.expectedCylinderNumber >= 0)
instance.unsync++;
/* save currect cylinder */
instance.cylinderNumber = cylinderNumber;
/* Skip integration, call driver task to prepare for next cylinder */
instance.state = NOT_READY;
hip_wake_driver();
}
2015-07-10 06:01:56 -07:00
}
2018-11-03 06:44:34 -07:00
void hipAdcCallback(adcsample_t adcValue) {
2019-01-03 21:22:35 -08:00
if (instance.state == WAITING_FOR_ADC_TO_SKIP) {
instance.state = WAITING_FOR_RESULT_ADC;
} else if (instance.state == WAITING_FOR_RESULT_ADC) {
/* offload calculations to driver thread */
instance.raw_value = adcValue;
instance.state = NOT_READY;
hip_wake_driver();
2015-07-10 06:01:56 -07:00
}
}
static int hip_testAdvMode(void) {
int ret;
uint8_t ret0, ret1, ret2;
2015-07-10 06:01:56 -07:00
/* do not care about configuration values, we meed replyes only.
* correct values will be uploaded later */
/* A control byte is written to the SDI and shifted with the MSB
* first. The response byte on the SDO is shifted out with the MSB
* first. The response byte corresponds to the previous command.
* Therefore, the SDI shifts in a control byte n and shifts out a
* response command byte n 1. */
ret = instance.hw->sendSyncCommand(SET_BAND_PASS_CMD(0), NULL);
if (ret)
return ret;
ret = instance.hw->sendSyncCommand(SET_GAIN_CMD(0), &ret0);
if (ret)
return ret;
ret = instance.hw->sendSyncCommand(SET_INTEGRATOR_CMD(0), &ret1);
if (ret)
return ret;
ret = instance.hw->sendSyncCommand(SET_INTEGRATOR_CMD(0), &ret2);
if (ret)
return ret;
/* magic reply bytes from DS Table 2 */
if ((ret0 == SET_BAND_PASS_REP) &&
(ret1 == SET_GAIN_REP) &&
(ret2 == SET_INTEGRATOR_REP))
return 0;
return -1;
}
static int hip_init(void) {
int ret;
ret = instance.hw->sendSyncCommand(SET_PRESCALER_CMD(instance.prescaler), NULL);
if (ret) {
/* NOTE: hip9011/tpic8101 can be in default or advansed mode at this point
* If we supposed not to support advanced mode this is definitely error */
if (!CONFIG(useTpicAdvancedMode))
return ret;
}
2015-07-10 20:01:24 -07:00
/* ...othervice or when no error is reported lets try to switch to advanced mode */
if (CONFIG(useTpicAdvancedMode)) {
/* enable advanced mode */
ret = instance.hw->sendSyncCommand(SET_ADVANCED_MODE_CMD, NULL);
if (ret) {
uint8_t rx;
/* communication error is detected for default mode...
* may be we are in advanced mode already?
* Now we dont care for return value */
instance.hw->sendSyncCommand(SET_ADVANCED_MODE_CMD, &rx);
if (rx != SET_ADVANCED_MODE_REP) {
/* this is realy a communication problem */
return ret;
}
}
/* now we should be in advanced mode... if chip supports...
* set advanced mode flag now so checkResponse will switch to
* advanced mode checkig (not implemented) */
instance.adv_mode = true;
ret = hip_testAdvMode();
if (ret) {
warning(CUSTOM_OBD_KNOCK_PROCESSOR, "TPIC/HIP does not support advanced mode");
instance.adv_mode = false;
}
2015-07-10 06:01:56 -07:00
}
/* reset error counter now */
instance.invalidResponsesCount = 0;
2019-01-03 21:22:35 -08:00
instance.state = READY_TO_INTEGRATE;
return 0;
2015-07-10 06:01:56 -07:00
}
2020-08-01 15:59:09 -07:00
static THD_WORKING_AREA(hipThreadStack, UTILITY_THREAD_STACK_SIZE);
2015-07-10 06:01:56 -07:00
static msg_t hipThread(void *arg) {
int ret;
UNUSED(arg);
chRegSetThreadName("hip9011 worker");
2015-07-11 12:01:54 -07:00
/* Acquire ownership of the bus. */
spiAcquireBus(spi);
2015-07-11 12:01:54 -07:00
// some time to let the hardware start
Cs.setValue(true);
2015-07-11 12:01:54 -07:00
chThdSleepMilliseconds(100);
Cs.setValue(false);
2015-07-11 12:01:54 -07:00
chThdSleepMilliseconds(100);
Cs.setValue(true);
/* Ownership release. */
spiReleaseBus(spi);
2015-07-11 12:01:54 -07:00
/* init semaphore */
chSemObjectInit(&wake, 10);
chThdSleepMilliseconds(100);
2015-07-11 12:01:54 -07:00
do {
/* retry until success */
ret = hip_init();
if (ret) {
warning(CUSTOM_OBD_KNOCK_PROCESSOR, "TPIC/HIP does not respond: %d", ret);
chThdSleepMilliseconds(10 * 1000);
}
} while (ret);
while (1) {
msg_t msg;
/* load new/updated settings */
instance.handleSettings(GET_RPM() DEFINE_PARAM_SUFFIX(PASS_HIP_PARAMS));
/* switch input channel */
instance.handleChannel(DEFINE_PARAM_SUFFIX(PASS_HIP_PARAMS));
/* State */
instance.state = READY_TO_INTEGRATE;
msg = chSemWaitTimeout(&wake, TIME_INFINITE);
if (msg == MSG_TIMEOUT) {
/* ??? */
} else {
/* Check for correct cylinder/input */
if (instance.cylinderNumber == instance.expectedCylinderNumber) {
/* calculations */
float knockVolts = instance.raw_value * adcToVolts(1) * CONFIG(analogInputDividerCoefficient);
hipValueMax = maxF(knockVolts, hipValueMax);
engine->knockLogic(knockVolts);
/* TunerStudio */
tsOutputChannels.knockLevels[instance.cylinderNumber] = knockVolts;
tsOutputChannels.knockLevel = knockVolts;
/* counters */
instance.samples++;
} else {
/* out of sync event already calculated, nothing to do */
}
2015-07-10 06:01:56 -07:00
}
}
2015-07-10 06:01:56 -07:00
return -1;
}
/*==========================================================================*/
/* Exported functions. */
/*==========================================================================*/
void stopHip9001_pins() {
intHold.deInit();
Cs.deInit();
#if EFI_PROD_CODE
hipSpiCfg.ssport = NULL;
#endif
}
void startHip9001_pins() {
intHold.initPin("hip int/hold", CONFIG(hip9011IntHoldPin), &CONFIG(hip9011IntHoldPinMode));
Cs.initPin("hip CS", CONFIG(hip9011CsPin), &CONFIG(hip9011CsPinMode));
}
2015-07-10 06:01:56 -07:00
void initHip9011(Logging *sharedLogger) {
logger = sharedLogger;
if (!CONFIG(isHip9011Enabled))
2015-07-10 06:01:56 -07:00
return;
2018-11-03 06:44:34 -07:00
#if EFI_PROD_CODE
spi = getSpiDevice(CONFIG(hip9011SpiDevice));
if (spi == NULL) {
// error already reported
return;
}
2015-07-10 06:01:56 -07:00
hipSpiCfg.ssport = getHwPort("hip", CONFIG(hip9011CsPin));
hipSpiCfg.sspad = getHwPin("hip", CONFIG(hip9011CsPin));
2019-05-27 14:02:46 -07:00
#endif /* EFI_PROD_CODE */
2015-07-10 06:01:56 -07:00
startHip9001_pins();
2015-07-10 06:01:56 -07:00
/* load settings */
instance.prescaler = CONFIG(hip9011PrescalerAndSDO);
2015-07-10 06:01:56 -07:00
scheduleMsg(logger, "Starting HIP9011/TPIC8101 driver");
chThdCreateStatic(hipThreadStack, sizeof(hipThreadStack), PRIO_HIP9011, (tfunc_t)(void*) hipThread, NULL);
hip_addconsoleActions();
}
/*==========================================================================*/
/* Debug functions. */
/*==========================================================================*/
static void showHipInfo(void) {
if (!CONFIG(isHip9011Enabled)) {
scheduleMsg(logger, "hip9011 driver not active");
return;
}
scheduleMsg(logger, "HIP9011: enabled %s state %s",
boolToString(CONFIG(isHip9011Enabled)),
getHip_state_e(instance.state));
scheduleMsg(logger, " Advanced mode: enabled %d used %d",
CONFIG(useTpicAdvancedMode),
instance.adv_mode);
scheduleMsg(logger, " Input Ch %d (cylinder %d next %d)",
instance.channelIdx,
instance.cylinderNumber,
instance.expectedCylinderNumber);
scheduleMsg(logger, " Cyl bore %.2fmm freq %.2fkHz band idx 0x%x",
engineConfiguration->cylinderBore,
instance.getBand(PASS_HIP_PARAMS),
instance.bandIdx);
scheduleMsg(logger, " Integrator idx 0x%x",
instance.intergratorIdx);
scheduleMsg(logger, " Gain %.2f idx 0x%x",
engineConfiguration->hip9011Gain,
instance.gainIdx);
scheduleMsg(logger, " PaSDO=0x%x",
instance.prescaler);
scheduleMsg(logger, " knockVThreshold=%.2f knockCount=%d maxKnockSubDeg=%.2f",
engineConfiguration->knockVThreshold,
engine->knockCount,
engineConfiguration->maxKnockSubDeg);
scheduleMsg(logger, " Adc input %s (%.2f V)",
getAdc_channel_e(engineConfiguration->hipOutputChannel),
getVoltage("hipinfo", engineConfiguration->hipOutputChannel));
scheduleMsg(logger, " IntHold %s (mode 0x%x)",
hwPortname(CONFIG(hip9011IntHoldPin)),
CONFIG(hip9011IntHoldPinMode));
scheduleMsg(logger, " Spi %s CS %s (mode 0x%x)",
getSpi_device_e(engineConfiguration->hip9011SpiDevice),
hwPortname(CONFIG(hip9011CsPin)),
CONFIG(hip9011CsPinMode));
#if EFI_PROD_CODE
printSpiConfig(logger, "hip9011", CONFIG(hip9011SpiDevice));
#endif /* EFI_PROD_CODE */
2015-07-10 06:01:56 -07:00
scheduleMsg(logger, " SPI good response %d incorrect response %d",
instance.correctResponsesCount,
instance.invalidResponsesCount);
scheduleMsg(logger, " hip %.2f vmax=%.2f",
engine->knockVolts,
hipValueMax);
scheduleMsg(logger, " Window start %.2f end %.2f",
engineConfiguration->knockDetectionWindowStart,
engineConfiguration->knockDetectionWindowEnd);
2015-07-10 06:01:56 -07:00
scheduleMsg(logger, " Counters: samples %d overruns %d sync miss %d",
instance.samples, instance.overrun, instance.unsync);
hipValueMax = 0;
engine->printKnockState();
}
static void setMaxKnockSubDeg(int value) {
engineConfiguration->maxKnockSubDeg = value;
showHipInfo();
}
static void setKnockThresh(float value) {
engineConfiguration->knockVThreshold = value;
showHipInfo();
}
static void setPrescalerAndSDO(int value) {
engineConfiguration->hip9011PrescalerAndSDO = value;
}
static void setHipBand(float value) {
engineConfiguration->knockBandCustom = value;
showHipInfo();
}
static void setHipGain(float value) {
engineConfiguration->hip9011Gain = value;
showHipInfo();
}
static void hip_addconsoleActions(void) {
addConsoleAction("hipinfo", showHipInfo);
2017-01-06 14:01:28 -08:00
addConsoleActionF("set_gain", setHipGain);
addConsoleActionF("set_band", setHipBand);
2015-07-10 06:01:56 -07:00
addConsoleActionI("set_hip_prescalerandsdo", setPrescalerAndSDO);
addConsoleActionF("set_knock_threshold", setKnockThresh);
addConsoleActionI("set_max_knock_sub_deg", setMaxKnockSubDeg);
}
2017-04-09 19:07:41 -07:00
#endif /* EFI_HIP_9011 */