汇编仅执行纽扣状态一次



我正在使用Atmega2560 MicroController处理一个简单的按钮主导项目。我对按钮有问题。当我单击按钮时,主循环停止工作,按钮功能只要我按下按钮就可以保持无限时间。当我按按钮时,主环不应停止。并且按钮函数必须仅运行一次。我该怎么做?

.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

编辑:(2017年11月26日)

我写了一个简单的按钮控制器。该控制器的目的是检查按钮是否按下或不保持按下。按钮代码只能工作一次。但是当我按按钮时它不起作用。我在哪里犯错?

.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

按下按钮时,输入引脚信号就像:

___---------------------------------___--__--___-_______
   ^ here the press starts     ^ released  ^ bounces (physically)

有时甚至在开始时即使在触点不够牢固的话,也可能会发生一些弹跳,或者主信号中的一些噪音。

如果是完美的干净数字信号,例如:

_______-------------------------------_______________

那么,您要做的就是保持在先前的PIN的主要状态,检查按钮是否单击,然后看起来像:

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

但是,由于按钮中实际开关的物理弹跳,您将必须在代码中编写更强大的逻辑,而在"上一个"的"位更改"也将重置一些" debounce tebounce timer",直到该计时器(count Count)-down计数器)将达到零,"以前的"状态已锁定,忽略了对真实I/O线上读取的任何状态更改。因此,拒绝逻辑将转弯:

___---------------------------------___--__--___-_______
   ^ 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 = 〜30ms,致电OnClick Handler
  • *2:Debounce达到零(直到这里"以前"锁定)
  • *3:上一个= 0,debounce = 〜30ms
  • *4:Debounce达到零(直到这里"以前"锁定 - 直到现在的任何按钮将被忽略)

并且,如果您想实现"主要不停止"的幻想,则需要保持OnClick处理程序非常短(非阻止,非延迟),并保留任何延迟的逻辑,以无限地循环和根据需要更新任何计时器/计数器(包括每个输入位的" debounce"计时器),并使用其他复杂逻辑在某些计时器或输入状态发射的某些事件上运行简短功能。


编辑:关于新代码的一些注释,我确实尝试了部分理解。

我对您对那件事的非常基本的体系结构有问题,看起来您将所有值保留在固定寄存器中的所有值,这将使LOOP_BUTTON固定在特定的PIN/PIN/按钮上,而在其他地方不可重复使用。

我建议您以更通用的方式设计子例程,而不是通过参数配置特定功能,而不是通过子例程的代码(只要有意义,并且所得代码非常简单)。

我会以某种方式设计它,以将一个寄存器的地址和PIN的另一个寄存器值(例如:

)登录。

免责声明:我无法验证这是否有效AVR ASM语法,甚至该代码是否有效,因此主要用作" Idea"源(我使用此链接来编写指令及其语法:http://www。

    ... 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当前状态(PINS)中采用,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的数量,直到按钮开始对当前状态做出反应(例如,如果您循环一次每个1ms,则可以尝试延迟30)。

>

哦,等等,所以我没有发表您的代码,而只是制作了自己的代码...即使我什至无法验证它有效……对不起。:)

(如果有效,那么我想这不是很高效的AVR组装,因为我觉得我总是从x86的角度遇到问题,而缺少指示可以帮助我,例如为什么AVR有这么多指示直接设置标志(零/携带/...所有),但不是另一种方式,将寄存器设置为0/1,根据标志等)

请让我知道它是否以某种形式对您有用 建议修复我的代码以使其有效,以使其更好地答案(如果您不响应,我可能会随着时间的推移将其删除,因为我恐怕这可能是完全错误的)。

最新更新