Q2:#define OSC_FREQ 22118400L這句宏命令里的“L”是什么意思?
Q4:為什么好多變量都是char類型?它不是字符類型嗎?怎么可以用來計數?
Q4.1:51單片機中的char,int,long,float,double各占多少個字節,取值范圍多大?
Q6:void timer() interrupt 1 using 2是什么意思?
Q8:經常看到TH0與TL0,例如TH0 = 0xD8;TL0 = 0xEF;這起什么作用?
Q2:P3口的8個引腳有哪些復用功能(第二功能),默認開啟嗎?
Q3:我的程序編譯后生成的HEX文件超過了8k,燒進單片機不會有問題嗎?
問題解決2:Keil生成超過8K的HEX文件會報錯,提示Target not created
程序示例1:蜂鳴器滴兩次、進入中斷服務——數碼管顯示8,延遲1秒后熄滅。
Q5:為了理解某段程序的作用,我需要詳細了解一下計時/計數系統。
程序示例3:蜂鳴器持續滴滴,利用定時器完成Nms的延時,進入中斷服務——數碼管顯示8,延遲1秒后熄滅。
Q7:單片機的有好多特殊寄存器,我需要總結一下他們的名稱及用途
最近做比賽,需要寫程序做一個智能小車。C語言的基礎和編程的能力我是有的,但是我對單片機等硬件不是很了解,特意進行了一番學習。估計以后也用不了多少,特此寫一篇筆記,方便后人參考學習。
我不喜歡翻着教材或視頻一節一節地學習,我的學習方式是問題啟發式學習:直接切入正題,遇到不會的問題就找度娘,學會之后再次進入正題,遇到問題再查閱資料,循環往復,直到走通為止~整個學習下來,雖然可能會有些漏洞,但是已經基本進入狀態了。
由於我是業余的,所以難免會有錯解或不妥之處,還請讀者能以挑剔的眼光為我指出。
一、C語言相關
Q1:sbit與sfr代表是什么?有什么作用?
A1:sfr用來聲明特殊功能的寄存器,sbit用來聲明特殊功能位。
sfr占用一個內存單元(8位,取值范圍為0 ~ 255 = 2^8-1。對於I/O端口來說,剛好每一位對應一個引腳),例如 sfr P0 = 0x80; 這一句定義了P0端口與地址0x90對應。特殊功能的寄存器一般在開發工具(Keil)中自帶的頭文件,例如reg52.H中聲明好了,只需要在程序中引入該頭文件就好了;
w 用法:sfr 變量名 = 地址值;
w 需要注意的一點是,例如P0對應的是一個”8“字型的數碼管,若要顯示3,則可對P0口賦值:P0 = 0x0D,若要將其熄滅,只需對其賦值:P0=0xFF。這里的值並不代表地址,而是一個16進制的數(值的前兩位0x代表16進制,后兩位。剛好FF代表十進制的255)。
w sfr16也是用來聲明特殊功能寄存器,所不同的是它用於操作占兩個字節(取值范圍為0~65535)的寄存器,比如定時器T0和T1。
sbit只占用一個位,也就是說用它定義的變量只能取0和1兩個值。一般用來給引腳取別名,例如sbit P1_0 = P0^1; 就是定義用符號P1_0來表示P1.0引腳。 需要注意的是,一單用了sbit定義某個變量,這個變量的地址就是確定的了(不能修改了);
w 對於引腳來說,這個0和1是有物理意義的:0代表低電平,1代表高電平。而機器是不懂代碼只能識別高低電平。(腦補:這樣我們就打通了從代碼/軟件通往硬件的大路~) 高電平就是5伏正電壓,低電平就是0伏,這個是理想值,實際上它也有一個范圍......(參考自https://zhidao.baidu.com/question/266456228.html)
w 用法1:sbit 位變量名 = 地址值;
w 用法2:sbit 位變量名 = SFR名稱^地址值;
w 用法3:sbit 位變量名 = SFR地址值^變量位地址值;
w 3種用法參考自:https://blog.csdn.net/guzicheng/article/details/7242981
關於8051單片機特殊功能寄存器的說明,可以查閱: https://wenku.baidu.com/view/6f7b242c0975f46526d3e1aa.html?from=search 為防止鏈接失效,這里給出文件名:《8051,STC89C52單片機特殊功能寄存器》 |
Q2:#define OSC_FREQ 22118400L這句宏命令里的“L”是什么意思?
A2:長整型數字在數字的后面加字母L,如104L,034L等。總結如下:
w 十進制:直接用一般數字來表示,例如123,111,-999等;
w 十六進制:以0x開頭,如0xFF,0x01等;
w 長整型:在數字后面加字母L,如104L,034L等
w 浮點型:分為十進制形式和指數形式兩種,統一格式為 [±](數)(.數){e[±]數},其中[ ]為可選項,( )表示二者必有其一,{ }十進制不填,指數必填。例如:3.14,-.1,+2;.3e-3,12e+3,6.66e13
w 字符型:用單引號括住括住,例如'a','c'等。對於特殊字符,例如換行符、反斜杠等請參考C語言等教材。
w 字符串:略
Q3:我粘貼了別人的代碼,怎么發現沒有unit這個類型?
A3:別人的代碼只給了函數部分,沒有給頭文件中的預處理命令。可以在自己的頭文件中加入:#typedef unsigned int uint; (后面要加分號),這樣就可以用uint類型來代表unsigned int類型了。
Q4:為什么好多變量都是char類型?它不是字符類型嗎?怎么可以用來計數?
A4:int在8位的51單片機是占用2個字節,char在占用1個字節,所以說char類型占用空間更小。單片機的存儲器很小,盡量不要浪費空間,能用小的就用小的,且一般都用無符號的。
參考:https://zhidao.baidu.com/question/342140311.html
至於它為什么可以計數,因為字符本來就是用二進制表示的,所以當你對char類型的變量賦值時(例如 char a = 'A'),它(a)底層仍然是二進制,將二進制轉化為十進制,當然可以用來計數。
Q4.1:51單片機中的char,int,long,float,double各占多少個字節,取值范圍多大?
表:Keil uVision4面向51單片機的基本數據類型各種屬性一覽表
參考:http://blog.sina.com.cn/s/blog_6ac7328f0102uzd2.html
Q5:unsigned char data是什么數據類型?
A5:定義一個變量的格式為:[存儲種類] 數據類型 [存儲器類型] 變量名表
在定義格式中除了觸及類型和變量名表是必要的,其他都是可選項。存儲種類有四種:
w auto(自動)、extern(外部)、static(靜態)和register(寄存器),默認類型為自動。
存儲器類型的說明是指定該變量在C51硬件系統中所使用的存儲區域,並在編譯是准確定位。如果省略存儲器類型,系統則會按編譯模式SMALL,COMPACT或LARGE所規定的默認存儲器類型去指定變量的存儲區域。89C51中的存儲器類型有:
w data :可直接尋址的內部數據存儲區(128B),訪問速度最快;
w idata:間接尋址的內部數據存儲區(256B),允許訪問全部內存地址;
w bdata:可位尋址內部數據存儲區(16B),允許位與字節混合訪問;
w pdata:分頁的外部數據存儲區(256字節),用MOVX @Ri指令訪問;
w xdata:外部數據存儲區(64KB),用MOVX @A+DPTR指令訪問;
w code :程序存儲區(64KB),用MOVC @A+DPTR指令訪問;
——《51單片機C語言入門教程》,磁動力工作室,第六課 變量
一般需要嚴格控制變量讀取速度的時候用data。例如變量更新速度很快,或者需要很短時間內讀取或者修改的變量。一般容量要求大的,但速度並沒有太大要求的,放在xdata里面。
如果所有變量都不加這些關鍵字的話,編譯器會自動分配,但編譯器的分配方案並不一定是最好的。而且一般都不會非常合理。
參考:https://bbs.csdn.net/topics/360163178
Q6:void timer() interrupt 1 using 2是什么意思?
注:關於“中斷”的詳細學習放在 第二節:51單片機相關
A5:關鍵字interrupt表示這是一個中斷函數,具體的書寫格式為:
void 函數名() interrupt n [using m]
{ do something; }
首先需要注意的是中斷函數沒有參數傳遞其無返回值。n表示中斷源,m為單片機工作寄存器編號。[using m]為非必須內容。在設計中斷時,盡量讓中斷函數做少量的工作,這樣中斷服務時間短,系統可以及時的響應其他中斷。有些系統如果丟失中斷或對中斷反應太慢將產生十分嚴重的后果,這時有充足的時間等待中斷是十分重要的。
89C51單片機的中斷系統有5個中斷源,2個優先級,可以實現二級中斷嵌套。(中斷服務進行中再進行一次優先級更高的中斷)
所以n的取值為0,1,2,3,4共5個,對應了5種中斷源,這5種中斷源可以分為三種類型:外部中斷,定時器中斷,串口中斷。
w 0:外部中斷0(INT0)
w 1:定時器0(T0)
w 2:外部中斷1(INT1)
w 3:定時器1中斷(T1)
w 4:串行口中斷(RX/TX)
m的取值有0,1,2,3共4個,它涉及到中斷的優先權,如果用不到二級中斷,using m可以不加,系統會為你自動分配。如果加可能會導致不必要的沖突。
Q7:如何寫一個1ms延遲的函數?
A6:在寫函數之前首先要認識到,假如采用for循環,則循環一次所花費的時間是多少?這就涉及到單片機深層的概念:機器周期。而單片機的機器周期並不是最小的周期,在計算它之前還要了解一下其他幾個周期的定義:
w 晶振頻率OSC:單片機的最小系統中有一個晶振,它能夠使得CPU跑起來,這個晶振為單片機的CPU提供主頻。這個晶振的頻率就稱為晶振頻率(外加頻率)。
w 時鍾周期Tc:又稱為“震盪周期”,它等於晶振頻率的導數。這是最基礎的周期。
w 機器周期Tm:1機器周期 = 12個震盪周期;單片機復位至少需要兩個機器周期的高電平。
w 指令周期Ti:執行一條指令所需的機器周期數。1指令周期 = 1、2、4個機器周期;
² 一條賦值語句(j = 0)2個機器周期,j是unsign char類型;
² 一條判斷語句(j < 1)4個機器周期,j是unsign char類型;
² 一條自增/減語句(j++)1個機器周期,j是unsign char類型;
² 一條空語句(循環體內)1個機器周期。
² https://zhidao.baidu.com/question/89303563.html
晶振頻率OSC |
11.0592 MHz |
12.0000 MHz |
時鍾周期 |
9.04225e-5 ms |
8.33333e-5 ms |
機器周期 |
1.08507e-3 ms |
1.00000e-3 ms |
假如采用for循環,例如for(j=X;j>0;j--){ }; 這行代碼有X個循環,每次循環有一條判斷語句(j>0,4Tm),一條空語句({ },1Tm),一條自減語句(j--,1Tm),略去第一個循環的賦值語句(j=X,2Tm),共6X個機器周期。略去最后一次的判斷語句(j=0時,4Tm),若要延遲一秒,只需令6X*Tm = 1,當采用12MHz的晶振時,X ≈ 167。被略去的語句達6Tm,剛好等於一次循環所耗費的時間,所以對X進行X=X-1的修正,最終可得:X ≈ 166。
這里講的是j為char類型的變量,它最大只能取到255,所以要獲得更大的延時,需要用到int類型。前面也學到int類型是16位的,而單片機是8位的,所以這會更加復雜。
參考:https://zhidao.baidu.com/question/89303563.html
下面給的兩個延時函數。這里多說一句:我查閱網絡資料發現延時1ms的程序不盡相同,甚至相差很大,如果你需要非常准確的延時,推薦你參考正規的教材或采用其他方法比如計時系統。
兩種晶振的單片機,延時1ms的函數 |
|
11.0592MHz晶振 |
12MHz晶振 |
void delay_ms(unsigned int i){ |
void delay_ms(unsigned int i){ |
參考:https://blog.csdn.net/feike24/article/details/52357772
這種方法也有很大的缺點:延遲過程中,CPU被占用,無法進行其他任務,導致系統效率降低。延遲時間越長,該缺點便越明顯,因此軟件延時只適用於短暫延時,或簡單項目。
參考:https://www.bilibili.com/video/av15466938/?p=20
Q8:經常看到TH0與TL0,例如TH0 = 0xD8;TL0 = 0xEF;這起什么作用?
A6:從上面的學習可知TH0與TL0是與定時器/計數器有關的SFR寄存器。這兩句的含義是給定時/計數器賦初值,寄存器會按固定的時間間隔累加,當寄存器的值達到最大時會觸發中斷,這時可以利用中斷函數進行一系列操作。而計時T就等於時間間隔*(最大值 – 初值)。大概就是這個意思。
二、51單片機相關
Q1:單片機的引腳電壓是多少?它的電壓是由誰控制的?
A1:單片機的引腳有兩種電平:高電平與低電平。高電平的電壓與單片機的工作電壓有關,一般有5V和3.3V兩種。低電平一般為0V。
P0,P1,P2,P3又稱為並行I/O端口,它的輸出/入是雙向的:當其作為輸出時,單片機可以通過程序指令控制其為高電平1還是低電平0;當其作為輸入時,單片機可以檢測其是高或低電平,例如擴展了紅外尋跡模塊,對應的引腳的高低電平由紅外模塊控制,低電平0代表紅外光被反射並被接收管接收,高電平1代表紅外光被外界(黑線等)吸收。
P1稱為端口,P1.1稱為P1端口的引腳,這兩個概念之間的關系就是“整體”與“個體”的關系。
參考:https://wenku.baidu.com/view/39a0bda0700abb68a982fbfa.html
Q2:P3口的8個引腳有哪些復用功能(第二功能),默認開啟嗎?
A2: 當復用功能沒有開啟時,P3可以做為普通I/O口使用。一般情況下,復位后第二功能都是關閉的,需要設置對應寄存器才能打開。
² P3.0 RXD 串行輸入口
² P3.1 TXD 串行輸出口
² P3.2 INT0 外部中斷0輸入口
² P3.3 INT1 外部中斷1輸入口
² P3.4 T0 定時器/計數器0外部時間脈沖輸入端
² P3.5 T1 定時器/計數器1外部時間脈沖輸入端
² P3.6 WR 外部數據存儲器寫脈沖
² P3.7 RD 外部數據存儲器讀脈沖
問題解決1:程序通過USB口無法燒入單片機
我的單片機插座要用一個USB轉TTL設備才能從電腦上給單片機燒程序,當時我就在P3.0和P3.1上連了其他模塊,結果每次下載都失敗。后來我才明白TTL插口是和單片機上的RXD,TXD連着的,下載時是開啟了它們的復用功能的。
Q3:我的程序編譯后生成的HEX文件超過了8k,燒進單片機不會有問題嗎?
A3:HEX文件不只包含了實際的操作指令,還包含了地址代碼,這個文件是為了易於下載器的理解。真正下載到單片機上的並不是HEX文件,參考下面的鏈接,給單片機燒入160+K的HEX文件仍然沒有問題。
參考:http://tieba.baidu.com/p/3408654325?red_tag=b2302518919
問題解決2:Keil生成超過8K的HEX文件會報錯,提示Target not created
這是因為你所使用的Keil沒有經過注冊,需要注冊一下就可以生成超過8K的文件了。至於如何注冊這里就不多說了。
注意:如果真的是代碼量超過所選單片機的容量(STC89C52RC的容量為8K),那么編譯器在生產HEX文件時會提示 xxx code limit 之類的。
參考:https://bbs.csdn.net/topics/390011912
Q4:為了寫中斷程序,我需要詳細了解一下中斷系統。
A3:CPU在處理某一事件A時,事件B請求CPU迅速去處理,CPU暫時中斷當前工作A,轉去處理事件B,待CPU將事件B處理完后再返回繼續處理A事件,這一過程稱為中斷。在這之中有幾個專業名詞需要解釋一下:
w 中斷發生:事件B請求CPU迅速去處理;
w 中斷響應:CPU暫時中斷當前工作A;
w 中斷服務:CPU轉去處理事件B;
w 中斷返回:CPU再返回繼續處理A事件;
w 斷點:程序A被中斷的地方;
w 中斷源:引起CPU中斷的根源。斷源能夠向CPU提出中斷請求;
中斷系統的結構如下圖所示。
w 第一列解釋:INT0:外部中斷0,INT1:外部中斷1,T0定時器中斷0,T1定時器中斷0,RX、TX:串口中斷(包裝在一起的)。中斷引起原因如下:
² INT0:P3.2引腳低電平或下降沿信號;
² T0:定時/計數器0計數回0溢出;
² INT1:P3.3引腳低電平或下降沿信號;
² T1:定時/計數器1計數0溢出;
² 串行通信完成一幀數據發送或接受引起中斷。
w 第二列TCON解釋:該列的第二列代表了五種中斷源的中斷標志,所謂中斷標志就是“中斷請求的標志”,CPU要進行中斷服務,首先要判斷中斷請求標志,再判斷中斷使能標志是否Enable,最后才會響應這個中斷。——https://zhidao.baidu.com/question/81735469.html
² 對於外部中斷,當中斷到來時(引腳的電平發生變化),硬件會自動將中斷標志置為1;而對於計時器中斷,中斷標志的值是可以認為修改,所以可以利用這一點進行人為中斷(通過軟件/程序),可以達到計數、時鍾累加、自檢、掃描等目的。
ü 外部中斷需要外部條件觸發,計時器中斷不用。
² 需要注意的是,無論是機器中斷還是人為中斷,在中斷服務完成后機器並不一定會清除該中斷標志位(不同的MCU情況不同),所以為安全起見,我們一般利用程序清除。
² 外部中斷的中斷標志前(第一列)各有兩個開關,對應了外部中斷的兩種觸發方式:當IT0/IT1=0時,選擇為低電平0觸發;當IT0/IT1=1時,選擇為下降沿觸發(從高電平1過渡到低電平0的過程)。這兩種觸發方式有不同的效果,低電平可以持續一段時間,而電平下降卻是一瞬間的事,所以兩種觸發方法在延時效果上不同。
w 第三列IE解釋:IE代表中斷允許/使能寄存器,它控制了所有中斷的開放和屏蔽。共有兩列開關,EA是總開關(EA=1時,第二列的5個開關全部閉合),第一列的5個開關:EX0、ET0、EX1、ET1、ES分別對應了第一列的5個中斷源。
² 如需開啟INT0中斷,需要將EX0與EA都合上,即EX0=1;EA=1;
w 第四列IP解釋:中斷優先級控制寄存器。其中IP.7、IP.6與IP.5為保留位,其他位(PS=IP.4, PT1=IP.3, PX1=IP.2, PT0=IP.1, PX0=IP.0)值為1時表示對應中斷源具有高優先級,值為0表示其具有低優先級。
² 若這5個中斷源被設置為同等優先級,則按自然優先級排序依次執行中斷服務。如下表所示:
89C51單片機的中斷優先級有三條原則:
1、CPU同時受到幾個中斷時,首先響應優先級別最高的中斷請求。
2、正在進行的中斷服務不能被新的同級或低級的中斷請求所打斷。
3、正在進行的低級中斷服務能被高級的中斷請求所打斷。
CPU響應中斷的條件:1、中斷源有中斷請求;2、此中斷的中斷允許/使能標志為1;CPU開總中斷(EA=1)。
參考:https://www.bilibili.com/video/av5833218
程序示例1:蜂鳴器滴兩次、進入中斷服務——數碼管顯示8,延遲1秒后熄滅。
#include <AT89X51.h> //預處理命令 #define Led P0 // 定義數碼管顯示端口 #define Buzz P2_3 // 定義蜂鳴器的端口 // 11.0592M的晶振延遲1ms。這個函數要放在Buzz_didi的上面,否則會報錯。 void delay_ms(unsigned int i){ unsigned int j; for(;i>0;i--) for(j=114;j>0;j--); } // 蜂鳴器發出滴滴聲 void Buzz_didi(){ Buzz=0; delay_ms(100); Buzz=1; delay_ms(300); } void main(){ EA = 1; // 打開總中斷開關 ET0 = 1; // 開定時器中斷0 while(1){ Buzz_didi(); // 蜂鳴器滴一次 Buzz_didi(); // 蜂鳴器滴兩次 TF0 = 1; // 定時器中斷0的中斷標志置1 } } // 中斷函數一般放在main函數的下面 void LED_Show8() interrupt 1{ Led = 0x01; // 數碼管顯示為8 delay_ms(1000); // 延遲1秒鍾 Led = 0xFF; // 數碼管不顯示 // TF0 = 0; // 定時器中斷0的中斷標志置0 } |
Q5:為了理解某段程序的作用,我需要詳細了解一下計時/計數系統。
A5:單片機中有多個小鬧鍾(T0,T1;52單片機還有一個T2小鬧鍾),可以用來計數、定時等。它們的結構圖如下
w 定時/計數器0,T0,它的觸發引腳為P3.4,計數器為8位寄存器TL0和TH0,用於存放數值,TL0是低八位,TH0是高八位。當低八位計數滿了之后會向高八位進一位。對於T1同理。
w 配置寄存器TCON:控制寄存器,控制T0、T1的啟動和停止及設置溢出標志,與之相關的sbit由TF1、TR1、TF0、TR0;T2CON是T2時鍾的控制寄存器,52單片機才有。
² TF0、TF1是溢出中斷請求標志為,詳細參考“本節 Q4:為了寫中斷程序,我需要詳細了解一下中斷系統。”
² TR0、TR1是運行控制位,TRX=1時,TX開始工作;TRX=0時,TX停止工作。TRX由軟件置1或清0,所以可以用軟件控制定時/計數器的啟動與停止。
w 配置寄存器TMOD:定時/計數器的工作方式寄存器,用來確定工作方式(M0和M1)和功能(GATE和C/T)
² C/T:定時器或計數器功能的選擇位。C/T=1時為計數器,通過外部引腳P3.4或P3.5輸入計數脈沖,這樣可以設置外部時鍾源,不過比較復雜一般不用;C/T=0時為定時器,由內部系統時鍾提供計時工作脈沖。,加1計數器的計時間隔為1個機器周期(計數頻率為晶振頻率的1/12)。所以定時時間T = 計數值N * 機器周期Tm。
² GATE:門控位。當GATE=0時,只要用軟件使TCON中的TR0或TR1為1,就可以啟動定時/計數器工作;當GATE=1時,要用軟件是TR0或TR1為1,同時外部中斷引腳為高電平時,才能啟動定時/計數器工作。我們一般讓GATE=0。
T0、T1定時/計數器可以在四種方式下工作,由M0和M1的取值來確定。
方式1,以T0為例:定時/計數器0的實質是的由計數脈沖觸發的按遞增規律(即累加方式)工作的循環累加計數器,這個寄存器是16位的,由高八位的TH0與第八位的TL0組成。從預先設定的初始值開始,每來一個計數脈沖(時間間隔固定)就加計數器1,當TL0溢出后,對TH0進位,當TH0溢出后,TF0會被硬件置1,從而發出中斷請求。
² 溢出:當計數器的每一位都是1時,對計數器再加1就會溢出,結果就是計數器的每一位都回0。
² 計數值N = 溢出時計數器的值(65536=2^16) - 計數初值X
² 當TF0=1時,cpu可以不做響應。學習了后面的中斷系統后就會知道,cpu對中斷做出響應需要兩個判斷條件,另外一個就是開啟中斷使能標志:EA=1;ET0=1;
參考:https://www.bilibili.com/video/av15466938/?p=20
Q6:如何用定時/計數器進行1ms的延遲?
A5:定時器的操作步驟(下面的X代表0或1):
w 選擇工作方式(設置M0,M1),這里選用方式1,即M0=1;M1=0;;
w 選擇控制方式(設置GATE),一般設GATE=0;;
w 選擇定時器還是計數器模式(設置C/T),一般采用定時器模式即C/T=0;;
w 給定時/計數器賦初值(設置THX和TLX),注意結合一下兩個公式:
² 計數值N = 溢出時計數器的值(65536=2^16) - 計數初值X
² 定時時間T = 計數值N * 機器周期Tm = N*12 / 晶振頻率
n 如果頻率的單位是MHz(兆赫茲),則時間的單位為us(微秒)
² 計算出初值X后將其轉化為16進制,THX就等於前2個數,TLX就等於后2個數。或者將其轉化為10進制,它除以256的商為THX,余數為TLX。
w 開啟定時器中斷(ETX=1;);
w 開總中斷(EA=1;);
w 打開計數器(TRX=1)。
程序示例2:定時器的配置
void TimerConfiguration(){ TMOD = 0x01; // 定時器0選擇工作方式1 TH0 = 0x3C; TL0 = 0xB0; // 設置初始值 EA = 1; // 打開總中斷 ET0 = 1; // 打開定時器0中斷 TR0 = 1; // 啟動定時器0 } |
程序示例3:蜂鳴器持續滴滴,利用定時器完成Nms的延時,進入中斷服務——數碼管顯示8,延遲1秒后熄滅。
#include <AT89X51.h> //預處理命令 #define Led P0 // 定義數碼管顯示端口 #define Buzz P2_3 // 定義蜂鳴器的端口 // 11.0592M的晶振延遲1ms。 void delay_ms(unsigned int i){ unsigned int j; for(;i>0;i--) for(j=114;j>0;j--); } // 初始化定時器0 void TimerConfiguration(){ // 延遲1ms的計數N的精確值為921.6,這里舍入為921 // 以計數的方式,一次性最多可以延遲70ms TMOD = 0x01; // 定時器0選擇工作方式1 TH0 = (65536-921)/256; // 設置初始值 TL0 = (65536-921)%256; // 設置初始值 EA = 1; // 打開總中斷 ET0 = 1; // 打開定時器0中斷 TR0 = 1; // 啟動定時器0 } unsigned int count = 1; //用於計數 void main(){ TimerConfiguration(); while(1){ Buzz=0; delay_ms(100); Buzz=1; delay_ms(200); } } // 中斷函數一般放在main函數的下面 void inter_t0() interrupt 1{ count++; //中斷一次(1ms)計數加一 TH0 = (65536-921)/256; // 設置初始值 TL0 = (65536-921)%256; // 設置初始值 //TF0 = 0; // 定時器0的中斷標志置0 if(count>1000){ // 延遲1000ms,這里可以修改為Nms count = 1; // 重置計數 Led = 0x01; // 數碼管顯示為8 delay_ms(1000); // 延遲200ms Led = 0xFF; // 數碼管不顯示 } } |
從程序示例1與程序示例3的對比可以得出以下結論:
l 利用定時器中斷有兩種方法,一種方法只需將定時器0的中斷標志置1(TF0 = 1;即程序示例1),另一種方法需要開啟定時器0(TR0 = 1;即程序示例3)
l 利用定時器進行延時,這個計時進程與main進程是並行的。
l 無論采用定時器延時中斷還是手動中斷,中斷服務與mian進程總是串行的。
l 改程序的測試結果表明:有時候,當數碼管顯示8時,蜂鳴器是靜音的;而有時候,當數碼管顯示8時,蜂鳴器在一直鳴響。這說明兩點:
² 定時器中斷可以中斷函數類型的延遲。
² 單片機在服務中斷的時候,既定的事實不會發生變化。也就是說若中斷發生在蜂鳴器響的中間時刻,則蜂鳴器會一直響下去,直到中斷服務返回。
Q7:單片機的有好多特殊寄存器,我需要總結一下他們的名稱及用途
A6:關於8051單片機特殊功能寄存器的說明,可以查閱:
https://wenku.baidu.com/view/6f7b242c0975f46526d3e1aa.html?from=search
為防止鏈接失效,這里給出文件名:《8051,STC89C52單片機特殊功能寄存器》
下面給出第一頁的預覽圖: