Дисплеи, датчики и прочие функциональные узлы, управляемые МК.
Ответить

обработка инкрементального энкодера на прерываниях (asm)

Чт дек 06, 2012 22:42:56

Приветствую сообщество.
Пытаюсь нарулить на ассемблере код обработки энкодера, оно вроде функционирует, но с большими глюками.

Вкратце: энкодер подключен к D1, D2. 4 светодиода подключены к PC0-PC3 (первые 2 подключены к -, вторые - к плюсу; так сделано на отладочной плате), поэтому в регистре перед выводом в порт инвертируются 2 последних бита с помощью. EOR.
Задача программы: при повороте вправо увеличиваем счетчик (если насчитываем 15, то ничего не делаем) и выводим в PortC. При движении влево уменьшаем счетчик (при достижении 0 ничего не делаем). Задержка по переполнению Таймера0 (с делителем 256 получается 16 мс при 4 МГц).
Дело не в дребезге, посмотрел осциллографом - фронты почти идеальные. Пробовал уменьшать/увеличивать величину задержки - не помогает.

При быстром вращении глючит (моргают ненужные светодиоды), при вращении назад ситуация такая же плюс вообще начинает инкременировать счетчик если быстро крутить.
Алгоритм:

Зашли в обработчик INT0
Запретили локально INT0
Пощупали второй канал
+1 или -1 (если 1, то проверяем счетчик на равенство 0b00001111, если равно, то выходим из прерывания, если нет, то увеличиваем счетчик; если 0, то проверяем счетчик на 0b00000000, если равно, то выходим, если нет, то уменьшаем на 1)
Запускаем Т0 с предделителем 256
Вышли из обработчика

вошли в обработчик прерывания Т0.
останавливаем таймер
разрешаем INT0
выходим из прерывания

Прерывание запрещаем сразу после входа в процедуру обработки прерывания.

Пробовал сбрасывать флаги внешних прерываний и в обработчике INT0, и в обработчике таймера перед разрешением INT0. Не помогает.
В аттаче проект студии.
Вложения
encoder_test.zip
(7.27 KiB) Скачиваний: 262

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 08:09:31

pryanic писал(а):Пробовал сбрасывать флаги внешних прерываний и в обработчике INT0, и в обработчике таймера перед разрешением INT0. Не помогает.

Флаг сбрасывался в обработчике таймера записью "1"?

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 09:47:07

Да

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 09:52:58

в Вашем листинге в упор не вижу сброса флагов внешнего прерывания:
Спойлер
Код:
EXT_INT0:   push   temp               
         in      temp,   SREG
         push   temp               ;сохраняем temp и SREG в стеке
         clr      temp
         out      GICR,   temp         ;запрещаем INT0
         sbic   pinD,   1            ;если на проверяемом пине высокий уровень,
         rjmp   move_right            ;идем по метке вправо, если низкий,
         rjmp   move_left            ;то влево.
move_right:   cpi      count,   0b00001111
         breq   exit_INT0   
         inc      count
         eor      count,   invert
         out      PortC,   count
         rjmp   exit_INT0
move_left:   cpi      count,   0b00000000
         breq   exit_INT0
         dec      count
         eor      count,   invert
         out      portC,   count

exit_INT0:   ldi      temp,   0b00000101      ;запускаем таймер0 с предделителем 256
         out      TCCR0,   temp         ;задержка при 4 MHz примерно 16 мс.   
         pop      temp
         out      SREG,   temp
         pop      temp
         reti

         
TIM0_OVF:   push   temp               ;в прерывани только лишь разрешаем INT0
         in      temp,   SREG
         push   temp
         clr      temp
         out      TCCR0,   temp         ;останавливаем Таймер0   
         ldi      temp,   0b01000000
         out      GICR,   temp         ;разрешаем INT0
         pop      temp
         out      SREG,   temp
         pop      temp
         reti      
;*****************************END INTERRUPTS****************************************            


RESET:      ldi    temp,   high(RAMEND)   ; Main program start
         out    SPH,   temp          ; Set Stack Pointer to top of RAM
         ldi    temp,   low(RAMEND)
         out    SPL,   temp
         ser      temp
         out      ddrc,   temp         ;C на вывод
         ldi      temp,   0b00001100
         out      portC,   temp         ;гасим светодиоды
         clr      temp
         out      ddrd,   temp         ;D на ввод
         ser      temp
         out      portd,   temp         ;потяжка
         ldi      temp,   0b00001100
         mov      invert,   temp


         ldi      temp,   0b00000001
         out      timsk,   temp         ;разрешаем перывание таймера 0 по переполнению
         ldi      temp,   0b00000010
         out      mcucr,   temp         ;устанавливаем прерывание INT0 по спаду.
         ldi      temp,   0b01000000
         out      GICR,   temp         ;разрешаем прерывание INT0
         clr      count
         sei                        ;глобальное разрешение прерываний
Gcykle:      rjmp   gcykle
ГДЕ?

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 10:11:47

Пардон. Приложил устаревшую версию листинга.

Код:
TIM0_OVF:   push   temp               
         in      temp,   SREG
         push   temp
         clr      temp
         out      TCCR0,   temp         ;останавливаем Таймер0   
         ser      temp
         out      GIFR,   temp         ;очищаем регистр флагов внешных прерываний
         ldi      temp,   0b01000000
         out      GICR,   temp         ;разрешаем INT0
         pop      temp
         out      SREG,   temp
         pop      temp
         reti      

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 12:29:14

TIM0_OVF: push temp
Код:
     in      temp,   SREG
         push   temp
         clr      temp
         out      TCCR0,   temp         
         ldi      temp,   0b01000000 ТАК ПРАВИЛЬНО
         out      GIFR,   temp         ;очищаем регистр флагов внешных прерываний
         ldi      temp,   0b01000000
         out      GICR,   temp         ;разрешаем INT0
         pop      temp
         out      SREG,   temp
         pop      temp
         reti     

Вы отлаживаетесь в студио. Поставьте breake на ret и посмотрите на флаг INT0

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 12:53:38

Попробовал Ваш вариант очистки флагов прерываний.
В железе результат не изменился.

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 13:31:29

А что с INT0 при выходе?

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 13:43:51

А что с INT0 при выходе?


При выходе из прерывания INT0? Все нормально.

http://img593.imageshack.us/img593/4741/int0r.jpg

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 14:19:21

При выходе из прерывания TIM0_OVF/ Я вроде как выше писал про него

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 14:29:59

Флаги чистые.

http://imageshack.us/photo/my-images/21/tim0.jpg/

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 14:49:46

Как то я не удивлён , в симмуляторе они всегда чистые. А вот в реальности?

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 14:52:14

В симуляторе все отлично. А вот в железе не могу понять в чем загвоздка.

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 15:08:51

Симмулятор - это фантастическая реальность , там нет дребезгов , задержек в проводах , уходов кварца ,емкостей ,резисторов, задержек , и прочей дребедени которым полна реальная жизнь. Подключайте свою плату в режиме debug и смотрите реальный мир. Или можете освободить один пин порта - подкл светодиод и по условиям введенные в основной код зажигать или гасить его.

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 15:16:22

Проблема здесь мне кажется именно в подавлении дребезга... слишком грубая задержка. На момент фиксации после подавления дребезга может пройти неопределенное количество времени за которое вторая шторка может успеть перекрыться и алгоритм посчитает обратное направление.

Дребезг нужно подавлять только по одной шторке, но фиксировать значение второй шторки по импульсам непосредственно от первой, идеальный вариант - аппаратный синхронный D-триггер, его конечно можно реализовать программно, но надо иметь в виду что прерывание по изменению уровня первой шторки должно иметь наивысший приоритет, иначе будет упущен момент фиксации второй шторки и возможны глюки.

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 15:56:40

Я пробовал делать по-другому: в обработчике внешнего прерывания только запускал таймер, а все вычисления и вывод делал в обработчике таймера. Получилось еще более глючно.

Дребезг нужно подавлять только по одной шторке, но фиксировать значение второй шторки по импульсам непосредственно от первой


Сейчас у меня вроде так и происходит. Сразу после срабатывания внешнего прерывания идут вычисления и вывод информации, а потом антидребезговая задержка.

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 16:08:08

pryanic писал(а):В симуляторе все отлично.
Не всё. Попробуйте.
Спойлер
Код:
;программа обработки энкодера
;Fcl = 4MHz
      
         .include      "m8def.inc"
         .def         temp   =   r16
         .def         count   =   r17
         .def         invert   =   r0
         .cseg
         .org   0
         rjmp RESET                   ; Reset Handler
         rjmp EXT_INT0                ; IRQ0 Handler
         reti;rjmp EXT_INT1             ; IRQ1 Handler
         reti;rjmp TIM2_COMP          ; Timer2 Compare Handler
         reti;rjmp TIM2_OVF             ; Timer2 Overflow Handler
         reti;rjmp TIM1_CAPT          ; Timer1 Capture Handler
         reti;rjmp TIM1_COMPA          ; Timer1 CompareA Handler
         reti;rjmp TIM1_COMPB          ; Timer1 CompareB Handler
         reti;rjmp TIM1_OVF             ; Timer1 Overflow Handler

TIM0_OVF:
;   push   temp               ;в прерывани только лишь разрешаем INT0
;         in      temp,   SREG
;         push   temp
;         clr      temp
         out      TCCR0,ZL         ;останавливаем Таймер0
         OUT      TCNT0,ZL   
         ldi      temp,   0b01000000
         out      GICR,   temp         ;разрешаем INT0
         OUT      GIFR,TEMP
;         pop      temp
;         out      SREG,   temp
;         pop      temp
         reti      

;         rjmp TIM0_OVF                ; Timer0 Overflow Handler
;         reti;rjmp SPI_STC             ; SPI Transfer Complete Handler
;         reti;rjmp USART_RXC          ; USART RX Complete Handler
;         reti;rjmp USART_UDRE          ; UDR Empty Handler
;         reti;rjmp USART_TXC          ; USART TX Complete Handler
;         reti;rjmp ADC                ; ADC Conversion Complete Handler
;         reti;rjmp EE_RDY             ; EEPROM Ready Handler
;         reti;rjmp ANA_COMP             ; Analog Comparator Handler
;         reti;rjmp TWSI                ; Two-wire Serial Interface Handler
;         reti;rjmp SPM_RDY             ; Store Program Memory Ready Handler
;***************************INTERRUPTS**********************************************
EXT_INT0:
;   push   temp               
;         in      temp,   SREG
;         push   temp               ;сохраняем temp и SREG в стеке
;         clr      temp
         out      GICR,ZL         ;запрещаем INT0
         sbic   pinD,   1            ;если на проверяемом пине высокий уровень,
         rjmp   move_right            ;идем по метке вправо, если низкий,
         rjmp   move_left            ;то влево.
move_right:   cpi      count,   0b00001111
         breq   exit_INT0   
         inc      count
         eor      count,   invert
         out      PortC,   count
         rjmp   exit_INT0
move_left:   cpi      count,   0b00000000
         breq   exit_INT0
         dec      count
         eor      count,   invert
         out      portC,   count

exit_INT0:
   ldi      temp,   0b00000001      ;запускаем таймер0
;   ldi      temp,   0b00000101      ;запускаем таймер0 с предделителем 256
         out      TCCR0,   temp         ;задержка при 4 MHz примерно 16 мс.   
;         pop      temp
;         out      SREG,   temp
;         pop      temp
         reti

;*****************************END INTERRUPTS****************************************            


RESET:
      ldi    temp,   high(RAMEND)   ; Main program start
         out    SPH,   temp          ; Set Stack Pointer to top of RAM
         ldi    temp,   low(RAMEND)
         out    SPL,   temp
;*************************************************
   SER   ZH
   CLR   ZL
;*************************************************
;         ser      temp
         out      ddrc,ZH            ;C на вывод
;         ldi      temp,   0b00001100
         out      portC,ZL         ;гасим светодиоды
;         clr      temp
         out      ddrd,ZL            ;D на ввод
;         ser      temp
         out      portd,ZH         ;потяжка
         clr      count
;         ldi      temp,   0b00001100
         CLR      invert
;         mov      invert,   temp


         ldi      temp,   0b00000001
         out      timsk,   temp         ;разрешаем перывание таймера 0 по переполнению
         ldi      temp,   0b00000010
         out      mcucr,   temp         ;устанавливаем прерывание INT0 по спаду.
         ldi      temp,   0b01000000
         out      GICR,   temp         ;разрешаем прерывание INT0
;         clr      count
   OUT   GIFR,ZH
   OUT   TIFR,ZH
         sei                        ;глобальное разрешение прерываний
Gcykle:      rjmp   gcykle

Ну и совет. Избавляйтесь от тяжелого наследия аккумуляторных контроллеров. AVR тем и хорош, что у него есть 32 регистра. :oops:

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 16:23:10

Это зачем закоментировали?
Код:
 ;         out      SREG,   temp
;         in      temp,   SREG

А то TC привыкнет и совсем не будет делать.

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 16:58:02

Тоже хотел так когда-то делать. Потом сделал опросом с фиксированной частотой (около 1кГц). Т.е. прерывания с частотой 1кГц делаются таймером, а дальше обработчик прерывания опрашивает энкодер и решает, крутится ли он и в какую сторону. Главное, придумать алгоритм подавления дребезга, т.к. задержки тут не прокатят, как в случае с обычными кнопками (т.к. дребезг может длиться гораздо дольше, чем длительность импульсов энкодера). Вот тут выкладывали программу опроса энкодера с подавлением дребезга за счет анализа двух последовательных состояний, правда, на Си:

viewtopic.php?f=20&t=32&start=6980

Re: обработка инкрементального энкодера на прерываниях (asm)

Пт дек 07, 2012 22:26:57

akl, а почему запускаете таймер без предделителя. Задержка меньше 1 мс получается.
Ответить