; mic_serial
;
; microphone input with RGB LED Output

.include "tn44def.inc"
;
; definitions
;
.equ tx_pin = PA3; transmit pin
.equ nloop = 200 ; number of samples between framing

.equ red = PA6; red pin
.equ green = PA7; green pin
.equ blue = PB2; blue pin

;
; registers
;
.def count = R30; loop counter
.def pwm = R31; PWM counter
.def bit_count = R16; bit counter
.def temp = R17; temporary storage
.def temp1 = R18; temporary storage
.def txbyte = R19; data byte
.def loop_count = R20 ; loop counter
.def min_lo = R21
.def min_hi = R22 
.def max_lo = R23 
.def max_hi = R24  
.def zero_lo = R25 
.def zero_hi = R26  
.def zero_crossings = R27
.def prev_lo = R28
.def prev_hi = R29
;
; code segment
;
.cseg
.org 0
rjmp reset

;
; print
; 
.macro print
   ldi zl,low(@0*2)
   ldi zh,high(@0*2)
   rcall print_db
   .endmacro
;
; print_db
; prints a null-terminated .db string
;
print_db:
   print_loop:
      lpm
      mov txbyte,R0
      cpi txbyte,0
      breq return
      rcall putchar
      adiw zl, 1
      rjmp print_loop
   return:
      ret


;
; half_bit_delay
; serial half bit delay
;
half_bit_delay:
   ldi temp, 25; 115200 baud (20 MHz clock /1)
   half_bit_delay_loop:
      dec temp
      brne half_bit_delay_loop
   ret

;do_pwm

do_pwm:

   ;
   ; init LED pins for output
   ;
   sbi PORTA, red
   sbi DDRA, red
   sbi PORTA, green
   sbi DDRA, green
   sbi PORTB, blue
   sbi DDRB, blue


      ldi count, 255
      off_red_loop:
         mov pwm, count
         sbi PORTA, red
         off_red_loop_off:
            dec pwm
            brne off_red_loop_off
         mov pwm, count
         cbi PORTA, red
         off_red_loop_on:
            inc pwm
            brne off_red_loop_on
         dec count
         brne off_red_loop
      ldi count, 255
      red_green_loop:
         mov pwm, count
         cbi PORTA, red
         sbi PORTA, green
         red_green_loop_off:
            dec pwm
            brne red_green_loop_off
         mov pwm, count
         sbi PORTA, red
         cbi PORTA, green
         red_green_loop_on:
            inc pwm
            brne red_green_loop_on
         dec count
	      brne red_green_loop
      green_blue_loop:
         mov pwm, count
         cbi PORTA, green
         sbi PORTB, blue
         green_blue_loop_off:
            dec pwm
            brne green_blue_loop_off
         mov pwm, count
         sbi PORTA, green
         cbi PORTB, blue
         green_blue_loop_on:
            inc pwm
            brne green_blue_loop_on
         dec count
         brne green_blue_loop
      blue_on_loop:
         mov pwm, count
         sbi PORTA, red
         sbi PORTA, green
         blue_on_loop_off:
            dec pwm
            brne blue_on_loop_off
         mov pwm, count
         cbi PORTA, red
         cbi PORTA, green
         blue_on_loop_on:
            inc pwm
            brne blue_on_loop_on
         dec count
         brne blue_on_loop
      on_off_loop:
         mov pwm, count
         cbi PORTA, red
         cbi PORTA, green
         cbi PORTB, blue
         on_off_loop_off:
            dec pwm
            brne on_off_loop_off
         mov pwm, count
         sbi PORTA, red
         sbi PORTA, green
         sbi PORTB, blue
         on_off_loop_on:
            inc pwm
            brne on_off_loop_on
         dec count
         brne on_off_loop
ret

;pwm off

ledoff:
   cbi PORTA, red
   cbi DDRA, red
   cbi PORTA, green
   cbi DDRA, green
   cbi PORTB, blue
   cbi DDRB, blue
ret

;
; putchar
; assumes no line driver (doesn't invert bits)
;
.equ sb = 1; number of stop bits
putchar:
   ldi bit_count, 9+sb; 1+8+sb
   com txbyte; invert everything
   sec; set start bit
   putchar0:
      brcc putchar1; if carry set
      sbi PORTA, tx_pin; send a '0'
      ;cpi txbyte, 0x0
      ;brne doit
      	;doit:
		;rcall do_pwm
      rjmp putchar2; else	
   putchar1:
       ;rcall do_pwm
       cbi PORTA, tx_pin	; send a '1'
       ;rcall ledoff
       nop ; even out timing
   putchar2:
       rcall half_bit_delay; one bit delay
       rcall half_bit_delay
       lsr txbyte; get next bit
       dec bit_count; if not all bits sent
;rcall do_pwm
       brne putchar0; send next bit
   ret;
;
; char_delay
; delay between characters
;
char_delay:
ldi temp, 255
char_delay_loop:
   ldi temp1, 10
   char_delay_loop1:
      dec temp1
      brne char_delay_loop1
   dec temp
   brne char_delay_loop
   ret
;
; main program
;
reset:
   ;
   ; set clock divider to /1
   ;
   ldi temp, (1 << CLKPCE)
   ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0)
   out CLKPR, temp
   out CLKPR, temp1
   ;
   ; set stack pointer to top of RAM
   ;
   ldi temp, high(RAMEND)
   out SPH, temp
   ldi temp, low(RAMEND)
   out SPL, temp
   ;
   ; init comm pin for output
   ;
   sbi PORTA, tx_pin
   sbi DDRA, tx_pin

   ;
   ; set up A/D
   ;
   sbi ADMUX, REFS1 ; use 1.1V reference
   cbi ADMUX, REFS0 ; "
   sbi ADMUX, MUX5 ; differential, 20x gain, PA2-PA1
   cbi ADMUX, MUX4 ; 
   sbi ADMUX, MUX3 ; 
   sbi ADMUX, MUX2 ; 
   cbi ADMUX, MUX1 ; 
   sbi ADMUX, MUX0 ; 
   cbi ADMUX, ADLAR ; right-adjust result
   sbi ADCSRA, ADEN ; enable A/D
   cbi ADCSRA, ADATE ; disable auto-trigger
   sbi ADCSRA, ADPS2 ; set prescaler for /32
   cbi ADCSRA, ADPS1 ; "
   sbi ADCSRA, ADPS0 ; "
   ;
   ; main loop
   ;
   main_loop:
		;
		; init
		;
		ldi min_lo, 255
		ldi min_hi, 255
		ldi max_lo, 0
		ldi max_hi, 0
		ldi zero_crossings, 0
		ldi prev_lo, 0
		ldi prev_hi, 0
      ;
      ; sample loop
      ;
      ldi loop_count, nloop
      sample_loop:
         ;
         ; read A/D
         ;
         sbi ADCSRA, ADSC ; start conversion
         adloopup:
            sbic ADCSRA, ADSC ; loop until complete
            rjmp adloopup
         ;	 
         ; send conversion
         ;
         in txbyte, ADCL ; low byte
         in txbyte, ADCH ; hi byte
			;
			; set min and max
 			; only if we're beyond the first few samples
			;
			in temp, ADCL
			in temp1, ADCH
			cpi loop_count, 170
			brsh donotsetmax
			cp temp, min_lo
			cpc temp1, min_hi 
			brsh donotsetmin
			in min_lo, ADCL
			in min_hi, ADCH
			donotsetmin:
			cp temp, max_lo
			cpc temp1, max_hi 
			brlo donotsetmax
			in max_lo, ADCL
			in max_hi, ADCH
			donotsetmax:		
			;
			; count zero crossings
			; using zero calculated in prev frame
			;
			; if current sample >= zero 
			; 		if prev sample < zero
			;			inc zero_crossings
			;	
			in temp, ADCL
			in temp1, ADCH
			cp temp, zero_lo
			cpc temp1, zero_hi
			brlo donotcount
			cp prev_lo, zero_lo
			cpc prev_hi, zero_hi
			brsh donotcount
			inc zero_crossings			
			donotcount:			
			; 
			; prev sample = current sample
			;
			in prev_lo, ADCL
			in prev_hi, ADCH
	 ;
	 ; loop
	 ;
	 dec loop_count
	 brne sample_loop
	 ;
	 ; calculate zero = max + min / 2	 
	 ; to use as a 'zero' for counting zero-crossings
    	 ;
	 mov zero_lo, min_lo
	 mov zero_hi, min_hi
	 add zero_lo, max_lo
	 adc zero_hi, max_hi
    lsr zero_hi
    ror zero_lo
    ;
    ; multiply zero crossings by 8
    ;
    lsl zero_crossings
    lsl zero_crossings
    lsl zero_crossings
    ;
    ; if max - min > threshold
    ;    send zero crossings
    ; else
    ;    send a 0    
          
    sub max_lo, min_lo
    sbc max_hi, min_hi
    ldi temp, 100
    ldi temp1, 0
    cp max_lo, temp
    cpc max_hi, temp1
    brlo send_zero
    mov txbyte, zero_crossings
    rcall do_pwm
    rcall putchar
    rcall char_delay
    rjmp main_loop
    send_zero:
    rcall ledoff
    ldi txbyte, 0
    rcall putchar
    rcall char_delay
    rjmp main_loop

