| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /* | |||
| 2 | * @file pt2001.cpp | |||
| 3 | * | |||
| 4 | * The NXP PT2001 is a programmable gate driver IC for precision solenoid control applications. | |||
| 5 | * | |||
| 6 | * Useful wires: | |||
| 7 | * 5v, 3(3.3v), Gnd, 12v, VccIO(3v) SPI, DRVEN, RSTB | |||
| 8 | * | |||
| 9 | * For MC33816 vs PT2000 differences see | |||
| 10 | * https://www.nxp.com/docs/en/application-note/AN5203.pdf | |||
| 11 | * | |||
| 12 | * @date May 3, 2019 | |||
| 13 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 14 | * @author Matthew Kennedy | |||
| 15 | */ | |||
| 16 | ||||
| 17 | #include <rusefi/pt2001.h> | |||
| 18 | #include <rusefi/arrays.h> | |||
| 19 | ||||
| 20 | #include <PT2001_LoadData.h> | |||
| 21 | ||||
| 22 | const int MC_CK = 6; // PLL x24 / CLK_DIV 4 = 6Mhz | |||
| 23 | ||||
| 24 | const int MAX_SPI_MODE_A_TRANSFER_SIZE = 31; //max size for register config transfer | |||
| 25 | ||||
| 26 | enum { | |||
| 27 | CODE_RAM1, | |||
| 28 | CODE_RAM2, | |||
| 29 | DATA_RAM | |||
| 30 | }; | |||
| 31 | enum { | |||
| 32 | REG_MAIN, | |||
| 33 | REG_CH1, | |||
| 34 | REG_CH2, | |||
| 35 | REG_IO, | |||
| 36 | REG_DIAG | |||
| 37 | }; | |||
| 38 | ||||
| 39 | ✗ | static bool validateChipId(uint16_t id) { | ||
| 40 | ✗ | return (id >> 8) == 0x9D; | ||
| 41 | } | |||
| 42 | ||||
| 43 | ✗ | void Pt2001Base::setupSpi() { | ||
| 44 | ✗ | select(); | ||
| 45 | // Select Channel command | |||
| 46 | ✗ | send(0x7FE1); | ||
| 47 | // Common Page | |||
| 48 | ✗ | send(0x0004); | ||
| 49 | ||||
| 50 | ||||
| 51 | // Configure SPI command | |||
| 52 | ✗ | send(0x3901); | ||
| 53 | // Mode A + Watchdog timer full | |||
| 54 | //send(0x001F); | |||
| 55 | ✗ | send(0x009F); // + fast slew rate on miso | ||
| 56 | ✗ | deselect(); | ||
| 57 | ✗ | } | ||
| 58 | ||||
| 59 | ✗ | uint16_t Pt2001Base::readId() { | ||
| 60 | ✗ | select(); | ||
| 61 | ✗ | send(0xBAA1); | ||
| 62 | ✗ | uint16_t ID = recv(); | ||
| 63 | ✗ | deselect(); | ||
| 64 | ✗ | return ID; | ||
| 65 | } | |||
| 66 | ||||
| 67 | // Read a single word in Data RAM | |||
| 68 | ✗ | uint16_t Pt2001Base::readDram(MC33816Mem addr) { | ||
| 69 | ✗ | uint16_t addrInt = static_cast<uint16_t>(addr); | ||
| 70 | ||||
| 71 | ✗ | select(); | ||
| 72 | // Select Channel command, Common Page | |||
| 73 | ✗ | send(0x7FE1); | ||
| 74 | ✗ | send(0x0004); | ||
| 75 | // read (MSB=1) at data ram x9 (SCV_I_Hold), and 1 word | |||
| 76 | ✗ | send((0x8000 | addrInt << 5) + 1); | ||
| 77 | ✗ | uint16_t readValue = recv(); | ||
| 78 | ||||
| 79 | ✗ | deselect(); | ||
| 80 | ✗ | return readValue; | ||
| 81 | } | |||
| 82 | ||||
| 83 | // Update a single word in Data RAM | |||
| 84 | ✗ | void Pt2001Base::writeDram(MC33816Mem addr, uint16_t data) { | ||
| 85 | ✗ | uint16_t addrInt = static_cast<uint16_t>(addr); | ||
| 86 | ||||
| 87 | ✗ | select(); | ||
| 88 | // Select Channel command, Common Page | |||
| 89 | ✗ | send(0x7FE1); | ||
| 90 | ✗ | send(0x0004); | ||
| 91 | // write (MSB=0) at data ram x9 (SCV_I_Hold), and 1 word | |||
| 92 | ✗ | send((addrInt << 5) + 1); | ||
| 93 | ✗ | send(data); | ||
| 94 | ||||
| 95 | ✗ | deselect(); | ||
| 96 | ✗ | } | ||
| 97 | ||||
| 98 | ✗ | static uint16_t dacEquation(float current) { | ||
| 99 | /* | |||
| 100 | Current, given in A | |||
| 101 | I = (DAC_VALUE * V_DAC_LSB - V_DA_BIAS)/(G_DA_DIFF * R_SENSEx) | |||
| 102 | DAC_VALUE = ((I*G_DA_DIFF * R_SENSEx) + V_DA_BIAS) / V_DAC_LSB | |||
| 103 | V_DAC_LSB is the DAC resolution = 9.77mv | |||
| 104 | V_DA_BIAS = 250mV | |||
| 105 | G_DA_DIFF = Gain: 5.79, 8.68, [12.53], 19.25 | |||
| 106 | R_SENSE = 10mOhm soldered on board | |||
| 107 | */ | |||
| 108 | ✗ | return ((current * 12.53f * 10) + 250.0f) / 9.77f; | ||
| 109 | } | |||
| 110 | ||||
| 111 | ✗ | void Pt2001Base::setTimings() { | ||
| 112 | ✗ | setBoostVoltage(getBoostVoltage()); | ||
| 113 | ||||
| 114 | // Convert mA to DAC values | |||
| 115 | ✗ | writeDram(MC33816Mem::Iboost, dacEquation(getBoostCurrent())); | ||
| 116 | ✗ | writeDram(MC33816Mem::Ipeak, dacEquation(getPeakCurrent())); | ||
| 117 | ✗ | writeDram(MC33816Mem::Ihold, dacEquation(getHoldCurrent())); | ||
| 118 | ||||
| 119 | // in micro seconds to clock cycles | |||
| 120 | ✗ | writeDram(MC33816Mem::Tpeak_off, (MC_CK * getTpeakOff())); | ||
| 121 | ✗ | writeDram(MC33816Mem::Tpeak_tot, (MC_CK * getTpeakTot())); | ||
| 122 | ✗ | writeDram(MC33816Mem::Tbypass, (MC_CK * getTbypass())); | ||
| 123 | ✗ | writeDram(MC33816Mem::Thold_off, (MC_CK * getTholdOff())); | ||
| 124 | ✗ | writeDram(MC33816Mem::Thold_tot, (MC_CK * getTHoldTot())); | ||
| 125 | ✗ | writeDram(MC33816Mem::Tboost_min, (MC_CK * getTBoostMin())); | ||
| 126 | ✗ | writeDram(MC33816Mem::Tboost_max, (MC_CK * getTBoostMax())); | ||
| 127 | ||||
| 128 | // HPFP solenoid settings | |||
| 129 | ✗ | writeDram(MC33816Mem::HPFP_Ipeak, dacEquation(getPumpPeakCurrent())); | ||
| 130 | ✗ | writeDram(MC33816Mem::HPFP_Ihold, dacEquation(getPumpHoldCurrent())); | ||
| 131 | ✗ | writeDram(MC33816Mem::HPFP_Thold_off, MC_CK * getPumpTholdOff()); | ||
| 132 | ✗ | writeDram(MC33816Mem::HPFP_Thold_tot, MC_CK * getPumpTholdTot()); | ||
| 133 | ✗ | } | ||
| 134 | ||||
| 135 | ✗ | void Pt2001Base::setBoostVoltage(float volts) { | ||
| 136 | // Sanity checks, Datasheet says not too high, nor too low | |||
| 137 | ✗ | if (volts > 72.0f) { | ||
| 138 | ✗ | onError("DI Boost voltage setpoint too high"); | ||
| 139 | ✗ | return; | ||
| 140 | } | |||
| 141 | ✗ | if (volts < 10.0f) { | ||
| 142 | ✗ | onError("DI Boost voltage setpoint too low"); | ||
| 143 | ✗ | return; | ||
| 144 | } | |||
| 145 | ||||
| 146 | // There's a 1/32 divider on the input, then the DAC's output is 9.77mV per LSB. (1 / 32) / 0.00977 = 3.199 counts per volt. | |||
| 147 | ✗ | uint16_t data = (volts * 3.25f) + 1.584f; | ||
| 148 | ✗ | writeDram(MC33816Mem::Vboost_high, data+1); | ||
| 149 | ✗ | writeDram(MC33816Mem::Vboost_low, data-1); | ||
| 150 | // Remember to strobe driven!! | |||
| 151 | } | |||
| 152 | ||||
| 153 | ✗ | bool Pt2001Base::checkFlash() { | ||
| 154 | ✗ | select(); | ||
| 155 | ||||
| 156 | // ch1 | |||
| 157 | // read (MSB=1) at location, and 1 word | |||
| 158 | ✗ | send((0x8000 | 0x100 << 5) + 1); | ||
| 159 | ✗ | if (!(recv() & (1<<5))) { | ||
| 160 | ✗ | deselect(); | ||
| 161 | ✗ | return false; | ||
| 162 | } | |||
| 163 | ||||
| 164 | // ch2 | |||
| 165 | // read (MSB=1) at location, and 1 word | |||
| 166 | ✗ | send((0x8000 | 0x120 << 5) + 1); | ||
| 167 | ||||
| 168 | ✗ | if (!(recv() & (1<<5))) { | ||
| 169 | ✗ | deselect(); | ||
| 170 | ✗ | return false; | ||
| 171 | } | |||
| 172 | ||||
| 173 | ✗ | deselect(); | ||
| 174 | ✗ | return true; | ||
| 175 | } | |||
| 176 | ||||
| 177 | ✗ | void Pt2001Base::clearDriverStatus(){ | ||
| 178 | // Note: There is a config at 0x1CE & 1 that can reset this status config register on read | |||
| 179 | // otherwise the reload/recheck occurs with this write | |||
| 180 | // resetting it is necessary to clear default reset behavoir, as well as if an issue has been resolved | |||
| 181 | ✗ | setupSpi(); // ensure on common page? | ||
| 182 | ✗ | select(); | ||
| 183 | ✗ | send((0x0000 | 0x1D2 << 5) + 1); // write, location, one word | ||
| 184 | ✗ | send(0x0000); // anything to clear | ||
| 185 | ✗ | deselect(); | ||
| 186 | ✗ | } | ||
| 187 | ||||
| 188 | ✗ | uint16_t Pt2001Base::readStatus(int reg) { | ||
| 189 | ✗ | setupSpi(); // ensure on common page? | ||
| 190 | ✗ | select(); | ||
| 191 | ✗ | send((0x8000 | reg << 5) + 1); | ||
| 192 | ✗ | uint16_t driverStatus = recv(); | ||
| 193 | ✗ | deselect(); | ||
| 194 | ✗ | return driverStatus; | ||
| 195 | } | |||
| 196 | ||||
| 197 | ✗ | uint16_t Pt2001Base::readDriverStatus() { | ||
| 198 | ✗ | return readStatus(0x1D2); | ||
| 199 | } | |||
| 200 | ||||
| 201 | ✗ | static bool checkUndervoltVccP(uint16_t driverStatus){ | ||
| 202 | ✗ | return (driverStatus & (1<<0)); | ||
| 203 | } | |||
| 204 | ||||
| 205 | ✗ | static bool checkUndervoltV5(uint16_t driverStatus){ | ||
| 206 | ✗ | return (driverStatus & (1<<1)); | ||
| 207 | } | |||
| 208 | ||||
| 209 | // static bool checkOverTemp(uint16_t driverStatus){ | |||
| 210 | // return (driverStatus & (1<<3)); | |||
| 211 | // } | |||
| 212 | ||||
| 213 | ✗ | static bool checkDrivenEnabled(uint16_t driverStatus){ | ||
| 214 | ✗ | return (driverStatus & (1<<4)); | ||
| 215 | } | |||
| 216 | ||||
| 217 | ✗ | void Pt2001Base::enableFlash() { | ||
| 218 | ✗ | select(); | ||
| 219 | ✗ | send(0x2001); //ch1 | ||
| 220 | ✗ | send(0x0018); //enable flash | ||
| 221 | ✗ | send(0x2401); //ch2 | ||
| 222 | ✗ | send(0x0018); // enable flash | ||
| 223 | ✗ | deselect(); | ||
| 224 | ✗ | } | ||
| 225 | ||||
| 226 | ✗ | void Pt2001Base::periodicCallback() { | ||
| 227 | // todo: read status/diag via SPI | |||
| 228 | ✗ | } | ||
| 229 | ||||
| 230 | ✗ | void Pt2001Base::downloadRam(int target) { | ||
| 231 | ✗ | uint16_t memory_area = 0; // memory area | ||
| 232 | ✗ | uint16_t start_address = 0; // start address | ||
| 233 | ✗ | uint16_t codeWidthRegAddr = 0; // code width register address | ||
| 234 | ✗ | uint16_t size = 0; // size of RAM data | ||
| 235 | ✗ | uint16_t command = 0; // command data | ||
| 236 | ✗ | const uint16_t *RAM_ptr = nullptr; // pointer to array of data to be sent to the chip | ||
| 237 | ||||
| 238 | //Why Again? For Every time, just in case? | |||
| 239 | ✗ | setupSpi(); | ||
| 240 | ||||
| 241 | ✗ | switch(target) // selects target | ||
| 242 | { | |||
| 243 | ✗ | case CODE_RAM1: | ||
| 244 | ✗ | memory_area = 0x1; | ||
| 245 | ✗ | start_address = 0; | ||
| 246 | ✗ | codeWidthRegAddr = 0x107; | ||
| 247 | ✗ | RAM_ptr = PT2001_code_RAM1; | ||
| 248 | // todo: use efi::size? | |||
| 249 | ✗ | size = sizeof(PT2001_code_RAM1) / 2; | ||
| 250 | ✗ | break; | ||
| 251 | ||||
| 252 | ✗ | case CODE_RAM2: | ||
| 253 | ✗ | memory_area = 0x2; | ||
| 254 | ✗ | start_address = 0; | ||
| 255 | ✗ | codeWidthRegAddr = 0x127; | ||
| 256 | ✗ | RAM_ptr = PT2001_code_RAM2; | ||
| 257 | // todo: use efi::size? | |||
| 258 | ✗ | size = sizeof(PT2001_code_RAM2) / 2; | ||
| 259 | ✗ | break; | ||
| 260 | ||||
| 261 | ✗ | case DATA_RAM: // ch1 only? | ||
| 262 | ✗ | memory_area = 0x4; | ||
| 263 | ✗ | start_address = 0; | ||
| 264 | ✗ | RAM_ptr = PT2001_data_RAM; | ||
| 265 | // todo: use efi::size? | |||
| 266 | ✗ | size = sizeof(PT2001_data_RAM) / 2; | ||
| 267 | ✗ | break; | ||
| 268 | // optional, both data_rams with 0x3, writes same code to both | |||
| 269 | ✗ | default: | ||
| 270 | ✗ | break; | ||
| 271 | } | |||
| 272 | ||||
| 273 | // Chip-Select high | |||
| 274 | ✗ | select(); | ||
| 275 | ||||
| 276 | ✗ | if (target != DATA_RAM) | ||
| 277 | { | |||
| 278 | ✗ | command = codeWidthRegAddr << 5; // control width register address | ||
| 279 | ✗ | command |= 1; // number of words to follow | ||
| 280 | ✗ | send(command); // sends code_width command | ||
| 281 | ✗ | send(size); // sends size (Code Width) | ||
| 282 | } | |||
| 283 | ||||
| 284 | // Select Channel command | |||
| 285 | ✗ | send(0x7FE1); | ||
| 286 | // RAM1, RAM2, or Common Page (Data RAM) | |||
| 287 | ✗ | send(memory_area); | ||
| 288 | ||||
| 289 | // "Command" of starting address | |||
| 290 | // up to 0x03FE of code ram | |||
| 291 | // up to 0x0080 of data ram | |||
| 292 | ✗ | command = start_address << 5; | ||
| 293 | ✗ | send(command); // sends start address command | ||
| 294 | ||||
| 295 | ✗ | sendLarge(RAM_ptr, size); | ||
| 296 | ✗ | deselect(); | ||
| 297 | ✗ | } | ||
| 298 | ||||
| 299 | ✗ | void Pt2001Base::downloadRegister(int r_target) { | ||
| 300 | ✗ | uint16_t r_start_address = 0; // start address | ||
| 301 | ✗ | uint16_t r_size = 0; // size of configuration data | ||
| 302 | ✗ | uint16_t r_command = 0; // command data | ||
| 303 | ✗ | uint16_t remainder_size = 0; // remainder size | ||
| 304 | ✗ | const uint16_t *reg_ptr = nullptr; // pointer to array of data to be sent to the chip | ||
| 305 | ||||
| 306 | ✗ | switch(r_target) // selects target | ||
| 307 | { | |||
| 308 | ✗ | case REG_CH1: // channel 1 configurations | ||
| 309 | ✗ | r_start_address = 0x100; | ||
| 310 | ✗ | reg_ptr = PT2001_ch1_config; | ||
| 311 | ✗ | r_size = efi::size(PT2001_ch1_config); | ||
| 312 | ✗ | break; | ||
| 313 | ||||
| 314 | ✗ | case REG_CH2: // channel 2 configurations | ||
| 315 | ✗ | r_start_address = 0x120; | ||
| 316 | ✗ | reg_ptr = PT2001_ch2_config; | ||
| 317 | ✗ | r_size = efi::size(PT2001_ch2_config); | ||
| 318 | ✗ | break; | ||
| 319 | ||||
| 320 | ✗ | case REG_DIAG: // diagnostic configurations | ||
| 321 | ✗ | r_start_address = 0x140; | ||
| 322 | ✗ | reg_ptr = PT2001_diag_config; | ||
| 323 | ✗ | r_size = efi::size(PT2001_diag_config); | ||
| 324 | ✗ | break; | ||
| 325 | ||||
| 326 | ✗ | case REG_IO: // IO configurations | ||
| 327 | ✗ | r_start_address = 0x180; | ||
| 328 | ✗ | reg_ptr = PT2001_io_config; | ||
| 329 | ✗ | r_size = efi::size(PT2001_io_config); | ||
| 330 | ✗ | break; | ||
| 331 | ||||
| 332 | ✗ | case REG_MAIN: // main configurations | ||
| 333 | ✗ | r_start_address = 0x1C0; | ||
| 334 | ✗ | reg_ptr = PT2001_main_config; | ||
| 335 | ✗ | r_size = efi::size(PT2001_main_config); | ||
| 336 | ✗ | break; | ||
| 337 | ||||
| 338 | ✗ | default: | ||
| 339 | ✗ | break; | ||
| 340 | } | |||
| 341 | ||||
| 342 | //for location < size(remainder?) | |||
| 343 | // is location == 0? or past max xfer, send command + expected size | |||
| 344 | // if location = max xfer | |||
| 345 | // | |||
| 346 | // retrieve data, send it, increase pointer | |||
| 347 | // increase | |||
| 348 | ||||
| 349 | ✗ | if (r_size > MAX_SPI_MODE_A_TRANSFER_SIZE) //if size is too large, split into two sections ... MULTIPLE sections.. | ||
| 350 | { | |||
| 351 | ✗ | remainder_size = r_size - MAX_SPI_MODE_A_TRANSFER_SIZE; // creates remaining size | ||
| 352 | ✗ | r_size = MAX_SPI_MODE_A_TRANSFER_SIZE; // sets first size | ||
| 353 | } | |||
| 354 | ||||
| 355 | ✗ | r_command = r_start_address << 5; // start address | ||
| 356 | ✗ | r_command += r_size; // number of words to follow | ||
| 357 | ||||
| 358 | ✗ | select(); // Chip | ||
| 359 | ||||
| 360 | ✗ | send(r_command); // sends address and number of words to be sent | ||
| 361 | ||||
| 362 | ✗ | sendLarge(reg_ptr, r_size); | ||
| 363 | ||||
| 364 | ✗ | if (remainder_size > 0) // if remainder size is greater than 0, download the rest | ||
| 365 | { | |||
| 366 | ✗ | r_start_address += r_size; // new start address | ||
| 367 | ✗ | r_command = r_start_address << 5; // start address | ||
| 368 | ✗ | r_command += remainder_size; // number of words to follow | ||
| 369 | ||||
| 370 | ✗ | send(r_command); // sends address and number of words to be sent | ||
| 371 | ✗ | sendLarge(reg_ptr + r_size, remainder_size); | ||
| 372 | } | |||
| 373 | ✗ | deselect(); | ||
| 374 | ✗ | } | ||
| 375 | ||||
| 376 | // void initMc33816() { | |||
| 377 | // // | |||
| 378 | // // see setTest33816EngineConfiguration for default configuration | |||
| 379 | // // Pins | |||
| 380 | // if (!isBrainPinValid(engineConfiguration->mc33816_cs) || | |||
| 381 | // !isBrainPinValid(engineConfiguration->mc33816_rstb) || | |||
| 382 | // !isBrainPinValid(engineConfiguration->mc33816_driven)) { | |||
| 383 | // return; | |||
| 384 | // } | |||
| 385 | // if (isBrainPinValid(engineConfiguration->mc33816_flag0)) { | |||
| 386 | // efiSetPadMode("mc33816 flag0", engineConfiguration->mc33816_flag0, getInputMode(PI_DEFAULT)); | |||
| 387 | // } | |||
| 388 | ||||
| 389 | // chipSelect.initPin("mc33 CS", engineConfiguration->mc33816_cs /*, &engineConfiguration->csPinMode*/); | |||
| 390 | ||||
| 391 | // // Initialize the chip via ResetB | |||
| 392 | // resetB.initPin("mc33 RESTB", engineConfiguration->mc33816_rstb); | |||
| 393 | // // High Voltage via DRIVEN | |||
| 394 | // driven.initPin("mc33 DRIVEN", engineConfiguration->mc33816_driven); | |||
| 395 | ||||
| 396 | ||||
| 397 | // spiCfg.ssport = getHwPort("hip", engineConfiguration->mc33816_cs); | |||
| 398 | // spiCfg.sspad = getHwPin("hip", engineConfiguration->mc33816_cs); | |||
| 399 | ||||
| 400 | // // hard-coded for now, just resolve the conflict with SD card! | |||
| 401 | // engineConfiguration->mc33816spiDevice = SPI_DEVICE_3; | |||
| 402 | ||||
| 403 | // driver = getSpiDevice(engineConfiguration->mc33816spiDevice); | |||
| 404 | // if (driver == NULL) { | |||
| 405 | // // error already reported | |||
| 406 | // return; | |||
| 407 | // } | |||
| 408 | ||||
| 409 | // spiStart(driver, &spiCfg); | |||
| 410 | ||||
| 411 | // addConsoleAction("mc33_stats", showStats); | |||
| 412 | // addConsoleAction("mc33_restart", mcRestart); | |||
| 413 | // //addConsoleActionI("mc33_send", sendWord); | |||
| 414 | ||||
| 415 | // initMc33816IfNeeded(); | |||
| 416 | // } | |||
| 417 | ||||
| 418 | ✗ | const char * mcFaultToString(McFault fault) { | ||
| 419 | ✗ | switch (fault) { | ||
| 420 | ✗ | case McFault::NoComm: | ||
| 421 | ✗ | return "NoComm"; | ||
| 422 | ✗ | case McFault::NoFlash: | ||
| 423 | ✗ | return "NoFlash"; | ||
| 424 | ✗ | case McFault::UnderVoltageAfter: | ||
| 425 | ✗ | return "UnderVoltageAfter"; | ||
| 426 | ✗ | case McFault::flag0: | ||
| 427 | ✗ | return "flag0"; | ||
| 428 | ✗ | case McFault::UnderVoltage5: | ||
| 429 | ✗ | return "UnderVoltage5"; | ||
| 430 | ✗ | case McFault::Driven: | ||
| 431 | ✗ | return "Driven"; | ||
| 432 | ✗ | case McFault::UnderVoltage7: | ||
| 433 | ✗ | return "UnderVoltage7"; | ||
| 434 | ✗ | default: | ||
| 435 | ✗ | return "TODO"; | ||
| 436 | } | |||
| 437 | return "TODO"; | |||
| 438 | } | |||
| 439 | ||||
| 440 | ✗ | void Pt2001Base::shutdown() { | ||
| 441 | ✗ | setDriveEN(false); // ensure HV is off | ||
| 442 | ✗ | setResetB(false); // turn off the chip | ||
| 443 | ✗ | } | ||
| 444 | ||||
| 445 | ✗ | bool Pt2001Base::restart() { | ||
| 446 | ✗ | bool flag0before = false; | ||
| 447 | ✗ | bool flag0after = false; | ||
| 448 | ||||
| 449 | // Start with everything off | |||
| 450 | ✗ | shutdown(); | ||
| 451 | ✗ | deselect(); | ||
| 452 | ||||
| 453 | ✗ | if (getVbatt() < 8) { | ||
| 454 | ✗ | onError("GDI not Restarting until we see VBatt"); | ||
| 455 | ✗ | return false; | ||
| 456 | } | |||
| 457 | ||||
| 458 | // Wait for chip to reset, then release reset and wait again | |||
| 459 | ✗ | sleepMs(1); | ||
| 460 | ✗ | setResetB(true); | ||
| 461 | ✗ | sleepMs(1); | ||
| 462 | ||||
| 463 | // Flag0 should be floating - pulldown means it should read low | |||
| 464 | ✗ | flag0before = readFlag0(); | ||
| 465 | ||||
| 466 | ✗ | acquireBus(); | ||
| 467 | ✗ | setupSpi(); | ||
| 468 | ||||
| 469 | ✗ | clearDriverStatus(); // Initial clear necessary | ||
| 470 | ✗ | status = readDriverStatus(); | ||
| 471 | ✗ | if (checkUndervoltV5(status)) { | ||
| 472 | ✗ | onError(McFault::UnderVoltage5); | ||
| 473 | ✗ | shutdown(); | ||
| 474 | ✗ | releaseBus(); | ||
| 475 | ✗ | return false; | ||
| 476 | } | |||
| 477 | ||||
| 478 | ✗ | uint16_t chipId = readId(); | ||
| 479 | ✗ | if (!validateChipId(chipId)) { | ||
| 480 | ✗ | onError(McFault::NoComm); | ||
| 481 | ✗ | shutdown(); | ||
| 482 | ✗ | releaseBus(); | ||
| 483 | ✗ | return false; | ||
| 484 | } | |||
| 485 | ||||
| 486 | ✗ | downloadRam(CODE_RAM1); // transfers code RAM1 | ||
| 487 | ✗ | downloadRam(CODE_RAM2); // transfers code RAM2 | ||
| 488 | ✗ | downloadRam(DATA_RAM); // transfers data RAM | ||
| 489 | ✗ | downloadRegister(REG_MAIN); // download main register configurations | ||
| 490 | ||||
| 491 | // current configuration of REG_MAIN would toggle flag0 from LOW to HIGH | |||
| 492 | ✗ | flag0after = readFlag0(); | ||
| 493 | ✗ | if (flag0before || !flag0after) { | ||
| 494 | ✗ | if (errorOnUnexpectedFlag()) { | ||
| 495 | ✗ | onError(McFault::flag0); | ||
| 496 | ✗ | shutdown(); | ||
| 497 | ✗ | releaseBus(); | ||
| 498 | ✗ | return false; | ||
| 499 | } | |||
| 500 | } | |||
| 501 | ||||
| 502 | ✗ | downloadRegister(REG_CH1); // download channel 1 register configurations | ||
| 503 | ✗ | downloadRegister(REG_CH2); // download channel 2 register configurations | ||
| 504 | ✗ | downloadRegister(REG_IO); // download IO register configurations | ||
| 505 | ✗ | downloadRegister(REG_DIAG); // download diag register configuration | ||
| 506 | ||||
| 507 | ✗ | setTimings(); | ||
| 508 | ||||
| 509 | // Finished downloading, let's run the code | |||
| 510 | ✗ | enableFlash(); | ||
| 511 | ||||
| 512 | // give it a moment to take effect | |||
| 513 | ✗ | sleepMs(10); | ||
| 514 | ||||
| 515 | ✗ | if (!checkFlash()) { | ||
| 516 | ✗ | onError(McFault::NoFlash); | ||
| 517 | ✗ | shutdown(); | ||
| 518 | ✗ | releaseBus(); | ||
| 519 | ✗ | return false; | ||
| 520 | } | |||
| 521 | ||||
| 522 | ✗ | clearDriverStatus(); | ||
| 523 | ✗ | sleepMs(5); | ||
| 524 | ||||
| 525 | ✗ | status = readDriverStatus(); | ||
| 526 | ✗ | if (checkUndervoltVccP(status)) { | ||
| 527 | ✗ | onError(McFault::UnderVoltage7); | ||
| 528 | ✗ | shutdown(); | ||
| 529 | ✗ | releaseBus(); | ||
| 530 | ✗ | return false; | ||
| 531 | } | |||
| 532 | ||||
| 533 | // Drive High Voltage | |||
| 534 | ✗ | setDriveEN(true); // driven = HV | ||
| 535 | ✗ | sleepMs(10); // Give it a moment | ||
| 536 | ✗ | status = readDriverStatus(); | ||
| 537 | ✗ | if (!checkDrivenEnabled(status)) { | ||
| 538 | ✗ | onError(McFault::Driven); | ||
| 539 | ✗ | shutdown(); | ||
| 540 | ✗ | releaseBus(); | ||
| 541 | ✗ | return false; | ||
| 542 | } | |||
| 543 | ||||
| 544 | ✗ | status = readDriverStatus(); | ||
| 545 | ✗ | if (checkUndervoltVccP(status)) { | ||
| 546 | ✗ | onError(McFault::UnderVoltageAfter); // Likely DC-DC LS7 is dead! | ||
| 547 | ✗ | shutdown(); | ||
| 548 | ✗ | releaseBus(); | ||
| 549 | ✗ | return false; | ||
| 550 | } | |||
| 551 | ||||
| 552 | ✗ | onError(McFault::None); | ||
| 553 | ||||
| 554 | ✗ | releaseBus(); | ||
| 555 | ✗ | return true; | ||
| 556 | } | |||
| 557 |