-
DSP
课程设计
实
验
报
告
DTMF
的产生与检测
1
目
录
一、设计任务书……………………………………………………
…
3
二、设计内容…………………
………………………………………
3
三、设计方案、算法原理说明………………………………………
3
四、程序设计、调试与结果分析…………………………………
…
7
五、设计(安装)与
调试的体会……………………………………
24
p>
六、
参考文献……………………………………………………………<
/p>
25
2
一、
设计任务书
要求完成的任务:
(
1
)编写
C
语言程序,并在<
/p>
CCS
集成开发环境下调试通过。
p>
(
2
)实现设计所要求的各项功能。
(
3
)按要求撰写
设计报告。
要求达到的目的:
(1)
熟悉
CCS
的编程环境和基本试验调试流程和方法;
(2)
熟悉并掌握使用
C
语言编写
较为复杂的程序并用
CCS
实现;
(3)
了解并掌握
DTMF
p>
信号的产生和检测远离核试验流程;
(4)
熟练使用软件
CCS5000
对程序的完整调试过程。
二、
设计内容
基本部分:
(
1
)使用
C
语言编写
DTMF
信号的发生程序,要求循环产生
0~9
、
*
、
#
、
A
、
B
、
C
、
D
对应
的
DTMF
信号,并且符合<
/p>
CCITT
对
DTMF
< br>信号规定的指标。
(
2
)使用
C
语言编写
D
TMF
信号的检测程序,检测到的
DTMF
编码在屏幕上显示。
发挥部分:
利用
DTMF
信号完成数据通讯的功能
,并试改进
DTMF
信号的规定指标,使每秒内传送的
DTMF
编码越多越好。
三、
设计方案、算法原理说明
表一
双音多频
DTMF
(
Dual Tone
Multi Frequency
),双音多频,由高频群和低频群组成,高低
频群各包含
4
个频率。
一个高频信号和一个低频信号叠加组成一个组合信号,
代表一个数字。
< br>DTMF
信令有
16
个编码。利
用
DTMF
信令可选择呼叫相应的对讲机
双
音多频信号(
DTMF
),电话系统中电话机与交换机之间的一
种用户信令,通常用于
发送被叫号码。
在使用
双音多频信号之前,
电话系统中使用一连串的断续脉冲来传送被叫号码,
称为脉
冲拨号。脉冲拨号需要电信局中的操作员手工完成长途接续
(
早期方法,很老很古董
)
。
<
/p>
双音多频信号是贝尔实验室发明的,其目的是为了自动完成长途呼叫。
双音多频的拨号键盘是
4×
4
的矩阵,
每一行代表一个低频,每一列代表一个高频。每
按一个键就发送一个高频和低频的正弦信
号组合,比如
'1'
相当于
697
p>
和
1209
赫兹
(
Hz)
。交
换机可以解码这些频率组合并确定所对应的按键。<
/p>
在编码时将击键或数字信息转换成双音信号并发送,解码时在收
到的
DTMF
信号中检
测击键或数字信
息的存在性。一个
DTMF
信号由两个频率的音频信号叠加构成
。这两个音
频信号的频率来自两组预分配的频率组:
行频组或列
频组。
每一对这样的音频信号唯一表示
一个数字或符号。
电话机中通常有
16
个按键,
其中有
10
个数字键
0
~
9
和
6
个功能键
*
、
#
、
A
、
B
、
C
、
D
< br>。由于按照组合原理,一般应有
8
种不同的单音频信号。
因此可采用的频率也
3
有
8
种,<
/p>
故称之为多频,
又因它采用分别从高低频中任意抽出
1
种进行组合来进行编码,
所
以又称之为
“8
中取
2”
的编码技术。根据
CCITT
的建议,国际上采用
的多种频率为
697Hz
、
770Hz
、
852Hz
、
941Hz
、
1209Hz
、
1336Hz
、
1477Hz
和
1633Hz
等
8
种。用这
8
种频率可形
成
16
种不同的组合,从而代表
16<
/p>
种不同的数字或功能键,具体组合见表
1
。
双音多频
DTMF
信令,逐渐在全世界范围内使用在按键式电话机上,因其提供更高的拨
号速率,
迅速取代了传统转盘式电话机使用的拨号脉冲信令。近年来
DTMF
也应用在交互式
控制中,诸如语言菜单、语言邮件、电话银行和
ATM
终端等。通过软件产生与检测
DTMF
信
令,是一项较有价值的工程应用。
DTMF
编解码器在编码时将击键或数字信息转换成双音信号并发送,解码时
在收到的
DTMF
信号中检测击键或数字信息的存在性。
DTMF
的编解码方案无需过多的计算量,以目前
计算机的运算速度,可以很轻松地实现。
一个
DTMF
信号由两个频率的音频信号叠加构成。这两个音频信号的频率来自
两组预分
配的频率组:
行频组或列频组。
每一对这样的音频信号唯一表示一个数字或符号。
为了产生
D
TMF
信号,可以通过软件产生两个正弦波叠加在一起后发送,解码时软件可以采用改进
的
Goertzel
算法,从频域搜索两个正弦波的存在,从而
解调出
DTMF
信号。
的产生
DT
MF
发生器基于两个二阶数字正弦振荡器,一个用于产生行频,一个用于产生列频。
p>
DSP
只要装载相应的系数和初始条件,就可以只用两个振荡器产生
所需的八种音频信号。典
型的
DTMF
信号频率范围是
700
~
1700Hz
,选取
8000Hz
作为采样频率,即
可满足奈奎斯特
定理。
我们用的是<
/p>
sin
函数产生离散的正弦值,
因为这种
方法我们比较熟悉而且通过查阅资料
这种方法的也能达到误差要求,
生成
DTMF
的公式为:
buff
er[t]=sin(t*2*pi*f1/fs
)+
sin(
t
*2*pi*f2/fs
)
,
其中
t
为采样序数,由
0
开始递增;
f1
,
f2
为生成
DTMF
信号的两
个正弦波
的频率;
fs
为采样频率;<
/p>
buffer[t]
为序数
t
时的得出的采样值。将这些数据转换为
Q15
格<
/p>
式然后通过
codec
发送出去。
CCITT<
/p>
对
DTMF
信号规定的指标是,
传送
/
接收率为每秒
1
0
个数字,
即每个数字
100ms
p>
。
代表数字的音频信号必须持续至少
45m
s
,但不超过
55ms
。
100ms
内其他时间为静音,以便
区别连续的两个
按键信号。我们使用
8000Hz
的采样频率(电话信号的典型
抽样频率为
f
sam
=8kHZ
)
,即
1
秒采样
p>
8000
个点,则
100ms
采样
800
个点,我们设置
800
个点的缓存,其中
用
400
p>
个存产生的
DTMF
信号值,即音频信号必
须持续
50ms
,另外
400
个存
0
值,即静音信
号
。
的检测
DTMF
信号包含两组音频信号,
解码器的任务是通过数学变换把它从时域转换到频域,
然后得出对应的数字信息。由于
DSP
芯片处理
的是数字信号,所以必须把输入信号数字化,
再用
DSP
芯片进行处理。频率检测时,整个检测过程分两步:首先采用
Goer
tzel
算法在输
入信号中提取频谱信息;接着作检测结果的有
效性检查。
DTMF
解码时在输入信
号中搜索出有效的行频和列频。
计算数字信号的频谱可以采用
D
FT
及其快速算法
FFT
,而在实现<
/p>
DTMF
解码时,采用
Goertzel
算法要比
FFT
更快。通过
FFT
可
以计算得到信号所有谱线,
了解信号整个频域信息,
而对于
DTMF
信号只需关心其
8
个行频
/
列频及其二次谐波信息即可,二次谐波的信息用于将
DTMF
信号与声音信号区别开。此时
4
Goertzel
算法能更加快速的在输入信号中提取频谱信息。
Goertzel
算法实质是一个两极点
的
IIR
滤波器。
Goertzel
算法简介
块大小的设置
Goertzel
算法中的块大小
N
与相应的
FFT
中的点数类似,它控制了频率分辨率的大小。
例
如,若采样率为
8kHz
,而
N
为
100
个采样,
那么频率分辨率就是
80Hz
。
这就可能使我们为了获取最大的频率分辨率而尽量将
N
取高。然而
N
越大,
检测到每个音
调所需的时间就越多,因为我们必须等所有这
N<
/p>
个采样都完成后才能开始处理。例如,采
样率为
< br>8kHz
时,累积
800
个采样
需要
100ms
。若想缩短检测音调的时间,就必须适当调整<
/p>
N
的值。
<
/p>
影响
N
的选择的另一个因素是采样率和目
标频率之间的关系。比较理想情况是目标频率在
相应的频率分辨率的中点范围内,也就是
说,我们希望目标频率是
sample_rate/N
比值的整
数倍。值得庆幸的是,
Goertzel
算法中的
N
与
FFT
中不同,不必是
2
的整数次幂。
预计算常数
在采样率和块大小确定之后,只须通过下面
< br>5
个简单的计算来得出处理时所需要的常数
:
k =
(N*target_freq)/sample_tate
w =
(2*π/N)*k
cosine = cos w
sine = sin w
coeff = 2 *
cosine
每一次采样处理中都需要
3
个变量,我们称其为
Q0'
、
Q1
'
和
Q2
。
Q
1
是前一次采样处理的
Q0
值,
Q2
是在两次采样前的
Q0
< br>值
(
或
Q1
在本次采样前的值
)
。
在每个采样块的开始时,都必须将
Q
1
和
Q2
初始化为
0
。每个采样都需要按照下面三个等
式进行计算:
Q0 = coeff * Q1 - Q2 + sample
Q2 = Q1
Q1 = Q0
在
进行
N
次预采样计算之后,可以检测到音调是否存在。
real = (Q1 - Q2 * cosine)
imag = (Q2 * sine)
magnitude2 = real2 + imag2
这时只需进行一次简单的幅度门限测试就可以判断出是否有音调存在。之后,将
Q2
和
Q1
复位到
p>
0
,开始下一个块的处理。
Goertzel
优化算法
Goertzel
优化算法比
Goertzel
基本算法所需的计算量小,但这是以损失相位信息为
代价。
在
Goertzel
优化算法中每个采样处理完全一样,
但处理的
结果与
Goertzel
基本算法不同。
在
Goertzel
基本算法中,通常需要计算信号的实部和
虚部,然后将实部和虚部的计算结果转换
为相应的幅度平方。而在优化
< br>Goertzel
算法中则不需计算实部和虚部,直接计算下式:
magnitude2 = Q12 +
Q22-Q1*Q2*coeff
Goertzel
算法实质
是一个两极点的
IIR
滤波器,离散傅立叶变换值,快速有效的
提取
输入信号的频谱信息
,
如图:
p>
5
图
(6)
Goertzel
算法原理框图
如图
知,
DFT
计算可以等价为:
V
k
(n)=x(n)+2cos(2k
p>
π
/N)V
k
(n
-1)-V
k
(n-
2),
0≤n≤N
①
发生
X(k)=y
< br>k
(N)=V
k
(N)-e
p>
②
检验
由于在
DTMF
检测中,输入的信号是实数序
列,并不需要检测出
8
个行频/列频的相位,
< br>只需要计算出其幅度平方即可。对于实序列
x(n)
,<
/p>
Goertzel
算法所需的内部变量
V
k(n)
也为
2
实数。因此计算
/X(k)/
如下:
/X(k)/
=/y
k
(N)/
p>
= V
k
(n)+ V
k
(n-1)-2cos(2k
π
/N)V
k
(N)V
k
(N-1)
2
2
2
2
-j2k
π
p>
/N
V
k
(N-1
)
1st
Harmonics
(
N
=
205)
fs
= 8 ksps
2nd Harmonics
(
N
=205)
fs
= 8 ksps
k
18
20
22
24
31
34
38
42
信号频率
(Hz)
697
770
852
frequency
(
k/N
)
fs
/Hz
697
770
852
941
1209
1336
1477
1633
计算值
k
10.890625
12.03125
13.3125
coefficient
cos(2pi
k/N
)
0.85162
0.81793
0.78115
0.74142
0.58157
0.50442
0.39505
0.27972
取整值
k
11
12
13
k
35
39
43
47
61
67
74
82
frequency
(
k/N
)
fs
/Hz
1393
1552
1711
1871
2428
2667
2945
3264
coefficient
cos(2pi
k/N
)
0.45886
0.34445
0.22470
0.10141
-0.32974
-0.50000
-0.67606
-0.83740
相对误差
1.00430%
0.25974%
2.34741%
绝对误差
0.109375
0.03125
0.3125
6
941
1209
1336
1477
1633
14.703125
18.890625
20.875
23.078125
25.515625
15
19
21
23
25
0.296875
0.109375
0.125
0.078125
0.515625
2.01912%
0.57899%
0.59880%
0.33852%
2.02082%
(
N=125
p>
)
③
DTMF<
/p>
信号的有效性检测
得到了
DTMF
信号的基波及二次谐波的频谱平方幅度信息后,
需要通过一系列检测才能确
定信号的有效性:
(1)DTMF
信号的强度是否足够大,行列频率分量平方幅度和是否高于
规定的门限值.
(2)
如果
DTMF
信号存在,
比较行列频率的最大频率分
量差值,
因为电话线具有低通特性,
列频比行频衰减得要厉害,
因此要设置一门限值.
(3)
分别在
行列频率组比较频谱分量,
最强的谱线至少要比其他音频信号高一个门限值.
四、程序设计与调试结果分析
我们的设计由两个
DSP
板构成进行通信,一个负责产
生信号,一个接收信号。
(一)
基本要求
(
1
)
DTMF
的产生
#include
#include
#include
#include
#include
#include
void delay(int period);
void generate(int num);
HANDLE hHandset;
float
buffer[800];
s16 num=0;
int
count=0;
float freq[16][2]={ 941,1336,
//0
697,1209, //1
697,1336, //2
697,1477, //3
770,1209, //4
770,1336, //5
770,1477, //6
852,1209, //7
852,1336, //8
7
852,1477, //9
697,1633, //A
770,1633, //B
852,1633, //C
941,1633, //D
941,1209, //*
941,1477 //#
};
float pi=3.1415926;
void
main()
{
int
cnt=2;
if(brd_init(100))
{
return;
}
/*
blink the leds a couple times
通过观察
LED
指示灯的闪动频率来验证程序
运行
*/
while ( cnt-- )
{
brd_led_toggle(BRD_LED0);
//brd_delay_msec(1000);
delay(1000);
brd_led_toggle(BRD_LED1);
//brd_delay_msec(1000);
delay(1000);
brd_led_toggle(BRD_LED2);
//brd_delay_msec(1000);
delay(1000);
}
/* Open Handset Codec */
hHandset =
codec_open(HANDSET_CODEC); /* Acquire handle to
codec */
/* Set codec
parameters */
codec_dac_mode(hHandset, CODEC_DAC_15BIT); /*
DAC in 15-bit mode */
codec_adc_mode(hHandset, CODEC_ADC_15BIT); /*
ADC in 15-bit mode */
codec_ain_gain(hHandset,
CODEC_AIN_6dB);/*
6dB
gain
on
analog
input
to
ADC
*/
codec_aout_gain(hHandset,
CODEC_AOUT_MINUS_12dB); /* -12dB gain on analog
*/
/* output from DAC */
codec_sample_rate(hHandset,SR_8000); /* 8KHz
sampling rate */
generate(num);
8
}
void generate(int num)
{
f32 x,y;
int k=0;
int i;
i=0;
while(1)
{
// Wait for sample from
handset
while
(!MCBSP_XRDY(HANDSET_CODEC)) {};
*(volatile
u16*)DXR1_ADDR(HANDSET_CODEC)=buffer[i];
i++;
if(i==800)
{
i=0;
num++;
if(num==16)
num=0;
x=freq[num][0]/8000;
y=freq[num][1]/8000;
for(k=0;k<400;k++)
{
buffer[k]=
(0.65*sin(2*pi*y*k)+0.8*sin(2*pi*x*k))*16384;
buffer[k+400]=0;
}
}
}
}
void delay(int period)
{
int i, j;
<
br>2
for(i=0; i
{
for(j=0; j
}
}
(
)
DTMF
的检测
#include
#include
#include
9
#include
#include
#include
HANDLE
hHandset;
float buffer[125];
float pi=3.1415926;
s16 receive[125];
s16 dacdata;
int k=0;
int
detect_result[100]={0};
int l=0;
int flag=0;
void delay(int period);
void
detect();
void main()
{
int cnt=2;
/********************************************
Description:
* vk(n) =
2*coef*vk(n-1) - vk(n-2) + x(n)
*
* Coefficients are in w[8]
*
x(n) is in buffer[256]
* vk(n-2) is
a[i][0]
* vk(n-1) is a[i][1]
....
* vk(n) is a[i][2]
***************************************
******/
if(brd_init(100))
{return;}
// blink the leds
a couple times
while (
cnt-- )
{
brd_led_toggle(BRD_LED0);
//brd_delay_msec(1000);
delay(1000);
brd_led_toggle(BRD_LED1);
//brd_delay_msec(1000);
delay(1000);
brd_led_toggle(BRD_LED2);
//brd_delay_msec(1000);
delay(1000);
}
10
/*
Open Handset Codec */
hHandset =
codec_open(HANDSET_CODEC); /* Acquire handle to
codec */
/* Set codec
parameters */
codec_dac_mode(hHandset, CODEC_DAC_15BIT); /*
DAC in 15-bit mode */
codec_adc_mode(hHandset, CODEC_ADC_15BIT); /* ADC
in 15-bit mode */
codec_ain_gain(hHandset,
CODEC_AIN_6dB);
/*
6dB
gain
on
analog
input
to
ADC
*/
codec_aout_gain(hHandset, CODEC_AOUT_MINUS_6dB);
/* -6dB gain on analog output from DAC
*/
codec_sample_rate(hHandset,SR_8000); /* 8KHz
sampling rate */
while(1)
{
while
(!MCBSP_RRDY(HANDSET_CODEC)) {};
dacdata = *(volatile
u16*)DRR1_ADDR(HANDSET_CODEC);
receive[k]=dacdata;
buffer[k]=dacdata/16384.0;
k++;
if(k==125)
{
k=0;
detect();
}
}
}
void detect()
{ int i,j,x,y;
float
w[8],a[8][3],amp[8];
w[0]=2*cos(2*pi*11/125);
w[1]=2*cos(2*pi*12/125);
w[2]=2*cos(2*pi*13/125);
w[3]=2*cos(2*pi*15/125);
w[4]=2*cos(2*pi*19/125);
w[5]=2*cos(2*pi*21/125);
w[6]=2*cos(2*pi*23/125);
w[7]=2*cos(2*pi*26/125);
for(i=0;i<8;i++)
{
a[i][0]=0;
a[i][1]=0;
for(j=1;j<=125;j++)
{
a[i][2]=w[i]*a[i][1]-a[i][0
]+buffer[j-1];
a[i][0]=a[i][1];
a[i][1]=a[i][2];
11
}
//
计算频谱的幅度平方值
amp[i]=a[i][1]*
a[i][1]+a[i][0]*a[i][0]-w[i]*a[i][1]*a[i][0];}
j=0;
for(i=0;i<8;i++)
{
if(amp[i]>500)
//
门限设为
500
{ //printf(
j++;
if(j==1)
{
x=i;
}
else if(j==2)
{
y=i;
}
}
}
i=-1;
if(flag==0)
{
if(j==2)
{
if(x==0 && y==4)
{ i='1'; }
else if(x==0 && y==5)
{ i='2'; }
else if(x==0 &&
y==6)
{ i='3';
}
else if(x==1
&& y==4)
{
i='4'; }
else
if(x==1 && y==5)
{ i='5'; }
else if(x==1 && y==6)
{ i='6'; }
else if(x==2 && y==4)
{ i='7'; }
else if(x==2 &&
y==5)
{ i='8';
}
else if(x==2
&& y==6)
{
i='9'; }
else
if(x==3 && y==5)
{ i='0'; }
12
else if(x==0 && y==7)
{ i='A';
}
else if(x==1 &&
y==7)
{
i='B'; }
else
if(x==2 && y==7)
{ i='C'; }
else if(x==3 && y==7)
{ i='D'; }
else if(x==3 && y==4)
{ i='*'; }
else if(x==3 &&
y==6)
{
i='#'; }
}
if(i!=-1)
{
detect_result[l]=i;
l++;
if(l==100)
{
for(l=0;l<100;l++)
printf(
}
flag++;
}
}
else if(j==0)
flag=0;
}
void delay(int period)
{
int i, j;
for(i=0;
i
{
for(j=0; j
}
}
(
3
)<
/p>
cmd
文件
源代码如下:
/*########
##################################################
##################
#
## $$Id:
$$
13