PIC16F1937之定時(shí)器
這篇文章是談?wù)勱P(guān)于1937的定時(shí)器的,剛開始被晶振頻率、時(shí)鐘頻率、振蕩周期、振蕩頻率、指令周期、指令頻率等等的名詞繞暈了。先來解決這個(gè)問題。
晶振頻率是代表振蕩器的頻率,說的是晶振這個(gè)器件的頻率,因?yàn)橐粋€(gè)單片機(jī)有內(nèi)部外部晶振,比如你選擇了內(nèi)部晶振,那么這個(gè)晶振的頻率就是你單片機(jī)的時(shí)鐘頻率,
振蕩頻率和晶振頻率說的是一回事。振蕩周期是1/(晶振頻率),T = 1/f 嘛。指令周期這個(gè)根據(jù)單片機(jī)的不同會(huì)不同,8位的PIC單片機(jī)(PIC10/12/16/18系列)是4個(gè)時(shí)鐘周期為一個(gè)指令周期。16位的PIC24單片機(jī)和dsPIC數(shù)字處理芯片和32位PIC32處理器是2個(gè)時(shí)鐘周期為一個(gè)指令周期。(以上關(guān)于指令周期的內(nèi)容是百度到的,內(nèi)容較可靠http://zhidao.baidu.com/link?url=uEnsn0C-bb-xdDNG_qEI0HmhIpoDNVc4d2lheztGKsQMpflMcpbnlWAGdXyeMz05fJAhXardxSrQDLHEdDrCx_)
理清上面的內(nèi)容,就開始我們的正題。
TIMER0
/*
* TIMER0是一個(gè)8位定時(shí)器/計(jì)數(shù)器,有8位預(yù)分頻器(1:2-1:256),是所有定時(shí)器中預(yù)分頻最大的(可以這么說吧。。)
* 可編程的內(nèi)外部時(shí)鐘源,可編程的外部時(shí)鐘邊沿選擇。
* 溢出時(shí)中斷
* TMR0可用于門控Timer1(還沒試過。。)
* 休眠模式中無法工作
* 此時(shí)選擇內(nèi)部晶振8mhz,預(yù)分頻1:16,每2.04s燈狀態(tài)改變一次。TMR0從0計(jì)數(shù)到255
* 所以是255*16*1000/(Fosc(晶振頻率)/4) = 2.04
*/
這些是我寫在單片機(jī)程序開頭的內(nèi)容,大致描述了TIMER0模塊,接下來告訴你們怎么使用它吧。(只寫關(guān)于初始化TIMER0的,CPU的初始化之類的略過)
1.先初始化時(shí)鐘源,在OSSCON寄存器中設(shè)置SCS位來選擇內(nèi)部振蕩模塊,IRCF設(shè)置內(nèi)部頻率
2.現(xiàn)在初始化TIMER0,還是一樣,先選擇時(shí)鐘源,TMR0CS = 0;表示內(nèi)部指令周期(注意是指令周期,fosc/4)
3.選擇預(yù)分頻,PSA位來選擇需不需要預(yù)分頻,PS<2:0>來設(shè)置預(yù)分頻
4.時(shí)鐘都和中斷有關(guān),所以這里要允許有效中斷,GIE = 1;
5.然后允許TIM0IE中斷,TIM0IE = 1;
6.最后是溢出中斷標(biāo)志位,TMR0IF = 0;表示未溢出,當(dāng)定時(shí)器開啟的時(shí)候TMRO就會(huì)開始計(jì)數(shù),一個(gè)指令周期加一,從0-255.當(dāng)加到255后再加一使TMR0IF = 1;計(jì)數(shù)會(huì)跳到0繼續(xù)。
7.TMR0可以不用使能,自動(dòng)計(jì)數(shù),但因?yàn)閳?zhí)行時(shí)會(huì)延遲2個(gè)指令周期,所以TMR0的初值需要設(shè)置,來抵消這個(gè)延遲。
還是上代碼吧
1 void InitTime();
2 void Init_fosc(); //設(shè)置內(nèi)部振蕩器,不過好像沒用。。
3
4 unsigned int count = 0;
5
6 int main(int argc, char** argv)
7 {
8 InitCPU();
9 Init_fosc();
10 InitTime();
11 TRISC = 0x00;
12 // LATC = 0x00;
13 while(1);
14 return (EXIT_SUCCESS);
15 }
16
17 void InitTime()
18 {
19 // INTEDG = 0; //bit6 中斷邊沿選擇位,1 = 上升沿,0 = 下降沿
20 TMR0CS = 0; //bit5 Timer0時(shí)鐘源選擇位,0 = 內(nèi)部指令周期時(shí)鐘(Fosc/4)
21 TMR0SE = 0; //bit4 Timer0時(shí)鐘源邊沿選擇位,1 = 在T0CKI引腳電平下降沿時(shí)遞增,0 = 上升沿時(shí)遞增
22 PSA = 0; //bit3 預(yù)分頻器分配位,1 = 不分給Timer0,0 = 預(yù)分頻器分給Timer0
23 PS0 = 1;
24 PS1 = 1;
25 PS2 = 0; //1:16
26 //PS<2:0>,預(yù)分頻器分頻比選擇位
27 GIE = 1; //允許所有有效中斷
28 PEIE = 0; //禁止所有外設(shè)中斷,有待考慮
29 TMR0IE = 1; //允許TMR0中斷
30 TMR0IF = 0; //溢出中斷標(biāo)志位,未溢出
31 TMR0 = 1;
32 }
33
34 void Init_fosc()
35 {
36 // OSCCON = 0x6a; 下面的設(shè)置為設(shè)置內(nèi)部振蕩器頻率的
37 SCS0 = 1;
38 SCS1 = 0; //1x內(nèi)部振蕩器模塊
39 IRCF0 = 0;
40 IRCF1 = 1;
41 IRCF2 = 1;
42 IRCF3 = 0; //1101 = 250kHz
43
44 }
45
46 void interrupt ISR()
47 {
48 TMR0 = 1;
49 count++;
50 if(count ==10)
51 {
52 LATC = ~LATC;
53 count = 0;
54 }
55 TMR0IF = 0;
56 }
TMR0是不用使能的,其他的時(shí)候也許不用管他,就算它在計(jì)數(shù),沒有允許它中斷(TMR0IE位),也是沒啥用的啊,殘念ね。。
接下來是TIMER1模塊,TIMER1模塊的特殊的地方是帶門控,是16位的定時(shí)計(jì)數(shù)器,有專用32kHz的振蕩器電路。
這次先上代碼吧,感覺會(huì)更有條理些
1 void InitTime();
2 void Init_fosc(); //設(shè)置內(nèi)部振蕩器,不過好像沒用。。
3
4 unsigned int count = 0;
5
6 int main(int argc, char** argv)
7 {
8 InitCPU();
9 Init_fosc();
10 InitTime();
11 TRISC = 0x00;
12 // LATC = 0x00;
13 while(1);
14 return (EXIT_SUCCESS);
15 }
16
17 void InitTime()
18 {
19 // INTEDG = 0; //bit6 中斷邊沿選擇位,1 = 上升沿,0 = 下降沿
20 TMR0CS = 0; //bit5 Timer0時(shí)鐘源選擇位,0 = 內(nèi)部指令周期時(shí)鐘(Fosc/4)
21 TMR0SE = 0; //bit4 Timer0時(shí)鐘源邊沿選擇位,1 = 在T0CKI引腳電平下降沿時(shí)遞增,0 = 上升沿時(shí)遞增
22 PSA = 0; //bit3 預(yù)分頻器分配位,1 = 不分給Timer0,0 = 預(yù)分頻器分給Timer0
23 PS0 = 1;
24 PS1 = 1;
25 PS2 = 0; //1:16
26 //PS<2:0>,預(yù)分頻器分頻比選擇位
27 GIE = 1; //允許所有有效中斷
28 PEIE = 0; //禁止所有外設(shè)中斷,有待考慮
29 TMR0IE = 1; //允許TMR0中斷
30 TMR0IF = 0; //溢出中斷標(biāo)志位,未溢出
31 TMR0 = 1;
32 }
33
34 void Init_fosc()
35 {
36 // OSCCON = 0x6a; 下面的設(shè)置為設(shè)置內(nèi)部振蕩器頻率的
37 SCS0 = 1;
38 SCS1 = 0; //1x內(nèi)部振蕩器模塊
39 IRCF0 = 0;
40 IRCF1 = 1;
41 IRCF2 = 1;
42 IRCF3 = 0; //1101 = 250kHz
43
44 }
45
46 void interrupt ISR()
47 {
48 TMR0 = 1;
49 count++;
50 if(count ==10)
51 {
52 LATC = ~LATC;
53 count = 0;
54 }
55 TMR0IF = 0;
56 }
再來寫下總結(jié)的步驟
1.初始時(shí)鐘源
2.在Timer1的初始中先選擇時(shí)鐘源
3.設(shè)置預(yù)分頻
4.允許中斷和溢出位清零
5.使能Timer1
雖然看起來簡單,但是Timer1的功能比Timer0多,好些自己也沒有用到過,有一個(gè)注意的地方,TMR1H和TMR1L的初值設(shè)定最好剛剛把延時(shí)拿回來就行了。使能也應(yīng)該放最后,剛好需要起振時(shí)間
最后最后,1937里面最多的定時(shí)器,數(shù)量超過我的想象,竟然多達(dá),,竟然擁有3個(gè)!!何等的數(shù)量啊。。。。。好了,不裝怪了,他是TIMER2/4/6葫蘆三兄弟。
/*1937共含有3個(gè)相同的Timer2模塊,分別為Timer2、Timer4、Timer6
* 8位定時(shí)器和周期寄存器(TMRx和PRx)
* 可讀寫、預(yù)分頻(1:1:4:16:64)后分頻(1:1至1:16)
* TMRx與相應(yīng)的PRx分別匹配時(shí)中斷、可選擇作為MSSP模塊的移位時(shí)鐘(僅Timer2)
*/
看到了嗎,這里是TMRx與相應(yīng)的PRx分別匹配時(shí)中斷,不像前面兩個(gè)あほ(TIMER0與TIMER1)是溢出中斷
還是先上代碼吧,,,這里直接上初始化Timer2/4/6的程序了,
1 void Init_Timer2() //時(shí)鐘是系統(tǒng)指令時(shí)鐘 Fosc/4,沿邊沿遞增計(jì)數(shù)。
2 {
3 T2CON = 0x0d; //設(shè)置后分頻,使能Timer2,設(shè)置預(yù)分頻
4 TMR2 = 0;
5 PR2 = 100; //TMR2與PR2的值越相近,進(jìn)入中斷越快
6 GIE = 1; //允許所有有效中斷,?。。?!實(shí)驗(yàn)無此語句能否運(yùn)作
7 PEIE = 1;
8 TMR2IE = 1; //允許Timer2中斷
9 TMR2IF = 0; //溢出標(biāo)志位,未溢出
10 }
TIMER2/4/6是無法自己選擇內(nèi)外部時(shí)鐘的,系統(tǒng)選什么他就得選什么,作為對比,TIMER0和TIMER1是可以自己選的,素晴らしい!
所有的時(shí)鐘設(shè)置總結(jié)來說,必須考慮的要素有以下幾條
1.設(shè)置時(shí)鐘,系統(tǒng)時(shí)鐘和定時(shí)器要用的時(shí)鐘(TIMER0和TIMER1是可以選的)
2.預(yù)分頻及后分頻,所有時(shí)鐘里TIMER0的預(yù)分頻最大(1:2-1:256),TIMER2/4/6既有預(yù)分頻又有后分頻
3.TMRxIE、PEIE、GIE(允許TIMERx中斷、允許外設(shè)中斷、允許所有有效中斷),這三個(gè)的賦值。除PEIE在TIMER0中沒有也可以運(yùn)作外,其他兩個(gè)在每個(gè)定時(shí)器中必須有
4.TMRxIF,溢出的標(biāo)志位,一般會(huì)在中斷程序里面清零,所以在初始化里面可有可無。
5.計(jì)數(shù)的設(shè)置TIMER0中的TMRO(0-255),TIMER1中的TMR1H和TMR1L,TIMER2/4/6中的TMRx(0-PRx),PRx(0-255)