/*加法器
一1先取键值(去抖动在中断,数码管显示在中断(读取缓存器))
二键值的处理在主函数
三处理的结果十六进制转十进制,并存入缓存器*/
#include <reg52.h>
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit keyin1 = P2^4;
sbit keyin2 = P2^5;
sbit keyin3 = P2^6;
sbit keyin4 = P2^7;
sbit keyout1 = P2^3;
sbit keyout2 = P2^2;
sbit keyout3 = P2^1;
sbit keyout4 = P2^0;
unsigned char code ledchar[]={0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E};
unsigned char ledbuf[6]={0xff,0xff,0xff,0xff,0xff,0xff}; //16进制转10金之后的计算后的结果送入这个缓存器 P0直接读取这个用于显示
/*矩阵按键到标准键盘的映射变 ASCLL表查询可知*/
unsigned char code keycodemap[4][4]={
{0x31,0x32,0x33,0x26}, //1 2 3 向上
{0x34,0x35,0x36,0x25}, //4 5 6 向左
{0x37,0x38,0x39,0x28}, //7 8 9 向下
{0x30,0x1B,0x0D,0x27}, //0 ESC 回车 向右
};
unsigned char keystate[4][4]={{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}}; //16个按键的当前键值 1代表的是这个按键是按下还是松开 最好用BIT 但是数组不允许
void keydrive();
void keyaction(unsigned char keycode);
void shownumber(unsigned long number);
void keydrive();
void ledscan(); //数码管显示函数
void keyscan(); //按键扫描函数
void main()
{
TMOD=0x01; //用定时器T0模式1
TH0=0XFC; //定时以为时间12/11059200,需要定时0.001S 需要
TL0=0X66;
EA=1;
ET0=1;
TR0=1;
ADDR3=1;
ENLED=0;
ledbuf[0]=ledchar[0];
/*按键当前状态刷出来后 取键值 */
while(1)
{
keydrive();
}
}
void shownumber(unsigned long num)
{
signed char i; /这里为什么不能用 unsigned char
unsigned char buf[6]; //待显示的数字
for(i=0;i<6;i++) //将数取出来放入数组
{
buf[i]=num%10;
num=num/10;
}
for(i=5;i>=1;i--)
{
if(buf[i]==0x00) //从最高位起,遇到0转换为空格,遇到非0则退出循环
{
ledbuf[i]=0xff;
}
else
{
break;
}
} //剩余低位都如实转换为数码管显示字符
for(;i>=0;i--)
{
ledbuf[i]=ledchar[buf[i]];
}
}
void keyaction(unsigned char keycode)
{
static unsigned long addend=0; //中间值
static unsigned long result=0; //结果值
if((keycode>=0x30)&&(keycode<=0x39)) {addend=((addend*10)+(keycode-0x30));shownumber(addend);}
else if(keycode == 0x26) {result=(addend+result);addend=0; shownumber(result);} // +
else if(keycode == 0x0D) {result=(addend+result);addend=0; shownumber(result);} //回车
else if(keycode == 0x1B) {result=0;addend=0; shownumber(addend);} //ESC
}
/*将按键(根据位置)取出来的值,根据映射表提取值,并且进行运算*/
void keydrive()
{
unsigned char i,j;
static unsigned char backup[4][4]={{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}}; //16个按键的上一个状态
for(i=0;i<4;i++)
{
for(j=0;j<4;j++)
{
if( keystate[i][j]!=backup[i][j]) //按键有动作
{
if(backup[i][j]!=0) //按键处于松开状态
{
keyaction(keycodemap[i][j]);
}
backup[i][j]=keystate[i][j];
}
}
}
}
void ledscan() //数码管扫描函数
{
static unsigned char i=0;
/*下面用处数码管的显示,读取缓存ledbuf的值*/
P0=0xff; //消影
switch(i) //进一次中断刷新一位
{
case 0: ADDR0=0; ADDR1=0; ADDR2=0; P0=ledbuf[0];i++;break;
case 1: ADDR0=1; ADDR1=0; ADDR2=0; P0=ledbuf[1];i++;break;
case 2: ADDR0=0; ADDR1=1; ADDR2=0; P0=ledbuf[2];i++;break;
case 3: ADDR0=1; ADDR1=1; ADDR2=0; P0=ledbuf[3];i++;break;
case 4: ADDR0=0; ADDR1=0; ADDR2=1; P0=ledbuf[4];i++;break;
case 5: ADDR0=1; ADDR1=0; ADDR2=1; P0=ledbuf[5];i=0;break;
default: break;
}
}
void keyscan() //按键扫描函数
{
unsigned char i;
static unsigned char keyout=0 ; //行索引
static unsigned char keytemp[4][4]={{0xff,0xff,0xff,0xff},{0xff,0xff,0xff,0xff},{0xff,0xff,0xff,0xff},{0xff,0xff,0xff,0xff}};
keytemp[keyout][0]=(keytemp[keyout][0]<<1)|keyin1; //扫完16个按键的第一位需要4MS 扫完8位需要32MS
keytemp[keyout][1]=(keytemp[keyout][1]<<1)|keyin2; //一个按键每隔4MS扫一次
keytemp[keyout][2]=(keytemp[keyout][2]<<1)|keyin3; // 8个状态是32MS之间的稳态值
keytemp[keyout][3]=(keytemp[keyout][3]<<1)|keyin4; //每进中断一次(1MS)扫描一行
for(i=0;i<4;i++)
{
if(( keytemp[keyout][i]&0x0f) == 0x00 ) {keystate[keyout][i]=0;}
else if( ( keytemp[keyout][i]&0x0f) == 0x0f ) {keystate[keyout][i]=1;} //扫描结果的赋值
}
keyout++; //下一次扫描下一行
keyout=keyout&0x03; //逢三复位
/*读取列值 行首先要置位0 为什么把置位放在后面 ? 1如果放在中断扫描前
马上就能扫描 而行的置位是需要一定时间的,就会导致列值不准确 2 如果放在后面
其实是给下一次行扫描的置位,经过了主程序的延迟 ,置位完成,程序才足够强壮*/
switch(keyout)
{
case 0: keyout4=1; keyout1=0; break;
case 1: keyout1=1; keyout2=0; break;
case 2: keyout2=1; keyout3=0; break;
case 3: keyout3=1; keyout4=0; break;
default: break;
}
}
void time0() interrupt 1 //中断中 行要置位0
{
TH0=0XFC; //定时以为时间12/11059200,需要定时0.001S 需要
TL0=0X66;
ledscan();
keyscan();
}
|