共3条
1/1 1 跳转至页
DTMF,DTMF,PWM 双龙DTMF演示程序的两点疑问,做过DTMF(软件PWM方法)的请进
问
这是双龙DTMF演示程序
#include <io8515v.h>
#include <macros.h>
#define Xtal 8000000 // 系统时钟频率
#define prescaler 1 // T1预分频系数
#define N_samples 128 // 在查找表中的样本数
#define Fck Xtal/prescaler // T1工作频率
#define delaycyc 10 // 读取port C口延时循环数
#pragma interrupt_handler ISR_T1_Overflow:7
/*************************** 正弦表 *****************************
样本表: 一个周期分成128个点,每点按7位进行量化
****************************************************************/
flash unsigned char auc_SinParam [128] =
{64,67,70,73,76,79,82,85,88,91,94,96,99,102,104,106,109,111,113,115,117,
118,120,121,123,124,125,126,126,127,127,127,127,127,127,127,126,126,125,
124,123,121,120,118,117,115,113,111,109,106,104,102,99,96,94,91,88,85,82,
79,76,73,70,67,64,60,57,54,51,48,45,42,39,36,33,31,28,25,23,21,18,16,14,
12,10,9,7,6,4,3,2,1,1,0,0,0,0,0,0,0,1,1,2,3,4,6,7,9,10,12,14,16,18,21,23,
25,28,31,33,36,39,42,45,48,51,54,57,60};
//*************************** x_SW *************************
// x_SW 表(8倍): x_SW = ROUND(8*N_samples*f*510/Fck)
//************************************************************
const unsigned char auc_frequencyH [4] = {
107,96,
87,79};
const unsigned char auc_frequencyL [4] = {
61,56,
50,46};
//************************** 全局变量 ****************************
unsigned char x_SWa = 0x00; // 高频信号脉冲宽度
unsigned char x_SWb = 0x00; // 低频信号脉冲宽度
unsigned int X_LUTaExt = 0;
unsigned int X_LUTbExt = 0;
unsigned int X_LUTa;
unsigned int X_LUTb;
/*****************************************************************
定时器溢出中断服务程序
******************************************************************/
void ISR_T1_Overflow (void)
{
X_LUTaExt += x_SWa;
X_LUTbExt += x_SWb;
X_LUTa = (char)(((X_LUTaExt+4) >> 3)&(0x007F));
X_LUTb = (char)(((X_LUTbExt+4) >> 3)&(0x007F));
// 计算 PWM 值: 高频值 + 3/4 低频值
OCR1A = (auc_SinParam[X_LUTa] + (auc_SinParam[X_LUTb]-(auc_SinParam[X_LUTb]>>2)));
}
/***********************************************************
初始化
***********************************************************/
void init (void)
{
MCUCR=0x00;
TIMSK = 0x80; // T1 溢出中断使能
TCCR1A = (1<<COM1A1)+(1<<PWM10); // 不翻转、8位PWM
TCCR1B = (1<<CS10); // 预分频系数为1、即CLK/1
DDRD = (1 <<PD5); // PD5 (OC1A)用作输出
_SEI(); // 全局中断使能
}
/*********************************************************************
为从PORT C口读取稳定的按键数据,所必须的延时程序(消抖延时)
*********************************************************************/
void Delay (void)
{
int i;
for (i = 0; i < delaycyc; i++) _NOP();
}
/********************************************************************
主程序
从PORT C口读取按键数据(如:SL+ AVR实验板) ,来确定产生哪个
高频(列)和低频(行)信号的混合信号,并且修正 x_SWa 和 x_SWb。
行 -> PINC 高四位
列 -> PINC 低四位
*********************************************************************/
void main (void)
{
unsigned char uc_Input;
unsigned char uc_Counter = 0;
init();
for(;;){
// 高四位 - 行
DDRC = 0x0F; // 高四位输入、低四位输出
PORTC = 0xF0; // 高四位打开上位、低四位输出低电平
uc_Counter = 0;
Delay(); // 延时等待 Port C 电平稳定
uc_Input = PINC; // 读取 Port C
do
{
if(!(uc_Input & 0x80)) // 检查MSB是否为低
{
// 取低音脉冲宽度并结束循环
x_SWb = auc_frequencyL[uc_Counter];
uc_Counter = 4;
}
else
{
x_SWb = 0; // 没有频率调制要求
}
uc_Counter++;
uc_Input = uc_Input << 1; // 左移一位
}
while ((uc_Counter < 4));
// 低四位 - 列
DDRC = 0xF0; // 高四位输出、低四位输入
PORTC = 0x0F; // 高四位输出低电平、低四位打开上拉
uc_Counter = 0;
Delay(); // 延时等待 Port C 电平稳定
uc_Input = PINC;
uc_Input = uc_Input << 4;
do
{
if(!(uc_Input & 0x80)) // 检查 MSB 是否为低
{
x_SWa = auc_frequencyH[uc_Counter];//取高音脉冲宽度并结束循环
uc_Counter = 4;
}
else
{
x_SWa = 0;
}
uc_Counter++;
uc_Input = uc_Input<< 1;
}
while (uc_Counter < 4);
}
}
答 1: 关于两点疑问1:
DTMF音频矩阵的高频组为:
1209,1336,1477,1633
低频组为:697,770,852,941(Hz)
为什么在程序中定义为:
const unsigned char auc_frequencyH [4] = {
107,96,
87,79};
const unsigned char auc_frequencyL [4] = {
61,56,
50,46};
2:
atmel官方网站提供的查表算法为
X_LUTa=ROUND( (X_LUTaExt+8*N_samples*f*510/Fck) /8 )
程序中相应语句为:
x_SW = ROUND(8*N_samples*f*510/Fck)
X_LUTaExt += x_SWa;
X_LUTa = (char)(((X_LUTaExt+4) >> 3)&(0x007F));
其中的
((X_LUTaExt+4) >> 3)
怎么理解?
答 2: 第一个问题已经解决定义中
const unsigned char auc_frequencyH [4] = {
107,96,
87,79};
不是频率,是步长,是经过x_SW = ROUND(8*N_samples*f*510/Fck)
计算过的,如107=(8*128*1633*510)/8 000 000
我主观地认为auc_frequency就是频率的定义了
见笑了 答 3: 这么久了,都没有高人指点?我该怎么办呢? 答 4: 眼睁睁的看着它沉下去了,伤心 答 5: 请 把电路图 粘贴出来请 把电路图 粘贴出来
不然很难 解决 问题 答 6: 没有认真分析,简单提供一个想法而已atmel官方网站提供的查表算法为
X_LUTa=ROUND( (X_LUTaExt+8*N_samples*f*510/Fck) /8 )
程序中相应语句为:
x_SW = ROUND(8*N_samples*f*510/Fck)
X_LUTaExt += x_SWa;
X_LUTa = (char)(((X_LUTaExt+4) >> 3)&(0x007F));
其中的
((X_LUTaExt+4) >> 3)
怎么理解?
简单判断,仅为提供思路!
其中的(X_LUTaExt + 4)>>3有点象除以8!
答 7: TO:mylovetus AND 123654789TO:mylovetus
其中的(X_LUTaExt + 4)>>3其中>>3除以8很容易理解
但是+ 4的涵义我一直搞不懂
TO:123654789
电路图我也没有,我仅仅是研究算法,我觉得这跟硬件关系不大,很显然是个4*4键盘扫描电路,我觉得这跟硬件关系不大,关键是产生DTMF信号的算法实现
不过,要谢谢两位热心人
等待再有高人指点 答 8: 我自己顶! 答 9: 我再顶... 答 10: TO 94+4应该是为了调整进位,用来划分比例查表,N=x*1/4的关系。
比例对应0-3对应0
4-7对应1
。。。。
答 11: 若有所悟!但是划分查表的意义何在?划分比例查表到底是怎么回事?都在什么地方有应用?还请极限大哥好人做到底,再点化一下小弟,不胜感激! 答 12: 呵呵到ATMEL 网站上去看看 那里又详细介绍
不过该程序产生的软件DTMF波还不能用于电话网,需要外加电路
如果真要发DTMF还是用硬件(HT9200)比较可靠 答 13: 我做过用5个I/O再加R-2R电阻网络做D/A的DTMF发生。我用的51,没PWM,但我想软件上核心是一样的。我的东西已经做成产品了,在省内各偏僻农村用做小数据传输,效果很好。
我没看你写的代码,我也看不懂,跟你讲讲思路,可能更有效。
Fdtmf=Fh+0.9Fl;这里的系数0.9我记不太清了标准是多少了,只告诉你原因:高频群在传输时衰减较大,所以发送时做大点,这样到达交换机时和低频群信号强度差不多,试验时可忽略这系数,程序简单点,城里用没一点问题。
好了说正题,显然你应该知道了基本思路就是用一个较高的采样频率,计算每个采样时刻的Fdtmf值,然后送到I/O口或刷新PWM寄存器。我做的5位D/A采样周期小于60US,我没条件做试验,据我另一个朋友讲,采样频率比DA分辨率更重要,当然分辨率高点,采样率可低点。
f=sin(2*pi*f*t),设采样周期为T,则
f=sin(2*pi*f*T*n),其中,2*pi*f*T是一个随f不同而不同的纯小数,n=0,1,2,,,,,,,N.对应各采样时刻。DTMF持续时间=N*T。显然为了不算SIN,我们就需要一个SIN表。
我们只需每时刻把2*pi*f*T加上上次的值(初始值显然是零),然后查表就可以得到当前时刻得频率幅度了。
基本原理就这么简单,也许难理解的是技巧。
技巧如下:
如果是51,它有10进制加法指令,这样,最直观的,我们把2*pi*f*T保留两个字节(考虑误差允许),每时刻做十进制累加就可以了。理解最关键的一点(个人认为,呵呵),这样的双字节加法正好不要考虑溢出,也就是正好不要考虑累加的整数部分。整数部分的物理意义是什么?它表示正弦波的周期数,而正弦波是周期函数,计算幅度时不用考虑的。查表的时候取累加和的第一个字节就可以了,记住,这时SIN表是分成100等份的。
这样,每时刻高频群算一次SIN幅度值,低频群算一次SIN幅度值,加起来就是DTMF当前幅度值了。
当用的单片机没有十进制加法时,用其他指令去模拟也是可以的,有没有更好的办法呢?有。
令1.0000(十进制)=10000H(16进制)则2*pi*f*T=?
这样我们就可以用最基本的2进制加法了,每个单片机都有的指令,而且更方便。这时,SIN表是256等份的。
其他:T原则上是随便取的,当然应该是定时器的整数值,而且越小越好;
如果用0.9的系数,最简单的是做个0.9*sin的表;
如果嫌表大,可以再利用对称性把表做小点;
效果是相当好的,我做的软DTMF指标在仪器上测比硬件做的还好。
答 14: 好人啊,感激涕零!!! 答 15: 顶teddeng ,好! 答 16: 我的道理是说清楚了,但有个明显的错误不更正了,正好让你想想,呵呵。 答 17: 由于发现了我解释中的错误谨慎起见,还是看了ATMEL的文档。结论:放心,思路是一样的,我解释得直观点,精度保守起见高点(代价应该是多两个字节RAM)。
+4的意思费好大劲才想通,这个例子里不加也没关系,但这样才是严谨的。想通了很简单,就是+0.5的意思。即算出是10.5XX时,按11查表,而不是按10查表。从表达式看不影响长期累加精度。 答 18: teddeng大哥太热心了,我太感动了
#include <io8515v.h>
#include <macros.h>
#define Xtal 8000000 // 系统时钟频率
#define prescaler 1 // T1预分频系数
#define N_samples 128 // 在查找表中的样本数
#define Fck Xtal/prescaler // T1工作频率
#define delaycyc 10 // 读取port C口延时循环数
#pragma interrupt_handler ISR_T1_Overflow:7
/*************************** 正弦表 *****************************
样本表: 一个周期分成128个点,每点按7位进行量化
****************************************************************/
flash unsigned char auc_SinParam [128] =
{64,67,70,73,76,79,82,85,88,91,94,96,99,102,104,106,109,111,113,115,117,
118,120,121,123,124,125,126,126,127,127,127,127,127,127,127,126,126,125,
124,123,121,120,118,117,115,113,111,109,106,104,102,99,96,94,91,88,85,82,
79,76,73,70,67,64,60,57,54,51,48,45,42,39,36,33,31,28,25,23,21,18,16,14,
12,10,9,7,6,4,3,2,1,1,0,0,0,0,0,0,0,1,1,2,3,4,6,7,9,10,12,14,16,18,21,23,
25,28,31,33,36,39,42,45,48,51,54,57,60};
//*************************** x_SW *************************
// x_SW 表(8倍): x_SW = ROUND(8*N_samples*f*510/Fck)
//************************************************************
const unsigned char auc_frequencyH [4] = {
107,96,
87,79};
const unsigned char auc_frequencyL [4] = {
61,56,
50,46};
//************************** 全局变量 ****************************
unsigned char x_SWa = 0x00; // 高频信号脉冲宽度
unsigned char x_SWb = 0x00; // 低频信号脉冲宽度
unsigned int X_LUTaExt = 0;
unsigned int X_LUTbExt = 0;
unsigned int X_LUTa;
unsigned int X_LUTb;
/*****************************************************************
定时器溢出中断服务程序
******************************************************************/
void ISR_T1_Overflow (void)
{
X_LUTaExt += x_SWa;
X_LUTbExt += x_SWb;
X_LUTa = (char)(((X_LUTaExt+4) >> 3)&(0x007F));
X_LUTb = (char)(((X_LUTbExt+4) >> 3)&(0x007F));
// 计算 PWM 值: 高频值 + 3/4 低频值
OCR1A = (auc_SinParam[X_LUTa] + (auc_SinParam[X_LUTb]-(auc_SinParam[X_LUTb]>>2)));
}
/***********************************************************
初始化
***********************************************************/
void init (void)
{
MCUCR=0x00;
TIMSK = 0x80; // T1 溢出中断使能
TCCR1A = (1<<COM1A1)+(1<<PWM10); // 不翻转、8位PWM
TCCR1B = (1<<CS10); // 预分频系数为1、即CLK/1
DDRD = (1 <<PD5); // PD5 (OC1A)用作输出
_SEI(); // 全局中断使能
}
/*********************************************************************
为从PORT C口读取稳定的按键数据,所必须的延时程序(消抖延时)
*********************************************************************/
void Delay (void)
{
int i;
for (i = 0; i < delaycyc; i++) _NOP();
}
/********************************************************************
主程序
从PORT C口读取按键数据(如:SL+ AVR实验板) ,来确定产生哪个
高频(列)和低频(行)信号的混合信号,并且修正 x_SWa 和 x_SWb。
行 -> PINC 高四位
列 -> PINC 低四位
*********************************************************************/
void main (void)
{
unsigned char uc_Input;
unsigned char uc_Counter = 0;
init();
for(;;){
// 高四位 - 行
DDRC = 0x0F; // 高四位输入、低四位输出
PORTC = 0xF0; // 高四位打开上位、低四位输出低电平
uc_Counter = 0;
Delay(); // 延时等待 Port C 电平稳定
uc_Input = PINC; // 读取 Port C
do
{
if(!(uc_Input & 0x80)) // 检查MSB是否为低
{
// 取低音脉冲宽度并结束循环
x_SWb = auc_frequencyL[uc_Counter];
uc_Counter = 4;
}
else
{
x_SWb = 0; // 没有频率调制要求
}
uc_Counter++;
uc_Input = uc_Input << 1; // 左移一位
}
while ((uc_Counter < 4));
// 低四位 - 列
DDRC = 0xF0; // 高四位输出、低四位输入
PORTC = 0x0F; // 高四位输出低电平、低四位打开上拉
uc_Counter = 0;
Delay(); // 延时等待 Port C 电平稳定
uc_Input = PINC;
uc_Input = uc_Input << 4;
do
{
if(!(uc_Input & 0x80)) // 检查 MSB 是否为低
{
x_SWa = auc_frequencyH[uc_Counter];//取高音脉冲宽度并结束循环
uc_Counter = 4;
}
else
{
x_SWa = 0;
}
uc_Counter++;
uc_Input = uc_Input<< 1;
}
while (uc_Counter < 4);
}
}
答 1: 关于两点疑问1:
DTMF音频矩阵的高频组为:
1209,1336,1477,1633
低频组为:697,770,852,941(Hz)
为什么在程序中定义为:
const unsigned char auc_frequencyH [4] = {
107,96,
87,79};
const unsigned char auc_frequencyL [4] = {
61,56,
50,46};
2:
atmel官方网站提供的查表算法为
X_LUTa=ROUND( (X_LUTaExt+8*N_samples*f*510/Fck) /8 )
程序中相应语句为:
x_SW = ROUND(8*N_samples*f*510/Fck)
X_LUTaExt += x_SWa;
X_LUTa = (char)(((X_LUTaExt+4) >> 3)&(0x007F));
其中的
((X_LUTaExt+4) >> 3)
怎么理解?
答 2: 第一个问题已经解决定义中
const unsigned char auc_frequencyH [4] = {
107,96,
87,79};
不是频率,是步长,是经过x_SW = ROUND(8*N_samples*f*510/Fck)
计算过的,如107=(8*128*1633*510)/8 000 000
我主观地认为auc_frequency就是频率的定义了
见笑了 答 3: 这么久了,都没有高人指点?我该怎么办呢? 答 4: 眼睁睁的看着它沉下去了,伤心 答 5: 请 把电路图 粘贴出来请 把电路图 粘贴出来
不然很难 解决 问题 答 6: 没有认真分析,简单提供一个想法而已atmel官方网站提供的查表算法为
X_LUTa=ROUND( (X_LUTaExt+8*N_samples*f*510/Fck) /8 )
程序中相应语句为:
x_SW = ROUND(8*N_samples*f*510/Fck)
X_LUTaExt += x_SWa;
X_LUTa = (char)(((X_LUTaExt+4) >> 3)&(0x007F));
其中的
((X_LUTaExt+4) >> 3)
怎么理解?
简单判断,仅为提供思路!
其中的(X_LUTaExt + 4)>>3有点象除以8!
答 7: TO:mylovetus AND 123654789TO:mylovetus
其中的(X_LUTaExt + 4)>>3其中>>3除以8很容易理解
但是+ 4的涵义我一直搞不懂
TO:123654789
电路图我也没有,我仅仅是研究算法,我觉得这跟硬件关系不大,很显然是个4*4键盘扫描电路,我觉得这跟硬件关系不大,关键是产生DTMF信号的算法实现
不过,要谢谢两位热心人
等待再有高人指点 答 8: 我自己顶! 答 9: 我再顶... 答 10: TO 94+4应该是为了调整进位,用来划分比例查表,N=x*1/4的关系。
比例对应0-3对应0
4-7对应1
。。。。
答 11: 若有所悟!但是划分查表的意义何在?划分比例查表到底是怎么回事?都在什么地方有应用?还请极限大哥好人做到底,再点化一下小弟,不胜感激! 答 12: 呵呵到ATMEL 网站上去看看 那里又详细介绍
不过该程序产生的软件DTMF波还不能用于电话网,需要外加电路
如果真要发DTMF还是用硬件(HT9200)比较可靠 答 13: 我做过用5个I/O再加R-2R电阻网络做D/A的DTMF发生。我用的51,没PWM,但我想软件上核心是一样的。我的东西已经做成产品了,在省内各偏僻农村用做小数据传输,效果很好。
我没看你写的代码,我也看不懂,跟你讲讲思路,可能更有效。
Fdtmf=Fh+0.9Fl;这里的系数0.9我记不太清了标准是多少了,只告诉你原因:高频群在传输时衰减较大,所以发送时做大点,这样到达交换机时和低频群信号强度差不多,试验时可忽略这系数,程序简单点,城里用没一点问题。
好了说正题,显然你应该知道了基本思路就是用一个较高的采样频率,计算每个采样时刻的Fdtmf值,然后送到I/O口或刷新PWM寄存器。我做的5位D/A采样周期小于60US,我没条件做试验,据我另一个朋友讲,采样频率比DA分辨率更重要,当然分辨率高点,采样率可低点。
f=sin(2*pi*f*t),设采样周期为T,则
f=sin(2*pi*f*T*n),其中,2*pi*f*T是一个随f不同而不同的纯小数,n=0,1,2,,,,,,,N.对应各采样时刻。DTMF持续时间=N*T。显然为了不算SIN,我们就需要一个SIN表。
我们只需每时刻把2*pi*f*T加上上次的值(初始值显然是零),然后查表就可以得到当前时刻得频率幅度了。
基本原理就这么简单,也许难理解的是技巧。
技巧如下:
如果是51,它有10进制加法指令,这样,最直观的,我们把2*pi*f*T保留两个字节(考虑误差允许),每时刻做十进制累加就可以了。理解最关键的一点(个人认为,呵呵),这样的双字节加法正好不要考虑溢出,也就是正好不要考虑累加的整数部分。整数部分的物理意义是什么?它表示正弦波的周期数,而正弦波是周期函数,计算幅度时不用考虑的。查表的时候取累加和的第一个字节就可以了,记住,这时SIN表是分成100等份的。
这样,每时刻高频群算一次SIN幅度值,低频群算一次SIN幅度值,加起来就是DTMF当前幅度值了。
当用的单片机没有十进制加法时,用其他指令去模拟也是可以的,有没有更好的办法呢?有。
令1.0000(十进制)=10000H(16进制)则2*pi*f*T=?
这样我们就可以用最基本的2进制加法了,每个单片机都有的指令,而且更方便。这时,SIN表是256等份的。
其他:T原则上是随便取的,当然应该是定时器的整数值,而且越小越好;
如果用0.9的系数,最简单的是做个0.9*sin的表;
如果嫌表大,可以再利用对称性把表做小点;
效果是相当好的,我做的软DTMF指标在仪器上测比硬件做的还好。
答 14: 好人啊,感激涕零!!! 答 15: 顶teddeng ,好! 答 16: 我的道理是说清楚了,但有个明显的错误不更正了,正好让你想想,呵呵。 答 17: 由于发现了我解释中的错误谨慎起见,还是看了ATMEL的文档。结论:放心,思路是一样的,我解释得直观点,精度保守起见高点(代价应该是多两个字节RAM)。
+4的意思费好大劲才想通,这个例子里不加也没关系,但这样才是严谨的。想通了很简单,就是+0.5的意思。即算出是10.5XX时,按11查表,而不是按10查表。从表达式看不影响长期累加精度。 答 18: teddeng大哥太热心了,我太感动了
共3条
1/1 1 跳转至页
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
【笔记】生成报错synthdesignERROR被打赏50分 | |
【STM32H7S78-DK评测】LTDC+DMA2D驱动RGBLCD屏幕被打赏50分 | |
【STM32H7S78-DK评测】Coremark基准测试被打赏50分 | |
【STM32H7S78-DK评测】浮点数计算性能测试被打赏50分 | |
【STM32H7S78-DK评测】Execute in place(XIP)模式学习笔记被打赏50分 | |
每周了解几个硬件知识+buckboost电路(五)被打赏10分 | |
【换取逻辑分析仪】RA8 PMU 模块功能寄存器功能说明被打赏20分 | |
野火启明6M5适配SPI被打赏20分 | |
NUCLEO-U083RC学习历程2-串口输出测试被打赏20分 | |
【笔记】STM32CUBEIDE的Noruletomaketarget编译问题被打赏50分 |