;  No Copyright
;
;  The person who associated a work with this deed has dedicated the work to the
;  public domain by waiving all of his or her rights to the work worldwide under
;  copyright law, including all related and neighboring rights, to the extent
;  allowed by law.
;
;  You can copy, modify, distribute and perform the work, even for commercial
;  purposes, all without asking permission.
;
;  See https://creativecommons.org/share-your-work/public-domain/cc0/ for more
;  information on the license.


;  This file is part of the momentary / latching switch project, made by
;  Maarten Tromp <maarten@geekabit.nl>, 2018-04-28.
;
;  More info on the project:
;  https://www.geekabit.nl/projects/momentary-latching-switch/


; include register defenitions for this proc
.include "2313def.inc"

; register defenitions
	.def tmp	= r16
	.def ms_counter1	= r17
	.def pressed1	= r18
	.def debounced1	= r19
	.def momentary1	= r20
	.def ms_counter2	= r21
	.def pressed2	= r22
	.def debounced2	= r23
	.def momentary2	= r24

; defines
	.equ led1	= PB4	; green, guitar channel
	.equ switch1	= PB5
	.equ rel1a	= PB6
	.equ rel1b	= PB7
	.equ led2	= PD3	; red, bass channel
	.equ switch2	= PD2
	.equ rel2a	= PD0
	.equ rel2b	= PD1
	.equ timeout	= 250	; timeout in ms
;  a note on timing:
;  100ms gives a lot of false momentary modes
;  200ms can give some false latching modes
;  150ms feels about right on my fingers, but gives many false momentaries on my feet
;  200ms feels better already
;  250ms trying it now


; static values
	.cseg			; code segment
	.org 0			; code starts at address 0

; interrupt vectors
	rjmp reset		; reset
	reti			; external int0
	reti	 		; external int1
	reti			; timer1 capture
	rjmp t1_cmp		; timer1 compare A
	reti			; timer1 (16bit) overflow
	reti			; timer0 (8bit) overflow
	reti			; uart rx complete
	reti			; udr empty
	reti			; uart tx complete
	reti			; analog comparator
	; tiny2313 interrupts
	reti			; pin change int
	reti			; timer 1 compare B
	reti			; timer 0 compare A
	reti			; timer 0 compare B
	reti			; USI start
	reti			; USI overflow
	reti			; EEPROM ready
	reti			; Watchdog ovf

reset:
	; stackpointer
	ldi tmp, RAMEND
	out spl, tmp

	; watchdog
	ldi tmp, 0		; disable
	out WDTCR, tmp

	; ports
	ldi tmp, (1<<led1)|(1<<rel1a)|(1<<rel1b)	; set led and relay pins as output, rest as input
	out DDRB, tmp
	ldi tmp, (1<<led2)|(1<<rel2a)|(1<<rel2b)
	out DDRD, tmp
	ldi tmp, (1<<rel1a)|(1<<rel1b)|(1<<switch1)	; this should work to setup initial values for relays
	out PORTB, tmp
	ldi tmp, (1<<switch2)
	out PORTD, tmp

	; timer1
	ldi tmp, 0		; disable timer1 compare output and pwm
	out TCCR1A, tmp
	ldi tmp, (1<<CTC1)|(1<<CS10)	; clear timer1 on match, timer1 clock input = system clock
	out TCCR1B, tmp

	; compare match
	; system clock is 8MHz / 8 = 1MHz
	; timer1 clock = system clock = 1MHz
	; divide by 1k to get an 1kHz interrupt, 1ms beteen interrupts
	ldi tmp, high(1000)
	out OCR1AH, tmp
	ldi tmp, low(1000)
	out OCR1AL, tmp

	; timer interrupt
	ldi tmp, (1<<OCIE1A)	; timer1 compare match interrupt enable
	out TIMSK, tmp

	; init registers
	ldi pressed1, 0
	ldi debounced1, 0
	ldi ms_counter1, 10 // for initialisation, skip debouncing
	ldi pressed2, 0
	ldi debounced2, 0
	ldi ms_counter2, 10

	; set initial values
	; this works together with the configuration of portb/d earlier
	rjmp rel1_power
	rjmp rel2_power

	; sleep mode
	ldi tmp, (1<<SE)	; enable sleep mode, set mode to idle
	out MCUCR, tmp

	sei			; global interrupt enable

main:
	sleep
	rjmp main

t1_cmp: 			; timer1 compare match
				; this interrupt fires at exactly 1kHz
	; test if switch1 is currently pressed
	;sbi PORTB, switch1	; enable pull-up
	sbic PINB, switch1
	rjmp switch1_released

switch1_pressed:
	;cbi PORTB, switch1	; disable pull-up
	; test if switch1 is newly pressed
	cpi pressed1, 1
	breq switch1_press_debounce

	; switch1 is newly pressed
	ldi pressed1, 1
	ldi debounced1, 0
	ldi momentary1, 0	; set mode to latching
	ldi ms_counter1, 0	; start counting
	rjmp test_switch2

switch1_press_debounce:
	; some debouncing
	; after 10ms input is concidered stable
	; todo maybe write a state machine
	cpi ms_counter1, 10
	brne test_for_long_press1

	cpi debounced1, 1
	breq test_switch2

	ldi debounced1, 1
	rcall rel1_power
	rjmp test_switch2

test_for_long_press1:
	; is switch is long pressed we go into momentary mode
	cpi ms_counter1, timeout
	brne test_rel1_release

	ldi momentary1, 1	; set mode to momentary
	rjmp test_switch2

switch1_released:
	;cbi PORTB, switch1	; disable pull-up
	; test if switch1 is newly released
	cpi pressed1, 0
	breq switch1_release_debounce

	; switch1 is newly released
	ldi pressed1, 0
	ldi debounced1, 0
	ldi ms_counter1, 0	; start counting
	rjmp test_switch2

switch1_release_debounce:
	; some debouncing
	; after 10ms input is concidered stable
	cpi ms_counter1, 10
	brne test_rel1_release

	cpi debounced1, 1
	breq test_switch2

	ldi debounced1, 1
	; test if mode is latching or momentary
	cpi momentary1, 0
	breq test_switch2

	; mode is momentary
	ldi momentary1, 0
	rcall rel1_power
	rjmp test_switch2

test_rel1_release:
	; relay should be released after 6ms
	cpi ms_counter1, 15
	brne test_switch2

	rcall rel1_release
	; it is no problem to call this function repeatedly

test_switch2:
	; test if switch2 is currently pressed
	;sbi PORTD, switch2	; enable pull-up
	sbic PIND, switch2
	rjmp switch2_released

switch2_pressed:
	;cbi PORTB, switch2	; disable pull-up
	; test if switch2 is newly pressed
	cpi pressed2, 1
	breq switch2_press_debounce

	; switch2 is newly pressed
	ldi pressed2, 1
	ldi debounced2, 0
	ldi momentary2, 0	; set mode to latching
	ldi ms_counter2, 0	; start counting
	rjmp t1_cmp_done

switch2_press_debounce:
	; some debouncing
	; after 10ms input is concidered stable
	cpi ms_counter2, 10
	brne test_for_long_press2

	cpi debounced2, 1
	breq t1_cmp_done

	ldi debounced2, 1
	rcall rel2_power
	rjmp t1_cmp_done

test_for_long_press2:
	; is switch is long pressed we go into momentary mode
	cpi ms_counter2, timeout
	brne test_rel2_release

	ldi momentary2, 1	; set mode to momentary
	rjmp t1_cmp_done

switch2_released:
	;cbi PORTB, switch2	; disable pull-up
	; test if switch2 is newly released
	cpi pressed2, 0
	breq switch2_release_debounce

	; switch2 is newly released
	ldi pressed2, 0
	ldi debounced2, 0
	ldi ms_counter2, 0	; start counting
	rjmp t1_cmp_done

switch2_release_debounce:
	; some debouncing
	; after 10ms input is concidered stable
	cpi ms_counter2, 10
	brne test_rel2_release

	cpi debounced2, 1
	breq t1_cmp_done

	ldi debounced2, 1
	; test if mode is latching or momentary
	cpi momentary2, 0
	breq t1_cmp_done

	; mode is momentary
	ldi momentary2, 0
	rcall rel2_power
	rjmp t1_cmp_done

test_rel2_release:
	; relay should be released after 6ms
	cpi ms_counter2, 15
	brne t1_cmp_done

	rcall rel2_release
	; it is no problem to call this function repeatedly

t1_cmp_done:
	inc ms_counter1
	inc ms_counter2
	reti


rel1_power:
	; power relay1 by setting rel1a = !rel1b
	sbic PORTB, rel1b
	rjmp rel1a_low
	sbi PORTB, rel1a
	rjmp rel1_power_done
rel1a_low:
	cbi PORTB, rel1a
rel1_power_done:
	; toggle led1
	sbic PORTB, led1
	rjmp led1_off
	sbi PORTB, led1
	rjmp led1_done
led1_off:
	cbi PORTB, led1
led1_done:
	ret


rel2_power:
	; power relay2 by setting rel2a = !rel2b
	sbic PORTD, rel2b
	rjmp rel2a_low
	sbi PORTD, rel2a
	rjmp rel2_power_done
rel2a_low:
	cbi PORTD, rel2a
rel2_power_done:
	; toggle led2
	sbic PORTD, led2
	rjmp led2_off
	sbi PORTD, led2
	rjmp led2_done
led2_off:
	cbi PORTD, led2
led2_done:
	ret


rel1_release:
	; release relay1 by setting rel1b = rel1a
	sbis PORTB, rel1a
	rjmp rel1b_low
	sbi PORTB, rel1b
	rjmp rel1_release_done
rel1b_low:
	cbi PORTB, rel1b
rel1_release_done:
	ret


rel2_release:
	; release relay2 by setting rel2b = rel2a
	sbis PORTD, rel2a
	rjmp rel2b_low
	sbi PORTD, rel2b
	rjmp rel2_release_done
rel2b_low:
	cbi PORTD, rel2b
rel2_release_done:
	ret


; end of file
