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

Подавление дребезга - как лучше сделать

Добавлено: Чт фев 12, 2015 09:40:42
asidalv
Удалось, в целом, побороть дребезг кнопок, но не на 100% . Как улучшить?

Сделано в общем пока так: стандартная 16 кнопочная клавиатура (0-9 *, #, F1 - F4) из микриков имеет 4 вертикальные и 4 горизонтальные линии. В режиме ожидания на верт. линиях присутствует '1' а горизонтальные линии - читаются. От горизонтальных линий (при отпущенных кнопках на них стоит 0) через диоды подается на входы 2,6 ne555 , а с выхода - инвертированный сигнал на линию INT0 проца. В момент нажатия любой кнопки на входе ne555 появляется 1, на выходе, соответсвенно, 0 что и вызывает обработчик прерывания который взводит флаг в r12. Далее, в теле программы этот флаг анализируется и при его установки считывается матрица клавиатуры (функция inkey ниже). Между флагом и считываем введена задержка 10 мс.

В общем, все работает. Функция поддерживает такие события как rate и delay (аналогичные клавиатуре PC) , но иногда - все же возникает дребезг и введенный символ задваивается... Как побороть до конца?

Вот исходники клавиатурного модуля и обработчиков прерываний:
Спойлер.equ key_delay = 5 ; параметр delay клавиатуры 0.5s
.equ key_rate = 1 ; параметр rate клавиатуры 0.1s

; функция inkey() возвращает в r16 символ нажатой клавиши (0-9, A-D, *, #)
; в остальных случаях возвращает 0 если ниодна клавиша не была нажата
; при r16 != 0 подавление дребезга не делается, иногда это необходимо

inkey: push r21
push r17
mov r17, r12 ; состояние флага INT0 в r12
cpi r17, 0 ; все сброшены?
breq no_key ; да - событий нет, на выход

andi r17, 0b11110000 ; флаги клавиатуры
cpi r17, 0b10000000 ; установлен только флаг INT?
breq key_press_first ; да - первое нажатие клавиши

rcall key_down ; читаем состояние линий клавиатуры
cpi r16, 0 ; клавишу отпустили или дребезг?
breq key_up ; да - снимаем все флаги и выходим
rcall get_scancode
cpi r17, 0b11000000 ; а до этого было первое нажатие клавиши?
brne delay_rate ; не первое, тогда delay а затем rate

ldi r17, 0b10111111 ; сброс флага RDOK
and r12, r17
ldi r17, 0b10100000 ; и установка флагов INT+DELAY
or r12, r17
ldi r17, key_delay
rcall get_state_timer
rjmp no_key ; и на выход, с кодом клавиши в r16

delay_rate:
cpi r17, 0b10110000 ; флаги INT+DELAY+RATE установлены?
breq event_rate
cp r10, r13
brne no_rate
ldi r17, 0b10110000 ; установка флагов INT+DELAY+RATE
or r12, r17
ldi r17, key_rate ; значение параметра rate
rcall get_state_timer
rjmp no_key

event_rate:
cp r10, r13
brne no_rate
ldi r17, key_rate ; значение параметра rate
rcall get_state_timer
rjmp no_key

key_up:
ldi r16, 0b00001111 ; сброс всех флагов клавиатуры
and r12, r16
rcall keyb_h_line ; возвращение порта C в исходное состояние
no_rate:
clr r16
rjmp no_key

key_press_first:
cpi r16, 0 ; если при вызове был не 0
brne no_dreb ; дребезг не подавляем
ldi r16, 8
rcall delay_10 ; задержка для подавления дребезга
no_dreb:
rcall key_down ; читаем матрицу клавиатуры 4x4
cpi r16, 0
breq no_key ; дребезг, выходим сразу
ldi r16, 0b11000000
or r12, r16 ; установка флагов INT+RDOK
rcall keyb_h_line ; возвращение порта C в исходное состояние
clr r16
;----- выход из inkey, в r16 код клавиши
no_key: pop r17
pop r21
ret

;-----------------------
; текущее состояние младшего байта таймера
get_state_timer:
mov r13, r10 ; сохранение состояния таймера
add r13, r17 ; + к нему значение параметра
ret

;-----------------------
; чтение матрицы клавиатуры
key_down:
push r17
clr r16
sbic PIND, 4
ori r16, 0b00010000
sbic PIND, 5
ori r16, 0b00100000
sbic PIND, 6
ori r16, 0b01000000
sbic PIND, 7
ori r16, 0b10000000

; порт C тут должно быть перед портом D!!!
ldi r17, 0b00110000
out DDRC, r17

ldi r17, 0b11110011
out DDRD, r17

ldi r17, 0b00000001
or r17, r16
out PORTD, r17

sbic PINC, 0
ori r16, 0b00000001
sbic PINC, 1
ori r16, 0b00000010
sbic PINC, 2
ori r16, 0b00000100
sbic PINC, 3
ori r16, 0b00001000

rcall keyb_h_line ; возврат портов C и D в исходное состояние
pop r17
ret
;------------------------------------------------------------------------
;функция по битовой маске из r16 возвращает символ нажатой клавиши в r16
get_scancode:
cpi r16, $18
breq key1_
cpi r16, $28
breq key2_
cpi r16, $48
breq key3_
cpi r16, $88
breq keyA_
cpi r16, $14
breq key4_
cpi r16, $24
breq key5_
cpi r16, $44
breq key6_
cpi r16, $84
breq keyB_
cpi r16, $12
breq key7_
cpi r16, $22
breq key8_
cpi r16, $42
breq key9_
cpi r16, $82
breq keyC_
cpi r16, $10
breq keyz_
cpi r16, $20
breq key0_
cpi r16, $40
breq keyr_
cpi r16, $80
breq keyD_
rjmp key_end
key1_: ldi r16, '1'
rjmp key_end
key2_: ldi r16, '2'
rjmp key_end
key3_: ldi r16, '3'
rjmp key_end
keyA_: ldi r16, 'A'
rjmp key_end
key4_: ldi r16, '4'
rjmp key_end
key5_: ldi r16, '5'
rjmp key_end
key6_: ldi r16, '6'
rjmp key_end
keyB_: ldi r16, 'B'
rjmp key_end
key7_: ldi r16, '7'
rjmp key_end
key8_: ldi r16, '8'
rjmp key_end
key9_: ldi r16, '9'
rjmp key_end
keyC_: ldi r16, 'C'
rjmp key_end
keyz_: ldi r16, $2A ; звезда
rjmp key_end
key0_: ldi r16, '0'
rjmp key_end
keyr_: ldi r16, $23 ; решетка
rjmp key_end
keyD_: ldi r16, 'D'
rjmp key_end
key_end:
ret

; процедура включения горизонтальных линий клавиатуры
keyb_h_line:
push r16
ldi r16, 0b00000011
out DDRD, r16
ldi r16, 0b00000001
out PORTD, r16
ldi r16, 0b00101111
out DDRC, r16
in r16, PORTC
ori r16, 0b00101111
out PORTC, r16
pop r16
ret

;-------
TIM1_COMPA: ; обработчик таймера 1 (1 раз в 100 мсек)
cli
push r16
in r16, SREG
push r16
ldi r16, 1
add r10, r16
clr r16
adc r11, r16
pop r16
out SREG, r16
pop r16
sei
reti

;-------
EXT_INT0: ; обработчик прерывания клавиатуры
cli
push r16
in r16, SREG
push r16
ldi r16, 0b10000000
or r12, r16 ; старший бит - флаг возникновения прерывания
pop r16
out SREG, r16
pop r16
sei
reti

Re: Подавление дребезга - как лучше сделать

Добавлено: Чт фев 12, 2015 11:37:41
otest
Не догоняю для чего 555 ? Любой дребезг устраняется програмно.

Re: Подавление дребезга - как лучше сделать

Добавлено: Чт фев 12, 2015 11:46:22
asidalv
555я формирует сигнал низкого уровня на линии INT0 при нажатии на любую клавишу. В этом случае, программный код несколько упрощается и становится проще сделать такие вещи как повтор нажатия кливиши при ее удержании (rate и delay) т.е. получается клава работает почти так же как и компе. Логика такая же, как int16 в биосе PC.