2017-5-9 10:13 上傳
點擊文件名下載附件
程序與解析
下面是部分內容預覽(無圖版):
Project1: 反應速度測試(主要內容,有擴展)
一、 設計內容
(1) 數碼管倒計時進入準備狀態;
(2) 亮燈;
(3) 測試人員看見亮燈后按鍵;
(4) MCU記錄反應時間;
(5) 在數碼管上顯示反應時間;
(6) 按另一鍵重新開始;
(7) 如果出現搶跑的情況,給出提示。
二、設計思路
(1)數碼管倒計時進入準備狀態:檢測SW1是否按下,如果按下,則讓數碼管SELECT-LED0按順序顯示四個數字:3、2、1、0。
(2)判斷是否搶跑并亮燈:在倒計時中,判斷SW5是否按下,如果按下,則為搶跑,點亮led1,2,3,4,且數碼管顯示0;如果倒計時過程中SW5沒有按下,則在數碼管正常倒計時到0的同時點亮led1燈;
(3)設定定時中斷中斷時間:設定PIT時鐘通道0的倒計時開始時間,令number_ms等于1,則設定了1ms的定時中斷;
(4)定時中斷設計:每次中斷都執行判斷SW5是否按下,如果沒有按下,則計數count=count+1;如果SW5按下,則進入數碼管顯示子程序,數碼管顯示當前count值,即單位為毫秒;
(5)重新重啟:循環檢測SW1狀態,SW1一旦按下,則重新進入程序初始,實現重新啟動功能;
三、主要程序流程圖(加簡單文字解釋)
file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C46.tmp.jpg
四、程序清單(加注釋)
4.1:程序初始化:
4.1.1 數碼管顯示初始化:
//第一步,使能port A和port D, 即將系統時鐘門控寄存器SIM_SCGC5的第9位和第12位置1
IM_SCGC5 |=(0x1000 | 0x0200);
//第二步,設置port A和port D為通用輸入輸出端口
PORTA_PCR12=0x100;
PORTA_PCR13=0x100;
PORTA_PCR14=0x100;
PORTA_PCR15=0x100;
PORTA_PCR16=0x100;
PORTA_PCR17=0x100;
PORTD_PCR0 = 0x0100;
PORTD_PCR1 = 0x0100;
PORTD_PCR2 = 0x0100;
PORTD_PCR3 = 0x0100;
PORTD_PCR4 = 0x0100;
PORTD_PCR5 = 0x0100;
PORTD_PCR6 = 0x0100;
PORTD_PCR7 = 0x0100;
4.1.2 LED燈初始化:
SIM_SCGC5|=SIM_SCGC5_PORTC_MASK; //使能port C,port C 的0到11位設置為通用輸入輸出端口
PORTC_PCR0=PORT_PCR_MUX(0X1); //LED1
PORTC_PCR1=PORT_PCR_MUX(0X1);
PORTC_PCR2=PORT_PCR_MUX(0X1);
PORTC_PCR3=PORT_PCR_MUX(0X1); //LED2
PORTC_PCR4=PORT_PCR_MUX(0X1);
PORTC_PCR5=PORT_PCR_MUX(0X1);
PORTC_PCR6=PORT_PCR_MUX(0X1); //LED3
PORTC_PCR7=PORT_PCR_MUX(0X1);
PORTC_PCR8=PORT_PCR_MUX(0X1);
PORTC_PCR9=PORT_PCR_MUX(0X1); //LED4
PORTC_PCR10=PORT_PCR_MUX(0X1);
PORTC_PCR11=PORT_PCR_MUX(0X1);
GPIOC_PDDR|=0XFFF; //設置port C的第0位到第11位為輸出 GPIOC_PDOR=0XFFF;
//將存放port C輸出數據的寄存器相應位置1,表示LED燈全滅
4.1.3 左右按鍵初始化:
SIM_SCGC5|=SIM_SCGC5_PORTB_MASK; //使能port C
SIM_SCGC5|=SIM_SCGC5_PORTE_MASK;
//設置port B和port E為通用輸入輸出端口,使能中斷,并設置中斷方式為下降沿觸發中斷(將第0位和第1位置1,邊沿觸發使能;將IRQx位設置為1010表示下降沿觸發)
PORTB_PCR17=0x0A0103;
PORTB_PCR16=0x0A0103;
PORTB_PCR11=0x0A0103;
PORTB_PCR10=0x0A0103;
PORTE_PCR2=0x0A0103;
PORTE_PCR3=0x0A0103;
PORTE_PCR4=0x0A0103;
PORTE_PCR5=0x0A0103;
GPIOB_PDDR&= ~0x30C00; //按鍵端口全部設置為輸入
GPIOE_PDDR&= ~0x3C;
4.1.4 IRQ使能初始化:
int div;
div=irq/32; //得到非優先寄存器的寄存器號
if (irq > 91) irq=91; //確定irq號為有效的irq號
switch(div) {
case 0x0: NVICICPR0=1<<(irq%32); //中斷清除使能
NVICISER0=1<<(irq%32); //中斷設置使能
break;
case 0x1: NVICICPR1=1<<(irq%32);
NVICISER1=1<<(irq%32);
break;
case 0x2: NVICICPR2=1<<(irq%32);
NVICISER2=1<<(irq%32);
break;
} 注意:調用該函數時需要帶參數 IRQ number=1;
4.1.5 PIT時鐘初始化:
/*開啟PIT 時鐘,即將系統時鐘門控寄存器SIM_SCGC6的PIT位置1*/
SIM_SCGC6|=SIM_SCGC6_PIT_MASK;
PIT_MCR&=~(PIT_MCR_MDIS_MASK); //使能PIT時鐘模塊
PIT_LDVAL0=20000*number_ms; //設定PIT時鐘通道0的倒計時開始時間
PIT_TCTRL0|=PIT_TCTRL_TIE_MASK;// 使能PIT時鐘通道0的IRQ功能
PIT_TCTRL0|=PIT_TCTRL_TEN_MASK; //使能定時器
4.2 SWI中斷程序:
int a[4]={3,2,1,0}; //倒計時
for(i=0;i<=3;i++)
{ if(PORTE_ISFR&0X04||PORTE_ISFR&0X020)
GPIOC_PDOR&=~0x249; //若搶跑,則四個LED燈均亮
else { GPIOD_PDOR = num[a];
GPIOA_PDOR = Select_LED0;
if(a!=0) delay2();
} //3,2,1后面均有延時,0后面沒有,保證數到0時燈即亮
}//數碼管顯示倒計時
GPIOC_PDOR&=~0x01; //LED1亮
PORTB_ISFR |= 0x00FF00;
4.3 定時中斷程序:
根據前面的初始化程序可知,定時器中斷周期為1ms,因此計數器counter加1,對應的延遲時間為1ms,為了簡化程序,我們在數碼管上顯示的時間單位為ms,即測量反應時間的靈敏度為1ms;這樣我們只需把計數器counter的值轉化為十進制,在數碼管上顯示出來即可。
void pit_channel0_ISR(void)
{ if((PORTE_ISFR&0X04)==0) count++; //反應鍵未按下,計時
else renew(count); //反應鍵按下,停止計時,用數碼管顯示
PIT_TFLG0|=PIT_TFLG_TIF_MASK;//Clear the flag of channel0,PIT
}
4.4 數碼管顯示子程序:
void renew (int s)
{ int i; int f=s;
f=f%1000000;
int a[6]={10,10,10,10,10,10};
Select_LED=Select_LED0;
for(i=5;i>=0;i--)
{ a=f%10; f/=10;
if(f==0) break; }
for(i=5;i>=0;i--)
{ GPIOD_PDOR = num[a];
GPIOA_PDOR =Select_LED;
Select_LED=Select_LED<<1;
Select_LED++;
delay1(); }
}
4.5 擴展部分程序:
4.5.1 數碼管顯示程序擴展:
void renew1(int s)
{ int i; int f=s; f=f%1000;
int a[6]={10,10,10,10,10,10};
Select_LED=Select_LED0;
for(i=2;i>=0;i--)
{ a=f%10; f/=10;
if(f==0) break; }
for(i=2;i>=0;i--)
{ GPIOD_PDOR = num[a];
GPIOA_PDOR =Select_LED;
Select_LED=Select_LED<<1;
Select_LED++;
delay1(); }
}
同理,void renew2(int s)與void renew1(int s)非常相似,只要把LED=Select_LED0改為LED=Select_LED3即可。
4.5.2 定時中斷程序擴展:
void pit_channel0_ISR(void)
{ if((PORTE_ISFR&0X04)==0) count1++; //反應鍵sw5未按下,計時
else if((PORTE_ISFR&0X04)!=0)
renew1(count1); //反應鍵按下,停止計時,用數碼管顯示
if((PORTE_ISFR&0X020)==0) count2++; //反應鍵sw8未按下,計時
else if((PORTE_ISFR&0X020)!=0)
renew2(count2); //反應鍵按下,停止計時,用數碼管顯示
PIT_TFLG0|=PIT_TFLG_TIF_MASK;
}
五、調試過程
1、在調試過程中,我們遇到了摁鍵中斷和定時中斷沖突的情況,后來通過一點一點理順思路發現,正在運行摁鍵中斷時,即使初始化了PIT時鐘模塊,也只能開啟定時器倒計時的功能,但是定時器倒計時到0的時候并不能進入定時中斷子程序中執行相應語句。
2、倒計時至0時,LED1燈沒有立即亮,而是延遲了一會兒才亮,檢查發現,原來程序中3,2,1和0后面均有延時,于是我們就將0從for循環中提取出來,單獨顯示,并且不加延時,保證數到0時燈即亮。
3、因為要使用摁鍵SW1來進行“開始”和“重啟”操作,必須處理摁鍵抖動的問題,我們嘗試了各種不同長度的延時,終于找到了合適時長,不影響操作和響應,也可以防抖。
4、我們要測試指示燈亮到摁鍵被摁下的反應時間,就必須防止摁鍵被重復觸發。除了使用延時去除抖動的方式來防止硬件干擾,我們還必須利用在其它端口的中斷服務子程序中清除該摁鍵中斷標志的方式來防止人為錯誤操作影響測試結果的顯示。
5、我們在定時中斷服務子程序中要用到PORTE的中斷標志位,如果在PORTE_Hanlder里清除中斷,那么將不會執行定時中斷服務子程序,也就不會有任何顯示,所以應該把PORTE的中斷清除語句拿出來,放到PORTB_Hanlder程序中,因為SW1是復位鍵,每按下一次,表示程序初始化,需要清除所有操作。
6、當反應時間較長時,數碼管顯示的數值不合邏輯,后來考慮到數碼管可能出現溢出現象,于是就添加了f=f%1000000。
六、實際結果(有相關照片)
| file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C57.tmp.pngfile:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C58.tmp.png |
| file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C59.tmp.png |
| file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C5A.tmp.png |
| file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml/wps1C6B.tmp.png |
七、結論與改進(改進可以只有想法,沒有實現)
1、結果:實現了基本功能;
2、實現了兩個人比賽,分別顯示各自的反應時間的功能;
3、實現了單人、雙人兩種模式且完成了單/雙人模式切換;
4、實現了以秒和毫秒兩種單位的數值顯示;
5、改進:將倒計時部分做成準確的3秒倒計時,而不是僅用delay函數做。