Когда вы нажимаете кнопку, сигнал входного контакта выглядит следующим образом:
___---------------------------------___--__--___-_______
^ here the press starts ^ released ^ bounces (physically)
Иногда могут быть какие-то дребезги даже в начале или шумы в основном сигнале, если контакт недостаточно плотный.
Если бы это был идеальный чистый цифровой сигнал, например:
_______-------------------------------_______________
тогда все, что вам нужно сделать, это сохранить основное предыдущее состояние булавки, проверка того, была ли нажата кнопка, выглядит так:
current reading | previous state | action
-------------------------------------------------------------------
0 | 0 | N/A (or refresh "previous")
1 | 0 | previous=1, call click function
1 | 1 | N/A (or refresh "previous")
0 | 1 | previous=0
Но из-за физического дребезга фактической кнопки включения вам придется кодировать более надежную логику в коде, которая при «предыдущем» изменении бита также сбрасывает некоторый «таймер устранения дребезга», и до этого таймера (счетчик обратного отсчета ) достигнет нуля, «предыдущее» состояние заблокировано, игнорируя любые изменения состояния, считанные с реальной линии ввода-вывода. Так что тогда развенчанная логика повернется:
___---------------------------------___--__--___-_______
^ here the press starts ^ released ^ bounces (physically)
в:
real-time in from button pin:
___-_--_------------------------------___--__--___-____________
"previous" state:
___-----------------------------------_________________________
"debounce timer": (active means "> 0") (preventing change of previous)
___--------------------_______________--------------------_____
action in code:
*1 *2 *3 *4
Действия:
- *1: предыдущая = 1, debounce = ~30 мс, вызов обработчика onClick
- *2: debounce достиг нуля (до этого "предыдущий" был заблокирован)
- *3: предыдущее = 0, устранение дребезга = ~30 мс
- *4: debounce достиг нуля (до этого "предыдущий" был заблокирован - любое нажатие кнопки до сих пор игнорировалось)
И если вы хотите добиться иллюзии «основного не останавливающегося», вам нужно, чтобы обработчик onClick был очень коротким (не блокирующим, не задерживающим) и сохранял любую логику задержки для «основного», которая может бесконечно зацикливаться и обновлять любые таймеры. /counters по мере необходимости (включая таймеры «отката» для каждого входного бита) и использовать дополнительную сложную логику для запуска коротких и быстрых функций при определенных событиях, запускаемых каким-либо таймером или состоянием ввода.
РЕДАКТИРОВАТЬ: некоторые примечания к новому коду, которые я пытался частично понять.
У меня проблема с вашей очень простой архитектурой этой штуки, похоже, вы храните все значения, связанные с этой кнопкой, в фиксированных регистрах, что делает LOOP_BUTTON
фиксированным для конкретного контакта/кнопки, а не для повторного использования в другом месте.
Я бы предложил вам разрабатывать подпрограммы более общим способом, настраивая конкретные функции с помощью аргументов, а не с помощью кода подпрограммы (всякий раз, когда это имеет смысл и результирующий код достаточно прост).
Я бы разработал его таким образом, чтобы в одном регистре был адрес экземпляра объекта кнопки, а в другом регистре значение вывода, например:
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: мне не удалось проверить, является ли это допустимым синтаксисом AVR asm или даже работает ли этот код, поэтому используйте его в основном как источник «идеи» (я использовал эту ссылку для написания инструкций и их синтаксиса: http://www.avr-tutorials.com/sites/default/files/Instruction%20Set%20Summary.pdf ):
... main loop code continues with button tests...
; read current state of PINB into R23
in R23,PINB
; button 1 check (data in memory at button1_data, pin: bit 0 of PINB)
ldi ZH,high(button1_data)
ldi ZL,low(button1_data)
ldi R24,0b00000001 ; bit 0 bitmask
rcall BUTTON_HANDLER ; R24 = 0/1 when onClick should be called
sbrc R24,0 ; skip onClick call, when result was 0
rcall BTN_1_ON_CLICK ; BTN1 was clicked, call onClick handler
; ^^ must preserve R23!
; button 2 check (data in memory at button2_data, pin: bit 1 of PINB)
ldi ZH,high(button1_data)
ldi ZL,low(button1_data)
ldi R24,0b00000010 ; bit 1 bitmask
rcall BUTTON_HANDLER ; R24 = 0/1 when onClick should be called
sbrc R24,0 ; skip onClick call, when result was 0
rcall BTN_2_ON_CLICK ; BTN2 was clicked, call onClick handler
; button 3 check (data in memory at button3_data, pin: bit 2 of PINB)
ldi ZH,high(button1_data)
ldi ZL,low(button1_data)
ldi R24,0b00000100 ; bit 2 bitmask
rcall BUTTON_HANDLER ; R24 = 0/1 when onClick should be called
sbrc R24,0 ; skip onClick call, when result was 0
rcall BTN_3_ON_CLICK ; BTN3 was clicked, call onClick handler
... continuation of main loop ...
Данные кнопки определены в .dseg как:
.dseg
button1_data:
.byte 2 ; two bytes of storage per button
button2_data:
.byte 2 ; two bytes of storage per button
button3_data:
.byte 2 ; two bytes of storage per button
Не забудьте очистить их во время инициализации программы, примерно так:
main:
; during program init don't forget to clear button data in memory
clr R1 ; R1 = 0
sts button1_data, R1
sts button1_data+1, R1 ; Not sure if this is legal syntax :/
sts button2_data, R1
sts button2_data+1, R1
sts button3_data, R1
sts button3_data+1, R1
Наконец, процедура обработчика ввода кнопки, которая будет принимать R23
текущее состояние кнопок (пинов), R24
— это битовая маска кнопки для проверки, а Z
должна указывать на данные кнопки. Он вернет R24 = 0/1
в состоянии, была ли нажата кнопка:
; button input handler:
; R23 = pins state (preserved), R24 = pin bitmask, Z = button data
; returns R24 = 0/1 when onClick should be called
; button data structure: +0 = previous state, +1 = debounce timer
BUTTON_HANDLER:
; check debounce timer first, if > 0, state is locked
ldd R0,Z+1 ; debounce timer is second byte
tst R0 ; is it zero?
breq BUTTON_HANDLER_DEBOUNCE_OK
; debounce timer is > 0, just decrement it and ignore input
dec R0
std Z+1,R0
clr R24 ; R24 = 0 (no click)
ret
BUTTON_HANDLER_DEBOUNCE_OK:
; process input
ld R0,Z ; R0 = previous state of bit
and R24,R23 ; R24 = current state
cpse R0,R24 ; if previous == current, skip change
rjmp BUTTON_HANDLER_CHANGE_DETECTED
clr R24 ; R24 = no click (no change on pin)
ret
BUTTON_HANDLER_CHANGE_DETECTED:
st Z,R24 ; store new state into "previous" data
; EDIT - bugfix added, debounce timer need to be set too!
ldi R0,DEBOUNCE_DELAY ; amount of main_loops to pass
std Z+1,R0
tst R24 ; when new state is zero => released button
breq BUTTON_HANDLER_RELEASED ; return 0
ldi R24,1 ; when new state is non-zero, return 1 (click!)
BUTTON_HANDLER_RELEASED:
ret
Затем, когда какая-либо кнопка была нажата, вы вызовете свою процедуру onClick для конкретной кнопки:
; button 1 onClick handler (must preserve R23 (input pins of buttons)).
BTN_1_ON_CLICK:
; TODO anything you wish to do upon BTN1 pressed
ret
И определите некоторую постоянную времени устранения дребезга DEBOUNCE_DELAY
, которая представляет собой количество main_loops, которые нужно пройти, пока кнопка не начнет реагировать на текущее состояние (если вы выполняете цикл, например, один раз в 1 мс, тогда вы можете попробовать DELAY 30).
О, подождите, так что вместо того, чтобы комментировать ваш код, я просто создал свой собственный... даже если я даже не могу проверить, работает ли он... извините. :)
(и если это работает, то я предполагаю, что это не очень эффективная сборка AVR, так как я чувствовал, что всегда сталкиваюсь с проблемами с точки зрения x86 и пропускаю инструкции, чтобы помочь мне, например, почему у AVR так много инструкций для прямой установки флагов (ноль/перенос/... все из них), но не наоборот, чтобы установить регистр в 0/1 в соответствии с флагом и т. д.)
Пожалуйста, дайте мне знать, если это сработало для вас в той или иной форме + предложите исправления в моем коде, чтобы сделать его действительным, чтобы сделать этот ответ немного лучше (если вы не ответите, я, вероятно, со временем удалю его, поскольку я боюсь это может быть совершенно неправильно).
person
Ped7g
schedule
22.11.2017