使用AT89S52構建延時函數與輸出PWM波


任務要求:

  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波了。

  

  至此,以上實驗圓滿完成。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM