2022-03-28 18:32:05 +01:00
|
|
|
#include "motor.hpp"
|
2022-04-22 13:59:40 +01:00
|
|
|
#include "hardware/clocks.h"
|
2022-03-28 18:32:05 +01:00
|
|
|
#include "pwm.hpp"
|
2022-04-22 13:59:40 +01:00
|
|
|
#include "math.h"
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-04 20:00:03 +01:00
|
|
|
namespace motor {
|
2022-04-22 13:59:40 +01:00
|
|
|
Motor::Motor(const pin_pair &pins, Direction direction, float speed_scale, float deadzone, float freq, DecayMode mode)
|
|
|
|
: motor_pins(pins), state(direction, speed_scale, deadzone), pwm_frequency(freq), motor_mode(mode) {
|
2022-03-28 18:32:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Motor::~Motor() {
|
2022-04-22 13:59:40 +01:00
|
|
|
gpio_set_function(motor_pins.positive, GPIO_FUNC_NULL);
|
|
|
|
gpio_set_function(motor_pins.negative, GPIO_FUNC_NULL);
|
2022-03-28 18:32:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Motor::init() {
|
|
|
|
bool success = false;
|
|
|
|
|
|
|
|
uint16_t period; uint16_t div16;
|
|
|
|
if(pimoroni::calculate_pwm_factors(pwm_frequency, period, div16)) {
|
|
|
|
pwm_period = period;
|
|
|
|
|
|
|
|
pwm_cfg = pwm_get_default_config();
|
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
// Set the new wrap (should be 1 less than the period to get full 0 to 100%)
|
|
|
|
pwm_config_set_wrap(&pwm_cfg, pwm_period - 1);
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
// Apply the divider
|
|
|
|
pwm_config_set_clkdiv(&pwm_cfg, (float)div16 / 16.0f); // There's no 'pwm_config_set_clkdiv_int_frac' for some reason...
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
pwm_init(pwm_gpio_to_slice_num(motor_pins.positive), &pwm_cfg, true);
|
|
|
|
pwm_init(pwm_gpio_to_slice_num(motor_pins.negative), &pwm_cfg, true);
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
gpio_set_function(motor_pins.positive, GPIO_FUNC_PWM);
|
|
|
|
gpio_set_function(motor_pins.negative, GPIO_FUNC_PWM);
|
|
|
|
|
|
|
|
pwm_set_gpio_level(motor_pins.positive, 0);
|
|
|
|
pwm_set_gpio_level(motor_pins.negative, 0);
|
2022-03-28 18:32:05 +01:00
|
|
|
|
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
pin_pair Motor::pins() const {
|
|
|
|
return motor_pins;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Motor::enable() {
|
|
|
|
apply_duty(state.enable_with_return(), motor_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Motor::disable() {
|
|
|
|
apply_duty(state.disable_with_return(), motor_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Motor::is_enabled() const {
|
|
|
|
return state.is_enabled();
|
|
|
|
}
|
|
|
|
|
|
|
|
float Motor::duty() const {
|
|
|
|
return state.get_duty();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Motor::duty(float duty) {
|
|
|
|
apply_duty(state.set_duty_with_return(duty), motor_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
float Motor::speed() const {
|
|
|
|
return state.get_speed();
|
2022-03-28 18:32:05 +01:00
|
|
|
}
|
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::speed(float speed) {
|
|
|
|
apply_duty(state.set_speed_with_return(speed), motor_mode);
|
2022-03-28 18:32:05 +01:00
|
|
|
}
|
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
float Motor::frequency() const {
|
2022-03-28 18:32:05 +01:00
|
|
|
return pwm_frequency;
|
|
|
|
}
|
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
bool Motor::frequency(float freq) {
|
2022-03-28 18:32:05 +01:00
|
|
|
bool success = false;
|
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
if((freq >= MotorState::MIN_FREQUENCY) && (freq <= MotorState::MAX_FREQUENCY)) {
|
|
|
|
// Calculate a suitable pwm wrap period for this frequency
|
|
|
|
uint16_t period; uint16_t div16;
|
|
|
|
if(pimoroni::calculate_pwm_factors(freq, period, div16)) {
|
|
|
|
|
|
|
|
// Record if the new period will be larger or smaller.
|
|
|
|
// This is used to apply new pwm speeds either before or after the wrap is applied,
|
|
|
|
// to avoid momentary blips in PWM output on SLOW_DECAY
|
|
|
|
bool pre_update_pwm = (period > pwm_period);
|
|
|
|
|
|
|
|
pwm_period = period;
|
|
|
|
pwm_frequency = freq;
|
|
|
|
|
|
|
|
uint pos_pin_num = pwm_gpio_to_slice_num(motor_pins.positive);
|
|
|
|
uint neg_pin_num = pwm_gpio_to_slice_num(motor_pins.negative);
|
|
|
|
|
|
|
|
// Apply the new divider
|
|
|
|
uint8_t div = div16 >> 4;
|
|
|
|
uint8_t mod = div16 % 16;
|
|
|
|
pwm_set_clkdiv_int_frac(pos_pin_num, div, mod);
|
|
|
|
if(neg_pin_num != pos_pin_num)
|
|
|
|
pwm_set_clkdiv_int_frac(neg_pin_num, div, mod);
|
|
|
|
|
|
|
|
// If the period is larger, update the pwm before setting the new wraps
|
|
|
|
if(pre_update_pwm) {
|
|
|
|
apply_duty(state.get_deadzoned_duty(), motor_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the new wrap (should be 1 less than the period to get full 0 to 100%)
|
|
|
|
pwm_set_wrap(pos_pin_num, pwm_period - 1);
|
|
|
|
if(neg_pin_num != pos_pin_num)
|
|
|
|
pwm_set_wrap(neg_pin_num, pwm_period - 1);
|
|
|
|
|
|
|
|
// If the period is smaller, update the pwm after setting the new wraps
|
|
|
|
if(!pre_update_pwm) {
|
|
|
|
apply_duty(state.get_deadzoned_duty(), motor_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::stop() {
|
|
|
|
apply_duty(state.stop_with_return(), motor_mode);
|
|
|
|
}
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::coast() {
|
|
|
|
apply_duty(state.stop_with_return(), FAST_DECAY);
|
|
|
|
}
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::brake() {
|
|
|
|
apply_duty(state.stop_with_return(), SLOW_DECAY);
|
|
|
|
}
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::full_negative() {
|
|
|
|
apply_duty(state.full_negative_with_return(), motor_mode);
|
|
|
|
}
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::full_positive() {
|
|
|
|
apply_duty(state.full_positive_with_return(), motor_mode);
|
|
|
|
}
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::to_percent(float in, float in_min, float in_max) {
|
|
|
|
apply_duty(state.to_percent_with_return(in, in_min, in_max), motor_mode);
|
|
|
|
}
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::to_percent(float in, float in_min, float in_max, float speed_min, float speed_max) {
|
|
|
|
apply_duty(state.to_percent_with_return(in, in_min, in_max, speed_min, speed_max), motor_mode);
|
|
|
|
}
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
Direction Motor::direction() const {
|
|
|
|
return state.get_direction();
|
2022-03-28 18:32:05 +01:00
|
|
|
}
|
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::direction(Direction direction) {
|
|
|
|
state.set_direction(direction);
|
2022-03-28 18:32:05 +01:00
|
|
|
}
|
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
float Motor::speed_scale() const {
|
|
|
|
return state.get_speed_scale();
|
2022-03-28 18:32:05 +01:00
|
|
|
}
|
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::speed_scale(float speed_scale) {
|
|
|
|
state.set_speed_scale(speed_scale);
|
2022-03-28 18:32:05 +01:00
|
|
|
}
|
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
float Motor::deadzone() const {
|
|
|
|
return state.get_deadzone();
|
2022-03-28 18:32:05 +01:00
|
|
|
}
|
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::deadzone(float deadzone) {
|
|
|
|
apply_duty(state.set_deadzone_with_return(deadzone), motor_mode);
|
|
|
|
}
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
DecayMode Motor::decay_mode() {
|
|
|
|
return motor_mode;
|
|
|
|
}
|
2022-03-28 18:32:05 +01:00
|
|
|
|
2022-04-22 13:59:40 +01:00
|
|
|
void Motor::decay_mode(DecayMode mode) {
|
|
|
|
motor_mode = mode;
|
|
|
|
apply_duty(state.get_deadzoned_duty(), motor_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Motor::apply_duty(float duty, DecayMode mode) {
|
|
|
|
if(isfinite(duty)) {
|
|
|
|
int32_t signed_level = MotorState::duty_to_level(duty, pwm_period);
|
|
|
|
|
|
|
|
switch(mode) {
|
|
|
|
case SLOW_DECAY: //aka 'Braking'
|
|
|
|
if(signed_level >= 0) {
|
|
|
|
pwm_set_gpio_level(motor_pins.positive, pwm_period);
|
|
|
|
pwm_set_gpio_level(motor_pins.negative, pwm_period - signed_level);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
pwm_set_gpio_level(motor_pins.positive, pwm_period + signed_level);
|
|
|
|
pwm_set_gpio_level(motor_pins.negative, pwm_period);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FAST_DECAY: //aka 'Coasting'
|
|
|
|
default:
|
|
|
|
if(signed_level >= 0) {
|
|
|
|
pwm_set_gpio_level(motor_pins.positive, signed_level);
|
|
|
|
pwm_set_gpio_level(motor_pins.negative, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
pwm_set_gpio_level(motor_pins.positive, 0);
|
|
|
|
pwm_set_gpio_level(motor_pins.negative, 0 - signed_level);
|
|
|
|
}
|
|
|
|
break;
|
2022-03-28 18:32:05 +01:00
|
|
|
}
|
2022-04-22 13:59:40 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
pwm_set_gpio_level(motor_pins.positive, 0);
|
|
|
|
pwm_set_gpio_level(motor_pins.negative, 0);
|
2022-03-28 18:32:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|