實例目的:學時定時器、計數器、中斷應用
說明:選用24MHz的晶體,主頻可達2MHz。用T1產生100us的時標,T0作信號脈沖計數器。假設晶體頻率沒有誤差,而且穩定不變(實際上可達萬分之一);被測信號是周期性矩形波(正負脈沖寬度都不能小于0.5us),頻率小于1MHz,大于1Hz。要求測量時標1S,測量精度為0.1%。
解:從測量精度要求來看,當頻率超過1KHz時,可采用1S時標內計數信號脈沖個數來測量信號頻,而信號頻率低于1KHz時,可以通過測量信號的周期來求出信號頻率。兩種方法自動轉換。
對于低于1KHz的信號,信號周期最小為1ms,也就是說超過1000us,而我們用的定時器計時脈沖周期為0.5us,如果定時多計或少計一個脈沖,誤差為1us,所以相對誤差為1us/1000us=0.1%。信號周期越大,即信號頻率越低,相對誤差就越小。
從上面描述來看,當信號頻率超過1KHz后,信號周期就少于1000us,顯然采用上面的測量方法,不能達到測量精度要求,這時我們采用1S單位時間計數信號的脈沖個數,最少能計到1000個脈沖,由于信號頻率不超過1MHz,而我們定時脈沖為2MHz,最差多計或少計一個信號脈沖,這樣相對誤差為1/1000,可見信號頻率越高,相對誤差越小。
信號除輸入到T1(P3.5)外,還輸入到INT1(P3.3)。
unsigned int us100; //對100us時間間隔單位計數,即有多少個100us。
unsigned char Second;
unsigned int K64; //對64K單位計數,即有多少個64K
unsigned char oldT0;
unsigned int oldus, oldK64, oldT1;
unsigned long fcy; //存放頻率值,單位為Hz
bit HighLow=1; //1:表示信號超過1KHz;0:表示信號低于1KHz。
void InitialHigh( void )
{
IE=0; IP=0; HighLow=1;
TMOD = (TMOD & 0xf0) | 0x02; TH0=-200; TL0=TH0; PX0=1; T0=1;
TMOD = (TMOD & 0x0f) | 0x50; TH1=0; TL1=0; T1=1; ET1=1;
Us100=0; Second=0; K64=0;
oldK64=0; oldT1=0;
TCON |= 0x50; //同時置 TR0=1; TR1=1;
EA = 1;
}
void InitialLow( void )
{
IE=0; IP=0; HighLow=0;
TMOD = (TMOD & 0xf0) | 0x02; TH0=-200; TL0=TH0; ET0=1; TR0=1;
INT1 = 1; IT1=1; EX1=1;
Us100=0; Second=0; K64=0;
oldK64=0; oldT1=0;
EA = 1;
}
void T0intr( void ) interrupt 1
{ if( HighLow==0 ) ++us100;
else
if( ++us100 >= 10000 )
{ unsigned int tmp1, tmp2;
TR1=0; tmp1=(TH1<<8) + (TL1); tmp2=K64; TR1=1;
fcy=((tmp2-oldK64)<<16) + (tmp1-oldT1);
oldK64=tmp1; oldT1=tmp2;
Second++;
us100=0;
}
}
void T1intr( void ) interrupt 3 { ++K64; }
void X1intr( void ) interrupt 2
{ static unsigned char sts=0;
switch( sts )
{
case 0: sts = 1; break;
case 1: oldT0=TL0; oldus=us100; sts=2; break;
case 2:
{
unsigned char tmp1, tmp2;
TR0=0; tmp1=TL0; tmp2=us100; TR0=1;
fcy = 1000000L/( (tmp2-oldus)*100L + (256-tmp1)/2 );
Second ++;
}
Sts = 0;
break;
}
}
void main( void )
{
if( HighLow==1) InitialHigh(); else InitialLow();
While(1)
{
if( Second != 0 )
{
Second = 0;
//display fcy 引用前面的數碼管驅動程序,注意下面對T0中斷服務程序的修改
{ unsigned char i;
for( i=0; i<8; i++ ){ Display(i, fcy%10); fcy /= 10; }
}
if( HighLow==1 )
if( fcy<1000L ){ InitalLow();}
else
if( fcy>1000L ){ InitalHigh();}
}
}
}
//修改T0的中斷服務程序,讓它在完成時標的功能時,同時完成數碼管顯示刷新
void T0intr( void ) interrupt 1
{
static unsigned char ms = 0;
if( HighLow==0 ) ++us100;
else
if( ++us100 >= 10000 )
{ unsigned int tmp1, tmp2;
TR1