
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[8] ' Circular buffer - raw samples (directly after ADC)
dim output_f1 as Integer[8] ' Circular buffer - samples after IIR BP filter
dim output_f2 as Integer[8] ' 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.