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了,希望出题的学长看不到(狗头保命))。