自己電腦怎么做web網(wǎng)站嗎百度云服務器
藍橋杯單片機組備賽指南請查看這篇文章:戳此跳轉(zhuǎn)藍橋杯備賽指南文章
本文章針對藍橋杯-單片機組比賽開發(fā)板所寫,代碼可直接在比賽開發(fā)板上使用。
型號:國信天長4T開發(fā)板(綠板),芯片:IAP15F2K61S2
(使用國信天長藍板也可以完美兼容,與綠板幾乎無差別)
1.編程目的
????????通過定時/計數(shù)器實現(xiàn)數(shù)碼管的時鐘顯示,顯示格式例如“12-13-14”,表示12點13分14秒時刻。并通過設置外部中斷0,當S5按下時計時暫停,再次按下時繼續(xù)計時。
2.定時/計數(shù)器原理
2.1 單片機的基本中斷系統(tǒng)
????????比賽用單片機最多允許兩層中斷的嵌套。即在低級的中斷服務函數(shù)執(zhí)行過程中,收到了更高級的中斷信號,而去先執(zhí)行更高級的中斷工作,執(zhí)行完再返回繼續(xù)執(zhí)行低級的中斷工作,低級的中斷工作做完,再回到最原本的代碼繼續(xù)執(zhí)行。
? ? ? ? 本文中,我們使用到的外部中斷0與定時/計數(shù)器中斷0,本身自帶優(yōu)先級,且INT0的優(yōu)先級已經(jīng)高于T0,因此我們不需要對兩個中斷的優(yōu)先級進行單獨設置。
比賽用芯片IAP15F2K61S2 涉及的中斷共有14個,這里介紹常用的5個:
中斷號 | 中斷名 | 中斷名解釋 |
0 | INT0 | 外部中斷0 |
1 | T0 | 定時/計數(shù)器中斷0 |
2 | INT1 | 外部中斷1 |
3 | T1 | 定時/計數(shù)器1 |
4 | TI/RI | 串口中斷 |
5 | T2 | 定時/計數(shù)器中斷2 |
本文聚焦外部中斷0與定時/計數(shù)器中斷0,只對INT0和T0進行講解,他們的中斷號對應為0和1。比賽用開發(fā)板中還有INT2,INT3,INT4,T2共計5個外部中斷信號源,三個定時/計數(shù)器中斷信號源,其余中斷源將在專欄“藍橋杯-單片機組進階”中講解。
2.2 什么是定時/計數(shù)器中斷
????????定時/計數(shù)器可以想象成一個每隔一定時間,就會自動產(chǎn)生一次中斷的開關。但它產(chǎn)生中斷的時間間隔是固定的,由我們來設置。中斷產(chǎn)生的感覺可以想象成一個正在接水的水桶,當水桶里的水接滿溢出時產(chǎn)生中斷,由此每次產(chǎn)生中斷所需要的時間就可以比擬成水桶接滿水的時間。那我們?nèi)绻凑兆约旱男枨?#xff0c;讓水桶接滿水的時間被人為精確控制呢?設置初值——即在最開始讓水桶里裝有一定數(shù)量的水,最開始水桶里面的水越多,水桶就越快溢出,產(chǎn)生中斷所需要的時間就越短。上述為口語化的理解,更本質(zhì)的解釋如下:
????????
????????定時/計數(shù)器的核心是16位的加法計數(shù)器,在8位的51單片機中,使用兩個8位的寄存器THx和TLx共同組成16位,并分別叫做高8位和低8位。當這16位加法計數(shù)器計數(shù)溢出,則產(chǎn)生一次定時/計數(shù)器中斷。單片機的晶振我們假定是12Mhz,單片機會將12Mhz進行12分頻后傳入加法計數(shù)器中,即一個機器周期(12Mhz/12=1Mhz,Tcy=1us),此時,我們可以認為在晶振為12Mhz的單片機中,沒經(jīng)過1us的時長,加法計數(shù)器的值加1 。假設我們讓16位加法計數(shù)器從0開始計數(shù),即計數(shù)初值為0(0000 0000 0000 0000) ,當變成計數(shù)值變成(1111 1111 1111 1111)時在經(jīng)過1us,計數(shù)器就會溢出,并產(chǎn)生一次定時/計數(shù)器中斷,此時計數(shù)值為65535? 。此時,我們可以認為在初值為0,16位計數(shù)器,晶振為12Mhz的單片機中,產(chǎn)生一次中斷所需要的時間為65535us=65.535ms 。當我們需要單片機50ms就產(chǎn)生一次中斷時,就可以設置初值為15535 (65.535ms-50.000ms) 。
????????實際的單片機中,為了方便我們的使用,晶振頻率可能為12Mhz,11.05926Mhz;同時在定時/計數(shù)器不同的工作方式下,計數(shù)器的位數(shù)可能為16位(最大值65535),13位(最大值8191),8位(最大值255);因此我們需要根據(jù)不同的設置,進行不同的計算。相關的計算過程可查看TMOD寄存器設置的部分。
2.3 定時/計數(shù)器中斷涉及的寄存器
????????為了簡短篇幅,下面將寄存器IP、IE、TCON簡略說明,詳細內(nèi)容可以查看文章:戳此跳轉(zhuǎn)
我們著重介紹TMOD寄存器
兩級中斷允許控制:IE(interrupt enable)
EA | - | ET2 | ES | ET1 | EX1 | ET0 | EX0 |
IE中各位的功能如下:
EA—中斷總開關控制位,EA=1,所有的中斷請求開放。
ES—串行口中斷允許位。
ET1—定時器/計數(shù)器T1溢出中斷允許位。
EX1—外部中斷1中斷允許位。
ET0—定時器/計數(shù)器T0的溢出中斷允許位。
EX0—外部中斷0中斷允許位。
? ? ? ? 該寄存器允許位尋址,在編程時,如果只用到一個外部中斷0和T0,則我們只需要設置總開關EA、外部中斷0允許位EX0 、T0中斷允許為ET0。
兩級優(yōu)先級控制:IP(interrupt priority)
- | - | PT2 | PS | PT1 | PX1 | PT0 | PX0 |
中斷優(yōu)先級寄存器IP各位含義:
PS—串行口中斷優(yōu)先級控制位,1—高級;0—低級。
PT1—T1中斷優(yōu)先級控制位,1—高級;0—低級。
PX1—外部中斷1中斷優(yōu)先級控制位,1—高級;0—低級。
PT0—T0中斷優(yōu)先級控制位,1—高級;0—低級。
PX0—外部中斷0中斷優(yōu)先級控制位,1—高級;0—低級。
? ? ? ? 該寄存器允許位尋址,本題中采用默認優(yōu)先級,因此不用管這個寄存器。
特殊功能寄存器:TCON(timer controller)
TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
TCON寄存器中與中斷系統(tǒng)有關各標志位功能如下:
TF1—定時器/計數(shù)器T1的溢出中斷請求標志位。
TF0—定時器/計數(shù)器T0溢出中斷請求標志位似。
IE1—外部中斷請求1中斷請求標志位。
IE0—外部中斷請求0中斷請求標志位,與IE1類似。
IT1—選擇外中斷1請求方式。0--電平觸發(fā)方式,1--跳沿觸發(fā)方式。
IT0—選擇外中斷0請求方式。0--電平觸發(fā)方式,1--跳沿觸發(fā)方式。
?????????該寄存器允許位尋址,在編程時,如果只用到一個外部中斷0,則我們只需要設置IT0從而確定外部中斷0的觸發(fā)方式 。而對于TF0,它不需要我們設置,只是在程序中我們可以通過查詢TF0是否等于1,從而判斷T0是否溢出。
特殊方式寄存器:TMOD(timer mode)
GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 |
<----? 定時器1(T1)? ----> | <----? 定時器0(T0)? ----> |
TMOD寄存器中與中斷系統(tǒng)有關各標志位功能如下:
????????通過觀察可以發(fā)現(xiàn),TMOD寄存器左邊4位與右邊4位幾乎一樣,只是左邊針對定時/計數(shù)中斷1,右邊針對定時/計數(shù)中斷2。本文中我們使用T0,因此我們介紹T0各位的含義,T1與T0相同:
GATE: 用于控制定時器啟動是否受外部中斷源的影響?
? ? ? 當GATE=0時,只要用軟件使TR0或TR1等于1,就可以啟動T0或T1定時/計數(shù)器工作。
? ? ? 當GATE=1時,除了用軟件使TR0或TR1等于1外,還要使外部中斷引腳INT0/1為高電平,這樣定時/計數(shù)器才能啟動工作。
? ? ? 注意:GATE=0表示控制定時器啟動不受外部中斷源的影響?C/T=0為定時模式,C/T=1為計數(shù)模式。
?M1 M0工作方式選擇位,兩位一起看:
M1 M0 工作方式 工作說明 00 方式0 13位定時/計數(shù)器,最大值8192 01 方式1 16位定時/計數(shù)器,最大值65535 10 方式2 8位自動重裝定時/計數(shù)器,最大值255 11 方式3 T1分成兩個獨立的定時/計數(shù)器,T1停止計數(shù) 舉例:我們在12Mhz的單片機中,不設置外部中斷觸發(fā)定時,設置T0工作在方式1,實現(xiàn)50ms的定時中斷。則GATE=0,C/T=0,M1 M0=01 ,剩下的位全部賦值0,則TMOD應該賦值:TMOD=0000 0001=0x01? 。
????????該寄存器不可位尋址,編程時,我們必須對TMOD寄存器進行整體賦值,才能正確設置。充分考慮要使用T1或者T0,然后進行對應位的設置,并轉(zhuǎn)換成BCD碼寫入代碼。?
初值寄存器:THx TLx
????????如果是使用T1,則初值的高8位寄存器為TH1,低8位寄存器為TL1
????????如果是使用T0,則初值的高8位寄存器為TH0,低8位寄存器為TL0
????????如果T1和T0都使用,則TH1,TL1,TH0,TL0都需要設置初值
????????
????????藍橋杯比賽使用的開發(fā)板是12Mhz的,剛好對于加法器加1的時長為1us。假如我們設置TMOD寄存器使得定時/計數(shù)器工作在方式1,最大計數(shù)值為65535,則最長的中斷間隔時長為65535us=65.535ms 。如果我們需要的中斷間隔時長為50ms,即每50ms產(chǎn)生一次中斷,則我們需要讓加法器有一個固定的初值為65.535ms-50.000ms=15.535ms。則初值為15535,轉(zhuǎn)換成16進制為:3caf。高8位為3c,需要賦值給THx,低8位位af,需要賦值給TLx。
????????但每次轉(zhuǎn)換進制很麻煩,因此我們可以通過以下公式,快速寫出代碼:
????????最大值就是工作方式的最大計數(shù)值,方式1為16位定時/計數(shù)器,2 ^16=65535,即最大值為65535 。定時值是我們的定時時長,如果是50ms,則為50 000us,即定時值為50000 。帶入式子即可在運行時由單片機自己進行運算,方便我們進行代碼編寫。注意:當計算式確定之后,還是要手動把計算結(jié)果替換進賦值表達式,減少單片機資源占用。
3.編程中涉及的中斷函數(shù)
3.1 定時/計數(shù)器初始化函數(shù)
????????目的:使用T0產(chǎn)生一個9.216ms的定時/計數(shù)中斷
????????該函數(shù)中,首先是對TMOD進行設置,方式一,定時中斷,無需外部中斷,只是用T0,因此為0x01 。然后設定計數(shù)初值,對TH0,TL0進行賦值。EA和ET0是IE寄存器的對應位,作用分別為打開總中斷,打開定時/計數(shù)器T0中斷。TR0是TCON寄存器的對應位,作用為T0開始計數(shù)。
3.2 定時/計數(shù)器服務函數(shù)
????????中斷的服務函數(shù),作用是當中斷產(chǎn)生時,系統(tǒng)暫停當前工作,而去執(zhí)行中斷服務函數(shù)里面的程序,執(zhí)行完服務函數(shù)的程序后,再返回繼續(xù)執(zhí)行先前的工作。在這個函數(shù)中,首先我們對初值寄存器TH0和TL0進行賦值,因為工作方式1不會自動重裝初值,只有工作方式2(8位自動重裝方式)會自動重新裝載初值,因此除了方式2以外的所有方式,都需要在中斷服務函數(shù)中重新對THx和TL0重新賦值。然后下面的自增指令是我們自己定義的功能。
4.代碼參考
4.1 編程目的代碼,代碼效果:
實現(xiàn)數(shù)碼管的時鐘顯示,顯示格式例如“23-20-00”,表示23點20分00秒時刻。并通過設置外部中斷0,當S5按下時計時暫停,再次按下時繼續(xù)計時。
//寫一個計時器,分-秒-20ms
#include < REGX52.h >
#include < INTRINS.h >unsigned char code SMG_duanma [18]={ 0xc0 , 0xf9 , 0xa4 , 0xb0 , 0x99 , 0x92 , 0x82 , 0xf8 , 0x80 , 0x90 , 0x88 , 0x80 , 0xc6 , 0xc0 , 0x86 , 0x8e ,0xbf , 0x7f };sbit S5 = P3^2;void select_HC573 ( unsigned char channal )
{switch ( channal ){case 4:P2 = ( P2 & 0x1f ) | 0x80;break;case 5:P2 = ( P2 & 0x1f ) | 0xa0;break; case 6:P2 = ( P2 & 0x1f ) | 0xc0;break;case 7:P2 = ( P2 & 0x1f ) | 0xe0;break;}
}void Input_SMG ( unsigned char pos_SMG , unsigned char value_SMG )
{select_HC573 ( 6 );P0 = 0x01 << pos_SMG;select_HC573 ( 7 );P0 = value_SMG;
}void Delay ( unsigned char t )
{while ( t-- ){unsigned char i,j;_nop_ ();_nop_ ();i = 22;j = 128;do {while ( --j );}while ( --i ); }
}//=======================================
void Init_timer0_INT0 ()
{//設置T0初始化TMOD = 0x01;TH0 = ( 65535 - 18433 ) / 256;TL0 = ( 65535 - 18433 ) % 256;ET0 = 1;//設置INT0初始化IT0 = 1;EX0 = 1;//打開總中斷開關,以及啟動定時器EA = 1;TR0 = 1;
}unsigned char state_INT0 = 0;
void Service_INT0 () interrupt 0
{ state_INT0+=1;//注意 這里有按鍵產(chǎn)生中斷的操作,但是沒有進行消抖處理//但是中斷的執(zhí)行過程會過濾掉部分抖動時間//因此按下S5只要不是很刁鉆,單片機都會正確響應//正常情況下,不推薦在服務函數(shù)中寫入if語句,這里為了使用外部中斷,無法使用消抖處理,因此出此下策if( state_INT0%2==0 ){TR0 = 1;}else{TR0 = 0;}}unsigned char timer0_50ms = 0;
void Service_timer0 () interrupt 1
{TH0 = ( 65535 - 50000 ) / 256;TL0 = ( 65535 - 50000 ) % 256;timer0_50ms ++;
}
//========================================unsigned char timer0_s = 00;
unsigned char timer0_m = 20;
unsigned char timer0_h = 23;
void time_SMGrunning ()
{if ( timer0_50ms == 20 ){timer0_s ++;timer0_50ms = 0;if ( timer0_s == 60 ){timer0_m ++;timer0_s = 0;timer0_50ms = 0;if ( timer0_m == 60 ){ timer0_m = 0;timer0_h++;} }}Input_SMG ( 7 , SMG_duanma[ timer0_s%10 ] );Delay ( 1 );Input_SMG ( 6 , SMG_duanma[ timer0_s/10 ] );Delay ( 1 );Input_SMG ( 5 , SMG_duanma[16] );Delay ( 1 );Input_SMG ( 4 , SMG_duanma[ timer0_m%10 ] );Delay ( 1 );Input_SMG ( 3 , SMG_duanma[ timer0_m/10 ] );Delay ( 1 );Input_SMG ( 2 , SMG_duanma[16] );Delay ( 1 );Input_SMG ( 1 , SMG_duanma[ timer0_h%10 ] );Delay ( 1 );Input_SMG ( 0 , SMG_duanma[ timer0_h/10 ] );Delay ( 1 );
}void main ()
{Init_timer0_INT0 ();while ( 1 ){time_SMGrunning ();}
}
4.2 小蜜蜂課程代碼:
代碼1://使用定時器實現(xiàn)間隔時間閃爍燈光
//使用定時器實現(xiàn)間隔時間閃爍燈光
#include < REGX52.h >sbit L1 = P0^0;
sbit L8 = P0^7;
unsigned char code SMG_duanma [18]={ 0xc0 , 0xf9 , 0xa4 , 0xb0 , 0x99 , 0x92 , 0x82 , 0xf8 , 0x80 , 0x90 , 0x88 , 0x80 , 0xc6 , 0xc0 , 0x86 , 0x8e ,0xbf , 0x7f };void select_HC573 ( unsigned char channal )
{switch ( channal ){case 4:P2 = ( P0 & 0x1f ) | 0x80;break;case 5:P2 = ( P0 & 0x1f ) | 0xa0;break; case 6:P2 = ( P0 & 0x1f ) | 0xc0;break; case 7:P2 = ( P0 & 0x1f ) | 0xe0;break; }
}void Input_SMG ( unsigned char pos_SMG , unsigned char value_SMG )
{select_HC573 ( 6 );P0 = 0x01 << pos_SMG;select_HC573 ( 7 );P0 = value_SMG;}///============================================
void Init_Timer0 ()
{TMOD = 0x01;TH0 = (65535-9216) / 256;TL0 = (65535-9216) % 256;EA = 1; TR0 = 1; ET0 = 1;
}unsigned char count_timer0;
unsigned char value_SMG=0;
void Service_timer0 () interrupt 1
{TH0 = (65535-9216) / 256;TL0 = (65535-9216) % 256;count_timer0++;
}void Time_Led_Timer0 ()
{ if ( count_timer0 == 100 ){count_timer0 = 0;value_SMG++; Input_SMG ( 0 , SMG_duanma [value_SMG] );}if ( value_SMG == 9 ){value_SMG = 0;}}//=============================================void main ()
{Init_Timer0 ();while ( 1 ){Time_Led_Timer0 ();}
}
代碼2://寫一個計時器,分-秒-20ms
//寫一個計時器,分-秒-20ms
#include < REGX52.h >
#include < INTRINS.h >unsigned char code SMG_duanma [18]={ 0xc0 , 0xf9 , 0xa4 , 0xb0 , 0x99 , 0x92 , 0x82 , 0xf8 , 0x80 , 0x90 , 0x88 , 0x80 , 0xc6 , 0xc0 , 0x86 , 0x8e ,0xbf , 0x7f };sbit S5 = P3^2;
sbit S4 = P3^3;void select_HC573 ( unsigned char channal )
{switch ( channal ){case 4:P2 = ( P2 & 0x1f ) | 0x80;break;case 5:P2 = ( P2 & 0x1f ) | 0xa0;break; case 6:P2 = ( P2 & 0x1f ) | 0xc0;break;case 7:P2 = ( P2 & 0x1f ) | 0xe0;break;}
}void Input_SMG ( unsigned char pos_SMG , unsigned char value_SMG )
{select_HC573 ( 6 );P0 = 0x01 << pos_SMG;select_HC573 ( 7 );P0 = value_SMG;
}void Delay ( unsigned char t )
{while ( t-- ){unsigned char i,j;_nop_ ();_nop_ ();i = 22;j = 128;do {while ( --j );}while ( --i ); }
}//=======================================
void Init_timer0 ()
{TMOD = 0x01;TH0 = ( 65535 - 18433 ) / 256;TL0 = ( 65535 - 18433 ) % 256;ET0 = 1;EA = 1;TR0 = 1;
}unsigned char timer0_20ms = 0;
void Service_timer0 () interrupt 1
{TH0 = ( 65535 - 18433 ) / 256;TL0 = ( 65535 - 18433 ) % 256;timer0_20ms ++;
}
//========================================void time_SMGrunning ()
{static unsigned char timer0_s = 0;static unsigned char timer0_m = 0;if ( timer0_20ms == 50 ){timer0_s ++;timer0_20ms = 0;if ( timer0_s == 60 ){timer0_m ++;timer0_s = 0;timer0_20ms = 0;if ( timer0_m == 99 ){ timer0_m = 0;} }}if ( S5 == 0 ){Delay ( 3 );if ( S5 == 0 ){TR0 = 0;}else ;}else {TR0 = 1;}if ( S4 == 0 ){Delay ( 3 );if ( S4 == 0 ){timer0_20ms = 0;timer0_s = 0;timer0_m = 0;}}Input_SMG ( 7 , SMG_duanma[ timer0_20ms%10 ] );Delay ( 1 );Input_SMG ( 6 , SMG_duanma[ timer0_20ms/10 ] );Delay ( 1 );Input_SMG ( 5 , SMG_duanma[16] );Delay ( 1 );Input_SMG ( 4 , SMG_duanma[ timer0_s%10 ] );Delay ( 1 );Input_SMG ( 3 , SMG_duanma[ timer0_s/10 ] );Delay ( 1 );Input_SMG ( 2 , SMG_duanma[16] );Delay ( 1 );Input_SMG ( 1 , SMG_duanma[ timer0_m%10 ] );Delay ( 1 );Input_SMG ( 0 , SMG_duanma[ timer0_m/10 ] );Delay ( 1 );
}void main ()
{Init_timer0 ();while ( 1 ){time_SMGrunning ();}
}
5.編程思路重述
????????對定時計數(shù)器的使用在比賽中至關重要,主要就是在初始化的時候:對TMOD的正確設置(GATE、C/T),對THx和TLx的初值正確賦值,然后不要忘記對TCON寄存器的TR0和TR1要賦值為1啟動,最后是IE寄存器里面的各個開關要打開。