0x00 前言
有一段時間沒寫51的單片機了,實驗室的學長留了個小練習,我就用來當“復健”了。一個極其簡易的計算器,看起來很簡單,但有些細節也是琢磨了一下的(俺還是菜),用的是藍橋杯的板子,代碼的質量着實堪憂。
0x01 題目
小練習:簡易計算器
初始顯示界面
使用16個按鍵每個按鍵對應的值爲
0 1 2 3
4 5 6 7
8 9 + -
* / 清除 =
可以實現兩位數的加減
前兩個數碼管顯示第一位數,第三個數碼管顯示+、-、*、/(然後+、-、*、/分別用A,B,C,D來表示)。第四五個數碼管顯示第二位數,第六位顯示等號,第七八位顯示結果。
限制範圍,第一個數和第二個數限制在0-30以內的數,結果的範圍在0—99,將回到最初的狀態(都是0).
舉個列子:
假設我要計算12+13=25;首先我要按下對應鍵值爲1的按鍵,這時第一個數碼管顯示1,然後繼續按下對應鍵值爲2的按鍵,這時第二個數碼管顯示2,然後按下鍵值爲+號的按鍵,這時第三個數碼管顯示A,然後繼續按下對應鍵值爲1的按鍵,這時第四個數碼管顯示爲1,然後繼續按下對應鍵值爲3的按鍵,這時第五個數碼管顯示爲3,再次按鍵計算按鈕,第六個數碼管顯示等於號,並且第78數碼管顯示運算結果25。
清除鍵可以直接清除數碼管,讓數碼管清0。
0x02 思路
1. 初始化系統。
2.判斷當前將要賦值的位選,啓用對應的按鍵。
3.噹噹前位選賦值完畢後,進行下一個位選(向右)的賦值。
4.實現計算的操作和一些其他的限制。
0x03 代碼
質量着實堪憂(菜雞瑟瑟發抖)
#include <REGX52.H>
typedef unsigned char u8;
typedef unsigned int u16;
sfr P4 = 0xc0;//藍橋杯比單純的51多了一個p4,手動加上
sbit R1 = P3^0;
sbit R2 = P3^1;
sbit R3 = P3^2;
sbit R4 = P3^3;
sbit C4 = P3^4;
sbit C3 = P3^5;
sbit C2 = P4^2;
sbit C1 = P4^4;
unsigned char code SMC_duanxuan[]={
// 0 1 2 3 4 5 6 7 8 9 A B C D E =
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x48,
//black - H J K L N o P U t G Q r M y
0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46}; //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1
u8 SMC_weixuan[]={0,0,0,0,0,0,0,0};
Key_flag = 0;//狀態,位選是否已被按鍵賦值
Key_Value = 0;
Key_count = 0;//計數器,用來表示是哪個位選
result = 0;//計算結果
//使能
void InitHc138(unsigned char n)
{
switch (n)
{
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 DelaySMC(u16 t)
{
while(t--);
}
//亮單個數碼管
void SMC_Show(u8 dat, u8 poc)
{
InitHc138(6);
P0 = 0x01 << poc;
InitHc138(7);
P0 = ~dat;
}
//動態顯示
void Display()
{
SMC_Show(SMC_duanxuan[SMC_weixuan[0]], 0);
DelaySMC(500);
SMC_Show(SMC_duanxuan[SMC_weixuan[1]], 1);
DelaySMC(500);
SMC_Show(SMC_duanxuan[SMC_weixuan[2]], 2);
DelaySMC(500);
SMC_Show(SMC_duanxuan[SMC_weixuan[3]], 3);
DelaySMC(500);
SMC_Show(SMC_duanxuan[SMC_weixuan[4]], 4);
DelaySMC(500);
SMC_Show(SMC_duanxuan[SMC_weixuan[5]], 5);
DelaySMC(500);
SMC_Show(SMC_duanxuan[result / 10], 6);
DelaySMC(500);
SMC_Show(SMC_duanxuan[result % 10], 7);
DelaySMC(500);
}
//出現錯誤時的顯示和數據清除
void ERROR_Display()
{
SMC_weixuan[0]=0;
SMC_weixuan[1]=0;
SMC_weixuan[2]=0;
SMC_weixuan[3]=0;
SMC_weixuan[4]=0;
SMC_weixuan[5]=0;
Key_count = 0;
Key_flag =0;
Key_count = 0;
result = 0;
Key_flag =0;
Key_count = 0;
result = 0;
}
//數字位的按鍵
void ScanKeyMulet_num()
{
R1 = 0;
R2 = R3 = R4 = 1;
C1 = C2 = C3 = C4 = 1;
if (C1 == 0)
{
Key_Value = 0;
Key_count++;
Key_flag = 1;
while(C1 == 0)
{
Display();
}
}
else if(C2 == 0)
{
Key_Value = 1;
Key_count++;
Key_flag = 1;
while(C2 == 0)
{
Display();
}
}
else if(C3 == 0)
{
Key_Value = 2;
Key_count++;
Key_flag = 1;
while(C3 == 0)
{
Display();
}
}
else if(C4 == 0)
{
Key_Value = 3;
Key_count++;
Key_flag = 1;
while(C4 == 0)
{
Display();
}
}
R2 = 0;
R1 = R3 = R4 = 1;
C1 = C2 = C3 = C4 = 1;
if (C1 == 0)
{
Key_Value = 4;
Key_count++;
Key_flag = 1;
while(C1 == 0)
{
Display();
}
}
else if(C2 == 0)
{
Key_Value = 5;
Key_count++;
Key_flag = 1;
while(C2 == 0)
{
Display();
}
}
else if(C3 == 0)
{
Key_Value = 6;
Key_count++;
Key_flag = 1;
while(C3 == 0)
{
Display();
}
}
else if(C4 == 0)
{
Key_Value = 7;
Key_count++;
Key_flag = 1;
while(C4 == 0)
{
Display();
}
}
R3 = 0;
R2 = R1 = R4 = 1;
C1 = C2 = C3 = C4 = 1;
if (C1 == 0)
{
Key_Value = 8;
Key_count++;
Key_flag = 1;
while(C1 == 0)
{
Display();
}
}
else if(C2 == 0)
{
Key_Value = 9;
Key_count++;
Key_flag = 1;
while(C2 == 0)
{
Display();
}
}
R4 = 0;
R2 = R3 = R1 = 1;
C1 = C2 = C3 = C4 = 1;
if(C3 == 0)
{
DelaySMC(100);
if(C3 == 0)
{
ERROR_Display();
while(C3 == 0)
{
Display();
}
}
}
}
//符號位的按鍵
void ScanKeyMulet_sym()//·ûºÅλ
{
R3 = 0;
R2 = R1 = R4 = 1;
C1 = C2 = C3 = C4 = 1;
if(C3 == 0)
{
Key_Value = 10;
Key_count++;
Key_flag = 1;
while(C3 == 0)
{
Display();
}
}
else if(C4 == 0)
{
Key_Value = 11;
Key_count++;
Key_flag = 1;
while(C4 == 0)
{
Display();
}
}
R4 = 0;
R2 = R3 = R1 = 1;
C1 = C2 = C3 = C4 = 1;
if (C1 == 0)
{
Key_Value = 12;
Key_count++;
Key_flag = 1;
while(C1 == 0)
{
Display();
}
}
else if(C2 == 0)
{
Key_Value = 13;
Key_count++;
Key_flag = 1;
while(C2 == 0)
{
Display();
}
}
R4 = 0;
R2 = R3 = R1 = 1;
C1 = C2 = C3 = C4 = 1;
if(C3 == 0)
{
DelaySMC(100);
if(C3 == 0)
{
ERROR_Display();
while(C3 == 0)
{
Display();
}
}
}
}
////計算位的按鍵
void ScanKeyMulet_cal()//¼ÆËãλ
{
R4 = 0;
R2 = R3 = R1 = 1;
C1 = C2 = C3 = C4 = 1;
if(C3 == 0)
{
DelaySMC(100);
if(C3 == 0)
{
ERROR_Display();
while(C3 == 0)
{
Display();
}
}
}
else if(C4 == 0)
{
Key_Value = 15;
Key_count++;
Key_flag = 1;
while(C4 == 0)
{
Display();
}
}
}
//判斷當前的位選,從而對應相對的按鍵顯示
void Wei_sel()
{
if(Key_count == 2)
{
ScanKeyMulet_sym();
}
else if(Key_count !=2 &&Key_count !=5)
{
ScanKeyMulet_num();
}
else if(Key_count == 5)
{
ScanKeyMulet_cal();
}
}
//系統初始化
void InitSystem()
{
InitHc138(5);
P0 = 0x00;
InitHc138(4);
P0 = 0xff;
}
void main()
{
InitSystem();
while(1)
{
Display();
Wei_sel();
if(Key_flag == 1)
{
SMC_weixuan[Key_count%7 - 1]= Key_Value;
Key_flag =0;
}
if(Key_count == 6 && SMC_weixuan[5] == 15
&&(SMC_weixuan[0]*10+SMC_weixuan[1])>0 && (SMC_weixuan[0]*10+SMC_weixuan[1])<30
&&(SMC_weixuan[3]*10+SMC_weixuan[4])>0 && (SMC_weixuan[3]*10+SMC_weixuan[4])<30)
{
switch (SMC_weixuan[2])//計算
{
//¡°+¡±
case 10:
result=(SMC_weixuan[0]*10+SMC_weixuan[1]) + (SMC_weixuan[3]*10+SMC_weixuan[4]);
break;
//"-"
case 11:
result=(SMC_weixuan[0]*10+SMC_weixuan[1]) - (SMC_weixuan[3]*10+SMC_weixuan[4]);
break;
//"*"
case 12:
result=(SMC_weixuan[0]*10+SMC_weixuan[1]) * (SMC_weixuan[3]*10+SMC_weixuan[4]);
break;
//"/"
case 13:
result=(SMC_weixuan[0]*10+SMC_weixuan[1]) / (SMC_weixuan[3]*10+SMC_weixuan[4]);
break;
}
}
if((SMC_weixuan[0]*10+SMC_weixuan[1])<0 || (SMC_weixuan[0]*10+SMC_weixuan[1])>30
||(SMC_weixuan[3]*10+SMC_weixuan[4])<0 || (SMC_weixuan[3]*10+SMC_weixuan[4])>30
||(result < 0) || (result >99))
{
ERROR_Display();
}
}
}
0x04 總結
確實有些生疏了,程序比較簡陋,一些代碼其實還可以再封裝,封裝,命名不太規範,按鍵那塊寫的有些冗長。。。總之,作爲“復健”確實讓我意識到了自己的問題。有錯誤,希望路過的大佬斧正。(順便吐槽一下,這個計算器也太low了,希望出題的學長看不到(狗頭保命))。