Josh Benson
Josh Benson

Reputation: 236

Creating a toggle switch in AVR assembly

I want to create a program in AVR assembly that will poll the state of a momentary pushbutton switch and toggle the state of an LED whenever the switch is pressed. I am using an Arduino Duemilanove with an ATMega328P chip. I have a pushbutton switch connected between digital pin 0 and ground, and an LED with 330 ohm resistor connected between digital pin 8 and +5V. Here is my code so far:

;==============
; 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

Unfortunately all this does is light the LED and keep it on no matter how many times the button is pushed. I am basing this code off of a program found here that turns the LED on as long as the button is pushed. I simply want to extend that to hold the LED in its current state until the button is pushed again. Any suggestions?

Upvotes: 0

Views: 7776

Answers (3)

duck
duck

Reputation: 379

you can toggle it by using NOT operator

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

Upvotes: 1

L. Zeda
L. Zeda

Reputation: 123

This code changes the value so quickly, that you won't be able to notice any change. Everytime you press the button, it will keep toggling the value for whole time it's pressed down. You should add some delay or simply ignore the on state for some time. Also, you should pick up from PIND only what you want, by masking it (easiest way is to use 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

Upvotes: 1

Shift_Left
Shift_Left

Reputation: 1243

Your only ever writing a HIGH to PB0. Each time the key is pressed you need to invert the pins state for example

in  temp, PORTB
com temp
out PINB, temp

As you've previously set temp to 1 then 1's compliment would change it to 11111110 thus writing a zero to PINB0 and the next time it would be 00000001 turning the led back on.

This over simplistic solution has an unpredictable side affect in that it doesn't take bounce into consideration, therefor you're not really guaranteed the LED will be on or off when you release the button as expected. That is a question that digresses from the scope of this one and should be asked separately. Just wanted to give you a heads up here.

Upvotes: -1

Related Questions