任務要求:
51單片機精准延時以及中斷的設計-無RTOS模式。設單片機的時鍾12MHz,型號為AT89S52。
1.構造一個不依賴定時器(采用nop+nop()的研視函數;非中斷模式);
2.用單片機的引腳輸出2KHz占空比為20%的方波;
3.用中斷模式實現單片機的引腳輸出2KHz占空比為20%的方波;
注:在TIMER0、1或2中斷中修改單片機的引腳電平,並修改下一輪溢出的時間(如設置TLx和THx的值);
這個操作耗時較少,而且是TIMER的僅有任務(修改引腳電平、修改THx和TLx)無必要另行封裝為函數;
4.將波形在虛擬邏輯分析儀顯示出來;
任務分析:
首先,使用nop模式的任務要求最終輸出一個占空比位20%,頻率位2KHz的方波,通過公式T=1/f可知,每個PWM周期為500微秒。因為占空比為20%,所以輸出高電平100微秒,低電平400微秒。那么我們需要構建兩個延時函數,一個延時100微秒,一個延時400微秒,並且在延時的時候改變引腳極性就可以輸出實驗要求的PWM波。
實驗要求使用nop();方式構造延時函數。nop();代表空指令,即運行一次整個指令,單片機就會空閑一個機器周期什么都不工作。實驗給出的單片機是AT89S52,時鍾頻率為12MHz。AT89S52是51架構單片機,受限於51的指令集,他的機器周期為時鍾周期的十二分之一,即1MHz。那么每運行一次nop指令就可以看作單片機延時1微秒。
可以構造一個循環函數,這個循環函數內執行nop指令,就可以延時較長時間而不用重復寫很多行nop指令。由於循環函數每次進入時需要對循環條件進行判斷,而這部分遠遠大於1微秒,可以記循環一次的時間為k,循環次數為x,需要延時的時間長度為y,那么可以將這個延時時間長度看做一元方程組:y=kx
假設k的大小不夠合適,比如要延時100微秒,k為3,那么可以進一步修改為:y=kx+b,b為多少,就在循環函數結束后運行多少次nop指令。
接着要求使用中斷輸出方波,並且每次修改定時器重裝載值來完成延時長度的變化。
對於中斷方式,首先我們需要一個變量用於記錄進入中斷的次數,通過次數不同,來修改中斷時間,因為中斷時間就兩個,一個是100微秒,一個是400微秒,那么這個變量可以任意定義,只需要每次在中斷結束后對這個變量自加,要修改重裝載值得時候,判斷一下這個變量得奇偶就行了。
修改重裝載值這個步驟本身會占據一定的時長,即中斷函數的運行會占用時間,因此下一輪溢出的時間要比100us短才對。
任務實現:
首先新建一個keil5工程。
打開keil,project->New uVision Project 新建本項目的keil工程文件。接着在新的工程中選擇單片機為AT89S52,配置仿真時鍾頻率為12MHz如圖所示。
接着新建”main.c”,並且在”main.c”中添加包含有初始化代碼的頭文件”init.h”、包含有51寄存器聲明的頭文件"C8051F020_defs.h"以及包含有nop指令的頭文件”intrins.h”。假設使用P10輸出PWM,所以在定義完頭文件后,對P10進行定義:sbit P1^0 = P1_0,方便書寫識別引腳電平轉換。
下一步構建main函數:
1 void main() 2 { 3 Init_Device(); 4 5 for (;;) 6 { 7 P1_0 = 0; 8 delay400us(17); 9 P1_0 = 1; 10 delay100us(3); 11 } 12 }
如上所示的main函數中只有第8行和第10行的延時函數是沒有定義的,其他都在前面步驟里完成了。
所以最后,也是最重要的,本實驗的核心內容:構建精准延時函數。
按照實驗分析中的思路,我使用函數入口參數控制循環的次數,並且在循環外加上固定的nop指令,試湊出指定時常的延時函數。
下面以delay400us(17);為例,傳遞給這個函數的參數“17”為試湊出來的結果,因此在后期調用時,不應該修改其數值,也就是“17”不可以修改。
在P1_0=0處和P1_0=1處設置斷點,通過仿真讀出各自運行過的機器周期,再將它們相減就是delay400us(17);延時的時間長度。
其中,監視窗口的states代表着機器周期。
通過上兩張圖可知程序第一次停止在P1_0 = 0處單片機運行過的機器周期為22255,程序第一次停止在P1_0 = 1處,單片機運行過的機器周期為22655,兩者相減為400,即delay400us(17)這個函數延時了400個機器周期,按照之前項目分析那樣,一個機器周期對應一微秒,所以這個延時函數算是成功了。
同理,繼續運行仿真下去。
通過上圖可以看到程序第二次運行到P1_0 = 0處單片機運行過的機器周期為22755。也就是delay100us(3)這個函數延時了100個機器周期也就是100us。
再通過示波器觀看P1_0引腳的波形,檢查是否按照需求產生了PWM脈沖,如下圖所示。
上圖左側P1_0代表波形為P1_0口產生的,通過觀察高低電平的比值可知確實產生了一個占空比為20%的方波,但是由於仿真系統的問題,所以邏輯分析儀中的時間和設計的時間對不上。按照機器周期來計算是正確的,所以可以判斷是仿真系統出現BUG導致時間顯示錯誤。
使用中斷方式關鍵在於中斷函數的書寫,主函數與非中斷模式大同小異:
1 int a = 0; 2 3 void Timer0_ISR() interrupt 1 4 { 5 6 if (a == 0) 7 { 8 TH0 = (65535 - 32) / 256; 9 TL0 = (65535 - 32) % 256; 10 P1_0 = 0; 11 a++; 12 } 13 14 else 15 { 16 TH0 = (65535 - 6) / 256; 17 TL0 = (65535 - 6) % 256; 18 P1_0 = 1; 19 a = 0; 20 } 21 }
以上為中斷函數的代碼。
首先定義一個用於計數的變量a,每次進入中斷函數時,首先判斷a是否為0,如果為0,就將下一次中斷設置為400微秒,並且將a自加,在這次中斷里,引腳應該輸出低電平;如果a不等於0,那么將下一次中斷設置為100微妙,然后將a置0,在這次中斷引腳應該輸出高電平。
驗證的方法和上一個實驗一樣,在P1_0 = 0處 和 P1_0 = 1處各設置一個斷點,並且觀察每次進入斷點的時間間隔。
通過計算可以得出,引腳輸出低電平保持了409個機器周期,即409微秒,而高電平保持了106個機器周期,106微秒。
通過邏輯分析儀也可以看到引腳正常輸出PWM波了。
至此,以上實驗圓滿完成。