News:

Let's find out together what makes a PIC Tick!

Main Menu

Program for decoding DTMF signal

Started by Giuseppe, Mar 03, 2022, 06:49 PM

Previous topic - Next topic

Giuseppe

Hi someone has a program for decoding DTMF signal ?
Thank you

top204

#1
Look at the Positron16 demos:

C:\Users\User ID\PDS\Samples24\DTMF_Decoder_GLCD.bas

and

C:\Users\User ID\PDS\Samples24\DTMF_Decoder_Mk2.bas

Giuseppe

Thanks for the reply
I have seen the listings all use a Pic 33FJ128GP802.
Do you think it is possible to convert the program to something like a pic 16f1827? without using the graphic display all I need is an alphanumeric or even a serial output


John Drew

It was me Charlie. There are plenty of the MT8870 about, both DIL and SM.
They work well. Just need a clock crystal and I've also successfully used ceramic crystal substitute.
They are voice and noise resistant and are quick to decode. 30msec of tone pair will decode correctly.
Be wary of some of the cheap modules from China. 50% might work.
John

charliecoutas

Sorry John, of course, I remember now. As it happened I ordered one of those modules, it arrived, I then discovered that the push-button phone I was trying to decode had a switch on the back: PULSE or TONE dialling! So I have a spare if anybody wants it. Where are you Giuseppe?

Charlie

Giuseppe

Here I am .
I was intrigued by the fact that an Italian amateur radio colleague made the decoder with only one pic. He used the 16f1705 I put the program in asm but I had the intention of translating it into basic. So I thought that it can also be achieved with only one MCU. Also how to distinguish good MT8870?

top204

#7
I still remember using an MT8870 chip when DTMF first came out in telephones back in the late 1980s. It was like magic. :-)

I did create a DTMF decoder on an 18F25K20 device years ago, using an FFT mechanism, but it was a bit slow to detect fast DTMF signal changes. I wrote it when I wrote the DTMF signal generator code.

I was sent some code many years ago by Gabi, that uses the Goertzel filter mechanism for DTMF decoding on a PIC18F25K20 device, but I never tried it out. I have listed it below, so you can experiment with it. It may work on an enhanced 14-bit core device with a bit of tweaking.

'****************************************************************
'*  Name    : UNTITLED.BAS                                      *
'*  Author  : [select VIEW...EDITOR OPTIONS]                    *
'*  Notice  : Copyright (c) 2010 Gabi Mihaila                   *
'*          : All Rights Reserved                               *
'*  Date    : 7/9/2010                                          *
'*  Version : 1.0                                               *
'*  Notes   :  Based on AN218 Silicon Laboratories              *
'*          :  DTMF DECODER REFERENCE DESIGN                    *
'****************************************************************

    Device = 18F25K20
    Declare Xtal = 64

    On_Hardware_Interrupt GoTo _INTERRUPT_HANDLER

    Declare Hserial_Baud = 9600

TRISA.0 = 1                             ' Setup bit-0 of PortA as an input
TRISC.6 = 1
TRISC.7 = 1

Dim Q0             As Float
Dim Q1             As Float
Dim Q2             As Float

Dim FLOAT_TEMP     As Float
Dim real           As Float
Dim imag           As Float

Dim MAGNITUDE      As Word
Dim MAGNITUDE_0    As Word
Dim MAGNITUDE_1    As Word
Dim MAGNITUDE_2    As Word
Dim MAGNITUDE_3    As Word
Dim MAGNITUDE_4    As Word
Dim MAGNITUDE_5    As Word
Dim MAGNITUDE_6    As Word
Dim MAGNITUDE_7    As Word

Dim MAX_MAG_ROW    As Word
Dim MAX_MAG_COLUMN As Word
Dim ROW            As Byte
Dim COLUMN         As Byte

Dim DTMF_CODE_DETECTED As String * 1

Dim RAW_ADC_READING       As ADRESL.Word                        ' raw AD values read
Dim TIMER1REG             As TMR1L.Word                         '
Dim TIMER0REG             As TMR0L.Word

Dim SAMPLE_NUMBER         As Byte
Dim TMR_8KHZ_ENABLED      As Byte ' BIT
Dim SAMPLE_BANK_COMPLETE  As Byte ' BIT
Dim PROC_SAMP  As Byte
Dim CHECK_FREQ As Byte

Dim delta_x         As Word
Dim x_result        As Word
Dim x_old           As Word
Symbol XMIN         =  200     ' noise level difference: dc / valid signal (0 to 1023)
                               ' adjust this for best results
Dim new_tone        As Byte    ' or bit
Dim start_goertzel  As Byte    ' or bit


Dim gain_cnt            As Byte ' word
Dim gain_calc           As Byte ' bit
Dim high_x              As Word ' initializat ca 0
Dim low_x               As Word ' initializat ca 1023
Dim gain                As Word


Symbol _LED_1 = PORTB.4
Output _LED_1

Symbol _LED_2 = PORTB.7
Output _LED_2

Symbol PEIE    INTCON.6                                 ' Peripheral Interrupt Enable
Symbol GIE     INTCON.7                                 ' Global Interrupt Enable

' declares TMR0
Symbol TMR0ON  T0CON.7
Symbol TMR0IF  INTCON.2
Symbol TMR0IE  INTCON.5
Symbol T08BIT  T0CON.6   ' pentru 25K20 utilizare 8bit sau 16bit Timer

' declares TMR1
Symbol TMR1ON  T1CON.0                                  ' Timer1 ON
Symbol TMR1IF  PIR1.0                                   ' TMR1 interrupt flag
Symbol TMR1IE  PIE1.0                                   ' TMR1 interrupt enable

Symbol FUDGE_FACTOR = 3                                 ' calibrare software pentru timere

' DECLARATII TIMER0  (sint dependente de valoarea Xtal ! )
Symbol _20mS_TICK = 25536 + FUDGE_FACTOR               ' @64 Mhz Xtal :: Calcul valori offset pentru intreruperi de 20mS

' DECLARATII TIMER 1 (sint dependente de valoarea XTAL ! )
Symbol _125uS_TICK = 57536 + FUDGE_FACTOR               ' @64 Mhz Xtal :: Calcul valori offset pentru intreruperi de 125uS
Symbol TMR0_VAL = _20mS_TICK                            '
Symbol _N = 105                                         ' number of samples to collect in a detection bank
'
' Create the actual seriel buffer in high memory
'
Dim SAMPLES_BANK[_N] As Word
'
Symbol PI = 3.14159265358979323                         ' PI constant
Symbol PIx2 = 6.28318530717958                          ' PI * 2


$define INIT_SAMPLING_BANK()    '
    Clear SAMPLE_NUMBER         '
    Clear SAMPLE_BANK_COMPLETE

' The next coeficient values are calculated in the excel file
' for the eight DTMF frequencies needed
' COEFF, SINE and COSINE

'  PRECOMPUTED VALUES ' for Sample rate = 8Khz, bins N=105
'
$define _coeff_0  1.716897587      ' Excel cell: E10
$define _coeff_1  1.652477549      ' Excel cell: F10
$define _coeff_2  1.582142069      ' etc
$define _coeff_3  1.506142932
$define _coeff_4  1.151234131
$define _coeff_5  0.947737325
$define _coeff_6  0.840714457
$define _coeff_7  0.618033989
Dim COEFF          As Float        ' where x_result = 0 to 7
'
$define _sin_0    0.512899277
$define _sin_1    0.563320058
$define _sin_2    0.611724299
$define _sin_3    0.657938726
$define _sin_4    0.817719386
$define _sin_5    0.880595532
$define _sin_6    0.907358695
$define _sin_7    0.951056516
Dim SINE           As Float         ' where x_result = 0 to 7
'
$define _cos_0    0.858448794
$define _cos_1    0.826238774
$define _cos_2    0.791071035
$define _cos_3    0.753071466
$define _cos_4    0.575617066
$define _cos_5    0.473868662
$define _cos_6    0.420357228
$define _cos_7    0.309016994
Dim COSINE         As Float         ' where x_result = 0 to 7
'

    GoTo _OVER_TIMER_HANDLER                               ' Jump over the subroutines
'Small_Micro_Model = On

_INTERRUPT_HANDLER:
    ' Verificare intrerupere overflow Timer1
    If TMR1IF = 1 Then                                 '
        TIMER1REG = _125uS_TICK                        ' reload timer1
        If TMR_8KHZ_ENABLED = 1 Then                   '
            '=====================================================================
            ' Block 1
            '=====================================================================
            'SAMPLES_BANK[SAMPLE_NUMBER] = ADIn 0       ' RAW_ADC_READING    ' read the result
            ' sau citire adc alternativa
            ADCON0.1 = 1                               ' start conversie ADC
            While ADCON0.1 = 1 : Wend                  ' Asteptam citirea valorii ADC
            x_result = RAW_ADC_READING                 ' update var
            '===============================================================================
            '                               BLOCK 2
            '                          Signal Detection
            '===============================================================================
            delta_x = x_result - x_old
            x_old = x_result                      ' salvam valoarea citita
            ' Verificam daca semnalul este peste pragul de zgomot (XMIN )
            If delta_x > XMIN Then                ' **** or ( delta_x < -XMIN ) then
                If new_tone = 1 Then              ' The required pause between two tones has passed
                   new_tone = 0                  ' Reset new_tone, only way for this code To execute again is If there
                                                 ' is a 20ms gap in the signal and Timer0 overflows.
                   start_goertzel = 1            ' A new tone has been detected, so start the DTMF detect.
                   '
                   high_x = 0                    ' preload initial values for gain calculations
                   low_x  = 1023
                   gain_cnt  = 0                 ' Reset gain counter
                   gain_calc = 1                 ' se poate incepe calcul de gain?
                   '
                EndIf
            EndIf
            '===============================================================================
            '                               BLOCK 3
            '                        Automatic Gain Control
            '===============================================================================
            ' Initially, the signal input is passed through the following gain
            ' calculation routine.  The gain is produced by dividing the maximum
            ' possible signal magnitude by the greatest input magnitude of the
            ' current tone.  This produces a gain that when multiplied with the
            ' incoming signal will increase the signal amplitude to nearly full
            ' scale. Also the routine finds the lowest input magnitude, which will
            ' be used to compute the average value of the signal for DC bias removal.
            '===============================================================================
            If start_goertzel = 1 Then                          ' New tone detected
                 If gain_calc = 1 Then                          ' Gain calculation phase
                    '
                    If x_result > high_x Then
                        high_x = x_result                       ' Find maximum value
                    Else
                       If x_result < low_x Then
                           low_x = x_result                     ' Find minimum value
                       EndIf
                    EndIf
                    '
                    Inc gain_cnt '

                    If  gain_cnt >= 70 Then
                        gain_cnt  = 0                           ' Reset gain counter
                        gain_calc = 0                           ' Reset gain calculation flag
                        ' compute gain
                        gain =  1024 / (high_x - low_x)         ' 1024 daca e 10 biti A/D
                        ' low_x will contain the average value
                        low_x = low_x + ((high_x - low_x) >> 1) ' low_x += (high_x - low_x)>>1'

                    EndIf
                Else                                            ' Gain calculation completed
                    ' !!! debug
                    If x_result < low_x Then                    ' *** for 10 bit A/D
                       x_result = 0
                    Else
                       x_result = (x_result - low_x) * gain     ' Scale input to nearly full scale of ADC0, And remove DC bias
                    EndIf
                EndIf
                '==========================================================================
                ' Block 4
                '==========================================================================
                SAMPLES_BANK[SAMPLE_NUMBER] = x_result     ' RAW_ADC_READING
                '
                Inc SAMPLE_NUMBER
                If SAMPLE_NUMBER = _N Then
                    SAMPLE_BANK_COMPLETE = 1               ' notificam terminarea unei transe de sampling
                    TMR_8KHZ_ENABLED = 0                   ' disable 8khz timer
                    TMR1IE   = 0                           ' and disable interrupts pe TMR1
                    start_goertzel = 0                     ' oprim starea de analiza semnal
                    TIMER0REG = TMR0_VAL                   ' reload TMR0 registers (TMR0L and TMR0H)
                    TMR0IE = 1                             ' enable Timer de pauza intre semnale
                    TMR0ON = 1                             ' start timer 0
                EndIf
           EndIf
           '===============================================================================
        EndIf
        Clear TMR1IF                                   ' clear timer1 int flag so we wait for new interrup
    EndIf
    '
    ' Verificare intrerupere overflow Timer0
    If TMR0IF = 1 Then                                 ' TMR0IF
       TMR0ON = 0                                      ' stop the timer
       'TIMER0REG = TMR0_VAL              ' reload TMR0 registers (TMR0L and TMR0H)
       TMR0IE = 0                                      ' si facem si disable la intreruperi pe TMR0
       new_tone = 1                                    ' anuntam ca se poate face procesare Goertzel
       TMR_8KHZ_ENABLED = 1                   ' enable 8khz timer
       TMR1IE = 1  ' re-enable timer 1
       Clear TMR0IF                                    ' resetam steagul de intrerupere pe TMR0
    EndIf
    '
    Retfie Fast                                        ' Exit from the interrupt, restoring the WREG, STATUS, and BSR registers
'------------------------------------------------------------------------------
ENABLE_TMR1_INTERRUPT Macro                            ' activare intrerupere TMR1
    GoSub _ENABLE_TMR1
    Endm
#ifdef ENABLE_TMR1_INTERRUPT#REQ
_ENABLE_TMR1:
    INTCON = 0
    INTCON.7 = 1                                       ' Enable General Interrupts
    INTCON.6 = 1                                       ' Enable Peripherals Interrupts
    TMR1IF   = 0                                       ' Clear TMR1 Interrupt Flag
    TMR1IE   = 1                                       ' TMR1 Interrupt Enable
    Return
#endif
'
DISABLE_TMR1_INTERRUPT Macro                           ' dezactivare intrerupere TMR1
    GoSub _DISABLE_TMR1
    Endm
#ifdef DISABLE_TMR1_INTERRUPT#REQ
_DISABLE_TMR1:
    'INTCON.7 = 0                                       ' Disable General Interrupts
    'INTCON.6 = 0                                       ' Disable Peripherals Interrupts
    'TMR1IF   = 0
    TMR1IE   = 0                                        ' TMR1 Interrupt Disable
    Return
#endif

'------------------------------------------------------------------------------
_OVER_TIMER_HANDLER:

' INITIALIZARE
'
SETARE_TIMER:
    ' setare TMR0
    T0CON  = %00000100             ' TMR0 cu 1:32 prescaler
    T08BIT = 0                     ' TMR0 declarat de tip 16bit
    TMR0ON = 0                     ' TMR0 oprit
    TMR0IF = 0                     ' clear TMR0 int flag
    TMR0IE = 0                     ' TMR0 Int Disabled

    ' setare TMR1
    T1CON   = %10000000            ' SET UP TMR1 TO HAVE 1:1 PRESCALER AND ACT AS A TIMER
    TMR1IF  = 0                    ' CLEAR TMR1 INTERRUPT FLAG
    TMR1IE  = 1                    ' ENABLE TMR1 AS PERIPHERAL INTERRUPT SOURCE
    TMR1ON  = 0                    ' TMR1 not running

SETARE_ADC:
     ADCON2 = %10101111       ' Right justify, FRC and 12 TAD ACQ time
     ADCON1 = 0               ' Vref = VDD/VSS
     ADCON0.0 = 1             ' Turn ON the AD module
     ANSEL.0 = 1              ' set RA.0 to Analog Input
'-------------------------------------------------------------------------------
    ' Initializare variabile

    new_tone = 1                       '  sintem in perioada ce analiza ( = 1 adica asteptam semnal nou)
    x_result = 0
    x_old = 0
    delta_x = 0
    start_goertzel = 0
'===============================================================================
    TMR1ON = 0                                  ' TMR1 is off
    INIT_SAMPLING_BANK()                        ' prepare sampling related flags, counters....
    Clear TIMER1REG                             '
    TIMER1REG = _125uS_TICK                     ' incarcare TMR1 cu valoarea "tick" de 125uS
    TMR_8KHZ_ENABLED = 1                        ' enable counter timer de 8khz
    ENABLE_TMR1_INTERRUPT                       ' activam intreruperi pe TMR1 overflow
    TMR1ON = 1                                  ' start TMR1
'================================================================================

MAIN_LOOP:
    '
    While 1 = 1                                 ' waiting for sampling bank to complete
          '
          If SAMPLE_BANK_COMPLETE = 1 Then      ' acum putem incepe calclulele pentru fiecare frecventa

                 ' incercam procesare pentru toate cele 8 frecvente
                 For CHECK_FREQ = 0 To 7         ' number of bins - 1
                     Select CHECK_FREQ
                            Case 0
                                 COEFF  = _coeff_0
                                 SINE   = _sin_0
                                 COSINE = _cos_0
                            Case 1
                                 COEFF = _coeff_1
                                 SINE   = _sin_1
                                 COSINE = _cos_1
                            Case 2
                                 COEFF = _coeff_2
                                 SINE   = _sin_2
                                 COSINE = _cos_2
                            Case 3
                                 COEFF = _coeff_3
                                 SINE   = _sin_3
                                 COSINE = _cos_3
                            Case 4
                                 COEFF = _coeff_4
                                 SINE   = _sin_4
                                 COSINE = _cos_4
                            Case 5
                                 COEFF = _coeff_5
                                 SINE   = _sin_5
                                 COSINE = _cos_5
                            Case 6
                                 COEFF = _coeff_6
                                 SINE   = _sin_6
                                 COSINE = _cos_6
                            Case 7
                                 COEFF = _coeff_7
                                 SINE   = _sin_7
                                 COSINE = _cos_7
                     EndSelect
                     ' procesare Q0, Q1, Q2
                     Q1 = 0                                ' start with these two cleared
                     Q2 = 0
                     PROC_SAMP = 0                         ' incepem cu elementul 0
                     '
                     Repeat
                         Q0 = COEFF  * Q1
                         FLOAT_TEMP = Q2 + SAMPLES_BANK[PROC_SAMP]
                         Q0 = Q0 - FLOAT_TEMP
                         Q2 = Q1
                         Q1 = Q0
                         Inc  PROC_SAMP
                     Until PROC_SAMP = _N
                     '
                     Nop
                     ' now get the magnitude
                     ' varianta complexa cu imaginar & real
                     FLOAT_TEMP = Q2 * COSINE
                     real = Q1 - FLOAT_TEMP
                     imag = Q2 * SINE
                     real = real * real
                     imag = imag * imag
                     FLOAT_TEMP = real + imag
                     MAGNITUDE = Sqr  FLOAT_TEMP
                     '
                     Select CHECK_FREQ
                            Case 0
                                 MAGNITUDE_0 = MAGNITUDE
                                 If MAGNITUDE_0 > MAX_MAG_ROW Then
                                    MAX_MAG_ROW = MAGNITUDE
                                    ROW = 0
                                 EndIf
                            Case 1
                                 MAGNITUDE_1 = MAGNITUDE
                                 If MAGNITUDE_1 > MAX_MAG_ROW Then
                                    MAX_MAG_ROW = MAGNITUDE
                                    ROW = 1
                                 EndIf
                            Case 2
                                 MAGNITUDE_2 = MAGNITUDE
                                 If MAGNITUDE_2 > MAX_MAG_ROW Then
                                    MAX_MAG_ROW = MAGNITUDE
                                    ROW = 2
                                 EndIf
                            Case 3
                                 MAGNITUDE_3 = MAGNITUDE
                                 If MAGNITUDE_3 > MAX_MAG_ROW Then
                                    MAX_MAG_ROW = MAGNITUDE
                                    ROW = 3
                                 EndIf
                            Case 4
                                 MAGNITUDE_4 = MAGNITUDE
                                 If MAGNITUDE_4 > MAX_MAG_COLUMN Then
                                    MAX_MAG_COLUMN = MAGNITUDE
                                    COLUMN = 4
                                 EndIf
                            Case 5
                                 MAGNITUDE_5 = MAGNITUDE
                                 If MAGNITUDE_5 > MAX_MAG_COLUMN Then
                                    MAX_MAG_COLUMN = MAGNITUDE
                                    COLUMN = 5
                                 EndIf
                            Case 6
                                 MAGNITUDE_6 = MAGNITUDE
                                 If MAGNITUDE_6 > MAX_MAG_COLUMN Then
                                    MAX_MAG_COLUMN = MAGNITUDE
                                    COLUMN = 6
                                 EndIf
                            Case 7
                                 MAGNITUDE_7 = MAGNITUDE
                                 If MAGNITUDE_7 > MAX_MAG_COLUMN Then
                                    MAX_MAG_COLUMN = MAGNITUDE
                                    COLUMN = 7
                                 EndIf
                     EndSelect
                 Next
                 '
                 GoSub DECODE_DTMF_CHAR    ' decodificam caracterul DTMF receptionat
                 ' debug
                 HRSOut "DTMF CHAR = ", DTMF_CODE_DETECTED, 13, 10
                 '
                 SAMPLE_BANK_COMPLETE = 0      ' resetare steag de stare
          EndIf
    Wend

(* Cautam dupa celel doua frecvente gasite cu magnitudinea cea mai mare
ROW     = FRECVENTA_1
COLUMN  = FRECVENTA_2
*)
DECODE_DTMF_CHAR:
    If ROW = 0 Then
            Select COLUMN
                Case 4
                    DTMF_CODE_DETECTED = "1"
                Case 5
                    DTMF_CODE_DETECTED = "2"
                Case 6
                    DTMF_CODE_DETECTED = "3"
                Case 7
                    DTMF_CODE_DETECTED = "A"
            EndSelect
       ElseIf ROW = 1 Then
            Select COLUMN
                Case 4
                    DTMF_CODE_DETECTED = "4"
                Case 5
                    DTMF_CODE_DETECTED = "5"
                Case 6
                    DTMF_CODE_DETECTED = "6"
                Case 7
                    DTMF_CODE_DETECTED = "B"
            EndSelect
       ElseIf ROW = 2 Then
            Select COLUMN
                Case 4
                    DTMF_CODE_DETECTED = "7"
                Case 5
                    DTMF_CODE_DETECTED = "8"
                Case 6
                    DTMF_CODE_DETECTED = "9"
                Case 7
                    DTMF_CODE_DETECTED = "C"
            EndSelect
       ElseIf ROW = 3 Then
            Select COLUMN
                Case 4
                    DTMF_CODE_DETECTED = "*"
                Case 5
                    DTMF_CODE_DETECTED = "0"
                Case 6
                    DTMF_CODE_DETECTED = "#"
                Case 7
                    DTMF_CODE_DETECTED = "D"
            EndSelect
    EndIf
    Return

'-------------------------------------------------------------
' Setup the fuses on a PIC18F25K20 device for the 4xPLL
'
Config_Start
    FOSC = HSPLL        ' HS oscillator, PLL enabled and under software control
    Debug = Off         ' Background debugger disabled' RB6 and RB7 configured as general purpose I/O pins
    XINST = Off         ' Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
    STVREN = Off        ' Reset on stack overflow/underflow disabled
    WDTEN = Off         ' WDT disabled (control is placed on SWDTEN bit)
    FCMEN = Off         ' Fail-Safe Clock Monitor disabled
    IESO = Off          ' Two-Speed Start-up disabled
    WDTPS = 128         ' Watchdog is 1:128
    BOREN = Off         ' Brown-out Reset disabled in hardware and software
    BORV = 18           ' VBOR set to 1.8 V nominal
    MCLRE = On          ' MCLR pin enabled, RE3 input pin disabled
    HFOFST = Off        ' The system clock is held Off until the HF-INTOSC is stable.
    LPT1OSC = Off       ' T1 operates in standard power mode
    PBADEN = Off        ' PORTB<4:0> pins are configured as digital I/O on Reset
    CCP2MX = PORTC      ' CCP2 input/output is multiplexed with RC1
    LVP = Off           ' Single-Supply ICSP disabled
    Cp0 = Off           ' Block 0 (000800-001FFFh) not code-protected
    CP1 = Off           ' Block 1 (002000-003FFFh) not code-protected
    CPB = Off           ' Boot block (000000-0007FFh) not code-protected
    CPD = Off           ' Data eeprom not code-protected
    WRT0 = Off          ' Block 0 (000800-001FFFh) not write-protected
    WRT1 = Off          ' Block 1 (002000-003FFFh) not write-protected
    WRTB = Off          ' Boot block (000000-0007FFh) not write-protected
    WRTC = Off          ' Configuration registers (300000-3000FFh) not write-protected
    WRTD = Off          ' Data eeprom not write-protected
    EBTR0 = Off         ' Block 0 (000800-001FFFh) not protected from table reads executed in other blocks
    EBTR1 = Off         ' Block 1 (002000-003FFFh) not protected from table reads executed in other blocks
    EBTRB = Off         ' Boot block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End

Gabi

I remember it was used on some Sell-call project at that time, and scored a high repeatability on detection rate.
The coefficients were pre computed in an Excel file (included in the zip attached), where the eight DTMF frequencies are listed.
Input audio data was sampled at 8Khz, and samples stored in a number of 105 bins with approximate 80Hz bandwidth each,
which actually seems to be enough for such application.
Detailed info could be gathered from the references below.

Simple VSM attached.

Reference:
https://www.embedded.com/the-goertzel-algorithm/
https://www.silabs.com/documents/public/application-notes/an218.pdf
GL & 73
YO4WM

top204

#9
Excellent code Gabi, and many thanks for the simulator and full source code.

So the Goertzel mechanism is a type of FFT mechanism, but more efficient and faster? Is there a reverse of it by any chance, like IFFT, where a certain bin can be brought back to analogue and used as a comb filter?

I'll run it through the simulator over the weekend. Isn't it wonderful to learn something new. :-)

Gabi

While Goertzel could be having a higher order of complexity than FFT, it seems to be more numerically efficient only when dealing with a small number of selected frequencies.

Have not used and inverse function of it, I guess it could be something like:  iGoertzel(Goertzel(x)) == x, but hard to tell if doable.

For some time series filtering to achieve a faster response I was using a multiple pass recursive MAF due to the extended range of impulse response I was expecting.

For other DSP works I believe the proven FIR/IIR algos should be used.
GL & 73
YO4WM

top204

I've never gotten an IIR filter to work well. It has always oscillated. :-)

The FIR filtering is very fast and quite efficient, but more difficult to adjust the filter frequency in real time.

Giuseppe

Thanks Gabi for sharing your program.

John Drew

#13
Another problem is that of noise or speech immunity. The software needs to be good enough to avoid false decodes.
My applications involve radio.

keytapper

At Roman Black site there's some code, but it's mostly C. Not a difficult task to translate it to basic.
I saw other method which are using 12F6xx or 16F6xx to decode the DTMF. Unfortunately nothing found in basic.
Ignorance comes with a cost

Giuseppe

In fact, some have even succeeded with small devices such as 12F ...

Giuseppe

#16
Hi I'm trying the code provided by Top 204 based on 18f25k20. I have set the 18f25k22 as mcu.
I was wondering the interface that connects to the audio can be like the one in the photo below?

Giuseppe

#17

Giuseppe

I kindly ask Gabi if he can publish the schematic of the hardware of the dtmf decoder?
thank you

Gabi

@Giuseppe 
Unfortunately I do not have it anymore, was something related to a radio repeater automation and happened more than 12 years ago  :)

Eventually it should be related to your project audio input source specs, or if you start from scratch, or just testing, you might want to try some simple circuit  like this below (reference: Google search):

GL & 73
YO4WM