下面为DSP中生成DTMF的代码:
DTMF Generation
The tone generator is suitable for multi channel operation as defined by DTMF_CHAN_MAX. This allows us to use the same code to generate DTMF tones for a whole bunch of phone lines.
The actual tone is generated by a routine called dtmf_play(), which could be called at regular intervals from somewhere in your application to fill up a buffer with a block of DTMF samples. Every time this routine runs, it decrements the play timer, until it eventually becomes zero.
Typically, one will either send telephone voice, or DTMF, so you need to add a call to dtmf_play() somewhere in your voice processing handler to generate one or more DTMF samples. Bear in mind that you also need to zero the gaps between the tones, with a constant value (the last one generated by dtmf_play()), so there is some work for you to do to use this generator.
Sample Code
Example 2 is a multi channel DTMF modulator. For debugging, it is best to play a single tone, e.g. 1kHz continuously, then look at it on an oscilloscope. The tone should be clean, without discontinuities or sudden phase changes. As soon as one tries to play two tones simultaneously, the 'scope picture becomes muddled.
Example 2. Multi Channel DTMF Modulator
/*********************************************************************
* Filename: dtmf.dsp
* Description: Multi channel DTMF modulator
*
* Copyright Aerospace Software Ltd., 1996
*
* Version Author
* -------------------------------------------------------------------
* Feb 96 Herman Oosthuysen
*********************************************************************/
.MODULE DTMF;
#include "macro.h"
#define DTMF_CHAN_MAX 4
#define DTMF_CHAN_MASK (DTMF_CHAN_MAX - 1)
#define DTMF_TONE_SIZE 20
#if 1
#define DTMF_PLAY_TIME 400 /* 400 = 50ms, 600 = 75ms */
#else
#define DTMF_PLAY_TIME 4000 /* 4000 = 500ms for test and measuring */
#endif
#define DTMF_SYMBOL_TIME 800 /* 100ms symbol time */
#define DTMF_SIN_MASK 0x0FFF
#define DTMF_NUMSTR_LENGTH 32
#define DTMF_NUMSTR_MAX (DTMF_NUMSTR_LENGTH * DTMF_CHAN_MAX)
/*
* DTMF global variables
*/
.VAR/DM
dtmf_playtmr[DTMF_CHAN_MAX],
dtmf_step1[DTMF_CHAN_MAX],
dtmf_step2[DTMF_CHAN_MAX],
dtmf_phase1[DTMF_CHAN_MAX],
dtmf_phase2[DTMF_CHAN_MAX],
dtmf_strtmr[DTMF_CHAN_MAX],
dtmf_numstr[DTMF_NUMSTR_MAX],
dtmf_numptr[DTMF_CHAN_MAX];
/*
* DTMF lookup tables in PM
*/
.VAR/PM DTMF_STEP1_TBL[DTMF_TONE_SIZE];
.INIT DTMF_STEP1_TBL:
0x01e100, /* 0 = 941Hz */
0x016500, /* 1 = 697Hz */
0x016500, /* 2 = 697Hz */
0x016500, /* 3 = 697Hz */
0x018a00, /* 4 = 770Hz */
0x018a00, /* 5 = 770Hz */
0x018a00, /* 6 = 770Hz */
0x01b400, /* 7 = 852Hz */
0x01b400, /* 8 = 852Hz */
0x01b400, /* 9 = 852Hz */
0x01e100, /* * = 941Hz */
0x01e100, /* # = 941Hz */
0x016500, /* A = 697Hz */
0x018a00, /* B = 770Hz */
0x01b400, /* C = 852Hz */
0x01e100, /* D = 941Hz */
0x00b300, /* dial tone = 350Hz */
0x000000, /* toot = 0Hz */
0x000000, /* beep = 0Hz */
0x000000; /* tweet = 0Hz */
.VAR/PM DTMF_STEP2_TBL[DTMF_TONE_SIZE];
.INIT DTMF_STEP2_TBL:
0x02ac00, /* 0 = 1336Hz */
0x026b00, /* 1 = 1209Hz */
0x02ac00, /* 2 = 1336Hz */
0x02f400, /* 3 = 1477Hz */
0x026b00, /* 4 = 1209Hz */
0x02ac00, /* 5 = 1336Hz */
0x02f400, /* 6 = 1447Hz */
0x026b00, /* 7 = 1209Hz */
0x02ac00, /* 8 = 1336Hz */
0x02f400, /* 9 = 1447Hz */
0x026b00, /* * = 1209Hz */
0x02f400, /* # = 1477Hz */
0x034400, /* A = 1633Hz */
0x034400, /* B = 1633Hz */
0x034400, /* C = 1633Hz */
0x034400, /* D = 1633Hz */
0x00e100, /* dial tone = 440Hz */
0x010000, /* toot = 500Hz */
0x020000, /* beep = 1000Hz */
0x040000; /* tweet = 2000Hz */
.VAR/PM DTMF_SIN_TBL[256];
.INIT DTMF_SIN_TBL:
0x000000, 0x032300, 0x064700, 0x096900, 0x0C8A00, 0x0FA900, 0x12C500, 0x15DF00,
0x18F500, 0x1C0800, 0x1F1600, 0x221F00, 0x252300, 0x282100, 0x2B1900, 0x2E0B00,
0x30F500, 0x33D800, 0x36B300, 0x398500, 0x3C4F00, 0x3F0F00, 0x41C500, 0x447200,
0x471400, 0x49AA00, 0x4C3600, 0x4EB600, 0x512900, 0x539000, 0x55EA00, 0x583700,
0x5A7700, 0x5CA800, 0x5ECB00, 0x60E000, 0x62E500, 0x64DB00, 0x66C200, 0x689900,
0x6A6000, 0x6C1600, 0x6DBC00, 0x6F5100, 0x70D400, 0x724600, 0x73A700, 0x74F600,
0x763200, 0x775D00, 0x787500, 0x797A00, 0x7A6D00, 0x7B4D00, 0x7C1A00, 0x7CD400,
0x7D7A00, 0x7E0D00, 0x7E8D00, 0x7EF900, 0x7F5200, 0x7F9700, 0x7FC800, 0x7FE600,
0x7FF000, 0x7FE600, 0x7FC800, 0x7F9700, 0x7F5200, 0x7EF900, 0x7E8D00, 0x7E0D00,
0x7D7A00, 0x7CD400, 0x7C1A00, 0x7B4D00, 0x7A6D00, 0x797A00, 0x787500, 0x775D00,
0x763200, 0x74F600, 0x73A700, 0x724600, 0x70D400, 0x6F5100, 0x6DBC00, 0x6C1600,
0x6A6000, 0x689900, 0x66C200, 0x64DB00, 0x62E500, 0x60E000, 0x5ECB00, 0x5CA800,
0x5A7700, 0x583700, 0x55EA00, 0x539000, 0x512900, 0x4EB600, 0x4C3600, 0x49AA00,
0x471400, 0x447200, 0x41C500, 0x3F0F00, 0x3C4F00, 0x398500, 0x36B300, 0x33D800,
0x30F500, 0x2E0B00, 0x2B1900, 0x282100, 0x252300, 0x221F00, 0x1F1600, 0x1C0800,
0x18F500, 0x15DF00, 0x12C500, 0x0FA900, 0x0C8A00, 0x096900, 0x064700, 0x032300,
0x000000, 0xFCDD00, 0xF9B900, 0xF69700, 0xF37600, 0xF05700, 0xED3B00, 0xEA2100,
0xE70B00, 0xE3F800, 0xE0EA00, 0xDDE100, 0xDADD00, 0xD7DF00, 0xD4E700, 0xD1F500,
0xCF0B00, 0xCC2800, 0xC94D00, 0xC67B00, 0xC3B100, 0xC0F100, 0xBE3B00, 0xBB8E00,
0xB8EC00, 0xB65600, 0xB3CA00, 0xB14A00, 0xAED700, 0xAC7000, 0xAA1600, 0xA7C900,
0xA58900, 0xA35800, 0xA13500, 0x9F2000, 0x9D1B00, 0x9B2500, 0x993E00, 0x976700,
0x95A000, 0x93EA00, 0x924400, 0x90AF00, 0x8F2C00, 0x8DBA00, 0x8C5900, 0x8B0A00,
0x89CE00, 0x88A300, 0x878B00, 0x868600, 0x859300, 0x84B300, 0x83E600, 0x832C00,
0x828600, 0x81F300, 0x817300, 0x810700, 0x80AE00, 0x806900, 0x803800, 0x801A00,
0x801000, 0x801A00, 0x803800, 0x806900, 0x80AE00, 0x810700, 0x817300, 0x81F300,
0x828600, 0x832C00, 0x83E600, 0x84B300, 0x859300, 0x868600, 0x878B00, 0x88A300,
0x89CE00, 0x8B0A00, 0x8C5900, 0x8DBA00, 0x8F2C00, 0x90AF00, 0x924400, 0x93EA00,
0x95A000, 0x976700, 0x993E00, 0x9B2500, 0x9D1B00, 0x9F2000, 0xA13500, 0xA35800,
0xA58900, 0xA7C900, 0xAA1600, 0xAC7000, 0xAED700, 0xB14A00, 0xB3CA00, 0xB65600,
0xB8EC00, 0xBB8E00, 0xBE3B00, 0xC0F100, 0xC3B100, 0xC67B00, 0xC94D00, 0xCC2800,
0xCF0B00, 0xD1F500, 0xD4E700, 0xD7DF00, 0xDADD00, 0xDDE100, 0xE0EA00, 0xE3F800,
0xE70B00, 0xEA2100, 0xED3B00, 0xF05700, 0xF37600, 0xF69700, 0xF9B900, 0xFCDD00;
/*********************************************************************
* Name: dtmf_init
* Description: initialize the DTMF modulator
* Constraints: none
*********************************************************************/
dtmf_init:
MAC_ENTER
ar = 0;
cntr = DTMF_CHAN_MAX;
do dtmf_init_loop until ce;
MAC_WR_DM(0, ^dtmf_step1, ar)
MAC_WR_DM(0, ^dtmf_step2, ar)
MAC_WR_DM(0, ^dtmf_phase1, ar)
MAC_WR_DM(0, ^dtmf_phase2, ar)
MAC_WR_DM(0, ^dtmf_playtmr, ar)
ar = ar + 1;
dtmf_init_loop: nop;
MAC_EXIT
rts;
/*********************************************************************
* Name: dtmf_dial
* Description: lookup the step sizes for the two DTMF tones
* Constraints: ar = channel, ay1 = digit
*********************************************************************/
dtmf_dial:
MAC_ENTER
dis ints;
ay0 = DTMF_CHAN_MAX;
af = ar - ay0;
if gt jump dtmf_dial_exit;
ax0 = ar;
ax1 = DTMF_TONE_SIZE;
af = ax1 - ay1;
if lt jump dtmf_dial_exit;
/*
* if a tone is already playing then
* it will be disrupted
*/
MAC_RD_PM(ar, ^DTMF_STEP1_TBL, ay1)
MAC_WR_DM(ar, ^dtmf_step1, ax0)
MAC_RD_PM(ar, ^DTMF_STEP2_TBL, ay1)
MAC_WR_DM(ar, ^dtmf_step2, ax0)
MAC_WR_DM(0, ^dtmf_phase1, ax0)
MAC_WR_DM(0, ^dtmf_phase2, ax0)
ar = DTMF_PLAY_TIME;
MAC_WR_DM(ar, ^dtmf_playtmr, ax0)
dtmf_dial_exit:
ena ints;
MAC_EXIT
rts;
/*********************************************************************
* Name: dtmf_play
* Description: play the tones until the timer expires
* Constraints: ar = channel
*********************************************************************/
dtmf_play:
MAC_ENTER
ay0 = ar;
/*
* if playtimer > 0 then
* decrement playtimer
*/
MAC_RD_DM(ar, ^dtmf_playtmr, ay0)
ar = pass ar;
if eq jump dtmf_play_exit;
ar = ar - 1;
MAC_WR_DM(ar, ^dtmf_playtmr, ay0)
/*
* lookup two tone samples
* mix tones
* convert to 12 bit value
*/
MAC_RD_DM(ay1, ^dtmf_step1, ay0)
MAC_RD_DM(ar, ^dtmf_phase1, ay0)
call dtmf_tone;
ax1 = ar;
ar = pass af;
MAC_WR_DM(ar, ^dtmf_phase1, ay0)
MAC_RD_DM(ay1, ^dtmf_step2, ay0)
MAC_RD_DM(ar, ^dtmf_phase2, ay0)
call dtmf_tone;
ay1 = ar;
ar = pass af;
MAC_WR_DM(ar, ^dtmf_phase2, ay0)
ar = ax1 + ay1;
sr = ashift ar by -4 (lo);
ar = sr0;
/*
* Compress Output
* convert signed value to log value
*/
tx1 = ar;
nop; /* one clock cycle needed for conversion */
ar = tx1;
dtmf_play_exit:
MAC_EXIT
rts;
/*********************************************************************
* Name: dtmf_tone
* Description: DTMF tone generator
* Constraints: ar = step, ay1 = phase
* returns ar = tone, af = phase
*********************************************************************/
dtmf_tone:
MAC_QUICK_ENTER
/*
* calc new phase
* lookup tone sample in sine table
* drop level by 3dB
* (Alternatively one can change the table for a lower level)
*/
ar = ar + ay1;
ay1 = DTMF_SIN_MASK;
ar = ar AND ay1;
af = pass ar;
sr = lshift ar by -4 (lo);
MAC_RD_PM(ar, ^DTMF_SIN_TBL, sr0)
sr = ashift ar by -1 (lo);
ar = sr0;
MAC_QUICK_EXIT
rts;
.ENDMOD;
/********************************************************************/