;***************************************************************************
;* A P P L I C A T I O N   N O T E   F O R   T H E   A V R   F A M I L Y
;*
;* Number               : AVR314
;* File Name            : "dtmf.asm"
;* Title                : DTMF Generator
;* Date                 : 00.06.27
;* Version              : 1.1
;* Target MCU           : Any AVR with SRAM, 8 I/O pins and PWM
;*
;* DESCRIPTION
;* This Application note describes how to generate DTMF tones using a single
;* 8 bit PWM output.
;*
;***************************************************************************

.include "4414def.inc"

;**************************************************************************
;  REGISTERS
;**************************************************************************
.def   XL_Tmp = r1                    	; low  part temporary register
.def   XH_Tmp = r2                    	; high part temporary register

; Number of Element (Position) in LUT in Extended format
.def   XL_LUTbExt   = r3              	; number of LUT-Element of frequency a (low byte)
.def   XH_LUTbExt   = r4              	; number of LUT-Element of frequency a (high byte)
.def   XL_LUTaExt   = r5              	; number of LUT-Element of frequency b (low byte)
.def   XH_LUTaExt   = r6              	; number of LUT-Element of frequency b (high byte)

.def   OCR_RelValb = r7               	; low  frequency reload value to be written to OCR                           
.def   OCR_RelVala = r8               	; High frequency reload value to be written to OCR

.def   x_SWa = r9                    	; step width in LUT to get high frequency
.def   x_SWb = r10                   	; step width in LUT to get low frequency

.def   count    = r18                   ; temporary counter register
.def   tmp      = r16                   ; temp register

.def   input    = r17                   ; input from portB

;**************************************************************************
;**************************************************************************
.equ     Xtal      = 8000000            ; system clock frequency
.equ     prescaler = 1                  ; timer1 prescaler
.equ     N_samples = 128                ; Number of samples in lookup table
.equ     Fck = Xtal/prescaler           ; timer1 working frequency
.equ	 delaycyc  = 10			; port B setup delay cycles

;**************************************************************************
;  PROGRAM START - EXECUTION STARTS HERE
;**************************************************************************
.cseg
.org    $0
    rjmp    start                       ; Reset handler
.org    OVF1addr
    rjmp    tim1_ovf                    ; Timer1 overflow Handle

;**************************************************************************
; Interrupt timer1
;**************************************************************************
tim1_ovf:
   push   tmp    			; Store temporary register
   in     tmp,SREG
   push   tmp                           ; Store status register
   push   ZL
   push   ZH                            ; Store Z-Pointer
   push   r0                            ; Store R0 Register

;high frequency
   mov    XL_Tmp,XL_LUTaExt
   mov    XH_Tmp,XH_LUTaExt
   add    XL_LUTaExt,x_SWa
   clr    tmp                           ; (tmp is cleared, but not the carry flag)
   adc    XH_LUTaExt,tmp                ; Refresh pointer for the next sample
   rcall  getsample                     ; read from Sin Wave Sample Table
   mov    OCR_RelVala,r0                ; OCR_RelVala = high frequency value

;low frequency
   mov    XL_Tmp,XL_LUTbExt
   mov    XH_Tmp,XH_LUTbExt
   add    XL_LUTbExt,x_SWb
   clr    tmp                           ; (tmp is cleared, but not the carry flag)
   adc    XH_LUTbExt,tmp                ; refresh pointer for the next sample
   rcall  getsample                     ; read from Sin Wave Sample Table
   mov    OCR_RelValb,r0                ; OCR_RelValb = low frequency value

; scale amplitude
   ldi     tmp,2
   add     OCR_RelValb,tmp
   lsr     OCR_RelValb
   lsr     OCR_RelValb                  ; divide 4 and round off
   sub     r0,OCR_RelValb               ; 4/4 - 1/4 = 3/4
   mov     OCR_RelValb,r0               ; now OCR_RelValb has the right amplitude

   clr     tmp
   out     OCR1AH,tmp
   mov     tmp,OCR_RelVala
   add     tmp,OCR_RelValb
   out     OCR1AL,tmp                   ; send the sum of the two amplitudes to PWM

   pop     r0                           ; Restore R0 Register
   pop     ZH
   pop     ZL                           ; Restore Z-Pointer
   pop     tmp
   out     SREG,tmp                     ; Restore SREG
   pop     tmp                          ; Restore temporary register;
   reti

;*********************************
; RESET Interrupt
;*********************************
start:
    sbi   DDRD,PD5                      ; Set pin PD5 as output
    ldi   tmp,low(RAMEND)
    out   SPL,tmp
    ldi   tmp,high(RAMEND)
    out   SPH,tmp                       ; Initialize Stackpointer

;Initialization of the registers
    clr   XL_LUTaExt
    clr   XH_LUTaExt
    clr   XL_LUTbExt
    clr   XH_LUTbExt                    ; Set both table ponters to 0x0000

;enable timer1 interrupt
    ldi   tmp,(1<<TOIE1)
    out   TIMSK,tmp                     ; Enable Timer1_ovf interrupt

;set timer1 PWM mode
     ldi   tmp,(1<<PWM10)+(1<<COM1A1)
     out   TCCR1A,tmp                   ; 8 bit PWM not reverse (Fck/510)
     ldi   tmp,(1<<CS10)
     out   TCCR1B,tmp                   ; prescaler = 1
     sei                                ; Enable interrupts

;**************************************************************************
; MAIN
; Read from portB (eg: using evaluation board switch) which
; tone to generate, extract mixing high frequency
; (column) and low frequency (row), and then
; fix x_SWa and x_SWb
; row    -> PINB high nibble
; column -> PINB low nibble
;**************************************************************************

main:
;high frequency (Esteem only high nibble that is row)
;PB_High_Nibble:
   ldi   tmp,0x0F
   out 	 DDRB,tmp			; High nibble Input / Low nibble. Outp. 
   ldi   tmp,0xF0
   out   PORTB,tmp			; High nibble PullUp / Low nibble Zero Outp. 
   rcall delay
   clr   count
   in    input,PINB

main10:
   inc   count
   lsl   input	    
   brcc  main20
   ldi   tmp,4
   cp    count,tmp
   brne  main10
   clr   x_SWb
   clr   count
   rjmp  main30

main20:
   dec   count
   ldi   ZL,low(frequencyL*2)
   ldi   ZH,high(frequencyL*2)
   add   ZL,count
   clr   tmp
   adc   ZH,tmp
   lpm
   mov   x_SWb,r0                    	; this is low frequency x_SW
   clr   count
   
;low frequency
;PB_Low_Nibble:
main30:
   ldi   tmp,0xF0
   out 	 DDRB,tmp			; High nibble. Outp. / Low nibble Input  
   ldi   tmp,0x0F
   out   PORTB,tmp			; High nibble Zero Outp. / Low nibble PullUp 
   rcall delay
   in    input,PINB
   swap  input

main40:
   inc   count
   lsl   input	    
   brcc  main50
   ldi   tmp,4
   cp    count,tmp
   brne  main40
   clr   x_SWa
   rjmp  main

main50:                                 ; there is a zero bit in count-1  position
   dec   count
   ldi   ZL,low(frequencyH*2)
   ldi   ZH,high(frequencyH*2)
   add   ZL,count
   clr   tmp
   adc   ZH,tmp
   lpm
   mov   x_SWa,r0                    	; this is high frequency x_SW
   rjmp  main


;******************   DELAY   ***********************************
;****************************************************************
delay:
   ldi	  tmp,delaycyc
loop:
   dec	  tmp
   brne	  loop
ret


;******************   GET SAMPLE   ******************************
;****************************************************************
getsample:
   ldi    tmp,0x0f
   and    XH_Tmp,tmp

; ROUND	- add four
   ldi    tmp,4
   add    XL_Tmp,tmp
   clr    tmp
   adc    XH_Tmp,tmp

; shift (divide by eight):
   lsr    XH_Tmp
   ror    XL_Tmp
   lsr    XH_Tmp
   ror    XL_Tmp
   lsr    XH_Tmp
   ror    XL_Tmp

   ldi    tmp,0x7f
   and    XL_Tmp,tmp                  ; module 128 (samples number sine table)

   ldi    ZL,low(sine_tbl*2)
   ldi    ZH,high(sine_tbl*2)
   add    ZL,XL_Tmp
   clr    tmp
   adc    ZH,tmp                        ; Z is a pointer to the correct
                                        ; sine_tbl value
   lpm
   ret

;*************************** SIN TABLE *************************************
; Samples table : one period sampled on 128 samples and
; quantized on 7 bit
;******************************************************************************
sine_tbl:
.db 64,67
.db 70,73
.db 76,79
.db 82,85
.db 88,91
.db 94,96
.db 99,102
.db 104,106
.db 109,111
.db 113,115
.db 117,118
.db 120,121
.db 123,124
.db 125,126
.db 126,127
.db 127,127
.db 127,127
.db 127,127
.db 126,126
.db 125,124
.db 123,121
.db 120,118
.db 117,115
.db 113,111
.db 109,106
.db 104,102
.db 99,96
.db 94,91
.db 88,85
.db 82,79
.db 76,73
.db 70,67
.db 64,60
.db 57,54
.db 51,48
.db 45,42
.db 39,36
.db 33,31
.db 28,25
.db 23,21
.db 18,16
.db 14,12
.db 10,9
.db 7,6
.db 4,3
.db 2,1
.db 1,0
.db 0,0
.db 0,0
.db 0,0
.db 1,1
.db 2,3
.db 4,6
.db 7,9
.db 10,12
.db 14,16
.db 18,21
.db 23,25
.db 28,31
.db 33,36
.db 39,42
.db 45,48
.db 51,54
.db 57,60

;*******************************  x_SW  ***********************************
;Table of x_SW (excess 8): x_SW = ROUND(8*N_samples*f*510/Fck)
;**************************************************************************

;high frequency (coloun)
;1209hz  ---> x_SW = 79
;1336hz  ---> x_SW = 87
;1477hz  ---> x_SW = 96
;1633hz  ---> x_SW = 107

frequencyH:
.db 107,96
.db 87,79

;low frequency (row)
;697hz  ---> x_SW = 46
;770hz  ---> x_SW = 50
;852hz  ---> x_SW = 56
;941hz  ---> x_SW = 61

frequencyL:
.db 61,56
.db 50,46




