Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: MEGA+энкодер
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Страницы: 1, 2
676038
Цитата(wired @ Jun 17 2008, 17:06) *
взял за основу, прерьівание по спаду
volume - вьівожу на дисплейчик
сделал так:

Код
interrupt [EXT_INT0] void ext_int0_isr(void)
{
GICR = 0x00;  
delay_ms(3);  
if (!PIND.2)    
{  
if ((PIND.1==0) && (volume <255))  volume++;
if ((PIND.1==1) && (volume > 0)) volume--;                        
}  
GICR = 0x40;
}


получил интересную картину, на один щелчок енкодера проходит 2 значения
т.е. на дисплейчике видньі только четньіе или нечетньіе числа
что я не так делаю?
или советуете обрабатьівать в основном цикле как у Леонид Иванович


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

Обычно есть проблема с недостаточной чувствительностью (пропуски щелчков), а здесь же наоборот - и это хорошо. Значит надо просто посмотреть, как настроено прерывание - по переднему фронту, по заднему или изменению состояния. Если стоит измение состояния - то задать по фронту. Ну а если же настроено по фронту, то "real_volume = volume >> 1;" должно помочь.
wired
Цитата(676038 @ Jun 17 2008, 16:45) *
Я считаю, что обрабатывать энкодер в основном цикле - пустая трата ресурсов процессора, ведь обычно энкодер - это устройство ввода команд пользователя, и прибор обычно находится в ожидании таких команд. (Услилитель играет музыку 2 часа в день, а громкость мы выставляем за 5 секунд...)

Обычно есть проблема с недостаточной чувствительностью (пропуски щелчков), а здесь же наоборот - и это хорошо. Значит надо просто посмотреть, как настроено прерывание - по переднему фронту, по заднему или изменению состояния. Если стоит измение состояния - то задать по фронту. Ну а если же настроено по фронту, то "real_volume = volume >> 1;" должно помочь.

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

обработка результата работьі енкодера у меня в основном цикле
если изменился volume то...
отключаем прерьівания глобально
пишем в индикатор,
пишем в регулятор громкости
пишем в память EEPROM
включаем прерьівания....
едем дельше по основному циклу
Палыч
Цитата(wired @ Jun 17 2008, 14:06) *
взял за основу, прерьівание по спаду
volume - вьівожу на дисплейчик
.......
получил интересную картину, на один щелчок енкодера проходит 2 значения
т.е. на дисплейчике видньі только четньіе или нечетньіе числа
что я не так делаю?
или советуете обрабатьівать в основном цикле как у Леонид Иванович
Достался мне как-то "в наследство" проект, в котором обработывался энкодер... "Дядя" его обработку сделал - как и Вы: прерывание по одному выходу энкодера; направление вращения - анализ состояния другого выхода. Получалось - примерно тоже, что и у Вас: то изменение значения на 1 за щелчок, то на 2... Пришёл к следующему заключению: выход энкодера, заведённый на прерывание - он тоже, конечно, "дребезжит", поэтому после изменения этого выхода -> взводится соответствующий флаг -> происходит прерывание -> флаг сбрасывается, но "дребезг" приводит к повторной установке флага во время обрабоки прерывания -> после выхода из прерывания имеем ещё одно прерывание на тот же щелчок энкодера. Иногда повторного прерывания не происходит, тогда - приращение на 1. В результате - переделал обработку энкодера - получил, что-то аналогичное обработке от Леонида Ивановича. Рекомендую и Вам сделать что-то такое же.
wired
Цитата(Палыч @ Jun 17 2008, 17:51) *
Достался мне как-то "в наследство" проект, в котором обработывался энкодер... "Дядя" его обработку сделал - как и Вы: прерывание по одному выходу энкодера; направление вращения - анализ состояния другого выхода. Получалось - примерно тоже, что и у Вас: то изменение значения на 1 за щелчок, то на 2... Пришёл к следующему заключению: выход энкодера, заведённый на прерывание - он тоже, конечно, "дребезжит", поэтому после изменения этого выхода -> взводится соответствующий флаг -> происходит прерывание -> флаг сбрасывается, но "дребезг" приводит к повторной установке флага во время обрабоки прерывания -> после выхода из прерывания имеем ещё одно прерывание на тот же щелчок энкодера. Иногда повторного прерывания не происходит, тогда - приращение на 1. В результате - переделал обработку энкодера - получил, что-то аналогичное обработке от Леонида Ивановича. Рекомендую и Вам сделать что-то такое же.

но на время обработки я прерьівание то запрещаю... жду... странно.
собсно ничего не мешает устроить обработчик в основном цикле
676038
Цитата(wired @ Jun 17 2008, 21:01) *
но на время обработки я прерьівание то запрещаю... жду... странно.
собсно ничего не мешает устроить обработчик в основном цикле


Добавь в конце обработчика прерывания строчку типа:
Код
PCIFR|=(1<<PCIF1); //сбрасываем флаг прерывания, если оно произошло во время обработки. Это такая защита от дребезга.


А в обработчике прерывания запрещать прерывания не надо, они и так запрещены, и разрешаются после выхода из прерывания автоматически (компилятором), хотя про Codevision я не уверен.
Палыч
Цитата(wired @ Jun 17 2008, 18:01) *
но на время обработки я прерьівание то запрещаю... жду...
Чего их (прерывания) запрещать? Они при входе в процедуру прерывания и так - запрещены. Дело в том, что при прерывании сбрасывается флаг, который это прерывание порадил, но поскольку уровень сигнала некоторое время скачет ("дребезг"), то за время выполнения процедуры прерывания флаг взводится повторно! После того как будет произведен выход из прерывания - оно (прерывание) повторится. Как вариант - очистка флага перед выходом из прерывания.
676038
Цитата(676038 @ Jun 17 2008, 21:09) *
Добавь в конце обработчика прерывания строчку типа:
Код
PCIFR|=(1<<PCIF1); //сбрасываем флаг прерывания, если оно произошло во время обработки. Это такая защита от дребезга.


А в обработчике прерывания запрещать прерывания не надо, они и так запрещены, и разрешаются после выхода из прерывания автоматически (компилятором), хотя про Codevision я не уверен.


Может стоит добавить, сейчас обрабатываю энкодер так:
Код
//PCINT8-14 interrupt implementation
#pragma vector=PCINT1_vect
__interrupt void handler_pcint1(void)
{
static unsigned char flag=0xFF; //переменная для хранения предыдущего значения порта

unsigned char tmp; //переменная для хранения текущего значения порта

tmp = PINC & 0x0F; //запоминаем состояние порта

switch (tmp ^ flag) //сравниваем с предыдущим и выполняем действие если изменился соответствующий бит
  {
  case 0x01:
      if (((tmp >> 1) & 0x01) == (tmp & 0x01))  Execute(0x11); //если поворот первого энкодера вправо
          else Execute(0x10); //иначе это поворот первого энкодера влево
      break;//выход
  case 0x02:
      if (((tmp << 1) & 0x02) == (tmp & 0x02))  Execute(0x10); //если поворот первого энкодера влево
          else Execute(0x11); //иначе поворот первого энкодера вправо
      break;//выход
  case 0x04:
      if (((tmp >> 1) & 0x04) == (tmp & 0x04))  Execute(0x21); //если поворот второго энкодера вправо
          else Execute(0x20); //иначе поворот второго энкодера вправо
      break;//выход
//  case 0x08: !!! второй энкодер обрбатываем при изменении только одной линии, уменьшая чувствительность вдвое...
  }

flag = tmp; //сохраняем значение для следующего опроса

PCIFR|=(1<<PCIF1); //сбрасываем флаг прерывания если оно произошло во время обработки. Это такая защита от дребезга.

}
Палыч
Цитата(676038 @ Jun 17 2008, 18:12) *
Может стоит добавить, сейчас обрабатываю энкодер так:
Прерывание на что настроено: изменение, или фронт/спад? Второй сигнал - он тоже дребезжит... В предыдущем варианте, вроде, было ожидание окончания дребезга (delay_ms(3); ). Кстати, Вы уверены, что дребезг закончится за 3 мс?
676038
В своем последнем варианте - по изменению состояния, используется PIN-Change Interrupt на все четыре пина, куда подключены два энкодера.

Просто надо проанализировать логику работы и решить для себя, какое событие считать информационным, а какое дребезгом. Из моего опыта - все что срабатывает в то время, пока идет обработка обработчика - это дребезг. Вот его и сбрасываем заканчивая обработчик. Конечно, в этом варианте есть завязка на время нахождения в обработчике прерывания (время подавления дребезга), но у меня это оказалось не критичным.
wired
Цитата(Палыч @ Jun 17 2008, 18:09) *
Чего их (прерывания) запрещать? Они при входе в процедуру прерывания и так - запрещены. Дело в том, что при прерывании сбрасывается флаг, который это прерывание порадил, но поскольку уровень сигнала некоторое время скачет ("дребезг"), то за время выполнения процедуры прерывания флаг взводится повторно! После того как будет произведен выход из прерывания - оно (прерывание) повторится. Как вариант - очистка флага перед выходом из прерывания.

УПС... smile.gif вот про єто я не знал, что прерьівания во время входа в обработчик запрещеньі.
а про флаг спасибо, буду сбрасьівать.



Цитата(676038 @ Jun 17 2008, 18:12) *
Может стоит добавить, сейчас обрабатываю энкодер так:
Код
      if (((tmp >> 1) & 0x01) == (tmp & 0x01))  Execute(0x11); //если поворот первого энкодера


я правильно понял? в Execute(0x11) тьі вьізьіваешь внешний обработчик?
Maik-vs
Цитата(676038 @ Jun 17 2008, 19:48) *
Просто надо проанализировать логику работы и решить для себя, какое событие считать информационным, а какое дребезгом.

Из моего опыта - все что срабатывает в то время, пока идет обработка обработчика - это дребезг. Вот его и сбрасываем заканчивая обработчик.

+100 первому высказыванию.

О втором: странный критерий. Сколько времени длится прерывание, устанавливающее флаг? Будем задержки совать в прерывание? Если перейти к пункту 1 smile.gif то станет понятно, что для энкодера информационное событие - это фронт одного сигнала, регистрирующий новое состояние другого. Всё. Хоть 100 прерываний на один дребезг (если конечно дребезги не пересекаются во времени - но это уже негодный энкодер).

Любителям задержек для подавления дребезга. Подключите "резиновую" кнопку к осциллографу, понажимайте, посмотрите. Затянутые фронты, дребезг чуть ли не в секунду длиной - как здрасьте.
wired
Цитата(Maik-vs @ Jun 18 2008, 12:41) *
О втором: странный критерий. Сколько времени длится прерывание, устанавливающее флаг? Будем задержки совать в прерывание? Если перейти к пункту 1 smile.gif то станет понятно, что для энкодера информационное событие - это фронт одного сигнала, регистрирующий новое состояние другого. Всё. Хоть 100 прерываний на один дребезг (если конечно дребезги не пересекаются во времени - но это уже негодный энкодер).

какое новое состояние другого если вращение идет в одну сторону?
если прерьівание по изменению первого то второй свое значение не меняет, иначе - никакого нового состояния, кроме как вращения в противоположную сторону.

или анализировать по двум прерьіваниям...? тогда без ввода задержек может и рполучится... НО внешних прерьіваний всего 2 а мне еще хочется пульт прикошачить, ему тоже кстати надо внешнее прерьівание
Палыч
Цитата(676038 @ Jun 17 2008, 18:48) *
Просто надо проанализировать логику работы и решить для себя, какое событие считать информационным, а какое дребезгом.
Итак, как можно построить обработчик энкодера?
1. Можно повесить один из выходов энкодера на прерывание (например, по фронту); второй выход - для определения направления. Каждое прерывание - приращение (или декримент, в зависимости от направления) значения. Недостаток: тяжело бороться с дребезгом (причем, при этом методе нужно бороться с дребезгом того выхода энкодера, который заведен на прерывание). Сброс флага прерывания в конце процедуры обработки прерывания может и не помочь (зависит от длительности дребезга и от времени работы процедуры обработки прерывания). Достоинства: простота обработчика.

2. Можно вспомнить, что за один щелчок энкодера (шаг) состояние его выходов меняется четыре раза (00, 01, 11, 10 и т.д. - "микрошаги"). Второй вариант обработчика энкодера. Некая процедура запускается периодически: в бесконецном цикле main (если не жалко и цикл не загружен иными задачами), или по таймеру и проверяет состояние выходов энкодера. Максимальный период следует выбирать из максимальной скорости вращения энкодера. По состоянию выходов энкодера на предыдущем шаге и текущему состоянию выходов определяется микрошаг поворота энкодера (с учетом направления). "Микрошаги" суммируются и при значении числа "микрошагов" =4 инкрементируется значение регулируемой величины, при числе "микрошагов" =-4 - величина уменьшается на единицу; и сумма "микрошагов" сбрасывается в ноль. Недостатки: 1) некая грамозкоскть обработчика, 2) возможно, жалко отдать под обработчик таймер. Достоинства: нет нужды принимать специальные меры по борьбе с дребезгом, посколько дребезг приводит к колебанию числа "микрошагов" на единицу и повлиять на значение регулируемой величины не может.

P.S. Кто-то предложит ещё (может, не один) метод?
gte
Цитата(Палыч @ Jun 18 2008, 16:47) *
грамозкоскть обработчика, 2) возможно, жалко отдать под обработчик таймер. Достоинства: нет нужды принимать специальные меры по борьбе с дребезгом, посколько дребезг приводит к колебанию числа "микрошагов" на единицу и повлиять на значение регулируемой величины не может.


При использовании механических энкодеров pec16 с мелким шагом после одного щелчка с большой вероятностью он может не встать на родное место и счета не произойдет. Не фатально, но неприятно.
Палыч
Цитата(gte @ Jun 18 2008, 15:58) *
При использовании механических энкодеров pec16 с мелким шагом после одного щелчка с большой вероятностью он может не встать на родное место и счета не произойдет.
Спорное утверждение... При использовании PEC16 с 24 фиксированными положениями такого замечено не было ни разу... Бывают ещё более мелкие шаги?
gte
Именно такой. Я смотрел анализатором переключая по одному щелчку. Да и откуда там четкая фиксация?
Палыч
Посмотрел DS. В нём есть рисунок "Quadrature Output Table", в котором фиксированное положение ротора прихотится на серидину "микрошага". Конечно, этот рисунок производителя ни к чему не обязывает, но, ИМХО, при производстве энкодеров стараются выдержать нечто близкое... Фиксация положения - вроде, давольно чёткая. Недавно выпускали серию приборов с энкодерами. ОТК их придирчиво все проверяли, особенно как работает энкодер, потому, что в пилотном экземпляре были выявлены глюки с энкодером (его обработка производилась по фронту одного из выходов и очень часто бывало изменение регулируемой величины на 2 за шаг). Нареканий не было. Может быть, потому, что - энкодеры новые, ещё не обтёрлись...
Maik-vs
Цитата(wired @ Jun 18 2008, 15:36) *
какое новое состояние другого если вращение идет в одну сторону?
если прерьівание по изменению первого то второй свое значение не меняет, иначе - никакого нового состояния, кроме как вращения в противоположную сторону.

или анализировать по двум прерьіваниям...? тогда без ввода задержек может и рполучится... НО внешних прерьіваний всего 2 а мне еще хочется пульт прикошачить, ему тоже кстати надо внешнее прерьівание


Да, получается, что нужно 2 прерывания. Лучше периодически опрашивать сигналы, причём можно не через равные интервалы, лишь бы достаточно часто. При вращении в одну сторону получается последовательность 0-2-3-1-0-2-3-1... в другую - 0-1-3-2-0-1-3-2... Принятые сигналы сдвигаем в регистр. Получается, что в регистре хранится 4 последних состояния энкодера. Отбрасывая старые 2 бита и сравнивая его с константой, получаем ответ - был ли шаг и в какую сторону.
Это, в общем, реализация способа 2 Палыча.
PS А, я уже писал об этом. (#3 и #4) Анализировать надо таки 3 последних состояния, особенно если опрос. Потому что частота дребезга непредсказуема, пропустить переход в последовательности 2-3-2-3-2-3 вполне вероятно, и вот оно - двойное срабатывание на один "зуб".
wired
Цитата(Maik-vs @ Jun 18 2008, 17:33) *
Да, получается, что нужно 2 прерывания. Лучше периодически опрашивать сигналы, причём можно не через равные интервалы, лишь бы достаточно часто. При вращении в одну сторону получается последовательность 0-2-3-1-0-2-3-1... в другую - 0-1-3-2-0-1-3-2... Принятые сигналы сдвигаем в регистр. Получается, что в регистре хранится 4 последних состояния энкодера. Отбрасывая старые 2 бита и сравнивая его с константой, получаем ответ - был ли шаг и в какую сторону.
Это, в общем, реализация способа 2 Палыча.
PS А, я уже писал об этом. (#3 и #4) Анализировать надо таки 3 последних состояния, особенно если опрос. Потому что частота дребезга непредсказуема, пропустить переход в последовательности 2-3-2-3-2-3 вполне вероятно, и вот оно - двойное срабатывание на один "зуб".

ОК..
я пока новичок. и прерьівания обрабатьівать както нагляднее, пройдусь по єтим граблям smile.gif потом буду разбираться с регистром... может примерчик ? а....
gte
Цитата(Палыч @ Jun 18 2008, 18:25) *
Посмотрел DS. В нём есть рисунок "Quadrature Output Table", в котором фиксированное положение ротора прихотится на серидину "микрошага". Конечно, этот рисунок производителя ни к чему не обязывает, но, ИМХО, при производстве энкодеров стараются выдержать нечто близкое... Фиксация положения - вроде, давольно чёткая. Недавно

Да, в фиксированном положении оба контакта должны быть разомкнутыми. Фиксация положения обеспечивается пружинным кольцом с выступом. Кольцо хлипкое. В общем, мне больше нравятся на 12 положений.
ozzy
Цитата(wired @ Jun 18 2008, 14:36) *
...

или анализировать по двум прерьіваниям...? тогда без ввода задержек может и рполучится... НО внешних прерьіваний всего 2 а мне еще хочется пульт прикошачить, ему тоже кстати надо внешнее прерьівание


Лично я когда то сделал так (потому как второе прерывание тоже надо было для других целей):

Внешнее прерывание по фазе A выставил на срабатывание по любому событию, фазу B на любой вход.
И сам обработчик:
Код
// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
if ( FASE_A  ^ FASE_B)

                              {
                              if (FASE_A){
                                          if (FASE_B) current_counter++;
                                          else current_counter--;
                                          }
                              }
else                           {
                                
                                if (!FASE_A){
                                              if (!FASE_B) current_counter++;
                                              else current_counter--;
                                             }                            
                                }
}


Работает замечательно до сих пор.
wired
Цитата(Maik-vs @ Jun 18 2008, 17:33) *
Да, получается, что нужно 2 прерывания. Лучше периодически опрашивать сигналы, причём можно не через равные интервалы, лишь бы достаточно часто. При вращении в одну сторону получается последовательность 0-2-3-1-0-2-3-1... в другую - 0-1-3-2-0-1-3-2... Принятые сигналы сдвигаем в регистр. Получается, что в регистре хранится 4 последних состояния энкодера. Отбрасывая старые 2 бита и сравнивая его с константой, получаем ответ - был ли шаг и в какую сторону.
Это, в общем, реализация способа 2 Палыча.
PS А, я уже писал об этом. (#3 и #4) Анализировать надо таки 3 последних состояния, особенно если опрос. Потому что частота дребезга непредсказуема, пропустить переход в последовательности 2-3-2-3-2-3 вполне вероятно, и вот оно - двойное срабатывание на один "зуб".


я правильно понял: по каждому изменению заполняем некую переменную, состояниями входов,
while...
{
reg = (reg<<1)+A
reg = (reg<<1)+B
}

соответственно заполняется
влево
013 00000111
132 00011110
320 00111000
201 00100001
вправо
023 00001011
231 00101101
310 00110100
102 00010010

отбросив старшие 5 бит
reg = (reg & 0x07);

далее сравниваю
влево
reg = 1 001
reg = 7 111
reg = 6 110
reg = 0 000

вправо
reg = 2 010
reg = 3 011
reg = 5 101
reg = 4 100
haker_fox
Использую датчики от мышки для управления двигателем: измерение пути и угловой скорости. Дешевых энкодеров, способных работать на максимальной скорости 60 об/с не нашел, поэтому "мышинное решение" самое то. НО. Разочаровало то, что зубья и прорези на колесе не одинаковые по ширине, это видно и визуально на колесе и на экране осциллографа по ширине импульсов. Соответственно использовать 4 фазы смены состояний между двумя "главными" импульсами энкодера невозможно: будет погрешность при измерении двух вышеназванных величин. Для элемента управления это не страшно, а вот в моем случае - критично. Соответсвенно вместо 180 импульсов на оборот получаем только 45.
P.S. Есть ли в природе дешевые энкодеры с небольшим количеством импульсов на оборот, но не дорогие?
tremor
Читать всю тему влом, поэтому просто приведу свой код обработки энкодера. Работает с энкодером PEC16, частоту сканирования менял в пределах 100uS....1mS, все работает очень четко.

Внешний обработчик события поворот в вправо или влево просто проверяет содержимое байта в ОЗУ-Encoder_Status, вообщем из кода вроде должно быть все ясно.

Код
;===============================================================================
==============================================
; Подпрограмма обработки энкодера
;===============================================================================
==============================================
                    
Encoder:                lds        temp,(Encoder_Status)    ; Прочитать из памяти состояние кольцевого буфера энкодера
                        cpi        temp,0b00110100            ; Если кольцевой буфер содержит код состояния энкодера "против часовой"
                        breq    Encoder_End                ; то перейти на выход
                        cpi        temp,0b00011100            ; Если кольцевой буфер содержит код состояния энкодера "по часовой"
                        breq    Encoder_End                ; то перейти на выход
                        clr        temp                    ; Обнулить все разряды РОН
                        out        DDRB,temp                ; Сделать все выводы порта В входами и записать конфигурацю
                        nop                                ; подождать пока установиться напряжение на выводах порта В
                        nop                                ; подождать пока установиться напряжение на выводах порта В
                        nop                                ; подождать пока установиться напряжение на выводах порта В
                        nop                                ; подождать пока установиться напряжение на выводах порта В
                        nop                                ; подождать пока установиться напряжение на выводах порта В
                        lds        temp3,(Encoder_rep_state); Прочитать из памяти число повторов состояние контактов энкодера
                        in        temp,PINB                ; Сканировать состояние энкодера
                        com        temp                    ; Получить обратный код замкнутых контактов
                        andi    temp,0b00010100            ; Выделить разряды к которым подключен энкодер через маску контактов энкодера
                        lsr        temp
                        lsr        temp
                        inc        temp                    ; Сдвинуть первый бит влево если он равен "1"
                        lsr        temp                    ; Сдвинуть байт влево, приведя код состояния энкодера к нормальному виду
                        lds        temp2,(Encoder_prev)    ; Прочитать из памяти состояние кольцевого буфера энкодера
                        cpse    temp,temp2                ; Сравнить состояние контактов энкодера с предыдущим, если они неравны,
                        clr        temp3                    ; то очистить счетчик повторов
                        sts        (Encoder_prev),temp        ; Сохранить текущее состояние контактов энкодера в "предыдущем"

                        cpi        temp3,2                    ; Сравнить счетчик числа повторов с контстантой (=3 для частоты сканирования 1кГц и 15 для 10кГц)

                        breq    Encoder_1                ; Перейти если равно, в обновлению состояния кольцевого буфера
                        brsh    Encoder_End                ; Перейти если больше, на выход
                        inc        temp3                    ; Увеличить счетчие числа повторов
                        sts        (Encoder_rep_state),temp3; Сохранить в памяти число повторов состояние контактов энкодера
                        clr        temp                    ; вывести на все выводы порта В нули
                        out        PORTB,temp                ; запись нули в РВВ порта В
                        com        temp                    ; Установить все разряды РОН в единицу,
                        out        DDRB,temp                ; Сделать все выводы порта В выходами и записать конфигурацию
                        ret                                ; Выйти из подпрограммы обработки энкодера
Encoder_1:                lds        temp,(Encoder_Status)    ; Прочитать из памяти состояние кольцевого буфера энкодера
                        lsl        temp                    ; Сдвинуть кольцевой буфер состояния энкодера на шаг в лево
                        lsl        temp                    ; Сдвинуть кольцевой буфер состояния энкодера на шаг в лево
                        add        temp,temp2                ; Добавить новое состояние контактов энкодера в кольцевой буфер
                        inc        temp3                    ; Увеличить счетчие числа повторов
                        sts        (Encoder_Status),temp    ; Сохранить в памяти состояние кольцевого буфера энкодера
Encoder_End:            sts        (Encoder_rep_state),temp3; Сохранить в памяти число повторов состояние контактов энкодера
                        clr        temp                    ; вывести на все выводы порта В нули
                        out        PORTB,temp                ; запись нули в РВВ порта В
                        com        temp                    ; Установить все разряды РОН в единицу,
                        out        DDRB,temp                ; Сделать все выводы порта В выходами и записать конфигурацию
                        ret


Цитата(haker_fox @ Jun 24 2008, 06:33) *
P.S. Есть ли в природе дешевые энкодеры с небольшим количеством импульсов на оборот, но не дорогие?


PEC16-4020F-S0012 - имеют 12 отсчетов на 1 оборот
haker_fox
Цитата(tremor @ Jun 25 2008, 03:08) *
PEC16-4020F-S0012 - имеют 12 отсчетов на 1 оборот

Только вряд ли он выдержит вращения на высокой скорости.
tremor
Цитата
Только вряд ли он выдержит вращения на высокой скорости.


В ДШ пишут что 100 RPM (Operating), наверно связанно с тем что контакты будут подвисать при большей частоте вращения. Можно посоветовать порыться на сайте Chip&Dip там может чего и есть.
haker_fox
Цитата(tremor @ Jun 25 2008, 10:54) *
В ДШ пишут что 100 RPM (Operating), наверно связанно с тем что контакты будут подвисать при большей частоте вращения. Можно посоветовать порыться на сайте Chip&Dip там может чего и есть.

Есть-то есть, но как минмум 700 р за штуку. Хотелось бы дешевле. Но изделие если не разовое, то не высокотиражное - датчики из мышек пойдут. Лишь бы высокой вибрации не было в самодельных крепежных элементах, иначе могут быть помехи в измерениях мгновенной скорости и потеря пройденного пути. А в остальном полностью устраивают: мышек дохлых полно.
Maik-vs
Цитата(wired @ Jun 23 2008, 10:39) *
я правильно понял: по каждому изменению заполняем некую переменную, состояниями входов,
while...
{
reg = (reg<<1)+A
reg = (reg<<1)+B
}

соответственно заполняется
влево
013 00000111
132 00011110
320 00111000
201 00100001
вправо
023 00001011
231 00101101
310 00110100
102 00010010

отбросив старшие 5 бит
reg = (reg & 0x07);

далее сравниваю
влево
reg = 1 001
reg = 7 111
reg = 6 110
reg = 0 000

вправо
reg = 2 010
reg = 3 011
reg = 5 101
reg = 4 100


нет.
Периодически принимаем состояние енкодера, сдвигаем в регистр.
Если входы сидят, допустим, на битах 6 и 7 порта А, то:

encoder:
in tmp,pina
rol tmp
lsr reg
rol tmp
lsl reg ;reg содержит 4 последних состояния: aabbccdd, aa самое старое, dd самое новое
andi reg,$3f ; убрали самое старое
cpi reg,$2d ; bbccdd = 10 11 01? влево 231
breq toleft
cpi reg,$1e ; bbccdd = 01 11 10? вправо 132
breq toright
ret

Почему 3 состояния. Было так. Колесо с оптическим датчиком (с гистерезисом, 2 канала, как на мыше) стоит на тросе. Трос стоит, счётчик бежит вперёд. Оказывается, трос мелко дрожит, и один сигнал (иногда!) быстренько меняется тоже. Какие к нему претензии? Построил граф состояний - понял.
wired
Цитата(Maik-vs @ Jun 26 2008, 15:46) *
нет.
Периодически принимаем состояние енкодера, сдвигаем в регистр.
Если входы сидят, допустим, на битах 6 и 7 порта А, то:

encoder:
in tmp,pina
rol tmp
lsr reg
rol tmp
lsl reg ;reg содержит 4 последних состояния: aabbccdd, aa самое старое, dd самое новое
andi reg,$3f ; убрали самое старое
cpi reg,$2d ; bbccdd = 10 11 01? влево 231
breq toleft
cpi reg,$1e ; bbccdd = 01 11 10? вправо 132
breq toright
ret

Почему 3 состояния. Было так. Колесо с оптическим датчиком (с гистерезисом, 2 канала, как на мыше) стоит на тросе. Трос стоит, счётчик бежит вперёд. Оказывается, трос мелко дрожит, и один сигнал (иногда!) быстренько меняется тоже. Какие к нему претензии? Построил граф состояний - понял.

все, "догнал"... в моем варианте отрабатьіваются 4 фазьі т.е. за 1 щелчок в идеале получаю 4 отчсета, что в принципе с практикой согласовьівается

вьізьівается из основного цикла..
Код
void rd_encoder(void)
{int temp = 0;
pinstate = (ENC_PIN & 0x06);  // pind.1 pind.2
if (state != pinstate) //состояние изменилось   pind.1 pind.2
{
state = pinstate;      //обновили state
/*  заполняю буфер */
encoder = encoder<<1;
encoder = encoder+ENC_A;
encoder = encoder<<1;
encoder = encoder+ENC_B;

switch((encoder & 0x07)) //смотрим последние 3 бита
{
case 1:temp = 1;
break;
case 7:temp = 1;
break;
case 6:temp = 1;
break;
case 0:temp = 1;
break;
case 2:temp = -1;
break;
case 3:temp = -1;
break;
case 5:temp = -1;
break;
case 4:temp = -1;
break;
default: temp = 0;
}

твой вариант будет приблизительно таким буквально:
Код
void rd_encoder(void)
{int temp = 0;
pinstate = (ENC_PIN & 0x06);  // pind.1 pind.2
if (state != pinstate) //состояние изменилось   pind.1 pind.2
{
state = pinstate;      //обновили state
/*  заполняю буфер */
encoder = encoder<<1;
encoder = encoder+ENC_A;
encoder = encoder<<1;
encoder = encoder+ENC_B;

switch((encoder & 0x3F)) //смотрим последние 6 бит
{
case 0x2D:temp = 1;
break;
case 0x1E:temp = -1;
break;
default: temp = 0;
}
sansnotfor
мой вариант. энкодер опрашиваю в прерывании таймера.

Код
#define PORT_Enc     PORTA     
#define PIN_Enc      PINA
#define DDR_Enc     DDRA
#define Pin1_Enc     1
#define Pin2_Enc     2

#define _0b00000011 3
#define _0b00111111 63
#define _0b00010010 18
#define _0b00100001 33

#define SBI(port, bit) port|= (1<<bit)
#define CBI(port, bit) port&= ~(1<<bit)

//подпрограмма инициализации
void Init_Encoder(void)
{
CBI(DDR_Enc, Pin1_Enc); //вход
CBI(DDR_Enc, Pin2_Enc);
CBI(PORT_Enc, Pin1_Enc);//вкл подтягивающий резистор
CBI(PORT_Enc, Pin2_Enc);
}

//подпрограмма опроса энкодера
/*считывает значения выводов энкодера, если на обоих выводах единицы, то возвращает 0.
если текущее состояние равно предыдущему, то возвращает 0.
если состояние изменилось, то сдвигает регистр state_enc влево на 2 разряда и записывает
2 разряда текущего состояния. Проверяет получившуюся последовательность. если
последовательность соответствует вращению влево - возвращает (-1), вправо - возвращает (1)
*/
unsigned char Read_Encoder(void)
{
unsigned char tmp,tmp2;
static unsigned char state_enc;     //хранит последовательность состояний энкодера
    
tmp=0;
if ((PIN_Enc&(1<<Pin1_Enc))!=0) {SBI(tmp,0);}
else {CBI(tmp,0);}
if ((PIN_Enc&(1<<Pin2_Enc))!=0) {SBI(tmp,1);}
else {CBI(tmp,1);}

if (tmp==_0b00000011) {return 0;}

tmp2=(state_enc & _0b00000011);
if (tmp==tmp2)    {return 0;}

tmp2=state_enc<<2;
state_enc=tmp2 | tmp;

tmp2=tmp2 & _0b00111111;
    
if (tmp2==_0b00100001) {return 0x01;}
if (tmp2==_0b00010010) {return 0xff;}

return 0;
}
Genadi Zawidowski
Ух ты... А тема ещй жива... Попробую предложиьть изящный (с моей точки зрения) способ реализации этого алгоритма. Используется с оптическими валкодерами, проблемы с подавлением дребезга механических датчиков в данном месте решал бы только с помощью RC-цепочки и триггера Шмидта.
Начальное значение old_val - считанное значение из функции, дающей состояние битов прерываний. Входы прерываний программируются на срабатывание по любому перепаду. Ничего (кроме максимальной скорости вращения) не изменится, если функцию вызывать и из таймерного прерывания.
Код
static uint8_t old_val;

void spool_encinterrupt(void)
{
    uint8_t new_val = hardware_get_encoder_bits();

    // dimensions are:
    // old_bits new_bits
    const static signed char v [4][4] =
    {
        {
            +0,        /* 00 -> 00 stopped */
            -1,        /* 00 -> 01 rotate left */
            +1,        /* 00 -> 10 rotate right */
            +0,        /* 00 -> 11 invalid combination */        
        },
        {
            +1,        /* 01 -> 00 rotate right */
            +0,        /* 01 -> 01 stopped */
            +0,        /* 01 -> 10 invalid combination */
            -1,        /* 01 -> 11 rotate left */
        },
        {
            -1,        /* 10 -> 00 rotate left */
            +0,        /* 10 -> 01 invalid combination */
            +0,        /* 10 -> 10 stopped */
            +1,        /* 10 -> 11 rotate right */
        },
        {
            +0,        /* 11 -> 00 invalid combination */
            +1,        /* 11 -> 01 rotate right */
            -1,        /* 11 -> 10 rotate left */
            +0,        /* 11 -> 11 stopped */
        },
    };


    rotate += v [old_val][new_val];

    old_val = new_val;
}


Код
#if defined (CPUSTYLE_ATMEGA128)

ISR(INT4_vect)
{
    spool_encinterrupt();
}

ISR(INT5_vect)
{
    spool_encinterrupt();
}

#elif defined (CPUSTYLE_ATMEGA32)

ISR(INT0_vect)
{
    spool_encinterrupt();
}

ISR(INT1_vect)
{
    spool_encinterrupt();
}
#else

#error Undefined processor

#endif


Код
/* получение накопленного значения прерываний от валкодера.
        накопитель сбрасывается */
uint_least16_t
getRotateHiRes(
    uint_least32_t * jumpsize,
    uint_least16_t granulation)
{
#if ENCODER_HIRES
#define BIGJUMPSIZE (10000UL / 4)
    static const uint8_t velotable [] =
    {
        1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    };
#else
#define BIGJUMPSIZE (10000UL)
    static const uint8_t velotable [] =
    {
        1, 1, 1, 1, 1, 1,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    };
#endif

    div_t d, h;
    uint8_t ticks;
    uint16_t nrotate, hrotate;
    cli();
    if (tickcount != 0)
    {
        ticks = tickcount;
        tickcount = 0;
        hrotate = rotate;
        rotate = 0;
    }
    else
    {
        ticks = 1;
        hrotate = 0;
    }
    sei();

    /* Уменьшение разрешения валкодера в зависимости от установок в меню */
    h = div(hrotate, hiresdiv);

    cli();
    rotate += h.rem;
    sei();

    nrotate = h.quot;

    d = div(nrotate + stepcount, ticks);
    stepcount += d.rem;    /* остаток пригодится в следующий раз */

    if (d.quot < 0)
        d.quot = - d.quot;
    if (d.quot < (sizeof velotable / sizeof velotable [0]))
        * jumpsize = (uint32_t) granulation * velotable [d.quot];
    else if (noaccelerate != 0)
        * jumpsize = (uint32_t) granulation * velotable [(sizeof velotable / sizeof velotable [0]) - 1];
    else
        * jumpsize = BIGJUMPSIZE;
        

    return nrotate;
}


Код
/* получение "редуцированного" количества прерываний от валкодера.
* То что осталось после деления на scale, остается в накопителе
*/

int getRotateLoRes(void)
{
    int nrotate;
    div_t d;
    cli();
    nrotate = rotate;
    rotate = 0;
    sei();

    d = div(nrotate, ROTATE_LORES_DIV);

    cli();
    rotate += d.rem;
    sei();

    return d.quot;
}


void encoder_initialize(void)
{
    rotate = 0;
    stepcount = 0;
    tickcount = TICKCOUNT_MAX;

    hardware_encoder_initialize();


#if ENCODER_MULTICLICK
    old_val = hardware_get_encoder_bits();
#endif // ENCODER_MULTICLICK
}


Проект лежит здесь - http://forum.cqham.ru/viewtopic.php?t=15274 и в аттаче.
Andrew O. Shadoura
Добавлю немного в копилку. Знаю, не самый лучший вариант, писалось на скорую руку, но работает.

Код
void check_encoder(void)
{
  static uint8_t state=0;
  static uint8_t prevstate=0;
  static uint8_t prevcount=0;
  static uint8_t laststate=0;
  register uint8_t temp;
  temp=(PINA&0x60)<<1;

  if (temp==prevstate)
  {
    if (prevcount<4)
    {
      prevcount++;
    }
    if (prevcount!=3)
    {
      return;
    }
  }
  else
  {
    prevcount = 0;
    prevstate = temp;
    return;
  }

  if (laststate==prevstate)
    return;

  laststate=prevstate;

  switch (prevstate&0xc0)
  {
    case 0xc0:
    {
      if (state==4)
      {
        if (value>VALUEMIN)
        {
          value--;
        }
      }
      if (state==5)
      {
        if (value<VALUEMAX)
        {
          value++;
        }
      }
      state = 1;
    } break;
    case 0x00:
    {
      if ((state==1) || (state==3)) state++;
    } break;
    case 0x40:
    {
      if (state==1) state=3;
      if (state==2) state=5;
    } break;
  }
}


--
WBR, Andrew
pavel-pervomaysk
Приложу свою первую подпрограмку для работы с Энкодером типа PEC-16 :

.def tmp = r16
.equ encod = PB2
.equ enc_2 = PB1

.org INT2addr // прерывание по внешнему сигналу INT2 для энкодера громкости
rjmp Encoder // обработчик прерывания от энкодера

.CSEG // сегмент кода
//Векторы прерываний:
.org 0 // по адресу 0 вектор сброса
rjmp RESET // перейти на метку ресет

RESET:
in temp,MCUCR
ori temp,(1 << ISC10) ;по спаду
out MCUCR,temp
in temp,GIMSK
ori temp,(1 << INT2) ;бит INT2 в GIMSK равен 1
out GIMSK,temp ;разрешаем внешнее прерывание INT2
sei

// тут пишем всякую инициализацию //

// Подпрограмма
Encoder:
sbi PORTD,7 // контроль на PD7 внешнего прерывания
push tmp // сохранение темп
in tmp,SREG // читаем статус регистр
push tmp // сохраняем его
sbis pinb,encod // проверяем наличие прерывания
rcall encoder_read // читаем энкодер
pop tmp // извлекаем статус регистр
out SREG,tmp // восст. SREG
pop tmp // восст. temp
cbi PORTD,7 // гасим светодиод контроля
reti // выходим и разрешаем прерывания

Encoder_read: // чтение энкодера
sbis pinb,1 // если установлен бит 1 в порте то пропуск след ком
rcall plus // вызываем плюс
sbic pinb,1 // если очищен бит 1 в порте то пропуск след ком
rcall minus // вызываем минус
reti // выход из прерывания

minus: //
rcall lcd_sub //
reti //

plus: //
rcall lcd_add //
reti //
sansnotfor
оооо... заметил, что мой код можно сократить в этом месте
Код
tmp=0;
if ((PIN_Enc&(1<<Pin1_Enc))!=0) {SBI(tmp,0);}
else {CBI(tmp,0);}
if ((PIN_Enc&(1<<Pin2_Enc))!=0) {SBI(tmp,1);}
else {CBI(tmp,1);}


и будет то же самое
Код
tmp=0;
if ((PIN_Enc&(1<<Pin1_Enc))!=0) {SBI(tmp,0);}
if ((PIN_Enc&(1<<Pin2_Enc))!=0) {SBI(tmp,1);}
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.