The example shows a method of detecting DTMF signal.The following detection algorithm has been applied. The level of input signal denoting the presence of DTMF signal is awaited. Frequency estimation over 1024 samples is performed. The sampling frequency is 20kHz. In other words, the process of estimation lasts approximately 50ms. The minimum length of one character is 65ms. The minimum pause after one character is 80ms. For this reason each estimation is followed by an 80ms pause, and then the next character is awaited. The estimate of the signal frequency can be performed by counting zero crossings, as shown in Fig. 14-6.
' This project is designed to work with PIC P30F6014A. It has been tested ' on dsPICPRO3 board with 10.0 MHz crystal and 8xPLL. It should work with any ' other crystal. Note: the maximum operating frequency for dsPIC is 120MHz. ' With minor adjustments, this example should work with any other dsPIC MCU program DTMFin ' *** DAC pinout *** ' const LOAD_PIN = 2 ' DAC load pin const CS_PIN = 1 ' DAC CS pin const ' Filter setup: ' Filter kind: IIR ' Filter type: lowpass filter ' Filter order: 4 ' Design method: Chebyshev type II BUFFER_SIZE = 8 FILTER_ORDER = 4 BPF1_COEFF_B as Integer[FILTER_ORDER+1] = ( 0x1BD7, 0xAB5D, 0x753A, 0xAB5D, 0x1BD7) BPF1_COEFF_A as Integer[FILTER_ORDER+1] = ( 0x2000, 0xA1C7, 0x6C59, 0xC6EA, 0x0BDE) BPF1_SCALE_B = 0 BPF1_SCALE_A = -2 ' Filter setup: ' Filter kind: IIR ' Filter type: Highpass filter ' Filter order: 4 ' Design method: Chebyshev type II BPF2_COEFF_B as Integer[FILTER_ORDER+1] = (0x0BF7, 0xD133, 0x45AF, 0xD133, 0x0BF7) BPF2_COEFF_A as Integer[FILTER_ORDER+1] = (0x1000, 0xCA8B, 0x44B5, 0xD7E5, 0x08F3) BPF2_SCALE_B = -3 BPF2_SCALE_A = -3 MinLevel = 18 ' Min voltage offset level on ADC that can be detected as DTMF dim SignalActive as boolean ' Indicator (if input1 signal exists) dim sample as Integer ' Temp variable used for reading from ADC dim Key as Char ' Detected character dim f as longint ' Detected frequency dim SampleCounter as word ' Indicates the number of samples in circular buffer dim sample_index as word ' Index of next sample dim input1 as Integer ' Circular buffer - raw samples (directly after ADC) dim output_f1 as Integer ' Circular buffer - samples after IIR BP filter dim output_f2 as Integer ' Circular buffer - samples after IIR BP filter dim TransitLow as Integer TransitHigh as Word ' Counts of transitions (low, high freq) dim sgnLow as Integer sgnHigh as Integer ' Current signs of low and high freq signal dim KeyCnt as integer ' Number of recived DTFM and displayed on LCD sub procedure Estimate dim fd as Word ' DTMF frequencies: ' 1209 Hz 1336 Hz 1477 Hz 1633 Hz ' 697 Hz 1 2 3 A ' 770 Hz 4 5 6 B ' 852 Hz 7 8 9 C ' 941 Hz * 0 # D ' ' calculating index of lower freq f = TransitLow*20000 ' f = No_Of_Transitions*Sampling_Freq [Hz] f = f >> 11 ' f = f div 2048 = f/2/1024 (2 transitions in each period) if f < 733 then fd=1 ' Index of Low_freq = 1 else if f < 811 then fd=2 ' Index of Low_freq = 2 else if f < 896 then fd=3 ' Index of Low_freq = 3 else fd=4 ' Index of Low_freq = 4 end if end if end if ' calculating index of higher freq f=TransitHigh*20000 ' f = No_Of_Transitions*Sampling_Freq f = f >> 11 ' f = f/2048 = f/2/1024 (2 transitions in each period) if f<1272 then fd=fd+10 ' encode Index of higher freq as 10 else if f<1406 then fd=fd+20 ' encode Index of higher freq as 20 else if f<1555 then fd=fd+30 ' encode Index of higher freq as 30 else fd=fd+40 ' encode Index of higher freq as 40 end if end if end if select case fd ' Reading of input1 char from DTMF matrix case 11 Key= "1" case 12 Key= "4" case 13 Key= "7" case 14 Key= "*" case 21 Key= "2" case 22 Key= "5" case 23 Key= "8" case 24 Key= "0" case 31 Key="3" case 32 Key="6" case 33 Key="9" case 34 Key="#" case 41 Key="A" case 42 Key="B" case 43 Key="C" case 44 Key="D" end select ' diplay recived char on second row of LCD if(KeyCnt >= 16) then ' if second row is full erase it and postion cursor at first column Lcd_Cmd(LCD_SECOND_ROW) Lcd_Out_CP(" ") Lcd_Cmd(LCD_SECOND_ROW) KeyCnt = 0 ' reset recived DTFM signals counter end if Lcd_Chr_CP(Key) ' output recived on LCD inc(KeyCnt) ' increment counter end sub sub procedure DAC_Output(dim valueDAC as word) LATC.CS_PIN = 0 ' CS enable for DAC ' filter output range is 16-bit number DAC input1 range is 12-bit number valueDAC = valueDAC >> 4 ' now both numbers are 12-bit but filter output is signed and DAC input1 is unsigned. ' Half of DAC range 4096/2=2048 is added to correct this valueDAC = valueDAC + 2048 SPI2BUF = 0x3000 or valueDAC ' write valueDAC to DAC (0x3 is required by DAC) while (SPI2STAT.1 = 1) ' wait for SPI module to finish sending nop wend LATC.CS_PIN = 1 ' CS disable for DAC end sub sub procedure InitDec() SampleCounter = 1024 ' Init low-freq transitions counter TransitLow = 0 ' Init high-freq transitions counter TransitHigh = 0 ' Init input1 circular buffer (zero-filled) Vector_Set(input1, 8, 0) ' Init filtered circular buffer (zero-filled) Vector_Set(output_f1, 8, 0) ' Init filtered circular buffer (zero-filled) Vector_Set(output_f2, 8, 0) ' Points on first element of circular buffer sample_index = 0 ' Current sign is positive sgnLow=0 ' Current sign is positive sgnHigh=0 DAC_Output(0) end sub sub procedure ADC1Int org $2A sample = ADCBUF0 ' read input1 ADC signal if (sample > 2048+MinLevel) and not(SignalActive) then ' detecting signal SignalActive = true ' activate estimation algorithm InitDec() ' initialize variables end if ' since ADC is configured to get samples as intgers ' mean value of input1 signal is expected to be located at ' middle of ADC voltage range sample = sample << 4 sample = sample-(2048 << 4) 'expanding signal to full scale ' now sample is ready to be filtred if SignalActive then input1[sample_index] = sample ' Write sample in circular buffer ' Filter input1 signal (for low-freq estimation) sample = IIR_Radix(BPF1_SCALE_B, BPF1_SCALE_A, @BPF1_COEFF_B, @BPF1_COEFF_A, FILTER_ORDER+1, @input1, BUFFER_SIZE, @output_f1, sample_index) DAC_Output(sample) ' output filtred signal to DAC for Visual check output_f1[sample_index]=sample ' transition_Low? if sample.15<>sgnLow then ' If transition trough 0 sgnLow=sample.15 ' save current sign Inc(TransitLow) ' Increment transition counter end if ' Filter input1 signal (for high-freq estimation) sample = IIR_Radix(BPF2_SCALE_B, BPF2_SCALE_A, @BPF2_COEFF_B, @BPF2_COEFF_A, FILTER_ORDER+1, @input1, BUFFER_SIZE, @output_f2, sample_index) output_f2[sample_index]=sample ' Write filtered signal in buffer ' transition_High? if sample.15<>sgnHigh then ' If transition sgnHigh=sample.15 ' save current sign Inc(TransitHigh) ' Increment transition counter end if sample_index=(sample_index+1) and 7 ' Move pointer on next element dec(SampleCounter) ' Decrement sample counter if SampleCounter = 0 then ' If all of 1024 samples are readed SignalActive=false ' Deactivate estimation algorithm Estimate() ' Read estimated character DAC_Output(0) ' set DAC output to 0 Delay_ms(80) ' Wait for next char end if end if IFS0.11 = 0 ' clear ADC complete IF end sub sub procedure Timer1Int org $1A ADCON1.1 = 1 ' ASAM=0 and SAMP=1 begin sampling ADCON1.15 = 1 ' start ADC IFS0.3 = 0 ' clear Timer1 IF end sub main: KeyCnt = 0 ' set to 0 SignalActive = false ' no signal is present ADPCFG = $FFFF ' configure pins as digital Lcd_Init_DsPicPro3() ' initialize LCD Lcd_Out(1,1,"tone is:") ' print message at first row Lcd_Cmd(LCD_SECOND_ROW) ' position cursor at second row LATC.CS_PIN = 1 ' set DAC CS to inactive LATC.LOAD_PIN = 0 ' set DAC LOAD to inactive TRISC.LOAD_PIN = 0 ' configure DAC LOAD pin as output TRISC.CS_PIN = 0 ' configure DAC CS pin as output ' Initialize SPI2 module Spi2_Init_Advanced(_SPI_MASTER, _SPI_16_BIT, _SPI_PRESCALE_SEC_1, _SPI_PRESCALE_PRI_1, _SPI_SS_DISABLE, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_HIGH, _SPI_ACTIVE_2_IDLE) TRISB.10 = 1 ' configure RB10 pin as input1 ADPCFG = $FBFF ' configure RB10 pin as analog ADCON1 = $00E0 ' auto-convert, auto-conversion ADCON2 = $0000 ADCON3 = $021A ' sampling time=2*Tad, minimum Tad selected ADCHS = $000A ' sample input1 on RB10 ADCSSL = 0 ' no input1 scan ' clear interrupt flags IFS0 = 0 IFS1 = 0 IFS2 = 0 INTCON1 = $8000 ' disable nested interrupts INTCON2 = 0 IEC0 = $0808 ' enable Timer1 and ADC interrupts IPC0.12 = 1 ' Timer1 interrupt priority level = 1 IPC2.13 = 1 ' ADC interrupt priority level = 2 ' Timer1 input1 clock is Fosc/4. Sampling frequency is 20kHz. Timer should ' raise interrupt every 50 microseconds. PR1 = (Fosc[Hz]/4) / 20000Hz = Fosc[kHz]/(4*20) PR1 = Clock_kHz() div 80 T1CON = $8000 ' Enable Timer1 while true ' Infinite loop nop wend end.