一、原理部分
注:P36转P42,P37转P44,跳线帽连接1和2
按键状态扫描
按键有两个状态,一个是弹起态,一个是按下态。通过判断按键的状态持续的时间来确定按键是否按下或者弹起。设置一定的时间扫描一次按键的状态,如果按键的状态和之前的状态不一样就说明按键发生了状态的改变,过一段时间后,按键状态再次发生改变,则说明按键完成了一个按下又弹起的操作,此时就可以执行此按键对应的功能。用keysta[4][4]
来储存按键的当前,用keyback[4][4]
来储存按键上一个状态。通过扫描的方式判断keysta[i][j] != keyback[i][j]
,如果不相等则说明按键的状态发生了改变,此时再次判断keysta[i][j] != 0
,若不为零则说明按键弹起,即完成了一次按下又弹起的操作,即可执行对应的按键功能。注意,在判断完按键状态发生改变后应当把keysta的值赋值给keyback方便下一次的判断即keyback[i][j] = keysta[i][j];
unsigned char keysta[4][4] = {{1, 1, 1, 1}, //4*4矩阵按键的状态,1为抬起,0为按下
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}};
static unsigned char keyback[4][4] = {{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}};
行扫描和列扫描获取键值
定义数组keybuff[4][4]
全部为0xff相当于是给矩阵按键初始态。使用变量keyout从0到3的循环进行行扫描,通过switch语句实现每一个时刻只有一行是低电平。即完成了行扫描。后面的四行即列扫描。当keyout行被拉低的时候,此时按键按下,则此时对应的有且仅有一个列扫描的IO口会变为低电平,把此时列扫描获得的低电平左移到keybuff对应的位置。
假设理想状态下(无抖动)按键按下了1ms,扫描间隔也为1ms时,则对应keybuff对应的值会变为0xfe。同理如果按键按下了8ms或者更长,扫描间隔还是1ms时,则对应的keybuff对应的值就会变为0x00。实际情况中,按键有抖动,但是抖动的时间比较短,只需要判断在连续的某一段时间内,按键的状态是持续按下的即可完成消抖的动作。
假设现在有一个按键按下,设置的扫描间隔为1ms,设置消抖的时间为8ms,那么就需要对keybuff中的每一个数字进行判断是否等于0x00即keybuff[keyout][i] == 0x00
如果等于则对应的按键的确按下,如果不等于则说明按键没有按下。同理如果设置消抖时间为4ms则说明按键至少要按下4ms才是有效状态,即可以用语句(keybuff[keyout][i]&0x0f) == 0x00
来进行判断。与上0x0f是为了清除高四位的影响,因为4ms只需要判断第四位是否为0即可,如果按键按下的时间多于4ms,单纯的判断keybuff[keyout][i] == 0xf0
肯定是不行的。
确定按键按下后,即可以对keysta中对应的数字赋0,即说明按键按下。
同理判断按键弹起后,对keysta中对应的数字赋,即说明按键弹起。
sbit key_out_1 = P3^0;//行扫描
sbit key_out_2 = P3^1;
sbit key_out_3 = P3^2;
sbit key_out_4 = P3^3;
sbit key_in_1 = P4^4;//列扫描
sbit key_in_2 = P4^2;
sbit key_in_3 = P3^5;
sbit key_in_4 = P3^4;
void keyscan()
{
uchar i;
static uchar keyout = 0;
static uchar keybuff[4][4] = {{0xff,0xff,0xff,0xff},
{0xff,0xff,0xff,0xff},
{0xff,0xff,0xff,0xff},
{0xff,0xff,0xff,0xff}};
switch(keyout)
{
case 0:key_out_1 = 0;key_out_4 = 1;break;
case 1:key_out_2 = 0;key_out_1 = 1;break;
case 2:key_out_3 = 0;key_out_2 = 1;break;
case 3:key_out_4 = 0;key_out_3 = 1;break;
}
keybuff[keyout][0]=keybuff[keyout][0]<<1|key_in_1;
keybuff[keyout][1]=keybuff[keyout][1]<<1|key_in_2;
keybuff[keyout][2]=keybuff[keyout][2]<<1|key_in_3;
keybuff[keyout][3]=keybuff[keyout][3]<<1|key_in_4;
for(i=0;i<4;i++)
{
if((keybuff[keyout][i]&0x0f) == 0x00)
{
keysta[keyout][i] = 0;
}
else if((keybuff[keyout][i]&0x0f) == 0x0f)
{
keysta[keyout][i] = 1;
}
}
keyout++;
keyout &=0x03;
}
二、代码部分
实验平台:CT107D
实验芯片:stc15f2k60s2
实验现象:按下对应按键分别显示1-16。
代码如下
#include<stc15f2k60s2.h>
#define uchar unsigned char
#define uint unsigned int
sbit buzz = P0^6;
sbit key_out_1 = P3^0;//行扫描
sbit key_out_2 = P3^1;
sbit key_out_3 = P3^2;
sbit key_out_4 = P3^3;
sbit key_in_1 = P4^4;//列扫描
sbit key_in_2 = P4^2;
sbit key_in_3 = P3^5;
sbit key_in_4 = P3^4;
uchar code duan[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00};//定义段码数组
uchar disbuff[8];//定义显示数字数字
uchar keysta[4][4] = {{1, 1, 1, 1}, //4*4矩阵按键的状态,1为抬起,0为按下
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}};
uchar keymap[4][4] = {{1,2,3,4}, //按键值数组
{5,6,7,8},
{9,10,11,12},
{13,14,15,16}};
uchar index,num = 0;
uchar count = 0;
void keyscan()//循环扫描按键
{
uchar i;
static uchar keyout = 0;
static uchar keybuff[4][4] = {{0xff,0xff,0xff,0xff}, //按键初始态
{0xff,0xff,0xff,0xff},
{0xff,0xff,0xff,0xff},
{0xff,0xff,0xff,0xff}};
switch(keyout)//行扫描,保证每一个时刻只有一行为低电平
{
case 0:key_out_1 = 0;key_out_4 = 1;break;
case 1:key_out_2 = 0;key_out_1 = 1;break;
case 2:key_out_3 = 0;key_out_2 = 1;break;
case 3:key_out_4 = 0;key_out_3 = 1;break;
}
keybuff[keyout][0]=keybuff[keyout][0]<<1|key_in_1;//把读取到的行扫描的IO口电平移位到keybuff对应的数中
keybuff[keyout][1]=keybuff[keyout][1]<<1|key_in_2;
keybuff[keyout][2]=keybuff[keyout][2]<<1|key_in_3;
keybuff[keyout][3]=keybuff[keyout][3]<<1|key_in_4;
for(i=0;i<4;i++)
{
if((keybuff[keyout][i]&0x0f) == 0x00)//中断为1ms,此处为4ms消抖,判断按键是否按下
{
keysta[keyout][i] = 0;
}
else if((keybuff[keyout][i]&0x0f) == 0x0f)//中断为1ms,此处为4ms消抖,判断按键是否弹起
{
keysta[keyout][i] = 1;
}
}
keyout++;
keyout &=0x03;//对keyout进行清零
}
void keyfun(uchar key_val)//按键功能函数,可以用switch语句选择
{
num = key_val;
}
void keydrive()//放在主函数不断的判断按键状态是否改变
{
uchar i,j;
static uchar keyback[4][4] = {{1, 1, 1, 1},//备用状态数组
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}};
for(i=0;i<4;i++)
for(j=0;j<4;j++)
{
if(keysta[i][j] != keyback[i][j])
{
if(keysta[i][j] != 0)
{
keyfun(keymap[i][j]);//获取按键值传递给keyfun
}
} keyback[i][j] = keysta[i][j]; //更新状态,方便下一次判断
}
}
void closebuzz()
{
P2 = 0xa0;
buzz = 0;
P2 = 0x00;
}
void shownum()//显示数字函数
{
disbuff[0]=num/10;
disbuff[1]=num%10;
disbuff[2]=10;
disbuff[3]=10;
disbuff[4]=10;
disbuff[5]=10;
disbuff[6]=10;
disbuff[7]=10;
}
void display()//数码管扫描函数
{
P2 = 0xe0;
P0 = 0xff;
P2 = 0x00;
P2 = 0xc0;
P0 = 1<<index;
P2 = 0x00;
P2 = 0xe0;
P0 = ~duan[disbuff[index]];
P2 = 0x00;
index++;
index &= 0x07;
}
void Timer0Init(void) //1毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x9A; //设置定时初值
TH0 = 0xA9; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1 ;
}
void time0() interrupt 1
{
keyscan();
display();
}
void main()
{
closebuzz();
Timer0Init();
while(1)
{
shownum();
keydrive();
}
}
天地神仙
发布了10 篇原创文章 · 获赞 11 · 访问量 3631
私信
关注