подниму старую тему.
на днях занялся освоением энкодера.
при "изучении" интернета понял, что практически существует только два алгоритма обработки:
1. через внешнее прерывание. канал А заводится на прерывание, например, INT0, а при обработке прерывания опрашивается канал В.
2. опрос по таймеру обоих сигналов энкодера.
логика обработки через внешнее прерывание на первый взгляд выглядит "железобетонно", и такое впечатление, что сбоев давать не должна. хотя в различных статьях надежность работы через внешнее прерывание оценивается гораздо ниже, чем второй способ (алгоритм).
соответственно кажущейся "железобетонности", я начал осваивать по внешнему прерыванию (INT0). в обработке прерывания я сразу же запрещал это прерывание и запускал таймер, которой через некоторое время опять разрешал это внешнее прерывание. прерывание по таймеру отключало этот таймер.
в общем, по внешнему прерыванию у меня вообще ничего не получилось. при вращении в одну сторону параметр и увеличивался и уменьшался, то есть, "крутился" вокруг исходного значения. то же самое происходило при вращении энкодера в другую сторону.
тогда я реализовал второй алгоритм. и, о чудо!, энкодер прекрасно заработал с первого раза, даже ничего не пришлось подправлять в тексте программы.
для МК я пишу только на ассемблере. тексты на С я даже читать отказываюсь.
даю фрагменты своего текста, определяющие работу с энкодером.
Спойлер
Код: Выделить всё
.include "m8def.inc" ; ATMega8
; внутренний RC генератор 1 МГц
; R14, R15 - счетчик энкодера
...
;------ настройка Таймера0 ------
ldi R16, 2 ; предделитель = 8, интервал срабатывания Таймера0 2048 мкс
out TCCR0, R16
...
;--- прерывание по Таймеру0 ---
timer0:
push R16 ; сохраним регистр, который использует прерывание
in R16, SREG ; сохраним SREG
push R16 ; сохраним SREG
; канал A энкодера - порт D0
; канал B энкодера - порт D1
in new_state_enc, PinD
andi new_state_enc, 3
cp old_state_enc, new_state_enc ; если состояние не поменялось, то выходим
breq nothing
cpi new_state_enc, 0 ; если состояние поменялось, выбираем подходящий вариант
breq case0
cpi new_state_enc, 1
breq case1
cpi new_state_enc, 2
breq case2
cpi new_state_enc, 3
breq case3
rjmp nothing ; блокировка на всякий случай, хотя до этой команды никогда не должно дойти
case0:
cpi old_state_enc, 1
breq Iref_plus
cpi old_state_enc, 2
breq Iref_minus
rjmp nothing ; блокировка на случай ошибочной комбинации состояний энкодера
case1:
cpi old_state_enc, 3
breq Iref_plus
cpi old_state_enc, 0
breq Iref_minus
rjmp nothing ; блокировка на случай ошибочной комбинации состояний энкодера
case2:
cpi old_state_enc, 0
breq Iref_plus
cpi old_state_enc, 3
breq Iref_minus
rjmp nothing ; блокировка на случай ошибочной комбинации состояний энкодера
case3:
cpi old_state_enc, 2
breq Iref_plus
cpi old_state_enc, 1
breq Iref_minus
rjmp nothing ; блокировка на случай ошибочной комбинации состояний энкодера
Iref_plus:
mov old_state_enc, new_state_enc ; копируем в темп1 новое состояние выводов
inc R14
brne no_0_R14
inc R15
no_0_R14:
rjmp nothing
Iref_minus:
mov old_state_enc, new_state_enc ; копируем в темп1 новое состояние выводов
ldi R16, 1
sub R14, R16
brcc no_R14
sbc R15, R31
no_R14:
nothing:
pop R16 ; восстановим SREG
out SREG, R16 ; восстановим SREG
pop R16 ; восстановим регистр, который использует прерывание
reti
...
;--- это делается в основном цикле программы с интервалом 0,1 секунды ---
;--- обработка результата вращения энкодера ---
ldi R16, par_Iref ; подставим номер параметра "задание тока"
rcall get_parametr ; получим в R21:R20 параметр
add R20, R14
adc R21, R15
cp R20, R31
cpc R21, R31
brpl no_Iref_min
mov R20, R31
mov R21, R31
no_Iref_min:
ldi R16, low(500)
ldi R17, high(500)
cp R16, R20
cpc R17, R21
brcc no_Iref_max
mov R20, R16
mov R21, R17
no_Iref_max:
ldi R30, spisok_par + (par_Iref<<1)
st Z+, R20
st Z, R21
clr R14 ; очищаем регистры счетчика энкодера (приращение к параметру)
clr R15
...
как можно видеть, признаком направления вращения служит само число, точнее, знак числа в счетчике энкодера. при вращении вправо получается положительное число, а при вращении влево - отрицательное.
поэтому в основном цикле пропадает необходимость в анализе направления, и достаточно к параметру просто прибавить получившееся число энкодера.
после суммирования я делаю проверку на минимум, на ноль (в регистре R31 находится ноль), и проверку на максимум (число 500).
после чего я сохраняю новое значение параметра (или старое, если энкодер не вращали) и обнуляю счетчик энкодера.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.