diff --git a/fw/bldc_servo.h b/fw/bldc_servo.h index 9bd9437d..d0747961 100644 --- a/fw/bldc_servo.h +++ b/fw/bldc_servo.h @@ -143,8 +143,8 @@ class BldcServo { float i_gain = 20.0f; // should match csa_gain from drv8323 // PWM rise time compensation - float pwm_comp_off = 0.015; - float pwm_comp_mag = 0.005; + float pwm_comp_off = (g_measured_hw_rev <= 6) ? 0.015 : 0.048; + float pwm_comp_mag = (g_measured_hw_rev <= 6) ? 0.005 : 0.003; // We pick a default maximum voltage based on the board revision. float max_voltage = (g_measured_hw_rev <= 5) ? 37.0f : 46.0f; diff --git a/fw/drv8323.cc b/fw/drv8323.cc index 31dd1778..b883f850 100644 --- a/fw/drv8323.cc +++ b/fw/drv8323.cc @@ -163,6 +163,19 @@ class Drv8323::Impl { } void HandleConfigUpdate() { + if (g_measured_hw_rev == 7) { + // hw rev 7 (silk r4.8) can be damaged with higher gate drive + // strength than this. + config_.idrivep_hs_ma = std::min(config_.idrivep_hs_ma, 50); + config_.idriven_hs_ma = std::min(config_.idriven_hs_ma, 100); + config_.idrivep_ls_ma = std::min(config_.idrivep_ls_ma, 50); + config_.idriven_ls_ma = std::min(config_.idriven_ls_ma, 100); + } else if (g_measured_hw_rev > 7) { + // If the gate drive strength issue has been resolved, then this + // restriction can be removed in later versions. + MJ_ASSERT(false); + } + WriteConfig(); } @@ -187,16 +200,28 @@ class Drv8323::Impl { return (val ? 1 : 0) << pos; }; - constexpr uint16_t idrivep_table[] = { + const bool drv8323 = g_measured_hw_rev <= 6; + + constexpr uint16_t idrivep_table_drv8323[] = { 10, 30, 60, 80, 120, 140, 170, 190, 260, 330, 370, 440, 570, 680, 820, 1000, }; - constexpr uint16_t idriven_table[] = { + constexpr uint16_t idrivep_table_drv8353[] = { + 50, 50, 100, 150, 300, 350, 400, 450, + 550, 600, 650, 700, 850, 900, 950, 1000, + }; + + constexpr uint16_t idriven_table_drv8323[] = { 20, 60, 120, 160, 240, 280, 340, 380, 520, 660, 740, 880, 1140, 1360, 1640, 2000, }; + constexpr uint16_t idriven_table_drv8353[] = { + 100, 100, 200, 300, 600, 700, 800, 900, + 1100, 1200, 1300, 1400, 1700, 1800, 1900, 2000, + }; + constexpr uint16_t tdrive_ns_table[] = { 500, 1000, 2000, 4000, }; @@ -205,15 +230,24 @@ class Drv8323::Impl { 50, 100, 200, 400, }; - constexpr uint16_t deglitch_table[] = { + constexpr uint16_t deglitch_table_drv8323[] = { 2, 4, 6, 8, }; - constexpr uint16_t vds_lvl_table[] = { + constexpr uint16_t deglitch_table_drv8353[] = { + 1, 2, 4, 8, + }; + + constexpr uint16_t vds_lvl_table_drv8323[] = { 60, 130, 200, 260, 310, 450, 530, 600, 680, 750, 940, 1130, 1300, 1500, 1700, 1880, }; + constexpr uint16_t vds_lvl_table_drv8353[] = { + 60, 70, 80, 90, 100, 200, 300, 400, + 500, 600, 700, 800, 900, 1000, 1500, 2000, + }; + constexpr uint16_t csa_gain_table[] = { 5, 10, 20, 40, }; @@ -232,6 +266,11 @@ class Drv8323::Impl { // Drive Control Register const uint16_t reg2 = + // OCP_ACT, which needs to be set on the drv8353 so that all 3 + // half-bridges are shut down in response to a fault. That is + // the only possible behavior on the drv8323. + (drv8323 ? 0 : bit(true, 10)) | + bit(config_.dis_cpuv, 9) | bit(config_.dis_gdf, 8) | bit(config_.otw_rep, 7) | @@ -241,21 +280,27 @@ class Drv8323::Impl { const uint16_t reg3 = (3 << 8) | - (map_choice(idrivep_table, config_.idrivep_hs_ma) << 4) | - (map_choice(idriven_table, config_.idriven_hs_ma) << 0); + (map_choice(drv8323 ? idrivep_table_drv8323 : idrivep_table_drv8353, + config_.idrivep_hs_ma) << 4) | + (map_choice(drv8323 ? idriven_table_drv8323 : idriven_table_drv8353, + config_.idriven_hs_ma) << 0); const uint16_t reg4 = bit(config_.cbc, 10) | (map_choice(tdrive_ns_table, config_.tdrive_ns) << 8) | - (map_choice(idrivep_table, config_.idrivep_ls_ma) << 4) | - (map_choice(idriven_table, config_.idriven_ls_ma) << 0); + (map_choice(drv8323 ? idrivep_table_drv8323 : idrivep_table_drv8353, + config_.idrivep_ls_ma) << 4) | + (map_choice(drv8323 ? idriven_table_drv8323 : idrivep_table_drv8353, + config_.idriven_ls_ma) << 0); const uint16_t reg5 = bit(config_.tretry, 10) | (map_choice(dead_time_table, config_.dead_time_ns) << 8) | (static_cast(config_.ocp_mode) << 6) | - (map_choice(deglitch_table, config_.ocp_deg_us) << 4) | - (map_choice(vds_lvl_table, config_.vds_lvl_mv) << 0); + (map_choice(drv8323 ? deglitch_table_drv8323 : deglitch_table_drv8353, + config_.ocp_deg_us) << 4) | + (map_choice(drv8323 ? vds_lvl_table_drv8323 : vds_lvl_table_drv8353, + config_.vds_lvl_mv) << 0); const uint16_t reg6 = bit(config_.csa_fet, 10) | @@ -265,16 +310,21 @@ class Drv8323::Impl { bit(config_.dis_sen, 5) | (map_choice(sen_lvl_table, config_.sen_lvl_mv) << 0); - const uint16_t regs[] = { 0, 0, reg2, reg3, reg4, reg5, reg6 }; + const uint16_t reg7 = + // This is the CAL_MODE bit, that must be 1 on the drv8353 to + // have equivalent behavior to the drv8323. + (drv8323 ? 0 : bit(true, 0)); + + const uint16_t regs[] = { 0, 0, reg2, reg3, reg4, reg5, reg6, reg7 }; // First set all the registers. - for (int i = 2; i <= 6; i++) { + for (int i = 2; i <= 7; i++) { Write(i, regs[i]); } // Then verify that all registers got the value we want. uint8_t fault_config = 0; - for (int i = 2; i <= 6; i++) { + for (int i = 2; i <= 7; i++) { const auto result = Read(i); if (result != regs[i]) { diff --git a/fw/drv8323.h b/fw/drv8323.h index 034a07e2..4c874fe3 100644 --- a/fw/drv8323.h +++ b/fw/drv8323.h @@ -170,20 +170,24 @@ class Drv8323 : public MotorDriver { // Gate Drive HS Register - uint16_t idrivep_hs_ma = 370; - uint16_t idriven_hs_ma = 740; + + // hw rev 7 boards use a drv8353, which is sensitive to damage + // ringing on the gate drives. This version requires lower gate + // drive strength to avoid damage. + uint16_t idrivep_hs_ma = (g_measured_hw_rev <= 6) ? 370 : 50; + uint16_t idriven_hs_ma = (g_measured_hw_rev <= 6) ? 740 : 100; // Gate Drive LS Register bool cbc = true; // Cycle-by cycle operation. uint16_t tdrive_ns = 1000; // peak gate-current drive time - uint16_t idrivep_ls_ma = 370; - uint16_t idriven_ls_ma = 740; + uint16_t idrivep_ls_ma = (g_measured_hw_rev <= 6) ? 370 : 50; + uint16_t idriven_ls_ma = (g_measured_hw_rev <= 6) ? 740 : 100; // OCP Control Register bool tretry = false; // false = 4ms, true = 50us - uint16_t dead_time_ns = 50; + uint16_t dead_time_ns = (g_measured_hw_rev <= 6) ? 50 : 200; OcpMode ocp_mode = OcpMode::kLatchedFault; uint8_t ocp_deg_us = 4; // valid options of 2, 4, 6, 8 diff --git a/fw/moteus_hw.h b/fw/moteus_hw.h index 3c16c862..e196b7e7 100644 --- a/fw/moteus_hw.h +++ b/fw/moteus_hw.h @@ -37,10 +37,13 @@ extern volatile uint8_t g_measured_hw_rev; // r4.4 silk // #define MOTEUS_HW_REV 5 +// r4.5 silk +// #define MOTEUS_HW_REV 6 + // The most recent version of the HW. #ifndef MOTEUS_HW_REV -// r4.5 silk -#define MOTEUS_HW_REV 6 +// r4.5b-r4.8 silk +#define MOTEUS_HW_REV 7 #endif // The mapping between MOTEUS_HW_REV and the version pins on the @@ -54,6 +57,7 @@ constexpr int kHardwareInterlock[] = { 0, // r4.2/r4.3 (unfortunately, indistinguishable from the interlock) 1, // r4.4 2, // r4.5 + 3, // r4.5b }; #else constexpr int kHardwareInterlock[] = { @@ -64,6 +68,7 @@ constexpr int kHardwareInterlock[] = { -1, // never printed for f4 -1, // never printed for f4 -1, // never printed for f4 + -1, // never printed for f4 }; #endif @@ -72,7 +77,7 @@ constexpr int kCompatibleHwRev[] = { // 3 isn't compatible, but we forgot to rev the version pins 3, 4, 5, - 6, + 6, 7 }; #define DRV8323_ENABLE PA_3 diff --git a/utils/dynamometer_drive.cc b/utils/dynamometer_drive.cc index 1aba9efd..8a92f05e 100644 --- a/utils/dynamometer_drive.cc +++ b/utils/dynamometer_drive.cc @@ -1387,7 +1387,7 @@ class Application { const double estimated_velocity = (results[i].position - results[i - 1].position) / kDelayS; // TODO: Lower this threshold. - if (std::abs(estimated_velocity - speed) > 0.18) { + if (std::abs(estimated_velocity - speed) > 0.198) { throw mjlib::base::system_error::einval( fmt::format("estimated speed at index {} too far off {} != {}", i, estimated_velocity, speed)); @@ -1859,7 +1859,7 @@ class Application { { 100.0, 4.04 }, { 20.0, 3.0 }, { 10.0, 2.09 }, - { 5.0, 1.45 }, + { 5.0, 1.55 }, }; for (const auto test : tests) {