mirror of
https://github.com/odriverobotics/ODrive.git
synced 2026-08-18 01:18:52 +08:00
- Cleaner separation between CAN protocol and platform-specific CAN bus implementation - Assimilate several CubeMX files into board.cpp and Drivers/STM32 - Move brake resistor into separate class - introduce PwmOutputGroup interface
169 lines
5.1 KiB
C++
169 lines
5.1 KiB
C++
|
|
#include "drv8301.hpp"
|
|
#include "utils.hpp"
|
|
#include "cmsis_os.h"
|
|
#include "board.h"
|
|
|
|
const Stm32Spi::Config Drv8301::spi_config_ = {
|
|
.max_baud_rate = 2625000, // TODO: look up actual max
|
|
.clk_polarity = SPI_POLARITY_LOW,
|
|
.clk_phase = SPI_PHASE_2EDGE,
|
|
};
|
|
|
|
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;
|
|
}
|