【问题标题】:Creating a toggle switch in AVR assembly在 AVR 组件中创建拨动开关
【发布时间】:2016-09-07 04:48:49
【问题描述】:

我想在 AVR 程序集中创建一个程序,该程序将轮询瞬时按钮开关的状态,并在按下开关时切换 LED 的状态。我正在使用带有 ATMega328P 芯片的 Arduino Duemilanove。我在数字引脚 0 和地之间连接了一个按钮开关,在数字引脚 8 和 +5V 之间连接了一个带有 330 欧姆电阻的 LED。到目前为止,这是我的代码:

;==============
; Declarations:

.def temp = r16
.org 0x0000
rjmp Reset

;==============

Reset:
        ser temp
        out DDRB, temp         ; set all pins on Port B to OUTPUT mode
        ldi temp, 0b11111110   ; set pin 0 on Port D to INPUT mode
        out DDRD, temp
        clr temp
        out PORTB, temp        ; clear temp and set all pins on Port B to LOW state
        ldi temp, 0b00000001   ; set pin 0 on Port D to HIGH state
        out PORTD, temp

;==============
; Main Program:

switch:
        in temp, PIND          ; get state of pins on Port D
        cpi temp, 0            ; compare result to 0 (pushbutton is pressed)
        brne PC+3              ; if != 0, go check again
        ldi temp, (1<<PB0)     ; otherwise, write logic 1 to pin 0 of Port B
        out PINB, temp         ; which toggles the state of the pin
        rjmp switch

不幸的是,这一切只是点亮 LED 并让 LED 保持亮起,无论按多少次按钮。我将这段代码基于找到的一个程序here,该程序只要按下按钮就会打开LED。我只是想扩展它以将 LED 保持在当前状态,直到再次按下按钮。有什么建议吗?

【问题讨论】:

  • 您应该只比较 PIND00,方法是使用掩码而不是整个 PIND。特别是如果您的端口处于浮动状态(二极管上的照明可能会改变浮动引脚的电平并使cpi temp, 0 始终错误)。此外,您可以使用SBI 指令更改PINB 中的单个位。不确定这是否可行,但目前我没有看到其他问题。
  • 按钮弹跳。这在示例代码中无关紧要,但在您的情况下,即使在@Julien 的提示之后,您也会收到随机结果。
  • 我已经尝试过如何使用sbi 将逻辑 1 写入 PB0,但我似乎无法让它工作。使用 `sbi PORTB0, 1` 不会给出预期的结果。我明白你关于按钮弹跳的观点。也许主循环中某处的延迟子程序会使开关反跳?
  • @JoshBenson 我对数据表的理解是sbi PORTB, 0(不需要 1,因为 Sbi 用于设置位(cbi 清除位))。是的,按下检测后会有一些延迟会去抖动

标签: assembly arduino avr atmega


【解决方案1】:

此代码更改值的速度如此之快,以至于您不会注意到任何变化。 每次按下按钮时,它都会在按下的整个过程中不断切换值。您应该添加一些延迟或干脆忽略 on 状态一段时间。此外,您应该只从 PIND 中提取您想要的内容,方法是屏蔽它(最简单的方法是使用 andi)。

.def del = r15
  clr del
switch:
  in temp, PIND          ; get state of pins on Port D
  andi temp, (1<<PD0)    ; you should mask to get only what you want
  cpi temp, 0            ; compare result to 0 (pushbutton is pressed)
  brne switch            ; if != 0, go check again
  cpi del, 0             ; compare del to 0
  brne dec_jmp           ; if != 0, ignore this run
  ldi temp, (1<<PB0)     ; otherwise, write logic 1 to pin 0 of Port B
  out PINB, temp         ; which toggles the state of the pin
  ldi del, 250           ; next one will be ignored for 250 runs

dec_jmp:
  dec del                ; decrement del
  rjmp switch

【讨论】:

    【解决方案2】:

    您可以使用 NOT 运算符来切换它

    ldi temp2,0
    switch:
    in temp,PIND
    andi temp,1 ; remove all results except bit.0
    cpi temp,0  ; if pressed (i assume the button is active low)
    brne switch ; loop again if not pressed
    mov temp2,!temp2 ; not operator
    out PORTB,temp2  ; toggle PORTB output
    rjmp switch      ; back to main loop
    

    【讨论】:

      【解决方案3】:

      您唯一一次向 PB0 写入 HIGH。例如,每次按下键时,您都需要反转引脚状态

      in  temp, PORTB
      com temp
      out PINB, temp
      

      由于您之前将 temp 设置为 1,因此 1 的补码会将其更改为 11111110,从而将 0 写入 PINB0,下​​一次将是 00000001 重新打开 LED。

      这种过于简单的解决方案具有不可预测的副作用,因为它没有考虑反弹,因此您不能真正保证当您按预期释放按钮时 LED 会打开或关闭。这个问题偏离了这个问题的范围,应该单独提出。只是想在这里提醒您一下。

      【讨论】:

      • 这不是问题。来自数据表:向 PINxn 写入逻辑 1 会切换 PORTxn 的值,与 DDRxn 的值无关。请注意,SBI 指令可用于切换端口中的单个位。
      猜你喜欢
      • 2021-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-31
      • 2020-10-18
      相关资源
      最近更新 更多