Сборка, выполняющая состояние ButtonClick только один раз

Я работаю над простым проектом с кнопками, используя микроконтроллер Atmega2560. У меня проблема с кнопками. Когда я нажимаю кнопку, основной цикл перестает работать, и функция кнопки продолжает работать бесконечное количество раз, пока я нажимаю кнопку. Основной цикл не должен останавливаться, когда я нажимаю кнопку. И функция кнопки должна быть запущена только один раз. Как я могу это сделать ?

.def LEDS = R16
.def LED_DATA = R21

.org 0
    rjmp MAIN

MAIN:
    ldi LEDS, 0xFF  ; 0xFF = 1111 1111

    ldi LED_DATA, 0x01

    out DDRC, LEDS  ; PORTC
    sbi PORTB, 0
    sbi PORTB, 1

LOOP_MAIN:
    sbis PINB, 0        ; If PORTB-0's pin = 1, skip next
    rjmp BUTTON_CLICK_H

    sbis PINB, 1
    rjmp BUTTON_CLICK_Y 

    call DELAY
    out PORTC, LED_DATA

    lsl LED_DATA
    brcc SHIFT_0_IN     ;if carry not set, the MSB was not set, so skip setting the LSB
    ori LED_DATA, 1

    SHIFT_0_IN:         ; keep LSB as 0 -> do nothing, just continue
    rjmp LOOP_MAIN

    END:
    rjmp LOOP_MAIN


BUTTON_CLICK_H:
    lsl LED_DATA
    cpi LED_DARA, 0x40
    breq SPEED_RESET
    rjmp SPEED_END
    SPEED_RESET:
    ldi LED_SPEED, 0x04
    SPEED_END:
    rjmp LOOP_MAIN

РЕДАКТИРОВАНИЕ: (26.11.2017)

Я написал простой кнопочный контроллер. Цель этого контроллера - проверить, нажата ли кнопка или нет, или удерживается нажатой. код кнопки должен работать только один раз. Но он не работает, когда я нажимаю кнопку. Где я делаю ошибки?

.def BTN_STATE_FIRST = R23
.def BTN_STATE_CHANGED = R24
.def BTN_STATE_PINB = R25
.def BTN_STATE_TEMP = R26

LOOP_MAIN:
    out PORTC, LED_DATA         
    call LOOP_BUTTON        
    call DELAY  

    ; ...Codes

    rjmp LOOP_MAIN

LOOP_BUTTON:

    ; (Is the button pressed, not pressed or kept pressed?) Controller

    in BTN_STATE_PINB, PINB     ; Read PINB data and put it current state in BTN_STATE_PINB
    mov BTN_STATE_TEMP, BTN_STATE_PINB  ; Move it to BTN_STATE_TEMP
    eor BTN_STATE_PINB, BTN_STATE_FIRST ; XOR BTN_STATE_PINB and BTN_STATE_FIRST. And write result to the BTN_STATE_PINB
    mov BTN_STATE_FIRST, BTN_STATE_TEMP ; Move it the BTN_STATE_FIRST
    breq BUTTON_PRESSED
    brne BUTTON_NOTPRESSED

    BUTTON_PRESSED:
    cpi BTN_STATE_PINB, 0x01    ; 1st button 0x01, 2nd button 0x02, 3rd button 0x04
    breq BUTTON_CHANGED
    rjmp BUTTON_NOTPRESSED

    BUTTON_CHANGED:
    cpi BTN_STATE_CHANGED, 0x01 ; When pressed and held, have been processed before ? 0x01 true, 0x00 false
    breq BUTTON_UP              ; If yes, branch to BUTTON_UP
    brne BUTTON_DOWN            ; Otherwise, branch to BUTTON_DOWN

    BUTTON_UP:
    dec BTN_STATE_CHANGED       ; Decrement the BTN_STATE_CHANGED to 0x00

    ldi LED_DATA, 0x40

    rjmp BUTTON_END

    BUTTON_DOWN:
    inc BTN_STATE_CHANGED       ; Increment the BTN_STATE_CHANGED to 0x01

    ldi LED_DATA, 0x80


    BUTTON_END:
    BUTTON_NOTPRESSED:

    ret

person Dentrax    schedule 22.11.2017    source источник
comment
Если процессор не имеет 2+ ядер, вы не можете избежать остановки main во время выполнения функции кнопки. ЦП может выполнять только одну инструкцию (или только пару из них для больших современных ЦП, но AVR, вероятно, выполняет не более одной инструкции за цикл). Конечно, должна быть возможность организовать код таким образом, чтобы функция кнопки выполнялась за микросекунды. , а основное будет продолжаться оттуда, так что для человеческого восприятия это будет так, как если бы основное было непрерывным.   -  person Ped7g    schedule 22.11.2017


Ответы (1)


Когда вы нажимаете кнопку, сигнал входного контакта выглядит следующим образом:

___---------------------------------___--__--___-_______
   ^ 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
comment
Спасибо за пример и теорию. Но как мне настроить это в сборке? - person Dentrax; 22.11.2017
comment
ну... большая часть из них переводится в ассемблерные инструкции почти 1:1 (больше похоже на соотношение 1:4). Я не знаю инструкций AVR, поэтому мне было бы проще просто просмотреть, как вы пытаетесь, и дать вам подсказки... Но немного непонятно, в чем ваша проблема, хотите ли вы научиться программированию на ассемблере AVR или нет? Если да, это должно быть достаточно простой задачей, чтобы написать ее самостоятельно, я могу попытаться разбить ее даже на более мелкие подробные теоретические шаги, если вы чувствуете, что это поможет вам. Но я не хочу на самом деле кодировать это в AVR, это заняло бы у меня около 30-60 минут, и мне пришлось бы прочитать много документов, чтобы изучить AVR. - person Ped7g; 22.11.2017
comment
@FurkanTürkal: я особенно не знаком с этими MC-подобными процессорами, они часто используют дизайн, совершенно отличный от обычных инструкций CISC / RISC (иногда даже с аккумулятором или только с ограниченным косвенным доступом к памяти), и ваш источник выглядит неполным, например, я понятия не имею, что такое PINB, из-за чего еще сложнее угадать, какие инструкции поддерживаются и что в вашем коде является прямым вводом-выводом, что такое переменная памяти (адрес в памяти) и что константа определена при компиляции время. - person Ped7g; 22.11.2017
comment
Если вы планируете отредактировать свой вопрос, чтобы сделать этот источник более полным + читаемым, теперь я еще раз посмотрел и увидел это .def LEDS = R16 ... Мне это действительно не нравится, поскольку они делают использование регистра невидимым для читатель кода. Но я вижу, как это может помочь писателю сохранить жизнь лучше. Как насчет использования таких сокращений, как .def R16_LEDS = R16? Таким образом, вы получите имя для регистрации, но оно не будет полностью скрыто, поэтому вы не сможете случайно повторно использовать его в каком-либо другом коде. И улучшить комментарии: out DDRC, LEDS ; PORTC PORTC что? Это как включить 8 светодиодов? так далее.. - person Ped7g; 23.11.2017
comment
Спасибо за Ваш интерес. Я обновил вопрос. Я написал простой кнопочный контроллер. Не могли бы вы еще раз просмотреть мой вопрос, пожалуйста? (out DDRC, LEDS; установить DDRC в качестве выходного контакта) - person Dentrax; 26.11.2017
comment
Извините, это снова беспорядок в макросах, где я не вижу легко, что будет результатом (машинный код). У меня нет вашей IDE, чтобы скомпилировать и проверить дизассемблирование. Вы не последовали моему совету не скрывать использование регистров и т. д. Это делает рассуждения о коде очень трудными для опытного программиста на ассемблере, для меня сбивает с толку то, что я не вижу регистры, такие имена, как BTN_STATE_TEMP, сбивают с толку мой виртуальный процессор в голове, поскольку они ничего не значат в AVR, когда я сосредотачиваюсь на симуляции, делать также перевод слишком утомительно. - person Ped7g; 26.11.2017
comment
@FurkanTürkal то же самое касается этого DDRC, я не знаю, что это такое в машинном коде, это номер порта? Это адрес памяти? Это регистрация? И вы не добавили его определение (я понимаю, что оно, вероятно, скрыто во включенных платформах, но, может быть, вы можете скопировать его из него?). Также некоторый логический комментарий, что он делает (я думаю, 8-битная маска включает / выключает 8 светодиодов? Сопоставляется с портом ввода-вывода C?). Вы пишете исходник прямо противоположным образом, чтобы скрыть весь низкоуровневый материал за какими-то именами, знакомыми вам, но не другим читателям. - person Ped7g; 26.11.2017
comment
@FurkanTürkal и Но это не работает, когда я нажимаю кнопку. похоже, не работает описание... постарайтесь быть очень конкретным, каждая деталь может помочь... например, обработчик onClick не выполняется, но основной цикл не зависает или основной цикл все еще зависает, когда я удерживаю кнопку, но onClick не выполняется и т. д. Из вашего описания я не уверен, что именно происходит в вашей новой версии. ... Имейте в виду, что я ничего не знаю о вашей системе (даже инструкции по сборке процессора). Возможно, я достаточно опытен, чтобы понять 90% на лету и погуглить 10%, но немного помогу. - person Ped7g; 26.11.2017