mirror of
https://github.com/odriverobotics/ODrive.git
synced 2026-09-27 22:38:10 +08:00
refactor drv8301 driver
This commit is contained in:
@@ -10,6 +10,9 @@
|
|||||||
#include <spi.h>
|
#include <spi.h>
|
||||||
#include <tim.h>
|
#include <tim.h>
|
||||||
#include <main.h>
|
#include <main.h>
|
||||||
|
#include "cmsis_os.h"
|
||||||
|
|
||||||
|
#include <arm_math.h>
|
||||||
|
|
||||||
#if HW_VERSION_MAJOR == 3
|
#if HW_VERSION_MAJOR == 3
|
||||||
#if HW_VERSION_MINOR <= 3
|
#if HW_VERSION_MINOR <= 3
|
||||||
@@ -20,6 +23,29 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <Drivers/STM32/stm32_gpio.hpp>
|
||||||
|
#include <Drivers/DRV8301/drv8301.hpp>
|
||||||
|
#include <MotorControl/thermistor.hpp>
|
||||||
|
|
||||||
|
using TGateDriver = Drv8301;
|
||||||
|
using TOpAmp = Drv8301;
|
||||||
|
|
||||||
|
#include <MotorControl/motor.hpp>
|
||||||
|
|
||||||
|
extern Motor m0;
|
||||||
|
extern Motor m1;
|
||||||
|
extern OnboardThermistorCurrentLimiter m0_fet_thermistor;
|
||||||
|
extern OnboardThermistorCurrentLimiter m1_fet_thermistor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Period in [s]
|
||||||
|
static const float current_meas_period = CURRENT_MEAS_PERIOD;
|
||||||
|
|
||||||
|
// Frequency in [Hz]
|
||||||
|
static const int current_meas_hz = CURRENT_MEAS_HZ;
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t step_gpio_pin;
|
uint16_t step_gpio_pin;
|
||||||
uint16_t dir_gpio_pin;
|
uint16_t dir_gpio_pin;
|
||||||
@@ -38,16 +64,6 @@ typedef struct {
|
|||||||
uint16_t hallC_pin;
|
uint16_t hallC_pin;
|
||||||
SPI_HandleTypeDef* spi;
|
SPI_HandleTypeDef* spi;
|
||||||
} EncoderHardwareConfig_t;
|
} EncoderHardwareConfig_t;
|
||||||
typedef struct {
|
|
||||||
TIM_HandleTypeDef* timer;
|
|
||||||
uint16_t control_deadline;
|
|
||||||
float shunt_conductance;
|
|
||||||
} MotorHardwareConfig_t;
|
|
||||||
typedef struct {
|
|
||||||
const float* const coeffs;
|
|
||||||
size_t num_coeffs;
|
|
||||||
size_t adc_ch;
|
|
||||||
} ThermistorHardwareConfig_t;
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SPI_HandleTypeDef* spi;
|
SPI_HandleTypeDef* spi;
|
||||||
GPIO_TypeDef* enable_port;
|
GPIO_TypeDef* enable_port;
|
||||||
@@ -60,18 +76,12 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
AxisHardwareConfig_t axis_config;
|
AxisHardwareConfig_t axis_config;
|
||||||
EncoderHardwareConfig_t encoder_config;
|
EncoderHardwareConfig_t encoder_config;
|
||||||
MotorHardwareConfig_t motor_config;
|
|
||||||
ThermistorHardwareConfig_t thermistor_config;
|
|
||||||
GateDriverHardwareConfig_t gate_driver_config;
|
|
||||||
} BoardHardwareConfig_t;
|
} BoardHardwareConfig_t;
|
||||||
|
|
||||||
extern const BoardHardwareConfig_t hw_configs[2];
|
extern const BoardHardwareConfig_t hw_configs[2];
|
||||||
|
|
||||||
//TODO stick this in a C file
|
//TODO stick this in a C file
|
||||||
#ifdef __MAIN_CPP__
|
#ifdef __MAIN_CPP__
|
||||||
const float fet_thermistor_poly_coeffs[] =
|
|
||||||
{363.93910201f, -462.15369634f, 307.55129571f, -27.72569531f};
|
|
||||||
const size_t fet_thermistor_num_coeffs = sizeof(fet_thermistor_poly_coeffs)/sizeof(fet_thermistor_poly_coeffs[1]);
|
|
||||||
|
|
||||||
const BoardHardwareConfig_t hw_configs[2] = { {
|
const BoardHardwareConfig_t hw_configs[2] = { {
|
||||||
//M0
|
//M0
|
||||||
@@ -92,26 +102,6 @@ const BoardHardwareConfig_t hw_configs[2] = { {
|
|||||||
.hallC_pin = M0_ENC_Z_Pin,
|
.hallC_pin = M0_ENC_Z_Pin,
|
||||||
.spi = &hspi3,
|
.spi = &hspi3,
|
||||||
},
|
},
|
||||||
.motor_config = {
|
|
||||||
.timer = &htim1,
|
|
||||||
.control_deadline = TIM_1_8_PERIOD_CLOCKS,
|
|
||||||
.shunt_conductance = 1.0f / SHUNT_RESISTANCE, //[S]
|
|
||||||
},
|
|
||||||
.thermistor_config = {
|
|
||||||
.coeffs = &fet_thermistor_poly_coeffs[0],
|
|
||||||
.num_coeffs = fet_thermistor_num_coeffs,
|
|
||||||
.adc_ch = 15,
|
|
||||||
},
|
|
||||||
.gate_driver_config = {
|
|
||||||
.spi = &hspi3,
|
|
||||||
// Note: this board has the EN_Gate pin shared!
|
|
||||||
.enable_port = EN_GATE_GPIO_Port,
|
|
||||||
.enable_pin = EN_GATE_Pin,
|
|
||||||
.nCS_port = M0_nCS_GPIO_Port,
|
|
||||||
.nCS_pin = M0_nCS_Pin,
|
|
||||||
.nFAULT_port = nFAULT_GPIO_Port, // the nFAULT pin is shared between both motors
|
|
||||||
.nFAULT_pin = nFAULT_Pin,
|
|
||||||
}
|
|
||||||
},{
|
},{
|
||||||
//M1
|
//M1
|
||||||
.axis_config = {
|
.axis_config = {
|
||||||
@@ -136,35 +126,9 @@ const BoardHardwareConfig_t hw_configs[2] = { {
|
|||||||
.hallC_pin = M1_ENC_Z_Pin,
|
.hallC_pin = M1_ENC_Z_Pin,
|
||||||
.spi = &hspi3,
|
.spi = &hspi3,
|
||||||
},
|
},
|
||||||
.motor_config = {
|
|
||||||
.timer = &htim8,
|
|
||||||
.control_deadline = (3 * TIM_1_8_PERIOD_CLOCKS) / 2,
|
|
||||||
.shunt_conductance = 1.0f / SHUNT_RESISTANCE, //[S]
|
|
||||||
},
|
|
||||||
.thermistor_config = {
|
|
||||||
.coeffs = &fet_thermistor_poly_coeffs[0],
|
|
||||||
.num_coeffs = fet_thermistor_num_coeffs,
|
|
||||||
#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3
|
|
||||||
.adc_ch = 4,
|
|
||||||
#else
|
|
||||||
.adc_ch = 1,
|
|
||||||
#endif
|
|
||||||
},
|
|
||||||
.gate_driver_config = {
|
|
||||||
.spi = &hspi3,
|
|
||||||
// Note: this board has the EN_Gate pin shared!
|
|
||||||
.enable_port = EN_GATE_GPIO_Port,
|
|
||||||
.enable_pin = EN_GATE_Pin,
|
|
||||||
.nCS_port = M1_nCS_GPIO_Port,
|
|
||||||
.nCS_pin = M1_nCS_Pin,
|
|
||||||
.nFAULT_port = nFAULT_GPIO_Port, // the nFAULT pin is shared between both motors
|
|
||||||
.nFAULT_pin = nFAULT_Pin,
|
|
||||||
}
|
|
||||||
} };
|
} };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define I2C_A0_PORT GPIO_3_GPIO_Port
|
#define I2C_A0_PORT GPIO_3_GPIO_Port
|
||||||
#define I2C_A0_PIN GPIO_3_Pin
|
#define I2C_A0_PIN GPIO_3_Pin
|
||||||
#define I2C_A1_PORT GPIO_4_GPIO_Port
|
#define I2C_A1_PORT GPIO_4_GPIO_Port
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* @brief Contains board specific variables and initialization functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <board.h>
|
||||||
|
|
||||||
|
Stm32SpiArbiter spi3_arbiter{&hspi3};
|
||||||
|
|
||||||
|
Drv8301 m0_gate_driver{
|
||||||
|
&spi3_arbiter,
|
||||||
|
{M0_nCS_GPIO_Port, M0_nCS_Pin}, // nCS
|
||||||
|
{EN_GATE_GPIO_Port, EN_GATE_Pin}, // EN pin (shared between both motors)
|
||||||
|
{nFAULT_GPIO_Port, nFAULT_Pin} // nFAULT pin (shared between both motors)
|
||||||
|
};
|
||||||
|
|
||||||
|
Drv8301 m1_gate_driver{
|
||||||
|
&spi3_arbiter,
|
||||||
|
{M1_nCS_GPIO_Port, M1_nCS_Pin}, // nCS
|
||||||
|
{EN_GATE_GPIO_Port, EN_GATE_Pin}, // EN pin (shared between both motors)
|
||||||
|
{nFAULT_GPIO_Port, nFAULT_Pin} // nFAULT pin (shared between both motors)
|
||||||
|
};
|
||||||
|
|
||||||
|
const float fet_thermistor_poly_coeffs[] =
|
||||||
|
{363.93910201f, -462.15369634f, 307.55129571f, -27.72569531f};
|
||||||
|
const size_t fet_thermistor_num_coeffs = sizeof(fet_thermistor_poly_coeffs)/sizeof(fet_thermistor_poly_coeffs[1]);
|
||||||
|
|
||||||
|
OnboardThermistorCurrentLimiter m0_fet_thermistor{
|
||||||
|
15, // adc_channel
|
||||||
|
&fet_thermistor_poly_coeffs[0], // coefficients
|
||||||
|
fet_thermistor_num_coeffs // num_coeffs
|
||||||
|
};
|
||||||
|
|
||||||
|
OnboardThermistorCurrentLimiter m1_fet_thermistor{
|
||||||
|
#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3
|
||||||
|
4, // adc_channel
|
||||||
|
#else
|
||||||
|
1, // adc_channel
|
||||||
|
#endif
|
||||||
|
&fet_thermistor_poly_coeffs[0], // coefficients
|
||||||
|
fet_thermistor_num_coeffs // num_coeffs
|
||||||
|
};
|
||||||
|
|
||||||
|
Motor m0{
|
||||||
|
&htim1, // timer
|
||||||
|
TIM_1_8_PERIOD_CLOCKS, // control_deadline
|
||||||
|
1.0f / SHUNT_RESISTANCE, // shunt_conductance [S]
|
||||||
|
m0_gate_driver, // gate_driver
|
||||||
|
m0_gate_driver // opamp
|
||||||
|
};
|
||||||
|
|
||||||
|
Motor m1{
|
||||||
|
&htim8, // timer
|
||||||
|
(3 * TIM_1_8_PERIOD_CLOCKS) / 2, // control_deadline
|
||||||
|
1.0f / SHUNT_RESISTANCE, // shunt_conductance [S]
|
||||||
|
m1_gate_driver, // gate_driver
|
||||||
|
m1_gate_driver // opamp
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||||
|
HAL_SPI_TxRxCpltCallback(hspi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||||
|
HAL_SPI_TxRxCpltCallback(hspi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||||
|
if (hspi == &hspi3) {
|
||||||
|
spi3_arbiter.on_complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,276 @@
|
|||||||
|
/* --COPYRIGHT--,BSD
|
||||||
|
* Copyright (c) 2015, Texas Instruments Incorporated
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||||
|
* its contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
* --/COPYRIGHT--*/
|
||||||
|
//! \file drivers/drvic/drv8301/src/32b/f28x/f2806x/drv8301.c
|
||||||
|
//! \brief Contains the various functions related to the DRV8301 object
|
||||||
|
//!
|
||||||
|
//! (C) Copyright 2015, Texas Instruments, Inc.
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// the includes
|
||||||
|
|
||||||
|
#include "drv8301.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include "cmsis_os.h"
|
||||||
|
#include <math.h>
|
||||||
|
#include <array>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
Drv8301::FaultType_e Drv8301::get_error() {
|
||||||
|
uint16_t readWord;
|
||||||
|
FaultType_e faultType = FaultType_NoFault;
|
||||||
|
|
||||||
|
// read the data
|
||||||
|
if (!read_spi(RegName_Status_1, &readWord)) {
|
||||||
|
return (FaultType_e)0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readWord & DRV8301_STATUS1_FAULT_BITS) {
|
||||||
|
faultType = (FaultType_e)(readWord & DRV8301_FAULT_TYPE_MASK);
|
||||||
|
|
||||||
|
if (faultType == FaultType_NoFault) {
|
||||||
|
// read the data
|
||||||
|
if (!read_spi(RegName_Status_2, &readWord)) {
|
||||||
|
return (FaultType_e)0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readWord & DRV8301_STATUS2_GVDD_OV_BITS) {
|
||||||
|
faultType = FaultType_GVDD_OV;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return faultType;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Drv8301::set_gain(float requested_gain, float* actual_gain) {
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Snap down to have equal or larger range as requested or largest possible range otherwise
|
||||||
|
|
||||||
|
// Decoding array for snapping gain
|
||||||
|
std::array<std::pair<float, ShuntAmpGain_e>, 4> gain_choices = {
|
||||||
|
std::make_pair(10.0f, ShuntAmpGain_10VpV),
|
||||||
|
std::make_pair(20.0f, ShuntAmpGain_20VpV),
|
||||||
|
std::make_pair(40.0f, ShuntAmpGain_40VpV),
|
||||||
|
std::make_pair(80.0f, ShuntAmpGain_80VpV)
|
||||||
|
};
|
||||||
|
|
||||||
|
// We use lower_bound in reverse because it snaps up by default, we want to snap down.
|
||||||
|
auto gain_snap_down = std::lower_bound(gain_choices.crbegin(), gain_choices.crend(), requested_gain,
|
||||||
|
[](std::pair<float, ShuntAmpGain_e> pair, float val){
|
||||||
|
return (bool)(pair.first > val);
|
||||||
|
});
|
||||||
|
|
||||||
|
// If we snap to outside the array, clip to smallest val
|
||||||
|
if (gain_snap_down == gain_choices.crend())
|
||||||
|
--gain_snap_down;
|
||||||
|
|
||||||
|
Registers_t regs;
|
||||||
|
if (!read_regs(®s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
regs.Ctrl_Reg_1.OC_MODE = OcMode_LatchShutDown;
|
||||||
|
// Overcurrent set to approximately 150A at 100degC. This may need tweaking.
|
||||||
|
regs.Ctrl_Reg_1.OC_ADJ_SET = VdsLevel_0p730_V;
|
||||||
|
regs.Ctrl_Reg_2.GAIN = gain_snap_down->second;
|
||||||
|
|
||||||
|
if (!write_regs(®s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actual_gain) {
|
||||||
|
*actual_gain = gain_snap_down->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Drv8301::check_fault() {
|
||||||
|
if (nfault_gpio_) {
|
||||||
|
return nfault_gpio_.read();
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Drv8301::set_enabled(bool enabled) {
|
||||||
|
enable_gpio_.write(enabled);
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
// Wait for driver to come online
|
||||||
|
osDelay(10);
|
||||||
|
|
||||||
|
// Make sure the Fault bit is not set during startup
|
||||||
|
uint16_t reg;
|
||||||
|
while (!read_spi(RegName_Status_1, ®) || (reg & DRV8301_STATUS1_FAULT_BITS))
|
||||||
|
; // TODO: don't spin
|
||||||
|
|
||||||
|
// Wait for the DRV8301 registers to update
|
||||||
|
osDelay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Drv8301::read_spi(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datasheet says you don't have to pulse the nCS between transfers, (16
|
||||||
|
// clocks should commit the transfer) but for some reason you actually need
|
||||||
|
// to pulse it.
|
||||||
|
delay_us(1);
|
||||||
|
|
||||||
|
tx_buf_ = 0;
|
||||||
|
rx_buf_ = 0xbeef;
|
||||||
|
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_ & DRV8301_DATA_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Drv8301::write_spi(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Drv8301::write_regs(Registers_t *regs) {
|
||||||
|
uint16_t ctrl1 = regs->Ctrl_Reg_1.DRV8301_CURRENT |
|
||||||
|
regs->Ctrl_Reg_1.DRV8301_RESET |
|
||||||
|
regs->Ctrl_Reg_1.PWM_MODE |
|
||||||
|
regs->Ctrl_Reg_1.OC_MODE |
|
||||||
|
regs->Ctrl_Reg_1.OC_ADJ_SET;
|
||||||
|
|
||||||
|
uint16_t ctrl2 = regs->Ctrl_Reg_2.OCTW_SET |
|
||||||
|
regs->Ctrl_Reg_2.GAIN |
|
||||||
|
regs->Ctrl_Reg_2.DC_CAL_CH1p2 |
|
||||||
|
regs->Ctrl_Reg_2.OC_TOFF;
|
||||||
|
|
||||||
|
return write_spi(RegName_Control_1, ctrl1)
|
||||||
|
&& write_spi(RegName_Control_2, ctrl2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Drv8301::read_regs(Registers_t *regs) {
|
||||||
|
bool success = true;
|
||||||
|
uint16_t drvDataNew;
|
||||||
|
|
||||||
|
// Update Status Register 1
|
||||||
|
if (read_spi(RegName_Status_1, &drvDataNew)) {
|
||||||
|
regs->Stat_Reg_1.FAULT = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FAULT_BITS);
|
||||||
|
regs->Stat_Reg_1.GVDD_UV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_GVDD_UV_BITS);
|
||||||
|
regs->Stat_Reg_1.PVDD_UV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_PVDD_UV_BITS);
|
||||||
|
regs->Stat_Reg_1.OTSD = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_OTSD_BITS);
|
||||||
|
regs->Stat_Reg_1.OTW = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_OTW_BITS);
|
||||||
|
regs->Stat_Reg_1.FETHA_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHA_OC_BITS);
|
||||||
|
regs->Stat_Reg_1.FETLA_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLA_OC_BITS);
|
||||||
|
regs->Stat_Reg_1.FETHB_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHB_OC_BITS);
|
||||||
|
regs->Stat_Reg_1.FETLB_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLB_OC_BITS);
|
||||||
|
regs->Stat_Reg_1.FETHC_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHC_OC_BITS);
|
||||||
|
regs->Stat_Reg_1.FETLC_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLC_OC_BITS);
|
||||||
|
regs->Stat_Reg_1_Value = drvDataNew;
|
||||||
|
} else {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Status Register 2
|
||||||
|
if (read_spi(RegName_Status_2, &drvDataNew)) {
|
||||||
|
regs->Stat_Reg_2.GVDD_OV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS2_GVDD_OV_BITS);
|
||||||
|
regs->Stat_Reg_2.DeviceID = (uint16_t)(drvDataNew & (uint16_t)DRV8301_STATUS2_ID_BITS);
|
||||||
|
regs->Stat_Reg_2_Value = drvDataNew;
|
||||||
|
} else {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Control Register 1
|
||||||
|
if (read_spi(RegName_Control_1, &drvDataNew)) {
|
||||||
|
regs->Ctrl_Reg_1.DRV8301_CURRENT = (PeakCurrent_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_GATE_CURRENT_BITS);
|
||||||
|
regs->Ctrl_Reg_1.DRV8301_RESET = (Reset_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_GATE_RESET_BITS);
|
||||||
|
regs->Ctrl_Reg_1.PWM_MODE = (PwmMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_PWM_MODE_BITS);
|
||||||
|
regs->Ctrl_Reg_1.OC_MODE = (OcMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_OC_MODE_BITS);
|
||||||
|
regs->Ctrl_Reg_1.OC_ADJ_SET = (VdsLevel_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_OC_ADJ_SET_BITS);
|
||||||
|
regs->Ctrl_Reg_1_Value = drvDataNew;
|
||||||
|
} else {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Control Register 2
|
||||||
|
if (read_spi(RegName_Control_2, &drvDataNew)) {
|
||||||
|
regs->Ctrl_Reg_2.OCTW_SET = (OcTwMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_OCTW_SET_BITS);
|
||||||
|
regs->Ctrl_Reg_2.GAIN = (ShuntAmpGain_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_GAIN_BITS);
|
||||||
|
regs->Ctrl_Reg_2.DC_CAL_CH1p2 = (DcCalMode_e)(drvDataNew & (uint16_t)(DRV8301_CTRL2_DC_CAL_1_BITS | DRV8301_CTRL2_DC_CAL_2_BITS));
|
||||||
|
regs->Ctrl_Reg_2.OC_TOFF = (OcOffTimeMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_OC_TOFF_BITS);
|
||||||
|
regs->Ctrl_Reg_2_Value = drvDataNew;
|
||||||
|
} else {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,436 @@
|
|||||||
|
/* --COPYRIGHT--,BSD
|
||||||
|
* Copyright (c) 2015, Texas Instruments Incorporated
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||||
|
* its contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
* --/COPYRIGHT--*/
|
||||||
|
#ifndef _DRV8301_HPP_
|
||||||
|
#define _DRV8301_HPP_
|
||||||
|
|
||||||
|
//! \file drivers/drvic/drv8301/src/32b/f28x/f2806x/drv8301.h
|
||||||
|
//! \brief Contains public interface to various functions related
|
||||||
|
//! to the DRV8301 object
|
||||||
|
//!
|
||||||
|
//! (C) Copyright 2015, Texas Instruments, Inc.
|
||||||
|
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// the includes
|
||||||
|
|
||||||
|
#include "stdbool.h"
|
||||||
|
#include "stdint.h"
|
||||||
|
|
||||||
|
// drivers
|
||||||
|
|
||||||
|
#include "stm32f4xx_hal.h"
|
||||||
|
|
||||||
|
#include <Drivers/gate_driver.hpp>
|
||||||
|
#include <Drivers/STM32/stm32_spi_arbiter.hpp>
|
||||||
|
#include <Drivers/STM32/stm32_gpio.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// the defines
|
||||||
|
|
||||||
|
//! \brief Defines the address mask
|
||||||
|
//!
|
||||||
|
#define DRV8301_ADDR_MASK (0x7800)
|
||||||
|
|
||||||
|
|
||||||
|
//! \brief Defines the data mask
|
||||||
|
//!
|
||||||
|
#define DRV8301_DATA_MASK (0x07FF)
|
||||||
|
|
||||||
|
|
||||||
|
//! \brief Defines the R/W mask
|
||||||
|
//!
|
||||||
|
#define DRV8301_RW_MASK (0x8000)
|
||||||
|
|
||||||
|
|
||||||
|
//! \brief Defines the R/W mask
|
||||||
|
//!
|
||||||
|
#define DRV8301_FAULT_TYPE_MASK (0x07FF)
|
||||||
|
|
||||||
|
|
||||||
|
//! \brief Defines the location of the FETLC_OC (FET Low side, Phase C Over Current) bits in the Status 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS1_FETLC_OC_BITS (1 << 0)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the FETLC_OC (FET High side, Phase C Over Current) bits in the Status 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS1_FETHC_OC_BITS (1 << 1)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the FETLC_OC (FET Low side, Phase B Over Current) bits in the Status 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS1_FETLB_OC_BITS (1 << 2)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the FETLC_OC (FET High side, Phase B Over Current) bits in the Status 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS1_FETHB_OC_BITS (1 << 3)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the FETLC_OC (FET Low side, Phase A Over Current) bits in the Status 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS1_FETLA_OC_BITS (1 << 4)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the FETLC_OC (FET High side, Phase A Over Current) bits in the Status 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS1_FETHA_OC_BITS (1 << 5)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the OTW (Over Temperature Warning) bits in the Status 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS1_OTW_BITS (1 << 6)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the OTSD (Over Temperature Shut Down) bits in the Status 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS1_OTSD_BITS (1 << 7)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the PVDD_UV (Power supply Vdd, Under Voltage) bits in the Status 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS1_PVDD_UV_BITS (1 << 8)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the GVDD_UV (DRV8301 Vdd, Under Voltage) bits in the Status 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS1_GVDD_UV_BITS (1 << 9)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the FAULT bits in the Status 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS1_FAULT_BITS (1 << 10)
|
||||||
|
|
||||||
|
|
||||||
|
//! \brief Defines the location of the Device ID bits in the Status 2 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS2_ID_BITS (15 << 0)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the GVDD_OV (DRV8301 Vdd, Over Voltage) bits in the Status 2 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_STATUS2_GVDD_OV_BITS (1 << 7)
|
||||||
|
|
||||||
|
|
||||||
|
//! \brief Defines the location of the GATE_CURRENT bits in the Control 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_CTRL1_GATE_CURRENT_BITS (3 << 0)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the GATE_RESET bits in the Control 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_CTRL1_GATE_RESET_BITS (1 << 2)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the PWM_MODE bits in the Control 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_CTRL1_PWM_MODE_BITS (1 << 3)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the OC_MODE bits in the Control 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_CTRL1_OC_MODE_BITS (3 << 4)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the OC_ADJ bits in the Control 1 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_CTRL1_OC_ADJ_SET_BITS (31 << 6)
|
||||||
|
|
||||||
|
|
||||||
|
//! \brief Defines the location of the OCTW_SET bits in the Control 2 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_CTRL2_OCTW_SET_BITS (3 << 0)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the GAIN bits in the Control 2 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_CTRL2_GAIN_BITS (3 << 2)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the DC_CAL_1 bits in the Control 2 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_CTRL2_DC_CAL_1_BITS (1 << 4)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the DC_CAL_2 bits in the Control 2 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_CTRL2_DC_CAL_2_BITS (1 << 5)
|
||||||
|
|
||||||
|
//! \brief Defines the location of the OC_TOFF bits in the Control 2 register
|
||||||
|
//!
|
||||||
|
#define DRV8301_CTRL2_OC_TOFF_BITS (1 << 6)
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // extern "C"
|
||||||
|
|
||||||
|
|
||||||
|
class Drv8301 : public GateDriverBase, public OpAmpBase {
|
||||||
|
public:
|
||||||
|
typedef enum {
|
||||||
|
FaultType_NoFault = (0 << 0), //!< No fault
|
||||||
|
FaultType_FETLC_OC = (1 << 0), //!< FET Low side, Phase C Over Current fault
|
||||||
|
FaultType_FETHC_OC = (1 << 1), //!< FET High side, Phase C Over Current fault
|
||||||
|
FaultType_FETLB_OC = (1 << 2), //!< FET Low side, Phase B Over Current fault
|
||||||
|
FaultType_FETHB_OC = (1 << 3), //!< FET High side, Phase B Over Current fault
|
||||||
|
FaultType_FETLA_OC = (1 << 4), //!< FET Low side, Phase A Over Current fault
|
||||||
|
FaultType_FETHA_OC = (1 << 5), //!< FET High side, Phase A Over Current fault
|
||||||
|
FaultType_OTW = (1 << 6), //!< Over Temperature Warning fault
|
||||||
|
FaultType_OTSD = (1 << 7), //!< Over Temperature Shut Down fault
|
||||||
|
FaultType_PVDD_UV = (1 << 8), //!< Power supply Vdd Under Voltage fault
|
||||||
|
FaultType_GVDD_UV = (1 << 9), //!< DRV8301 Vdd Under Voltage fault
|
||||||
|
FaultType_GVDD_OV = (1 << 10) //!< DRV8301 Vdd Over Voltage fault
|
||||||
|
} FaultType_e;
|
||||||
|
|
||||||
|
Drv8301(Stm32SpiArbiter* spi_arbiter, Stm32Gpio ncs_gpio,
|
||||||
|
Stm32Gpio enable_gpio, Stm32Gpio nfault_gpio)
|
||||||
|
: spi_arbiter_(spi_arbiter), ncs_gpio_(ncs_gpio),
|
||||||
|
enable_gpio_(enable_gpio), nfault_gpio_(nfault_gpio) {}
|
||||||
|
|
||||||
|
bool set_gain(float requested_gain, float* actual_gain) final;
|
||||||
|
bool check_fault() final;
|
||||||
|
bool set_enabled(bool enabled) final;
|
||||||
|
FaultType_e get_error();
|
||||||
|
|
||||||
|
float get_midpoint() final {
|
||||||
|
return 0.5f; // [V]
|
||||||
|
}
|
||||||
|
|
||||||
|
float get_max_output_swing() final {
|
||||||
|
return 1.35f / 1.65f; // +-1.35V, normalized from a scale of +-1.65V to +-0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum CtrlMode_e {
|
||||||
|
DRV8301_CtrlMode_Read = 1 << 15, //!< Read Mode
|
||||||
|
DRV8301_CtrlMode_Write = 0 << 15 //!< Write Mode
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RegName_e {
|
||||||
|
RegName_Status_1 = 0 << 11, //!< Status Register 1
|
||||||
|
RegName_Status_2 = 1 << 11, //!< Status Register 2
|
||||||
|
RegName_Control_1 = 2 << 11, //!< Control Register 1
|
||||||
|
RegName_Control_2 = 3 << 11 //!< Control Register 2
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Enumeration for the DC calibration modes
|
||||||
|
enum DcCalMode_e {
|
||||||
|
DcCalMode_Ch1_Load = (0 << 4), //!< Shunt amplifier 1 connected to load via input pins
|
||||||
|
DcCalMode_Ch1_NoLoad = (1 << 4), //!< Shunt amplifier 1 disconnected from load and input pins are shorted
|
||||||
|
DcCalMode_Ch2_Load = (0 << 5), //!< Shunt amplifier 2 connected to load via input pins
|
||||||
|
DcCalMode_Ch2_NoLoad = (1 << 5) //!< Shunt amplifier 2 disconnected from load and input pins are shorted
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Enumeration for the Over Current modes
|
||||||
|
enum OcMode_e {
|
||||||
|
OcMode_CurrentLimit = 0 << 4, //!< current limit when OC detected
|
||||||
|
OcMode_LatchShutDown = 1 << 4, //!< latch shut down when OC detected
|
||||||
|
OcMode_ReportOnly = 2 << 4, //!< report only when OC detected
|
||||||
|
OcMode_Disabled = 3 << 4 //!< OC protection disabled
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Enumeration for the Over Current Off Time modes
|
||||||
|
enum OcOffTimeMode_e {
|
||||||
|
OcOffTimeMode_Normal = 0 << 6, //!< normal CBC operation
|
||||||
|
OcOffTimeMode_Ctrl = 1 << 6 //!< off time control during OC
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Enumeration for the Over Current, Temperature Warning modes
|
||||||
|
enum OcTwMode_e {
|
||||||
|
OcTwMode_Both = 0 << 0, //!< report both OT and OC at /OCTW pin
|
||||||
|
OcTwMode_OT_Only = 1 << 0, //!< report only OT at /OCTW pin
|
||||||
|
OcTwMode_OC_Only = 2 << 0 //!< report only OC at /OCTW pin
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Enumeration for the drv8301 peak current levels
|
||||||
|
enum PeakCurrent_e {
|
||||||
|
PeakCurrent_1p70_A = 0 << 0, //!< drv8301 driver peak current 1.70A
|
||||||
|
PeakCurrent_0p70_A = 1 << 0, //!< drv8301 driver peak current 0.70A
|
||||||
|
PeakCurrent_0p25_A = 2 << 0 //!< drv8301 driver peak current 0.25A
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Enumeration for the PWM modes
|
||||||
|
enum PwmMode_e {
|
||||||
|
PwmMode_Six_Inputs = 0 << 3, //!< six independent inputs
|
||||||
|
PwmMode_Three_Inputs = 1 << 3 //!< three independent nputs
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Enumeration for the shunt amplifier gains
|
||||||
|
enum Reset_e {
|
||||||
|
Reset_Normal = 0 << 2, //!< normal
|
||||||
|
Reset_All = 1 << 2 //!< reset all
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Enumeration for the shunt amplifier gains
|
||||||
|
enum ShuntAmpGain_e {
|
||||||
|
ShuntAmpGain_10VpV = 0 << 2, //!< 10 V per V
|
||||||
|
ShuntAmpGain_20VpV = 1 << 2, //!< 20 V per V
|
||||||
|
ShuntAmpGain_40VpV = 2 << 2, //!< 40 V per V
|
||||||
|
ShuntAmpGain_80VpV = 3 << 2 //!< 80 V per V
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Enumeration for the shunt amplifier number
|
||||||
|
enum ShuntAmpNumber_e {
|
||||||
|
ShuntAmpNumber_1 = 1, //!< Shunt amplifier number 1
|
||||||
|
ShuntAmpNumber_2 = 2 //!< Shunt amplifier number 2
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Enumeration for the Vds level for th over current adjustment
|
||||||
|
enum VdsLevel_e {
|
||||||
|
VdsLevel_0p060_V = 0 << 6, //!< Vds = 0.060 V
|
||||||
|
VdsLevel_0p068_V = 1 << 6, //!< Vds = 0.068 V
|
||||||
|
VdsLevel_0p076_V = 2 << 6, //!< Vds = 0.076 V
|
||||||
|
VdsLevel_0p086_V = 3 << 6, //!< Vds = 0.086 V
|
||||||
|
VdsLevel_0p097_V = 4 << 6, //!< Vds = 0.097 V
|
||||||
|
VdsLevel_0p109_V = 5 << 6, //!< Vds = 0.109 V
|
||||||
|
VdsLevel_0p123_V = 6 << 6, //!< Vds = 0.123 V
|
||||||
|
VdsLevel_0p138_V = 7 << 6, //!< Vds = 0.138 V
|
||||||
|
VdsLevel_0p155_V = 8 << 6, //!< Vds = 0.155 V
|
||||||
|
VdsLevel_0p175_V = 9 << 6, //!< Vds = 0.175 V
|
||||||
|
VdsLevel_0p197_V = 10 << 6, //!< Vds = 0.197 V
|
||||||
|
VdsLevel_0p222_V = 11 << 6, //!< Vds = 0.222 V
|
||||||
|
VdsLevel_0p250_V = 12 << 6, //!< Vds = 0.250 V
|
||||||
|
VdsLevel_0p282_V = 13 << 6, //!< Vds = 0.282 V
|
||||||
|
VdsLevel_0p317_V = 14 << 6, //!< Vds = 0.317 V
|
||||||
|
VdsLevel_0p358_V = 15 << 6, //!< Vds = 0.358 V
|
||||||
|
VdsLevel_0p403_V = 16 << 6, //!< Vds = 0.403 V
|
||||||
|
VdsLevel_0p454_V = 17 << 6, //!< Vds = 0.454 V
|
||||||
|
VdsLevel_0p511_V = 18 << 6, //!< Vds = 0.511 V
|
||||||
|
VdsLevel_0p576_V = 19 << 6, //!< Vds = 0.576 V
|
||||||
|
VdsLevel_0p648_V = 20 << 6, //!< Vds = 0.648 V
|
||||||
|
VdsLevel_0p730_V = 21 << 6, //!< Vds = 0.730 V
|
||||||
|
VdsLevel_0p822_V = 22 << 6, //!< Vds = 0.822 V
|
||||||
|
VdsLevel_0p926_V = 23 << 6, //!< Vds = 0.926 V
|
||||||
|
VdsLevel_1p043_V = 24 << 6, //!< Vds = 1.403 V
|
||||||
|
VdsLevel_1p175_V = 25 << 6, //!< Vds = 1.175 V
|
||||||
|
VdsLevel_1p324_V = 26 << 6, //!< Vds = 1.324 V
|
||||||
|
VdsLevel_1p491_V = 27 << 6, //!< Vds = 1.491 V
|
||||||
|
VdsLevel_1p679_V = 28 << 6, //!< Vds = 1.679 V
|
||||||
|
VdsLevel_1p892_V = 29 << 6, //!< Vds = 1.892 V
|
||||||
|
VdsLevel_2p131_V = 30 << 6, //!< Vds = 2.131 V
|
||||||
|
VdsLevel_2p400_V = 31 << 6 //!< Vds = 2.400 V
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Registers_t {
|
||||||
|
struct {
|
||||||
|
bool FAULT;
|
||||||
|
bool GVDD_UV;
|
||||||
|
bool PVDD_UV;
|
||||||
|
bool OTSD;
|
||||||
|
bool OTW;
|
||||||
|
bool FETHA_OC;
|
||||||
|
bool FETLA_OC;
|
||||||
|
bool FETHB_OC;
|
||||||
|
bool FETLB_OC;
|
||||||
|
bool FETHC_OC;
|
||||||
|
bool FETLC_OC;
|
||||||
|
} Stat_Reg_1;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool GVDD_OV;
|
||||||
|
uint16_t DeviceID;
|
||||||
|
} Stat_Reg_2;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
PeakCurrent_e DRV8301_CURRENT;
|
||||||
|
Reset_e DRV8301_RESET;
|
||||||
|
PwmMode_e PWM_MODE;
|
||||||
|
OcMode_e OC_MODE;
|
||||||
|
VdsLevel_e OC_ADJ_SET;
|
||||||
|
} Ctrl_Reg_1;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
OcTwMode_e OCTW_SET;
|
||||||
|
ShuntAmpGain_e GAIN;
|
||||||
|
DcCalMode_e DC_CAL_CH1p2;
|
||||||
|
OcOffTimeMode_e OC_TOFF;
|
||||||
|
} Ctrl_Reg_2;
|
||||||
|
|
||||||
|
uint16_t Stat_Reg_1_Value;
|
||||||
|
uint16_t Stat_Reg_2_Value;
|
||||||
|
uint16_t Ctrl_Reg_1_Value;
|
||||||
|
uint16_t Ctrl_Reg_2_Value;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Builds the control word
|
||||||
|
//! \param[in] ctrlMode The control mode
|
||||||
|
//! \param[in] regName The register name
|
||||||
|
//! \param[in] data The data
|
||||||
|
//! \return The control word
|
||||||
|
static inline uint16_t build_ctrl_word(const CtrlMode_e ctrlMode,
|
||||||
|
const RegName_e regName,
|
||||||
|
const uint16_t data) {
|
||||||
|
return ctrlMode | regName | (data & DRV8301_DATA_MASK);
|
||||||
|
} // end of DRV8301_buildCtrlWord() function
|
||||||
|
|
||||||
|
//! \brief Enables the DRV8301
|
||||||
|
void enable();
|
||||||
|
|
||||||
|
//! \brief Reads data from the DRV8301 register
|
||||||
|
//! \param[in] regName The register name
|
||||||
|
//! \return The data value
|
||||||
|
bool read_spi(const RegName_e regName, uint16_t* data);
|
||||||
|
|
||||||
|
//! \brief Writes data to the DRV8301 register
|
||||||
|
//! \param[in] regName The register name
|
||||||
|
//! \param[in] data The data value
|
||||||
|
bool write_spi(const RegName_e regName, const uint16_t data);
|
||||||
|
|
||||||
|
//! \brief Interface to all 8301 SPI variables
|
||||||
|
//!
|
||||||
|
//! \details Call this function periodically to be able to read the DRV8301 Status1, Status2,
|
||||||
|
//! Control1, and Control2 registers and write the Control1 and Control2 registers.
|
||||||
|
//! This function updates the members of the structure Registers_t.
|
||||||
|
//! <b>How to use in Setup</b>
|
||||||
|
//! <b>Code</b>
|
||||||
|
//! Add the structure declaration Registers_t to your code
|
||||||
|
//! Make sure the SPI and 8301 EN_Gate GPIO are setup for the 8301 by using HAL_init and HAL_setParams
|
||||||
|
//! During code setup, call HAL_enableDrv and HAL_setupDrvSpi
|
||||||
|
//! In background loop, call DRV8301_writeData and DRV8301_readData
|
||||||
|
//! <b>How to use in Runtime</b>
|
||||||
|
//! <b>Watch window</b>
|
||||||
|
//! Add the structure, declared by Registers_t above, to the watch window
|
||||||
|
//! <b>Runtime</b>
|
||||||
|
//! Pull down the menus from the Registers_t strcuture to the desired setting
|
||||||
|
//! Set SndCmd to send the settings to the DRV8301
|
||||||
|
//! If a read of the DRV8301 registers is required, se RcvCmd
|
||||||
|
//!
|
||||||
|
//! \param[in] regs The (Registers_t) structure that contains all DRV8301 Status/Control register options
|
||||||
|
bool write_regs(Registers_t *regs);
|
||||||
|
|
||||||
|
//! \param[in] regs The (Registers_t) structure that contains all DRV8301 Status/Control register options
|
||||||
|
bool read_regs(Registers_t *regs);
|
||||||
|
|
||||||
|
Stm32SpiArbiter* spi_arbiter_;
|
||||||
|
Stm32Gpio ncs_gpio_;
|
||||||
|
Stm32Gpio enable_gpio_;
|
||||||
|
Stm32Gpio nfault_gpio_;
|
||||||
|
|
||||||
|
// We don't put these buffers on the stack because we place the stack in
|
||||||
|
// a RAM section which cannot be used by DMA.
|
||||||
|
uint16_t tx_buf_;
|
||||||
|
uint16_t rx_buf_;
|
||||||
|
|
||||||
|
static const SPI_InitTypeDef spi_config_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _DRV8301_HPP_
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef STM32_GPIO_HPP__
|
||||||
|
#define STM32_GPIO_HPP__
|
||||||
|
|
||||||
|
#include <gpio.h>
|
||||||
|
|
||||||
|
class Stm32Gpio {
|
||||||
|
public:
|
||||||
|
Stm32Gpio(GPIO_TypeDef* port, uint16_t pin) : port_(port), pin_(pin) {}
|
||||||
|
|
||||||
|
operator bool() const { return port_; }
|
||||||
|
|
||||||
|
void write(bool state) {
|
||||||
|
HAL_GPIO_WritePin(port_, pin_, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool read() {
|
||||||
|
return HAL_GPIO_ReadPin(port_, pin_) != GPIO_PIN_RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GPIO_TypeDef* port_;
|
||||||
|
uint16_t pin_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STM32_GPIO_HPP__
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
|
||||||
|
#include "stm32_spi_arbiter.hpp"
|
||||||
|
#include "stm32_system.h"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
bool equals(const SPI_InitTypeDef& lhs, const SPI_InitTypeDef& rhs) {
|
||||||
|
return (lhs.Mode == rhs.Mode)
|
||||||
|
&& (lhs.Direction == rhs.Direction)
|
||||||
|
&& (lhs.DataSize == rhs.DataSize)
|
||||||
|
&& (lhs.CLKPolarity == rhs.CLKPolarity)
|
||||||
|
&& (lhs.CLKPhase == rhs.CLKPhase)
|
||||||
|
&& (lhs.NSS == rhs.NSS)
|
||||||
|
&& (lhs.BaudRatePrescaler == rhs.BaudRatePrescaler)
|
||||||
|
&& (lhs.FirstBit == rhs.FirstBit)
|
||||||
|
&& (lhs.TIMode == rhs.TIMode)
|
||||||
|
&& (lhs.CRCCalculation == rhs.CRCCalculation)
|
||||||
|
&& (lhs.CRCPolynomial == rhs.CRCPolynomial);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Stm32SpiArbiter::start() {
|
||||||
|
if (!task_list_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiTask& task = *task_list_;
|
||||||
|
if (!equals(task.config, hspi_->Init)) {
|
||||||
|
HAL_SPI_DeInit(hspi_);
|
||||||
|
hspi_->Init = task.config;
|
||||||
|
HAL_SPI_Init(hspi_);
|
||||||
|
}
|
||||||
|
task.ncs_gpio.write(false);
|
||||||
|
|
||||||
|
HAL_StatusTypeDef status = HAL_ERROR;
|
||||||
|
if (task.tx_buf && task.rx_buf) {
|
||||||
|
status = HAL_SPI_TransmitReceive_DMA(hspi_, (uint8_t*)task.tx_buf, task.rx_buf, task.length);
|
||||||
|
} else if (task.tx_buf) {
|
||||||
|
status = HAL_SPI_Transmit_DMA(hspi_, (uint8_t*)task.tx_buf, task.length);
|
||||||
|
} else if (task.rx_buf) {
|
||||||
|
status = HAL_SPI_Receive_DMA(hspi_, task.rx_buf, task.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != HAL_OK) {
|
||||||
|
task.ncs_gpio.write(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status == HAL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Stm32SpiArbiter::transfer(SPI_InitTypeDef config, Stm32Gpio ncs_gpio, const uint8_t* tx_buf, uint8_t* rx_buf, size_t length, uint32_t timeout_ms) {
|
||||||
|
bool done = false;
|
||||||
|
|
||||||
|
SpiTask task = {
|
||||||
|
.config = config,
|
||||||
|
.ncs_gpio = ncs_gpio,
|
||||||
|
.tx_buf = tx_buf,
|
||||||
|
.rx_buf = rx_buf,
|
||||||
|
.length = length,
|
||||||
|
.on_complete = [](void* ctx) { *(bool*)ctx = true; },
|
||||||
|
.cb_ctx = &done,
|
||||||
|
.next = nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Append new task to task list.
|
||||||
|
// We could try to do this lock free but we could also use our time for useful things.
|
||||||
|
SpiTask** ptr = &task_list_;
|
||||||
|
{
|
||||||
|
uint32_t prim = cpu_enter_critical();
|
||||||
|
while (*ptr)
|
||||||
|
ptr = &(*ptr)->next;
|
||||||
|
*ptr = &task;
|
||||||
|
cpu_exit_critical(prim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the list was empty before, kick off the SPI arbiter now
|
||||||
|
if (ptr == &task_list_) {
|
||||||
|
if (!start()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
osDelay(1); // TODO: honor timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
/* HAL_StatusTypeDef status = HAL_ERROR;
|
||||||
|
// delay_us(1);
|
||||||
|
task.ncs_gpio.write(false);
|
||||||
|
// delay_us(1);
|
||||||
|
if (task.tx_buf && task.rx_buf) {
|
||||||
|
status = HAL_SPI_TransmitReceive(hspi_, (uint8_t*)task.tx_buf, task.rx_buf, task.length, 1000);
|
||||||
|
} else if (task.tx_buf) {
|
||||||
|
status = HAL_SPI_Transmit(hspi_, (uint8_t*)task.tx_buf, task.length, 1000);
|
||||||
|
} else if (task.rx_buf) {
|
||||||
|
status = HAL_SPI_Receive(hspi_, task.rx_buf, task.length, 1000);
|
||||||
|
}
|
||||||
|
// delay_us(1);
|
||||||
|
task.ncs_gpio.write(true);
|
||||||
|
// delay_us(1);
|
||||||
|
return status == HAL_OK;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stm32SpiArbiter::on_complete() {
|
||||||
|
if (!task_list_) {
|
||||||
|
return; // this should not happen
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap up transfer
|
||||||
|
task_list_->ncs_gpio.write(true);
|
||||||
|
if (task_list_->on_complete) {
|
||||||
|
(*task_list_->on_complete)(task_list_->cb_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start next task if any
|
||||||
|
SpiTask* next = task_list_->next;
|
||||||
|
task_list_ = next;
|
||||||
|
if (next) {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#ifndef SPI_ARBITER_HPP__
|
||||||
|
#define SPI_ARBITER_HPP__
|
||||||
|
|
||||||
|
#include "stm32_gpio.hpp"
|
||||||
|
|
||||||
|
#include <spi.h>
|
||||||
|
|
||||||
|
class Stm32SpiArbiter {
|
||||||
|
public:
|
||||||
|
struct SpiTask {
|
||||||
|
SPI_InitTypeDef config;
|
||||||
|
Stm32Gpio ncs_gpio;
|
||||||
|
const uint8_t* tx_buf;
|
||||||
|
uint8_t* rx_buf;
|
||||||
|
size_t length;
|
||||||
|
void (*on_complete)(void*);
|
||||||
|
void* cb_ctx;
|
||||||
|
struct SpiTask* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
Stm32SpiArbiter(SPI_HandleTypeDef* hspi): hspi_(hspi) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Executes a blocking transfer.
|
||||||
|
*
|
||||||
|
* If the SPI is busy this function waits until it becomes available or
|
||||||
|
* the specified timeout passes, whichever comes first.
|
||||||
|
*
|
||||||
|
* Returns true on successful transfer or false otherwise.
|
||||||
|
*
|
||||||
|
* This function is thread-safe with respect to itself.
|
||||||
|
*
|
||||||
|
* @param config: The SPI configuration to apply for this transfer.
|
||||||
|
* @param ncs_gpio: The active low GPIO to actuate during this transfer.
|
||||||
|
* @param tx_buf: Buffer for the outgoing data to be sent. Can be null unless
|
||||||
|
* rx_buf is null too.
|
||||||
|
* @param rx_buf: Buffer for the incoming data to be sent. Can be null unless
|
||||||
|
* tx_buf is null too.
|
||||||
|
*/
|
||||||
|
bool transfer(SPI_InitTypeDef config, Stm32Gpio ncs_gpio, const uint8_t* tx_buf, uint8_t* rx_buf, size_t length, uint32_t timeout_ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Completion method to be called from HAL_SPI_TxCpltCallback,
|
||||||
|
* HAL_SPI_RxCpltCallback and HAL_SPI_TxRxCpltCallback.
|
||||||
|
*/
|
||||||
|
void on_complete();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool start();
|
||||||
|
|
||||||
|
SPI_HandleTypeDef* hspi_;
|
||||||
|
SpiTask* task_list_ = nullptr;
|
||||||
|
SpiTask* current_task_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef __STM32_SYSTEM_H
|
||||||
|
#define __STM32_SYSTEM_H
|
||||||
|
|
||||||
|
#include <cmsis_os.h>
|
||||||
|
|
||||||
|
inline uint32_t cpu_enter_critical() {
|
||||||
|
uint32_t primask = __get_PRIMASK();
|
||||||
|
__disable_irq();
|
||||||
|
return primask;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void cpu_exit_critical(uint32_t priority_mask) {
|
||||||
|
__set_PRIMASK(priority_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __STM32_SYSTEM_H
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef __GATE_DRIVER_HPP
|
||||||
|
#define __GATE_DRIVER_HPP
|
||||||
|
|
||||||
|
struct GateDriverBase {
|
||||||
|
/**
|
||||||
|
* @brief Unlocks or locks the gate signals of the gate driver.
|
||||||
|
*
|
||||||
|
* While locked the PWM inputs are ignored and the switches are always in
|
||||||
|
* OFF state.
|
||||||
|
* Not all gate drivers implement this function and may return true even if
|
||||||
|
* the gate driver was not locked.
|
||||||
|
*/
|
||||||
|
virtual bool set_enabled(bool enabled) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks for a fault condition. Returns false if the driver is in a
|
||||||
|
* fault state and true if it is in a nominal state.
|
||||||
|
*/
|
||||||
|
virtual bool check_fault() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OpAmpBase {
|
||||||
|
/**
|
||||||
|
* @brief Tries to set the OpAmp gain to the specified value or lower.
|
||||||
|
*/
|
||||||
|
virtual bool set_gain(float requested_gain, float* actual_gain) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the neutral voltage of the OpAmp in Volts
|
||||||
|
*/
|
||||||
|
virtual float get_midpoint() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the maximum voltage swing away from the midpoint voltage (in Volts)
|
||||||
|
*/
|
||||||
|
virtual float get_max_output_swing() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __GATE_DRIVER_HPP
|
||||||
@@ -25,10 +25,11 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#include <stm32f4xx_hal.h> // Sets up the correct chip specifc defines required by arm_math
|
|
||||||
#define ARM_MATH_CM4 // TODO: might change in future board versions
|
#include <board.h>
|
||||||
#include "arm_math.h"
|
#include "arm_math.h"
|
||||||
#include "arm_common_tables.h"
|
#include "arm_common_tables.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup groupFastMath
|
* @ingroup groupFastMath
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -26,8 +26,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stm32f4xx_hal.h> // Sets up the correct chip specifc defines required by arm_math
|
#include <board.h>
|
||||||
#define ARM_MATH_CM4 // TODO: might change in future board versions
|
|
||||||
#include "arm_math.h"
|
#include "arm_math.h"
|
||||||
#include "arm_common_tables.h"
|
#include "arm_common_tables.h"
|
||||||
|
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ static void step_cb_wrapper(void* ctx) {
|
|||||||
|
|
||||||
// @brief Sets up all components of the axis,
|
// @brief Sets up all components of the axis,
|
||||||
// such as gate driver and encoder hardware.
|
// such as gate driver and encoder hardware.
|
||||||
void Axis::setup() {
|
bool Axis::setup() {
|
||||||
motor_.setup();
|
return motor_.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run_state_machine_loop_wrapper(void* ctx) {
|
static void run_state_machine_loop_wrapper(void* ctx) {
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
#ifndef __AXIS_HPP
|
#ifndef __AXIS_HPP
|
||||||
#define __AXIS_HPP
|
#define __AXIS_HPP
|
||||||
|
|
||||||
#ifndef __ODRIVE_MAIN_H
|
class Axis;
|
||||||
#error "This file should not be included directly. Include odrive_main.h instead."
|
|
||||||
#endif
|
#include "encoder.hpp"
|
||||||
|
#include "sensorless_estimator.hpp"
|
||||||
|
#include "controller.hpp"
|
||||||
|
#include "trapTraj.hpp"
|
||||||
|
#include "endstop.hpp"
|
||||||
|
#include "low_level.h"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
@@ -84,7 +90,7 @@ public:
|
|||||||
Endstop& min_endstop,
|
Endstop& min_endstop,
|
||||||
Endstop& max_endstop);
|
Endstop& max_endstop);
|
||||||
|
|
||||||
void setup();
|
bool setup();
|
||||||
void start_thread();
|
void start_thread();
|
||||||
void signal_current_meas();
|
void signal_current_meas();
|
||||||
bool wait_for_current_meas();
|
bool wait_for_current_meas();
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
#ifndef __CONTROLLER_HPP
|
#ifndef __CONTROLLER_HPP
|
||||||
#define __CONTROLLER_HPP
|
#define __CONTROLLER_HPP
|
||||||
|
|
||||||
#ifndef __ODRIVE_MAIN_H
|
|
||||||
#error "This file should not be included directly. Include odrive_main.h instead."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class Controller : public ODriveIntf::ControllerIntf {
|
class Controller : public ODriveIntf::ControllerIntf {
|
||||||
public:
|
public:
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
#ifndef __CURRENT_LIMITER_HPP
|
#ifndef __CURRENT_LIMITER_HPP
|
||||||
#define __CURRENT_LIMITER_HPP
|
#define __CURRENT_LIMITER_HPP
|
||||||
|
|
||||||
#ifndef __ODRIVE_MAIN_H
|
|
||||||
#error "This file should not be included directly. Include odrive_main.h instead."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CurrentLimiter {
|
class CurrentLimiter {
|
||||||
public:
|
public:
|
||||||
virtual ~CurrentLimiter() = default;
|
virtual ~CurrentLimiter() = default;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
#include "odrive_main.h"
|
#include "odrive_main.h"
|
||||||
|
#include <Drivers/STM32/stm32_system.h>
|
||||||
|
|
||||||
|
|
||||||
Encoder::Encoder(const EncoderHardwareConfig_t& hw_config,
|
Encoder::Encoder(const EncoderHardwareConfig_t& hw_config,
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
#ifndef __ENCODER_HPP
|
#ifndef __ENCODER_HPP
|
||||||
#define __ENCODER_HPP
|
#define __ENCODER_HPP
|
||||||
|
|
||||||
#ifndef __ODRIVE_MAIN_H
|
#include "utils.hpp"
|
||||||
#error "This file should not be included directly. Include odrive_main.h instead."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class Encoder : public ODriveIntf::EncoderIntf {
|
class Encoder : public ODriveIntf::EncoderIntf {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// otherwise chip specific defines are ommited
|
// otherwise chip specific defines are ommited
|
||||||
#include <stm32f405xx.h>
|
#include <stm32f405xx.h>
|
||||||
#include <stm32f4xx_hal.h> // Sets up the correct chip specifc defines required by arm_math
|
#include <stm32f4xx_hal.h> // Sets up the correct chip specifc defines required by arm_math
|
||||||
#define ARM_MATH_CM4
|
#include <Drivers/STM32/stm32_system.h>
|
||||||
#include <arm_math.h>
|
#include <arm_math.h>
|
||||||
|
|
||||||
#include <cmsis_os.h>
|
#include <cmsis_os.h>
|
||||||
@@ -114,7 +114,7 @@ bool safety_critical_disarm_motor_pwm(Motor& motor) {
|
|||||||
uint32_t mask = cpu_enter_critical();
|
uint32_t mask = cpu_enter_critical();
|
||||||
bool was_armed = motor.armed_state_ != Motor::ARMED_STATE_DISARMED;
|
bool was_armed = motor.armed_state_ != Motor::ARMED_STATE_DISARMED;
|
||||||
motor.armed_state_ = Motor::ARMED_STATE_DISARMED;
|
motor.armed_state_ = Motor::ARMED_STATE_DISARMED;
|
||||||
__HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(motor.hw_config_.timer);
|
__HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(motor.timer_);
|
||||||
cpu_exit_critical(mask);
|
cpu_exit_critical(mask);
|
||||||
return was_armed;
|
return was_armed;
|
||||||
}
|
}
|
||||||
@@ -130,9 +130,9 @@ void safety_critical_apply_motor_pwm_timings(Motor& motor, uint16_t timings[3])
|
|||||||
motor.armed_state_ = Motor::ARMED_STATE_DISARMED;
|
motor.armed_state_ = Motor::ARMED_STATE_DISARMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
motor.hw_config_.timer->Instance->CCR1 = timings[0];
|
motor.timer_->Instance->CCR1 = timings[0];
|
||||||
motor.hw_config_.timer->Instance->CCR2 = timings[1];
|
motor.timer_->Instance->CCR2 = timings[1];
|
||||||
motor.hw_config_.timer->Instance->CCR3 = timings[2];
|
motor.timer_->Instance->CCR3 = timings[2];
|
||||||
|
|
||||||
if (motor.armed_state_ == Motor::ARMED_STATE_WAITING_FOR_TIMINGS) {
|
if (motor.armed_state_ == Motor::ARMED_STATE_WAITING_FOR_TIMINGS) {
|
||||||
// timings were just loaded into the timer registers
|
// timings were just loaded into the timer registers
|
||||||
@@ -144,7 +144,7 @@ void safety_critical_apply_motor_pwm_timings(Motor& motor, uint16_t timings[3])
|
|||||||
// now we waited long enough. Enter armed state and
|
// now we waited long enough. Enter armed state and
|
||||||
// enable the actual PWM outputs.
|
// enable the actual PWM outputs.
|
||||||
motor.armed_state_ = Motor::ARMED_STATE_ARMED;
|
motor.armed_state_ = Motor::ARMED_STATE_ARMED;
|
||||||
__HAL_TIM_MOE_ENABLE(motor.hw_config_.timer); // enable pwm outputs
|
__HAL_TIM_MOE_ENABLE(motor.timer_); // enable pwm outputs
|
||||||
} else if (motor.armed_state_ == Motor::ARMED_STATE_ARMED) {
|
} else if (motor.armed_state_ == Motor::ARMED_STATE_ARMED) {
|
||||||
// nothing to do, PWM is running, all good
|
// nothing to do, PWM is running, all good
|
||||||
} else {
|
} else {
|
||||||
@@ -508,7 +508,7 @@ void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) {
|
|||||||
Axis& axis = injected ? *axes[0] : *axes[1];
|
Axis& axis = injected ? *axes[0] : *axes[1];
|
||||||
int axis_num = injected ? 0 : 1;
|
int axis_num = injected ? 0 : 1;
|
||||||
Axis& other_axis = injected ? *axes[1] : *axes[0];
|
Axis& other_axis = injected ? *axes[1] : *axes[0];
|
||||||
bool counting_down = axis.motor_.hw_config_.timer->Instance->CR1 & TIM_CR1_DIR;
|
bool counting_down = axis.motor_.timer_->Instance->CR1 & TIM_CR1_DIR;
|
||||||
bool current_meas_not_DC_CAL = !counting_down;
|
bool current_meas_not_DC_CAL = !counting_down;
|
||||||
|
|
||||||
// Check the timing of the sequencing
|
// Check the timing of the sequencing
|
||||||
@@ -812,7 +812,7 @@ void start_analog_thread() {
|
|||||||
osThreadCreate(osThread(thread_def), NULL);
|
osThreadCreate(osThread(thread_def), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
|
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
|
||||||
{
|
{
|
||||||
if(hspi->pRxBuffPtr == (uint8_t*)axes[0]->encoder_.abs_spi_dma_rx_)
|
if(hspi->pRxBuffPtr == (uint8_t*)axes[0]->encoder_.abs_spi_dma_rx_)
|
||||||
@@ -820,3 +820,4 @@ void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
|
|||||||
else if (hspi->pRxBuffPtr == (uint8_t*)axes[1]->encoder_.abs_spi_dma_rx_)
|
else if (hspi->pRxBuffPtr == (uint8_t*)axes[1]->encoder_.abs_spi_dma_rx_)
|
||||||
axes[1]->encoder_.abs_spi_cb();
|
axes[1]->encoder_.abs_spi_cb();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
@@ -2,10 +2,6 @@
|
|||||||
#ifndef __LOW_LEVEL_H
|
#ifndef __LOW_LEVEL_H
|
||||||
#define __LOW_LEVEL_H
|
#define __LOW_LEVEL_H
|
||||||
|
|
||||||
#ifndef __ODRIVE_MAIN_H
|
|
||||||
#error "This file should not be included directly. Include odrive_main.h instead."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -59,16 +55,6 @@ void start_analog_thread();
|
|||||||
|
|
||||||
void update_brake_current();
|
void update_brake_current();
|
||||||
|
|
||||||
inline uint32_t cpu_enter_critical() {
|
|
||||||
uint32_t primask = __get_PRIMASK();
|
|
||||||
__disable_irq();
|
|
||||||
return primask;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void cpu_exit_critical(uint32_t priority_mask) {
|
|
||||||
__set_PRIMASK(priority_mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ ODriveCAN::Config_t can_config;
|
|||||||
Encoder::Config_t encoder_configs[AXIS_COUNT];
|
Encoder::Config_t encoder_configs[AXIS_COUNT];
|
||||||
SensorlessEstimator::Config_t sensorless_configs[AXIS_COUNT];
|
SensorlessEstimator::Config_t sensorless_configs[AXIS_COUNT];
|
||||||
Controller::Config_t controller_configs[AXIS_COUNT];
|
Controller::Config_t controller_configs[AXIS_COUNT];
|
||||||
Motor::Config_t motor_configs[AXIS_COUNT];
|
|
||||||
OnboardThermistorCurrentLimiter::Config_t fet_thermistor_configs[AXIS_COUNT];
|
|
||||||
OffboardThermistorCurrentLimiter::Config_t motor_thermistor_configs[AXIS_COUNT];
|
OffboardThermistorCurrentLimiter::Config_t motor_thermistor_configs[AXIS_COUNT];
|
||||||
Axis::Config_t axis_configs[AXIS_COUNT];
|
Axis::Config_t axis_configs[AXIS_COUNT];
|
||||||
TrapezoidalTrajectory::Config_t trap_configs[AXIS_COUNT];
|
TrapezoidalTrajectory::Config_t trap_configs[AXIS_COUNT];
|
||||||
@@ -32,8 +30,8 @@ typedef Config<
|
|||||||
Encoder::Config_t[AXIS_COUNT],
|
Encoder::Config_t[AXIS_COUNT],
|
||||||
SensorlessEstimator::Config_t[AXIS_COUNT],
|
SensorlessEstimator::Config_t[AXIS_COUNT],
|
||||||
Controller::Config_t[AXIS_COUNT],
|
Controller::Config_t[AXIS_COUNT],
|
||||||
Motor::Config_t[AXIS_COUNT],
|
Motor::Config_t, Motor::Config_t,
|
||||||
OnboardThermistorCurrentLimiter::Config_t[AXIS_COUNT],
|
OnboardThermistorCurrentLimiter::Config_t, OnboardThermistorCurrentLimiter::Config_t,
|
||||||
OffboardThermistorCurrentLimiter::Config_t[AXIS_COUNT],
|
OffboardThermistorCurrentLimiter::Config_t[AXIS_COUNT],
|
||||||
TrapezoidalTrajectory::Config_t[AXIS_COUNT],
|
TrapezoidalTrajectory::Config_t[AXIS_COUNT],
|
||||||
Endstop::Config_t[AXIS_COUNT],
|
Endstop::Config_t[AXIS_COUNT],
|
||||||
@@ -47,8 +45,10 @@ void ODrive::save_configuration(void) {
|
|||||||
&encoder_configs,
|
&encoder_configs,
|
||||||
&sensorless_configs,
|
&sensorless_configs,
|
||||||
&controller_configs,
|
&controller_configs,
|
||||||
&motor_configs,
|
&m0.config_,
|
||||||
&fet_thermistor_configs,
|
&m1.config_,
|
||||||
|
&m0_fet_thermistor.config_,
|
||||||
|
&m1_fet_thermistor.config_,
|
||||||
&motor_thermistor_configs,
|
&motor_thermistor_configs,
|
||||||
&trap_configs,
|
&trap_configs,
|
||||||
&min_endstop_configs,
|
&min_endstop_configs,
|
||||||
@@ -69,8 +69,10 @@ extern "C" int load_configuration(void) {
|
|||||||
&encoder_configs,
|
&encoder_configs,
|
||||||
&sensorless_configs,
|
&sensorless_configs,
|
||||||
&controller_configs,
|
&controller_configs,
|
||||||
&motor_configs,
|
&m0.config_,
|
||||||
&fet_thermistor_configs,
|
&m1.config_,
|
||||||
|
&m0_fet_thermistor.config_,
|
||||||
|
&m1_fet_thermistor.config_,
|
||||||
&motor_thermistor_configs,
|
&motor_thermistor_configs,
|
||||||
&trap_configs,
|
&trap_configs,
|
||||||
&min_endstop_configs,
|
&min_endstop_configs,
|
||||||
@@ -79,12 +81,14 @@ extern "C" int load_configuration(void) {
|
|||||||
//If loading failed, restore defaults
|
//If loading failed, restore defaults
|
||||||
odrv.config_ = BoardConfig_t();
|
odrv.config_ = BoardConfig_t();
|
||||||
can_config = ODriveCAN::Config_t();
|
can_config = ODriveCAN::Config_t();
|
||||||
|
m0.config_ = Motor::Config_t();
|
||||||
|
m1.config_ = Motor::Config_t();
|
||||||
|
m0_fet_thermistor.config_ = OnboardThermistorCurrentLimiter::Config_t();
|
||||||
|
m1_fet_thermistor.config_ = OnboardThermistorCurrentLimiter::Config_t();
|
||||||
for (size_t i = 0; i < AXIS_COUNT; ++i) {
|
for (size_t i = 0; i < AXIS_COUNT; ++i) {
|
||||||
encoder_configs[i] = Encoder::Config_t();
|
encoder_configs[i] = Encoder::Config_t();
|
||||||
sensorless_configs[i] = SensorlessEstimator::Config_t();
|
sensorless_configs[i] = SensorlessEstimator::Config_t();
|
||||||
controller_configs[i] = Controller::Config_t();
|
controller_configs[i] = Controller::Config_t();
|
||||||
motor_configs[i] = Motor::Config_t();
|
|
||||||
fet_thermistor_configs[i] = OnboardThermistorCurrentLimiter::Config_t();
|
|
||||||
motor_thermistor_configs[i] = OffboardThermistorCurrentLimiter::Config_t();
|
motor_thermistor_configs[i] = OffboardThermistorCurrentLimiter::Config_t();
|
||||||
trap_configs[i] = TrapezoidalTrajectory::Config_t();
|
trap_configs[i] = TrapezoidalTrajectory::Config_t();
|
||||||
axis_configs[i] = Axis::Config_t();
|
axis_configs[i] = Axis::Config_t();
|
||||||
@@ -176,32 +180,26 @@ extern "C" int construct_objects(){
|
|||||||
HAL_GPIO_Init(GPIO_5_GPIO_Port, &GPIO_InitStruct);
|
HAL_GPIO_Init(GPIO_5_GPIO_Port, &GPIO_InitStruct);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
m0.reload_config();
|
||||||
|
m1.reload_config();
|
||||||
|
|
||||||
// Construct all objects.
|
// Construct all objects.
|
||||||
odCAN = new ODriveCAN(can_config, &hcan1);
|
odCAN = new ODriveCAN(can_config, &hcan1);
|
||||||
for (size_t i = 0; i < AXIS_COUNT; ++i) {
|
for (size_t i = 0; i < AXIS_COUNT; ++i) {
|
||||||
Encoder *encoder = new Encoder(hw_configs[i].encoder_config,
|
Encoder *encoder = new Encoder(hw_configs[i].encoder_config,
|
||||||
encoder_configs[i], motor_configs[i]);
|
encoder_configs[i], (i ? m1 : m0).config_);
|
||||||
SensorlessEstimator *sensorless_estimator = new SensorlessEstimator(sensorless_configs[i]);
|
SensorlessEstimator *sensorless_estimator = new SensorlessEstimator(sensorless_configs[i]);
|
||||||
Controller *controller = new Controller(controller_configs[i]);
|
Controller *controller = new Controller(controller_configs[i]);
|
||||||
|
|
||||||
OnboardThermistorCurrentLimiter *fet_thermistor = new OnboardThermistorCurrentLimiter(hw_configs[i].thermistor_config,
|
|
||||||
fet_thermistor_configs[i]);
|
|
||||||
OffboardThermistorCurrentLimiter *motor_thermistor = new OffboardThermistorCurrentLimiter(motor_thermistor_configs[i]);
|
OffboardThermistorCurrentLimiter *motor_thermistor = new OffboardThermistorCurrentLimiter(motor_thermistor_configs[i]);
|
||||||
|
|
||||||
Motor *motor = new Motor(hw_configs[i].motor_config,
|
|
||||||
hw_configs[i].gate_driver_config,
|
|
||||||
motor_configs[i]);
|
|
||||||
TrapezoidalTrajectory *trap = new TrapezoidalTrajectory(trap_configs[i]);
|
TrapezoidalTrajectory *trap = new TrapezoidalTrajectory(trap_configs[i]);
|
||||||
Endstop *min_endstop = new Endstop(min_endstop_configs[i]);
|
Endstop *min_endstop = new Endstop(min_endstop_configs[i]);
|
||||||
Endstop *max_endstop = new Endstop(max_endstop_configs[i]);
|
Endstop *max_endstop = new Endstop(max_endstop_configs[i]);
|
||||||
axes[i] = new Axis(i, hw_configs[i].axis_config, axis_configs[i],
|
axes[i] = new Axis(i, hw_configs[i].axis_config, axis_configs[i],
|
||||||
*encoder, *sensorless_estimator, *controller, *fet_thermistor,
|
*encoder, *sensorless_estimator, *controller, i ? m1_fet_thermistor : m0_fet_thermistor, *motor_thermistor, i ? m1 : m0, *trap, *min_endstop, *max_endstop);
|
||||||
*motor_thermistor, *motor, *trap, *min_endstop, *max_endstop);
|
|
||||||
|
|
||||||
controller_configs[i].parent = controller;
|
controller_configs[i].parent = controller;
|
||||||
encoder_configs[i].parent = encoder;
|
encoder_configs[i].parent = encoder;
|
||||||
motor_thermistor_configs[i].parent = motor_thermistor;
|
motor_thermistor_configs[i].parent = motor_thermistor;
|
||||||
motor_configs[i].parent = motor;
|
|
||||||
min_endstop_configs[i].parent = min_endstop;
|
min_endstop_configs[i].parent = min_endstop;
|
||||||
max_endstop_configs[i].parent = max_endstop;
|
max_endstop_configs[i].parent = max_endstop;
|
||||||
axis_configs[i].parent = axes[i];
|
axis_configs[i].parent = axes[i];
|
||||||
@@ -264,7 +262,9 @@ int odrive_main(void) {
|
|||||||
|
|
||||||
// Setup hardware for all components
|
// Setup hardware for all components
|
||||||
for (size_t i = 0; i < AXIS_COUNT; ++i) {
|
for (size_t i = 0; i < AXIS_COUNT; ++i) {
|
||||||
axes[i]->setup();
|
if (!axes[i]->setup()) {
|
||||||
|
for (;;); // TODO: proper error handling
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& axis : axes){
|
for(auto& axis : axes){
|
||||||
|
|||||||
@@ -1,24 +1,22 @@
|
|||||||
|
|
||||||
|
#include "motor.hpp"
|
||||||
|
#include "axis.hpp"
|
||||||
|
#include "low_level.h"
|
||||||
|
#include "odrive_main.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "drv8301.h"
|
Motor::Motor(TIM_HandleTypeDef* timer,
|
||||||
#include "odrive_main.h"
|
uint16_t control_deadline,
|
||||||
|
float shunt_conductance,
|
||||||
|
TGateDriver& gate_driver,
|
||||||
Motor::Motor(const MotorHardwareConfig_t& hw_config,
|
TOpAmp& opamp) :
|
||||||
const GateDriverHardwareConfig_t& gate_driver_config,
|
timer_(timer),
|
||||||
Config_t& config) :
|
control_deadline_(control_deadline),
|
||||||
hw_config_(hw_config),
|
shunt_conductance_(shunt_conductance),
|
||||||
gate_driver_config_(gate_driver_config),
|
gate_driver_(gate_driver),
|
||||||
config_(config),
|
opamp_(opamp) {
|
||||||
gate_driver_({
|
reload_config();
|
||||||
.spiHandle = gate_driver_config_.spi,
|
|
||||||
.EngpioHandle = gate_driver_config_.enable_port,
|
|
||||||
.EngpioNumber = gate_driver_config_.enable_pin,
|
|
||||||
.nCSgpioHandle = gate_driver_config_.nCS_port,
|
|
||||||
.nCSgpioNumber = gate_driver_config_.nCS_pin,
|
|
||||||
}) {
|
|
||||||
update_current_controller_gains();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @brief Arms the PWM outputs that belong to this motor.
|
// @brief Arms the PWM outputs that belong to this motor.
|
||||||
@@ -63,77 +61,36 @@ void Motor::update_current_controller_gains() {
|
|||||||
current_control_.i_gain = plant_pole * current_control_.p_gain;
|
current_control_.i_gain = plant_pole * current_control_.p_gain;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @brief Set up the gate drivers
|
void Motor::reload_config() {
|
||||||
void Motor::DRV8301_setup() {
|
config_.parent = this;
|
||||||
// for reference:
|
is_calibrated_ = config_.pre_calibrated;
|
||||||
// 20V/V on 500uOhm gives a range of +/- 150A
|
update_current_controller_gains();
|
||||||
// 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
|
|
||||||
|
|
||||||
|
// @brief Set up the gate drivers
|
||||||
|
bool Motor::setup() {
|
||||||
|
gate_driver_.set_enabled(true);
|
||||||
|
|
||||||
// Solve for exact gain, then snap down to have equal or larger range as requested
|
// Solve for exact gain, then snap down to have equal or larger range as requested
|
||||||
// or largest possible range otherwise
|
// or largest possible range otherwise
|
||||||
static const float kMargin = 0.90f;
|
static const float kMargin = 0.90f;
|
||||||
static const float kTripMargin = 1.0f; // Trip level is at edge of linear range of amplifer
|
static const float kTripMargin = 1.0f; // Trip level is at edge of linear range of amplifer
|
||||||
static const float max_output_swing = 1.35f; // [V] out of amplifier
|
static const float max_output_swing = 1.35f; // [V] out of amplifier
|
||||||
float max_unity_gain_current = kMargin * max_output_swing * hw_config_.shunt_conductance; // [A]
|
float max_unity_gain_current = kMargin * max_output_swing * shunt_conductance_; // [A]
|
||||||
float requested_gain = max_unity_gain_current / config_.requested_current_range; // [V/V]
|
float requested_gain = max_unity_gain_current / config_.requested_current_range; // [V/V]
|
||||||
|
|
||||||
// Decoding array for snapping gain
|
float actual_gain = NAN;
|
||||||
std::array<std::pair<float, DRV8301_ShuntAmpGain_e>, 4> gain_choices = {
|
bool success = opamp_.set_gain(requested_gain, &actual_gain);
|
||||||
std::make_pair(10.0f, DRV8301_ShuntAmpGain_10VpV),
|
if (!success)
|
||||||
std::make_pair(20.0f, DRV8301_ShuntAmpGain_20VpV),
|
return false;
|
||||||
std::make_pair(40.0f, DRV8301_ShuntAmpGain_40VpV),
|
|
||||||
std::make_pair(80.0f, DRV8301_ShuntAmpGain_80VpV)
|
|
||||||
};
|
|
||||||
|
|
||||||
// We use lower_bound in reverse because it snaps up by default, we want to snap down.
|
|
||||||
auto gain_snap_down = std::lower_bound(gain_choices.crbegin(), gain_choices.crend(), requested_gain,
|
|
||||||
[](std::pair<float, DRV8301_ShuntAmpGain_e> pair, float val){
|
|
||||||
return pair.first > val;
|
|
||||||
});
|
|
||||||
|
|
||||||
// If we snap to outside the array, clip to smallest val
|
|
||||||
if(gain_snap_down == gain_choices.crend())
|
|
||||||
--gain_snap_down;
|
|
||||||
|
|
||||||
// Values for current controller
|
// Values for current controller
|
||||||
phase_current_rev_gain_ = 1.0f / gain_snap_down->first;
|
phase_current_rev_gain_ = 1.0f / actual_gain;
|
||||||
// Clip all current control to actual usable range
|
// Clip all current control to actual usable range
|
||||||
current_control_.max_allowed_current = max_unity_gain_current * phase_current_rev_gain_;
|
current_control_.max_allowed_current = max_unity_gain_current * phase_current_rev_gain_;
|
||||||
// Set trip level
|
// Set trip level
|
||||||
current_control_.overcurrent_trip_level = (kTripMargin / kMargin) * current_control_.max_allowed_current;
|
current_control_.overcurrent_trip_level = (kTripMargin / kMargin) * current_control_.max_allowed_current;
|
||||||
|
|
||||||
// We now have the gain settings we want to use, lets set up DRV chip
|
|
||||||
DRV_SPI_8301_Vars_t* local_regs = &gate_driver_regs_;
|
|
||||||
DRV8301_enable(&gate_driver_);
|
|
||||||
DRV8301_setupSpi(&gate_driver_, local_regs);
|
|
||||||
|
|
||||||
local_regs->Ctrl_Reg_1.OC_MODE = DRV8301_OcMode_LatchShutDown;
|
|
||||||
// Overcurrent set to approximately 150A at 100degC. This may need tweaking.
|
|
||||||
local_regs->Ctrl_Reg_1.OC_ADJ_SET = DRV8301_VdsLevel_0p730_V;
|
|
||||||
local_regs->Ctrl_Reg_2.GAIN = gain_snap_down->second;
|
|
||||||
|
|
||||||
local_regs->SndCmd = true;
|
|
||||||
DRV8301_writeData(&gate_driver_, local_regs);
|
|
||||||
local_regs->RcvCmd = true;
|
|
||||||
DRV8301_readData(&gate_driver_, local_regs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @brief Checks if the gate driver is in operational state.
|
|
||||||
// @returns: true if the gate driver is OK (no fault), false otherwise
|
|
||||||
bool Motor::check_DRV_fault() {
|
|
||||||
//TODO: make this pin configurable per motor ch
|
|
||||||
GPIO_PinState nFAULT_state = HAL_GPIO_ReadPin(gate_driver_config_.nFAULT_port, gate_driver_config_.nFAULT_pin);
|
|
||||||
if (nFAULT_state == GPIO_PIN_RESET) {
|
|
||||||
// Update DRV Fault Code
|
|
||||||
gate_driver_exported_.drv_fault = (GateDriverIntf::DrvFault)DRV8301_getFaultType(&gate_driver_);
|
|
||||||
// Update/Cache all SPI device registers
|
|
||||||
// DRV_SPI_8301_Vars_t* local_regs = &gate_driver_regs_;
|
|
||||||
// local_regs->RcvCmd = true;
|
|
||||||
// DRV8301_readData(&gate_driver_, local_regs);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +102,7 @@ void Motor::set_error(Motor::Error error){
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Motor::do_checks() {
|
bool Motor::do_checks() {
|
||||||
if (!check_DRV_fault()) {
|
if (!gate_driver_.check_fault()) {
|
||||||
set_error(ERROR_DRV_FAULT);
|
set_error(ERROR_DRV_FAULT);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -201,7 +158,7 @@ float Motor::phase_current_from_adcval(uint32_t ADCValue) {
|
|||||||
int adcval_bal = (int)ADCValue - (1 << 11);
|
int adcval_bal = (int)ADCValue - (1 << 11);
|
||||||
float amp_out_volt = (3.3f / (float)(1 << 12)) * (float)adcval_bal;
|
float amp_out_volt = (3.3f / (float)(1 << 12)) * (float)adcval_bal;
|
||||||
float shunt_volt = amp_out_volt * phase_current_rev_gain_;
|
float shunt_volt = amp_out_volt * phase_current_rev_gain_;
|
||||||
float current = shunt_volt * hw_config_.shunt_conductance;
|
float current = shunt_volt * shunt_conductance_;
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,31 @@
|
|||||||
#ifndef __MOTOR_HPP
|
#ifndef __MOTOR_HPP
|
||||||
#define __MOTOR_HPP
|
#define __MOTOR_HPP
|
||||||
|
|
||||||
#ifndef __ODRIVE_MAIN_H
|
class Axis; // declared in axis.hpp
|
||||||
#error "This file should not be included directly. Include odrive_main.h instead."
|
class Motor;
|
||||||
|
|
||||||
|
#include <board.h>
|
||||||
|
|
||||||
|
#ifndef __TimingLog_t
|
||||||
|
#define __TimingLog_t
|
||||||
|
enum TimingLog_t { // TODO: remove
|
||||||
|
TIMING_LOG_GENERAL,
|
||||||
|
TIMING_LOG_ADC_CB_I,
|
||||||
|
TIMING_LOG_ADC_CB_DC,
|
||||||
|
TIMING_LOG_MEAS_R,
|
||||||
|
TIMING_LOG_MEAS_L,
|
||||||
|
TIMING_LOG_ENC_CALIB,
|
||||||
|
TIMING_LOG_IDX_SEARCH,
|
||||||
|
TIMING_LOG_FOC_VOLTAGE,
|
||||||
|
TIMING_LOG_FOC_CURRENT,
|
||||||
|
TIMING_LOG_SPI_START,
|
||||||
|
TIMING_LOG_SAMPLE_NOW,
|
||||||
|
TIMING_LOG_SPI_END,
|
||||||
|
TIMING_LOG_NUM_SLOTS
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "drv8301.h"
|
#include <autogen/interfaces.hpp>
|
||||||
|
|
||||||
class Motor : public ODriveIntf::MotorIntf {
|
class Motor : public ODriveIntf::MotorIntf {
|
||||||
public:
|
public:
|
||||||
@@ -76,20 +96,19 @@ public:
|
|||||||
void set_current_control_bandwidth(float value) { current_control_bandwidth = value; parent->update_current_controller_gains(); }
|
void set_current_control_bandwidth(float value) { current_control_bandwidth = value; parent->update_current_controller_gains(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
Motor(const MotorHardwareConfig_t& hw_config,
|
Motor(TIM_HandleTypeDef* timer,
|
||||||
const GateDriverHardwareConfig_t& gate_driver_config,
|
uint16_t control_deadline,
|
||||||
Config_t& config);
|
float shunt_conductance,
|
||||||
|
TGateDriver& gate_driver,
|
||||||
|
TOpAmp& opamp);
|
||||||
|
|
||||||
bool arm();
|
bool arm();
|
||||||
void disarm();
|
void disarm();
|
||||||
void setup() {
|
void reload_config();
|
||||||
DRV8301_setup();
|
bool setup();
|
||||||
}
|
|
||||||
void reset_current_control();
|
void reset_current_control();
|
||||||
|
|
||||||
void update_current_controller_gains();
|
void update_current_controller_gains();
|
||||||
void DRV8301_setup();
|
|
||||||
bool check_DRV_fault();
|
|
||||||
void set_error(Error error);
|
void set_error(Error error);
|
||||||
bool do_checks();
|
bool do_checks();
|
||||||
float effective_current_lim();
|
float effective_current_lim();
|
||||||
@@ -105,14 +124,19 @@ public:
|
|||||||
bool FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_phase);
|
bool FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_phase);
|
||||||
bool update(float current_setpoint, float phase, float phase_vel);
|
bool update(float current_setpoint, float phase, float phase_vel);
|
||||||
|
|
||||||
const MotorHardwareConfig_t& hw_config_;
|
// hardware config
|
||||||
const GateDriverHardwareConfig_t gate_driver_config_;
|
|
||||||
Config_t& config_;
|
TIM_HandleTypeDef* const timer_;
|
||||||
|
const uint16_t control_deadline_;
|
||||||
|
const float shunt_conductance_;
|
||||||
|
TGateDriver& gate_driver_;
|
||||||
|
TOpAmp& opamp_;
|
||||||
|
|
||||||
|
Config_t config_;
|
||||||
Axis* axis_ = nullptr; // set by Axis constructor
|
Axis* axis_ = nullptr; // set by Axis constructor
|
||||||
|
|
||||||
//private:
|
//private:
|
||||||
|
|
||||||
DRV8301_Obj gate_driver_; // initialized in constructor
|
|
||||||
uint16_t next_timings_[3] = {
|
uint16_t next_timings_[3] = {
|
||||||
TIM_1_8_PERIOD_CLOCKS / 2,
|
TIM_1_8_PERIOD_CLOCKS / 2,
|
||||||
TIM_1_8_PERIOD_CLOCKS / 2,
|
TIM_1_8_PERIOD_CLOCKS / 2,
|
||||||
@@ -155,11 +179,7 @@ public:
|
|||||||
.async_phase_vel = 0.0f,
|
.async_phase_vel = 0.0f,
|
||||||
.async_phase_offset = 0.0f,
|
.async_phase_offset = 0.0f,
|
||||||
};
|
};
|
||||||
struct : GateDriverIntf {
|
float effective_current_lim_ = 10.0f; // [A]
|
||||||
DrvFault drv_fault = DRV_FAULT_NO_FAULT;
|
|
||||||
} gate_driver_exported_;
|
|
||||||
DRV_SPI_8301_Vars_t gate_driver_regs_; //Local view of DRV registers (initialized by DRV8301_setup)
|
|
||||||
float effective_current_lim_ = 10.0f;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __MOTOR_HPP
|
#endif // __MOTOR_HPP
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
#ifndef __ODRIVE_MAIN_H
|
#ifndef __ODRIVE_MAIN_H
|
||||||
#define __ODRIVE_MAIN_H
|
#define __ODRIVE_MAIN_H
|
||||||
|
|
||||||
// Note on central include scheme by Samuel:
|
// Hardware configuration
|
||||||
// there are circular dependencies between some of the header files,
|
#include <board.h>
|
||||||
// e.g. the Motor header needs a forward declaration of Axis and vice versa
|
|
||||||
// so I figured I'd make one main header that takes care of
|
|
||||||
// the forward declarations and right ordering
|
|
||||||
// btw this pattern is not so uncommon, for instance IIRC the stdlib uses it too
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#include <fibre/protocol.hpp>
|
#include <fibre/protocol.hpp>
|
||||||
@@ -19,28 +15,13 @@ extern "C" {
|
|||||||
#include <stm32f4xx_hal.h> // Sets up the correct chip specifc defines required by arm_math
|
#include <stm32f4xx_hal.h> // Sets up the correct chip specifc defines required by arm_math
|
||||||
#include <can.h>
|
#include <can.h>
|
||||||
#include <i2c.h>
|
#include <i2c.h>
|
||||||
#define ARM_MATH_CM4 // TODO: might change in future board versions
|
|
||||||
#include <arm_math.h>
|
|
||||||
|
|
||||||
// OS includes
|
// OS includes
|
||||||
#include <cmsis_os.h>
|
#include <cmsis_os.h>
|
||||||
|
|
||||||
// Hardware configuration
|
|
||||||
#if HW_VERSION_MAJOR == 3
|
|
||||||
#include "board_config_v3.h"
|
|
||||||
#else
|
|
||||||
#error "unknown board version"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//default timeout waiting for phase measurement signals
|
//default timeout waiting for phase measurement signals
|
||||||
#define PH_CURRENT_MEAS_TIMEOUT 2 // [ms]
|
#define PH_CURRENT_MEAS_TIMEOUT 2 // [ms]
|
||||||
|
|
||||||
// Period in [s]
|
|
||||||
static const float current_meas_period = CURRENT_MEAS_PERIOD;
|
|
||||||
|
|
||||||
// Frequency in [Hz]
|
|
||||||
static const int current_meas_hz = CURRENT_MEAS_HZ;
|
|
||||||
|
|
||||||
// extern const float elec_rad_per_enc;
|
// extern const float elec_rad_per_enc;
|
||||||
extern uint32_t _reboot_cookie;
|
extern uint32_t _reboot_cookie;
|
||||||
|
|
||||||
@@ -178,22 +159,6 @@ inline ENUMTYPE &operator ^= (ENUMTYPE &a, ENUMTYPE b) { return reinterpret_cast
|
|||||||
inline ENUMTYPE operator ~ (ENUMTYPE a) { return static_cast<ENUMTYPE>(~static_cast<std::underlying_type_t<ENUMTYPE>>(a)); }
|
inline ENUMTYPE operator ~ (ENUMTYPE a) { return static_cast<ENUMTYPE>(~static_cast<std::underlying_type_t<ENUMTYPE>>(a)); }
|
||||||
|
|
||||||
|
|
||||||
enum TimingLog_t {
|
|
||||||
TIMING_LOG_GENERAL,
|
|
||||||
TIMING_LOG_ADC_CB_I,
|
|
||||||
TIMING_LOG_ADC_CB_DC,
|
|
||||||
TIMING_LOG_MEAS_R,
|
|
||||||
TIMING_LOG_MEAS_L,
|
|
||||||
TIMING_LOG_ENC_CALIB,
|
|
||||||
TIMING_LOG_IDX_SEARCH,
|
|
||||||
TIMING_LOG_FOC_VOLTAGE,
|
|
||||||
TIMING_LOG_FOC_CURRENT,
|
|
||||||
TIMING_LOG_SPI_START,
|
|
||||||
TIMING_LOG_SAMPLE_NOW,
|
|
||||||
TIMING_LOG_SPI_END,
|
|
||||||
TIMING_LOG_NUM_SLOTS
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#include "autogen/interfaces.hpp"
|
#include "autogen/interfaces.hpp"
|
||||||
|
|
||||||
@@ -201,7 +166,6 @@ enum TimingLog_t {
|
|||||||
#include <utils.hpp>
|
#include <utils.hpp>
|
||||||
#include <gpio_utils.hpp>
|
#include <gpio_utils.hpp>
|
||||||
#include <low_level.h>
|
#include <low_level.h>
|
||||||
#include <motor.hpp>
|
|
||||||
#include <encoder.hpp>
|
#include <encoder.hpp>
|
||||||
#include <sensorless_estimator.hpp>
|
#include <sensorless_estimator.hpp>
|
||||||
#include <controller.hpp>
|
#include <controller.hpp>
|
||||||
|
|||||||
@@ -49,14 +49,13 @@ float ThermistorCurrentLimiter::get_current_limit(float base_current_lim) const
|
|||||||
return std::min(thermal_current_lim, base_current_lim);
|
return std::min(thermal_current_lim, base_current_lim);
|
||||||
}
|
}
|
||||||
|
|
||||||
OnboardThermistorCurrentLimiter::OnboardThermistorCurrentLimiter(const ThermistorHardwareConfig_t& hw_config, Config_t& config) :
|
OnboardThermistorCurrentLimiter::OnboardThermistorCurrentLimiter(uint16_t adc_channel, const float* const coefficients, size_t num_coeffs) :
|
||||||
ThermistorCurrentLimiter(hw_config.adc_ch,
|
ThermistorCurrentLimiter(adc_channel,
|
||||||
hw_config.coeffs,
|
coefficients,
|
||||||
hw_config.num_coeffs,
|
num_coeffs,
|
||||||
config.temp_limit_lower,
|
config_.temp_limit_lower,
|
||||||
config.temp_limit_upper,
|
config_.temp_limit_upper,
|
||||||
config.enabled),
|
config_.enabled)
|
||||||
config_(config)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,31 @@
|
|||||||
#ifndef __THERMISTOR_HPP
|
#ifndef __THERMISTOR_HPP
|
||||||
#define __THERMISTOR_HPP
|
#define __THERMISTOR_HPP
|
||||||
|
|
||||||
#ifndef __ODRIVE_MAIN_H
|
class Axis; // declared in axis.hpp
|
||||||
#error "This file should not be included directly. Include odrive_main.h instead."
|
|
||||||
|
#include "current_limiter.hpp"
|
||||||
|
|
||||||
|
#ifndef __TimingLog_t
|
||||||
|
#define __TimingLog_t
|
||||||
|
enum TimingLog_t { // TODO: remove
|
||||||
|
TIMING_LOG_GENERAL,
|
||||||
|
TIMING_LOG_ADC_CB_I,
|
||||||
|
TIMING_LOG_ADC_CB_DC,
|
||||||
|
TIMING_LOG_MEAS_R,
|
||||||
|
TIMING_LOG_MEAS_L,
|
||||||
|
TIMING_LOG_ENC_CALIB,
|
||||||
|
TIMING_LOG_IDX_SEARCH,
|
||||||
|
TIMING_LOG_FOC_VOLTAGE,
|
||||||
|
TIMING_LOG_FOC_CURRENT,
|
||||||
|
TIMING_LOG_SPI_START,
|
||||||
|
TIMING_LOG_SAMPLE_NOW,
|
||||||
|
TIMING_LOG_SPI_END,
|
||||||
|
TIMING_LOG_NUM_SLOTS
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <autogen/interfaces.hpp>
|
||||||
|
|
||||||
class ThermistorCurrentLimiter : public CurrentLimiter, public ODriveIntf::ThermistorCurrentLimiterIntf {
|
class ThermistorCurrentLimiter : public CurrentLimiter, public ODriveIntf::ThermistorCurrentLimiterIntf {
|
||||||
public:
|
public:
|
||||||
virtual ~ThermistorCurrentLimiter() = default;
|
virtual ~ThermistorCurrentLimiter() = default;
|
||||||
@@ -23,7 +44,7 @@ public:
|
|||||||
uint16_t adc_channel_;
|
uint16_t adc_channel_;
|
||||||
const float* const coefficients_;
|
const float* const coefficients_;
|
||||||
const size_t num_coeffs_;
|
const size_t num_coeffs_;
|
||||||
float temperature_;
|
float temperature_ = NAN; // [°C] NaN while the ODrive is initializing.
|
||||||
const float& temp_limit_lower_;
|
const float& temp_limit_lower_;
|
||||||
const float& temp_limit_upper_;
|
const float& temp_limit_upper_;
|
||||||
const bool& enabled_;
|
const bool& enabled_;
|
||||||
@@ -40,9 +61,9 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
virtual ~OnboardThermistorCurrentLimiter() = default;
|
virtual ~OnboardThermistorCurrentLimiter() = default;
|
||||||
OnboardThermistorCurrentLimiter(const ThermistorHardwareConfig_t& hw_config, Config_t& config);
|
OnboardThermistorCurrentLimiter(uint16_t adc_channel, const float* const coefficients, size_t num_coeffs);
|
||||||
|
|
||||||
Config_t& config_;
|
Config_t config_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OffboardThermistorCurrentLimiter : public ThermistorCurrentLimiter, public ODriveIntf::OffboardThermistorCurrentLimiterIntf {
|
class OffboardThermistorCurrentLimiter : public ThermistorCurrentLimiter, public ODriveIntf::OffboardThermistorCurrentLimiterIntf {
|
||||||
|
|||||||
+71
-66
@@ -34,45 +34,51 @@ tup.frule{
|
|||||||
outputs={'autogen/version.c'}
|
outputs={'autogen/version.c'}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
board_v3 = {
|
||||||
|
dir = 'Board/v3',
|
||||||
|
sources = {'Drivers/DRV8301/drv8301.cpp', 'Board/v3/board.cpp'},
|
||||||
|
flags = {'-DSTM32F405xx', '-DARM_MATH_CM4', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16'},
|
||||||
|
ldflags = {'-TBoard/v3/STM32F405RGTx_FLASH.ld', '-LBoard/v3/Drivers/CMSIS/Lib', '-larm_cortexM4lf_math', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16'}
|
||||||
|
}
|
||||||
|
|
||||||
-- Switch between board versions
|
-- Switch between board versions
|
||||||
boardversion = tup.getconfig("BOARD_VERSION")
|
boardversion = tup.getconfig("BOARD_VERSION")
|
||||||
if boardversion == "v3.1" then
|
if boardversion == "v3.1" then
|
||||||
boarddir = 'Board/v3' -- currently all platform code is in the same v3.3 directory
|
board = board_v3
|
||||||
FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=1"
|
board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=1"
|
||||||
FLAGS += "-DHW_VERSION_VOLTAGE=24"
|
board.flags += "-DHW_VERSION_VOLTAGE=24"
|
||||||
elseif boardversion == "v3.2" then
|
elseif boardversion == "v3.2" then
|
||||||
boarddir = 'Board/v3'
|
board = board_v3
|
||||||
FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=2"
|
board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=2"
|
||||||
FLAGS += "-DHW_VERSION_VOLTAGE=24"
|
board.flags += "-DHW_VERSION_VOLTAGE=24"
|
||||||
elseif boardversion == "v3.3" then
|
elseif boardversion == "v3.3" then
|
||||||
boarddir = 'Board/v3'
|
board = board_v3
|
||||||
FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=3"
|
board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=3"
|
||||||
FLAGS += "-DHW_VERSION_VOLTAGE=24"
|
board.flags += "-DHW_VERSION_VOLTAGE=24"
|
||||||
elseif boardversion == "v3.4-24V" then
|
elseif boardversion == "v3.4-24V" then
|
||||||
boarddir = 'Board/v3'
|
board = board_v3
|
||||||
FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=4"
|
board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=4"
|
||||||
FLAGS += "-DHW_VERSION_VOLTAGE=24"
|
board.flags += "-DHW_VERSION_VOLTAGE=24"
|
||||||
elseif boardversion == "v3.4-48V" then
|
elseif boardversion == "v3.4-48V" then
|
||||||
boarddir = 'Board/v3'
|
board = board_v3
|
||||||
FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=4"
|
board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=4"
|
||||||
FLAGS += "-DHW_VERSION_VOLTAGE=48"
|
board.flags += "-DHW_VERSION_VOLTAGE=48"
|
||||||
elseif boardversion == "v3.5-24V" then
|
elseif boardversion == "v3.5-24V" then
|
||||||
boarddir = 'Board/v3'
|
board = board_v3
|
||||||
FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=5"
|
board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=5"
|
||||||
FLAGS += "-DHW_VERSION_VOLTAGE=24"
|
board.flags += "-DHW_VERSION_VOLTAGE=24"
|
||||||
elseif boardversion == "v3.5-48V" then
|
elseif boardversion == "v3.5-48V" then
|
||||||
boarddir = 'Board/v3'
|
board = board_v3
|
||||||
FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=5"
|
board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=5"
|
||||||
FLAGS += "-DHW_VERSION_VOLTAGE=48"
|
board.flags += "-DHW_VERSION_VOLTAGE=48"
|
||||||
elseif boardversion == "v3.6-24V" then
|
elseif boardversion == "v3.6-24V" then
|
||||||
boarddir = 'Board/v3'
|
board = board_v3
|
||||||
FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=6"
|
board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=6"
|
||||||
FLAGS += "-DHW_VERSION_VOLTAGE=24"
|
board.flags += "-DHW_VERSION_VOLTAGE=24"
|
||||||
elseif boardversion == "v3.6-56V" then
|
elseif boardversion == "v3.6-56V" then
|
||||||
boarddir = 'Board/v3'
|
board = board_v3
|
||||||
FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=6"
|
board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=6"
|
||||||
FLAGS += "-DHW_VERSION_VOLTAGE=56"
|
board.flags += "-DHW_VERSION_VOLTAGE=56"
|
||||||
elseif boardversion == "" then
|
elseif boardversion == "" then
|
||||||
error("board version not specified - take a look at tup.config.default")
|
error("board version not specified - take a look at tup.config.default")
|
||||||
else
|
else
|
||||||
@@ -121,22 +127,19 @@ if tup.getconfig("STRICT") == "true" then
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- C-specific flags
|
-- C-specific flags
|
||||||
|
FLAGS += board.flags
|
||||||
FLAGS += '-D__weak="__attribute__((weak))"'
|
FLAGS += '-D__weak="__attribute__((weak))"'
|
||||||
FLAGS += '-D__packed="__attribute__((__packed__))"'
|
FLAGS += '-D__packed="__attribute__((__packed__))"'
|
||||||
FLAGS += '-DUSE_HAL_DRIVER'
|
FLAGS += '-DUSE_HAL_DRIVER'
|
||||||
FLAGS += '-DSTM32F405xx'
|
|
||||||
|
|
||||||
FLAGS += '-mthumb'
|
FLAGS += '-mthumb'
|
||||||
FLAGS += '-mcpu=cortex-m4'
|
|
||||||
FLAGS += '-mfpu=fpv4-sp-d16'
|
|
||||||
FLAGS += '-mfloat-abi=hard'
|
FLAGS += '-mfloat-abi=hard'
|
||||||
FLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'}
|
FLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'}
|
||||||
|
|
||||||
-- linker flags
|
-- linker flags
|
||||||
LDFLAGS += '-T'..boarddir..'/STM32F405RGTx_FLASH.ld'
|
LDFLAGS += board.ldflags
|
||||||
LDFLAGS += '-L'..boarddir..'/Drivers/CMSIS/Lib' -- lib dir
|
LDFLAGS += '-lc -lm -lnosys' -- libs
|
||||||
LDFLAGS += '-lc -lm -lnosys -larm_cortexM4lf_math' -- libs
|
LDFLAGS += '-mthumb -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float -Wl,--cref -Wl,--gc-sections'
|
||||||
LDFLAGS += '-mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float -Wl,--cref -Wl,--gc-sections'
|
|
||||||
LDFLAGS += '-Wl,--undefined=uxTopUsedPriority'
|
LDFLAGS += '-Wl,--undefined=uxTopUsedPriority'
|
||||||
|
|
||||||
-- debug build
|
-- debug build
|
||||||
@@ -156,19 +159,18 @@ toolchain = GCCToolchain('arm-none-eabi-', 'build', FLAGS, LDFLAGS)
|
|||||||
|
|
||||||
|
|
||||||
-- Load list of source files Makefile that was autogenerated by CubeMX
|
-- Load list of source files Makefile that was autogenerated by CubeMX
|
||||||
vars = parse_makefile_vars(boarddir..'/Makefile')
|
vars = parse_makefile_vars(board.dir..'/Makefile')
|
||||||
all_stm_sources = (vars['C_SOURCES'] or '')..' '..(vars['CPP_SOURCES'] or '')..' '..(vars['ASM_SOURCES'] or '')
|
all_stm_sources = (vars['C_SOURCES'] or '')..' '..(vars['CPP_SOURCES'] or '')..' '..(vars['ASM_SOURCES'] or '')
|
||||||
for src in string.gmatch(all_stm_sources, "%S+") do
|
for src in string.gmatch(all_stm_sources, "%S+") do
|
||||||
stm_sources += boarddir..'/'..src
|
stm_sources += board.dir..'/'..src
|
||||||
end
|
end
|
||||||
for src in string.gmatch(vars['C_INCLUDES'] or '', "%S+") do
|
for src in string.gmatch(vars['C_INCLUDES'] or '', "%S+") do
|
||||||
stm_includes += boarddir..'/'..string.sub(src, 3, -1) -- remove "-I" from each include path
|
stm_includes += board.dir..'/'..string.sub(src, 3, -1) -- remove "-I" from each include path
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: cleaner separation of the platform code and the rest
|
-- TODO: cleaner separation of the platform code and the rest
|
||||||
stm_includes += '.'
|
stm_includes += '.'
|
||||||
stm_includes += 'Drivers/DRV8301'
|
--stm_includes += 'Drivers/DRV8301'
|
||||||
stm_sources += boarddir..'/Src/syscalls.c'
|
|
||||||
build{
|
build{
|
||||||
name='stm_platform',
|
name='stm_platform',
|
||||||
type='objects',
|
type='objects',
|
||||||
@@ -178,39 +180,42 @@ build{
|
|||||||
includes=stm_includes
|
includes=stm_includes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sources = {
|
||||||
|
'syscalls.c',
|
||||||
|
'MotorControl/utils.cpp',
|
||||||
|
'MotorControl/arm_sin_f32.c',
|
||||||
|
'MotorControl/arm_cos_f32.c',
|
||||||
|
'MotorControl/low_level.cpp',
|
||||||
|
'MotorControl/nvm.c',
|
||||||
|
'MotorControl/axis.cpp',
|
||||||
|
'MotorControl/motor.cpp',
|
||||||
|
'MotorControl/thermistor.cpp',
|
||||||
|
'MotorControl/encoder.cpp',
|
||||||
|
'MotorControl/endstop.cpp',
|
||||||
|
'MotorControl/controller.cpp',
|
||||||
|
'MotorControl/sensorless_estimator.cpp',
|
||||||
|
'MotorControl/trapTraj.cpp',
|
||||||
|
'MotorControl/main.cpp',
|
||||||
|
'Drivers/STM32/stm32_spi_arbiter.cpp',
|
||||||
|
'communication/can_simple.cpp',
|
||||||
|
'communication/communication.cpp',
|
||||||
|
'communication/ascii_protocol.cpp',
|
||||||
|
'communication/interface_uart.cpp',
|
||||||
|
'communication/interface_usb.cpp',
|
||||||
|
'communication/interface_can.cpp',
|
||||||
|
'communication/interface_i2c.cpp',
|
||||||
|
'fibre/cpp/protocol.cpp',
|
||||||
|
'FreeRTOS-openocd.c',
|
||||||
|
'autogen/version.c'
|
||||||
|
}
|
||||||
|
tup.append_table(sources, board.sources)
|
||||||
|
|
||||||
build{
|
build{
|
||||||
name='ODriveFirmware',
|
name='ODriveFirmware',
|
||||||
toolchains={toolchain},
|
toolchains={toolchain},
|
||||||
--toolchains={LLVMToolchain('x86_64', {'-Ofast'}, {'-flto'})},
|
--toolchains={LLVMToolchain('x86_64', {'-Ofast'}, {'-flto'})},
|
||||||
packages={'stm_platform'},
|
packages={'stm_platform'},
|
||||||
sources={
|
sources=sources,
|
||||||
'Drivers/DRV8301/drv8301.c',
|
|
||||||
'MotorControl/utils.cpp',
|
|
||||||
'MotorControl/arm_sin_f32.c',
|
|
||||||
'MotorControl/arm_cos_f32.c',
|
|
||||||
'MotorControl/low_level.cpp',
|
|
||||||
'MotorControl/nvm.c',
|
|
||||||
'MotorControl/axis.cpp',
|
|
||||||
'MotorControl/motor.cpp',
|
|
||||||
'MotorControl/thermistor.cpp',
|
|
||||||
'MotorControl/encoder.cpp',
|
|
||||||
'MotorControl/endstop.cpp',
|
|
||||||
'MotorControl/controller.cpp',
|
|
||||||
'MotorControl/sensorless_estimator.cpp',
|
|
||||||
'MotorControl/trapTraj.cpp',
|
|
||||||
'MotorControl/main.cpp',
|
|
||||||
'communication/can_simple.cpp',
|
|
||||||
'communication/communication.cpp',
|
|
||||||
'communication/ascii_protocol.cpp',
|
|
||||||
'communication/interface_uart.cpp',
|
|
||||||
'communication/interface_usb.cpp',
|
|
||||||
'communication/interface_can.cpp',
|
|
||||||
'communication/interface_i2c.cpp',
|
|
||||||
'fibre/cpp/protocol.cpp',
|
|
||||||
'FreeRTOS-openocd.c',
|
|
||||||
'autogen/version.c'
|
|
||||||
},
|
|
||||||
includes={
|
includes={
|
||||||
'Drivers/DRV8301',
|
'Drivers/DRV8301',
|
||||||
'MotorControl',
|
'MotorControl',
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#define __INTERFACE_CAN_HPP
|
#define __INTERFACE_CAN_HPP
|
||||||
|
|
||||||
#include <cmsis_os.h>
|
#include <cmsis_os.h>
|
||||||
#include <stm32f4xx_hal.h>
|
|
||||||
#include "fibre/protocol.hpp"
|
#include "fibre/protocol.hpp"
|
||||||
#include "odrive_main.h"
|
#include "odrive_main.h"
|
||||||
#include "can_helpers.hpp"
|
#include "can_helpers.hpp"
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
* a function-oriented approach and a more powerful object model.
|
* a function-oriented approach and a more powerful object model.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef __FIBRE_INTERFACES_HPP
|
#ifndef __FIBRE_ENDPOINTS_HPP
|
||||||
#define __FIBRE_INTERFACES_HPP
|
#define __FIBRE_ENDPOINTS_HPP
|
||||||
|
|
||||||
#include <fibre/introspection.hpp>
|
#include <fibre/introspection.hpp>
|
||||||
|
|
||||||
@@ -87,4 +87,4 @@ bool set_endpoint_from_float(endpoint_ref_t endpoint_ref, float value) {
|
|||||||
|
|
||||||
#pragma GCC pop_options
|
#pragma GCC pop_options
|
||||||
|
|
||||||
#endif // __FIBRE_INTERFACES_HPP
|
#endif // __FIBRE_ENDPOINTS_HPP
|
||||||
@@ -9,6 +9,10 @@
|
|||||||
* interfaces.
|
* interfaces.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#ifndef __FIBRE_INTERFACES_HPP
|
||||||
|
#define __FIBRE_INTERFACES_HPP
|
||||||
|
|
||||||
|
#include <fibre/protocol.hpp>
|
||||||
|
|
||||||
#pragma GCC push_options
|
#pragma GCC push_options
|
||||||
#pragma GCC optimize ("s")
|
#pragma GCC optimize ("s")
|
||||||
@@ -92,3 +96,5 @@ inline [[enum.c_name]] operator ~ ([[enum.c_name]] a) { return static_cast<[[enu
|
|||||||
|
|
||||||
|
|
||||||
#pragma GCC pop_options
|
#pragma GCC pop_options
|
||||||
|
|
||||||
|
#endif // __FIBRE_INTERFACES_HPP
|
||||||
@@ -355,6 +355,29 @@ interfaces:
|
|||||||
doc: Both axes will have the same id to start
|
doc: Both axes will have the same id to start
|
||||||
can_node_id_extended: bool
|
can_node_id_extended: bool
|
||||||
can_heartbeat_rate_ms: uint32
|
can_heartbeat_rate_ms: uint32
|
||||||
|
gate_driver:
|
||||||
|
c_name: gate_driver_exported_
|
||||||
|
c_is_class: False
|
||||||
|
attributes:
|
||||||
|
drv_fault:
|
||||||
|
typeargs: {fibre.Property.mode: readonly}
|
||||||
|
nullflag: NoFault
|
||||||
|
flags:
|
||||||
|
FetLowCOvercurrent: {bit: 0, doc: FET Low side, Phase C Over Current fault}
|
||||||
|
FetHighCOvercurrent: {bit: 1, doc: FET High side, Phase C Over Current fault}
|
||||||
|
FetLowBOvercurrent: {bit: 2, doc: FET Low side, Phase B Over Current fault}
|
||||||
|
FetHighBOvercurrent: {bit: 3, doc: FET High side, Phase B Over Current fault}
|
||||||
|
FetLowAOvercurrent: {bit: 4, doc: FET Low side, Phase A Over Current fault}
|
||||||
|
FetHighAOvercurrent: {bit: 5, doc: FET High side, Phase A Over Current fault}
|
||||||
|
OvertemperatureWarning: {bit: 6, doc: Over Temperature Warning fault}
|
||||||
|
OvertemperatureShutdown: {bit: 7, doc: Over Temperature Shut Down fault}
|
||||||
|
PVddUndervoltage: {bit: 8, doc: Power supply Vdd Under Voltage fault}
|
||||||
|
GVddUndervoltage: {bit: 9, doc: DRV8301 Vdd Under Voltage fault}
|
||||||
|
GVddOvervoltage: {bit: 10, doc: DRV8301 Vdd Over Voltage fault}
|
||||||
|
# status_reg_1: readonly uint32
|
||||||
|
# status_reg_2: readonly uint32
|
||||||
|
# ctrl_reg_1: readonly uint32
|
||||||
|
# ctrl_reg_2: readonly uint32
|
||||||
fet_thermistor: OnboardThermistorCurrentLimiter
|
fet_thermistor: OnboardThermistorCurrentLimiter
|
||||||
motor_thermistor: OffboardThermistorCurrentLimiter
|
motor_thermistor: OffboardThermistorCurrentLimiter
|
||||||
motor: Motor
|
motor: Motor
|
||||||
@@ -548,29 +571,6 @@ interfaces:
|
|||||||
acim_rotor_flux: float32
|
acim_rotor_flux: float32
|
||||||
async_phase_vel: readonly float32
|
async_phase_vel: readonly float32
|
||||||
async_phase_offset: float32
|
async_phase_offset: float32
|
||||||
gate_driver:
|
|
||||||
c_name: gate_driver_exported_
|
|
||||||
c_is_class: False
|
|
||||||
attributes:
|
|
||||||
drv_fault:
|
|
||||||
typeargs: {fibre.Property.mode: readonly}
|
|
||||||
nullflag: NoFault
|
|
||||||
flags:
|
|
||||||
FetLowCOvercurrent: {bit: 0, doc: FET Low side, Phase C Over Current fault}
|
|
||||||
FetHighCOvercurrent: {bit: 1, doc: FET High side, Phase C Over Current fault}
|
|
||||||
FetLowBOvercurrent: {bit: 2, doc: FET Low side, Phase B Over Current fault}
|
|
||||||
FetHighBOvercurrent: {bit: 3, doc: FET High side, Phase B Over Current fault}
|
|
||||||
FetLowAOvercurrent: {bit: 4, doc: FET Low side, Phase A Over Current fault}
|
|
||||||
FetHighAOvercurrent: {bit: 5, doc: FET High side, Phase A Over Current fault}
|
|
||||||
OvertemperatureWarning: {bit: 6, doc: Over Temperature Warning fault}
|
|
||||||
OvertemperatureShutdown: {bit: 7, doc: Over Temperature Shut Down fault}
|
|
||||||
PVddUndervoltage: {bit: 8, doc: Power supply Vdd Under Voltage fault}
|
|
||||||
GVddUndervoltage: {bit: 9, doc: DRV8301 Vdd Under Voltage fault}
|
|
||||||
GVddOvervoltage: {bit: 10, doc: DRV8301 Vdd Over Voltage fault}
|
|
||||||
# status_reg_1: readonly uint32
|
|
||||||
# status_reg_2: readonly uint32
|
|
||||||
# ctrl_reg_1: readonly uint32
|
|
||||||
# ctrl_reg_2: readonly uint32
|
|
||||||
timing_log:
|
timing_log:
|
||||||
c_is_class: False
|
c_is_class: False
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -107,20 +107,6 @@ ARMED_STATE_WAITING_FOR_TIMINGS = 1
|
|||||||
ARMED_STATE_WAITING_FOR_UPDATE = 2
|
ARMED_STATE_WAITING_FOR_UPDATE = 2
|
||||||
ARMED_STATE_ARMED = 3
|
ARMED_STATE_ARMED = 3
|
||||||
|
|
||||||
# ODrive.Motor.GateDriver.DrvFault
|
|
||||||
DRV_FAULT_NO_FAULT = 0x00000000
|
|
||||||
DRV_FAULT_FET_LOW_C_OVERCURRENT = 0x00000001
|
|
||||||
DRV_FAULT_FET_HIGH_C_OVERCURRENT = 0x00000002
|
|
||||||
DRV_FAULT_FET_LOW_B_OVERCURRENT = 0x00000004
|
|
||||||
DRV_FAULT_FET_HIGH_B_OVERCURRENT = 0x00000008
|
|
||||||
DRV_FAULT_FET_LOW_A_OVERCURRENT = 0x00000010
|
|
||||||
DRV_FAULT_FET_HIGH_A_OVERCURRENT = 0x00000020
|
|
||||||
DRV_FAULT_OVERTEMPERATURE_WARNING = 0x00000040
|
|
||||||
DRV_FAULT_OVERTEMPERATURE_SHUTDOWN = 0x00000080
|
|
||||||
DRV_FAULT_P_VDD_UNDERVOLTAGE = 0x00000100
|
|
||||||
DRV_FAULT_G_VDD_UNDERVOLTAGE = 0x00000200
|
|
||||||
DRV_FAULT_G_VDD_OVERVOLTAGE = 0x00000400
|
|
||||||
|
|
||||||
# ODrive.Controller.Error
|
# ODrive.Controller.Error
|
||||||
CONTROLLER_ERROR_NONE = 0x00000000
|
CONTROLLER_ERROR_NONE = 0x00000000
|
||||||
CONTROLLER_ERROR_OVERSPEED = 0x00000001
|
CONTROLLER_ERROR_OVERSPEED = 0x00000001
|
||||||
|
|||||||
Reference in New Issue
Block a user