introduce GPIO modes

This commit is contained in:
Samuel Sadok
2020-07-21 12:55:10 +02:00
parent c745983f95
commit 1f3391c81b
26 changed files with 459 additions and 293 deletions
+6
View File
@@ -1,6 +1,11 @@
# Unreleased Features
Please add a note of your changes below this heading if you make a Pull Request.
### API Miration Notes
* `enable_uart` and `uart_baudrate` were renamed to `enable_uart0` and `uart0_baudrate`.
* `enable_i2c_instead_of_can` was replaced by the separate settings `enable_i2c0` and `enable_can0`.
### Added
* AC Induction Motor support.
* Tracking of rotor flux through rotor time constant
@@ -30,6 +35,7 @@ Please add a note of your changes below this heading if you make a Pull Request.
* Added scripts for building via docker.
* Added ability to change uart baudrate via fibre
* Introduced `odrive-interface.yaml` as a root source for the ODrive's API. `odrivetool` connects much faster as a side effect.
* Introduced GPIO modes. GPIOs now need to be explicitly set to the mode corresponding to the feature that they are used by. See `<odrv>.config.gpioX_mode`.
### Changed
* Changed ratiometric `motor.config.current_lim_tolerance` to absolute `motor.config.current_lim_margin`
+11 -7
View File
@@ -18,23 +18,26 @@
#include <arm_math.h>
#if HW_VERSION_MAJOR == 3
#if HW_VERSION_MINOR <= 3
#define SHUNT_RESISTANCE (675e-6f)
#else
#define SHUNT_RESISTANCE (500e-6f)
#endif
#endif
#define AXIS_COUNT (2)
#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR <= 4
#define GPIO_COUNT (5)
#elif HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 5
#define GPIO_COUNT (8)
// Total count of GPIOs, including encoder pins, CAN pins and a dummy GPIO0.
// ODrive v3.4 and earlier don't have GPIOs 6, 7 and 8 but to keep the numbering
// consistent we just leave a gap in the counting scheme.
#define GPIO_COUNT (17)
#if HW_VERSION_MINOR >= 5 && HW_VERSION_VOLTAGE >= 48
#define DEFAULT_BRAKE_RESISTANCE (2.0f) // [ohm]
#else
#define DEFAULT_BRAKE_RESISTANCE (0.47f) // [ohm]
#endif
#define GPIO_AF_NONE ((uint8_t)0xff)
#ifdef __cplusplus
#include <Drivers/STM32/stm32_gpio.hpp>
@@ -51,6 +54,7 @@ extern Motor motors[AXIS_COUNT];
extern OnboardThermistorCurrentLimiter fet_thermistors[AXIS_COUNT];
extern Encoder encoders[AXIS_COUNT];
extern Stm32Gpio gpios[GPIO_COUNT];
extern uint8_t alternate_functions[GPIO_COUNT][6];
extern uint32_t pwm_in_gpios[4];
#include <Drivers/STM32/stm32_spi_arbiter.hpp>
-12
View File
@@ -106,12 +106,6 @@ void MX_GPIO_Init(void)
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : PBPin PBPin */
GPIO_InitStruct.Pin = GPIO_6_Pin|GPIO_8_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = EN_GATE_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
@@ -119,12 +113,6 @@ void MX_GPIO_Init(void)
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(EN_GATE_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = GPIO_7_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIO_7_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = nFAULT_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
-12
View File
@@ -84,23 +84,11 @@ void MX_I2C1_Init(uint8_t addr)
void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(i2cHandle->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspInit 0 */
/* USER CODE END I2C1_MspInit 0 */
/**I2C1 GPIO Configuration
PB8 ------> I2C1_SCL
PB9 ------> I2C1_SDA
*/
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* I2C1 clock enable */
__HAL_RCC_I2C1_CLK_ENABLE();
+4 -4
View File
@@ -451,19 +451,19 @@ void ADC_IRQ_Dispatch(ADC_HandleTypeDef* hadc, ADC_handler_t callback) {
void decode_tim_capture(TIM_HandleTypeDef *htim, TIM_capture_callback_t callback) {
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1)) {
__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);
callback(1, htim->Instance->CCR1);
callback(0, htim->Instance->CCR1);
}
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2)) {
__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC2);
callback(2, htim->Instance->CCR2);
callback(1, htim->Instance->CCR2);
}
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC3)) {
__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC3);
callback(3, htim->Instance->CCR3);
callback(2, htim->Instance->CCR3);
}
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC4)) {
__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC4);
callback(4, htim->Instance->CCR4);
callback(3, htim->Instance->CCR4);
}
}
-35
View File
@@ -462,7 +462,6 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle)
void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(tim_encoderHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspInit 0 */
@@ -470,17 +469,6 @@ void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle)
/* USER CODE END TIM3_MspInit 0 */
/* TIM3 clock enable */
__HAL_RCC_TIM3_CLK_ENABLE();
/**TIM3 GPIO Configuration
PB4 ------> TIM3_CH1
PB5 ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = M0_ENC_A_Pin|M0_ENC_B_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN TIM3_MspInit 1 */
@@ -493,17 +481,6 @@ void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle)
/* USER CODE END TIM4_MspInit 0 */
/* TIM4 clock enable */
__HAL_RCC_TIM4_CLK_ENABLE();
/**TIM4 GPIO Configuration
PB6 ------> TIM4_CH1
PB7 ------> TIM4_CH2
*/
GPIO_InitStruct.Pin = M1_ENC_A_Pin|M1_ENC_B_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM4;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN TIM4_MspInit 1 */
@@ -514,7 +491,6 @@ void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle)
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef* tim_icHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(tim_icHandle->Instance==TIM5)
{
/* USER CODE BEGIN TIM5_MspInit 0 */
@@ -522,17 +498,6 @@ void HAL_TIM_IC_MspInit(TIM_HandleTypeDef* tim_icHandle)
/* USER CODE END TIM5_MspInit 0 */
/* TIM5 clock enable */
__HAL_RCC_TIM5_CLK_ENABLE();
/**TIM5 GPIO Configuration
PA2 ------> TIM5_CH3
PA3 ------> TIM5_CH4
*/
GPIO_InitStruct.Pin = GPIO_3_Pin|GPIO_4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM5;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* TIM5 interrupt Init */
HAL_NVIC_SetPriority(TIM5_IRQn, 5, 0);
-19
View File
@@ -83,7 +83,6 @@ void MX_UART4_Init(void)
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(uartHandle->Instance==UART4)
{
/* USER CODE BEGIN UART4_MspInit 0 */
@@ -91,24 +90,6 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
/* USER CODE END UART4_MspInit 0 */
/* UART4 clock enable */
__HAL_RCC_UART4_CLK_ENABLE();
/**UART4 GPIO Configuration
PA0-WKUP ------> UART4_TX
PA1 ------> UART4_RX
*/
GPIO_InitStruct.Pin = GPIO_1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
HAL_GPIO_Init(GPIO_1_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
HAL_GPIO_Init(GPIO_2_GPIO_Port, &GPIO_InitStruct);
/* UART4 DMA Init */
/* UART4_RX Init */
+73 -5
View File
@@ -80,24 +80,54 @@ Encoder encoders[AXIS_COUNT] = {
};
// Note that GPIO1 as labeled on the board corresponds to gpios[0] in code.
#if (HW_VERSION_MINOR == 1) || (HW_VERSION_MINOR == 2)
Stm32Gpio gpios[] = {
{nullptr, 0}, // dummy GPIO0 so that PCB labels and software numbers match
{GPIOB, GPIO_PIN_2},
{GPIOA, GPIO_PIN_5},
{GPIOA, GPIO_PIN_4},
{GPIOA, GPIO_PIN_3}
{GPIOA, GPIO_PIN_3},
{nullptr, 0},
{nullptr, 0},
{nullptr, 0},
{nullptr, 0},
{GPIOB, GPIO_PIN_4}, // ENC0_A
{GPIOB, GPIO_PIN_5}, // ENC0_B
{GPIOA, GPIO_PIN_15}, // ENC0_Z
{GPIOB, GPIO_PIN_6}, // ENC1_A
{GPIOB, GPIO_PIN_7}, // ENC1_B
{GPIOB, GPIO_PIN_3}, // ENC1_Z
{GPIOB, GPIO_PIN_8}, // CAN_R
{GPIOB, GPIO_PIN_9}, // CAN_D
};
#elif (HW_VERSION_MINOR == 3) || (HW_VERSION_MINOR == 4)
Stm32Gpio gpios[] = {
{nullptr, 0}, // dummy GPIO0 so that PCB labels and software numbers match
{GPIOA, GPIO_PIN_0},
{GPIOA, GPIO_PIN_1},
{GPIOA, GPIO_PIN_2},
{GPIOA, GPIO_PIN_3},
{GPIOB, GPIO_PIN_2}
{GPIOB, GPIO_PIN_2},
{nullptr, 0},
{nullptr, 0},
{nullptr, 0},
{GPIOB, GPIO_PIN_4}, // ENC0_A
{GPIOB, GPIO_PIN_5}, // ENC0_B
{GPIOA, GPIO_PIN_15}, // ENC0_Z
{GPIOB, GPIO_PIN_6}, // ENC1_A
{GPIOB, GPIO_PIN_7}, // ENC1_B
{GPIOB, GPIO_PIN_3}, // ENC1_Z
{GPIOB, GPIO_PIN_8}, // CAN_R
{GPIOB, GPIO_PIN_9}, // CAN_D
};
#elif (HW_VERSION_MINOR == 5) || (HW_VERSION_MINOR == 6)
Stm32Gpio gpios[] = {
Stm32Gpio gpios[GPIO_COUNT] = {
{nullptr, 0}, // dummy GPIO0 so that PCB labels and software numbers match
{GPIOA, GPIO_PIN_0},
{GPIOA, GPIO_PIN_1},
{GPIOA, GPIO_PIN_2},
@@ -105,12 +135,50 @@ Stm32Gpio gpios[] = {
{GPIOC, GPIO_PIN_4},
{GPIOB, GPIO_PIN_2},
{GPIOA, GPIO_PIN_15},
{GPIOB, GPIO_PIN_3}
{GPIOB, GPIO_PIN_3},
{GPIOB, GPIO_PIN_4}, // ENC0_A
{GPIOB, GPIO_PIN_5}, // ENC0_B
{GPIOC, GPIO_PIN_9}, // ENC0_Z
{GPIOB, GPIO_PIN_6}, // ENC1_A
{GPIOB, GPIO_PIN_7}, // ENC1_B
{GPIOC, GPIO_PIN_15}, // ENC1_Z
{GPIOB, GPIO_PIN_8}, // CAN_R
{GPIOB, GPIO_PIN_9}, // CAN_D
};
#else
#error "unknown GPIOs"
#endif
#define GPIO_AF_NONE ((uint8_t)0xff)
uint8_t alternate_functions[GPIO_COUNT][6] = {
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
#if HW_VERSION_MINOR >= 3
{GPIO_AF8_UART4, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF8_UART4, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
#else
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
#endif
{GPIO_AF_NONE, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM3, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM3, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF2_TIM4},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF2_TIM4},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF9_CAN1, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF_NONE},
{GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF9_CAN1, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF_NONE},
};
#if HW_VERSION_MINOR <= 2
uint32_t pwm_in_gpios[4] = { 0, 0, 0, 4 }; // 0 means not in use
#else
+46
View File
@@ -38,6 +38,52 @@ IRQn_Type get_irq_number(uint16_t pin_number) {
}
}
#define GPIO_MODE 0x00000003U
#define GPIO_OUTPUT_TYPE 0x00000010U
bool Stm32Gpio::config(uint32_t mode, uint32_t pull, uint32_t speed) {
if (!port_)
return false;
size_t position = get_pin_number();
// The following code is mostly taken from HAL_GPIO_Init
/* Configure IO Direction mode (Input, Output, Alternate or Analog) */
uint32_t temp = port_->MODER;
temp &= ~(GPIO_MODER_MODER0 << (position * 2U));
temp |= ((mode & GPIO_MODE) << (position * 2U));
port_->MODER = temp;
/* In case of Output or Alternate function mode selection */
if((mode == GPIO_MODE_OUTPUT_PP) || (mode == GPIO_MODE_AF_PP) ||
(mode == GPIO_MODE_OUTPUT_OD) || (mode == GPIO_MODE_AF_OD))
{
/* Check the Speed parameter */
assert_param(IS_GPIO_SPEED(speed));
/* Configure the IO Speed */
temp = port_->OSPEEDR;
temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2U));
temp |= (speed << (position * 2U));
port_->OSPEEDR = temp;
/* Configure the IO Output Type */
temp = port_->OTYPER;
temp &= ~(GPIO_OTYPER_OT_0 << position) ;
temp |= (((mode & GPIO_OUTPUT_TYPE) >> 4U) << position);
port_->OTYPER = temp;
}
/* Activate the Pull-up or Pull down resistor for the current IO */
temp = port_->PUPDR;
temp &= ~(GPIO_PUPDR_PUPDR0 << (position * 2U));
temp |= ((pull) << (position * 2U));
port_->PUPDR = temp;
return true;
}
bool Stm32Gpio::subscribe(bool rising_edge, bool falling_edge, void (*callback)(void*), void* ctx) {
uint32_t pin_number = get_pin_number();
if (pin_number >= N_EXTI) {
+2 -14
View File
@@ -17,21 +17,9 @@ public:
*
* This can be done regardless of the current state of the GPIO.
*
* TODO: make glitch-free if the configuration is the same as before
* TODO: avoid disabling interrupt if it is enabled
* If any subscription is in place, it is not disabled by this function.
*/
bool config(uint32_t mode, uint32_t pull) {
if (!port_)
return false;
HAL_GPIO_DeInit(port_, pin_mask_);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = pin_mask_;
GPIO_InitStruct.Mode = mode;
GPIO_InitStruct.Pull = pull;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(port_, &GPIO_InitStruct);
return true;
}
bool config(uint32_t mode, uint32_t pull, uint32_t speed = GPIO_SPEED_FREQ_LOW);
void write(bool state) {
if (port_) {
+3 -1
View File
@@ -148,7 +148,9 @@ void Axis::set_step_dir_active(bool active) {
step_gpio_.config(GPIO_MODE_INPUT, GPIO_PULLDOWN);
// Subscribe to rising edges of the step GPIO
step_gpio_.subscribe(true, false, step_cb_wrapper, this);
if (!step_gpio_.subscribe(true, false, step_cb_wrapper, this)) {
odrv.misconfigured_ = true;
}
step_dir_active_ = true;
} else {
+5 -12
View File
@@ -104,7 +104,9 @@ void Encoder::enc_index_cb() {
void Encoder::set_idx_subscribe(bool override_enable) {
if (config_.use_index && (override_enable || !config_.find_idx_on_lockin_only)) {
index_gpio_.config(GPIO_MODE_INPUT, GPIO_PULLDOWN);
index_gpio_.subscribe(true, false, enc_index_cb_wrapper, this);
if (!index_gpio_.subscribe(true, false, enc_index_cb_wrapper, this)) {
odrv.misconfigured_ = true;
}
} else if (!config_.use_index || config_.find_idx_on_lockin_only) {
index_gpio_.unsubscribe();
}
@@ -444,18 +446,9 @@ void Encoder::abs_spi_cb() {
}
void Encoder::abs_spi_cs_pin_init(){
// Decode cs pin
// Decode and init cs pin
abs_spi_cs_gpio_ = get_gpio(config_.abs_spi_cs_gpio_pin);
// Init cs pin
// TODO: absorb into Stm32Gpio class
HAL_GPIO_DeInit(abs_spi_cs_gpio_.port_, abs_spi_cs_gpio_.pin_mask_);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = abs_spi_cs_gpio_.pin_mask_;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(abs_spi_cs_gpio_.port_, &GPIO_InitStruct);
abs_spi_cs_gpio_.config(GPIO_MODE_OUTPUT_PP, GPIO_PULLUP);
// Write pin high
abs_spi_cs_gpio_.write(true);
+24 -32
View File
@@ -537,12 +537,6 @@ void update_brake_current() {
/* RC PWM input --------------------------------------------------------------*/
void pwm_in_init() {
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM5;
TIM_IC_InitTypeDef sConfigIC;
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
@@ -552,16 +546,8 @@ void pwm_in_init() {
uint32_t channels[] = {TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, TIM_CHANNEL_4};
for (size_t i = 0; i < 4; ++i) {
uint32_t gpio_num = pwm_in_gpios[i];
if (gpio_num < 1 || gpio_num > GPIO_COUNT)
if (!fibre::is_endpoint_ref_valid(odrv.config_.pwm_mappings[i].endpoint))
continue;
if (!fibre::is_endpoint_ref_valid(odrv.config_.pwm_mappings[gpio_num - 1].endpoint))
continue;
Stm32Gpio gpio = gpios[gpio_num];
GPIO_InitStruct.Pin = gpio.pin_mask_;
HAL_GPIO_DeInit(gpio.port_, gpio.pin_mask_);
HAL_GPIO_Init(gpio.port_, &GPIO_InitStruct);
HAL_TIM_IC_ConfigChannel(&htim5, &sConfigIC, channels[i]);
HAL_TIM_IC_Start_IT(&htim5, channels[i]);
}
@@ -575,7 +561,10 @@ void pwm_in_init() {
#define PWM_MAX_LEGAL_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 2500UL) // ignore high periods longer than 2.5ms
#define PWM_INVERT_INPUT false
void handle_pulse(int gpio_num, uint32_t high_time) {
/**
* @param channel: A channel number in [0, 3]
*/
void handle_pulse(int channel, uint32_t high_time) {
if (high_time < PWM_MIN_LEGAL_HIGH_TIME || high_time > PWM_MAX_LEGAL_HIGH_TIME)
return;
@@ -584,33 +573,36 @@ void handle_pulse(int gpio_num, uint32_t high_time) {
if (high_time > PWM_MAX_HIGH_TIME)
high_time = PWM_MAX_HIGH_TIME;
float fraction = (float)(high_time - PWM_MIN_HIGH_TIME) / (float)(PWM_MAX_HIGH_TIME - PWM_MIN_HIGH_TIME);
float value = odrv.config_.pwm_mappings[gpio_num - 1].min +
(fraction * (odrv.config_.pwm_mappings[gpio_num - 1].max - odrv.config_.pwm_mappings[gpio_num - 1].min));
float value = odrv.config_.pwm_mappings[channel].min +
(fraction * (odrv.config_.pwm_mappings[channel].max - odrv.config_.pwm_mappings[channel].min));
fibre::set_endpoint_from_float(odrv.config_.pwm_mappings[gpio_num - 1].endpoint, value);
fibre::set_endpoint_from_float(odrv.config_.pwm_mappings[channel].endpoint, value);
}
/**
* @param channel: A channel number in [0, 3]
*/
void pwm_in_cb(int channel, uint32_t timestamp) {
static uint32_t last_timestamp[GPIO_COUNT] = { 0 };
static bool last_pin_state[GPIO_COUNT] = { false };
static bool last_sample_valid[GPIO_COUNT] = { false };
static uint32_t last_timestamp[4] = { 0 };
static bool last_pin_state[4] = { false };
static bool last_sample_valid[4] = { false };
if (channel >= 4)
return;
int gpio_num = pwm_in_gpios[channel];
if (gpio_num < 1 || gpio_num > GPIO_COUNT)
Stm32Gpio gpio = get_gpio(pwm_in_gpios[channel]);
if (!gpio)
return;
bool current_pin_state = get_gpio(gpio_num).read();
bool current_pin_state = gpio.read();
if (last_sample_valid[gpio_num - 1]
&& (last_pin_state[gpio_num - 1] != PWM_INVERT_INPUT)
if (last_sample_valid[channel]
&& (last_pin_state[channel] != PWM_INVERT_INPUT)
&& (current_pin_state == PWM_INVERT_INPUT)) {
handle_pulse(gpio_num, timestamp - last_timestamp[gpio_num - 1]);
handle_pulse(channel, timestamp - last_timestamp[channel]);
}
last_timestamp[gpio_num - 1] = timestamp;
last_pin_state[gpio_num - 1] = current_pin_state;
last_sample_valid[gpio_num - 1] = true;
last_timestamp[channel] = timestamp;
last_pin_state[channel] = current_pin_state;
last_sample_valid[channel] = true;
}
@@ -630,7 +622,7 @@ static void analog_polling_thread(void *)
struct PWMMapping_t *map = &odrv.config_.analog_mappings[i];
if (fibre::is_endpoint_ref_valid(map->endpoint))
update_analog_endpoint(map, i + 1);
update_analog_endpoint(map, i);
}
osDelay(10);
}
+100 -51
View File
@@ -154,42 +154,28 @@ void ODrive::enter_dfu_mode() {
extern "C" int construct_objects(){
#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3
if (odrv.config_.enable_i2c_instead_of_can) {
if (odrv.config_.enable_i2c0) {
// Set up the direction GPIO as input
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
get_gpio(3).config(GPIO_MODE_INPUT, GPIO_PULLUP);
get_gpio(4).config(GPIO_MODE_INPUT, GPIO_PULLUP);
get_gpio(5).config(GPIO_MODE_INPUT, GPIO_PULLUP);
GPIO_InitStruct.Pin = I2C_A0_PIN;
HAL_GPIO_Init(I2C_A0_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = I2C_A1_PIN;
HAL_GPIO_Init(I2C_A1_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = I2C_A2_PIN;
HAL_GPIO_Init(I2C_A2_PORT, &GPIO_InitStruct);
osDelay(1);
osDelay(1); // This has no effect but was here before.
i2c_stats_.addr = (0xD << 3);
i2c_stats_.addr |= HAL_GPIO_ReadPin(I2C_A0_PORT, I2C_A0_PIN) != GPIO_PIN_RESET ? 0x1 : 0;
i2c_stats_.addr |= HAL_GPIO_ReadPin(I2C_A1_PORT, I2C_A1_PIN) != GPIO_PIN_RESET ? 0x2 : 0;
i2c_stats_.addr |= HAL_GPIO_ReadPin(I2C_A2_PORT, I2C_A2_PIN) != GPIO_PIN_RESET ? 0x4 : 0;
i2c_stats_.addr |= get_gpio(3).read() ? 0x1 : 0;
i2c_stats_.addr |= get_gpio(4).read() ? 0x2 : 0;
i2c_stats_.addr |= get_gpio(5).read() ? 0x4 : 0;
MX_I2C1_Init(i2c_stats_.addr);
} else
}
#elif HW_VERSION_MAJOR != 3
#error "unsupported hardware"
#endif
MX_CAN1_Init();
HAL_UART_DeInit(&huart4);
huart4.Init.BaudRate = odrv.config_.uart_baudrate;
huart4.Init.BaudRate = odrv.config_.uart0_baudrate;
HAL_UART_Init(&huart4);
// Init general user ADC on some GPIOs.
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
for (Stm32Gpio& gpio: gpios) {
GPIO_InitStruct.Pin = gpio.pin_mask_;
HAL_GPIO_Init(gpio.port_, &GPIO_InitStruct);
}
// Construct all objects.
odCAN = new ODriveCAN(can_config, &hcan1);
for (size_t i = 0; i < AXIS_COUNT; ++i) {
@@ -250,33 +236,96 @@ int odrive_main(void) {
// Init timers
board_init();
// Init GPIOs according to their configured mode
for (size_t i = 0; i < GPIO_COUNT; ++i) {
// Skip unavailable GPIOs
if (!get_gpio(i)) {
continue;
}
ODriveIntf::GpioMode mode = odrv.config_.gpio_modes[i];
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = get_gpio(i).pin_mask_;
GPIO_InitStruct.Alternate = 0;
switch (mode) {
case ODriveIntf::GPIO_MODE_DIGITAL: {
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
} break;
case ODriveIntf::GPIO_MODE_ANALOG_IN: {
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
} break;
case ODriveIntf::GPIO_MODE_UART0: {
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = (i == 0) ? GPIO_PULLDOWN : GPIO_PULLUP; // this is probably swapped but imitates old behavior
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = alternate_functions[i][0];
if (!odrv.config_.enable_uart0) {
odrv.misconfigured_ = true;
}
} break;
case ODriveIntf::GPIO_MODE_PWM0: {
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = alternate_functions[i][1];
} break;
case ODriveIntf::GPIO_MODE_CAN0: {
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = alternate_functions[i][2];
if (!odrv.config_.enable_can0) {
odrv.misconfigured_ = true;
}
} break;
case ODriveIntf::GPIO_MODE_I2C0: {
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = alternate_functions[i][3];
if (!odrv.config_.enable_i2c0) {
odrv.misconfigured_ = true;
}
} break;
case ODriveIntf::GPIO_MODE_ENC0: {
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = alternate_functions[i][4];
} break;
case ODriveIntf::GPIO_MODE_ENC1: {
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = alternate_functions[i][5];
} break;
default: {
GPIO_InitStruct.Alternate = GPIO_AF_NONE;
}
}
// The selected mode is invalid for this GPIO. Leave GPIO uninitialized.
if (GPIO_InitStruct.Alternate == GPIO_AF_NONE) {
odrv.misconfigured_ = true;
continue;
}
HAL_GPIO_Init(get_gpio(i).port_, &GPIO_InitStruct);
}
// Some peripherals must be initialized after the GPIOs are set up.
if (odrv.config_.enable_can0) {
MX_CAN1_Init();
}
// Start ADC for temperature measurements and user measurements
start_general_purpose_adc();
// TODO: make dynamically reconfigurable
#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3
if (odrv.config_.enable_uart) {
GPIO_InitTypeDef GPIO_InitStruct;
// make sure nothing is hogging the GPIO's
Stm32Gpio{GPIO_1_GPIO_Port, GPIO_1_Pin}.unsubscribe();
Stm32Gpio{GPIO_2_GPIO_Port, GPIO_2_Pin}.unsubscribe();
GPIO_InitStruct.Pin = GPIO_1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
HAL_GPIO_Init(GPIO_1_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
HAL_GPIO_Init(GPIO_2_GPIO_Port, &GPIO_InitStruct);
}
#endif
//osDelay(100);
// Init communications (this requires the axis objects to be constructed)
init_communication();
+28 -35
View File
@@ -58,15 +58,33 @@ struct PWMMapping_t {
// @brief general user configurable board configuration
struct BoardConfig_t {
bool enable_uart = true;
bool enable_i2c_instead_of_can = false;
ODriveIntf::GpioMode gpio_modes[GPIO_COUNT] = {
ODriveIntf::GPIO_MODE_DIGITAL,
ODriveIntf::GPIO_MODE_UART0,
ODriveIntf::GPIO_MODE_UART0,
ODriveIntf::GPIO_MODE_ANALOG_IN,
ODriveIntf::GPIO_MODE_ANALOG_IN,
ODriveIntf::GPIO_MODE_ANALOG_IN,
ODriveIntf::GPIO_MODE_DIGITAL,
ODriveIntf::GPIO_MODE_DIGITAL,
ODriveIntf::GPIO_MODE_DIGITAL,
ODriveIntf::GPIO_MODE_ENC0,
ODriveIntf::GPIO_MODE_ENC0,
ODriveIntf::GPIO_MODE_DIGITAL,
ODriveIntf::GPIO_MODE_ENC1,
ODriveIntf::GPIO_MODE_ENC1,
ODriveIntf::GPIO_MODE_DIGITAL,
ODriveIntf::GPIO_MODE_CAN0,
ODriveIntf::GPIO_MODE_CAN0,
};
bool enable_uart0 = true;
uint32_t uart0_baudrate = 115200;
bool enable_can0 = true;
bool enable_i2c0 = false;
bool enable_ascii_protocol_on_usb = true;
float max_regen_current = 0.0f;
#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 5 && HW_VERSION_VOLTAGE >= 48
float brake_resistance = 2.0f; // [ohm]
#else
float brake_resistance = 0.47f; // [ohm]
#endif
float brake_resistance = DEFAULT_BRAKE_RESISTANCE;
float dc_bus_undervoltage_trip_level = 8.0f; //<! [V] minimum voltage below which the motor stops operating
float dc_bus_overvoltage_trip_level = 1.07f * HW_VERSION_VOLTAGE; //<! [V] maximum voltage above which the motor stops operating.
//<! This protects against cases in which the power supply fails to dissipate
@@ -96,34 +114,8 @@ struct BoardConfig_t {
float dc_max_positive_current = INFINITY; // Max current [A] the power supply can source
float dc_max_negative_current = -0.000001f; // Max current [A] the power supply can sink. You most likely want a non-positive value here. Set to -INFINITY to disable.
PWMMapping_t pwm_mappings[GPIO_COUNT];
PWMMapping_t pwm_mappings[4];
PWMMapping_t analog_mappings[GPIO_COUNT];
/**
* Defines the baudrate used on the UART interface.
* Some baudrates will have a small timing error due to hardware limitations.
*
* Here's an (incomplete) list of baudrates for ODrive v3.x:
*
* Configured | Actual | Error [%]
* -------------|---------------|-----------
* 1.2 KBps | 1.2 KBps | 0
* 2.4 KBps | 2.4 KBps | 0
* 9.6 KBps | 9.6 KBps | 0
* 19.2 KBps | 19.195 KBps | 0.02
* 38.4 KBps | 38.391 KBps | 0.02
* 57.6 KBps | 57.613 KBps | 0.02
* 115.2 KBps | 115.068 KBps | 0.11
* 230.4 KBps | 230.769 KBps | 0.16
* 460.8 KBps | 461.538 KBps | 0.16
* 921.6 KBps | 913.043 KBps | 0.93
* 1.792 MBps | 1.826 MBps | 1.9
* 1.8432 MBps | 1.826 MBps | 0.93
*
* For more information refer to Section 30.3.4 and Table 142 (the column with f_PCLK = 42 MHz) in the STM datasheet:
* https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf
*/
uint32_t uart_baudrate = 115200;
};
// Forward Declarations
@@ -176,7 +168,7 @@ extern const unsigned char fw_version_unreleased_;
}
static Stm32Gpio get_gpio(size_t gpio_num) {
return (gpio_num >= 1 && gpio_num <= GPIO_COUNT) ? gpios[gpio_num - 1] : GPIO_COUNT ? gpios[0] : Stm32Gpio::none;
return (gpio_num < GPIO_COUNT) ? gpios[gpio_num] : GPIO_COUNT ? gpios[0] : Stm32Gpio::none;
}
// general system functions defined in main.cpp
@@ -244,6 +236,7 @@ public:
BoardConfig_t config_;
bool user_config_loaded_;
bool misconfigured_ = false;
uint32_t test_property_ = 0;
};
+3 -2
View File
@@ -64,9 +64,10 @@ void communication_task(void * ctx) {
start_uart_server();
start_usb_server();
if (odrv.config_.enable_i2c_instead_of_can) {
if (odrv.config_.enable_i2c0) {
start_i2c_server();
} else {
}
if (odrv.config_.enable_can0) {
odCAN->start_can_server();
}
+76 -16
View File
@@ -81,10 +81,28 @@ interfaces:
config:
c_is_class: False
attributes:
enable_uart:
# TODO: add support for arrays
gpio1_mode: {type: GpioMode, doc: Mode of GPIO1 (changes take effect after reboot), c_name: 'gpio_modes[1]'}
gpio2_mode: {type: GpioMode, doc: Mode of GPIO2 (changes take effect after reboot), c_name: 'gpio_modes[2]'}
gpio3_mode: {type: GpioMode, doc: Mode of GPIO3 (changes take effect after reboot), c_name: 'gpio_modes[3]'}
gpio4_mode: {type: GpioMode, doc: Mode of GPIO4 (changes take effect after reboot), c_name: 'gpio_modes[4]'}
gpio5_mode: {type: GpioMode, doc: Mode of GPIO5 (changes take effect after reboot), c_name: 'gpio_modes[5]'}
gpio6_mode: {type: GpioMode, doc: Mode of GPIO6 (changes take effect after reboot), c_name: 'gpio_modes[6]'}
gpio7_mode: {type: GpioMode, doc: Mode of GPIO7 (changes take effect after reboot), c_name: 'gpio_modes[7]'}
gpio8_mode: {type: GpioMode, doc: Mode of GPIO8 (changes take effect after reboot), c_name: 'gpio_modes[8]'}
gpio9_mode: {type: GpioMode, doc: Mode of GPIO9 (changes take effect after reboot), c_name: 'gpio_modes[9]'}
gpio10_mode: {type: GpioMode, doc: Mode of GPIO10 (changes take effect after reboot), c_name: 'gpio_modes[10]'}
gpio11_mode: {type: GpioMode, doc: Mode of GPIO11 (changes take effect after reboot), c_name: 'gpio_modes[11]'}
gpio12_mode: {type: GpioMode, doc: Mode of GPIO12 (changes take effect after reboot), c_name: 'gpio_modes[12]'}
gpio13_mode: {type: GpioMode, doc: Mode of GPIO13 (changes take effect after reboot), c_name: 'gpio_modes[13]'}
gpio14_mode: {type: GpioMode, doc: Mode of GPIO14 (changes take effect after reboot), c_name: 'gpio_modes[14]'}
gpio15_mode: {type: GpioMode, doc: Mode of GPIO15 (changes take effect after reboot), c_name: 'gpio_modes[15]'}
gpio16_mode: {type: GpioMode, doc: Mode of GPIO16 (changes take effect after reboot), c_name: 'gpio_modes[16]'}
enable_uart0:
type: bool
doc: 'TODO: changing this currently requires a reboot - fix this'
uart_baudrate:
uart0_baudrate:
type: uint32
doc: |
Defines the baudrate used on the UART interface.
@@ -109,9 +127,17 @@ interfaces:
For more information refer to Section 30.3.4 and Table 142 (the column with f_PCLK = 42 MHz) in the
[STM datasheet](https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf).
enable_i2c_instead_of_can:
enable_can0:
type: bool
doc: Changing this requires a reboot.
doc: |
Enables CAN. Changing this setting requires a reboot.
enable_i2c0:
type: bool
doc: |
Enables I2C. The I2C pins on ODrive v3.x are in conflict with CAN.
This setting has no effect if `enable_can0` is also true.
This setting has no effect on ODrive v3.2 or earlier.
Changing this setting requires a reboot.
enable_ascii_protocol_on_usb: bool
max_regen_current: float32
brake_resistance:
@@ -173,13 +199,26 @@ interfaces:
brief: Max current the power supply can sink.
doc: You most likely want a non-positive value here. Set to -INFINITY to disable.
gpio1_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[0]'} # TODO: disable for ODrive v3.2 and older
gpio2_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[1]'} # TODO: disable for ODrive v3.2 and older
gpio3_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[2]'} # TODO: disable for ODrive v3.2 and older
gpio4_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[3]'}
gpio3_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[2]'}
gpio4_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[3]'}
gpio1_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[0]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.}
gpio2_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[1]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.}
gpio3_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[2]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.}
gpio4_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.}
gpio3_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.}
gpio4_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[4]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.}
user_config_loaded: readonly bool
misconfigured:
type: readonly bool
doc: |
If this property is true, something is bad in the configuration. The
ODrive can still be used in this state but the user should investigate
which setting is problematic. This variable does not cover all
misconfigurations.
Possible causes:
- A GPIO was set to a mode that it doesn't support
- A GPIO was set to a mode for which the corresponding feature was
not enabled. Example: `GPIO_MODE_UART0` was used without enabling
`config.enable_uart0`.
axis0: {type: Axis, c_name: get_axis(0)}
axis1: {type: Axis, c_name: get_axis(1)}
@@ -189,7 +228,7 @@ interfaces:
functions:
test_function: {in: {delta: int32}, out: {cnt: int32}}
get_oscilloscope_val: {in: {index: uint32}, out: {val: float32}}
get_adc_voltage: {in: {gpio: uint32}, out: {voltage: float32}}
get_adc_voltage: {in: {gpio: uint32}, out: {voltage: float32}, doc: Reads the ADC voltage of the specified GPIO. The GPIO should be in `GPIO_MODE_ANALOG_IN`.}
save_configuration:
erase_configuration:
reboot:
@@ -323,7 +362,7 @@ interfaces:
enable_step_dir:
type: bool
doc: Enable step/dir input after calibration.
For M0 this has no effect if `config.enable_uart` is true.
Make sure to set the corresponding GPIO's mode to `GPIO_MODE_DIGITAL`.
step_dir_always_on:
type: bool
doc: Keep step/dir enabled while the motor is disabled.
@@ -766,7 +805,7 @@ interfaces:
mode: Mode
use_index: {type: bool, c_setter: set_use_index}
find_idx_on_lockin_only: {type: bool, c_setter: set_find_idx_on_lockin_only}
abs_spi_cs_gpio_pin: {type: uint16, c_setter: set_abs_spi_cs_gpio_pin}
abs_spi_cs_gpio_pin: {type: uint16, c_setter: set_abs_spi_cs_gpio_pin, doc: Make sure that the GPIO is in `GPIO_MODE_DIGITAL`.}
zero_count_on_find_idx: bool
cpr: int32
offset: int32
@@ -779,8 +818,12 @@ interfaces:
calib_scan_omega: float32
idx_search_unidirectional: bool
ignore_illegal_hall_state: bool
sincos_gpio_pin_sin: uint16
sincos_gpio_pin_cos: uint16
sincos_gpio_pin_sin:
type: uint16
doc: Analog sine signal of a sin/cos encoder. The corresponding GPIO must be in `GPIO_MODE_ANALOG_IN`.
sincos_gpio_pin_cos:
type: uint16
doc: Analog cosine signal of a sin/cos encoder. The corresponding GPIO must be in `GPIO_MODE_ANALOG_IN`.
functions:
set_linear_count: {in: {count: int32}}
@@ -823,7 +866,7 @@ interfaces:
config:
c_is_class: False
attributes:
gpio_num: {type: uint16, c_setter: set_gpio_num}
gpio_num: {type: uint16, c_setter: set_gpio_num, doc: Make sure the corresponding GPIO is in `GPIO_MODE_DIGITAL`.}
enabled: {type: bool, c_setter: set_enabled}
offset: float32
is_active_high: bool
@@ -832,6 +875,23 @@ interfaces:
valuetypes:
ODrive.GpioMode:
values:
Digital:
doc: |
The pin can be used for one or more of these functions:
Step, dir, enable, encoder index, hall effect encoder, SPI encoder nCS (this one is exclusive).
AnalogIn:
doc: |
The pin can be used for one or more of these functions:
Sin/cos encoders, analog input, `get_adc_voltage`.
Uart0: {doc: See `config.enable_uart0`.}
Pwm0: {doc: See `config.gpio0_pwm_mapping`.}
Can0: {doc: See `config.enable_can0`.}
I2c0: {doc: See `config.enable_i2c0`.}
Enc0: {doc: The pin is used by quadrature encoder 0.}
Enc1: {doc: The pin is used by quadrature encoder 1.}
ODrive.Can.Protocol:
values: {Simple: }
+27 -13
View File
@@ -18,24 +18,38 @@ The ODrive can be controlled over various ports and protocols. If you're comfort
## Pinout
| GPIO | primary | step/dir | other |
|-----------|-----------|---------------|-------------------------|
| GPIO1 | UART TX | Axis0 Step | Analog input, PWM input |
| GPIO2 | UART RX | Axis0 Dir | Analog input, PWM input |
| GPIO3 | | Axis1 Step (+)| Analog input, PWM input |
| GPIO4 | | Axis1 Dir (+) | Analog input, PWM input |
| GPIO5 | | | Analog input (*) |
| GPIO6 (*) | | | |
| GPIO7 (*) | | Axis1 Step (*)| |
| GPIO8 (*) | | Axis1 Dir (*) | |
| # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART0` | `GPIO_MODE_PWM0` | `GPIO_MODE_CAN0` | `GPIO_MODE_I2C0` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` |
|----|---------------|------------------------|-----------------------|-------------------|------------------|------------------|------------------|------------------|------------------|
| 0 | _not a pin_ | | | | | | | | |
| 1 | GPIO1 (+) | general purpose | analog input | **UART0.TX** | PWM0.0 | | | | |
| 2 | GPIO2 (+) | general purpose | analog input | **UART0.RX** | PWM0.1 | | | | |
| 3 | GPIO3 | general purpose | **analog input** | | PWM0.2 | | | | |
| 4 | GPIO4 | general purpose | **analog input** | | PWM0.3 | | | | |
| 5 | GPIO5 | general purpose | **analog input** (*) | | | | | | |
| 6 | GPIO6 (*) (+) | **general purpose** | | | | | | | |
| 7 | GPIO7 (*) (+) | **general purpose** | | | | | | | |
| 8 | GPIO8 (*) (+) | **general purpose** | | | | | | | |
| 9 | M0.A | general purpose | | | | | | **ENC0.A** | |
| 10 | M0.B | general purpose | | | | | | **ENC0.B** | |
| 11 | M0.Z | **general purpose** | | | | | | | |
| 12 | M1.A | general purpose | | | | | I2C.SCL | | **ENC1.A** |
| 13 | M1.B | general purpose | | | | | I2C.SDA | | **ENC1.B** |
| 14 | M1.Z | **general purpose** | | | | | | | |
| 15 | _not exposed_ | general purpose | | | | **CAN0.RX** | I2C.SCL | | |
| 16 | _not exposed_ | general purpose | | | | **CAN0.TX** | I2C.SDA | | |
(+) on ODrive v3.4 and earlier <br>
(*) ODrive v3.5 and later
(*) ODrive v3.5 and later <br>
(+) On ODrive v3.5 and later these pins have noise suppression filters. This is useful for step/dir input. <br>
Notes:
* Changes to the pin configuration only take effect after `odrv0.save_configuration()` and `odrv0.reboot()`
* Bold font marks the default configuration.
* If a GPIO is set to an unsupported mode it will be left uninitialized.
* When setting a GPIO to a special purpose mode (e.g. `GPIO_MODE_UART0`) you must also enable the corresponding feature (e.g. `<odrv>.config.enable_uart`).
* Digital mode is a general purpose mode that can be used for these functions: step, dir, enable, encoder index, hall effect encoder, SPI encoder nCS.
* You must also connect GND between ODrive and your other board.
* ODrive v3.3 and onward have 5V tolerant GPIO pins.
* ODrive v3.5 and later have some noise suppression filters on the default step/dir pins
* You can change the step/dir pins using `axis.config.<step/dir>_gpio_pin`.
### Pin function priorities
+10
View File
@@ -3,6 +3,16 @@
# To regenerate this file, nagivate to the top level of the ODrive repository and run:
# python Firmware/interface_generator_stub.py --definitions Firmware/odrive-interface.yaml --template tools/enums_template.j2 --output tools/odrive/enums.py
# ODrive.GpioMode
GPIO_MODE_DIGITAL = 0
GPIO_MODE_ANALOG_IN = 1
GPIO_MODE_UART0 = 2
GPIO_MODE_PWM0 = 3
GPIO_MODE_CAN0 = 4
GPIO_MODE_I2C0 = 5
GPIO_MODE_ENC0 = 6
GPIO_MODE_ENC1 = 7
# ODrive.Can.Protocol
PROTOCOL_SIMPLE = 0
+2 -1
View File
@@ -91,7 +91,8 @@ class TestAnalogInput():
None, #odrive.handle.config.gpio5_analog_mapping,
][analog_in_num]
odrive.unuse_gpios()
odrive.disable_mappings()
setattr(odrive.handle.config, 'gpio' + str(analog_in_num+1) + '_mode', GPIO_MODE_ANALOG_IN)
analog_mapping.endpoint = odrive.handle.axis0.controller._remote_attributes['input_pos']
analog_mapping.min = min_val
analog_mapping.max = max_val
+5 -3
View File
@@ -107,9 +107,11 @@ class TestSimpleCAN():
yield (odrive, can_interfaces, 0xfedcba, True) # extended ID
def run_test(self, odrive: ODriveComponent, canbus: CanInterfaceComponent, node_id: int, extended_id: bool, logger: Logger):
# make sure no gpio input is overwriting our values
odrive.unuse_gpios()
odrive.disable_mappings()
odrive.handle.config.gpio15_mode = GPIO_MODE_CAN0
odrive.handle.config.gpio16_mode = GPIO_MODE_CAN0
odrive.handle.config.enable_can0 = True
odrive.save_config_and_reboot()
axis = odrive.handle.axis0
axis.config.enable_watchdog = False
+12 -1
View File
@@ -185,7 +185,9 @@ class TestSinCosEncoder(TestEncoderBase):
teensy.compile_and_program(code)
if enc.handle.config.mode != ENCODER_MODE_SINCOS:
enc.parent.unuse_gpios()
enc.parent.disable_mappings()
enc.parent.handle.config.gpio3_mode = GPIO_MODE_ANALOG_IN
enc.parent.handle.config.gpio4_mode = GPIO_MODE_ANALOG_IN
enc.handle.config.mode = ENCODER_MODE_SINCOS
enc.parent.save_config_and_reboot()
else:
@@ -255,6 +257,14 @@ class TestHallEffectEncoder(TestEncoderBase):
teensy.compile_and_program(code)
if enc.handle.config.mode != ENCODER_MODE_HALL:
if enc.num:
enc.parent.handle.config.gpio9_mode = GPIO_MODE_DIGITAL
enc.parent.handle.config.gpio10_mode = GPIO_MODE_DIGITAL
enc.parent.handle.config.gpio11_mode = GPIO_MODE_DIGITAL
else:
enc.parent.handle.config.gpio12_mode = GPIO_MODE_DIGITAL
enc.parent.handle.config.gpio13_mode = GPIO_MODE_DIGITAL
enc.parent.handle.config.gpio14_mode = GPIO_MODE_DIGITAL
enc.handle.config.mode = ENCODER_MODE_HALL
enc.parent.save_config_and_reboot()
else:
@@ -450,6 +460,7 @@ class TestSpiEncoder(TestEncoderBase):
logger.debug(f'Configuring absolute encoder in mode 0x{self.mode:x}...')
enc.handle.config.mode = self.mode
setattr(enc.parent.handle.config, 'gpio' + str(odrive_ncs_gpio) + '_mode', GPIO_MODE_ANALOG_IN)
enc.handle.config.abs_spi_cs_gpio_pin = odrive_ncs_gpio
enc.handle.config.cpr = true_cpr
# Also put the other encoder into SPI mode to make it more interesting
+2 -1
View File
@@ -61,7 +61,7 @@ class TestPwmInput():
teensy.compile_and_program(code)
logger.debug("Set up PWM input...")
odrive.unuse_gpios()
odrive.disable_mappings()
pwm_mapping = [
odrive.handle.config.gpio1_pwm_mapping,
@@ -70,6 +70,7 @@ class TestPwmInput():
odrive.handle.config.gpio4_pwm_mapping
][odrive_gpio_num - 1]
setattr(odrive.handle.config, 'gpio' + str(odrive_gpio_num) + '_mode', GPIO_MODE_PWM0)
pwm_mapping.endpoint = odrive.handle.axis0.controller._remote_attributes['input_pos']
pwm_mapping.min = min_val
pwm_mapping.max = max_val
+4 -3
View File
@@ -37,15 +37,16 @@ class TestStepDir():
yield (odrive.axes[1], 7, gpio_conns[6], 8, gpio_conns[7])
def run_test(self, axis: ODriveAxisComponent, step_gpio_num: int, step_gpio: LinuxGpioComponent, dir_gpio_num: int, dir_gpio: LinuxGpioComponent, logger: Logger):
def run_test(self, axis: ODriveAxisComponent, step_gpio_num: int, step_gpio: LinuxGpioComponent, dir_gpio_num: int, dir_gpio: LinuxGpioComponent, logger: Logger):
step_gpio.config(output=True)
step_gpio.write(False)
dir_gpio.config(output=True)
dir_gpio.write(True)
axis.parent.erase_config_and_reboot()
if axis.num == 0:
axis.parent.handle.config.enable_uart = False
setattr(axis.parent.handle.config, 'gpio' + str(step_gpio_num) + '_mode', GPIO_MODE_DIGITAL)
setattr(axis.parent.handle.config, 'gpio' + str(dir_gpio_num) + '_mode', GPIO_MODE_DIGITAL)
axis.parent.save_config_and_reboot()
axis.handle.config.enable_step_dir = True
axis.handle.config.step_dir_always_on = True # needed for testing
axis.handle.config.step_gpio_pin = step_gpio_num
+2 -5
View File
@@ -247,11 +247,8 @@ class ODriveComponent(Component):
for axis_idx, axis_ctx in enumerate(self.axes):
axis_ctx.handle = self.handle.__dict__['axis{}'.format(axis_idx)]
def unuse_gpios(self):
self.handle.config.enable_uart = False
self.handle.axis0.config.enable_step_dir = False
self.handle.axis1.config.enable_step_dir = False
self.handle.config.gpio1_pwm_mapping.endpoint = None
def disable_mappings(self):
self.handle.config.gpio1_pwm_mapping.endpoint = None # here
self.handle.config.gpio2_pwm_mapping.endpoint = None
self.handle.config.gpio3_pwm_mapping.endpoint = None
self.handle.config.gpio4_pwm_mapping.endpoint = None
+14 -9
View File
@@ -48,7 +48,9 @@ class TestUartAscii():
# GPIOs might be in use by something other than UART and some components
# might be configured so that they would fail in the later test.
odrive.erase_config_and_reboot()
odrive.handle.config.enable_uart = True
odrive.handle.config.enable_uart0 = True
odrive.handle.config.gpio1_mode = GPIO_MODE_UART0
odrive.handle.config.gpio2_mode = GPIO_MODE_UART0
with port.open(115200) as ser:
# reset port to known state
@@ -161,10 +163,11 @@ class TestUartBaudrate():
yield (odrive, ports)
def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: Logger):
odrive.handle.axis0.config.enable_step_dir = False
odrive.handle.config.enable_uart = True
odrive.handle.config.enable_uart0 = True
odrive.handle.config.gpio1_mode = GPIO_MODE_UART0
odrive.handle.config.gpio2_mode = GPIO_MODE_UART0
odrive.handle.config.uart_baudrate = 9600
odrive.handle.config.uart0_baudrate = 9600
odrive.save_config_and_reboot()
# Control test: talk to the ODrive with the wrong baudrate
@@ -184,7 +187,7 @@ class TestUartBaudrate():
response = float(ser.readline().strip())
test_assert_eq(response, odrive.handle.vbus_voltage, accuracy=0.1)
odrive.handle.config.uart_baudrate = 115200
odrive.handle.config.uart0_baudrate = 115200
odrive.save_config_and_reboot()
@@ -202,8 +205,9 @@ class TestUartBurnIn():
yield (odrive, ports)
def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: Logger):
odrive.handle.axis0.config.enable_step_dir = False
odrive.handle.config.enable_uart = True
odrive.handle.config.enable_uart0 = True
odrive.handle.config.gpio1_mode = GPIO_MODE_UART0
odrive.handle.config.gpio2_mode = GPIO_MODE_UART0
with port.open(115200) as ser:
with open('/dev/random', 'rb') as rand:
@@ -258,8 +262,9 @@ class TestUartNoise():
noise_enable.write(False)
time.sleep(0.1)
odrive.handle.axis0.config.enable_step_dir = False
odrive.handle.config.enable_uart = True
odrive.handle.config.enable_uart0 = True
odrive.handle.config.gpio1_mode = GPIO_MODE_UART0
odrive.handle.config.gpio2_mode = GPIO_MODE_UART0
with port.open(115200) as ser:
# reset port to known state