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

Дисплеи, датчики и прочие функциональные узлы, управляемые МК.
Ответить
Открыл глаза
Аватара пользователя
Сообщения: 59
Зарегистрирован: Вт янв 17, 2012 20:06:08

Сообщение pryanic »

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

Вкратце: энкодер подключен к 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 КБ) 288 скачиваний
Реклама
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

pryanic писал(а):Пробовал сбрасывать флаги внешних прерываний и в обработчике INT0, и в обработчике таймера перед разрешением INT0. Не помогает.
Флаг сбрасывался в обработчике таймера записью "1"?
Реклама
Открыл глаза
Аватара пользователя
Сообщения: 59
Зарегистрирован: Вт янв 17, 2012 20:06:08

Сообщение pryanic »

Да
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1900
Зарегистрирован: Сб фев 21, 2009 13:11:40
Откуда: Москва

Сообщение ibiza11 »

в Вашем листинге в упор не вижу сброса флагов внешнего прерывания:
Спойлер

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

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
ГДЕ?
Ставим плюсы: )
Реклама
Эиком - электронные компоненты и радиодетали
Открыл глаза
Аватара пользователя
Сообщения: 59
Зарегистрирован: Вт янв 17, 2012 20:06:08

Сообщение pryanic »

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

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

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		
Реклама
Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

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
Реклама
Открыл глаза
Аватара пользователя
Сообщения: 59
Зарегистрирован: Вт янв 17, 2012 20:06:08

Сообщение pryanic »

Попробовал Ваш вариант очистки флагов прерываний.
В железе результат не изменился.
Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

А что с INT0 при выходе?
Открыл глаза
Аватара пользователя
Сообщения: 59
Зарегистрирован: Вт янв 17, 2012 20:06:08

Сообщение pryanic »

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

http://img593.imageshack.us/img593/4741/int0r.jpg
Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

При выходе из прерывания TIM0_OVF/ Я вроде как выше писал про него
Открыл глаза
Аватара пользователя
Сообщения: 59
Зарегистрирован: Вт янв 17, 2012 20:06:08

Сообщение pryanic »

Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

Как то я не удивлён , в симмуляторе они всегда чистые. А вот в реальности?
Открыл глаза
Аватара пользователя
Сообщения: 59
Зарегистрирован: Вт янв 17, 2012 20:06:08

Сообщение pryanic »

В симуляторе все отлично. А вот в железе не могу понять в чем загвоздка.
Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

Симмулятор - это фантастическая реальность , там нет дребезгов , задержек в проводах , уходов кварца ,емкостей ,резисторов, задержек , и прочей дребедени которым полна реальная жизнь. Подключайте свою плату в режиме debug и смотрите реальный мир. Или можете освободить один пин порта - подкл светодиод и по условиям введенные в основной код зажигать или гасить его.
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич

Сообщение Alexeyslav »

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

Дребезг нужно подавлять только по одной шторке, но фиксировать значение второй шторки по импульсам непосредственно от первой, идеальный вариант - аппаратный синхронный D-триггер, его конечно можно реализовать программно, но надо иметь в виду что прерывание по изменению уровня первой шторки должно иметь наивысший приоритет, иначе будет упущен момент фиксации второй шторки и возможны глюки.
Контактная информация:
Открыл глаза
Аватара пользователя
Сообщения: 59
Зарегистрирован: Вт янв 17, 2012 20:06:08

Сообщение pryanic »

Я пробовал делать по-другому: в обработчике внешнего прерывания только запускал таймер, а все вычисления и вывод делал в обработчике таймера. Получилось еще более глючно.
Дребезг нужно подавлять только по одной шторке, но фиксировать значение второй шторки по импульсам непосредственно от первой
Сейчас у меня вроде так и происходит. Сразу после срабатывания внешнего прерывания идут вычисления и вывод информации, а потом антидребезговая задержка.
akl
Друг Кота
Сообщения: 4468
Зарегистрирован: Пт мар 07, 2008 06:54:43
Откуда: Ижевск

Сообщение akl »

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:
Держит паяльник хвостом
Аватара пользователя
Сообщения: 906
Зарегистрирован: Ср мар 28, 2012 21:45:24
Откуда: ВО

Сообщение ILYAUL »

Это зачем закоментировали?

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

 ;         out      SREG,   temp
;         in      temp,   SREG
А то TC привыкнет и совсем не будет делать.
Друг Кота
Сообщения: 6014
Зарегистрирован: Чт ноя 26, 2009 11:16:50
Откуда: Москва

Сообщение SmarTrunk »

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

http://radiokot.ru/forum/viewtopic.php? ... start=6980
Открыл глаза
Аватара пользователя
Сообщения: 59
Зарегистрирован: Вт янв 17, 2012 20:06:08

Сообщение pryanic »

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

Вернуться в «Периферия»