廣東科學技術職業學院廣州學院 實訓報告
一、 設計任務與要求
設計16鍵盤簡易計算器,實現以下功能。 1、4*4鍵盤輸入,用按鍵輸入數和運算符號。 1 2 3 + 4 5 6 - 7 8 9 * C 0 = ∕ 2、數碼管顯示運算過程和結果(十進制數),負號用一個LED燈顯示。 3、具有清零和復位功能。 4、具有連續運算功能。 5、具有簡易報錯和提示功能。
二、 總體方案與說明 利用PXA270的16個鍵盤,通過中斷輸入。16個按鍵分為10個數字鍵(0-9), +、-、*、/、=、清零等功能。整個計算器實現在結果為-9999到9999范圍內的加 減乘除運算。負數的時候通過LED1亮表示,加減乘除被按下后,對應得LED 燈會亮,在輸入數字后自動滅掉提示輸入運算符號的LED燈。按下C鍵具有 清零的效果(清除數碼管顯示,和相關變量初始化,以及LED燈初始化等) !=”的功能是:若沒有輸入數,則還是顯示0;若輸入了一個數(正數或 負數)則直接顯示這個數(負數顯示的時候,要LED1亮);具有連續運算的 功能(即具有記憶最近輸入的運算符和操作數的功能,如:剛輸入了1+5=, 顯示6之后,再按下“=”,則執行6+5=的操作,以此類推)。當操作數大于 -9999到9999的范圍后,顯示Eoor和亮所有LED燈報錯,當按下清零鍵后 ,清除報錯。同樣,在運算中,若被除數為0,則報錯。 聲明:本程序不具備多級運算,如:1+15-45=這種一次運算超過一個運算 符的運算。若按下了多次運算符,則取最后按下的為有效。 由于本人使用的硬件是4個直入鍵盤和12個矩陣鍵盤,所以程序分開處理。 三、 電路原理圖 數碼管:
1.001.jpg (34.11 KB, 下載次數: 50)
下載附件
2016-10-11 16:12 上傳
1.002.jpg (33.03 KB, 下載次數: 49)
下載附件
2016-10-11 16:12 上傳
LED燈:
1.003.jpg (19.96 KB, 下載次數: 48)
下載附件
2016-10-11 16:12 上傳
直入鍵盤:
1.004.jpg (26.08 KB, 下載次數: 48)
下載附件
2016-10-11 16:12 上傳
矩陣鍵盤:
1.005.jpg (31.59 KB, 下載次數: 58)
下載附件
2016-10-11 16:12 上傳
四、 軟件主要模塊流程圖
1.006.jpg (71.32 KB, 下載次數: 53)
下載附件
2016-10-11 16:12 上傳
1.007.jpg (39.4 KB, 下載次數: 45)
下載附件
2016-10-11 16:12 上傳
1.008.jpg (59.43 KB, 下載次數: 46)
下載附件
2016-10-11 16:12 上傳
1.009.jpg (70.26 KB, 下載次數: 55)
下載附件
2016-10-11 16:12 上傳
緊接上面的加減乘除、等于、清零鍵的流程圖:
1.010.jpg (72.02 KB, 下載次數: 49)
下載附件
2016-10-11 16:12 上傳
五、 問題分析與解決方案 問題1:如何做到連續運算功能? 答:首先,你要知道你在數學中是如何運算的。如:我要做(25+3-4)*2/3的運算,那么我的計算步驟為:25+3=28,28-4=24,24*2=48,48/3=16。其次,由數學運算推導出一個規律。(以剛才的舉例為例)從剛才的運算可以看出,我們在做完一次運算后,其結果都作為了下一次運算的一個成員。那么我們不妨把這個結果放到一個變量如:firstnum中,同時我們第一個運算數25也可以放到變量firstnum中。然后,我們在把要運算的數,即在運算符號后面的數存放到另一個變量secondnum中。這時,我們就可以把運算表達式寫為:firstnum = fisrtnum + secondnum;(在程序中,結果都是在=號左邊的。)之后的幾個表達式分別為:firstnum = firstnum – secondnum; firstnum = firstnum * secondnum; firstnum = firstnum / (secondnum*1.0);(在程序中除號運算的結果為整數,要做到真正的除法運算,就要運算數中至少一個為小數即可。) 最后,從這幾個表達式我們可以看出其基本規律是:firstnum = firstnum 運算符 secondnum; 這樣只要我們把第一個輸入的數和第二個輸入的數分開來存,就可以做到連續運算的效果。 問題2:如何正確的把一個表達式中的第一個運算數保存到firstnum中,第二個運算數保存到secondnum中? 答:根據問題1中的運算表達式,我們可以看出,在沒有到運算符之前的是firstnum,在運算符后面的是secondnum。這樣我們就可以根據有沒有輸入運算符來把輸入的數存放到相應的變量中。當然,由于你的firstnum是可以作為下一個運算的一個運算數的,所以這里要把firstnum定義為全局變量。 問題3:如何實現大于10而小于9999的數顯示? 答:由于我的硬件為4個數碼管,所以這里僅以我的硬件為例解釋。在我們的實驗箱上面,都有兩組兩個的靜態數碼管,它們的控制端從左到右為:LED_CS2,LED_CS3。一個“CS”就控制兩個數碼管。從這里我們就可以知都CS2是顯示千位和百位的,CS3是顯示十位和個位的。由于舊版的PXA270實驗箱的硬件時把要控制小數點端口用來控制數碼管的開與關,所以整個程序不考慮小數,只考慮整數的運算。 回到正題,在這里我們用一個數碼管顯示一個數字。如:我們要顯示14這個數,那么我們就要把這個兩位數分開來分別送到CS3的兩個數碼管顯示。如何分開這兩個數?在程序中,我們可以通過這兩個表達式獲取這個數(用num表示)的十位(用swei表示)和各位(用gwei表示):swei = num/10%10; gwei = num%10;在這里有必要解釋一下num/10和num%10的意思。num/10是表示num除以10,但這不是數學上的除法運算,而是把num除以10之后,取其能夠整除的部分,即可以理解為整數部分。而num%10剛好和num/10相反,它是把num除以10后,不能整除的部分,即余數部分取出來的意思。這樣,我們取到十位和個位后,就可以把這兩個數分別送到數碼管CS3中顯示就可以了。同樣的道理,要顯示百位(用bwei表示)和千位(用qwei表示),可以通過這兩個表達式獲得:bwei = num /100%10; qwei = num /1000%10;之后再把千位和百位送到數碼管CS2中顯示就可以了。 問題4:如何做到輸入幾位數(在-9999到9999之間)的效果? 答:要做到輸入幾位數的效果,我們不妨把整個效果想一下。在硬件上,我們是一個按鍵一個按鍵的按的,也就是說我們如果要輸入123的話,就要分別按下1鍵、2鍵、3鍵。而每按下一個鍵,我們的程序就會記錄剛按下的這個鍵。再考慮一下在硬件上按下123的效果:首先,我們按下1的時候,數碼管就顯示1;再按下2的時候,數碼管要顯示12,才能達到我們要的結果;再次按下3的時候,數碼管就要顯示123。那么從這里我們可以找出一個數學規律,那就是:1=1;12=1*10+2;123=12*10+3;這個規律就是第二個按下的鍵要顯示的結果就是第一個按下的數字乘以10,再加上第二個按下的數字;而第三個按下的鍵的結果就是第二個按下的結果乘以10,再加上第三個按下的數字。用程序的表達式可以寫成:firstnum = firstnum*10 +justnum;(justnum為最近按下的鍵)這個表達式就解決了硬件上實現輸入多位數的問題。如果你不相信這個表達式可以實現的話,那我們再來驗證一下,比如說你要輸入5,那么這個表達式就應該為firstnum = 0*10 +5;最后結果為5(這里要說明一點,firstnum的初始值為0)。又比如說,你要輸入的數為5678:那么每輸入一個數字的對應表達式分別為:firstnum = 0*10 + 5;即firstnum此時為5;輸入6的時候,firstnum = 5*10 +6;此時firstnum = 56;輸入7,firstnum = 56*10 +7;此時firstnum = 567;最后按下8的時候,firstnum = 567*10 + 8;最后firstnum = 5678;這就說明我們這個表達式是正確的。 問題5:為何實現了操作數為多位數運算,卻不能做到連續運算? 答:在解釋這個問題前,先說明一點:由于我們的按鍵都是中斷按鍵,所以每按一下按鍵就會重新進入一次鍵盤處理函數。在進行運算的時候,我們把第一個操作數和第二個操作數都定義為了全局變量。當我們進行完第一次運算,由于沒有清零,所以再按下運算符繼續運算的時候,你會發現數碼管是接著第一次運算的結果為上一次的輸入乘以10再加上你剛按下鍵顯示出來的。比如說:你進行完的第一次運算為56+64=120;然后你再按下+1的時候,數碼管卻顯示了641。這是為什么??如何解決這個問題?出現這個問題的原因是:在進行完第一次運算后,firstnum的值變了,而secondnum中的值還是64,而且由于你判斷把第二個操作數存放到secondnum中的條件是有沒有輸入運算符。這就導致你再輸入一次運算符的時候,它還是滿足條件,而直接進入到表達式:secondnum = secondnum*10 + justnum;其中的secondnum還保存到上一次輸入的值64,現在再按下一個數字1,程序重新進入到這個函數就執行了secondnum = 64*10 + 1;的表達式,這就是問題出現的原因。那如何解決這個問題?在我看來,因為你運算完一次運算后,都會有一次=號被按下的機會。我們可以給等號定義一個標志位,這樣就可以通過在輸入第二個操作數之前判斷這個等號是否被按下,按下就把secondnum清零,同時把等號標志位也清掉。這樣就可以避免上述情況的發生。 問題6:為何在硬件上調試的時候,會經常出現卡死的情況(就是數碼管顯示一個東西不變,按任何鍵都沒反應)? 答:開始我的程序出現這種情況的時候,檢測程序好幾遍都沒發現程序上有啥錯,也不關硬件的事。后來才知道原來是在boot.s里面把MDMRS配置成0x2800,MDMRS配置成這個的時候,會經常產生沖突,只要把其配置成0x320032就OK了。如: 原來是: ;MDMRS ldr r1, =0x2800 str r1,[r11] mov pc,r14 更改后: ;MDMRS ldr r1, =0x320032 str r1, [r11] mov pc,r14 問題7:為何下載鏡像文件到實驗箱的時候老是提示失? 答:出現這類情況的原因有多種,這里只介紹軟件設置錯誤的解決方法。軟件上到底是哪設置的問題會導致這種情況呢?這個地方的設置很容易被人忽視。下面以截圖說明: 步驟1:
1.011.jpg (40.14 KB, 下載次數: 50)
下載附件
2016-10-11 16:12 上傳
步驟2:
1.012.jpg (48.44 KB, 下載次數: 54)
下載附件
2016-10-11 16:12 上傳
步驟3:
1.013.jpg (51.41 KB, 下載次數: 48)
下載附件
2016-10-11 16:12 上傳
步驟4:
1.014.jpg (47.43 KB, 下載次數: 55)
下載附件
2016-10-11 16:12 上傳
步驟5:
1.015.jpg (45.76 KB, 下載次數: 48)
下載附件
2016-10-11 16:12 上傳
如果以上的設置都沒錯,但下載還是總是失敗的話,那就要看你的bootloader配置了。
六、 實驗體會 心得體會:在剛接觸到這個項目的時候,大概想了一下挺簡單的。但是在實踐寫程序的時候總是會出現各種各樣的原因,現實往往和自己想的不一樣。就像我自己按照自己的思路寫好的程序,在上硬件調試的時候會發現有很多情況自己都沒有考慮到。通過這個項目,使得自己對PXA270的這一部分硬件了解了很多,在寫程序方面也有點點的長進。在試驗中遇到的比較常見的情況都寫出來了,在這里就不羅嗦了。 - /******************************************************
- *
- *項目名稱:簡易計算器
- *
- *函 數:cal.c
- *
- *作 用:按鍵處理,主函數
- *
- *有待改進:本程序還可以進一步的完善,其一就有如按下1+2,
- * 再按下+號的時候,就直接計算出結果并顯示。
- * 希望有興趣的朋友能夠加以改進。
- *
- *制 作 人:沐雨青林
- *
- *******************************************************/
- #include "led_8.h"
- #define uchar unsigned char
- #define usint unsigned short int
- #define uint unsigned int
- extern void Led8_disp(int num);
- extern const uchar key_source_code[];
- extern void init_key(void);
- #define key_add '+'
- #define key_de '-'
- #define key_mul '*'
- #define key_mol '/'
- #define key_canal 'C'
- #define key_means '='
- const uchar key_code[] ={ 1, 2, 3, key_add, // +
- 4, 5, 6, key_de, // -
- 7, 8, 9, key_mul, // *
- key_canal, 0x00, key_means, key_mol};// /
- int firstnum=0;//存放第1個數
- int secondnum=0;//存放第2個數
- uchar operation='\0';//運算符號
- uchar mean_sign=0;//等號標志位 1為開,0位關
- uint optimes=0;//運算符號計數位
- //以運算符+、-、*、/為標志,當這些標志出現后,則輸入的數字存放到
- //secondnum中
- //變量初始化
- void init_clear(void)
- {
- firstnum = 0;
- secondnum = 0;
- operation = '\0';
- optimes = 0;
- LED_CS4 = 0xff;
- Led8_disp(0);
- return;
- }
- //計算多位數,num為新輸入的一位數
- //算好的多位數存放在snum,snum為原來存放有數的變量
- int duoweishu(int snum, int num)
- {
- snum = num + snum*10;
- return snum;
- }
- //直入式鍵盤處理
- void op_disdir(usint key_d)
- {
- uint i;
- for(i=0; i<4; i++)
- {
- if(key_d == key_source_code[i])
- {
- if(i == 3)// ‘+’ 按鍵處理
- {
- LED_CS4 = 0x7f;
- optimes++;
- operation = key_code[i];//operation 為‘+’
- }
- else
- {
- //在沒有按下運算符的時候,把按鍵對應的鍵值存到firstnum
- if(operation == '\0')
- {
- firstnum = duoweishu(firstnum,key_code[i]);
- Led8_disp(firstnum);
- }
- else//按下運算符的時候,把按鍵對應的鍵值存到secondnum
- {
- if(mean_sign == 1)//等號標志位
- {
- secondnum = 0;
- mean_sign = 0;//清等號標志位
- }
-
- if(operation == '-' && optimes == 1)//負數輸入
- {
- firstnum = duoweishu(firstnum,-key_code[i]);
- Led8_disp(firstnum);
- }
- else
- {
- secondnum = duoweishu(secondnum, key_code[i]);
- Led8_disp(secondnum);
- }
- }
- }
- }
- }
- }
- //矩陣鍵盤處理
- void op_dismatrix(usint key_d)
- {
- uint i;
- for(i=4; i<16; i++)
- {
- if(key_d == key_source_code[i])
- {
- if(i == 7) // ‘-’ 鍵處理
- {
- LED_CS4 = 0xbf;
- if(operation == '\0' && firstnum == 0)
- optimes = 1;
- else
- optimes += 2;
-
- operation = key_code[i];
- }
- else if(i == 11) // ‘*’ 鍵處理
- {
- LED_CS4 = 0xdf;
- optimes++;
- operation = key_code[i];
- }
- else if(i == 12) // ‘C’ 鍵處理
- {
- init_clear();//變量初始化
- break;
- }
- else if(i == 14) // ‘=’ 鍵處理
- {
- mean_sign=1;//等號標志位
- if(operation == '+')
- firstnum = firstnum + secondnum;
- else if(operation == '-')
- firstnum = firstnum - secondnum;
- else if(operation == '*')
- firstnum = firstnum * secondnum;
- else if(operation == '/')
- {
- if(secondnum == 0)//被除數為0,結果為無窮大
- firstnum = 88888;
- else
- firstnum = firstnum / secondnum*1.0;
- }
-
- //secondnum = 0;
- Led8_disp( firstnum);//顯示和
- }
- else if(i == 15)// ‘/‘ 鍵處理
- {
- LED_CS4 = 0xef;
- optimes++;
- operation = key_code[i];
- }
- else // 數字鍵輸入
- {
- //在沒有按下運算符的時候,把按鍵對應的鍵值存到firstnum
- if(operation == '\0')
- {
- firstnum = duoweishu(firstnum,key_code[i]);
- Led8_disp(firstnum);
- }
- else//按下運算符的時候,把按鍵對應的鍵值存到secondnum
- {
- if(mean_sign == 1)//等號標志位
- {
- secondnum = 0;
- mean_sign = 0;//清等號標志位
- }
-
- if(operation == '-' && optimes == 1)//負數輸入
- {
- firstnum = duoweishu(firstnum,-key_code[i]);
- Led8_disp(firstnum);
- }
- else
- {
- secondnum = duoweishu(secondnum, key_code[i]);
- Led8_disp(secondnum);
- }
- }
- }
- }
- }
- }
- void Main(void)
- {
- LED_CS4 = 0xff;
- //Led8_disp(0);
- init_clear();//變量初始化
- init_key();
- while (1)
- {
- }
- }
復制代碼- /******************************************************
- *
- *項目名稱:簡易計算器
- *
- *函 數:key.c
- *
- *作 用:按鍵GPIO定義及其相關定義,按鍵中斷函數,按鍵
- * 初始化函數
- *
- *有待改進:本程序還可以進一步的完善,其一就有如按下1+2,
- * 再按下+號的時候,就直接計算出結果并顯示。
- * 希望有興趣的朋友能夠加以改進。
- *
- *制 作 人:沐雨青林
- *
- *******************************************************/
- #define uchar unsigned char
- #define usint unsigned short int
- #define uint unsigned int
- #define KPC (*((volatile uint *)(0x41500000)))
- #define KPDK (*((volatile uint *)(0x41500008)))
- #define KPREC (*((volatile uint *)(0x41500010)))
- #define KPMK (*((volatile uint *)(0x41500018)))
- #define KPAS (*((volatile uint *)(0x41500020)))
- #define KPKDI (*((volatile uint *)(0x41500048)))
- #define GPSR2 (*((volatile uint *)(0x40E00020)))
- #define GPCR2 (*((volatile uint *)(0x40E0002C)))
- #define GPDR2 (*((volatile uint *)(0x40E00014)))
- #define GPSR3 (*((volatile uint *)(0x40E00118)))
- #define GPCR3 (*((volatile uint *)(0x40E00124)))
- #define GPDR3 (*((volatile uint *)(0x40E0010C)))
- #define GAFR3_L (*((volatile uint *)(0x40E0006C)))
- #define GAFR2_U (*((volatile uint *)(0x40E00068)))
- #define ICMR (*((volatile uint *)(0x40D00004)))
- #define ICLR (*((volatile uint *)(0x40D00008)))
- #define CKEN (*((volatile uint *)(0x41300004)))
- extern void op_disdir(usint key_d);
- extern void op_dismatrix(usint key_d);
- const uchar key_source_code[] ={ 0x40, 0x02, 0x04, 0x20,
- 0x00, 0x01, 0x02, 0x05,
- 0x10, 0x11, 0x12, 0x15,
- 0x20, 0x21, 0x22, 0x25 };
- void Delay(uint x)
- {
- uint i, j, k;
- for (i =0; i <=x; i++)
- for (j = 0; j <0xff; j++)
- for (k = 0; k <0xff; k++);
- }
- #define key_down 1
- #define key_up 0
- volatile uchar key_f;
- void init_key(void)
- {
- CKEN |= (1 << 19);
-
- //----gpio-----
- //dirkey
- //94-->KP_DKIN1(01)
- GAFR2_U |= (1<<28);
- GAFR2_U &=~(1<<29);
- //95-->KP_DKIN2(01)
- GAFR2_U |= (1<<30);
- GAFR2_U &=~(1ul<<31);
- //98-->KP_DKIN5(01)
- GAFR3_L |= (1<<4);
- GAFR3_L &= ~(1<<5);
- //99-->KP_DKIN6(01)
- GAFR3_L |= (1<<6);
- GAFR3_L &= ~(1<<7);
-
- //matrixkey
- //in
- //100
- GAFR3_L |= (1<<8);
- GAFR3_L &= ~(1<<9);
- GPDR3 &= ~(1<<4);
- //101
- GAFR3_L |= (1<<10);
- GAFR3_L &= ~(1<<11);
- GPDR3 &= ~(1<<5);
- //102
- GAFR3_L |= (1<<12);
- GAFR3_L &= ~(1<<13);
- GPDR3 &= ~(1<<6);
- //out
- //108-->KP_MKOUT5(10)
- GAFR3_L &= ~(1<<24);
- GAFR3_L |= (1<<25);
- GPDR3 |= (1<<12);
- //107-->KP_MKOUT4(10)
- GAFR3_L &= ~(1<<22);
- GAFR3_L |= (1<<23);
- GPDR3 |= (1<<11);
- //106-->KP_MKOUT3(10)
- GAFR3_L &= ~(1<<20);
- GAFR3_L |= (1<<21);
- GPDR3 |= (1<<10);
- //105-->KP_MKOUT2(10)
- GAFR3_L &= ~(1<<18);
- GAFR3_L |= (1<<19);
- GPDR3 |= (1<<9);
- //104-->KP_MKOUT1(10)
- GAFR3_L &= ~(1<<16);
- GAFR3_L |= (1<<17);
- GPDR3 |= (1<<8);
- //103-->KP_MKOUT0(10)
- GAFR3_L &= ~(1<<14);
- GAFR3_L |= (1<<15);
- GPDR3 |= (1<<7);
-
- //--------
- KPC = 0x3fbff3c2;
- KPC |= 0x01 ;
- KPC |= (1<<11);
- KPKDI = 0x6464;
-
- //interrupt
- ICMR |= (1<<4);
- ICLR &= ~(1<<4);
-
- //key_up
- key_f = key_up;
- }
- //key_interrupt
- void key_del()
- {
-
- ICMR &= ~(1<<4);
- key_f = !key_f;
- if(key_f == key_down)
- {
- usint key_d;
- uint kpc_tmp;
- kpc_tmp = KPC;
- if(kpc_tmp & (1<<5))
- {
- key_d = KPDK & 0xff;
-
- op_disdir(key_d);//直入式鍵盤處理
- //LED8_CS2 = LED_VALUE(10 , 10);
- }
- if(kpc_tmp & (1<<22))
- {
- key_d = KPAS & 0xff;
- op_dismatrix(key_d);//矩陣鍵盤處理
- //LED8_CS2 = LED_VALUE(10 , 10);
-
- }
- }
- KPC = KPC;
- ICMR |= (1<<4);
- }
- void key_dismatrix()
- {
- usint key_d;
- key_d = KPAS & 0xff;
-
- // LED8_CS3 = LED_VALUE((key_d & 0xf0) >> 4 ,key_d & 0xf);
- }
- void key_disdir()
- {
- usint key_d;
- while(key_d = KPDK & 0xff, key_d == 0x00);
- // LED8_CS3 = LED_VALUE((key_d & 0xf0) >> 4 ,key_d & 0xf);
- }
復制代碼
0.png (70.76 KB, 下載次數: 50)
下載附件
2016-10-11 16:13 上傳
所有資料下載地址:
http://www.zg4o1577.cn/bbs/dpj-4740-1.html
|