Тема изъезженная, но все же даже поиск по форуму не дал ответа...до этого все откладывал работу с энкодером, а теперь вот пришлось, давно с ними мучаюсь - самая главная проблема - дребезг, а если еще и частотник рядом фонит, вообще беда. Видел поведение даже на готовых приборах, когда например на дельтовских контролерах подаешь на один из входов "джиттер" (дребезг, меандр), а он берет и считает, а по идее интерфейс должен работать с самозащитой и не пропускать такое. По идее читая документацию на STM32 даже табличку такую видел, есть сигнал на входе измеряемом - считаем вверх при переднем фронте, и вниз при заднем, соответственно меняем направление счета только если между фронтами сменился сигнал на втором. Но вот то ли я не пойму, как настроить таймер, то ли чего, но вот считает он у меня по одному входу свободно вниз и все, а по идее должен "дрожжать" около одного значения, я ведь не с настоящим энкодером экспериментирую, один из входов меняю, второй нет. По идее режим, когда счет и по фронтам и по спадам да на обоих входах счетных не имеет защиты, хотя опять же читаю RM вижу волшебные графики типа Figure 93 и не особо верю, если конечно там не реализована защитная логика проверки прихода фронта с одного и только тогда зачитывается фронт на другом, а если спад пришел без смены на втором, значит не прибавляем а вычитаем, короче лишняя логика, которую должно что-то обрабатывать, а это сильно усложняет устройство. и сомневаюсь, что это реализовано. Еще не почему таймер загружает в CNT регистр ARR, по идее таймер без интерфейса энкодера считает с нуля и до ARR, а тут какая-то лажа, считает по одному входу и только вниз, хотя вроде как настройки UD/DOWN. Пробовал два варианта, может кто что подскажет? вот код инициализации, сам код опроса прост как тапок - читаем TIM2->CNT:
// 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 счет идет по одному сигналу и по фронтам и по спадам, и четверной, когда по обоим сигналам и по фронтам и спадам, интересует, как прошел опыт подачи меандра на один вход без изменения второго на обоих режимах (был ли счет или просто дребезг в районе одного значения, в оба ли направления счет).
auric, если у вас энкодер это элемент интерфейса с человеком, проще обойти все эти проблемы и сделать софтверно. При быстродействии STM32 ресурсов это практически не займёт. Если у вас быстродействующий датчик, тогда да, оправданно заморачиваться с аппаратной поддержкой. Добиться устойчивой работы не просто. Что касается вашего кода, то по моему в TIM_TimeBaseStructure.TIM_Period надо писать 65535, а не 65536.
Работал и с оптическими, и с дешевыми "реостатоподобными" энкодерами. Да, дребезг бывал, но не в такой же степени, чтобы влиять на счет более-менее весомо! Подумаешь, дрожит младший бит!
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
auric, если у вас энкодер это элемент интерфейса с человеком, проще обойти все эти проблемы и сделать софтверно. При быстродействии STM32 ресурсов это практически не займёт. Если у вас быстродействующий датчик, тогда да, оправданно заморачиваться с аппаратной поддержкой. Добиться устойчивой работы не просто. Что касается вашего кода, то по моему в TIM_TimeBaseStructure.TIM_Period надо писать 65535, а не 65536.
Быстродействие нужно, а по поводу значения это я сам пока код вставлял ручками поправил, там была переменная, но суть одна писал туда 20000, с 20000 и начинался счет, писал 10, в CNT это же читал.
Работал и с оптическими, и с дешевыми "реостатоподобными" энкодерами. Да, дребезг бывал, но не в такой же степени, чтобы влиять на счет более-менее весомо! Подумаешь, дрожит младший бит!
Дребезг в чистом виде не приводит к ошибке счета, но если интерфейс неправильный - реально счет идет например по одному каналу, хотя второй не меняется, у меня по крайней мере так и с STM счас и как уже писал выше на готовых ПЛК от фирмы Delta видел которые SV2.
Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей.
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
, то энкодер поврежден → нужно заменить на нормальный.
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Код попробовал - все примерно то же - загрузка значения ARR, счет правда теперь идет в обоих направлениях (обратно если на второй вход дать "единицу"), но если дать на второй вход 0 а на первый подавать импульсы - идет счет - а не должен, это обычный джиттер, я даже проверил на других ПЛК, отрабатывается правильно, а тут нет.
, то энкодер поврежден → нужно заменить на нормальный.
Слушай, ну не тот случай - не надо мне про неисправные энкодеры - нет такого устройства, которое избавит от дребезга контактов, тк у оптопар гистерезис хоть и есть , но небольшой, никто не гарантирует, что помеха не превысит этот гистререзис, да и вообще чисто физически бывает, что перемещение будет тоже с дребезгом, и будет все равно оптопара давать на одном входе меандр, а другой оставит без изменений, и тут на тебе - контроллер считает импульсы - а должен один добавить и тут же по спаду убавить. Что я по сути и имитирую - так сказать стресс-тест, который законченные устройства проходят (правда тоже не все), а реализованный мной интерфейс нет, вот и пытаюсь понять это интерфейс такой или все-таки настройки не те.
и тут на тебе - контроллер считает импульсы - а должен один добавить и тут же по спаду убавить.
Значит, у тебя таймер неправильно настроен. Т.к. в такой ситуации он должен просто на ±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); }
Фильтрация настроена?
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
и тут на тебе - контроллер считает импульсы - а должен один добавить и тут же по спаду убавить.
Значит, у тебя таймер неправильно настроен. Т.к. в такой ситуации он должен просто на ±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 либо подтяжку к питанию?
В первом случае считает первым входом без второго вверх, вторым без первого вниз. Во втором случае считает только первым входом в зависимости от состояния второго, но тоже без проблем насчитывает себе лишнего без изменения состояния второго.
Просто альтернативные функции, никаких подтяжек там не нужно.
А что там на осциллограмме творится, хотелось бы посмотреть? Можно попробовать еще грубей фильтр сделать.
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Просто альтернативные функции, никаких подтяжек там не нужно.
А что там на осциллограмме творится, хотелось бы посмотреть? Можно попробовать еще грубей фильтр сделать.
нашел проблему, не настраивал фильтры совсем, а видимо это не формировало задние фронты (спады).
Код:
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);
Естественно, это не работало, если ты оставил все остальное по-кальски, как и было!
В общем, советую не тыкать все, что попало, наугад, а почитать таки документацию!
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Естественно, это не работало, если ты оставил все остальное по-кальски, как и было!
В общем, советую не тыкать все, что попало, наугад, а почитать таки документацию!
Возможно, но именно добавив указанные строки к коду система заработала как надо. Главное заработала. У меня теперь новый вопрос созрел, допустим я хочу сделать прерывание, но для отслеживания мне недостаточно 16 бит. Допустим таймер генерирует прерывание по достижению arr, мы инкрементируюм в прерывании дополнительный счетчик. Но как быть с обратным счетом? Когда счетчик досчитает до нуля (и продолжит дальше) , использовать capture&compare? Ведь мне не нужно новое прерывание по update, я не могу определить каким направлением оно вызвано. Короче кто делал счетчики с большей разрядностью, как поступали идеологически?
Последний раз редактировалось auric Вт окт 05, 2021 21:36:58, всего редактировалось 1 раз.
Либо связать с этим таймером другой, либо в прерываниях вести счет.
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Либо связать с этим таймером другой, либо в прерываниях вести счет.
Со вторым таймером не очень хочется тк свободных мало, все заняты. С прерываниями волнует обратный счет, при переходе 0->ARR нужно тоже прерывание и со стопроцентной информацией о том, что это именно из нуля, а не (ARR-1) ->ARR. Пока затрудняюсь, что использовать.
на камне 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 вперед
Флаг TIM_CR1_DIR покажет, в какую сторону было переполнение. Так что, считать несложно. Вот если в районе полного оборота будут туда-сюда крутить энкодер, да, получится жопа… Надо подумать, как это решать. Единственная надежда, что человеку такое сделать физически невозможно: слишком шустро пришлось бы туда-сюда дергать энкодер. Я в качестве пробы вот такое наваял. А сейчас мне нужно постепенно разработать управлялку тремя шаговиками с оптическими энкодерами. Других дел полно, поэтому эта работа будет очень медленно продвигаться, иначе уже к выходным я мог бы пример показать.
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
// Посмотреть относительное перемещение по сравнению с прошлым вызовом int16_t enc_Get(void) { // Для хранения предыдущего значения счетчика static uint16_t CNT_last = 0; // Текущее значение, чтоб только один раз было запрошено uint16_t CNT_now = TIM3->CNT; // Посчитаем перемещение int16_t CNT_diff = (int16_t)(CNT_now - CNT_last); // Сохраним текущее значение как предыдущее CNT_last = CNT_now; // Вернем перемещение return CNT_diff; }
Код:
temp = (int16_t )enc_Get();
if (temp < 0) { x--; }
if (temp > 0) { x++; }
Этому коду всё равно, было переполнение регистра-счётчика или же его не было, разница будет верной. Правда, такое будет только если TIMx_ARR = 0xFFFF. В ином случае придётся учитывать программно, что переход через ноль идёт в другое крайнее значение, но это всего одна единичка, один тик таймера, можно считать просто сбоем.
Dimon456, вот именно, что такой способ работает лишь если ARR=0xffff. В этом случае количество оборотов придется считать, деля текущее положение на количество отсчетов на оборот. Т.е. в случае STM32F0 это - дополнительные тормоза. Это хорошо, если повезет, и будет кратное двойке. Но обычно у энкодеров 20, 50, 100, 200, 1000 и т.п. число отсчетов на один оборот, т.е. для таймера это 80, 200, 400, 800, 4000 и т.п. отсчетов.
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 29
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения