2016-09-12 4 views
1

В настоящее время я использую STM32F4 с платой Nucleo-144 STM32F429ZI. Я хочу использовать этот микроконтроллер для оценки положения вращающегося энкодера через интерфейс квадратурного энкодера. Рассматривая документацию, это делается с помощью таймеров. У меня есть выходы кодировщика A/B, подключенные к PA6 и PC7 на микро, но я заметил, что подсчеты, похоже, дрейфуют.STM32F4 Число энкодеров меняется, когда оно не должно

Во время отладки я заметил, что если я отсоединяю один из выходов энкодера к микроконтроллеру, и я перемещаю двигатель, счетчики все еще увеличиваются/уменьшаются, хотя только одна из линий энкодера подключается. Поскольку я рассчитываю на края TI1 и TI2, это не должно происходить. Если я правильно читаю приведенную ниже диаграмму, так как одна из моих линий удерживается высоко, используя внутреннее подтягивание, тактовые импульсы на другом входе должны идти вверх/вниз/вверх/вниз и действительно просто циклироваться между двумя разными значениями. Однако, если я вращаю энкодер, подсчеты продолжают увеличиваться или уменьшаться в зависимости от направления.

Почему изменяется счетчик энкодера только при подключении только одного входа энкодера? У меня также есть трассировка области, чтобы доказать, что активен только один счетчик, а также код.

EDIT: Я также попытался изменить полярность от BOTH EDGE до RISING EDGE, без каких-либо ощутимых преимуществ.

enter image description here

enter image description here

#include "stm32f4xx_hal.h" 
#include "encoder_test.h" 

GPIO_InitTypeDef GPIO_InitStruct; 
TIM_HandleTypeDef Timer_InitStruct; 
TIM_Encoder_InitTypeDef Encoder_InitStruct; 

void EncoderTest_Init() 
{ 
    __HAL_RCC_GPIOA_CLK_ENABLE(); 
    __HAL_RCC_GPIOC_CLK_ENABLE(); 
    __HAL_RCC_TIM3_CLK_ENABLE(); 

    /**TIM3 GPIO Configuration 
    PA6  ------> TIM3_CH1 
    PC7  ------> TIM3_CH2 
    */ 

    GPIO_InitStruct.Pin = GPIO_PIN_6; 
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 
    GPIO_InitStruct.Pull = GPIO_PULLUP; 
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; 
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3; 
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); 

    GPIO_InitStruct.Pin = GPIO_PIN_7; 
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 
    GPIO_InitStruct.Pull = GPIO_PULLUP; 
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; 
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3; 
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); 

    Timer_InitStruct.Instance = TIM3; 
    Timer_InitStruct.Init.Period = 0xFFFF; 
    Timer_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP; 
    Timer_InitStruct.Init.Prescaler = 1; 
    Timer_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; 

    Encoder_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12; 
    Encoder_InitStruct.IC1Filter = 0x00; 
    Encoder_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE; 
    Encoder_InitStruct.IC1Prescaler = TIM_ICPSC_DIV1; 
    Encoder_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI; 
    Encoder_InitStruct.IC2Filter = 0x00; 
    Encoder_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE; 
    Encoder_InitStruct.IC2Prescaler = TIM_ICPSC_DIV1; 
    Encoder_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI; 

    if (HAL_TIM_Encoder_Init(&Timer_InitStruct, &Encoder_InitStruct) != HAL_OK) 
    { 
     while (1); 
    } 

    if (HAL_TIM_Encoder_Start_IT(&Timer_InitStruct, TIM_CHANNEL_1) != HAL_OK) 
    { 
     while (1); 
    } 
} 


void TIM3_IRQHandler() 
{ 
    HAL_TIM_IRQHandler(&Timer_InitStruct); 
} 
+0

Похоже, это инкрементный датчик. Если вам нужно выяснить угол, лучше всего подходит абсолютный поворотный кодер? Вы все еще можете оценить позицию из исходной позиции (если она известна), но я думаю, что в какой-то момент вы будете пропускать прерывания и потерять счет – Emilien

ответ

0

При дальнейшем исследовании, представляется, что проблема связана с делителем. Предсекатор не работает в режиме кодирования, когда вы предоставляете четные значения. Поскольку precaler - введенное значение + 1, используя STM32F4 HAL, введенный предварительный делитель должен быть четным.

Я нашел подтверждение, что я не единственный человек с этой проблемой на этом forum post. На этой должности обсуждается, что пределители могут несовместимы с режимом кодирования, но это еще не подтверждено. Я отправил электронное письмо ST, чтобы разобраться. Безопасно вводить значение предварительного делителя 0, если оно не поддерживается.

Вот рабочий код ниже:

#include "stm32f4xx_hal.h" 
#include "encoder_test.h" 

GPIO_InitTypeDef GPIO_InitStruct; 

TIM_HandleTypeDef Timer3_InitStruct; 
TIM_Encoder_InitTypeDef EncoderTim3_InitStruct; 

void EncoderTest_Init_Tim3() 
{ 
    __HAL_RCC_GPIOA_CLK_ENABLE(); 
    __HAL_RCC_GPIOC_CLK_ENABLE(); 
    __HAL_RCC_TIM3_CLK_ENABLE(); 

    /**TIM3 GPIO Configuration 
    PA6  ------> TIM3_CH1 
    PC7  ------> TIM3_CH2 
    */ 

    GPIO_InitStruct.Pin = GPIO_PIN_6; 
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 
    GPIO_InitStruct.Pull = GPIO_PULLDOWN; 
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; 
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3; 
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); 

    GPIO_InitStruct.Pin = GPIO_PIN_7; 
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 
    GPIO_InitStruct.Pull = GPIO_PULLDOWN; 
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; 
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3; 
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); 

    Timer3_InitStruct.Instance = TIM3; 
    Timer3_InitStruct.Init.Period = 0xFFFF; 
    Timer3_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP; 
    Timer3_InitStruct.Init.Prescaler = 10; 
    Timer3_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; 

    EncoderTim3_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12; 
    EncoderTim3_InitStruct.IC1Filter = 0x00; 
    EncoderTim3_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_RISING; 
    EncoderTim3_InitStruct.IC1Prescaler = TIM_ICPSC_DIV4; 
    EncoderTim3_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI; 
    EncoderTim3_InitStruct.IC2Filter = 0x00; 
    EncoderTim3_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_RISING; 
    EncoderTim3_InitStruct.IC2Prescaler = TIM_ICPSC_DIV4; 
    EncoderTim3_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI; 

    if (HAL_TIM_Encoder_Init(&Timer3_InitStruct, &EncoderTim3_InitStruct) != HAL_OK) 
    { 
     while (1); 
    } 

    if (HAL_TIM_Encoder_Start_IT(&Timer3_InitStruct, TIM_CHANNEL_1) != HAL_OK) 
    { 
     while (1); 
    } 
} 



void TIM3_IRQHandler() 
{ 
    HAL_TIM_IRQHandler(&Timer3_InitStruct); 
} 

РЕДАКТИРОВАТЬ: После разговора с технической поддержки ST, интерфейс датчика не был предназначен для использования со значением предварительным делителем, четным или нечетным. Я добавил их ответ ниже, но даже с использованием значения предварительного делителя, которое, кажется, работает, кажется возможным, что кодер считает дрейф с течением времени.

Другим решением является использование предикаторов, но вместо этого увеличить 16-битное значение в 32-битное пространство, используя предложенный подход here. Я перепечатал подход здесь в случае, если ссылка идет мертв:


От пользователя goosen.kobus.001 на 11/19/2013 на форуме С.Т.:

В моем опыте, используя прерывание переполнения в масштабе что кодер не является надежным, особенно если у вас есть кодировщики с высоким разрешением: время от времени происходит изменение знака датчика в тот момент, когда вы вводите прерывание, заставляя систему увеличивать верхнее слово, когда оно должно быть уменьшено и т. д. Это особенно верно, если кодировщик должен быть остановлен на 0, как сервомотор, которому приказано перейти в положение кодера 0.

Лучший подход, который я нашел, - это сделать это вручную. это моя процедура:

  1. Убедитесь, что контур управления, который считывает значение датчика выполняется часто, (то есть, что если ваш датчик вращается на полной скорости, значение датчика до сих пор читают по крайней мере в 10-20 раз между перетоков. Для моего серводвигателя приложения интервал 1 мс цикл был достаточным.

  2. следить за последним значением считывания датчика.

  3. делят тока и последнее значение датчика в квадрантах (наиболее значимые 2 бита) т.е. pos_now & = 0xC000; pos_last & = 0xC000;

  4. проверьте, чтобы увидеть, если кодер переместился из квадранта 0 в квадранте 3 или 3 до 0 в последнем шаге:

    4,1, если (pos_now == 0 & & pos_last == 0xc000) upper_word ++;

    4.2 if (pos_now == 0xC000 & & pos_last == 0) upper_word--;

вот почему я говорю, что цикл чтения энкодера должен выполняться часто; вы должны быть уверены, что значение читается достаточно часто, что невозможно перейти от квадранта 0-> 1-> 2-> 3 между чтениями. Также следует поставить эту логику в другое прерывание таймера, которое работает на частоте 10 кГц. таким образом у вас есть значение энкодера, которое всегда актуально.


ST РЕПЛИКА:

Привет,

Я получил обратную связь от конструкции и архитектора таймеров. Интерфейс энкодера был разработан для работы без предварительного делителя, чтобы не понижать разрешение кодера.

Как вы заметили, они подтвердили, что он не может работать даже с предделителем, но только с нечетными. У нас есть под-счетчик для предварительного делителя, который является однонаправленным, поэтому не влияет на направление счетчика и увеличивается на каждом фронте таймера таймера (без предварительного делителя). Направление нарастания нарастающего фронта синхронизирующего таймера (без предделителя) обновляется, но счетчик увеличивается только тогда, когда под-счетчик предделителя достигает запрограммированного значения и соответствует значению в бите направления.

Таким образом, в одном случае поведение такое же, как без предварительного делителя, поскольку счетчик обновляется в другом направлении (каждый нечетный номер тактовых циклов), но в другом случае направление всегда одно и то же, счетчик обновлен, и интерфейс энкодера работает неправильно.

Таким образом, вы можете использовать предделитель, но с нечетным значением.

Рекомендуемый прецедент без предварительного делителя.

С наилучшими пожеланиями

ST MCU Tech Support