- #define _1231_C_
- #include "reg51.h"
- //sbit OE=P2^3;
- unsigned int SystemTime;
- void timer0(void) interrupt 1 using 3 //中斷部分代碼,見下文的釋疑
- {
- TH0 = 0xdb;
- TL0 = 0xff;
- // TF0 = 0;
- SystemTime++;
- }
- void main()
- {
- TMOD &= 0xF0;
- TMOD |= 0x01; //TMOD的值表示定時器工作方式選擇
- TH0 = 0xdb; //寫入初始值,初始值可以決定定時多久
- TL0 = 0xff;
- //根據下文的木桶比喻的話,如果TH0 = 0x00;TL0 = 0x00;則表示從桶底開始裝水。
- //TH0 = 0xdb;TL0 = 0xff;可以這樣子理解相當于木桶里已經有部分液鉛在里面,
- //TH0和TL0這個兩個值表示木桶里液鉛的高度,即此時桶里只能從液鉛的高度以上開始裝水,
- //TH0 = 0xff;TL0 = 0xff;即表示桶的最高位置.
- TF0 = 0; //計數到時TF0為1,即當TH0 = 0xff;TL0 = 0xff;再運行一步TF0 = 1;
- TR0 = 1; //開始計數,從這時起,每運行一步TH0和TL0都會增加,直到TH0 = 0xff;TL0 = 0xff;
- //相當于開水龍頭,如TR0=0則TH0和TL0不變
- ET0 = 1; //允許定時器0中斷
- EA=1; //開總中斷
- //下面是個死循環,程序里每運行一步TH0和TL0都會增加,當增加到TH0 = 0xff;TL0 = 0xff;
- //單片機會從死循環里退出,去執行中斷部分的代碼,即開始運行void timer0(void) interrupt 1 using 3{}
- //運行完中斷部分的代碼后,接著繼續執行死循環里的代碼。
- //注意:當TH0 = 0xff;TL0 = 0xff;再運行,TF0并沒有從0變為1,個人猜測TF0=1;時觸發了中斷,并重新被置零。
- //如把ET0 = 1;和EA=1;注釋掉,當TH0 = 0xff;TL0 = 0xff;再運行,TF0會變為1,此時不會再執行中斷部分代碼。
- while(1)
- {
- if ((SystemTime%100)<50) //SystemTime除以100,余數小于50為真
- {
- …………;
- }
- else
- {
- …………;
- }
- };
- }
復制代碼釋疑:void Timer0() interrupt 1 using 1 Timer0 是函數名,隨便取的 interrupt xx using y 跟在interrupt 后面的xx 值得是中斷號,就是說這個函數對應第幾個中斷端口,一般在51中 0 外部中斷0 1 定時器0 2 外部中斷1 3 定時器1 4 串行中斷 實際上編譯的時候就是把你這個函數的入口地址方到這個對應中斷的跳轉地址 using y 這個y是說這個中斷函數使用的那個寄存器組,51里面一般有4組 r0 -- r7寄存器,一共有32個,如果你的終端函數和別的程序用的不是同一個寄存器組則進入中斷的時候就不會將寄存器組壓入堆棧返回時也不會談出來節省代碼和時間 初始值算法:定時器是當總數達到FFFFH后產生中斷吧!那你要讓它計數10000,是不是用FFFF(16進制)減去10000(十進制)的數當計數初值 啊?TH0=-(10000/256); TL0=-(10000%256)跟FFFF(16進制)減去10000(十進制)的數是一樣的。從TH0=-(10000/256); TL0=-(10000%256)開始計數,計數到10000剛好滿。跟用FFFF(16進制)減去10000(十進制)的數一樣!!!寫起來更簡單,不 用算!!! 看看原碼、補碼就知道。正數的補碼是對應的二進制數,符號位為零,負數的補碼是它的絕對值對應的二進制數按位取反再加一,符號位為一。無符號數不考慮符號,那么這個結果就跟用FFFF減去它的絕對值一樣 我們學習了用指令延時閃燈,但是用指令方式閃燈有cpu不能做其他工作的缺點。 這一課,我們將學習如何使用定時器方式使燈閃爍。 中斷的理解。 這里將涉及到單片機中斷的應用,在cpu的一步步按照指令運行的過程中(主程序),可能會有其它的更緊急的需要做的事情(中斷服務程序),需要cpu暫時停止當前的程序(主程序),做完了(中斷服務程序)之后,又可以繼續去運行先前的程序(主程序)。就像你正在吃飯,一邊又在給水桶里放水,吃著吃著,水滿了,你就得趕快去把水龍頭關掉或者換一個空的水桶,再回來吃飯。 單片機的定時器就像是一個水桶,你讓它啟動了,也就是水龍頭打開了;開始裝水了;定時在每個機器周期不斷自動加1,最后溢出了;水桶的水不斷增加,最也就滿出來了;定時器溢出時,你就要去做處理了;水桶的水滿了,你也應該處理一下了;處理完后,單片機又可以回到剛剛開停止的地方繼續運行;水桶處理了,先前你在做什么也可以繼續去做什么了。 單片機的主程序是從0x0000開始運行的,單片機服務程序從哪里開始運行呢?在51里,有多個中斷服務程序入口,0號入口是外中斷0,地址在0x0003;1號入口是定時器0,在 0x000B;2號入口是外中斷1;地址在0x0013,3號入口是定時器2;地址在0x001B,等等。當中斷發生時,程序就記下當前運行的位置,跳到對應的中斷入口去運行中斷服務程序,運行完之后,又跳回到原來的位置繼續運行。 在C51中,你不用理會中斷服務程序放在哪里,會怎么跳轉。你只要把某個函數標識為幾號中斷服務函數就可以了。在發生了對應的中斷時,就會自動的運行這個函數。 請看一下相關的51的硬件的書,對定時器工作的寄存器設置做進一步的了解。也可以做完試驗再了解,因為例程中都已經為您設置好了。 請看程序,主程序里的循環里是個死循環,什么也沒有做,在實際應用中這里是放的主程序。 在定時器服務函數里,需要重新置入定時器的值,這樣才能保證每次溢出時,都是你指定的時間。這里置入的是0x0006,還需要走 0x10000-0x0006個機器周期才溢出。換成10進制也就是每65530個機器周期中斷一次。我們仿真的晶振是22118400HZ,每12個時鐘一個機器周期。65530×12/22118400=0.036秒。也就是差不多28HZ的閃爍頻率。 因為51的定時器最大只有0xffff,溢出的速度很快,無法做出更久的閃爍頻率來,這一課就先觀察一下這個28HZ左右頻率。在下一課我們會用靜態變量的辦法,做一個長達1秒鐘的LED閃爍頻率。 另外,由于51從中斷發生到進入中斷的時間不定,是3至8個機器周期,我們在進入了中斷后才重新置新的定時器初始值,這樣就會存在定時誤差。也就是不是精確定時,如果要精確定時,需要使用定時器自動裝載方式,也就是在定時器溢出的同時,硬件邏輯就自動把定時器初始值裝載進去了,而不是在中斷服務程序里賦初始值,這樣就可以實現精確定時,誤差只出現晶振的頻率上。現在請仔細研究一下程序,并編譯,進入仿真,全速運行,觀察運行結果。我們可以看到P10上的LED在快速閃爍。
|