/* * @file mc33816.cpp * * TL,DR: GDI * * The NXP MC33816 is a programmable gate driver IC for precision solenoid control applications. * * * Useful wires: * 5v, 3(3.3v), Gnd, 12v, VccIO(3v) SPI, DRVEN, RSTB * * For MC33816 vs PT2000 differences see * https://www.nxp.com/docs/en/application-note/AN5203.pdf * * @date May 3, 2019 * @author Andrey Belomutskiy, (c) 2012-2020 */ #include "pch.h" #if EFI_MC33816 #include "hardware.h" #include "mpu_util.h" #include "ignition_controller.h" static SPIConfig spiCfg = { .circular = false, #ifdef _CHIBIOS_RT_CONF_VER_6_1_ .end_cb = NULL, #else .slave = false, .data_cb = NULL, .error_cb = NULL, #endif .ssport = nullptr, .sspad = 0, .cr1 = SPI_CR1_MSTR | SPI_CR1_SSM | // Software Slave Management, the SSI bit will be internal reference SPI_CR1_SSI | // Internal Slave Select (active low) set High SPI_CR1_CPHA | //SPI_CR1_BR_1 // 5MHz SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR_2 | SPI_CR1_SPE, .cr2 = SPI_CR2_SSOE | SPI_CR2_16BIT_MODE }; class Pt2001 : public Pt2001Base { public: bool init(); void initIfNeeded(); protected: void acquireBus() override { criticalAssertVoid(driver != nullptr, "mc33816"); spiAcquireBus(driver); } void releaseBus() override { spiReleaseBus(driver); } void select() override { criticalAssertVoid(driver != nullptr, "mc33816"); // revive MC33816 driver, also support bus sharing #6781 // should be somewhere but not here spiStart(driver, &spiCfg); // efiPrintf("mc select %s", hwOnChipPhysicalPinName(driver->config->ssport, driver->config->sspad)); spiSelect(driver); //chris - "no implementation on stm32" chipSelect.setValue(0); } void deselect() override { spiUnselect(driver); //chris - "no implementation on stm32" chipSelect.setValue(1); } uint16_t sendRecv(uint16_t tx) override { return spiPolledExchange(driver, tx); } // Send `count` number of 16 bit words from `data` void sendLarge(const uint16_t* data, size_t count) override { spiSend(driver, count, data); } // GPIO reset and enable pins void setResetB(bool state) override { resetB.setValue(state); } void setDriveEN(bool state) override { driven.setValue(state); } // GPIO inputs for various pins we need bool readFlag0() const override { return efiReadPin(engineConfiguration->mc33816_flag0); } // Get battery voltage - only try to init chip when powered float getVbatt() const override { // reminder that we do not have sensor subsystem right away return Sensor::get(SensorType::BatteryVoltage).value_or(0); } // CONFIGURATIONS: currents, timings, voltages float getBoostVoltage() const override { return engineConfiguration->mc33_hvolt; } // Currents in amps float getBoostCurrent() const override { return engineConfiguration->mc33_i_boost; } float getPeakCurrent() const override { return engineConfiguration->mc33_i_peak; } float getHoldCurrent() const override { return engineConfiguration->mc33_i_hold; } float getPumpPeakCurrent() const override { return engineConfiguration->mc33_hpfp_i_peak; } float getPumpHoldCurrent() const override { return engineConfiguration->mc33_hpfp_i_hold; } // Timings in microseconds uint16_t getTpeakOff() const override { return engineConfiguration->mc33_t_peak_off; } uint16_t getTpeakTot() const override { return engineConfiguration->mc33_t_peak_tot; } uint16_t getTbypass() const override { return engineConfiguration->mc33_t_bypass; } uint16_t getTholdOff() const override { return engineConfiguration->mc33_t_hold_off; } uint16_t getTHoldTot() const override { return engineConfiguration->mc33_t_hold_tot; } uint16_t getTBoostMin() const override { return engineConfiguration->mc33_t_min_boost; } uint16_t getTBoostMax() const override { return engineConfiguration->mc33_t_max_boost; } uint16_t getPumpTholdOff() const override { return engineConfiguration->mc33_hpfp_i_hold_off; } uint16_t getPumpTholdTot() const override { return engineConfiguration->mc33_hpfp_max_hold; } // Print out an error message void onError(const char* why) override { efiPrintf("PT2001 error: %s", why); } bool errorOnUnexpectedFlag() override { efiPrintf("****** unexpected mc33 flag state ******"); return false; } void sleepMs(size_t ms) override { chThdSleepMilliseconds(ms); } private: SPIDriver* driver; OutputPin resetB; OutputPin driven; OutputPin chipSelect; }; static Pt2001 pt; /** * returns true if chip has configuration */ bool Pt2001::init() { // // see setTest33816EngineConfiguration for default configuration // Pins if (!isBrainPinValid(engineConfiguration->mc33816_cs) || !isBrainPinValid(engineConfiguration->mc33816_rstb) || !isBrainPinValid(engineConfiguration->mc33816_driven)) { return false; } if (isBrainPinValid(engineConfiguration->mc33816_flag0)) { efiSetPadMode("mc33816 flag0", engineConfiguration->mc33816_flag0, getInputMode(PI_DEFAULT)); } chipSelect.initPin("mc33 CS", engineConfiguration->mc33816_cs /*, &engineConfiguration->csPinMode*/); chipSelect.setValue(1); // Initialize the chip via ResetB resetB.initPin("mc33 RESTB", engineConfiguration->mc33816_rstb); // High Voltage via DRIVEN driven.initPin("mc33 DRIVEN", engineConfiguration->mc33816_driven); spiCfg.ssport = getHwPort("mc33816", engineConfiguration->mc33816_cs); spiCfg.sspad = getHwPin("mc33816", engineConfiguration->mc33816_cs); #if HW_MICRO_RUSEFI // hard-coded for now, just resolve the conflict with SD card! engineConfiguration->mc33816spiDevice = SPI_DEVICE_3; #endif if (engineConfiguration->mc33816spiDevice == SPI_NONE) { criticalError("mc33816 needs SPI selection"); } driver = getSpiDevice(engineConfiguration->mc33816spiDevice); if (driver == nullptr) { // error already reported return false; } spiStart(driver, &spiCfg); efiPrintf("mc33 started SPI"); // addConsoleAction("mc33_stats", showStats); addConsoleAction("mc33_restart", [](){ pt.initIfNeeded(); }); // todo: too soon to read voltage, it has to fail, right?! initIfNeeded(); return true; } static bool isInitialized = false; void Pt2001::initIfNeeded() { if (!isIgnVoltage()) { isInitialized = false; efiPrintf("unhappy mc33 due to battery voltage"); } else { if (!isInitialized) { isInitialized = restart(); if (isInitialized) { efiPrintf("happy mc33/PT2001!"); } else { efiPrintf("unhappy mc33 fault=%d %s", (int)fault, mcFaultToString(fault)); } } } } static THD_WORKING_AREA(mc33_thread_wa, 256); static THD_FUNCTION(mc33_driver_thread, p) { (void)p; while (true) { if (!isIgnVoltage()) { if (isInitialized) { efiPrintf("Power loss? Would have to re-init mc33/PT2001?"); isInitialized = false; } chThdSleepMilliseconds(100); continue; } pt.initIfNeeded(); chThdSleepMilliseconds(100); } } void initMc33816() { bool isConfigured = pt.init(); if (isConfigured) { chThdCreateStatic(mc33_thread_wa, sizeof(mc33_thread_wa), PRIO_GPIOCHIP, mc33_driver_thread, nullptr); } } #endif /* EFI_MC33816 */