-------------------------file: dtmf.asm | 1 |;;;----------------------------------------------------------------------------------- | 2 |;;; | 3 |;;; dtmf.asm: A DTMF decoder for the Alesis 1K DSP | 4 |;;; | 5 |;;; Description: This program listens to an audio signal and outputs a message | 6 |;;; corresponding to which touch-tone button, if any, it hears. | 7 |;;; | 8 |;;; Input: The audio signal. | 9 |;;; | 10 |;;; Output: The detected button number / 2^12, or zero if no valid touch-tone is heard. | 11 |;;; | 12 |;;; Background: When you press a button on a touch-tone phone, what you hear are two | 13 |;;; sine waves, with each wave's frequency indicating a particular row or column | 14 |;;; of the keypad respectively. The frequencies of the rows and columns are, in Hz: | 15 |;;; | 16 |;;; 1209 1336 1477 | 17 |;;; 697 [1] [2] [3] | 18 |;;; 770 [4] [5] [6] | 19 |;;; 852 [7] [8] [9] | 20 |;;; 941 [*] [0] [#] | 21 |;;; | 22 |;;; Theory of Operation: This DTMF detector correlates the input signal with seven | 23 |;;; locally-generated sine waves at the DTMF frequencies. If a valid pair of tones is | 24 |;;; detected, the appropriate message is output. The particular implemention below | 25 |;;; is not necessarily especially robust, which is why you found it in your assembler's | 26 |;;; reference manual and not in your answering machine. | 27 |;;; | 28 | | 29 | .option allow-destructive-if | 30 | .option list-macro-definitions=0 | 31 | .option list-included-files=0 | 32 | | 33 |;;; constants | 34 | | 35 |NUM_TONES .equ 7 | 36 |NUM_BUTTONS .equ 12 | 37 | | 38 |DTMF_FREQUENCIES .table 697, 770, 852, 941, 1209, 1336, 1477 ; Hz | 39 |FS .equ 48000 ; Hz | 40 | | 41 |OUTPUT_DEBOUNCE_TIME .equ 50 ; ms | 42 |TONE_DETECTION_THRESHOLD .equ 4 | 43 | | 44 |;;; make a table of bitmasks that correspond to the pair of tones for each button | 45 | | 46 |#define tonebit(Ptone) ( 0.5^(Ptone) ) | 47 |#define buttonmask(Pfirsttone,Psecondtone) ( tonebit(Pfirsttone) + tonebit(Psecondtone) ) | 48 | | 52 |BUTTON_MASKS .table buttonmask(0,4), buttonmask(0,5), buttonmask(0,6), buttonmask(1,4), buttonmask(1,5), buttonmask(1,6), buttonmask(2,4), buttonmask(2,5), buttonmask(2,6), buttonmask(3,4), buttonmask(3,5), buttonmask(3,6) | 53 | | 54 | | 55 |;;;----------------------------------------------------------------------------------- | 56 |;;; | 57 |;;; Macro: dtmf_detect_tone $tone | 58 |;;; Params: $tone is the tone number to listen to, from 0 to NUM_TONES - 1 | 59 |;;; Input: The audio signal is assumed to be in [input]. | 60 |;;; Output: Adds tonebit($tone) to [detected_tone_mask] if tone is heard. | 61 |;;; | 62 |;;; Description: Generates a sine wave at the tone's frequency and correlates it | 63 |;;; with the audio signal. Since we don't know the phase of the component we're | 64 |;;; listening for, we correlate with both a sine and cosine wave, and then take | 65 |;;; the magnitude of the resulting vector. If this magnitude is above a threshold, | 66 |;;; we claim to have heard the tone. The oscillator used is the modified coupled | 67 |;;; form or "magic circle" algorithm, which has a particularly convenient | 68 |;;; implementation (just four 1K instructions), long-term stability, and generates | 69 |;;; two outputs which are approximately in quadrature at low frequencies (close | 70 |;;; enough for our purposes.) In a real implementation, the oscillator would have | 71 |;;; to be initialized. | 72 |;;; | 73 | | 74 |dtmf_detect_tone .macro $tone | 120 | | 121 | | 122 |;;;----------------------------------------------------------------------------------- | 123 |;;; | 124 |;;; Macro: dtmf_detect $input, $output | 125 |;;; Params: The audio signal is assumed to be in [$input]. | 126 |;;; The output message is written to [$output]. | 127 |;;; | 128 |;;; Description: Listens for the seven DTMF tones, and outputs the appropriate message | 129 |;;; if they correspond to a valid DTMF pair, or zero if not. The detected tones (or | 130 |;;; lack thereof) must be stable for OUTPUT_DEBOUNCE_TIME ms before the output message | 131 |;;; is changed. | 132 |;;; | 133 | | 134 |dtmf_detect .macro $input, $output | 177 | | 178 | | 179 |;;;----------------------------------------------------------------------------------- | 180 |;;; | 181 |;;; Routine: main | 182 |;;; | 183 |;;; Description: This instantiates a DTMF detector and hooks it up to a couple of | 184 |;;; IO ports. | 185 |;;; | 186 | | 187 |main: | 188 |{ | 189 | dtmf_detect 0x400, 0x401 -------------------------macro: macro dtmf_detect | 1 |{ | 2 | `previous_tone_mask .ds ; detected_tone_mask from last sample | 3 | `output_debounce_counter .ds ; counter to debounce the output message | 4 | | 5 | detected_tone_mask .ls ; bitmask representing which tones were heard | 6 | input .lequ 0x400 ; memory location of the input signal | 7 | | 8 | ;;; listen for the seven DTMF tones | 9 | 000: sca 0.0000000 [$41b] | 10 | a = 0 ; clear all bits in the the detected_tone_mask 001: s1ac 0.0000000 [$000] | 11 | [detected_tone_mask] = a | 12 | | 13 | .for tone = 0 .. NUM_TONES - 1 ; go through all seven tones | 14 | dtmf_detect_tone tone ; and look for a signal at that frequency -------------------------macro: macro dtmf_detect_tone | 1 |{ | 2 | `sin_wave .ds ; reference sine wave for this tone | 3 | `sin_sum .ds ; correlation accumulator for the sine wave | 4 | `cos_wave .ds ; reference cosine wave for this tone | 5 | `cos_sum .ds ; correlation accumulator for the cosine wave | 6 | | 7 | epsilon .lequ 2 * sin(pi * DTMF_FREQUENCIES(tone) / FS) | 8 | | 9 | ;;; update oscillator | 10 | 002: lcm -0.0912056 [$003] | 11 | a = -epsilon * [`sin_wave] ; a = -ep * sin 003: cma 1.0000000 [$005] | 12 | a += [`cos_wave] ; a = cos - ep * sin | 13 | [`cos_wave] = a ; z*cos = cos - ep * sin | 14 | b = [`sin_wave] ; b = sin 004: scab 0.0912056 [$005] | 15 | a = epsilon * a + b ; a = sin + ep * (z*cos) 005: s1ac 0.0000000 [$003] | 16 | [`sin_wave] = a ; z*sin = sin + ep * (z*cos) | 17 | | 18 | ;;; correlate with sine wave | 19 | 006: lamc 0.0000000 [$400] | 20 | a *= [input] ; multiply sine wave by input signal 007: cam 0.0625000 [$004] | 21 | a = a/16 + [`sin_sum] ; scale down the result and add it to the sine accumulator 008: s1ac 0.0000000 [$004] | 22 | [`sin_sum] = a ; store it back | 23 | | 24 | ;;; correlate with cosine wave | 25 | | 26 | b = [input] ; b = input signal (piggybacks above) 009: bmc 0.0000000 [$005] | 27 | a = b * [`cos_wave] ; multiply cosine wave by input signal 00a: cam 0.0625000 [$006] | 28 | a = a/16 + [`cos_sum] ; scale down the result and add it to the cosine accumulator | 29 | [`cos_sum] = a ; store it back | 30 | | 31 | ;;; take vector magnitude (squared) | 32 | 00b: saac 0.0000000 [$006] | 33 | a = a^2 ; square the cosine accumulator value | 34 | b = a ; b = cos_sum^2 00c: xmmc 0.0000000 [$004] | 35 | a = [`sin_sum]^2 ; a = sin_sum^2 00d: cab 1.0000000 | 36 | a += b ; a = cos_sum^2 + sin_sum^2 | 37 | | 38 | ;;; set output bit if above threshold | 39 | 00e: 1ac -4.0000000 | 40 | if (a > TONE_DETECTION_THRESHOLD) { ; is the correlation magnitude above the threshold? 00f: skip N+Z $02 | 010: 1mc 1.0000000 [$000] | 41 | a = tonebit(tone) + [detected_tone_mask] ; if so, set a bit in the detected tone mask 011: s1ac 0.0000000 [$000] | 42 | [detected_tone_mask] = a ; and store it back | 43 | } | 44 |} -------------------------macro: macro dtmf_detect | 14 | dtmf_detect_tone tone ; and look for a signal at that frequency -------------------------macro: macro dtmf_detect_tone | 1 |{ | 2 | `sin_wave .ds ; reference sine wave for this tone | 3 | `sin_sum .ds ; correlation accumulator for the sine wave | 4 | `cos_wave .ds ; reference cosine wave for this tone | 5 | `cos_sum .ds ; correlation accumulator for the cosine wave | 6 | | 7 | epsilon .lequ 2 * sin(pi * DTMF_FREQUENCIES(tone) / FS) | 8 | | 9 | ;;; update oscillator | 10 | 012: lcm -0.1007500 [$007] | 11 | a = -epsilon * [`sin_wave] ; a = -ep * sin 013: cma 1.0000000 [$009] | 12 | a += [`cos_wave] ; a = cos - ep * sin | 13 | [`cos_wave] = a ; z*cos = cos - ep * sin | 14 | b = [`sin_wave] ; b = sin 014: scab 0.1007500 [$009] | 15 | a = epsilon * a + b ; a = sin + ep * (z*cos) 015: s1ac 0.0000000 [$007] | 16 | [`sin_wave] = a ; z*sin = sin + ep * (z*cos) | 17 | | 18 | ;;; correlate with sine wave | 19 | 016: lamc 0.0000000 [$400] | 20 | a *= [input] ; multiply sine wave by input signal 017: cam 0.0625000 [$008] | 21 | a = a/16 + [`sin_sum] ; scale down the result and add it to the sine accumulator 018: s1ac 0.0000000 [$008] | 22 | [`sin_sum] = a ; store it back | 23 | | 24 | ;;; correlate with cosine wave | 25 | | 26 | b = [input] ; b = input signal (piggybacks above) 019: bmc 0.0000000 [$009] | 27 | a = b * [`cos_wave] ; multiply cosine wave by input signal 01a: cam 0.0625000 [$00a] | 28 | a = a/16 + [`cos_sum] ; scale down the result and add it to the cosine accumulator | 29 | [`cos_sum] = a ; store it back | 30 | | 31 | ;;; take vector magnitude (squared) | 32 | 01b: saac 0.0000000 [$00a] | 33 | a = a^2 ; square the cosine accumulator value | 34 | b = a ; b = cos_sum^2 01c: xmmc 0.0000000 [$008] | 35 | a = [`sin_sum]^2 ; a = sin_sum^2 01d: cab 1.0000000 | 36 | a += b ; a = cos_sum^2 + sin_sum^2 | 37 | | 38 | ;;; set output bit if above threshold | 39 | 01e: 1ac -4.0000000 | 40 | if (a > TONE_DETECTION_THRESHOLD) { ; is the correlation magnitude above the threshold? 01f: skip N+Z $02 | 020: 1mc 0.5000000 [$000] | 41 | a = tonebit(tone) + [detected_tone_mask] ; if so, set a bit in the detected tone mask 021: s1ac 0.0000000 [$000] | 42 | [detected_tone_mask] = a ; and store it back | 43 | } | 44 |} -------------------------macro: macro dtmf_detect | 14 | dtmf_detect_tone tone ; and look for a signal at that frequency -------------------------macro: macro dtmf_detect_tone | 1 |{ | 2 | `sin_wave .ds ; reference sine wave for this tone | 3 | `sin_sum .ds ; correlation accumulator for the sine wave | 4 | `cos_wave .ds ; reference cosine wave for this tone | 5 | `cos_sum .ds ; correlation accumulator for the cosine wave | 6 | | 7 | epsilon .lequ 2 * sin(pi * DTMF_FREQUENCIES(tone) / FS) | 8 | | 9 | ;;; update oscillator | 10 | 022: lcm -0.1114693 [$00b] | 11 | a = -epsilon * [`sin_wave] ; a = -ep * sin 023: cma 1.0000000 [$00d] | 12 | a += [`cos_wave] ; a = cos - ep * sin | 13 | [`cos_wave] = a ; z*cos = cos - ep * sin | 14 | b = [`sin_wave] ; b = sin 024: scab 0.1114693 [$00d] | 15 | a = epsilon * a + b ; a = sin + ep * (z*cos) 025: s1ac 0.0000000 [$00b] | 16 | [`sin_wave] = a ; z*sin = sin + ep * (z*cos) | 17 | | 18 | ;;; correlate with sine wave | 19 | 026: lamc 0.0000000 [$400] | 20 | a *= [input] ; multiply sine wave by input signal 027: cam 0.0625000 [$00c] | 21 | a = a/16 + [`sin_sum] ; scale down the result and add it to the sine accumulator 028: s1ac 0.0000000 [$00c] | 22 | [`sin_sum] = a ; store it back | 23 | | 24 | ;;; correlate with cosine wave | 25 | | 26 | b = [input] ; b = input signal (piggybacks above) 029: bmc 0.0000000 [$00d] | 27 | a = b * [`cos_wave] ; multiply cosine wave by input signal 02a: cam 0.0625000 [$00e] | 28 | a = a/16 + [`cos_sum] ; scale down the result and add it to the cosine accumulator | 29 | [`cos_sum] = a ; store it back | 30 | | 31 | ;;; take vector magnitude (squared) | 32 | 02b: saac 0.0000000 [$00e] | 33 | a = a^2 ; square the cosine accumulator value | 34 | b = a ; b = cos_sum^2 02c: xmmc 0.0000000 [$00c] | 35 | a = [`sin_sum]^2 ; a = sin_sum^2 02d: cab 1.0000000 | 36 | a += b ; a = cos_sum^2 + sin_sum^2 | 37 | | 38 | ;;; set output bit if above threshold | 39 | 02e: 1ac -4.0000000 | 40 | if (a > TONE_DETECTION_THRESHOLD) { ; is the correlation magnitude above the threshold? 02f: skip N+Z $02 | 030: 1mc 0.2500000 [$000] | 41 | a = tonebit(tone) + [detected_tone_mask] ; if so, set a bit in the detected tone mask 031: s1ac 0.0000000 [$000] | 42 | [detected_tone_mask] = a ; and store it back | 43 | } | 44 |} -------------------------macro: macro dtmf_detect | 14 | dtmf_detect_tone tone ; and look for a signal at that frequency -------------------------macro: macro dtmf_detect_tone | 1 |{ | 2 | `sin_wave .ds ; reference sine wave for this tone | 3 | `sin_sum .ds ; correlation accumulator for the sine wave | 4 | `cos_wave .ds ; reference cosine wave for this tone | 5 | `cos_sum .ds ; correlation accumulator for the cosine wave | 6 | | 7 | epsilon .lequ 2 * sin(pi * DTMF_FREQUENCIES(tone) / FS) | 8 | | 9 | ;;; update oscillator | 10 | 032: lcm -0.1231003 [$00f] | 11 | a = -epsilon * [`sin_wave] ; a = -ep * sin 033: cma 1.0000000 [$011] | 12 | a += [`cos_wave] ; a = cos - ep * sin | 13 | [`cos_wave] = a ; z*cos = cos - ep * sin | 14 | b = [`sin_wave] ; b = sin 034: scab 0.1231003 [$011] | 15 | a = epsilon * a + b ; a = sin + ep * (z*cos) 035: s1ac 0.0000000 [$00f] | 16 | [`sin_wave] = a ; z*sin = sin + ep * (z*cos) | 17 | | 18 | ;;; correlate with sine wave | 19 | 036: lamc 0.0000000 [$400] | 20 | a *= [input] ; multiply sine wave by input signal 037: cam 0.0625000 [$010] | 21 | a = a/16 + [`sin_sum] ; scale down the result and add it to the sine accumulator 038: s1ac 0.0000000 [$010] | 22 | [`sin_sum] = a ; store it back | 23 | | 24 | ;;; correlate with cosine wave | 25 | | 26 | b = [input] ; b = input signal (piggybacks above) 039: bmc 0.0000000 [$011] | 27 | a = b * [`cos_wave] ; multiply cosine wave by input signal 03a: cam 0.0625000 [$012] | 28 | a = a/16 + [`cos_sum] ; scale down the result and add it to the cosine accumulator | 29 | [`cos_sum] = a ; store it back | 30 | | 31 | ;;; take vector magnitude (squared) | 32 | 03b: saac 0.0000000 [$012] | 33 | a = a^2 ; square the cosine accumulator value | 34 | b = a ; b = cos_sum^2 03c: xmmc 0.0000000 [$010] | 35 | a = [`sin_sum]^2 ; a = sin_sum^2 03d: cab 1.0000000 | 36 | a += b ; a = cos_sum^2 + sin_sum^2 | 37 | | 38 | ;;; set output bit if above threshold | 39 | 03e: 1ac -4.0000000 | 40 | if (a > TONE_DETECTION_THRESHOLD) { ; is the correlation magnitude above the threshold? 03f: skip N+Z $02 | 040: 1mc 0.1250000 [$000] | 41 | a = tonebit(tone) + [detected_tone_mask] ; if so, set a bit in the detected tone mask 041: s1ac 0.0000000 [$000] | 42 | [detected_tone_mask] = a ; and store it back | 43 | } | 44 |} -------------------------macro: macro dtmf_detect | 14 | dtmf_detect_tone tone ; and look for a signal at that frequency -------------------------macro: macro dtmf_detect_tone | 1 |{ | 2 | `sin_wave .ds ; reference sine wave for this tone | 3 | `sin_sum .ds ; correlation accumulator for the sine wave | 4 | `cos_wave .ds ; reference cosine wave for this tone | 5 | `cos_sum .ds ; correlation accumulator for the cosine wave | 6 | | 7 | epsilon .lequ 2 * sin(pi * DTMF_FREQUENCIES(tone) / FS) | 8 | | 9 | ;;; update oscillator | 10 | 042: lcm -0.1580925 [$013] | 11 | a = -epsilon * [`sin_wave] ; a = -ep * sin 043: cma 1.0000000 [$015] | 12 | a += [`cos_wave] ; a = cos - ep * sin | 13 | [`cos_wave] = a ; z*cos = cos - ep * sin | 14 | b = [`sin_wave] ; b = sin 044: scab 0.1580925 [$015] | 15 | a = epsilon * a + b ; a = sin + ep * (z*cos) 045: s1ac 0.0000000 [$013] | 16 | [`sin_wave] = a ; z*sin = sin + ep * (z*cos) | 17 | | 18 | ;;; correlate with sine wave | 19 | 046: lamc 0.0000000 [$400] | 20 | a *= [input] ; multiply sine wave by input signal 047: cam 0.0625000 [$014] | 21 | a = a/16 + [`sin_sum] ; scale down the result and add it to the sine accumulator 048: s1ac 0.0000000 [$014] | 22 | [`sin_sum] = a ; store it back | 23 | | 24 | ;;; correlate with cosine wave | 25 | | 26 | b = [input] ; b = input signal (piggybacks above) 049: bmc 0.0000000 [$015] | 27 | a = b * [`cos_wave] ; multiply cosine wave by input signal 04a: cam 0.0625000 [$016] | 28 | a = a/16 + [`cos_sum] ; scale down the result and add it to the cosine accumulator | 29 | [`cos_sum] = a ; store it back | 30 | | 31 | ;;; take vector magnitude (squared) | 32 | 04b: saac 0.0000000 [$016] | 33 | a = a^2 ; square the cosine accumulator value | 34 | b = a ; b = cos_sum^2 04c: xmmc 0.0000000 [$014] | 35 | a = [`sin_sum]^2 ; a = sin_sum^2 04d: cab 1.0000000 | 36 | a += b ; a = cos_sum^2 + sin_sum^2 | 37 | | 38 | ;;; set output bit if above threshold | 39 | 04e: 1ac -4.0000000 | 40 | if (a > TONE_DETECTION_THRESHOLD) { ; is the correlation magnitude above the threshold? 04f: skip N+Z $02 | 050: 1mc 0.0625000 [$000] | 41 | a = tonebit(tone) + [detected_tone_mask] ; if so, set a bit in the detected tone mask 051: s1ac 0.0000000 [$000] | 42 | [detected_tone_mask] = a ; and store it back | 43 | } | 44 |} -------------------------macro: macro dtmf_detect | 14 | dtmf_detect_tone tone ; and look for a signal at that frequency -------------------------macro: macro dtmf_detect_tone | 1 |{ | 2 | `sin_wave .ds ; reference sine wave for this tone | 3 | `sin_sum .ds ; correlation accumulator for the sine wave | 4 | `cos_wave .ds ; reference cosine wave for this tone | 5 | `cos_sum .ds ; correlation accumulator for the cosine wave | 6 | | 7 | epsilon .lequ 2 * sin(pi * DTMF_FREQUENCIES(tone) / FS) | 8 | | 9 | ;;; update oscillator | 10 | 052: lcm -0.1746597 [$017] | 11 | a = -epsilon * [`sin_wave] ; a = -ep * sin 053: cma 1.0000000 [$019] | 12 | a += [`cos_wave] ; a = cos - ep * sin | 13 | [`cos_wave] = a ; z*cos = cos - ep * sin | 14 | b = [`sin_wave] ; b = sin 054: scab 0.1746597 [$019] | 15 | a = epsilon * a + b ; a = sin + ep * (z*cos) 055: s1ac 0.0000000 [$017] | 16 | [`sin_wave] = a ; z*sin = sin + ep * (z*cos) | 17 | | 18 | ;;; correlate with sine wave | 19 | 056: lamc 0.0000000 [$400] | 20 | a *= [input] ; multiply sine wave by input signal 057: cam 0.0625000 [$018] | 21 | a = a/16 + [`sin_sum] ; scale down the result and add it to the sine accumulator 058: s1ac 0.0000000 [$018] | 22 | [`sin_sum] = a ; store it back | 23 | | 24 | ;;; correlate with cosine wave | 25 | | 26 | b = [input] ; b = input signal (piggybacks above) 059: bmc 0.0000000 [$019] | 27 | a = b * [`cos_wave] ; multiply cosine wave by input signal 05a: cam 0.0625000 [$01a] | 28 | a = a/16 + [`cos_sum] ; scale down the result and add it to the cosine accumulator | 29 | [`cos_sum] = a ; store it back | 30 | | 31 | ;;; take vector magnitude (squared) | 32 | 05b: saac 0.0000000 [$01a] | 33 | a = a^2 ; square the cosine accumulator value | 34 | b = a ; b = cos_sum^2 05c: xmmc 0.0000000 [$018] | 35 | a = [`sin_sum]^2 ; a = sin_sum^2 05d: cab 1.0000000 | 36 | a += b ; a = cos_sum^2 + sin_sum^2 | 37 | | 38 | ;;; set output bit if above threshold | 39 | 05e: 1ac -4.0000000 | 40 | if (a > TONE_DETECTION_THRESHOLD) { ; is the correlation magnitude above the threshold? 05f: skip N+Z $02 | 060: 1mc 0.0312500 [$000] | 41 | a = tonebit(tone) + [detected_tone_mask] ; if so, set a bit in the detected tone mask 061: s1ac 0.0000000 [$000] | 42 | [detected_tone_mask] = a ; and store it back | 43 | } | 44 |} -------------------------macro: macro dtmf_detect | 14 | dtmf_detect_tone tone ; and look for a signal at that frequency -------------------------macro: macro dtmf_detect_tone | 1 |{ | 2 | `sin_wave .ds ; reference sine wave for this tone | 3 | `sin_sum .ds ; correlation accumulator for the sine wave | 4 | `cos_wave .ds ; reference cosine wave for this tone | 5 | `cos_sum .ds ; correlation accumulator for the cosine wave | 6 | | 7 | epsilon .lequ 2 * sin(pi * DTMF_FREQUENCIES(tone) / FS) | 8 | | 9 | ;;; update oscillator | 10 | 062: lcm -0.1930389 [$01b] | 11 | a = -epsilon * [`sin_wave] ; a = -ep * sin 063: cma 1.0000000 [$01d] | 12 | a += [`cos_wave] ; a = cos - ep * sin | 13 | [`cos_wave] = a ; z*cos = cos - ep * sin | 14 | b = [`sin_wave] ; b = sin 064: scab 0.1930389 [$01d] | 15 | a = epsilon * a + b ; a = sin + ep * (z*cos) 065: s1ac 0.0000000 [$01b] | 16 | [`sin_wave] = a ; z*sin = sin + ep * (z*cos) | 17 | | 18 | ;;; correlate with sine wave | 19 | 066: lamc 0.0000000 [$400] | 20 | a *= [input] ; multiply sine wave by input signal 067: cam 0.0625000 [$01c] | 21 | a = a/16 + [`sin_sum] ; scale down the result and add it to the sine accumulator 068: s1ac 0.0000000 [$01c] | 22 | [`sin_sum] = a ; store it back | 23 | | 24 | ;;; correlate with cosine wave | 25 | | 26 | b = [input] ; b = input signal (piggybacks above) 069: bmc 0.0000000 [$01d] | 27 | a = b * [`cos_wave] ; multiply cosine wave by input signal 06a: cam 0.0625000 [$01e] | 28 | a = a/16 + [`cos_sum] ; scale down the result and add it to the cosine accumulator | 29 | [`cos_sum] = a ; store it back | 30 | | 31 | ;;; take vector magnitude (squared) | 32 | 06b: saac 0.0000000 [$01e] | 33 | a = a^2 ; square the cosine accumulator value | 34 | b = a ; b = cos_sum^2 06c: xmmc 0.0000000 [$01c] | 35 | a = [`sin_sum]^2 ; a = sin_sum^2 06d: cab 1.0000000 | 36 | a += b ; a = cos_sum^2 + sin_sum^2 | 37 | | 38 | ;;; set output bit if above threshold | 39 | 06e: 1ac -4.0000000 | 40 | if (a > TONE_DETECTION_THRESHOLD) { ; is the correlation magnitude above the threshold? 06f: skip N+Z $02 | 070: 1mc 0.0156250 [$000] | 41 | a = tonebit(tone) + [detected_tone_mask] ; if so, set a bit in the detected tone mask 071: s1ac 0.0000000 [$000] | 42 | [detected_tone_mask] = a ; and store it back | 43 | } | 44 |} -------------------------macro: macro dtmf_detect | 15 | .endloop | 16 | | 17 | ;;; if we've heard something consistent for the entire debounce time, | 18 | ;;; figure out which button we're hearing and set the output message. | 19 | 072: l1ac 0.0000000 [$000] | 20 | b = [detected_tone_mask] ; get the results of the search 073: cmb -1.0000000 [$001] | 21 | if (b != [`previous_tone_mask]) { ; are they different than last time? 074: skip Z $04 | 075: c T 0.5859375 | 22 | a = I(FS*OUTPUT_DEBOUNCE_TIME/1000) ; if so, get the max value for the debounce counter | 23 | [`output_debounce_counter] = a ; and reset output_debounce_counter 076: s1bc 0.0000000 [$002] | 24 | a = b ; set previous_tone_mask to detected_tone_mask, 077: s1ac 0.0000000 [$001] | 25 | [`previous_tone_mask] = a ; so we can start counting down next time | 26 | } 078: skip T $35 | 27 | else { ; if detected_tone_mask is the same as last time, 079: 1mc -0.0002441 [$002] | 28 | a = [`output_debounce_counter] - I(1) ; decrement the debounce counter 07a: s1ac 0.0000000 [$002] | 29 | [`output_debounce_counter] = a ; and store it back 07b: skip !N $32 | 30 | if (a < 0) { ; have we seen a consistent tone mask for OUTPUT_DEBOUNCE_TIME ms? 07c: sca 0.0000000 [$41b] | 31 | a = 0 ; if so, let's change the output message 07d: s1ac 0.0000000 [$401] | 32 | [0x401] = a ; initialize the output message to zero (meaning "no button") | 33 | .for button = 0 .. NUM_BUTTONS - 1 ; go through all twelve buttons 07e: 1mc -1.0625000 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 07f: skip !Z $02 | 080: c T 0.0002441 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 081: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } 082: 1mc -1.0312500 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 083: skip !Z $02 | 084: c T 0.0004883 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 085: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } 086: 1mc -1.0156250 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 087: skip !Z $02 | 088: c T 0.0007324 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 089: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } 08a: 1mc -0.5625000 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 08b: skip !Z $02 | 08c: c T 0.0009766 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 08d: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } 08e: 1mc -0.5312500 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 08f: skip !Z $02 | 090: c T 0.0012207 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 091: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } 092: 1mc -0.5156250 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 093: skip !Z $02 | 094: c T 0.0014648 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 095: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } 096: 1mc -0.3125000 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 097: skip !Z $02 | 098: c T 0.0017090 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 099: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } 09a: 1mc -0.2812500 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 09b: skip !Z $02 | 09c: c T 0.0019531 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 09d: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } 09e: 1mc -0.2656250 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 09f: skip !Z $02 | 0a0: c T 0.0021973 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 0a1: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } 0a2: 1mc -0.1875000 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 0a3: skip !Z $02 | 0a4: c T 0.0024414 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 0a5: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } 0a6: 1mc -0.1562500 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 0a7: skip !Z $02 | 0a8: c T 0.0026855 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 0a9: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } 0aa: 1mc -0.1406250 [$000] | 34 | if ([detected_tone_mask] == BUTTON_MASKS(button)) { ; did we hear just this button's two tones? 0ab: skip !Z $02 | 0ac: c T 0.0029297 | 35 | a = I(button + 1) ; if so, set the output message to indicate this button is down 0ad: s1ac 0.0000000 [$401] | 36 | [0x401] = a ; store it back | 37 | } | 38 | .endloop | 39 | } | 40 | } | 41 |} -------------------------file: dtmf.asm | 190 |} -------------------------file: linker | 1 |.autolink overlay | 2 |*