mirror of
https://github.com/infiniteCable2/panda.git
synced 2026-06-08 10:54:55 +08:00
* motors * can * cleanup unused stuff * initial clean * more clean * remove integral and derivative clamps, revert pwm driver to original * remove integral and derivative clamps, revert pwm driver to original * remove integral and derivative clamps, revert pwm driver to original * dont need this for now * clean * fix can rx and can version error * ignore body for misra mutation test * fix bus recovery, remove body rx hook
252 lines
7.9 KiB
C
252 lines
7.9 KiB
C
#pragma once
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "board/body/motor_common.h"
|
|
#include "board/body/motor_encoder.h"
|
|
|
|
// Motor pin map:
|
|
// M1 drive: PB8 -> TIM16_CH1, PB9 -> TIM17_CH1, PE2/PE3 enables
|
|
// M2 drive: PA2 -> TIM15_CH1, PA3 -> TIM15_CH2, PE8/PE9 enables
|
|
|
|
#define PWM_TIMER_CLOCK_HZ 120000000U
|
|
#define PWM_FREQUENCY_HZ 5000U
|
|
#define PWM_PERCENT_MAX 100
|
|
#define PWM_RELOAD_TICKS ((PWM_TIMER_CLOCK_HZ + (PWM_FREQUENCY_HZ / 2U)) / PWM_FREQUENCY_HZ)
|
|
|
|
#define KP 0.25f
|
|
#define KI 0.5f
|
|
#define KD 0.008f
|
|
#define KFF 0.9f
|
|
#define MAX_RPM 100.0f
|
|
#define OUTPUT_MAX 100.0f
|
|
#define DEADBAND_RPM 1.0f
|
|
#define UPDATE_PERIOD_US 1000U
|
|
|
|
typedef struct {
|
|
TIM_TypeDef *forward_timer;
|
|
uint8_t forward_channel;
|
|
TIM_TypeDef *reverse_timer;
|
|
uint8_t reverse_channel;
|
|
GPIO_TypeDef *pwm_port[2];
|
|
uint8_t pwm_pin[2];
|
|
uint8_t pwm_af[2];
|
|
GPIO_TypeDef *enable_port[2];
|
|
uint8_t enable_pin[2];
|
|
} motor_pwm_config_t;
|
|
|
|
static const motor_pwm_config_t motor_pwm_config[BODY_MOTOR_COUNT] = {
|
|
[BODY_MOTOR_LEFT - 1U] = {
|
|
TIM16, 1U, TIM17, 1U,
|
|
{GPIOB, GPIOB}, {8U, 9U}, {GPIO_AF1_TIM16, GPIO_AF1_TIM17},
|
|
{GPIOE, GPIOE}, {2U, 3U},
|
|
},
|
|
[BODY_MOTOR_RIGHT - 1U] = {
|
|
TIM15, 2U, TIM15, 1U,
|
|
{GPIOA, GPIOA}, {2U, 3U}, {GPIO_AF4_TIM15, GPIO_AF4_TIM15},
|
|
{GPIOE, GPIOE}, {8U, 9U},
|
|
},
|
|
};
|
|
|
|
typedef struct {
|
|
bool active;
|
|
float target_rpm;
|
|
float integral;
|
|
float previous_error;
|
|
float last_output;
|
|
uint32_t last_update_us;
|
|
} motor_speed_state_t;
|
|
|
|
static inline float motor_absf(float value) {
|
|
return (value < 0.0f) ? -value : value;
|
|
}
|
|
|
|
static inline float motor_clampf(float value, float min_value, float max_value) {
|
|
if (value < min_value) {
|
|
return min_value;
|
|
}
|
|
if (value > max_value) {
|
|
return max_value;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static motor_speed_state_t motor_speed_states[BODY_MOTOR_COUNT];
|
|
|
|
static inline void motor_pwm_write(TIM_TypeDef *timer, uint8_t channel, uint8_t percentage) {
|
|
uint32_t period = (timer->ARR != 0U) ? timer->ARR : PWM_RELOAD_TICKS;
|
|
uint16_t comp = (uint16_t)((period * (uint32_t)percentage) / 100U);
|
|
if (channel == 1U) {
|
|
register_set(&(timer->CCR1), comp, 0xFFFFU);
|
|
} else if (channel == 2U) {
|
|
register_set(&(timer->CCR2), comp, 0xFFFFU);
|
|
}
|
|
}
|
|
|
|
static inline motor_speed_state_t *motor_speed_state_get(uint8_t motor) {
|
|
return body_motor_is_valid(motor) ? &motor_speed_states[motor - 1U] : NULL;
|
|
}
|
|
|
|
static inline void motor_speed_state_reset(motor_speed_state_t *state) {
|
|
state->active = false;
|
|
state->target_rpm = 0.0f;
|
|
state->integral = 0.0f;
|
|
state->previous_error = 0.0f;
|
|
state->last_output = 0.0f;
|
|
state->last_update_us = 0U;
|
|
}
|
|
|
|
static void motor_speed_controller_init(void) {
|
|
for (uint8_t i = 0U; i < BODY_MOTOR_COUNT; i++) {
|
|
motor_speed_state_reset(&motor_speed_states[i]);
|
|
}
|
|
}
|
|
|
|
static void motor_speed_controller_disable(uint8_t motor) {
|
|
motor_speed_state_t *state = motor_speed_state_get(motor);
|
|
if (state == NULL) {
|
|
return;
|
|
}
|
|
motor_speed_state_reset(state);
|
|
}
|
|
|
|
static inline void motor_write_enable(uint8_t motor_index, bool enable) {
|
|
const motor_pwm_config_t *cfg = &motor_pwm_config[motor_index];
|
|
uint8_t level = enable ? 1U : 0U;
|
|
set_gpio_output(cfg->enable_port[0], cfg->enable_pin[0], level);
|
|
set_gpio_output(cfg->enable_port[1], cfg->enable_pin[1], level);
|
|
}
|
|
|
|
static void motor_init(void) {
|
|
register_set_bits(&(RCC->AHB4ENR), RCC_AHB4ENR_GPIOAEN | RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOEEN);
|
|
register_set_bits(&(RCC->APB2ENR), RCC_APB2ENR_TIM16EN | RCC_APB2ENR_TIM17EN | RCC_APB2ENR_TIM15EN);
|
|
|
|
for (uint8_t i = 0U; i < BODY_MOTOR_COUNT; i++) {
|
|
const motor_pwm_config_t *cfg = &motor_pwm_config[i];
|
|
motor_write_enable(i, false);
|
|
set_gpio_alternate(cfg->pwm_port[0], cfg->pwm_pin[0], cfg->pwm_af[0]);
|
|
set_gpio_alternate(cfg->pwm_port[1], cfg->pwm_pin[1], cfg->pwm_af[1]);
|
|
|
|
pwm_init(cfg->forward_timer, cfg->forward_channel);
|
|
pwm_init(cfg->reverse_timer, cfg->reverse_channel);
|
|
|
|
TIM_TypeDef *forward_timer = cfg->forward_timer;
|
|
register_set(&(forward_timer->PSC), 0U, 0xFFFFU);
|
|
register_set(&(forward_timer->ARR), PWM_RELOAD_TICKS, 0xFFFFU);
|
|
forward_timer->EGR |= TIM_EGR_UG;
|
|
register_set(&(forward_timer->BDTR), TIM_BDTR_MOE, 0xFFFFU);
|
|
|
|
if (cfg->reverse_timer != cfg->forward_timer) {
|
|
TIM_TypeDef *reverse_timer = cfg->reverse_timer;
|
|
register_set(&(reverse_timer->PSC), 0U, 0xFFFFU);
|
|
register_set(&(reverse_timer->ARR), PWM_RELOAD_TICKS, 0xFFFFU);
|
|
reverse_timer->EGR |= TIM_EGR_UG;
|
|
register_set(&(reverse_timer->BDTR), TIM_BDTR_MOE, 0xFFFFU);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void motor_apply_pwm(uint8_t motor, int32_t speed_command) {
|
|
if (!body_motor_is_valid(motor)) {
|
|
return;
|
|
}
|
|
|
|
int8_t speed = (int8_t)motor_clampf((float)speed_command, -(float)PWM_PERCENT_MAX, (float)PWM_PERCENT_MAX);
|
|
uint8_t pwm_value = (uint8_t)((speed < 0) ? -speed : speed);
|
|
uint8_t motor_index = motor - 1U;
|
|
motor_write_enable(motor_index, speed != 0);
|
|
const motor_pwm_config_t *cfg = &motor_pwm_config[motor_index];
|
|
|
|
if (speed > 0) {
|
|
motor_pwm_write(cfg->forward_timer, cfg->forward_channel, pwm_value);
|
|
motor_pwm_write(cfg->reverse_timer, cfg->reverse_channel, 0U);
|
|
} else if (speed < 0) {
|
|
motor_pwm_write(cfg->forward_timer, cfg->forward_channel, 0U);
|
|
motor_pwm_write(cfg->reverse_timer, cfg->reverse_channel, pwm_value);
|
|
} else {
|
|
motor_pwm_write(cfg->forward_timer, cfg->forward_channel, 0U);
|
|
motor_pwm_write(cfg->reverse_timer, cfg->reverse_channel, 0U);
|
|
}
|
|
}
|
|
|
|
static inline void motor_set_speed(uint8_t motor, int8_t speed) {
|
|
motor_speed_controller_disable(motor);
|
|
motor_apply_pwm(motor, (int32_t)speed);
|
|
}
|
|
|
|
static inline void motor_speed_controller_set_target_rpm(uint8_t motor, float target_rpm) {
|
|
motor_speed_state_t *state = motor_speed_state_get(motor);
|
|
if (state == NULL) {
|
|
return;
|
|
}
|
|
|
|
target_rpm = motor_clampf(target_rpm, -MAX_RPM, MAX_RPM);
|
|
if (motor_absf(target_rpm) <= DEADBAND_RPM) {
|
|
motor_speed_controller_disable(motor);
|
|
motor_apply_pwm(motor, 0);
|
|
return;
|
|
}
|
|
|
|
state->active = true;
|
|
state->target_rpm = target_rpm;
|
|
state->integral = 0.0f;
|
|
state->previous_error = target_rpm - motor_encoder_get_speed_rpm(motor);
|
|
state->last_output = 0.0f;
|
|
state->last_update_us = 0U;
|
|
}
|
|
|
|
static inline void motor_speed_controller_update(uint32_t now_us) {
|
|
for (uint8_t motor = BODY_MOTOR_LEFT; motor <= BODY_MOTOR_RIGHT; motor++) {
|
|
motor_speed_state_t *state = motor_speed_state_get(motor);
|
|
if (!state->active) {
|
|
continue;
|
|
}
|
|
|
|
bool first_update = (state->last_update_us == 0U);
|
|
uint32_t dt_us = first_update ? UPDATE_PERIOD_US : (now_us - state->last_update_us);
|
|
if (!first_update && (dt_us < UPDATE_PERIOD_US)) {
|
|
continue;
|
|
}
|
|
|
|
float measured_rpm = motor_encoder_get_speed_rpm(motor);
|
|
float error = state->target_rpm - measured_rpm;
|
|
|
|
if ((motor_absf(state->target_rpm) <= DEADBAND_RPM) && (motor_absf(error) <= DEADBAND_RPM) && (motor_absf(measured_rpm) <= DEADBAND_RPM)) {
|
|
motor_speed_controller_disable(motor);
|
|
motor_apply_pwm(motor, 0);
|
|
continue;
|
|
}
|
|
|
|
float dt_s = (float)dt_us * 1.0e-6f;
|
|
float control = KFF * state->target_rpm;
|
|
|
|
if (dt_s > 0.0f) {
|
|
state->integral += error * dt_s;
|
|
float derivative = first_update ? 0.0f : (error - state->previous_error) / dt_s;
|
|
|
|
control += (KP * error) + (KI * state->integral) + (KD * derivative);
|
|
} else {
|
|
state->integral = 0.0f;
|
|
control += KP * error;
|
|
}
|
|
|
|
if ((state->target_rpm > 0.0f) && (control < 0.0f)) {
|
|
control = 0.0f;
|
|
state->integral = 0.0f;
|
|
} else if ((state->target_rpm < 0.0f) && (control > 0.0f)) {
|
|
control = 0.0f;
|
|
state->integral = 0.0f;
|
|
}
|
|
|
|
control = motor_clampf(control, -OUTPUT_MAX, OUTPUT_MAX);
|
|
|
|
int32_t command = (control >= 0.0f) ? (int32_t)(control + 0.5f) : (int32_t)(control - 0.5f);
|
|
motor_apply_pwm(motor, command);
|
|
|
|
state->previous_error = error;
|
|
state->last_output = control;
|
|
state->last_update_us = now_us;
|
|
}
|
|
}
|