Разве вы не можете легко использовать «квадратурные» инкрементные поворотные энкодеры с увеличением количества «остановок» в 2 или 4 раза?
Я купил 12-ступенчатый «квадратурный» инкрементный поворотный энкодер в качестве устройства ввода для проекта, над которым работаю.
Когда я писал код для отслеживания «тактов» вращения, я заставил его реагировать на любое изменение контакта «A». В результате у меня получилось 24 шага, а не 12. Они выглядят равномерно расположенными и по-прежнему правильно указывают вращение по часовой стрелке или против часовой стрелки.
Мне пришло в голову, что если я отслеживаю изменения состояния как на контактах «A», так и на «B», пока я меняю логику для идентификации вращения по часовой стрелке и против часовой стрелки, я мог бы получить 48 шагов отсчета от тот же кодер.
Таким образом, не проще ли получить в 2 или 4 раза больше шагов от квадратурного поворотного энкодера, просто интерпретируя сигналы по-другому?
@Duncan C, 👍0
Обсуждение2 ответа
Может быть, неправильно описан ваш кодер? У меня есть несколько маленьких поворотных энкодеров со сквозными отверстиями, популярных в комплектах модулей Arduino, и у них есть 12 фиксаторов для одного оборота, но на самом деле это энкодеры с 48 позициями на оборот. Когда я их использую, мне приходится делить количество тактов на 4, чтобы в моем коде один физический «клик» считался одним тактом.
Для некоторых из них довольно просто открыть их и удалить фиксатор и, таким образом, получить плавно вращающийся энкодер с более высоким разрешением. К сожалению, другие, например те, которые есть у меня, трудно открыть, не разрушив их, и, поскольку они физически защелкиваются на фиксаторах, их невозможно использовать с правильным разрешением.
Отмеченный вами метод, при котором вы отслеживаете изменение высокого/низкого значения в любом направлении обоих выходов, является обычным способом считывания квадратурных энкодеров.
Я купил 12-ступенчатый энкодер, но без фиксаторов. Судя по тому, что я читал, нормой является отслеживание изменений нарастающего фронта только на одном из каналов, что дало бы мне 12 шагов. Поэтому ваш ответ меня смущает., @Duncan C
У меня энкодер Bourns PEC11-4020K-S0012 (номер Jameco 2277703) с переключателем., @Duncan C
Чтение таблицы данных показывает, что это описано как «12 импульсов на поворот на 360 градусов». Очевидно, они означают 12 импульсов (вверх, затем вниз, затем обратно вверх; или вниз, затем вверх, затем обратно вниз) на одном канале, а не 12 изменений уровня. Я полагаю, что «нормально» зависит от многих вещей. Многие сценарии Arduino используют менее полезный процесс поиска нарастающего фронта на одном канале и последующей проверки другого канала для определения «направления» вращения. Но это тратит столько информации!, @jose can u c
В данном случае «12 импульсов» означают 12 нарастающих импульсов в одном канале. Я хочу сказать, что в зависимости от того, как вы пишете свой код, вы можете рассматривать его как 12, 24 или 48 шагов плюс информацию о направлении, и вам не придется ни на что делить количество тактов., @Duncan C
Ответ на мой вопрос — да.
Обычно в качестве импульса учитывается только задний фронт одного из выходов инкрементального энкодера. Это дает вам указанное количество тактов для такого типа кодировщика — 12 тактов за ход в случае моего кодировщика.
Если вместо этого вы напишете свой код, реагирующий на изменения фронта нарастания или спада на одном из контактов, вы получите 24 такта за оборот.
Если вы вместо этого напишете свой код, реагирующий на изменения фронта нарастания или спада на любом выводе, вы получите 48 тактов за оборот.
Код, который я придумал, выглядит следующим образом:
void setup() {
//Используйте этот код настройки для 12 тиков/оборот
attachInterrupt(digitalPinToInterrupt(rotaryPinA), encode, FALLING);
/*
Use this setup code for 24 ticks/revolution
(trigger interrupt on rising and falling edge)
attachInterrupt(digitalPinToInterrupt(rotaryPinA), encode, CHANGE);
*/
/*
Use this setup code for 48 ticks/revolution
(trigger interrupts on rising and falling edge of either pin)
attachInterrupt(digitalPinToInterrupt(rotaryPinA), encode, CHANGE);
attachInterrupt(digitalPinToInterrupt(rotaryPinB), encodePinB, CHANGE);
*/
//Остальная часть вашего установочного кода...
}
И процедуры обслуживания прерываний (ISR):
void encode() {
rotaryValueChanged = true;
//Прочитайте PinA & PinB (цифровые контакты 2 и 3) с использованием регистра порта PINE, 4-й и 5-й бит.
//Быстрый эквивалент pinAState = digitalRead(rotaryPinA) == LOW
bool pinAState = (PINE & (1 << 4)) == 0;
bool pinBState = (PINE & (1 << 5)) == 0;
rotaryValue += (pinAState == pinBState) ? -1 : 1;
}
void encodePinB() {
rotaryValueChanged = true;
//Прочитайте PinA & PinB (цифровые контакты 2 и 3) с использованием регистра порта PINE, 4-й и 5-й бит.
bool pinAState = (PINE & (1 << 4)) == 0;
bool pinBState = (PINE & (1 << 5)) == 0;
rotaryValue += (pinAState != pinBState) ? -1 : 1;
}
Если вы настроите только одно прерывание на RotaryPinA, второй метод прерывания никогда не будет вызван.
Приведенный выше код предназначен для Arduino Mega 2560 и других плат, которые имеют одинаковый контакт для регистрации сопоставлений. Он использует прерывания, чтобы не пропустить изменения состояния контактов, и для скорости считывает состояния контактов, используя регистры порта.
Обратите внимание, что если вы добавляете только прерывание по заднему фронту, вы можете пропустить чтение вывода A (поскольку, когда состояние вывода падает, он по определению будет иметь логический 0 при каждом вызове ISR).
>Чтобы это работало хорошо, мне пришлось построить схему RC-фильтра, описанную в спецификации моего кодера:
https://www.bourns.com/docs/Product-Datasheets/PEC12R.pdf
Диаграмма выглядит следующим образом:
И мне пришлось добавить триггеры Шмитта между выходами фильтра, показанными выше, и входными линиями Arduino.
Без триггера Шмитта возникает дрожание, когда напряжение от фильтра не переключается между ВЫСОКИМ и НИЗКИМ значениями достаточно быстро. Триггеры Шмитта исключают ложные показания напряжений «мертвой зоны».
С этими изменениями показания энкодера стали довольно хорошими, хотя по какой-то причине он по-прежнему измеряет ± 1 такт или около того за оборот.
Мне бы сейчас хотелось купить версию кодировщика с фиксаторами, чтобы он вращался с дискретными щелчками.
Мне кажется несколько удивительным, что вам пришлось добавить триггеры Шмитта, поскольку Arduino уже имеет встроенные триггеры Шмитта на всех своих цифровых входах., @Edgar Bonet
- Сброс Arduino Uno в коде
- Как вызвать функции C из скетча ардуино?
- Считывание нескольких поворотных энкодеров
- Использование поворотных энкодеров с прерываниями смены контактов
- Выводы прерываний Arduino Mega 2560 и отображение портов с помощью поворотного энкодера
- Проблема с Software Serial: нет ответа
- Варианты программирования, отличные от C++
- Взаимодействие с датчиком SSI?
будет ли это работать правильно при запуске и остановке? казалось бы, направление могло бы быть неоднозначным, если бы изменился только один штифт..., @dandavis
Нет, с помощью квадратурного кодирования вы можете определить направление на основе изменения состояния одного контакта и состояния другого контакта., @Duncan C
пожалуйста, уточните, что означает «получил 24 шага», @jsotola
Да вы правы. Вы можете отслеживать 48 различных позиций за один оборот., @Gerben
@Gerben, можешь ли ты отправить ответ (с немного более подробной информацией), чтобы я мог его принять? в моем наивном коде, использующем digitalRead двух контактов, отсутствуют позиции, если я не вращаю кодировщик медленно. Сегодня утром я нашел в Интернете код, который использует прерывания по выводам. Я так понимаю, это правильный путь?, @Duncan C
Мой «ответ» такой же, как у Хосе, только более по существу. Избегайте использования digitalRead, поскольку он довольно медленный. Использование прерываний было бы правильным решением. В зависимости от количества дребезга контактов вам может потребоваться добавить некоторое оборудование для устранения дребезга (RC-фильтр), чтобы ваше программное обеспечение не было перегружено прерываниями и не пропустило некоторые изменения., @Gerben
Хорошо, я перепишу свой код, используя прерывания и регистры портов. Прошло несколько лет с тех пор, как я занимался чтением выводов с помощью регистров портов, так что придется немного покопаться. Я также никогда раньше не делал обработчики прерываний на Arduino. Я так понимаю, ISR должен быть глобальной функцией? (в остальной части моего проекта я использую объекты C++ для таких вещей, как дескрипторы датчиков, поэтому предпочел бы, чтобы это был метод экземпляра, но я думаю, что нет.), @Duncan C