From 43cb91b3b1d257a13a5770b44de92ed0064f8d69 Mon Sep 17 00:00:00 2001 From: Shaun Meehan Date: Tue, 18 Aug 2020 23:37:44 -0700 Subject: [PATCH] Mechanical Brake feature --- Firmware/MotorControl/axis.cpp | 8 ++- Firmware/MotorControl/axis.hpp | 4 +- Firmware/MotorControl/main.cpp | 9 ++- Firmware/MotorControl/mechanical_brake.cpp | 41 ++++++++++++++ Firmware/MotorControl/mechanical_brake.hpp | 36 ++++++++++++ Firmware/MotorControl/odrive_main.h | 1 + Firmware/Tupfile.lua | 1 + Firmware/odrive-interface.yaml | 12 +++- docs/_data/index.yaml | 2 + docs/mechanical-brakes.md | 66 ++++++++++++++++++++++ 10 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 Firmware/MotorControl/mechanical_brake.cpp create mode 100644 Firmware/MotorControl/mechanical_brake.hpp create mode 100644 docs/mechanical-brakes.md diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index f5771cd2..96df5ab0 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -19,7 +19,8 @@ Axis::Axis(int axis_num, Motor& motor, TrapezoidalTrajectory& trap, Endstop& min_endstop, - Endstop& max_endstop) + Endstop& max_endstop, + MechanicalBrake& mechanical_brake) : axis_num_(axis_num), hw_config_(hw_config), config_(config), @@ -32,6 +33,7 @@ Axis::Axis(int axis_num, trap_traj_(trap), min_endstop_(min_endstop), max_endstop_(max_endstop), + mechanical_brake_(mechanical_brake), current_limiters_(make_array( static_cast(&fet_thermistor), static_cast(&motor_thermistor))), @@ -48,6 +50,7 @@ Axis::Axis(int axis_num, trap_traj_.axis_ = this; min_endstop_.axis_ = this; max_endstop_.axis_ = this; + mechanical_brake_.axis_ = this; decode_step_dir_pins(); watchdog_feed(); } @@ -462,6 +465,7 @@ bool Axis::run_idle_loop() { // run_control_loop ignores missed modulation timing updates // if and only if we're in AXIS_STATE_IDLE safety_critical_disarm_motor_pwm(motor_); + mechanical_brake_.engage(); set_step_dir_active(config_.enable_step_dir && config_.step_dir_always_on); run_control_loop([this]() { return true; @@ -474,6 +478,7 @@ void Axis::run_state_machine_loop() { // arm! motor_.arm(); + mechanical_brake_.release(); for (;;) { // Load the task chain if a specific request is pending @@ -575,6 +580,7 @@ void Axis::run_state_machine_loop() { case AXIS_STATE_IDLE: { run_idle_loop(); status = motor_.arm(); // done with idling - try to arm the motor + mechanical_brake_.release(); } break; default: diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 3271e432..e96ab2ef 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -82,7 +82,8 @@ public: Motor& motor, TrapezoidalTrajectory& trap, Endstop& min_endstop, - Endstop& max_endstop); + Endstop& max_endstop, + MechanicalBrake& mechanical_brake); void setup(); void start_thread(); @@ -210,6 +211,7 @@ public: TrapezoidalTrajectory& trap_traj_; Endstop& min_endstop_; Endstop& max_endstop_; + MechanicalBrake& mechanical_brake_; // List of current_limiters and thermistors to // provide easy iteration. diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 2809f5da..62f7ec1e 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -21,6 +21,7 @@ Axis::Config_t axis_configs[AXIS_COUNT]; TrapezoidalTrajectory::Config_t trap_configs[AXIS_COUNT]; Endstop::Config_t min_endstop_configs[AXIS_COUNT]; Endstop::Config_t max_endstop_configs[AXIS_COUNT]; +MechanicalBrake::Config_t mechanical_brake_configs[AXIS_COUNT]; std::array axes; ODriveCAN *odCAN = nullptr; @@ -38,6 +39,7 @@ typedef Config< TrapezoidalTrajectory::Config_t[AXIS_COUNT], Endstop::Config_t[AXIS_COUNT], Endstop::Config_t[AXIS_COUNT], + MechanicalBrake::Config_t[AXIS_COUNT], Axis::Config_t[AXIS_COUNT]> ConfigFormat; void ODrive::save_configuration(void) { @@ -53,6 +55,7 @@ void ODrive::save_configuration(void) { &trap_configs, &min_endstop_configs, &max_endstop_configs, + &mechanical_brake_configs, &axis_configs)) { printf("saving configuration failed\r\n"); osDelay(5); } else { @@ -75,6 +78,7 @@ extern "C" int load_configuration(void) { &trap_configs, &min_endstop_configs, &max_endstop_configs, + &mechanical_brake_configs, &axis_configs)) { //If loading failed, restore defaults odrv.config_ = BoardConfig_t(); @@ -93,6 +97,7 @@ extern "C" int load_configuration(void) { Axis::load_default_can_id(i, axis_configs[i]); min_endstop_configs[i] = Endstop::Config_t(); max_endstop_configs[i] = Endstop::Config_t(); + mechanical_brake_configs[i] = MechanicalBrake::Config_t(); controller_configs[i].load_encoder_axis = i; } } else { @@ -194,9 +199,10 @@ extern "C" int construct_objects(){ TrapezoidalTrajectory *trap = new TrapezoidalTrajectory(trap_configs[i]); Endstop *min_endstop = new Endstop(min_endstop_configs[i]); Endstop *max_endstop = new Endstop(max_endstop_configs[i]); + MechanicalBrake *mechanical_brake = new MechanicalBrake(mechanical_brake_configs[i]); axes[i] = new Axis(i, hw_configs[i].axis_config, axis_configs[i], *encoder, *sensorless_estimator, *controller, *fet_thermistor, - *motor_thermistor, *motor, *trap, *min_endstop, *max_endstop); + *motor_thermistor, *motor, *trap, *min_endstop, *max_endstop, *mechanical_brake); controller_configs[i].parent = controller; encoder_configs[i].parent = encoder; @@ -204,6 +210,7 @@ extern "C" int construct_objects(){ motor_configs[i].parent = motor; min_endstop_configs[i].parent = min_endstop; max_endstop_configs[i].parent = max_endstop; + mechanical_brake_configs[i].parent = mechanical_brake; axis_configs[i].parent = axes[i]; } return 0; diff --git a/Firmware/MotorControl/mechanical_brake.cpp b/Firmware/MotorControl/mechanical_brake.cpp new file mode 100644 index 00000000..27707d2a --- /dev/null +++ b/Firmware/MotorControl/mechanical_brake.cpp @@ -0,0 +1,41 @@ +#include + +MechanicalBrake::MechanicalBrake(MechanicalBrake::Config_t& config) + : config_(config) { + update_config(); +} + +bool MechanicalBrake::get_state() { + return mechanical_brake_state_; +} + +void MechanicalBrake::update_config() { + set_enabled(config_.enabled); +} + +void MechanicalBrake::engage() { + uint16_t gpio_pin = get_gpio_pin_by_pin(config_.gpio_num); + GPIO_TypeDef* gpio_port = get_gpio_port_by_pin(config_.gpio_num); + HAL_GPIO_WritePin(gpio_port, gpio_pin, config_.is_active_low ? GPIO_PIN_RESET : GPIO_PIN_SET ); +} + +void MechanicalBrake::release() { + uint16_t gpio_pin = get_gpio_pin_by_pin(config_.gpio_num); + GPIO_TypeDef* gpio_port = get_gpio_port_by_pin(config_.gpio_num); + HAL_GPIO_WritePin(gpio_port, gpio_pin, config_.is_active_low ? GPIO_PIN_SET : GPIO_PIN_RESET ); +} + +void MechanicalBrake::set_enabled(bool enable) { + if (config_.gpio_num != 0) { + uint16_t gpio_pin = get_gpio_pin_by_pin(config_.gpio_num); + GPIO_TypeDef* gpio_port = get_gpio_port_by_pin(config_.gpio_num); + if (enable) { + HAL_GPIO_DeInit(gpio_port, gpio_pin); + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitStruct.Pin = gpio_pin; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = config_.pulldown ? GPIO_PULLDOWN : GPIO_PULLUP; + HAL_GPIO_Init(gpio_port, &GPIO_InitStruct); + } + } +} \ No newline at end of file diff --git a/Firmware/MotorControl/mechanical_brake.hpp b/Firmware/MotorControl/mechanical_brake.hpp new file mode 100644 index 00000000..9586f8b3 --- /dev/null +++ b/Firmware/MotorControl/mechanical_brake.hpp @@ -0,0 +1,36 @@ +#ifndef __MECHANICAL_BRAKE_HPP +#define __MECHANICAL_BRAKE_HPP + +class MechanicalBrake { + public: + struct Config_t { + uint16_t gpio_num = 0; + bool enabled = false; + bool is_active_low = true; + bool pulldown = true; + + // custom setters + MechanicalBrake* parent = nullptr; + void set_gpio_num(uint16_t value) { gpio_num = value; parent->update_config(); } + void set_enabled(uint32_t value) { enabled = value; parent->update_config(); } + }; + + explicit MechanicalBrake(MechanicalBrake::Config_t& config); + + MechanicalBrake::Config_t& config_; + Axis* axis_ = nullptr; + + void update_config(); + void set_enabled(bool enabled); + + void update(); + bool get_state(); + void release(); + void engage(); + + bool mechanical_brake_state_ = true; + + private: + bool pin_state_ = false; +}; +#endif // __MECHANICAL_BRAKE_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index d0dcface..88328993 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -210,6 +210,7 @@ enum TimingLog_t { #include #include #include +#include #include #include diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 7f5380ae..f8f6f8ec 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -194,6 +194,7 @@ build{ 'MotorControl/thermistor.cpp', 'MotorControl/encoder.cpp', 'MotorControl/endstop.cpp', + 'MotorControl/mechanical_brake.cpp', 'MotorControl/controller.cpp', 'MotorControl/sensorless_estimator.cpp', 'MotorControl/trapTraj.cpp', diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 2c9060eb..2a755275 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -364,6 +364,7 @@ interfaces: trap_traj: TrapezoidalTrajectory min_endstop: Endstop max_endstop: Endstop + mechanical_brake: MechanicalBrake functions: watchdog_feed: doc: Feed the watchdog to prevent watchdog timeouts. @@ -830,7 +831,6 @@ interfaces: accel_limit: float32 decel_limit: float32 - ODrive.Endstop: c_is_class: True attributes: @@ -845,6 +845,16 @@ interfaces: pullup: bool debounce_ms: {type: uint32, c_setter: set_debounce_ms} + ODrive.MechanicalBrake: + c_is_class: True + attributes: + config: + c_is_class: False + attributes: + gpio_num: {type: uint16, c_setter: set_gpio_num} + enabled: {type: bool, c_setter: set_enabled} + is_active_low: bool + pulldown: bool valuetypes: ODrive.Can.Protocol: diff --git a/docs/_data/index.yaml b/docs/_data/index.yaml index 0ff47762..273029b6 100644 --- a/docs/_data/index.yaml +++ b/docs/_data/index.yaml @@ -17,6 +17,8 @@ sections: url: /encoders - title: Homing & Endstops url: /endstops + - title: Mechanical Brakes + url: /mechanical-brakes - title: Thermistors url: /thermistors - title: Control & Tuning diff --git a/docs/mechanical-brakes.md b/docs/mechanical-brakes.md new file mode 100644 index 00000000..dc2cdaf6 --- /dev/null +++ b/docs/mechanical-brakes.md @@ -0,0 +1,66 @@ +# Mechanical Brake + +Some systems employ mechanical brakes on motors as a safety feature. These brakes can also be engaged as a power-saving function if the motor is not moving, but still under load. + +ODrive supports the use of its GPIO pins to connect to external brake drive electronics. + +When the ODrive engages the drive electronics, the brake will be disabled. When the drive enters a fault or idle state, the brake will be re-engaged. + +--- + +## Mechanical Brake Configuration +Each axis supports one mechanical brake. The following properties are accessible through `odrivetool`: + +Name | Type | Default +--- | -- | -- +gpio_num | int | 0 +enabled | boolean | false +is_active_low | boolean | true +pulldown | boolean | true + +### gpio_num +The GPIO pin number, according to the silkscreen labels on ODrive. Set with these commands: +``` +..mechanical_brake.config.gpio_num = <1, 2, 3, 4, 5, 6, 7, 8> +``` + +### enabled +Enables/disables the operation of the mechanical brake. +``` +..mechanical_brake.config.enabled = +``` + +### is_active_low +Most safety braking systems are active low, e.g. when the power is off, the brake is on. If the system uses brake drive electronics which use active high logic, flip this bit then reconsider the safety implications of your design... + +### pulldown +If `true`, it enables the GPIO pulldown resistor. If `false`, it enables the GPIO pullup resistor. + +### Example + +Let's say we're hacking away on an old ABB robotic arm. We've wired a 24V brake drive circuit triggered by GPIO5. When GPIO5 is driven, it will release the brakes on the axis we're moving. + +``` +..mechanical_brake.config.gpio_num = 5 +..mechanical_brake.config.enabled = True +``` + +Don't forget to save and reboot: +``` +.save_configuration() +.reboot() +``` + +### Testing The Mechanical Brakes +Depending on your system this could be a dangerous experiment. Ensure that you have taken all necessary precautions to confirm if the wrong brake were inadvertently released it would not lead to injury or damage to equipment. + +``` +..mechanical_brake.release() +``` +Note: If a brake is enabled, it will be engaged/disengaged during the next state machine step. + +After you're satisfied with the testing, you can re-enable the brake using the command + +``` +..mechanical_brake.engage() +```