Assembly 程序集执行按钮单击状态仅一次

Assembly 程序集执行按钮单击状态仅一次,assembly,avr,atmega,atmel,atmelstudio,Assembly,Avr,Atmega,Atmel,Atmelstudio,我正在使用Atmega2560微控制器开发一个简单的按钮led项目。我的按钮有问题。当我点击按钮时,主循环停止工作,只要我按下按钮,按钮功能就会无限次地运行。当我按下按钮时,主回路不应停止。按钮功能只能运行一次。我该怎么做 .def LEDS = R16 .def LED_DATA = R21 .org 0 rjmp MAIN MAIN: ldi LEDS, 0xFF ; 0xFF = 1111 1111 ldi LED_DATA, 0x01 out D

我正在使用Atmega2560微控制器开发一个简单的按钮led项目。我的按钮有问题。当我点击按钮时,主循环停止工作,只要我按下按钮,按钮功能就会无限次地运行。当我按下按钮时,主回路不应停止。按钮功能只能运行一次。我该怎么做

.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
但是,由于实际的switch in按钮的物理反弹,您必须在代码中编写更健壮的逻辑,在“previous”位更改后,也会重置一些“debounce timer”,直到该计时器(倒计时计数器)达到零,“previous”状态被锁定,忽略在实际I/O线上读取的任何状态更改。那么,脱口而出的逻辑将转向:

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

行动:

  • *1:previous=1,debounce=~30ms,调用onClick处理程序
  • *2:debounce为零(在此之前“previous”已锁定)
  • *3:previous=0,debounce=~30ms
  • *4:debounce达到零(在此之前“previous”被锁定-任何按钮点击到现在都将被忽略)

如果你想实现“main不停止”的假象,你需要保持onClick处理器非常短(非阻塞、非延迟),并为“main”保留任何延迟逻辑,它可以无限循环并根据需要更新任何计时器/计数器(包括每个输入位的“去盎司”计时器)并使用额外的复杂逻辑对某些计时器或输入状态触发的特定事件运行简短的快速函数


编辑:关于新代码的一些注释,我试图部分理解

我对这个东西的基本架构有问题,它看起来像是将与该按钮相关的所有值保留在固定寄存器中,这将使
循环按钮
固定到特定的pin/按钮,而不能在其他地方重复使用

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

我将以一种方式设计它,在一个寄存器中获取button对象实例的地址,在另一个寄存器中获取pin的值,如:

免责声明:我无法验证这是否是有效的AVR asm语法,或者该代码是否有效,因此请主要用作“idea”源代码(我使用此链接编写指令及其语法:):

按钮数据在.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
,这是在按钮开始对当前状态作出反应之前要传递的主循环量(例如,如果每1ms循环一次,则可以尝试延迟30)

哦,等等,所以我没有评论你的代码,而是制作了我自己的。。。即使我无法验证它是否有效。。。对不起。:)

(如果它能工作,那么我猜它不是非常高效的AVR汇编,因为我觉得我总是从x86的角度遇到问题,并且缺少帮助我的指令,比如为什么AVR有这么多指令直接设置标志(零/进位/…所有这些),而不是相反,根据标志将寄存器设置为0/1,等等。)


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

当您按下按钮时,输入pin信号如下所示:

___---------------------------------___--__--___-_______
   ^ 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
但是,由于实际的switch in按钮的物理反弹,您必须在代码中编写更健壮的逻辑,在“previous”位更改后,也会重置一些“debounce timer”,直到该计时器(倒计时计数器)达到零,“previous”状态被锁定,忽略在实际I/O线上读取的任何状态更改。那么,脱口而出的逻辑将转向:

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

行动:

  • *1:previous=1,debounce=~30ms,调用onClick处理程序
  • *2:debounce为零(在此之前“previous”已锁定)
  • *3:previous=0,debounce=~30ms
  • *4:debounce达到零(在此之前“previous”被锁定-任何按钮点击到现在都将被忽略)

如果你想实现“main不停止”的假象,你需要保持onClick处理器非常短(非阻塞、非延迟),并为“main”保留任何延迟逻辑,它可以无限循环并根据需要更新任何计时器/计数器(包括