1. 程式人生 > >51微控制器的延時及定時器

51微控制器的延時及定時器

2.軟體延時:軟體延時有時候不能夠做到非常精確地延時,主要靠迴圈體或是一些無意義的指令來完成。

微控制器都有一個屬於自己的晶振頻率:11.0592Mhz(主要是為了設定波特率的方便),12Mhz6Mhz等(後面的例子全都用12M晶振)。對於12Mhz的晶振頻率,一個機器週期為1us,對於51微控制器的庫函式就有nop()這個函式(呼叫時需要#include<intrins.h>),實現延時一個機器週期。那麼就有了簡單的軟體延時程式。可以有delay5us,delay10us等程式,只需要在程式裡用nop()就可以了,但是要注意呼叫該函式需要有一個呼叫指令(2us),結束後也有個結束指令(2us),而且在函式裡呼叫該函式,只有最內層的函式有結束指令。

例如:

void Delay5us( ) {

_NOP_( );

}

void Delay10us()

{

_NOP_( );

_NOP_( );

_NOP_( );

_NOP_( );

_NOP_( );

_NOP_( );

}

分別是兩個不同的延時函式。

再就是我們會經常使用的for迴圈延時程式了,我現在也是在學微控制器,在郭天祥老師的程式裡經常會有

void delay(unsigned int i)

{

while(i--);

}

在這個程式裡,如果沒有中斷完全可以用模擬模擬的方法並自己調整,直到自己想要的延時時間,因為在後面中斷,串列埠,模擬時序的時候並沒有那麼精確的延時,都是一個比較大的時間段,但是學了就儘量弄得精確一些。(如果是大神完全可以摳彙編,用示波器,當然我現在都不會。),因為這是c語言程式設計,不是彙編,彙編的一條指令(也就是機器指令)機器週期是一定的,也就是說可以很精確,但是c不行,需要取決於很多東西(如編譯器,cpu等等)。

繼續說這個程式,他就是用不斷迴圈的做一些無意義的事,達到延時的目的。因為你不能準確的知道一條c指令確是多少時間(或者說會有誤差),在上面的程式裡,當i=1時,大約延時10us。下面再給出幾個延時函式,僅供參考。

200ms延時子程式

程式:

void delay200ms(void)

{

unsigned char i,j,k;

for(i=5;i>0;i--)

for(j=132;j>0;j--)

for(k=150;k>0;k--);

}

10ms延時子程式

程式:

void delay10ms(void)

{

unsigned char i,j,k;

for(i=5;i>0;i--)

for(j=4;j>0;j--)

for(k=248;k>0;k--);

}

關於函式延時以及一些基本的程式,我也只瞭解這些,再來說說定時器實現精確延時。

定時器有4種模式,一般較為常用的為方式1,因為一旦溢位就會申請中斷,因此一次溢位共需要65536us,約等於63.5ms,若定時器工作在方式2,則可實現極短時間的精確延時;如使用其他定時方式,則要考慮重灌定時初值的時間(重灌定時器初值佔用2個機器週期)。

在實際應用中,定時常採用中斷方式,如進行適當的迴圈可實現幾秒甚至更長時間的延時。使用定時器/計數器延時從程式的執行效率和穩定性兩方面考慮都是最佳的方案。

在這裡,用定時器中斷服務程式中,需要給定時器重灌初值,完成定時器中斷服務程式就回到主程式,但是要注意,若是沒有關閉中斷,在執行中斷服務程式(進入中斷服務程式需要時間)而且沒有到給它重新賦值的語句前,定時器也在計數中,只有在重新賦初值後的瞬間,又開始從新的值處開始計時,因此,這是一個誤差,解決誤差的辦法就是賦值初值的時候加上它當前的值。TH0=TH0+初值,TL0=TL0+初值。

另外,中斷服務程式不要過長,或者有一個或多個延時程式(不是說不能,是不建議),否則中斷服務程式還沒結束就又進入中斷,會造成崩潰。

下面給出一個程式,實現數碼管每隔1s迴圈顯示0-F,實現準確延時,當然不是絕對的準確。(我就直接把我學習微控制器開發板上面的程式拷過來了)。這個程式重新賦值的時候沒有向我上面說的那樣,中斷服務程式也略顯贅餘,可以把if放在主函式while中。

/**************************************************************************************
*		              定時器1實驗												  *
實現現象:下載程式後數碼管最後一位間隔一秒迴圈顯示0-F。使用微控制器內部定時器可以實現準確延時。																				  
***************************************************************************************/

#include "reg52.h"			 //此檔案中定義了微控制器的一些特殊功能暫存器

typedef unsigned int u16;	  //對資料型別進行宣告定義
typedef unsigned char u8;

sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
					0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//顯示0~F的值
u8 n=0;
/*******************************************************************************
* 函 數 名         : Timer1Init
* 函式功能		   : 定時器1初始化
* 輸    入         : 無
* 輸    出         : 無
*******************************************************************************/
void Timer1Init()
{
	TMOD|=0X10;//選擇為定時器1模式,工作方式1,僅用TR1開啟啟動。

	TH1=0XFC;	//給定時器賦初值,定時1ms
	TL1=0X18;	
	ET1=1;//開啟定時器1中斷允許
	EA=1;//開啟總中斷
	TR1=1;//開啟定時器			
}

/*******************************************************************************
* 函 數 名       : main
* 函式功能		 : 主函式
* 輸    入       : 無
* 輸    出    	 : 無
*******************************************************************************/
void main()
{	
	LSA=0;
	LSB=0;
	LSC=0;
	Timer1Init();  //定時器1初始化
	while(1);		
}

/*******************************************************************************
* 函 數 名         : void Timer1() interrupt 3
* 函式功能		   : 定時器0中斷函式
* 輸    入         : 無
* 輸    出         : 無
*******************************************************************************/
void Timer1() interrupt 3
{
	static u16 i;
	TH1=0XFC;	//給定時器賦初值,定時1ms
	TL1=0X18;
	i++;
	if(i==1000)
	{
		i=0;
		P0=smgduan[n++];
		if(n==16)n=0;	
	}	
}

注:這裡數碼管用的是138譯碼器。

這就是我的一些理解,我現在也在學習中,想著花點時間總結會有更好的脈絡。之後會再來完善。有錯歡迎指出。