// * one file blink and toggle button program * #include <avr/io.h> .EQU LED_alive , 5 .EQU LED0 , 0 .EQU LED1 , 1 .EQU KEY0 , 4 ; note that "KEY0: is on the 4th PIN! .EQU KEY1 , 5 .DATA led_status: .BYTE 1 key_last: .BYTE 1 ; main program function starts here .GLOBAL main .TEXT main: main_initialize: ; setting LED "alive" and the lowest LED bit to be outputs ; other pins kept as inputs as they were originally in R16, _SFR_IO_ADDR(DDRB) ori R16, (1<<LED_alive | 1<<LED0) ; set up output pins out _SFR_IO_ADDR(DDRB), R16 in R16, _SFR_IO_ADDR(PORTB) ori R16, (1<<LED_alive | 1<<LED0) ; set initial output to 1 (LED light up/on) out _SFR_IO_ADDR(PORTB), R16 ; set pin 4 of port D to input with a pull-up resistor ; other pins kept as inputs as they were originally in R16, _SFR_IO_ADDR(DDRD) andi R16, ~(1<<KEY0 | 1<<KEY1) ; set the used push button pins to inputs ; no output pins to set up out _SFR_IO_ADDR(DDRD), R16 in R16, _SFR_IO_ADDR(PORTD) ori R16, (1<<KEY0 | 1<<KEY1) ; activates pull up resistors on used pin(s) out _SFR_IO_ADDR(PORTD), R16 ; Initialize variables and registers ldi R16, 0 sts led_status, R16 sts key_last, R16 main_superloop: ; reading the button status(es) in R16, _SFR_IO_ADDR(PIND) lds R18, key_last eor R18, R16 sts key_last, R16 ; start updating LEDs with preserving the unused bits in R16, _SFR_IO_ADDR(PORTB) sts led_status, R16 ; blinking the "alive" LED ldi R17, 1<<LED_alive lds R16, led_status eor R16, R17 sts led_status, R16 ; take care of button KEY0 -> LED0 check_b0: mov R16, R18 andi R16, 1<<KEY0 breq skip_b0 lds R16, key_last andi R16, 1<<KEY0 breq skip_b0 toggle_led0: ldi R17, 1<<LED0 lds R16, led_status eor R16, R17 sts led_status, R16 skip_b0: ; writing the updated LED output lds R16, led_status out _SFR_IO_ADDR(PORTB), R16 ; call delay function with number of MS in R3 ldi R16, 100 mov R3, R16 rcall delay_in_ms ; note: perhaps it would be better to design the function to receive its parameter in R16 or above ; as that would allow to set constant parameter with just one instruction instead of two? rjmp main_superloop ; this function ; - receives a unit8_t variable in register [r3] ; - causes time delay of that many milliseconds by wasting time and energy ; - returns 0 in [r3] ; .GLOBAL delay_in_ms .EQU OneMS, (16*1000/5) ; == 3072 ; 16MHz clock yields 16,0000 instructions per one millisecond ; each loop iteration takes five instructions to execute .TEXT delay_in_ms: push R16 in R16, _SFR_IO_ADDR(SREG) push R16 push R17 push R18 push R19 count_ms: ldi R19, ( 0xFF & ( OneMS>>16 ) ) ; it is 0 but we need 3byte counter for timing purpose ldi R18, ( 0xFF & ( OneMS>> 8 ) ) ldi R17, ( 0xFF & OneMS ) delay_1ms: subi R17, 1 ; 1 clock tick sbci R18, 0 ; 1 clock tick sbci R19, 0 ; 1 clock tick brcc delay_1ms ; 2 clock ticks when jumps, 1 when it does not dec R3 brne count_ms pop R19 pop R18 pop R17 pop R16 out _SFR_IO_ADDR(SREG), R16 pop R16 ret ; either expected by linker, equivalent to the "old style" .ORG 0 ; or alternatively generated by the C/C++ compiler on its own accord .TEXT _start: reset_vector: jmp start ; interrupt table would follow *** here *** if there are any interrupt services ; 32bits per entry, up to 34 entries ; this is the part of the loader program that initializes the stack etc. .TEXT start: ldi R16, 0xFF & (RAMEND>>8) out _SFR_IO_ADDR(SPH), R16 ldi R16, 0xFF & RAMEND out _SFR_IO_ADDR(SPL), R16 jmp main