rusefi-1/firmware/hw_layer/HIP9011.cpp

466 lines
14 KiB
C++

/**
* @file HIP9011.cpp
* @brief HIP9011/TPIC8101 driver
*
* 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
*
*
* http://rusefi.com/forum/viewtopic.php?f=4&t=400
* http://rusefi.com/forum/viewtopic.php?f=5&t=778
*
* pin1 VDD
* pin2 GND
*
* pin8 Chip Select - CS
* pin11 Slave Data Out - MISO
* 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
* @author Andrey Belomutskiy, (c) 2012-2018
* @Spilly
*/
#include "main.h"
#include "engine.h"
#include "settings.h"
#include "pin_repository.h"
#include "hardware.h"
#include "rpm_calculator.h"
#include "trigger_central.h"
#include "hip9011_lookup.h"
#include "HIP9011.h"
#include "adc_inputs.h"
#include "efilib2.h"
#include "engine_controller.h"
#if EFI_HIP_9011 || defined(__DOXYGEN__)
static NamedOutputPin intHold(HIP_NAME);
extern uint32_t lastExecutionCount;
extern EnginePins enginePins;
uint32_t hipLastExecutionCount;
/**
* band index is only send to HIP chip on startup
*/
static int currentBandIndex;
static int currentGainIndex = -1;
static int currentIntergratorIndex = -1;
static int settingUpdateCount = 0;
static int totalKnockEventsCount = 0;
static int currentPrescaler;
static float hipValueMax = 0;
static unsigned char tx_buff[1];
static unsigned char rx_buff[1];
int correctResponsesCount = 0;
int invalidResponsesCount = 0;
static char pinNameBuffer[16];
static float currentAngleWindowWidth;
/**
* Int/Hold pin is controlled from scheduler call-backs which are set according to current RPM
*
* The following state makes sure that we only have SPI communication while not integrating and that we take
* a good ADC reading after integrating.
*
* Once integration window is over, we wait for the 2nd ADC callback and then initiate SPI communication if needed
*
* hipOutput should be set to used FAST adc device
*/
static hip_state_e state = NOT_READY;
static scheduling_s startTimer[2];
static scheduling_s endTimer[2];
static Logging *logger;
// SPI_CR1_BR_1 // 5MHz
// SPI_CR1_CPHA Clock Phase
// todo: nicer method which would mention SPI speed explicitly?
static SPIConfig hipSpiCfg = { NULL,
/* HW dependent part.*/
NULL, 0,
SPI_CR1_MSTR |
//SPI_CR1_BR_1 // 5MHz
SPI_CR1_CPHA | SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR_2 };
static void checkResponse(void) {
if (tx_buff[0] == rx_buff[0]) {
correctResponsesCount++;
} else {
invalidResponsesCount++;
}
}
// this macro is only used on startup
#define SPI_SYNCHRONOUS(value) \
spiSelect(driver); \
tx_buff[0] = value; \
spiExchange(driver, 1, tx_buff, rx_buff); \
spiUnselect(driver); \
checkResponse();
static SPIDriver *driver;
EXTERN_ENGINE
;
static float getBand(void) {
return engineConfiguration->knockBandCustom == 0 ?
BAND(engineConfiguration->cylinderBore) : engineConfiguration->knockBandCustom;
}
static char hipPinNameBuffer[16];
static void showHipInfo(void) {
if (!boardConfiguration->isHip9011Enabled) {
scheduleMsg(logger, "hip9011 driver not active");
return;
}
printSpiState(logger, boardConfiguration);
scheduleMsg(logger, "enabled=%s state=%d bore=%.2fmm freq=%.2fkHz PaSDO=%d",
boolToString(boardConfiguration->isHip9011Enabled),
state,
engineConfiguration->cylinderBore, getBand(),
engineConfiguration->hip9011PrescalerAndSDO);
char *outputName = getPinNameByAdcChannel("hip", engineConfiguration->hipOutputChannel, hipPinNameBuffer);
scheduleMsg(logger, "band_index=%d gain %.2f/index=%d output=%s", currentBandIndex, boardConfiguration->hip9011Gain, currentGainIndex,
outputName);
scheduleMsg(logger, "integrator index=%d knockVThreshold=%.2f knockCount=%d maxKnockSubDeg=%.2f",
currentIntergratorIndex, engineConfiguration->knockVThreshold,
engine->knockCount, engineConfiguration->maxKnockSubDeg);
const char * msg = invalidResponsesCount > 0 ? "NOT GOOD" : "ok";
scheduleMsg(logger, "spi=%s IntHold@%s/%d response count=%d incorrect response=%d %s",
getSpi_device_e(engineConfiguration->hip9011SpiDevice),
hwPortname(boardConfiguration->hip9011IntHoldPin),
boardConfiguration->hip9011IntHoldPinMode,
correctResponsesCount, invalidResponsesCount,
msg);
scheduleMsg(logger, "CS@%s updateCount=%d", hwPortname(boardConfiguration->hip9011CsPin), settingUpdateCount);
scheduleMsg(logger, "hip %.2fv/last=%.2f@%s/max=%.2f adv=%d",
engine->knockVolts,
getVoltage("hipinfo", engineConfiguration->hipOutputChannel),
getPinNameByAdcChannel("hip", engineConfiguration->hipOutputChannel, pinNameBuffer),
hipValueMax,
boardConfiguration->useTpicAdvancedMode);
scheduleMsg(logger, "mosi=%s", hwPortname(getMosiPin(engineConfiguration->hip9011SpiDevice)));
scheduleMsg(logger, "miso=%s", hwPortname(getMisoPin(engineConfiguration->hip9011SpiDevice)));
scheduleMsg(logger, "sck=%s", hwPortname(getSckPin(engineConfiguration->hip9011SpiDevice)));
scheduleMsg(logger, "start %.2f end %.2f", engineConfiguration->knockDetectionWindowStart,
engineConfiguration->knockDetectionWindowEnd);
hipValueMax = 0;
engine->printKnockState();
}
void setHip9011FrankensoPinout(void) {
/**
* SPI on PB13/14/15
*/
// boardConfiguration->hip9011CsPin = GPIOD_0; // rev 0.1
boardConfiguration->isHip9011Enabled = true;
boardConfiguration->hip9011CsPin = GPIOB_0; // rev 0.4
boardConfiguration->hip9011CsPinMode = OM_OPENDRAIN;
engineConfiguration->hip9011PrescalerAndSDO = 6; // 8MHz chip
boardConfiguration->hip9011IntHoldPin = GPIOB_11;
boardConfiguration->hip9011IntHoldPinMode = OM_OPENDRAIN;
boardConfiguration->is_enabled_spi_2 = true;
// todo: convert this to rusEfi, hardware-independent enum
engineConfiguration->spi2SckMode = PAL_STM32_OTYPE_OPENDRAIN; // 4
engineConfiguration->spi2MosiMode = PAL_STM32_OTYPE_OPENDRAIN; // 4
engineConfiguration->spi2MisoMode = PAL_STM32_PUPDR_PULLUP; // 32
boardConfiguration->hip9011Gain = 1;
engineConfiguration->knockVThreshold = 4;
engineConfiguration->maxKnockSubDeg = 20;
if (!boardConfiguration->useTpicAdvancedMode) {
engineConfiguration->hipOutputChannel = EFI_ADC_10; // PC0
}
}
static void startIntegration(void) {
if (state == READY_TO_INTEGRATE) {
/**
* SPI communication is only allowed while not integrating, so we postpone the exchange
* until we are done integrating
*/
state = IS_INTEGRATING;
intHold.setHigh();
}
}
static void endIntegration(void) {
/**
* isIntegrating could be 'false' if an SPI command was pending thus we did not integrate during this
* engine cycle
*/
if (state == IS_INTEGRATING) {
intHold.setLow();
state = WAITING_FOR_ADC_TO_SKIP;
}
}
/**
* Shaft Position callback used to start or finish HIP integration
*/
static void intHoldCallback(trigger_event_e ckpEventType, uint32_t index DECLARE_ENGINE_PARAMETER_SUFFIX) {
// this callback is invoked on interrupt thread
if (index != 0)
return;
engine->m.beforeHipCb = GET_TIMESTAMP();
int rpm = engine->rpmCalculator.rpmValue;
if (!isValidRpm(rpm))
return;
int structIndex = getRevolutionCounter() % 2;
// todo: schedule this based on closest trigger event, same as ignition works
scheduleByAngle(rpm, &startTimer[structIndex], engineConfiguration->knockDetectionWindowStart,
(schfunc_t) &startIntegration, NULL, &engine->rpmCalculator);
hipLastExecutionCount = lastExecutionCount;
scheduleByAngle(rpm, &endTimer[structIndex], engineConfiguration->knockDetectionWindowEnd,
(schfunc_t) &endIntegration,
NULL, &engine->rpmCalculator);
engine->m.hipCbTime = GET_TIMESTAMP() - engine->m.beforeHipCb;
}
void setMaxKnockSubDeg(int value) {
engineConfiguration->maxKnockSubDeg = value;
showHipInfo();
}
void setKnockThresh(float value) {
engineConfiguration->knockVThreshold = value;
showHipInfo();
}
void setPrescalerAndSDO(int value) {
engineConfiguration->hip9011PrescalerAndSDO = value;
}
void setHipBand(float value) {
engineConfiguration->knockBandCustom = value;
showHipInfo();
}
void setHipGain(float value) {
boardConfiguration->hip9011Gain = value;
showHipInfo();
}
/**
* this is the end of the non-synchronous exchange
*/
static void endOfSpiExchange(SPIDriver *spip) {
spiUnselectI(driver);
state = READY_TO_INTEGRATE;
checkResponse();
}
static int getBandIndex(void) {
float freq = getBand();
return getHip9011BandIndex(freq);
}
void hipAdcCallback(adcsample_t value) {
if (state == WAITING_FOR_ADC_TO_SKIP) {
state = WAITING_FOR_RESULT_ADC;
} else if (state == WAITING_FOR_RESULT_ADC) {
engine->knockVolts = adcToVoltsDivided(value);
hipValueMax = maxF(engine->knockVolts, hipValueMax);
engine->knockLogic(engine->knockVolts);
float angleWindowWidth =
engineConfiguration->knockDetectionWindowEnd - engineConfiguration->knockDetectionWindowStart;
if (angleWindowWidth != currentAngleWindowWidth) {
currentAngleWindowWidth = angleWindowWidth;
prepareHip9011RpmLookup(currentAngleWindowWidth);
}
int integratorIndex = getIntegrationIndexByRpm(engine->rpmCalculator.rpmValue);
int gainIndex = getHip9011GainIndex(boardConfiguration->hip9011Gain);
int bandIndex = getBandIndex();
int prescalerIndex = engineConfiguration->hip9011PrescalerAndSDO;
if (currentGainIndex != gainIndex) {
currentGainIndex = gainIndex;
tx_buff[0] = SET_GAIN_CMD + gainIndex;
state = IS_SENDING_SPI_COMMAND;
spiSelectI(driver);
spiStartExchangeI(driver, 1, tx_buff, rx_buff);
} else if (currentIntergratorIndex != integratorIndex) {
currentIntergratorIndex = integratorIndex;
tx_buff[0] = SET_INTEGRATOR_CMD + integratorIndex;
state = IS_SENDING_SPI_COMMAND;
spiSelectI(driver);
spiStartExchangeI(driver, 1, tx_buff, rx_buff);
} else if (currentBandIndex != bandIndex) {
currentBandIndex = bandIndex;
tx_buff[0] = SET_BAND_PASS_CMD + bandIndex;
state = IS_SENDING_SPI_COMMAND;
spiSelectI(driver);
spiStartExchangeI(driver, 1, tx_buff, rx_buff);
} else if (currentPrescaler != prescalerIndex) {
currentPrescaler = prescalerIndex;
tx_buff[0] = SET_PRESCALER_CMD + prescalerIndex;
state = IS_SENDING_SPI_COMMAND;
spiSelectI(driver);
spiStartExchangeI(driver, 1, tx_buff, rx_buff);
} else {
state = READY_TO_INTEGRATE;
}
}
}
static bool needToInit = true;
static void hipStartupCode(void) {
// D[4:1] = 0000 : 4 MHz
// D[4:1] = 0001 : 5 MHz
// D[4:1] = 0010 : 6 MHz
// D[4:1] = 0011 ; 8 MHz
// D[4:1] = 0100 ; 10 MHz
// D[4:1] = 0101 ; 12 MHz
// D[4:1] = 0110 : 16 MHz
// D[4:1] = 0111 : 20 MHz
// D[4:1] = 1000 : 24 MHz
// 0 for 4MHz
// 6 for 8 MHz
currentPrescaler = engineConfiguration->hip9011PrescalerAndSDO;
SPI_SYNCHRONOUS(SET_PRESCALER_CMD + currentPrescaler);
chThdSleepMilliseconds(10);
// '0' for channel #1
SPI_SYNCHRONOUS(SET_CHANNEL_CMD + 0);
chThdSleepMilliseconds(10);
// band index depends on cylinder bore
SPI_SYNCHRONOUS(SET_BAND_PASS_CMD + currentBandIndex);
chThdSleepMilliseconds(10);
if (correctResponsesCount == 0) {
warning(CUSTOM_OBD_KNOCK_PROCESSOR, "TPIC/HIP does not respond");
}
if (boardConfiguration->useTpicAdvancedMode) {
// enable advanced mode for digital integrator output
SPI_SYNCHRONOUS(SET_ADVANCED_MODE);
chThdSleepMilliseconds(10);
}
/**
* Let's restart SPI to switch it from synchronous mode into
* asynchronous mode
*/
spiStop(driver);
hipSpiCfg.end_cb = endOfSpiExchange;
spiStart(driver, &hipSpiCfg);
state = READY_TO_INTEGRATE;
}
static THD_WORKING_AREA(hipTreadStack, UTILITY_THREAD_STACK_SIZE);
static msg_t hipThread(void *arg) {
chRegSetThreadName("hip9011 init");
// some time to let the hardware start
enginePins.hipCs.setValue(true);
chThdSleepMilliseconds(100);
enginePins.hipCs.setValue(false);
chThdSleepMilliseconds(100);
enginePins.hipCs.setValue(true);
while (true) {
chThdSleepMilliseconds(100);
if (needToInit) {
hipStartupCode();
needToInit = false;
}
}
return -1;
}
void initHip9011(Logging *sharedLogger) {
logger = sharedLogger;
addConsoleAction("hipinfo", showHipInfo);
if (!boardConfiguration->isHip9011Enabled)
return;
currentAngleWindowWidth =
engineConfiguration->knockDetectionWindowEnd - engineConfiguration->knockDetectionWindowStart;
prepareHip9011RpmLookup(currentAngleWindowWidth);
driver = getSpiDevice(engineConfiguration->hip9011SpiDevice);
hipSpiCfg.ssport = getHwPort("hip", boardConfiguration->hip9011CsPin);
hipSpiCfg.sspad = getHwPin("hip", boardConfiguration->hip9011CsPin);
intHold.initPin("hip int/hold", boardConfiguration->hip9011IntHoldPin,
&boardConfiguration->hip9011IntHoldPinMode);
enginePins.hipCs.initPin("hip CS", boardConfiguration->hip9011CsPin,
&boardConfiguration->hip9011CsPinMode);
scheduleMsg(logger, "Starting HIP9011/TPIC8101 driver");
spiStart(driver, &hipSpiCfg);
currentBandIndex = getBandIndex();
/**
* this engine cycle callback would be scheduling actual integration start and end callbacks
*/
addTriggerEventListener(&intHoldCallback, "DD int/hold", engine);
// MISO PB14
// palSetPadMode(GPIOB, 14, PAL_MODE_ALTERNATE(EFI_SPI2_AF) | PAL_STM32_PUDR_PULLUP);
// MOSI PB15
// palSetPadMode(GPIOB, 15, PAL_MODE_ALTERNATE(EFI_SPI2_AF) | PAL_STM32_OTYPE_OPENDRAIN);
addConsoleActionF("set_gain", setHipGain);
addConsoleActionF("set_band", setHipBand);
addConsoleActionI("set_hip_prescalerandsdo", setPrescalerAndSDO);
addConsoleActionF("set_knock_threshold", setKnockThresh);
addConsoleActionI("set_max_knock_sub_deg", setMaxKnockSubDeg);
chThdCreateStatic(hipTreadStack, sizeof(hipTreadStack), NORMALPRIO, (tfunc_t) hipThread, NULL);
}
#endif /* EFI_HIP_9011 */