单片机系统要实现DTMF拨号,常用的有2种方法:
第一是使用HT9200+晶震的方法,开销是3个I/O口和一片HT9200,一个晶震, 第二是使用具有PWM功能的单片机,软件合成DTMF,单片机运行要比较快才行,也要定时器。
本人提出第3种方法,就是使用普通2个I/O口加一个16位定时器的方法(当然也可以使用2个16位定时器,这样程序就很简单,问题是有的单片机只有一个16位定时器就不好办),这2个I/O分别输出高低频,再分别加2个RC低通滤波器即可。当然这些方法都要加DTMF放大。
本方法是:一个I/O输出高频,另一个输出低频。问题的关键是只有一个定时器怎么输出2个不同频率的波形呢?思路是:
先让定时器定时在高频所要的时间上,等定时到后,高频端口输出取反一次,然后看下一个高频输出和低频输出那个先到,就将那个剩下的时间做为新的定时常数给定时器,如此类推。如果这个过程完全由软件计算实现,MCU就有可能忙不过来(因为要计算int数据),最好的办法就是事先算好,只查表速度就完全没问题。经过计算,如果数据不事先经过任何选择就建表的话,这个表大约要2K空间,一般单片机无法接受。研究表明,在频率0.5%误差范围内,将定时常数适当改变,可以将这个表减少到620字节左右,这样就可以接受了。当然,这个计算方法要点技巧。下面是C51定时输出双音频的程序。TimeConst[]就是要建的表。程序很简洁,全是字节操作,生成的汇编代码也很少。有兴趣进一步讨论的请联系solar_pcb@163.com。
void TimeCount1(void) interrupt 3 using 2 { ii=TimeConst[addr]; //取定时常数高字节 TH1=(ii | 0x80); //高字节的D7是输出高频或低频的标志 addr++; TL1=TimeConst[addr]; //取定时常数低字节 addr++; if(cLen==0){ //一个轮回时高低同时改变 PH=~PH; PL=~PL; } else{ if((ii & 0x80)==0){ //如果是高频 PH=~PH; } else{ //如果是低频 PL=~PL; } } cLen=cLen+2; if(cLen>=CircleLen){//这里没使用cLen=cLen%CircleLen是因为这个表达式的汇编代码太长,执行时间也长。 cLen=0; //一个轮回,地址重新开始 addr=n+n; } }
|