前言
本节主要讲的是基于51单片机下通过程序配置矩阵按键和数码管制作出一个简单的计算器,可以实现八位数内加减乘除算法,具体操作如下。
一、模块介绍
本节制作计算器主要用到矩阵按键和数码管两个模块。
1. 矩阵按键
矩阵按键是指形成矩阵的按键,一般由多行多列组成,本节我以4*4的矩阵按键为例进行介绍。
1.1 基本原理
矩阵按键分为行和列,没有按键按下的时候,行和列是断开的,而当某一个按键按下时,改按键对应的行和列就会短接,电平会变成相同;由于矩阵键盘中没有电阻,所以当按键按下时,一个高电平和低电平短接会把高电平拉低。
其实物图和原理图如下:
可见,4*4矩阵按键只需占用8个MCU引脚。
1.2 扫描方式
矩阵键盘分为逐行扫描和行列扫描两种扫描方式。
逐行扫描:高4位P17-P14对应原理图上的四行,依次把每一行设置为低电平,把低4位设置未高电平,然后检测第四位是否全为1,如果不全为1,说明有按键按下,电平被拉低,也就是对应的按键被按下,如此循环检测每一行;逐列扫描也同理可得;
行列扫描:先把高4位设置为低电平,把低4位设置为高电平,这样如果低4位中有电平变低,说明该列有按键按下,但不知道具体是哪一行的按键;接着反过来操作,把高4位设置为高电平,低四位设置为低电平,检测高4位哪一位被拉低,那么按键就在对应的行上,这样通过两次扫描就知道该按键在哪一行哪一列了, 也就知道具体是哪个按键被按下了。
1.3 消抖
由于按键的机械特性,按键存在“抖动”的情况,比如,当按下某个键的瞬间,这个键可能还会来回抖动几次,当然这种抖动是手指感觉不出来的,但它确实会影响读取到的电平的高低,相反,按键弹起的瞬间,也会存在抖动的情况。如图3,按键抖动通常只持续不到10ms,按下到弹起的整个过程通常持续100ms以上,所以需要进行消抖处理。
2. 数码管
数码管(LED Segment Displays)由多个发光二极管封装在一起组成“8”字型的器件,引线已在内部连接完成,只需引出它们的各个笔划,公共电极。数码管实际上是由七个发光管组成8字形构成的,加上小数点就是8个。这些段分别由字母a,b,c,d,e,f,g,dp来表示,如图所示。数码管按段数可分为七段数码管和八段数码管,八段数码管比七段数码管多一个发光二极管单元(多一个小数点显示);按能显示多少个“8”可分为1位、2位、3位、4位、5位、6位、7位等数码管。
按发光二极管单元连接方式可分为共阳极数码管和共阴极数码管。共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极的数码管,共阳数码管在应用时应将公共极COM接到+5V,当某一字段发光二极管的阴极为低电时,相应字段就点亮,当某一字段的阴极为高电平时,相应字段就不亮。共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极的数码管,共阴数码管在应用时应将公共极接到地线GND上,当某一字段发光二极管的阳极为高电平时,相应字段就点亮,当某一字段的阳极为低电平时,相应字段就不亮。
原理图如下:
本节已共阴数码管为例进行配置
二、程序设计
4x4矩阵按键分布
0 1 2 3
4 5 6 7
8 9 + -
* / RES =
分为以下4个模块进行设计:
- 矩阵按键
- 数码管
- “+ - x ÷”算法设计
- main函数
定义声明
#include<reg52.h>
#define key P1 //定义key为矩阵按键P1口
#define dig P0 //定义dig为数码管P0口
typedef unsigned char u8;
typedef unsigned int u16;
sbit LSA=P2^2; //74LS138位选
sbit LSB=P2^3;
sbit LSC=P2^4;
u16 keyvalue; //存键值
u16 keyswitch; //判断是否按下按键
u8 code duanxuan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //段选0-F
u16 box[8]={0};
u16 i;
1. 矩阵按键
//延迟函数
void delay(u16 i)
{
while(i--);
}
//扫码函数(扫描矩阵按键)
void keydown()
{
u16 a=0;
key=0x0f;
if(key!=0x0f)
{
delay(1000);
if(key!=0x0f)
{
key=0x0f;
switch(key)
{
case 0x07 : keyvalue=0;break;
case 0x0b : keyvalue=1;break;
case 0x0d : keyvalue=2;break;
case 0x0e : keyvalue=3;break;
}
key=0xf0;
switch(key)
{
case 0x70 : keyvalue=keyvalue;break;
case 0xb0 : keyvalue=keyvalue+4;break;
case 0xd0 : keyvalue=keyvalue+8;break;
case 0xe0 : keyvalue=keyvalue+12;break;
}
if((keyvalue>=0)&&(keyvalue<=9))
{
keyswitch=1;
}
while((a<100)&&(key!=0xf0))
{
delay(1000);
a++;
}
}
}
}
2. 数码管
//输出函数(把值显示在数码管上)
void display()
{
LSA=0;LSB=0;LSC=0;
dig=duanxuan[box[0]];
delay(50);dig=0x00; //使动态数码管消隐
LSA=1;LSB=0;LSC=0;
dig=duanxuan[box[1]];
delay(50);dig=0x00; //使动态数码管消隐
LSA=0;LSB=1;LSC=0;
dig=duanxuan[box[2]];
delay(50);dig=0x00; //使动态数码管消隐
LSA=1;LSB=1;LSC=0;
dig=duanxuan[box[3]];
delay(50);dig=0x00; //使动态数码管消隐
LSA=0;LSB=0;LSC=1;
dig=duanxuan[box[4]];
delay(50);dig=0x00; //使动态数码管消隐
LSA=1;LSB=0;LSC=1;
dig=duanxuan[box[5]];
delay(50);dig=0x00; //使动态数码管消隐
LSA=0;LSB=1;LSC=1;
dig=duanxuan[box[6]];
delay(50);dig=0x00; //使动态数码管消隐
LSA=1;LSB=1;LSC=1;
dig=duanxuan[box[7]];
delay(50);dig=0x00; //使动态数码管消隐
}
3. “+ - x ÷”算法设计
if(keyswitch==1)
{
for(i=7;i>0;i--)
{
box[i]=box[i-1]; //使按键输入的数字从左至右显示在数码管上
}
box[0]=keyvalue;
keyswitch=0;
}
else if(keyvalue==14) //按键14为清零键
{
for(i=0;i<8;i++)
box[i]=0;
display();
}
else if(keyvalue==10) //按键10为加号‘+’
{
a=box[0]+box[1]*10+box[2]*100+box[3]*1000+box[4]*10000+box[5]*100000+box[6]*1000000+box[7]*10000000;
//使按下的第一个数的值存入a;
for(i=0;i<8;i++)
box[i]=0;
while(1)
{
display();
keydown();
if(keyswitch==1)
{
for(i=7;i>0;i--)
{
box[i]=box[i-1];
}
box[0]=keyvalue;
keyswitch=0;
}
if(keyvalue==15) //按键15为等于符号'='
break;
}
b=box[0]+box[1]*10+box[2]*100+box[3]*1000+box[4]*10000+box[5]*100000+box[6]*1000000+box[7]*10000000;
//使按下的第二个数的值存入b;
c=a+b;
box[0]=c%10;
box[1]=c/10%10;
box[2]=c/100%10;
box[3]=c/1000%10;
box[4]=c/10000%10;
box[5]=c/100000%10;
box[6]=c/1000000%10;
box[7]=c/10000000%10;
display();
}
else if(keyvalue==11) //按键11为加号‘-’
{
a=box[0]+box[1]*10+box[2]*100+box[3]*1000+box[4]*10000+box[5]*100000+box[6]*1000000+box[7]*10000000;
//使按下的第一个数的值存入a;
for(i=0;i<8;i++)
box[i]=0;
while(1)
{
display();
keydown();
if(keyswitch==1)
{
for(i=7;i>0;i--)
{
box[i]=box[i-1];
}
box[0]=keyvalue;
keyswitch=0;
}
if(keyvalue==15) //按键15为等于符号'='
break;
}
b=box[0]+box[1]*10+box[2]*100+box[3]*1000+box[4]*10000+box[5]*100000+box[6]*1000000+box[7]*10000000;
//使按下的第二个数的值存入b;
c=a-b;
box[0]=c%10;
box[1]=c/10%10;
box[2]=c/100%10;
box[3]=c/1000%10;
box[4]=c/10000%10;
box[5]=c/100000%10;
box[6]=c/1000000%10;
box[7]=c/10000000%10;
display();
}
else if(keyvalue==12) //按键12为加号‘*’
{
a=box[0]+box[1]*10+box[2]*100+box[3]*1000+box[4]*10000+box[5]*100000+box[6]*1000000+box[7]*10000000;
//使按下的第一个数的值存入a;
for(i=0;i<8;i++)
box[i]=0;
while(1)
{
display();
keydown();
if(keyswitch==1)
{
for(i=7;i>0;i--)
{
box[i]=box[i-1];
}
box[0]=keyvalue;
keyswitch=0;
}
if(keyvalue==15) //按键15为等于符号'='
break;
}
b=box[0]+box[1]*10+box[2]*100+box[3]*1000+box[4]*10000+box[5]*100000+box[6]*1000000+box[7]*10000000;
//使按下的第二个数的值存入b;
c=a*b;
box[0]=c%10;
box[1]=c/10%10;
box[2]=c/100%10;
box[3]=c/1000%10;
box[4]=c/10000%10;
box[5]=c/100000%10;
box[6]=c/1000000%10;
box[7]=c/10000000%10;
display();
}
else if(keyvalue==13) //按键13为加号‘/’
{
a=box[0]+box[1]*10+box[2]*100+box[3]*1000+box[4]*10000+box[5]*100000+box[6]*1000000+box[7]*10000000;
//使按下的第一个数的值存入a;
for(i=0;i<8;i++)
box[i]=0;
while(1)
{
display();
keydown();
if(keyswitch==1)
{
for(i=7;i>0;i--)
{
box[i]=box[i-1];
}
box[0]=keyvalue;
keyswitch=0;
}
if(keyvalue==15) //按键15为等于符号'='
break;
}
b=box[0]+box[1]*10+box[2]*100+box[3]*1000+box[4]*10000+box[5]*100000+box[6]*1000000+box[7]*10000000;
//使按下的第二个数的值存入b;
c=a/b;
box[0]=c%10;
box[1]=c/10%10;
box[2]=c/100%10;
box[3]=c/1000%10;
box[4]=c/10000%10;
box[5]=c/100000%10;
box[6]=c/1000000%10;
box[7]=c/10000000%10;
display();
}
4. main函数
void main()
{
unsigned long a=0,b=0,c=0;
while(1)
{
display();
keydown();
}
}
总结
以上是对矩阵按键和数码管的简单应用,实现了市场上那些计算器的基本功能,其在该基础上还可以进行延伸,比如你可以将独立按键也应用过来,实现数码管上每个位上的数做修改处理,加减清零等简单操作,其他具体应用就交给你们啦!