一、環境
我用的是Keil5做編譯工具,用proteus仿真。除了Keil5不知道有沒有其他好用的能生成.hex文件的軟件(要單片機運行是需要生成.hex文件的),Proteus則是一款很好用的仿真軟件,原件很多。當然,之前有試過multisim14,也是非常不錯的軟件,自帶有可以編寫代碼的文本編輯器,但沒找到我想要的原件。所以選擇了Proteus。
二、硬件部分
我們可以先打開Proteus:
1. Proteus新建工程
點擊開始界面的創建工程,先創建一個Proteus的工程。
(注意:最好每個項目單獨一個文件夾,后期的文件很亂很雜)
工程名寫好,選擇好文件夾,后面的可以一直下一步。
2. 添加元件
可以直接點紅色箭頭或者先點擊“元件模式”然后點擊“P”進入元件庫。
可以輸入80C51進行篩選,我用的是第一個80C51。
再找到篩選keypad,我用的是keypad-smallcalc。
接著找LED,選擇的是LED-BARGRAPH-GRN,作為輸出,也方便調試。
選好的元件就在這了。然后點擊就能放置元件。
3.連接線路
4、硬件效果
當然,紅色和藍色的點不是接上線就有的,這是仿真之后的效果。其中,紅色是高電平,藍色是低電平,無色是無電平或脈沖不穩定,黃色為短路。
注意:Proteus的部分原件默認接了電源和接地,所以找不到電源和接地管腳。比如T80C51就是默認了接電源接地,所以沒有20、40管腳。
三、軟件部分
剛剛完成了硬件部分,和真實的硬件一樣,我們都需要有程序才能讓單片機工作。現在我們來用Keil5編寫程序。雖然課程是用的匯編,但由于個人不太習慣匯編的程序,所以我嘗試的是C語言。目標是做成一個簡單的計算器。
1、Keil5新建工程
菜單欄的project下的new uVision project,選擇好芯片**T80C51,**選擇好地址 (可以和Proteus的工程放一個文件夾)。
然后新建一個.c源文件,并把源文件添加到工程的 Source Group內。
2、代碼:
(1 思路分析
想要做一個計算器,其中有“+、-、*、/ ” 。最開始我想到的是數字和運算符分開存放,然后再處理。后來發現無法預測輸入的數字位數(因為每次只能輸入一位,也不能像在黑窗口那樣回車)。于是我決定把數字變成字符,跟運算符存放在一個char數列里,再分析處理數列,找出數字和運算符。
(2 添加頭文件
添加頭文件,并設置全局變量。
#include int cro[4] = {0xFE,0xFD,0xFB,0xF7};//存放行值。分別表示是P2.0口低電平,P2.1低電平………… char indata[50];//用于存放鍵盤輸入的字符數列 int len=0;//數列的長度 int fnum=0;//用于存放第一個操作數(處理數列得到的數字) int lnum=0;//用于存放第二個操作數(處理數字得到的數字) int ans=0;//存放計算結果 char op;//存放運算符 這是51單片機的頭文件,里面包含了51單片機的存儲器、端口等 (3 延時程序 在單片機中延時程序經常用到,延時的方法也很多,有硬件延時、軟件延時,匯編中可能會用nop,或者 MOV R0,100 DJNZ R0,$ 在C語言中可以通過空循環來延時,就像下面這樣。當然也有其他方法。 void delay_ms(int n){ int i,j; for(i = 0; i < n; i++) for(j = 0; j < 1000; j++); }//這里的值只是大概寫的,n==1時不一定真是1ms。也可以算準確值。 如果一個循環夠用或者容易控制時間的話,可以不用嵌套。 最先嘗試的是矩陣鍵盤的線路反轉法,但是中間出了些問題,暫時放棄了,改用 逐行掃描法。 可見,我們的鍵盤連接了P2端口,低4位為行,高四位為列,LED則連接的P1端口。以此為例。 先使行端口(即P2端口低四位)輸出低電平,讀列值。若讀入的列值不為0FFH,則有鍵按下 int keyscan(){ int temp; P2 = 0xf0;//給P12口送入11110000B temp = P2 & 0xf0;//讀取列值 } 注意:在C語言里,二進制是前面加0b或0B,八進制是加0,十六進制加0x或0X。如十進制123,二進制0b123或0B123,八進制0123,十六進制0x123或0X123。 這樣就能完成 行輸出低電平,讀取列值的操作。如果列值不為0xf0,表示有鍵按下,有一位變成低電平。 當有鍵按下時,可以加10毫秒延時去抖。再進行逐行掃描。 掃描的過程是第四位逐位輸出低電平,讀不為0xff 的列值 int keyscan(){ int n; int row; int temp; P2 = 0xf0; temp = P2 & 0xf0; if(temp != 0xf0){ delay_ms(10)//這里需要加10毫秒延時去抖, for(n = 0; n < 4; n++ ){//遍歷,低4位逐位輸出0,直到找到按下鍵的列值 P2 = cro[n];//P2口低位輸出0 rol = P2 & 0xf0;//讀高四維 if(rol != 0xf0){ return (row|(cro[n]&0x0f));//找到按下鍵的列值后合成鍵碼,并返回 break; } } } } 這就是逐行掃描的主要程序。 (5 配置按鍵功能 這時候我們發現,鍵碼找對了,該怎么讓它執行特定的程序呢?比如現在按'1',它并不代表'1'這個數。這個時候我們就需要給鍵配置一個功能或者含義。 我是這樣定義的: void act(int key){ switch(key){ case 0x77:indata[len++] = '+';P1 = 0x80;break;//輸入的是運算符,輸出運算符按鍵對應的行。并存放到前面定義的數組里,長度+1 case 0xB7: show(); break;//”=“鍵的功能是展示運算結果 case 0xD7:indata[len++] = '0';P1 = 0x00;break;//輸入的是數字,輸出對應的二進制數 case 0xE7:clear(); break;//清零鍵的功能是清零 case 0x7B:indata[len++] = '-';P1 = 0x40;break; case 0xBB:indata[len++] = '3';P1 = 0x03;break; case 0xDB:indata[len++] = '2';P1 = 0x02;break; case 0xEB:indata[len++] = '1';P1 = 0x01;break; case 0x7D:indata[len++] = '*';P1 = 0x20;break; case 0xBD:indata[len++] = '6';P1 = 0x06;break; case 0xDD:indata[len++] = '5';P1 = 0x05;break; case 0xED:indata[len++] = '4';P1 = 0x04;break; case 0x7E:indata[len++] = '/';P1 = 0x10;break; case 0xBE:indata[len++] = '9';P1 = 0x09;break; case 0xDE:indata[len++] = '8';P1 = 0x08;break; case 0xEE:indata[len++] = '7';P1 = 0x07;break; default:break; } } 到這里我們的基本要求已經完成。接下來是完善的部分。 (6 補坑 剛才用到而沒有定義的函數,show()clear()。現在就讓我們來把這兩個函數寫出來。 首先是'='號鍵的功能函數show() void show(){ decode();//這是一個把存放按鍵的字符數組變成可以運算的數字的函數。 operat();//把字符型的數字變成int型的數字后,就該計算了。這是運算函數。 P1 = ans;//最終要把運算結果輸出到LED。由于之前定義的是全局變量,所以不需要傳參數 } 接下來是清零按鍵的功能,清零。 void clear(){ int i; for(i=0;i(4 鍵盤掃描程序
根據書上的原理,結合以上面的電路圖 寫出程序。