Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

Энкодер и STM32

Пн окт 04, 2021 20:46:12

Тема изъезженная, но все же даже поиск по форуму не дал ответа...до этого все откладывал работу с энкодером, а теперь вот пришлось, давно с ними мучаюсь - самая главная проблема - дребезг, а если еще и частотник рядом фонит, вообще беда. Видел поведение даже на готовых приборах, когда например на дельтовских контролерах подаешь на один из входов "джиттер" (дребезг, меандр), а он берет и считает, а по идее интерфейс должен работать с самозащитой и не пропускать такое. По идее читая документацию на STM32 даже табличку такую видел, есть сигнал на входе измеряемом - считаем вверх при переднем фронте, и вниз при заднем, соответственно меняем направление счета только если между фронтами сменился сигнал на втором. Но вот то ли я не пойму, как настроить таймер, то ли чего, но вот считает он у меня по одному входу свободно вниз и все, а по идее должен "дрожжать" около одного значения, я ведь не с настоящим энкодером экспериментирую, один из входов меняю, второй нет. По идее режим, когда счет и по фронтам и по спадам да на обоих входах счетных не имеет защиты, хотя опять же читаю RM вижу волшебные графики типа Figure 93 и не особо верю, если конечно там не реализована защитная логика проверки прихода фронта с одного и только тогда зачитывается фронт на другом, а если спад пришел без смены на втором, значит не прибавляем а вычитаем, короче лишняя логика, которую должно что-то обрабатывать, а это сильно усложняет устройство. и сомневаюсь, что это реализовано. Еще не почему таймер загружает в CNT регистр ARR, по идее таймер без интерфейса энкодера считает с нуля и до ARR, а тут какая-то лажа, считает по одному входу и только вниз, хотя вроде как настройки UD/DOWN. Пробовал два варианта, может кто что подскажет?
вот код инициализации, сам код опроса прост как тапок - читаем TIM2->CNT:
Код:
         /* TIM3 clock enable */
         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

         // Заполняем настройки по дефолту
         TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
         // Clock prescaler 72M/1 Запускаем таймер на тактовой частоте 72 MHz
         TIM_TimeBaseStructure.TIM_Prescaler= 1;
         // Частоту дополнительно не делим TIM_CKD_DIV1=0
         TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
         // Считаем вверх и вниз
         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Down | TIM_CounterMode_Up;

         TIM_TimeBaseStructure.TIM_Period = 65536;
         // Инициализируем TIM3
         TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

         // Clear overflow interrupt flag
         TIM_ClearFlag(TIM3, TIM_FLAG_Update);
         TIM_ClearFlag(TIM3, TIM_FLAG_CC1);
         // Disable interrupt for Update Event
         TIM_ITConfig(TIM3,TIM_IT_Update,DISABLE);
         TIM_ITConfig(TIM3,TIM_IT_CC1,DISABLE);

         /* Считать будем все переходы лог. уровня с обоих каналов */
         //TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_BothEdge, TIM_ICPolarity_BothEdge);
         TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI1, TIM_ICPolarity_BothEdge, TIM_ICPolarity_BothEdge);
         TIM_Cmd(TIM3, ENABLE);


Интересует оба режима: двойной счет, когда как пишут в RM счет идет по одному сигналу и по фронтам и по спадам, и четверной, когда по обоим сигналам и по фронтам и спадам, интересует, как прошел опыт подачи меандра на один вход без изменения второго на обоих режимах (был ли счет или просто дребезг в районе одного значения, в оба ли направления счет).

Re: Энкодер и STM32

Пн окт 04, 2021 20:58:36

auric, если у вас энкодер это элемент интерфейса с человеком, проще обойти все эти проблемы и сделать софтверно. При быстродействии STM32 ресурсов это практически не займёт. Если у вас быстродействующий датчик, тогда да, оправданно заморачиваться с аппаратной поддержкой. Добиться устойчивой работы не просто. Что касается вашего кода, то по моему в TIM_TimeBaseStructure.TIM_Period надо писать 65535, а не 65536.

Re: Энкодер и STM32

Пн окт 04, 2021 21:08:09

Работал и с оптическими, и с дешевыми "реостатоподобными" энкодерами. Да, дребезг бывал, но не в такой же степени, чтобы влиять на счет более-менее весомо! Подумаешь, дрожит младший бит!

Re: Энкодер и STM32

Пн окт 04, 2021 21:13:46

auric, если у вас энкодер это элемент интерфейса с человеком, проще обойти все эти проблемы и сделать софтверно. При быстродействии STM32 ресурсов это практически не займёт. Если у вас быстродействующий датчик, тогда да, оправданно заморачиваться с аппаратной поддержкой. Добиться устойчивой работы не просто. Что касается вашего кода, то по моему в TIM_TimeBaseStructure.TIM_Period надо писать 65535, а не 65536.

Быстродействие нужно, а по поводу значения это я сам пока код вставлял ручками поправил, там была переменная, но суть одна писал туда 20000, с 20000 и начинался счет, писал 10, в CNT это же читал.

Добавлено after 3 minutes 55 seconds:
Работал и с оптическими, и с дешевыми "реостатоподобными" энкодерами. Да, дребезг бывал, но не в такой же степени, чтобы влиять на счет более-менее весомо! Подумаешь, дрожит младший бит!

Дребезг в чистом виде не приводит к ошибке счета, но если интерфейс неправильный - реально счет идет например по одному каналу, хотя второй не меняется, у меня по крайней мере так и с STM счас и как уже писал выше на готовых ПЛК от фирмы Delta видел которые SV2.

Re: Энкодер и STM32

Пн окт 04, 2021 21:19:21

auric, вот мой код, счёт в обоих направлениях:
Код:
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // тактирование врубаем

   TIM_DeInit(TIM3); // в состояние "по умолчанию"
   TIM_TimeBaseInitStructure.TIM_Period = 65535; // до скольки крутим
   TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
   TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Down;
   TIM_TimeBaseInitStructure.TIM_Prescaler = 0x0000; // Предделитель учитывается как TIM_Prescaler+1, поэтому отнимаем 1
   TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

   TIM_PrescalerConfig(TIM3, 1, TIM_PSCReloadMode_Immediate);
   TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI1, TIM_ICPolarity_Falling, TIM_ICPolarity_Rising);

   TIM_Cmd(TIM3, ENABLE);

   a = TIM3->CNT; // читаем значение энкодера

Re: Энкодер и STM32

Вт окт 05, 2021 01:19:47

если интерфейс неправильный

, то энкодер поврежден → нужно заменить на нормальный.

Re: Энкодер и STM32

Вт окт 05, 2021 10:23:06

auric, вот мой код, счёт в обоих направлениях:
Код:
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // тактирование врубаем

   TIM_DeInit(TIM3); // в состояние "по умолчанию"
   TIM_TimeBaseInitStructure.TIM_Period = 65535; // до скольки крутим
   TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
   TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Down;
   TIM_TimeBaseInitStructure.TIM_Prescaler = 0x0000; // Предделитель учитывается как TIM_Prescaler+1, поэтому отнимаем 1
   TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

   TIM_PrescalerConfig(TIM3, 1, TIM_PSCReloadMode_Immediate);
   TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI1, TIM_ICPolarity_Falling, TIM_ICPolarity_Rising);

   TIM_Cmd(TIM3, ENABLE);

   a = TIM3->CNT; // читаем значение энкодера


Код попробовал - все примерно то же - загрузка значения ARR, счет правда теперь идет в обоих направлениях (обратно если на второй вход дать "единицу"), но если дать на второй вход 0 а на первый подавать импульсы - идет счет - а не должен, это обычный джиттер, я даже проверил на других ПЛК, отрабатывается правильно, а тут нет.

Добавлено after 2 minutes 36 seconds:
если интерфейс неправильный

, то энкодер поврежден → нужно заменить на нормальный.

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

Re: Энкодер и STM32

Вт окт 05, 2021 12:29:39

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

Значит, у тебя таймер неправильно настроен. Т.к. в такой ситуации он должен просто на ±1 дрожать!
Я в этих ваших калокубах не понимаю. Поэтому в код и не смотрю. Ну и да: кто сказал, что в самом калокубе нет ошибок?
Привожу код из сниппета:
Код:
static inline void timers_setup(){
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
    /* (1) Configure TI1FP1 on TI1 (CC1S = 01)
           configure TI1FP2 on TI2 (CC2S = 01) */
    /* (2) Configure TI1FP1 and TI1FP2 non inverted (CC1P = CC2P = 0, reset value) */
    /* (3) Configure both inputs are active on both rising and falling edges
          (SMS = 011), set external trigger filter to f_DTS/8, N=6 (ETF=1000) */
    /* (4) Enable the counter by writing CEN=1 in the TIMx_CR1 register. */
    TIM3->CCMR1 = TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC2S_0; /* (1)*/
    /* (2) */
    TIM3->SMCR =  TIM_SMCR_ETF_3 | TIM_SMCR_SMS_0 | TIM_SMCR_SMS_1; /* (3) */
    // enable update interrupt
    TIM3->DIER = TIM_DIER_UIE;
    // set ARR to 79 - generate interrupt each 80 counts (one revolution)
    TIM3->ARR = 79;
    // enable timer
    TIM3->CR1 = TIM_CR1_CEN; /* (4) */
    NVIC_EnableIRQ(TIM3_IRQn);
}

Фильтрация настроена?

Re: Энкодер и STM32

Вт окт 05, 2021 13:25:54

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

Значит, у тебя таймер неправильно настроен. Т.к. в такой ситуации он должен просто на ±1 дрожать!
Я в этих ваших калокубах не понимаю. Поэтому в код и не смотрю. Ну и да: кто сказал, что в самом калокубе нет ошибок?
Привожу код из сниппета:
Код:
static inline void timers_setup(){
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
    /* (1) Configure TI1FP1 on TI1 (CC1S = 01)
           configure TI1FP2 on TI2 (CC2S = 01) */
    /* (2) Configure TI1FP1 and TI1FP2 non inverted (CC1P = CC2P = 0, reset value) */
    /* (3) Configure both inputs are active on both rising and falling edges
          (SMS = 011), set external trigger filter to f_DTS/8, N=6 (ETF=1000) */
    /* (4) Enable the counter by writing CEN=1 in the TIMx_CR1 register. */
    TIM3->CCMR1 = TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC2S_0; /* (1)*/
    /* (2) */
    TIM3->SMCR =  TIM_SMCR_ETF_3 | TIM_SMCR_SMS_0 | TIM_SMCR_SMS_1; /* (3) */
    // enable update interrupt
    TIM3->DIER = TIM_DIER_UIE;
    // set ARR to 79 - generate interrupt each 80 counts (one revolution)
    TIM3->ARR = 79;
    // enable timer
    TIM3->CR1 = TIM_CR1_CEN; /* (4) */
    NVIC_EnableIRQ(TIM3_IRQn);
}

Фильтрация настроена?

Ну код не из CUBE, но не суть, в принципе да я могу и без CMSIS сделать, главное цель - если поведение было другим, надо думать, что не так здесь. А входы какую настройку имеют? Если память не изменяет AF альтернативная функция только для выходов, для входов можно либо float либо подтяжку к питанию?

Добавлено after 28 minutes 58 seconds:

не...не работает как надо, пробовал
Код:
TIM3->SMCR =  TIM_SMCR_ETF_3 | TIM_SMCR_SMS_0 | TIM_SMCR_SMS_1; /* (3) */

и
Код:
TIM3->SMCR =  TIM_SMCR_ETF_3 | TIM_SMCR_SMS_0; /* (3) */

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

Re: Энкодер и STM32

Вт окт 05, 2021 13:35:55

А входы какую настройку имеют?

Я ж в самом своем первом сообщении давал ссылку на код. Вот:
Код:
GPIOA->MODER = GPIO_MODER_MODER4_O | GPIO_MODER_MODER6_AF | GPIO_MODER_MODER7_AF;
GPIOA->AFR[0] = (GPIOA->AFR[0] &~ (GPIO_AFRL_AFRL6 | GPIO_AFRL_AFRL7)) \
                | (1 << (6 * 4)) | (1 << (7 * 4));

Просто альтернативные функции, никаких подтяжек там не нужно.

А что там на осциллограмме творится, хотелось бы посмотреть? Можно попробовать еще грубей фильтр сделать.

Re: Энкодер и STM32

Вт окт 05, 2021 13:59:13

А входы какую настройку имеют?

Я ж в самом своем первом сообщении давал ссылку на код. Вот:
Код:
GPIOA->MODER = GPIO_MODER_MODER4_O | GPIO_MODER_MODER6_AF | GPIO_MODER_MODER7_AF;
GPIOA->AFR[0] = (GPIOA->AFR[0] &~ (GPIO_AFRL_AFRL6 | GPIO_AFRL_AFRL7)) \
                | (1 << (6 * 4)) | (1 << (7 * 4));

Просто альтернативные функции, никаких подтяжек там не нужно.

А что там на осциллограмме творится, хотелось бы посмотреть? Можно попробовать еще грубей фильтр сделать.

нашел проблему, не настраивал фильтры совсем, а видимо это не формировало задние фронты (спады).
Код:
          TIM_ICInitTypeDef TIM_ICInitStructure;
          //Enter the content in the structure by default
          TIM_ICStructInit(&TIM_ICInitStructure);
          //filter value
          TIM_ICInitStructure.TIM_ICFilter = 6;
          //Initialize the specified parameters in TIM_ICInitStructure TIM3
          TIM_ICInit(TIM3, &TIM_ICInitStructure);


а входы все-таки так:
Код:
     /* Configure pin PA6, PA7*/
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;//Каналы 1 и 2 таймера TIM3 - на вход, подтянуть к питанию
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;         //вход с подтяжкой к питанию
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOA, &GPIO_InitStructure);

Re: Энкодер и STM32

Вт окт 05, 2021 14:55:58

нашел проблему

Не нашел, потому что это эквивалентно:
Код:
TIM3->SMCR |=  TIM_SMCR_ETF_3

Естественно, это не работало, если ты оставил все остальное по-кальски, как и было!

В общем, советую не тыкать все, что попало, наугад, а почитать таки документацию!

Re: Энкодер и STM32

Вт окт 05, 2021 21:24:38

нашел проблему

Не нашел, потому что это эквивалентно:
Код:
TIM3->SMCR |=  TIM_SMCR_ETF_3

Естественно, это не работало, если ты оставил все остальное по-кальски, как и было!

В общем, советую не тыкать все, что попало, наугад, а почитать таки документацию!

Возможно, но именно добавив указанные строки к коду система заработала как надо. Главное заработала.
У меня теперь новый вопрос созрел, допустим я хочу сделать прерывание, но для отслеживания мне недостаточно 16 бит. Допустим таймер генерирует прерывание по достижению arr, мы инкрементируюм в прерывании дополнительный счетчик. Но как быть с обратным счетом? Когда счетчик досчитает до нуля (и продолжит дальше) , использовать capture&compare? Ведь мне не нужно новое прерывание по update, я не могу определить каким направлением оно вызвано. Короче кто делал счетчики с большей разрядностью, как поступали идеологически?
Последний раз редактировалось auric Вт окт 05, 2021 21:36:58, всего редактировалось 1 раз.

Re: Энкодер и STM32

Вт окт 05, 2021 21:34:58

Либо связать с этим таймером другой, либо в прерываниях вести счет.

Re: Энкодер и STM32

Вт окт 05, 2021 21:40:38

Либо связать с этим таймером другой, либо в прерываниях вести счет.

Со вторым таймером не очень хочется тк свободных мало, все заняты. С прерываниями волнует обратный счет, при переходе 0->ARR нужно тоже прерывание и со стопроцентной информацией о том, что это именно из нуля, а не (ARR-1) ->ARR. Пока затрудняюсь, что использовать.

Re: Энкодер и STM32

Ср окт 13, 2021 21:06:26

на камне STM32F103 таймеров всего 4, а мне нужно будет по два таймера на канал, а еще ШИМ делать, так что вопрос актуальный, не пойму, как можно исхитриться, чтобы определить, в какую сторону было переполнение не каскадируя таймеры.
Я при переполнении вверх добавляю к каскадному регистру 65536, а при переполнении вниз вычитаю тоже 65536.
Бит DIR не помогает (TIM3->CR1 & TIM_CR1_DIR), пока в прерывание падает, может сигнал поменяться и бит тоже.
Пробовал контролировать на считывание вспомогательного значения, чтобы дважды не добавлял - помогает от двойного счета, но полностью нет (ну типа (cnt_dir != 1) если вперед или (cnt_dir != 2) если назад, сам их проставляю в соответствии с проверками и пока не пройдусь в обычной программе в диапазоне (TIM3->CNT > 16383)&&(TIM3->CNT < 49150) и не сброшу в 0, второй раз аналогичную операцию не проведу). Но это не дает возможность само направление определить.
Еще контролирую в прерывании значение счетчика, если (TIM3->CNT > 32767), получается переполнение назад и наоборот (TIM3->CNT < 32767) то вперед, если считать, что не успеет энкодер насчитать больше половины диапазона за прерывание. Но получается пока в прерывании кручусь, пока дойду до сравнения импульс может обратный прилететь, а значение счетчика поменяется например с 65535 на 0, хотя изначально прерывание было в минус, система не поймет и сделает счет каскада в плюс. Уже и старое значение запоминал и контролировал переход - все равно ошибку могу рукой набить, когда даю джиттер в районе переполнения вокруг нуля.
Код:
         if ((CNT_TIM3_old==1)&&(cnt_dir != 1)&&(TIM3->CNT > 32767))  //CNT_TIM3_old 0 база, 1 - близкое к нулю, 65534 - близкое к переполнению вверх
            CNT_Val_Reg = (s32)(CNT_Val_Reg - 65536), cnt_dir = 1;      //0 прошли середину диапазона CNT, 1 назад, 2 вперед
         if ((CNT_TIM3_old==65534)&&(cnt_dir != 2)&&(TIM3->CNT < 32767))
            CNT_Val_Reg = (s32)(CNT_Val_Reg + 65536), cnt_dir = 2;      //0 прошли середину диапазона CNT, 1 назад, 2 вперед

Re: Энкодер и STM32

Ср окт 13, 2021 21:37:16

Пока затрудняюсь, что использовать.

Флаг TIM_CR1_DIR покажет, в какую сторону было переполнение. Так что, считать несложно.
Вот если в районе полного оборота будут туда-сюда крутить энкодер, да, получится жопа… Надо подумать, как это решать. Единственная надежда, что человеку такое сделать физически невозможно: слишком шустро пришлось бы туда-сюда дергать энкодер.
Я в качестве пробы вот такое наваял. А сейчас мне нужно постепенно разработать управлялку тремя шаговиками с оптическими энкодерами. Других дел полно, поэтому эта работа будет очень медленно продвигаться, иначе уже к выходным я мог бы пример показать.

Re: Энкодер и STM32

Чт окт 14, 2021 06:28:43

Хммм

Re: Энкодер и STM32

Чт окт 14, 2021 09:25:50

Dimon456, вот именно, что такой способ работает лишь если ARR=0xffff. В этом случае количество оборотов придется считать, деля текущее положение на количество отсчетов на оборот. Т.е. в случае STM32F0 это - дополнительные тормоза. Это хорошо, если повезет, и будет кратное двойке. Но обычно у энкодеров 20, 50, 100, 200, 1000 и т.п. число отсчетов на один оборот, т.е. для таймера это 80, 200, 400, 800, 4000 и т.п. отсчетов.

Re: Энкодер и STM32

Чт окт 14, 2021 09:59:44

Eddy_Em писал(а):т.е. для таймера это 80, 200, 400, 800, 4000 и т.п. отсчетов.
И что?
Ответить