; time trigger for Olympus camera
; Copyright (C) 2013 Stefan Schuermans <stefan@blinkenarea.org>
; Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
; a BlinkenArea project - http://www.blinkenarea.org/



; 1 MHz internal RC oscillator



.INCLUDE        "m8def.inc"



; IO pins
.equ    PORT_LED        =       PORTB
.equ    BIT_LED         =       1
.equ    PORT_CAM        =       PORTB
.equ    BIT_CAM         =       2



; multiplication results
.def    MUL_RES_L       =       r0
.def    MUL_RES_H       =       r1



; general purpose registers
.def    DATA_L          =       r16
.def    DATA_H          =       r17
.def    TMP             =       r18
.def    CNT             =       r19



.DSEG
.ORG    0x060



.CSEG
.ORG    0x000
        rjmp    ENTRY                           ; RESET
        reti                                    ; INT0
        reti                                    ; INT1
        reti                                    ; TIMER2_COMP
        reti                                    ; TIMER2_OVF
        reti                                    ; TIMER1_CAPT
        reti                                    ; TIMER1_COMPA
        reti                                    ; TIMER1_COMPB
        reti                                    ; TIMER1_OVF
        reti                                    ; TIMER0_OVF
        reti                                    ; SPI_STC
        reti                                    ; USART_RXC
        reti                                    ; USART_UDRE
        reti                                    ; USART_TXC
        reti                                    ; ADC
        reti                                    ; EE_RDY
        reti                                    ; ANA_COMP
        reti                                    ; TWI
        reti                                    ; SPM_RDY



; code entry point
ENTRY:
; initialize stack pointer
        ldi     TMP,HIGH(RAMEND)
        out     SPH,TMP
        ldi     TMP,LOW(RAMEND)
        out     SPL,TMP
; enable watchdog (64ms)
        wdr
        ldi     TMP,1<<WDCE|1<<WDE
        out     WDTCR,TMP
        ldi     TMP,1<<WDE|1<<WDP1
        out     WDTCR,TMP
        wdr
; setup I/O pins
;   o = output, low
;   O = output, high
;   i = input, pull-up disabled
;   I = input, pull-up enabled
;   . = pin not available
        ldi     TMP,0xE0                        ; PB: IIIooooo
        out     PORTB,TMP
        ldi     TMP,0x1F
        out     DDRB,TMP
        ldi     TMP,0x00                        ; PC: ..ooooii
        out     PORTC,TMP
        ldi     TMP,0x3C
        out     DDRC,TMP
        ldi     TMP,0x00                        ; PD: iioooooo
        out     PORTD,TMP
        ldi     TMP,0x3F
        out     DDRD,TMP
; enable analog comparator
        ldi     TMP,0                           ; positive input: AIN0
        out     ACSR,TMP
        in      TMP,SFIOR                       ; negative input: AIN1
        cbr     TMP,1<<ACME
        out     SFIOR,TMP
; enable analaog to digital converter
        ldi     TMP,1<<REFS0                    ; AREF = AVCC, select ADC0
        out     ADMUX,TMP
        ldi     TMP,1<<ADEN|1<<ADPS1|1<<ADPS0   ; enable, clock 1:8
        out     ADCSRA,TMP
; jump to main program
        rjmp    MAIN



; wait CNT milliseconds (CNT = 0 -> wait 256ms)
WAIT_MS:
        ldi     TMP,250                         ; wait 1ms
WAIT_MS_INNER:
        wdr
        dec     TMP
        brne    WAIT_MS_INNER                   ; wait 1ms - bottom
        dec     CNT
        brne    WAIT_MS                         ; outer loop bottom
        ret                                     ; done



; wait CNT * 25us (CNT = 0 -> wait 6400us)
WAIT_25US:
        ldi     TMP,5                           ; wait 25us
WAIT_25US_INNER:
        wdr
        dec     TMP
        brne    WAIT_25US_INNER                 ; wait 25us - bottom
        dec     CNT
        brne    WAIT_25US                       ; outer loop bottom
        ret                                     ; done



; main program - initialization
MAIN:
        wdr                                     ; watchdog reset
        cbi     PORT_LED,BIT_LED                ; LED off
        cbi     PORT_CAM,BIT_CAM                ; camera trigger off

; main program - main loop
MAIN_LOOP:

; wait for trigger
MAIN_WAIT_LOW:                                  ; wait for comperator out low
        wdr
        sbic    ACSR,ACO
        rjmp    MAIN_WAIT_LOW
MAIN_WAIT_HIGH:                                 ; wait for comperator out high
        wdr
        sbis    ACSR,ACO
        rjmp    MAIN_WAIT_HIGH

; read configured coarse-grained time
        ldi     TMP,1<<REFS0                    ; AREF = AVCC, select ADC0
        out     ADMUX,TMP
        sbi     ADCSRA,ADSC                     ; start A/D conversion
MAIN_C_WAIT_ADC:                                ; wait for A/C conv to finish
        sbic    ADCSRA,ADSC
        rjmp    MAIN_C_WAIT_ADC

; LED on
        sbi     PORT_LED,BIT_LED

; wait for configured coarse-grained time
        in      DATA_L,ADCL                     ; read ADC
        in      DATA_H,ADCH
MAIN_C_WAIT_H:                                  ; wait ADCH * 256ms
        cpi     DATA_H,0                        ;   check if high part 0
        breq    MAIN_C_WAIT_H_END
        ldi     CNT,0                           ;   wait 256ms
        rcall   WAIT_MS
        dec     DATA_H                          ;   decrement high part
        rjmp    MAIN_C_WAIT_H                   ;   bottom of loop
MAIN_C_WAIT_H_END:                              ; wait ADCL * 1ms
        cpi     DATA_L,0                        ;   check if low part 0
        breq    MAIN_C_WAIT_L_END
        mov     CNT,DATA_L
        rcall   WAIT_MS
MAIN_C_WAIT_L_END:

; read configured fine-grained time
        ldi     TMP,1<<REFS0|1<<MUX0            ; AREF = AVCC, select ADC1
        out     ADMUX,TMP
        sbi     ADCSRA,ADSC                     ; start A/D conversion
MAIN_F_WAIT_ADC:                                ; wait for A/C conv to finish
        sbic    ADCSRA,ADSC
        rjmp    MAIN_F_WAIT_ADC

; wait for configured fine-grained time
        in      DATA_L,ADCL                     ; read ADC
        in      DATA_H,ADCH
MAIN_F_WAIT_H:                                  ; wait ADCH * 6400us
        cpi     DATA_H,0                        ;   check if high part 0
        breq    MAIN_F_WAIT_H_END
        ldi     CNT,0                           ;   wait 256ms
        rcall   WAIT_25US
        dec     DATA_H                          ;   decrement high part
        rjmp    MAIN_F_WAIT_H                   ;   bottom of loop
MAIN_F_WAIT_H_END:                              ; wait ADCL * 25us
        cpi     DATA_L,0                        ;   check if low part 0
        breq    MAIN_F_WAIT_L_END
        mov     CNT,DATA_L
        rcall   WAIT_25US
MAIN_F_WAIT_L_END:

; LED off
        cbi     PORT_LED,BIT_LED

; output camera trigger
        sbi     PORT_CAM,BIT_CAM                ; camera trigger on
        ldi     CNT,200                         ; wait 200ms
        rcall   WAIT_MS
        cbi     PORT_CAM,BIT_CAM                ; camera trigger off

; bottom of main loop
        rjmp    MAIN_LOOP

