本帖最后由 dabing89 于 2018-10-14 18:56 編輯
定時器的使用 ---20181013
好,繼續更新這個帖子,前邊我們點亮了流水燈,然而定時是用DELAY實現的,實際上在實際的項目中,DELAY基本上不會用,如果一個程序中,主循環有大量的DELAY出現,那么就不合理了,所以必須要用定時器 ,保證程序的實時性,這一貼,我們寫一個程序,用定時器0來實現LED間隔500MS閃爍的效果 ,代碼如下:
因為在實際使用中,定時器和中斷都是在一起配合使用,所以這兒我們就不分開,但是要說的是,定時器是硬件,是單片機內部存在的一個模塊,而中斷僅僅是一種處理問題的機制,上面這個d代碼看著不多,但是消息量很大,我們一點一點解剖,理解了定時器的的原理,等你上手STM32的時候,定時器原理可以直接不看,直接拿來用就好了。
先從STC89C52RC的開始說起,我們知道,STC89C52RC是標準51內核,在標準51的體系下,12個時鐘周期是一個機器周期,啥意思呢?比如你的外部晶振是11.0592MHZ,那么11059200的倒數,也就是周期了,這個倒數叫做時鐘周期,也叫震蕩周期,算一下時間,1/11059200 = 0.0904US,這就是STC89C52的時鐘周期,那么51單片機就規定,12個這樣的時鐘周期為一個機器周期,所以在乘以12,那么一個機器周期的數值是1.085US,注意這是在11.0592MHZ下,如果是在12MHZ下,那么一個機器周期就是1US,這就是定時器的時間基準。我們再來看下,如果我們用STC89C52來做一個500MS的定時器該怎么做呢?配置如下即可實現:
- /*******************************************************************
- * 文件名:void Bsp_Tim0_Init(void)
- * 描述: 定時器0初始化函數
- * 功 能
- * 作者:大核桃
- * 版本號:V1.00(2018.09.19)
- ********************************************************************/
- void Bsp_Tim0_Init(void) //1000微秒@11.0592MHz
- {
- TMOD &= 0xF0; //設置定時器模式
- TMOD |= 0X01;
- TH0 = 0xFC; //設置定時初值
- TL0 = 0x66; //設置定時初值
- TR0 = 1; //定時器0開始計時
- ET0 = 1; //使能定時器0的中斷
- EA = 1; //打開總中斷
- }
- /*******************************************************************
- * 文件名:TIM0_IRQ_Handler
- * 描 述:中斷服務函數
- * 功 能 中斷服務標號 INT0 ET0 INT1 ET1 UART1 ADC LVD TIME2
- * 優先級: 0 1 2 3 4 5 6 12
- * 版本號:V1.00(2018.09.19)
- ********************************************************************/
- void TIM0_IRQ_Handler(void) interrupt 1
- {
- static uint16 tmr500ms = 0;
- TH0 = 0xFC; //設置定時初值
- TL0 = 0x66; //設置定時初值
- tmr500ms++;
- if(tmr500ms >= 500)
- {
- tmr500ms = 0;
- flag500ms = !flag500ms; //500MS閃爍
- }
-
-
- }
復制代碼
還是來解釋下,首先定時器的配置步驟是這樣的:
1.先設置TMOD這個寄存器,選擇定時器0的模式寄存器,配置定時器0為16位不可重裝載模式
2.設置定時器的定時初值,高八位和低八位
3.打開定時器的運行標志位,因為TCON是一個可位尋址的寄存器,所以直接TR0 = 1;就好。
4.使能定時器0的中斷ET0
5.打開總中斷EA
OK,這樣就配置好了寄存器,定時器也可以工作了,然而我們了解定時器是怎么運行的了嗎?沒有!!!很多人不知道為啥是這個數值,而且定時器的的初值還有好幾種寫法,如果有人用了不一樣的而寫法,你一定要知道是等價的寫法。
關于初值的計算
我們知道定時器0是一個16位的定時器,最大計數65536(0-65535)分為高八位和低八位,TH0存儲的是高八位的數據,TL0存儲的是低八位的數據,0XFC是一個16進制數值,換算10進制是252,0X66是102,我們知道低八位最大計數到255,TH0就變成1,然后進位,清零,又開始從0計數,那么我們可以算算這個初值是多少?252*256 +102 = 64614,而64614的16進制表示形式就是0XFC66,這樣我們就搞清楚定時器的計時原理了,如下所示,初值代碼可以改寫成這樣:
- TH0 = (65535 - 921) / 256; //設置定時初值
- TL0 = (65535 - 921) % 256; //設置定時初值
復制代碼 也就是說,我們讓單片機從64614開始計數,到65535溢出,總共計數921個,而我們又知道1個機器周期是1.085US,那么921個機器周期是多少呢?921*1.085 = 1000US,正好是1MS的定時,我們在程序中讓其溢出500次,那么不就是500MS了嗎?就是這樣來的,原理一定要搞清楚。不管什么STM32,64,128都是這樣的原理{:lol:}。關于中斷使用的時候,打開使能就好了,EA是總中斷使能位,如果這個不打開,ET0單獨打開是沒用的,這才是一把手。
關于STC15W系列的定時器
好了,既然我們搞清楚了,STC89C52的定時器的原理了,我們來看下,STC15W的定時器配置,因為我們都是定時1MS,那么,為啥初值不一樣呢?我們來算下STC15W的這個初值對應的10進制數值是多少?是54477,好陌生的數字,怎么來的呢?65536-54477 = 11059,也就是說在STC15W的內核下,我們只要計數11059個,就可以達到1MS的定時,我們知道,STC15W是單周期的時鐘,也就是說我們不12分頻,我們直接就是一個時鐘周期就是一個機器周期,(1/11059200)*11059 = 1ms,明白了吧?所以,本節的程序代碼,也可以這樣寫,是一樣的作用的。
- /*******************************************************************
- * 文件名:void Bsp_Tim0_Init(void)
- * 描述: 定時器0初始化函數
- * 功 能
- * 作者:大核桃
- * 版本號:V1.00(2018.09.19)
- ********************************************************************/
- void Bsp_Tim0_Init(void) //1000微秒@11.0592MHz
- {
- AUXR |= 0x80; //定時器時鐘1T模式
- TMOD &= 0xF0; //設置定時器模式
- TMOD |= 0X01;
- // TH0 = 0xD4; //設置定時初值
- // TL0 = 0xCD; //設置定時初值
- TH0 = (65535 - 11059) / 256;//設置定時初值
- TL0 = (65535 - 11059) % 256;//設置定時初值
- TR0 = 1; //定時器0開始計時
- ET0 = 1; //使能定時器0的中斷
- EA = 1; //打開總中斷
- }
復制代碼
關于在單片機中大量存在的& |運算的詳細說明
其實這個,不太想說,但無奈上網看到好多初學者根本不知道& |的作用,還是詳細的說明下比較好,以定時器0的配置為例。
AUXR |= 0x80; AUXR是一個輔助寄存器,這不需要多說,|= 0x80有什么講究呢?|(或),是讓某一位置1的意思,讓那一位置一呢?很明顯,讓是1的那一位置一,0X80不就是最高位是1嗎?那就是讓最高位置一好了,有人說,這有啥用呢?本來不就是1嗎?人家還有后半句,而其他位保持不變,其他位?啥位?等于0的那些對吧?也就是低7位不變了。這樣操作有啥好處呢?我們知道ET0 = 1;TR0 = 1;之所以可以這樣操作,是因為他們可以被位尋址,可以進行單獨的位操作,而AUXR是不可以進行位尋址的,因此一次操作必須操作8個位,你想想看,AUXR這個寄存器的功能如下圖:
00009.jpg (104.42 KB, 下載次數: 168)
下載附件
2018-10-13 08:45 上傳
你能保證直接讓AUXR = 0X80,不對其他位造成影響嗎?現在大家的編程還比較簡單,只有一個定時器,要是用到3個,4個定時器呢?這樣不就互相干擾了嗎?扯淡么?所以 & |的重要性也就凸現出來了。
再來看后面這2句,很明顯TMOD是不可以位尋址的,按照我們剛才的分析,|是讓某一位置一,那么&,自然就是讓某一位清零了,來看下0XF0,二進制是1111_0000,也就是低4位置0,高4位不要管,因為&是乘法運算啊,只要都是1,那么就是1,很明顯讓低4位清零,下一句是0000_0001,讓最低為置一啊,對吧,要注意,這里的2句是連續操作的,不是單獨的的操作,什么意思?前者的運算結果,又給了后者,所以我們總體來看這2句代碼,先讓低四位清零,高四位不變,然后將這個結果進行或運算,讓最低位置一,而高7位都不變,因為任何數|還是任何數啊,對吧,這就達到了一個互不干擾的目的,這樣的代碼在STM32上好多好多的,都是起到一個互不干擾的作用。這樣的做法可以確保定時器0和定時器1是獨立的,如果我們不這樣做,你看看是工作在啥模式?除了定時器配置在16位不可重裝模式在,定時器1被配置在了16位自動重裝定時器,我們沒有使用定時器1,萬一出錯怎么辦?這就不好了。
00010.jpg (105.97 KB, 下載次數: 165)
下載附件
2018-10-13 08:45 上傳
關于自動重裝載和不可自動重裝模式
其實,沒啥太大的區別,如果是自動重裝,那么在中斷服務函數中,TH0 TL0就不需要去再去重新賦值了,直接刪掉就好,如果不是自動重裝,則必須要加。還差點忘了一個事,你怎么確定你的定時是500MS呢?答案不能靠眼睛看把,看看示波器觀察的結果,嗯,是對的。如下圖,1S 1HZ的方波信號
00011.jpg (1.34 MB, 下載次數: 152)
下載附件
2018-10-13 08:47 上傳
好了,今天就到這里吧,代碼上傳
004 定時器的使用.rar
(30.68 KB, 下載次數: 92)
2018-10-14 18:50 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|