系列文章目录
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
需要注意的是目前我拿到的是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 < sizeof(ports_to_sample) / sizeof(ports_to_sample[0]); ++i) {
port_samples_[i] = ports_to_sample[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冲突了,以后用的话 就得改改了。
总结
提示:这里对文章进行总结: