
//电路说明如下。
//单片机:使用51系列兼容的即可;
//4×4矩阵键盘:接在P1口;
//两位数码显示器: P0口输出七段码,P2口输出位选码。
//==============================================================
//C语言程序如下。
/
文件名: KEY_LEDc
功能 : 对4×4矩阵键盘进行输出,在数码管后两位显示按键值。
/
#include <reg51h>
#include <intrinsh>
#define uint unsigned int
#define uchar unsigned char
//uchar code table[10] = {0x03, 0x9f, 0x25, 0x0d, 0x99, 0x49, 0x41, 0x1f, 0x01, 0x09};
uchar code table[10] = {0xC0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};
/
名称 : Delay_1ms()
功能 : 延时子程序,延时时间为 1ms x
输入 : x (延时一毫秒的个数)
输出 : 无
/
void Delay_1ms(uint x)
{
uint i;
uchar j;
for(i = 0; i < x; i++) for(j = 0; j <= 148; j++);
}
/
名称: Keyscan()
功能: P1外接4×4按键, 按照扫描法读出键值
输出: 按键值0~15/如无键按下, 返回16
/
uchar Keyscan(void)
{
uchar i, j, temp, Buffer[4] = {0xef, 0xdf, 0xbf, 0x7f};
for(j = 0; j < 4; j++) { //循环四次
P1 = Buffer[j]; //在P1高四位分别输出一个低电平
temp = 0x01; //计划先判断P10位
for(i = 0; i < 4; i++) { //循环四次
if(!(P1 & temp)) //从P1低四位,截取1位
return (i + j 4); //返回取得的按键值
temp <<= 1; //判断的位,左移一位
} }
return 16; //判断结束,没有键按下,返回16
} //呵呵,实质性的语句不过8行,就是这么简练!
/
名称: Display(uchar k)
功能: 将参数分成十位、个位分别显示
输入: k (键盘数值)
输出: P0口输出七段码,P2口输出位选码
/
void Display(uchar k)
{
P2 = 0; //消隐
P0 = table[k / 10];
P2 = 0x02; Delay_1ms(5); //显示5ms十位
P2 = 0; //消隐
P0 = table[k % 10];
P2 = 0x01; Delay_1ms(5); //显示5ms个位
}
/
名称 : Main()
功能 : 主函数
/
void Main(void)
{
uchar Key_Value = 16, Key_Temp1, Key_Temp2; //两次读出的键值
while(1) {
//---------以下读入按键、消掉、等待按键释放
P1 = 0xff;
Key_Temp1 = Keyscan(); //先读入按键
if(Key_Temp1 != 16) { //如果有键按下
//Delay_1ms(10); //延时一下
Display(Key_Value); //可用显示代替延时
Key_Temp2 = Keyscan(); //再读一次按键
if (Key_Temp1 == Key_Temp2)//必须是两次相等
Key_Value = Key_Temp1; //才保存下来,这就是消除抖动
while(Keyscan() <= 16); //等待按键释放
}
//---------以下是对按键的处理
Display(Key_Value); //显示键值
}
}
首先要明确流程,例如:
单片机开机,设置定时器(10s),数码管显示为零,当按下A按键时开始计时,再在10s内多次按下B键,每按一次由中断程序记录次数并在数码管显示,10s后关闭计数功能,当再次按下A按键时,数码管显示再次为零
4X4太浪费单片机IO了,我为你介绍5个IO控制25按键的电路,如下文:
本文以循序渐进的思路,引导大家思考如何用最少的IO驱动更多的按键,并依次给出5种方案原理图提供参考。在实际项目中我们经常会遇到有按键输入的需求,但有的时候为了节省资源成本,我们都会选择在不增加硬件的情况下使用最少的控制器IO驱动更多的按键,那么具体是怎么做的呢,下面我们就以用5个IO引脚为例,讲下怎么设计可以实现更多的按键?共有5种设计思路,下面依次介绍。
思路一首先通常想到的可能是下面这样的设计:
上图形式的按键就是我们通常说的行列式按键,它的驱动思路是这样的:
1 对IO1、2、3配置为推挽输出,依次只让其中一个输出为0其他输出为1。
2 对IO4、5进行读 *** 作,根据读出的结果判断哪个按键按下。
例如:配置IO1、2、3为011,读IO4、5,若IO4为0则SW14按下,若IO5为0则SW15按下;
依次的配置IO1、2、3为101,读IO4、5,若IO4为0则SW24按下,若IO5为0则SW25按下;
依次的配置IO1、2、3为110,读IO4、5,若IO4为0则SW34按下,若IO5为0则SW35按下;
思路二但是我们在不知道行列式按键之前我们肯定是依次将IO口接一个按键到GND或者到VCC,然后去读IO口去判断哪个按键按下,这也是最简单的方法,但是很浪费IO口,下面这种就结合了这种简单方法和行列式的思路,实现了又多增加3个按键,如下图:
这里我们的思路是先依次读IO1、2、3的电平来识别S1、2、3,哪个按键按下,其后的流程和思路一是一样的,这样就可以识别11个按键了。
思路三按照扫描的思想,某一时刻设置一个IO口为0,其他IO口读,如果有IO口读到0,则有对应按键按下。比如IO1为0,然后读到IO5也为0,那么K15就是按下的。对照这样的思路,我们可以有下面的设计:
这个电路按键识别思路是这样的:
1 只配置IO1为0,其他IO读,若IO5读到0,则K15按下,若IO4读到0,则K14按下,依次识别K13,K12;
2 只配置IO2为0,其他IO读,若IO5读到0,则K25按下,若IO4读到0,则K24按下,依次识别K23;
3 只配置IO3为0,其他IO读,若IO5读到0,则K35按下,若IO4读到0,则K34按下;
4 只配置IO4为0,其他IO读,若IO5读到0,则K45按下;
思路四对于思路3我们发现,如果只配置IO5为0,其他IO读,若IO1读到0,则K15按下,若IO2读到0,则K25按下,依次可识别K35和K45。这样就存在重复,那么有么有好的方法,解决这样的重复呢?我们发现,若配置IO1为0,K15按下,电流流向IO1的,若配置IO5为0,同样K15按下,电流是流向IO5的。这样我们就可以通过区分电流的流向来避免重复。于是就有了下图的设计:
这样就可以避免重复,IO5为0时,按K15,IO1是读不到0的。那么怎样设计,IO5为0时对应一个按键按下IO1为0呢?如是就有人想到下面的设计:
这个电路按键识别思路是这样的:
1 只配置IO1为0,其他IO读,若IO5读到0,则K51按下,若IO4读到0,则K41按下,依次识别K31,K21;
2 只配置IO2为0,其他IO读,若IO5读到0,则K52按下,若IO4读到0,则K42按下,依次识别K32,K21';
3 只配置IO3为0,其他IO读,若IO5读到0,则K53按下,若IO4读到0,则K43按下,依次识别K32’,K31';
4 只配置IO4为0,其他IO读,若IO5读到0,则K54按下,若IO4读到0,则K43’按下,依次识别K42’,K41';
5 只配置IO5为0,其他IO读,若IO4读到0,则K54’按下,若IO3读到0,则K53’按下,依次识别K52’,K51'。
思路五很多人可能认为思路四已经识别20个按键了,但是真的就没有其他方法了吗?不要忘了,我们还没有将思路二你介绍的那种最简单的方法结合进去,于是又可以多5个按键,如下图:
这样我们可以先识别K01、K02、K03、K04、K05,若没有按键按下然后再和思路四的设计一样去识别其他按键。但这样存在一个问题,如果IO1配置为0,IO5读到0,那么怎么知道是K51按下还是K05按下呢,这里只需要在程序里做下判断,先判断下是不是K05按下,若不是就是K51,因为按键K01、K02、K03、K04、K05在5个IO口都为读取的情况下,就可以识别,不需要扫描识别处理,相当于这5个按键优先级高与其他按键。
总结综合上述,5个IO口最多可以识别25个按键,思路五程序上处理比较麻烦,若实际中只按思路四设计,也可识别20个按键,那么如果有N个IO口可识别多少按键呢?这里给出如下公式:
假设有N个IO口按照思路三可以识别N(N-1)/2个;
按照思路四可识别N(N-1)个;
按照思路5可以识别N(N-1)+N个。
最后再说下,如果实际设计时,还是按思路四设计好,软件也没那么麻烦。如果是你的话你会选择哪种方法呢?你还有没有其他的设计方法呢?
#include <reg51h>
#include <intrinsh>
sbit SPK=P3^4; //SPK定义为P3口的第4位,就是驱动蜂鸣器的那个脚
sbit JDQ=P3^5; //JDQ定义为P3口的第5位,就是驱动继电器的那个脚
code unsigned char table[]=
{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,
0x77,0x7c,0x39,0x5e,0x79,0x71};
//共阴数码管 0-9 a-f 表
code unsigned char key_tab[17]={0xed,0x7e,0x7d,0x7b,
0xbe,0xbd,0xbb,0xde,
0xdd,0xdb,0x77,0xb7,
0xee,0xd7,0xeb,0xe7,0XFF};//========================此数组为键盘编码,
//本人采用类式类似电话按键的编码方式,方便以后设计
// 1 2 3 a 0x01 0x02 0x03 0
// 4 5 6 b 对应16进制码: 0x04 0x05 0x06 0x0b
// 7 8 9 d 0x07 0x08 0x09 0x0d
// 0 # f 0x0c 0x00 0x0e 0x0f
//打个比方,如果你按下0键,P0口读到数据为0xed
//如果你按下2键,P0口读到数据为0x7d,按下9键为0xdb,
//我们将读到的P0口数据经过查表法就能得到相应的16进制码
//键盘的读取,我们采用分时扫描
unsigned char l_tmpdate[8]={0,0,0,0,0,0,0,0};//定义数组变量
unsigned char l_key=0x0; //定义变量,存放键值
unsigned char l_keyold=0xFF; //做为按键松开否的凭证
void ReadKey(void); //扫描键盘 获取键值
void delay();//延时子函数,5个空指令
void display(unsigned char lp,unsigned char lc);//数字的显示函数;lp为指向数组的地址,lc为显示的个数
//这个函数在第二节用过不用再说了吧!
void main(void) //入口函数
{
while(1){
ReadKey(); //调用键盘扫描
display(&l_key,1); //输出显示获取的键值码
if(l_key==0x0e) //这里我们检测是否按了0x0e键,
JDQ=0; //是,我们就驱动继电器打开
if(l_key==0x0c) //检测是否按下了0x0c键,
JDQ=1; //是,我们就驱动继电器断开
}
}
void ReadKey(void) //读键盘值
{
unsigned char i,j,key;
//分三个部份来理解,
//第一部份,用扫描来读取键盘,
j=0xfe;
key=0xff; //设定初值
for (i=0;i<4;i++){
P0=j; //P0口低4位循环输出0,扫描键盘
//leday();
if ((P0&0xf0)!=0xf0){ //如果有键按下,P0口高4位不会全为1,
key=P0; //读取P0口,退出循环,否则循环下次
break;
}
j=_crol_(j,1); //此函数功能为左循环移位
}
//第二部份,检测是否干扰或按键放开
if (key==0xff){ //如果读取不到P0口的值,比如是干扰或是键盘又松开,我们做相应复位,返回
l_keyold=0xff;
SPK=1; //按键有松开,停止蜂鸣器响
return;
}
else
SPK=0; //打开蜂鸣器
//第三部份,检测是新按键按下,获取新的键盘编码值
if(l_keyold!=key){ //检测按键放开否,如果一样表明没放开,不做处理,不一样表时另一个键按下做编码转换
l_keyold=key; //获取键码做为放开下次的凭证
for(i=0;i<17;i++){ //查表获得相应的16进制值存放l_key变量中
if (key==key_tab[i]){
l_key=i;
break;
}
}
}
//程序运行到这里,就表明有键值被读取存放于l_key变量中,主程序就可以检测此变量做相应外理,
//此时我们回到主程序
}
void display(unsigned char lp,unsigned char lc)//显示
{
unsigned char i; //定义变量
P2=0; //端口2为输出
P1=P1&0xF8; //将P1口的前3位输出0,对应138译门输入脚,全0为第一位数码管
for(i=0;i<lc;i++){ //循环显示
P2=table[lp[i]]; //查表法得到要显示数字的数码段
delay(); //延时5个空指令
if(i==7) //检测显示完8位否,完成直接退出,不让P1口再加1,否则进位影响到第四位数据
break;
P2=0; //清0端口,准备显示下位
P1++; //下一位数码管
}
}
void delay(void) //空5个指令
{
_nop_();_nop_();_nop_();_nop_();_nop_();
}
仔细读了一下程序,问题注在语句后
#include<reg51h>
void delay( int i,j) //此句有错,j变量没有声明类型,改为int i,int j。函数里不能这样写,函数体内可以。
{ for(i=100;i>0;i--)
for(j=0;j<100;j++); }
void matrixkeyscan()
{
char temp,key;
P3=0xfe;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(10); //此处函数delay的参数与上面不符,这里只有一个 参数
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0); //此处的分号去掉,否则起不到判断作用,下面的语句永远被执行
{
temp=P3;
switch(temp)
{
case 0xee:
key=0;
break;
case 0xde:
key=1;
break;
case 0xbe:
key=2;
break;
case 0x7e:
key=3;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
if(key==0) P1=0xfe;
if(key==1) P1=0xfd;
if(key==2) P1=0xfb;
if(key==3) P1=0xf7;}} }
P3=0xfd;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(10); //此处函数delay的参数与上面不符
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0); //此处的分号去掉
{
temp=P3;
switch(temp)
{
case 0xed: key=4; break;
case 0xdd:
key=5;
break;
case 0xbd:
key=6;
break;
case 0x7d:
key=7;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
if(key==4) P1=0xef;
if(key==5) P1=0xdf;
if(key==6) P1=0xbf;
if(key==7) P1=0x7f;} }}}
void main()
{ while(1)
{ matrixkeyscan(); }
}
其它的暂时未发现错误。改过后运行一下,有问题再联系。
不过你可以考虑精简一下程序,这样确实有些太繁琐了,万一需要修改软件容易发生遗漏。
1、键值可以采用查表的方法;
2、送LED可以单独做一个函数,把键值作为参数来传递。
3、LED送一次就行了,你的程序在等待按键抬起的过程中每次循环都判键值送显示,没必要
说得对别忘了选用哦,我可是用了心的^-^。
其实方法很多的,这只是简单的一种
#include<reg52h>
#define uchar unsigned char
#define uint unsigned int
#define KEYDOWN 1 //按键按下
#define KEYUP 0 //按键d开
#define led_date P0 //LED数据口
bit key_first = 1; //按键第一次按下
bit key_state = KEYUP; //按键状态
uchar key_value; //键值
code uchar LED[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,};
//LED段码 共阳管
void main(void);
void key_scan(void);
void key_scan()
{
uchar key_buff,key_date;
key_date = P1 & 0xFF;
if(key_date != 0xFF)
{
if(key_state != KEYDOWN)
{
if(key_first == 1)
{
key_buff = key_date;
key_first = 0;
}
else
{
if(key_date == key_buff)
{
key_value = ~key_date;
key_state = KEYDOWN;
}
}
}
}
else
{
key_first = 1;
key_state = KEYUP;
}
}
void main()
{
while(1)
{
key_scan();
switch(key_value)
{
case 0x01:
{
led_date = LED[1];
}break;
case 0x02:
{
led_date = LED[2];
}break;
case 0x04:
{
led_date = LED[3];
}break;
case 0x08:
{
led_date = LED[4];
}break;
case 0x10:
{
led_date = LED[5];
}break;
case 0x20:
{
led_date = LED[6];
}break;
case 0x40:
{
led_date = LED[7];
}break;
case 0x80:
{
led_date = LED[8];
}break;
default: break;
}
}
}
下面这个代码会短一些
#include<reg52h>
#define uchar unsigned char
#define uint unsigned int
#define KEYDOWN 1 //按键按下
#define KEYUP 0 //按键d开
#define led_date P0 //LED数据口
bit key_first = 1; //按键第一次按下
bit key_state = KEYUP; //按键状态
uchar key_value; //键值
code uchar LED[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,};
//LED段码 共阳管
void main(void);
void key_scan(void);
void key_scan()
{
uchar i,key_buff,key_date;
key_date = P1 & 0xFF;
if(key_date != 0xFF)
{
if(key_state != KEYDOWN)
{
if(key_first == 1)
{
key_buff = key_date;
key_first = 0;
}
else
{
if(key_date == key_buff)
{
for(i=0;i<8;i++)
{
if(!((key_date>>i)&0x01))
{
break;
}
}
key_value = LED[i+1];
key_state = KEYDOWN;
}
}
}
}
else
{
key_first = 1;
key_state = KEYUP;
}
}
void main()
{
while(1)
{
key_scan();
led_date = key_value;
}
}
以上就是关于关于一个c51单片机的4*4矩阵键盘扫描程序,我用keil运行,总是有错误,请各位帮帮忙,把错误给修改下。谢全部的内容,包括:关于一个c51单片机的4*4矩阵键盘扫描程序,我用keil运行,总是有错误,请各位帮帮忙,把错误给修改下。谢、在C51单片机中,通过单位时间如10秒内,按下键盘的次数来显示在数码管上,来模拟电机测速,请问程序怎么写、C51单片机用IO口做为几个按键的控制,现在想用4X4键盘控制,请问下面显示程序应该怎么改啊折磨死了等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)