单片机产生38kHz信号的问题

我用STC89C52RC,12T单片机,12M晶振,定时器0设置工作方式1,16位那个,在定时器中断中写入重装值,并用IO口自身取反的办法产生38kHz红外调制信号。
相关程序:
Void timer() interrupt 1
{
TH0=(65536-13)/256;
TL0=(65536-13)%256;
IR=~IR
}
其中IR是前面定义的信号输出口。
按理要产生38k应该让IO口每隔13us取反一下,即按上面这样写的代码。运行后接到频率计上,显示只有35000Hz,为什么误差如此之大?莫非是指令占用时间了?这种办法产生方波信号是否不合适?请高明人指点。

首先,楼主的分析是正确的,确实是指令占用了时间,而且这与软件编译时的优化有关。下面从理论上分析一下(事实其实如此)原因:

中断产生的一瞬间,TH0和TL0都为0,但由于其定时计数并未停止,所以直到下一次重装前TL0依旧在计数,也就是说直到TL0=(65536-13)%256执行后,计数值才被重装。那么这段时间有什么情况发生呢:
1、如果程序中还使用了别的中断:定时中断产生后,单片机开始执行中断服务程序。一般情况下,首先是要执行中断入口0x000B处的跳转指令,12M12T下跳转指令LJMP等的执行周期为2us,重复一下,此时TL0和TH0已经为0了;然后执行TH0和TL0赋值指令,各需2us(汇编中:立即数赋值给直接地址),这就增加了6个us,所以事实上是13+6=19us取反一次,大约28KHz。
2、同上,但如果将TL0赋值语句放到TH0前,由于方式1下TH0TL1相当于一个INT型数据来计数的,TL0是低8位,所以事实上TL0被赋值后基本可以说被重装,计数这时才恢复正常,由于TH0的赋值被放到后面,所以事实上就减少了2个us,即13+4=17,大约30KHz。
3、如果程序中的中断只使用了这一个:那么编译器编译时,就会将中断程序从000BH开始放置,这样就又少了一条跳转指令的周期,时间上就变为13+2=15,大约33KHz。我想这应该属于是楼主的实际情况。
所以,正如你所说,这种方式指令本身占据了时间,所以产生误差不足为奇。这里关键是TL0重装指令的位置。

解决的办法无外乎两种:
1、将初值中的13改成11,TL0重装前提,减掉2us赋值时间。
2、采用方式2,初值设为256-13。由于方式2初值的重装是自动的,不占指令时间,所以很准确。

不过照理楼主现在应该检测到的是30K才对,为什么会测到35K我不敢妄断,也许是其它误差,比如频率计、晶振等、也许是编译器原因,需要看一下实际的汇编指令才能确定。
温馨提示:答案为网友推荐,仅供参考
第1个回答  2011-08-05
TT0(1,(65536-9000)) //引导码:9ms
TT0(0,(65536-4500)) // 4.5ms







/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:38KHz载波发射 + 延时程序
入口:(是否发射脉冲,延时约 x (uS))
说明:BT=0,不发射38KHz载波只延时;
BT=1,发射38KHz载波同时延时。
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void TT0(bit BT,uint x)
{
TH0 = x>>8; //输入T0初始值
TL0 = x;
TF0=0; //清0
TR0=1; //启动定时器0
if(BT == 0) while(!TF0);
else while(1) //38KHz脉冲,低电平占空比5:26
{
IR = 0;
if(TF0)break;
if(TF0)break;
IR = 1;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
}
TR0=0; //关闭定时器0
TF0=0; //标志位溢出则清0
IR =1; //脉冲停止后,发射端口常态为高电平
}
第2个回答  2011-08-05
你这个结果很正常的,中断响应需要一定的时间。
如果你需要详细分析,要把完整程序拿出来才可以。
如果只是为了完工,你可以把-13改为-12和-11,看看哪个更接近38kHz
第3个回答  2011-08-06
算一下机器周期,运算周期没考虑进去也就是误差原因
第4个回答  2011-08-05
学习了!看帖是享受,回帖是个美德!
相似回答