Страница 1 из 1

Никак не поборю энкодер

Добавлено: Сб май 11, 2013 23:58:05
warezzzok
Добрый день\ночь.
Знаю, что тема уже избита, однако, перечитав кучу сообщений и статей, так и не смог накодить ничего вменяемого.
Выдрал из сторого ЭЛТ монитора энкодер (как в этой статье, только контакты выведены под 90 градусов)
Задача сделать обработчик энкодера, чтоб по щелчку происходило что-то, например, загорался один из 8 светодиодов (типа как счетчик).
Энкодер имеет, как и в статье, 24 щелчка на оборот, каждый щелчек - смена 4 состояний выводов.
Честно говоря, я не совсем понял, как в статье di halt его обрабатывает. Код, судя по всему, для энкодера без щелчков. Если нет, то зачем делается перебор всех 4 комбинаций состояний энкодера.
Я сделал следующим образом:
Изображение

increase\decrease - изменение значения регистра порта, на котором висит 8 светодиодов
newstate в коде обозначен как просто state

Код: Выделить всё

.include "m8def.inc"
.include "macro.asm"
.def temp	=	r16
.def oldstate	=	r17
.def state	=	r18
.cseg 

.org	0 
rjmp	reset
.org	$009
rjmp	T0OVF

reset:      
outi SPH,High(RAMEND)
outi SPL,Low(RAMEND)
;-------------------------------------------------------
outi DDRD,	0xff			;Порт D - выход
outi PORTD,	0b00000001		;Зажигаем 0 пин порта
;-------------------------------------------------------
outi DDRB,	0b00000000		;Порт В - вход
outi PORTB,	0b00000000		;В Hi-Z
;-------------------------------------------------------
outi TCCR0,	(1<<CS00 | 1<<CS02)	;T0 FastPWM, presc:1024
outi TIFR,	(1<<TOV0)
outi TIMSK,	(1<<TOIE0)
;-------------------------------------------------------
sei
in oldstate, pinb	;Тут всегда 0х00. Так устроен этот энкодер
;-------------------------------------------------------
main:
rjmp main           
;-------------------------------------------------------
T0OVF:
in state, pinb
	cp state, oldstate
		breq	reti_	;Если состояние не изменилось - выходим из прерывания
		brne	check
check:
	cpi state, 0x10		;Крутанули назад
		breq nazad
	cpi state, 0x01
		breq vpered	;Крутанули вперед
rjmp reti_
;----------------------
vpered:
in temp, portd			;Взяли значение из светодиодного порта и увеличили
	inc temp
	out portd, temp
	rcall delay
rjmp reti_
;----------------------
nazad:
in temp, portd			;То же, но наоборот - уменьшили
	dec temp
	out portd, temp
	rcall delay
rjmp reti_			;Знаю, что это тут не обязательно, просто так легче ориентироваться

reti_:
in state, pinb			;Не выходим из прерывания, пока энкодер не дощелкнется до конца и не станет 0х00
	cp state, oldstate
	brne reti_
reti
;-------------------------------------------------------
delay:        			;Задержка от дребезга 0,05сек
          ldil  R0, $65
WGLOOP0:  ldil  R1, $A4
WGLOOP1:  dec  R1
          brne WGLOOP1
          dec  R0
          brne WGLOOP0
          ldil  R0, $01
WGLOOP2:  dec  R0
          brne WGLOOP2
          nop
          nop
ret
На portD висят светодиоды
На portB,0 и 1 висят ноги энкодера

В макро

Код: Выделить всё

.macro   outi
   ldi   temp,@1
   out   @0,temp
.endm

.macro LDIL		;Т.к регистры r0-r15 ущербные, юзаю для них такой макрос
  push r16
  ldi r16, @1
  mov @0, r16 
  pop r16
.endm
В итоге, эта дрянь работает очень странно:
ютуб
Щелчки почти не слышны в начале, потом, похоже, что стол вошел в резонанс и стало слышно :)
Я пробовал разные задержки на дребезг, пробовал их вообще убрать и сделать предделитель таймера 1024, но, результат практически такой же.
Понимаю, что разбирать чужие ошибки влом, но может кто-нибудь подскажет, как обрабатывать энкодеры с щелчками? Или пример какой (лучше, если на асме)
Спасибо.

P.S На видео, видимо, сработал эффект показа, но после ресета периодически контроллер просто перестает реагировать на энкодер пока не отрубишь питалово или не прошьешь его заново. Странно

P.P.S Забыл написать, камень - AVR ATmega8

Re: Никак не поборю энкодер

Добавлено: Вс май 12, 2013 01:47:22
SPY
http://www.cqham.ru/swl_receiver.htm
синтез с исходниками , который я собирал.

Re: Никак не поборю энкодер

Добавлено: Вс май 12, 2013 02:38:27
warezzzok
Спасибо, добрый человек :)
Посмотрел исходники - сломал мозг окончательно, тем более, у вас PIC, а у меня AVR (прошу прощения - забыл указать). Знаю, что алгоритм одинаков, но тут с авр-овским ассемблером не разберусь до конца :(

Re: Никак не поборю энкодер

Добавлено: Вс май 12, 2013 05:35:39
КРАМ
Даже и не стал смотреть код. Принял алгоритм как он выше кода нарисован.
1. В прерываниях не обрабатывают механические устройства ввода, за исключением подавления их дребезга. После зачистки от дребезга (защелкивания состояния энкодера) по прерываниям от таймера (никаких зависаний в прерывании быть не может), обработка (машина состояний) энкодера производится в основном цикле.
2. В алгоритме не использовано СТАРОЕ состояние энкодера, а именно оно определяет инкремент или декремент произошел.

Re: Никак не поборю энкодер

Добавлено: Вс май 12, 2013 12:25:00
shads
Такой вариант глянь.....
http://asis-kbr.ru/forum/viewtopic.php?f=13&t=141

Re: Никак не поборю энкодер

Добавлено: Вс май 12, 2013 13:37:18
warezzzok
2. В алгоритме не использовано СТАРОЕ состояние энкодера, а именно оно определяет инкремент или декремент произошел.
Дак oldstate - это и есть предидущее состояние, если оно не равно состоянию в текущий момент времени, начинается алгоритм выяснения в какую сторону крутится.
Такой вариант глянь.....
Спасибо, поглядел. Попробую реализовать на асме - без задержки даже лучше.

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

Re: Никак не поборю энкодер

Добавлено: Ср май 15, 2013 01:51:43
warezzzok
Не хочется создавать новую тему, напишу здесь, тем более, что задача та же осталась :)

Наговнокодил что-то с задержками, оно даже вроде заработало, хотя и кривовато пока, ну да не суть.
Четко вылезла другая проблема:
ютуб
В 2 словах - после ресета, очень часто контроллер зависает (не реагирует ни на что) и так пока не протыкатешь ресет n-раз.
Иногда, когда все работает, зависает спустя некоторое время (н видео 0:47).
На этой меге были не правильно прошиты фьюзы, потом через avrdude и avr burn-o-mat делал сброс фьюзов на дефолт. Именно дефолтные фьюзы, как были при покупке меги я не нашел, может быть и сейчас стоит криво. Вообще, что это может быть? Вроде никаких "мертвых" циклов не делал

Код: Выделить всё

.include "m8def.inc"
.include "macro.asm"
.def temp      =   r16
.def oldstate   =   r17
.def state      =   r18
.cseg 
.org   0 
 rjmp   reset
reset:      
outi SPH,High(RAMEND)       ;Инициализируем стек
outi SPL,Low(RAMEND)      ;Инициализируем стек
;-------------------------------------------------------
outi DDRD,   0xff         ;Порт D как выход   |   Диоды
outi PORTD,   0b00001000      ;Порт D.1 лог.1      |   Диоды
;-------------------------------------------------------
outi DDRB,   0b00000000      ;Порт B как вход   |   Энкодер
outi PORTB,   0b00000000      ;Порт В - Hi-Z      |   Энкодер
in oldstate, pinb
;-------------------------------------------------------
main:
in state,pinb
   cp state, oldstate
      breq main
      brne case
rjmp main  
case:
   cpi state, 0x00
      breq state0
   cpi state, 0x01
      breq state1
   cpi state, 0x10
      breq state2
   cpi state, 0x11
      breq state3
rjmp main
state0:      ;энкодер 0, старое значение 1 (назад) или 2 (вперед)
cpi oldstate, 0b00000010
   breq nazad
cpi oldstate, 0b00000001
   breq vpered
rjmp main
state1:      ;энкодер 1, старое значение 3 (назад) или 0 (вперед)
cpi oldstate, 0b00000000
   breq nazad
cpi oldstate, 0b00000011
   breq vpered
rjmp main
state2:      ;энкодер 2, старое значение 0 (назад) или 3 (вперед)
cpi oldstate, 0b00000011
   breq nazad
cpi oldstate, 0b00000000
   breq vpered
rjmp main
state3:      ;энкодер 3, старое значение 2 (назад) или 1 (вперед)
cpi oldstate, 0b00000001
   breq nazad
cpi oldstate, 0b00000010
   breq vpered
rjmp main
vpered:
in temp, portd
   inc temp
   out portd, temp
   in oldstate, pinb
   rcall delay
rjmp main
nazad:
in temp, portd
   dec temp
   out portd, temp
   in oldstate, pinb
   rcall delay
rjmp main
delay:   ;70 миллисекунд (0,07с)
            ldil  R0, $0B
WGLOOP0:  ldil  R1, $D3
WGLOOP1:  dec  R1
          brne WGLOOP1
          dec  R0
          brne WGLOOP0
          ldil  R0, $01
WGLOOP2:  dec  R0
          brne WGLOOP2
          nop
ret

Re: Никак не поборю энкодер

Добавлено: Ср май 15, 2013 06:23:04
korsaj
Я бы вот так не делал

Код: Выделить всё

cpi oldstate, 0b00000010
   breq nazad
cpi oldstate, 0b00000001
   breq vpered
и так

Код: Выделить всё

case:
   cpi state, 0x00
      breq state0
   cpi state, 0x01
      breq state1
   cpi state, 0x10
      breq state2
   cpi state, 0x11
      breq state3
т.к. все пины порта на вход, мало ли чего там будет. Вы сначала выделите нужные пины по маске, а уж затем сравнивайте.
Попробуйте промоделируйте ситуацию если у вас oldstate вдруг станет 0b00100001. А именно такое и происходит т.к. у вас входы в 3-м состоянии и любая помеха может изменить состояния порта.

Re: Никак не поборю энкодер

Добавлено: Пт май 17, 2013 00:54:30
warezzzok
Спасибо вам за помощ, все оказалось именно как вы и написали из pinb читался мусор.

Re: Никак не поборю энкодер

Добавлено: Пт май 17, 2013 21:43:50
warezzzok
Сижу вот щас, играюсь с разными вариантами обработчика энкодера, переделал программу как у ДиХальта в статье - за 1 тик считвается 4 состояния и потом результат делится на 4.
Но никак не могу понять какую задержку делать - делаю маленькую - начинает регистрироваться дребезг, делаю большую - начинает пропускать некоторые состояния, пока контроллер слоупочит в задержке и, вместо 4 состояний, регистрирует меньше. Так и должно быть, или, может быть, это энкодер убитый в хлам уже и он просто так работает? К сожаленю, осцилографом не разжился пока, чтоб все точки расставить.

Re: Никак не поборю энкодер

Добавлено: Пт май 17, 2013 21:46:56
s_black
Здесь ещё посмотрите.

Re: Никак не поборю энкодер

Добавлено: Пт май 17, 2013 22:35:17
SmarTrunk
Алгоритм ДиХалта дилетанский и ущербный. Дребезг механического энкодера нельзя побороть задержками, т.к. длительность импульсов может быть сильно короче, чем дребезг. Выдавать 4 импульса на один щелчок -вообще глупость. По моему опыту, лучше всего работает опрос энкодера в прерывании с фиксированной частотой (1-2 кГц), с последовательным анализом состояний с учетом возможного дребезга. Но это сложно.

Re: Никак не поборю энкодер

Добавлено: Пт май 17, 2013 23:02:14
warezzzok
Честно говоря, мне, почему-то кажется, что тратить целый таймер с прерыванием на обработку энкодера - жирновато как-то. Им всегда можно придумать другое занятие. А вот сделть проверку без задержки - как-то более приемлемо, хотя, я так и не вкурил суть :(
Щас пока остановился на варианте обработки только первого по счету состояния энкодера, а остальные 3 - по боку. Вроде даже работает, хотя и проскальзывает иногда. Пытался вылавливть момент, когда оканчивается дребезг в самом крайнем состоянии энкодера минуя задержки, но что-то быстро запутался в алгоритме :)

Re: Никак не поборю энкодер

Добавлено: Пт май 17, 2013 23:09:33
SmarTrunk
Ну так можно использовать этот таймер одновременно и для других задач, например, для подсчета времени, разных длинвых задержек, динамической индикации, звукового сигнала (зуммера) и пр.

Re: Никак не поборю энкодер

Добавлено: Сб май 18, 2013 05:29:07
КРАМ
Это написано в обработчике системного таймера (таймер на всякую всячину...) с интервалом прерываний 0,256 мс (в другом изделии 0,512 мс - особой разницы нет)
Код в АСМе PIC18
С учетом комментов несложно перевести в чистый алгоритм.

Код: Выделить всё

; антидребезг и обработка энкодера
		clrf           inc_dec                        ; сбрасываем переменную исполнения
		movf		PORTB, w                    ; чтение порта к которому подключен энкодер
		andlw		b'00000011'        ; маска на разряды подключения
		movwf		temp_enc            ; сохраняем текущее состояние ПИНОВ
		xorwf		stack_enc, w        ; сравниваем с  предыдущим (стек пинов)
		movff		temp_enc, stack_enc    ; заносим текущее в стек
		btfss		STATUS, Z
		retfie	FAST		                        ; выход если состояние пинов изменились(дребезг)
		movf		temp_enc, w
		xorwf		old_enc, w            ; если дребезг закончился, то сравниваем текущее со старым ЭНКОДЕРОМ
		btfsc		STATUS, Z
		retfie	FAST		                           ; выход с 0 если старое состояние = новому
		movf		old_enc, f                     ; тест старого состояния на 0
		movff		temp_enc, old_enc     ; перенос текущего в старое
		btfss		STATUS, Z
		retfie	FAST		                          ; выход с 0 если старое состояние не ноль
		bcf		STATUS, C
		rlcf		old_enc, w	               ; умножаем на 2
		addlw		0xFD		         ; вычитаем 3
		movwf		inc_dec               ; сохраняем в переменной исполнения
		retfie	FAST		                          ; выход с плюс 1 или минус 1 в переменной исполнения
Применяем четыре переменные:
Стек энкодера (прежнее состояние) - защита от дребезга (текущее состояние = прежнему, значит дребезг окончен)
Текущее состояние
Старое состояние - предыдущее состояние с учетом защиты от дребезга
Переменная исполнения - при правом вращении равна +1, при левом равна -1
Но последняя переменная образуется корректно только если энкодер подключен к двум младшим разрядам порта, иначе придется привести переменные к этому виду сдвигом.

Re: Никак не поборю энкодер

Добавлено: Пн июн 17, 2013 14:41:31
СКАЗОЧНИК
:lol: может сейчас по дилетантски напишу, но делал Н-кодер по Ди-Халту. Тоже долго разбирался с его писаниной, очень не нравится, что он для новичков хорошо объясняет, но все время скатывается до конечных автоматов и диспетчера задач... Я не такой матерый программер, чтобы все это усвоить... =)))
Потом, еще и на Си про Н-кодеры у него написано.
Пришлось просто разбирать описание работы его алгоритма, прописывать все в конспекте, а когда дошло, что он хотел сказать, то все написал на ассемблере! =) И отличненько получилось. Подключил то, что было под рукой, т.е. н-кодер от ролика мышки, он щелкает. Все работает и управляет положением сервопривода, используются и таймеры и прерывания по таймерам, там много для чего.
Одно могу еще добавить, чтобы было меньше глюков, надо ставить внешние подтягивающие резисторы к плюсу, иначе шумит сильно (внутренних не хватает).

Кому может надо, и есть желание у кого поразбираться в исходнике, прикрепляю. Там много комментариев, кроме самого Н-кодера... =))))) Было лень...

Re: Никак не поборю энкодер

Добавлено: Пт июн 28, 2013 20:38:37
lazer
Доброго времени суток всем. Тема действительно сложная, если хочется добиться очень хорошей работы валкодера. Я тоже очень много вариантов рассматривал и пробовал, голову поломал изрядно и в конце концов, пришел к "своему": опрос одного вывода при перепаде на другом без использования прерываний и таймеров. Дребезг давится задержками. Если кому интересно - попробуйте. Программа сделана на ассемблере для AVR как раз в процессе изучения обработки валкодера.
У меня получился нормальный результат и данный фрагмент обработки валкодера встроен мной в программу синтезатора частот Изображение. Кстати, обработка валкодера по прерываниям не устроила меня по результату работы. Приглашаю к общению на эту тему.