ODrive学习笔记四——编码器流
系列文章目录
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 系列文章目录
- 前言
- 一、Encoder 初始化
- 二、Encoder采集
- 三、额外的一些 :温度传感器
- 总结
前言
需要注意的是目前我拿到的是ODrive3.6的硬件版本。再往上就不开源了,很难。ODrvie S1 PRO等版本的资料也拿不到。不巧的是目前电机使用的编码器是485版本的,没办法那就只能先研究一下编码器了。
一、Encoder 初始化
首先在rtos_main中找到encoder初始化的入口。
for(auto& axis: axes){ axis.encoder_.setup(); }
void Encoder::setup() { HAL_TIM_Encoder_Start(timer_, TIM_CHANNEL_ALL); set_idx_subscribe(); mode_ = config_.mode; spi_task_.config = { .Mode = SPI_MODE_MASTER, .Direction = SPI_DIRECTION_2LINES, .DataSize = SPI_DATASIZE_16BIT, .CLKPolarity = (mode_ == MODE_SPI_ABS_AEAT || mode_ == MODE_SPI_ABS_MA732) ? SPI_POLARITY_HIGH : SPI_POLARITY_LOW, .CLKPhase = SPI_PHASE_2EDGE, .NSS = SPI_NSS_SOFT, .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16, .FirstBit = SPI_FIRSTBIT_MSB, .TIMode = SPI_TIMODE_DISABLE, .CRCCalculation = SPI_CRCCALCULATION_DISABLE, .CRCPolynomial = 10, }; if (mode_ == MODE_SPI_ABS_MA732) { abs_spi_dma_tx_[0] = 0x0000; } if(mode_ & MODE_FLAG_ABS){ abs_spi_cs_pin_init(); if (axis_->controller_.config_.anticogging.pre_calibrated) { axis_->controller_.anticogging_valid_ = true; } } }
setup接口里面,更多的是底层驱动的初始化。像是TIMER,GPIO中断,SPI
二、Encoder采集
void TIM8_UP_TIM13_IRQHandler(void) { COUNT_IRQ(TIM8_UP_TIM13_IRQn); // Entry into this function happens at 21-23 clock cycles after the timer // update event. __HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE); // If the corresponding timer is counting up, we just sampled in SVM vector 0, i.e. real current // If we are counting down, we just sampled in SVM vector 7, with zero current bool counting_down = TIM8->CR1 & TIM_CR1_DIR; bool timer_update_missed = (counting_down_ == counting_down); if (timer_update_missed) { motors[0].disarm_with_error(Motor::ERROR_TIMER_UPDATE_MISSED); motors[1].disarm_with_error(Motor::ERROR_TIMER_UPDATE_MISSED); return; } counting_down_ = counting_down; timestamp_ += TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1); if (!counting_down) { TaskTimer::enabled = odrv.task_timers_armed_; // Run sampling handlers and kick off control tasks when TIM8 is // counting up. odrv.sampling_cb(); NVIC->STIR = ControlLoop_IRQn; } else { // Tentatively reset all PWM outputs to 50% duty cycles. If the control // loop handler finishes in time then these values will be overridden // before they go into effect. TIM1->CCR1 = TIM1->CCR2 = TIM1->CCR3 = TIM8->CCR1 = TIM8->CCR2 = TIM8->CCR3 = TIM_1_8_PERIOD_CLOCKS / 2; } }
是利用定时器中断 回调了 odrv.sampling_cb();
void Encoder::sample_now() { switch (mode_) { case MODE_INCREMENTAL: { tim_cnt_sample_ = (int16_t)timer_->Instance->CNT; } break; case MODE_HALL: { // do nothing: samples already captured in general GPIO capture } break; case MODE_SINCOS: { sincos_sample_s_ = get_adc_relative_voltage(get_gpio(config_.sincos_gpio_pin_sin)) - 0.5f; sincos_sample_c_ = get_adc_relative_voltage(get_gpio(config_.sincos_gpio_pin_cos)) - 0.5f; } break; case MODE_SPI_ABS_AMS: case MODE_SPI_ABS_CUI: case MODE_SPI_ABS_AEAT: case MODE_SPI_ABS_RLS: case MODE_SPI_ABS_MA732: { abs_spi_start_transaction(); // Do nothing } break; default: { set_error(ERROR_UNSUPPORTED_ENCODER_MODE); } break; } // Sample all GPIO digital input data registers, used for HALL sensors for example. for (size_t i = 0; i IDR; } }
这里面根据编码器格式的不同,进行了分别的处理。如果想添加485的编码器,就需要仿照SPI类型的编码器,在这里添加对应的读取接口。
先看下SPI是如何进行读取的。
bool Encoder::abs_spi_start_transaction() { if (mode_ & MODE_FLAG_ABS){ if (Stm32SpiArbiter::acquire_task(&spi_task_)) { spi_task_.ncs_gpio = abs_spi_cs_gpio_; spi_task_.tx_buf = (uint8_t*)abs_spi_dma_tx_; spi_task_.rx_buf = (uint8_t*)abs_spi_dma_rx_; spi_task_.length = 1; spi_task_.on_complete = [](void* ctx, bool success) { ((Encoder*)ctx)->abs_spi_cb(success); }; spi_task_.on_complete_ctx = this; spi_task_.next = nullptr; spi_arbiter_->transfer_async(&spi_task_); } else { return false; } } return true; }
通过接口启动了一个SPI的DMA传输。发送的数据存放在abs_spi_dma_tx_ 收到的数据存储在abs_spi_dma_rx_。
没注意看目前支持的几个SPI传感器的通信协议,但是没有找到对abs_spi_dma_tx特殊的赋值,除了
if (mode_ == MODE_SPI_ABS_MA732) { abs_spi_dma_tx_[0] = 0x0000; }
可能其他编码器直接读就可以嘛。读取完成之后,通过abs_spi_cb回调,将获得编码器值反馈到rawVal变量,部分编码器需要判断一下方向,之后传给pos_abs_。
void Encoder::abs_spi_cb(bool success) { uint16_t pos; if (!success) { goto done; } switch (mode_) { case MODE_SPI_ABS_AMS: { uint16_t rawVal = abs_spi_dma_rx_[0]; // check if parity is correct (even) and error flag clear if (ams_parity(rawVal) || ((rawVal >> 14) & 1)) { goto done; } pos = rawVal & 0x3fff; } break; case MODE_SPI_ABS_CUI: { uint16_t rawVal = abs_spi_dma_rx_[0]; // check if parity is correct if (cui_parity(rawVal)) { goto done; } pos = rawVal & 0x3fff; } break; case MODE_SPI_ABS_RLS: { uint16_t rawVal = abs_spi_dma_rx_[0]; pos = (rawVal >> 2) & 0x3fff; } break; case MODE_SPI_ABS_MA732: { uint16_t rawVal = abs_spi_dma_rx_[0]; pos = (rawVal >> 2) & 0x3fff; } break; default: { set_error(ERROR_UNSUPPORTED_ENCODER_MODE); goto done; } break; } pos_abs_ = pos; abs_spi_pos_updated_ = true; if (config_.pre_calibrated) { is_ready_ = true; } done: Stm32SpiArbiter::release_task(&spi_task_); }
最后在update中更新编码器值,并给系统其他方面调用。
bool Encoder::update() { // update internal encoder state. int32_t delta_enc = 0; int32_t pos_abs_latched = pos_abs_; //LATCH
过程中还有一些校准的操作需要再研究一下。
三、额外的一些 :温度传感器
进入入口之后前几条是温度传感器的刷新操作
// @brief Set up the gate drivers bool Motor::setup() { fet_thermistor_.update(); motor_thermistor_.update();
然后先看一下这两个传感器所使用的I/O口。
在board.cpp中
OnboardThermistorCurrentLimiter fet_thermistors[AXIS_COUNT] = { { 15, // adc_channel &fet_thermistor_poly_coeffs[0], // coefficients fet_thermistor_num_coeffs // num_coeffs }, { #if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 4, // adc_channel #else 1, // adc_channel #endif &fet_thermistor_poly_coeffs[0], // coefficients fet_thermistor_num_coeffs // num_coeffs } };
板载的传感器用的是ADC 15通道和4通道。另外还有1路板外的可接入的温度传感器。
class OffboardThermistorCurrentLimiter : public ThermistorCurrentLimiter, public ODriveIntf::OffboardThermistorCurrentLimiterIntf { public: static const size_t num_coeffs_ = 4; struct Config_t { float thermistor_poly_coeffs[num_coeffs_]; #if HW_VERSION_MAJOR == 3 uint16_t gpio_pin = 4; #elif HW_VERSION_MAJOR == 4 uint16_t gpio_pin = 2; #endif float temp_limit_lower = 100; float temp_limit_upper = 120; bool enabled = false; // custom setters OffboardThermistorCurrentLimiter* parent; void set_gpio_pin(uint16_t value) { gpio_pin = value; parent->decode_pin(); } }; virtual ~OffboardThermistorCurrentLimiter() = default; OffboardThermistorCurrentLimiter(); Config_t config_; bool apply_config(); private: void decode_pin(); };
这次初始化是从GPIO数组里取得,看了下是PA3。这样就跟UART2冲突了,以后用的话 就得改改了。
总结
提示:这里对文章进行总结: