#include "drv8301.hpp" #include "utils.hpp" #include "cmsis_os.h" #include "board.h" const SPI_InitTypeDef Drv8301::spi_config_ = { .Mode = SPI_MODE_MASTER, .Direction = SPI_DIRECTION_2LINES, .DataSize = SPI_DATASIZE_16BIT, .CLKPolarity = SPI_POLARITY_LOW, .CLKPhase = SPI_PHASE_2EDGE, .NSS = SPI_NSS_SOFT, .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16, .FirstBit = SPI_FIRSTBIT_MSB, .TIMode = SPI_TIMODE_DISABLE, .CRCCalculation = SPI_CRCCALCULATION_DISABLE, .CRCPolynomial = 10, }; bool Drv8301::config(float requested_gain, float* actual_gain) { // Calculate gain setting: Snap down to have equal or larger range as // requested or largest possible range otherwise // for reference: // 20V/V on 500uOhm gives a range of +/- 150A // 40V/V on 500uOhm gives a range of +/- 75A // 20V/V on 666uOhm gives a range of +/- 110A // 40V/V on 666uOhm gives a range of +/- 55A uint16_t gain_setting = 3; float gain_choices[] = {10.0f, 20.0f, 40.0f, 80.0f}; while (gain_setting && (gain_choices[gain_setting] > requested_gain)) { gain_setting--; } if (actual_gain) { *actual_gain = gain_choices[gain_setting]; } RegisterFile new_config; new_config.control_register_1 = (21 << 6) // Overcurrent set to approximately 150A at 100degC. This may need tweaking. | (0b01 << 4) // OCP_MODE: latch shut down | (0b0 << 3) // 6x PWM mode | (0b0 << 2) // don't reset latched faults | (0b00 << 0); // gate-drive peak current: 1.7A new_config.control_register_2 = (0b0 << 6) // OC_TOFF: cycle by cycle | (0b00 << 4) // calibration off (normal operation) | (gain_setting << 2) // select gain | (0b00 << 0); // report both over temperature and over current on nOCTW pin bool regs_equal = (regs_.control_register_1 == new_config.control_register_1) && (regs_.control_register_2 == new_config.control_register_2); if (!regs_equal) { regs_ = new_config; state_ = kStateUninitialized; enable_gpio_.write(false); } return true; } bool Drv8301::init() { uint16_t val; if (state_ == kStateReady) { return true; } // Reset DRV chip. The enable pin also controls the SPI interface, not only // the driver stages. enable_gpio_.write(false); delay_us(40); // mimumum pull-down time for full reset: 20us state_ = kStateUninitialized; // make is_ready() ignore transient errors before registers are set up enable_gpio_.write(true); osDelay(20); // t_spi_ready, max = 10ms // Write current configuration bool wrote_regs = write_reg(kRegNameControl1, regs_.control_register_1) && write_reg(kRegNameControl1, regs_.control_register_1) && write_reg(kRegNameControl1, regs_.control_register_1) && write_reg(kRegNameControl1, regs_.control_register_1) && write_reg(kRegNameControl1, regs_.control_register_1) // the write operation tends to be ignored if only done once (not sure why) && write_reg(kRegNameControl2, regs_.control_register_2); if (!wrote_regs) { return false; } // Wait for configuration to be applied delay_us(100); state_ = kStateStartupChecks; bool is_read_regs = read_reg(kRegNameControl1, &val) && (val == regs_.control_register_1) && read_reg(kRegNameControl2, &val) && (val == regs_.control_register_2); if (!is_read_regs) { return false; } if (get_error() != FaultType_NoFault) { return false; } // There could have been an nFAULT edge meanwhile. In this case we shouldn't // consider the driver ready. CRITICAL_SECTION() { if (state_ == kStateStartupChecks) { state_ = kStateReady; } } return state_ == kStateReady; } void Drv8301::do_checks() { if (state_ != kStateUninitialized && !nfault_gpio_.read()) { state_ = kStateUninitialized; } } bool Drv8301::is_ready() { return state_ == kStateReady; } Drv8301::FaultType_e Drv8301::get_error() { uint16_t fault1, fault2; if (!read_reg(kRegNameStatus1, &fault1) || !read_reg(kRegNameStatus2, &fault2)) { return (FaultType_e)0xffffffff; } return (FaultType_e)((uint32_t)fault1 | ((uint32_t)(fault2 & 0x0080) << 16)); } bool Drv8301::read_reg(const RegName_e regName, uint16_t* data) { tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Read, regName, 0); if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) { return false; } delay_us(1); tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Read, regName, 0); rx_buf_ = 0xffff; if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), (uint8_t *)(&rx_buf_), 1, 1000)) { return false; } delay_us(1); if (rx_buf_ == 0xbeef) { return false; } if (data) { *data = rx_buf_ & 0x07FF; } return true; } bool Drv8301::write_reg(const RegName_e regName, const uint16_t data) { // Do blocking write tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Write, regName, data); if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) { return false; } delay_us(1); return true; }