2019-03-02 17:44:08 Caoyang_He 阅读数 2628
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4001 人正在学习 去看看 朱有鹏

矩阵键盘的扫描大抵分为行扫式和反转式。

KeyPort为连接的端口

行扫描式的原理为:

Created with Raphaël 2.2.0行电位置高,列电位拉低去抖检测有无按下?扫描第一行是否按下?返回键值退出扫描第二行是否按下?返回键值退出扫描第三行是否按下?返回键值退出扫描第四行是否按下?返回键值退出返回0xffyesnoyesnoyesnoyesnoyesno

行扫描式代码

/*------------------------------------------------
        按键扫描函数,返回扫描键值
------------------------------------------------*/
unsigned char KeyScan(void)  //键盘扫描函数,使用行列逐级扫描法
{
 unsigned char Val;
 KeyPort=0xf0;//高四位置高,低四位拉低
 if(KeyPort!=0xf0)//表示有按键按下
   {
    DelayMs(10);  //去抖
	if(KeyPort!=0xf0)
	  {           //表示有按键按下
    	KeyPort=0xfe; //检测第一行
		if(KeyPort!=0xfe)
	  		{
			  Val=KeyPort&0xf0;
	  	      Val+=0x0e;
	  		  while(KeyPort!=0xfe);
			  DelayMs(10); //去抖
			  while(KeyPort!=0xfe);
	     	  return Val;
	        }
        KeyPort=0xfd; //检测第二行
		if(KeyPort!=0xfd)
	  		{
			  Val=KeyPort&0xf0;
	  	      Val+=0x0d;
	  		  while(KeyPort!=0xfd);
			  DelayMs(10); //去抖
			  while(KeyPort!=0xfd);
	     	  return Val;
	        }
    	KeyPort=0xfb; //检测第三行
		if(KeyPort!=0xfb)
	  		{
			  Val=KeyPort&0xf0;
	  	      Val+=0x0b;
	  		  while(KeyPort!=0xfb);
			  DelayMs(10); //去抖
			  while(KeyPort!=0xfb);
	     	  return Val;
	        }
    	KeyPort=0xf7; //检测第四行
		if(KeyPort!=0xf7)
	  		{
			  Val=KeyPort&0xf0;
	  	      Val+=0x07;
	  		  while(KeyPort!=0xf7);
			  DelayMs(10); //去抖
			  while(KeyPort!=0xf7);
	     	  return Val;
	        }
     }
   }
  return 0xff;
}
/*------------------------------------------------
         按键值处理函数,返回扫键值
------------------------------------------------*/
unsigned char KeyPro(void)
{
 switch(KeyScan())
 {
  case 0x7e:return 0;break;//0 按下相应的键显示相对应的码值
  case 0x7d:return 1;break;//1
  case 0x7b:return 2;break;//2
  case 0x77:return 3;break;//3
  case 0xbe:return 4;break;//4
  case 0xbd:return 5;break;//5
  case 0xbb:return 6;break;//6
  case 0xb7:return 7;break;//7
  case 0xde:return 8;break;//8
  case 0xdd:return 9;break;//9
  case 0xdb:return 10;break;//a
  case 0xd7:return 11;break;//b
  case 0xee:return 12;break;//c
  case 0xed:return 13;break;//d
  case 0xeb:return 14;break;//e
  case 0xe7:return 15;break;//f
  default:return 0xff;break;
 }
}

反转式原理

  1. 行线输出全为 0
  2. 读入列线值
  3. 列线输出上次读入的值
  4. 读入行线值
  5. 组合 2 种读入值

优点:m*n 个按键值需要一次反转(2 次输入输出)就可以检测到结果,比行列扫描简单方便。

反转式代码

/*------------------------------------------------
          按键扫描函数,返回扫描键值
------------------------------------------------*/
unsigned char KeyScan(void)  //键盘扫描函数,使用行列反转扫描法
{
 unsigned char cord_h,cord_l;//行列值中间变量
 KeyPort=0x0f;            //行线输出全为0
 cord_h=KeyPort&0x0f;     //读入列线值
 if(cord_h!=0x0f)    //先检测有无按键按下
 {
  DelayMs(10);        //去抖
  if((KeyPort&0x0f)!=0x0f)
  {
    cord_h=KeyPort&0x0f;  //读入列线值
    KeyPort=cord_h|0xf0;  //输出当前列线值
    cord_l=KeyPort&0xf0;  //读入行线值

    while((KeyPort&0xf0)!=0xf0);//等待松开并输出

    return(cord_h+cord_l);//键盘最后组合码值
   }
  }return(0xff);     //返回该值
}
/*------------------------------------------------
              按键值处理函数,返回扫键值
------------------------------------------------*/
unsigned char KeyPro(void)
{
 switch(KeyScan())
 {
  case 0x7e:return 0;break;//0 按下相应的键显示相对应的码值
  case 0x7d:return 1;break;//1
  case 0x7b:return 2;break;//2
  case 0x77:return 3;break;//3
  case 0xbe:return 4;break;//4
  case 0xbd:return 5;break;//5
  case 0xbb:return 6;break;//6
  case 0xb7:return 7;break;//7
  case 0xde:return 8;break;//8
  case 0xdd:return 9;break;//9
  case 0xdb:return 10;break;//a
  case 0xd7:return 11;break;//b
  case 0xee:return 12;break;//c
  case 0xed:return 13;break;//d
  case 0xeb:return 14;break;//e
  case 0xe7:return 15;break;//f
  default:return 0xff;break;
 }
}
2018-04-20 14:28:40 pang9998 阅读数 856
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4001 人正在学习 去看看 朱有鹏
开发环境(蓝色粗体字为特别注意内容)
1,开发板:STC12C5A60S2,3x3矩阵键盘。
2,开发环境:Keil uv5

3、参考文献:http://www.360doc.com/content/17/0829/12/2289804_682989833.shtml

一个项目中要用到矩阵键盘,之前只是用过4x4的矩阵键盘,本来想拿过来直接用,后来发现,3x3矩阵键盘就够了,用4x4矩阵键盘需要8个IO口(行4个列4个)有点浪费,其他IO口还有其他用处。

但这时候问题就来了,用3x3矩阵键盘的时候,需求是扫描行线列线时不能影响其余两个IO口,我的接线方式如下:

P1.0 <---> ADC
P1.1 <---> 行线1
P1.2 <---> 行线2
P1.3 <---> 行线3
P1.4 <---> 列线1
P1.5 <---> 列线2
P1.6 <---> 列线3
P1.7 <---> 其他

51单片机如何高效操作某几个IO口同时不影响其余的IO口呢?

想到了用位操作,试了多种操作方式,还是不妥。于是看到了文章前面参考文献,才恍然大悟。经过深入的思考,终于推算出一套有效的算法,具体算法如下:

P1(IO口)   0 1 2 3 4 5 6 7 
起始状态   ? ? ? ? ? ? ? ?   Hex:0x??
目标状态   ? 1 1 1 0 0 0 ?   "?"表示不改变的位
P1  & =    1 0 0 0 0 0 0 1   需要改变的位置为9,不需要改变的位为1,  Hex:0x81
    =      ? 0 0 0 0 0 0 ?
P1  | =    0 1 1 1 0 0 0 0   需要改变的位置为相应的数,不需要改变的位置为0
    =      ? 1 1 1 0 0 0 ?   成功输出预期的状态


以上是设置扫描行线状态,设置扫描列线状态原理也一样。这样经过巧妙的算法处理,就能够实现只改变IO口的某几位的状态,而不影响其他位,从而使得其余两个IO口不受矩阵键盘的影响,依然能够正常使用,能够节省宝贵的IO口资源。

根据以上的算法可以把矩阵键盘的驱动程序写成如下:

/*
键码i
230  214 182 
234  218  186
236  220  188
*/
	i=keyscan(1);
得到键码之后就可以根据具体情况进行处理,另外需要注意的一点是,如果你 不确定你的键码是多少,可以把该键码通过串口输出到上位机,这样来确定按键对应的键码。


51单片机3x3矩阵键盘驱动我已经打好包,放到文章后面,供需要的朋友下载。

点击下载51单片机3x3矩阵键盘驱动







2010-07-08 14:58:00 winmenaruto 阅读数 7587
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4001 人正在学习 去看看 朱有鹏

//4x4矩阵键盘扫描例程(C51)
//使用P0口上的LED灯显示4x4键盘扫描得到的键值
//
//广西民大物电学院 李映超 2010.4.19

#include <reg52.h>                    //52系列单片机头文件
#define uchar unsigned char
#define uint unsigned int
#define key_4x4_port P3                //定义4x4键盘使用的单片机端口
uchar key;                                        //4x4键盘扫描所得的键值保存到这里
void delayms(uint xms);        //声明延时子函数
void key_4x4_scan();    //声明4x4键盘扫描子函数,得到的键值送至全局变量key
//========================================================
void main()
{
        P0=0xff;  //P0开机初始化
        key=0xff; //键盘值开机初始化为ff(检测到的键值应为0----15)
  while(1)
  {
        key_4x4_scan();//不停调用键盘扫描程序
        P0=key;                //用P0来显示键值
  }
}
//========================================================
void delayms(uint xms)      //延时子函数                       
{        uint i,j;
        for(i=xms;i>0;i--)                      //i=xms即延时约xms毫秒
                for(j=110;j>0;j--);
}
//---------------------------------------------
void key_4x4_scan()                    //4x4键盘扫描子函数,得到的键值送至全局变量key
{        uchar temp ;
    key_4x4_port=0xfe;           //送出最高位0电平去扫描
    temp=key_4x4_port;           //读出整个口的得到的值
    temp=temp&0xf0;                   //屏蔽低4位
    if(temp!=0xf0)                   //假如高4位不是全1
    { delayms(10);                   //延时消抖再读
          temp=key_4x4_port;
          temp=temp&0xf0;
      if(temp!=0xf0)           //消抖后如果再次确定高4位不是全1
      { temp=key_4x4_port; //读出此次按键的值
        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=key_4x4_port;
           temp=temp&0xf0;
         }
      }
    }
    key_4x4_port=0xfd;
    temp=key_4x4_port;
    temp=temp&0xf0;
    if(temp!=0xf0)
    { delayms(10);
      temp=key_4x4_port;
      temp=temp&0xf0;
      if(temp!=0xf0)
      {        temp=key_4x4_port;
        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=key_4x4_port;
           temp=temp&0xf0;
         }
      }
      }
    key_4x4_port=0xfb;
    temp=key_4x4_port;
    temp=temp&0xf0;
    if(temp!=0xf0)
    { delayms(10);
             temp=key_4x4_port;
      temp=temp&0xf0;
      if(temp!=0xf0)
      { temp=key_4x4_port;
        switch(temp)
        { case 0xeb:
               key=8;  break;
          case 0xdb:
               key=9;  break;
          case 0xbb:
               key=10; break;
          case 0x7b:
               key=11; break;
         }
         while(temp!=0xf0)
         { temp=key_4x4_port;
           temp=temp&0xf0;
         }
      }
      }
    key_4x4_port=0xf7;
    temp=key_4x4_port;
    temp=temp&0xf0;
    if(temp!=0xf0)
    { delayms(10);
      temp=key_4x4_port;
      temp=temp&0xf0;
      if(temp!=0xf0)
      { temp=key_4x4_port;
        switch(temp)
        { case 0xe7:
               key=12; break;
          case 0xd7:
               key=13; break;
          case 0xb7:
               key=14; break;
          case 0x77:
               key=15; break;
         }
         while(temp!=0xf0)
         { temp=key_4x4_port;
           temp=temp&0xf0;
         }
      }
    }
}
//---------------------------------------------

2019-05-15 22:11:59 wangjiaweiwei 阅读数 895
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4001 人正在学习 去看看 朱有鹏

      独立键盘与单片机连接时,每一个按键都需要单片机的一个I/O口若某单片机系统需较多按键,如果用独立按键便会占用过多的I/O口资源。单片机系统中I/O口资源往往比较宝贵,当用到多个按键时为了节省I/O口口线,我们引入矩阵键盘。

      我们以4X4矩阵键盘为例讲解其工作原理和检测方法。将16个按键排成4行4列,第一行将每个按键的一端连接在一起构成行线,第一列将每个按键的另一端连接在一起构成列线,这样便一共有4行4列共8根线,我们将这8根线连接到单片机的8个I/O口上,通过程序扫描键盘就可检测16个键。用这种方法我们也可实现3行3列9个键、5行5列25个键、6行6列36个键等。

     无论是独立键盘还是矩阵键盘,单片机检测其是否被按下的依据都是一样的,也就是检测与该键对应的I/O口是否为低电平。独立键盘有一端固定为低电平,单片机写程序检测时比较方便。而矩阵键盘两端都与单片机I/O口相连,因此在检测时需人为通过单片机I/O口送出低电平。检测时,先送一列为低电平,其余几列全为高电平(此时我们确定了列数),然后立即轮流检测一次各行是否有低电平,若检测到某一行为低电平(这时我们又确定了行数),则我们便可确认当前被按下的键是哪一行哪一列的,用同样方法轮流送各列一次低电平,再轮流检测一次各行是否变为低电平,这样即可检测完所有的按键,当有键被按下时便可判断出按下的键是哪一个键。当然我们也可以将行线置低电平,扫描列是否有低电平。这就是矩阵键盘检测的原理和方法。

    首先看一下电路图

    

      上图是一个4X4 的矩阵键盘,一共是16 个按键。我们照习惯称横为“行”,“竖”为列。那么5、6、7、8 我们称之为“行线”,则1、2、3、4 称为“列线”。要正确记住各个行列线各自对应的IO。注意看,每一个按键的两端,都分别接在某一个列线和行线上,即:“行线和列线是通过某个按键的按下和抬起实现联通和断开的”,和“导线两端上的信号是经过“与”的关系再体现到导线上的。”这两句话便构成了矩阵键盘扫描的全部。要理解好,理解不了就背下来。

现在详细讲述一下矩阵键盘扫描的原理和步骤:

    

矩阵键盘扫描原理
方法一:
             逐行扫描:我们可以通过高四位轮流输出低电平来对矩阵键盘进行逐行扫描,当低四位接收到的数据不全为1的时候,说明有按键按下,然后通过接收到的数据是哪一位为0来判断是哪一个按键被按下。
方法二:
             行列扫描:我们可以通过高四位全部输出低电平,低四位输出高电平。当接收到的数据,低四位不全为高电平时,说明有按键按下,然后通过接收的数据值,判断是哪一列有按键按下,然后再反过来,高四位输出高电平,低四位输出低电平,然后根据接收到的高四位的值判断是那一行有按键按下,这样就能够确定是哪一个按键按下了。
 

   接下来举一个矩阵键盘的程序例子,该例子使用了P0、P1两组IO口,P1用来检测矩阵键盘中是否有按键按下,P0用来驱动静态数码管的显示。

   以下是示例程序:

 

#include "reg52.h"

   typedef unsigned char u8;
   typedef unsigned int u16;

   #define GPIO_DIG P0
   #define GPIO_KEY P1

   u8 code smgduan[16]= {0x3f, 0x06, 0x5b, 0x4f,
                    0x66, 0x6d, 0x7d, 0x07,
					0x7f, 0x6f, 0x77, 0x7c,
					0x39, 0x5e, 0x79, 0x71};//静态数码管码值

  u8 KeyColValue;
  u8 KeyLineValue;

  void delay(u16 i) //延时函数
  {
   while(i --);
  }

  void KeyDown() //键盘按键扫描函数
  {

  char a;

  GPIO_KEY = 0x0f;
  if(GPIO_KEY != 0x0f)//检测4行中哪一行按键是否按下
  {
   delay(1000); //延时消抖
   if(GPIO_KEY != 0x0f) //再次检测4行中哪一行按键是否按下
   {
   switch(GPIO_KEY) //根据IO的值来确定哪一行按键按下
   {
   case(0x07): KeyColValue = 0; break;
   case(0x0b): KeyColValue = 1; break;
   case(0x0d): KeyColValue = 2; break;
   case(0x0e): KeyColValue = 3; break;
   }
   }
  }

  GPIO_KEY = 0xf0;
  if(GPIO_KEY != 0xf0) //检测4行中哪一列按键是否按下
  {
   delay(1000);  //延时消抖
   if(GPIO_KEY != 0xf0) //再次检测4行中哪一列按键是否按下
   {
   switch(GPIO_KEY) //根据IO的值来确定哪一列按键按下
   {
   case(0x70): KeyLineValue = 0; break;
   case(0xb0): KeyLineValue = 1; break;
   case(0xd0): KeyLineValue = 2; break;
   case(0xe0): KeyLineValue = 3; break;
   }
   }

   while((a < 50) && (GPIO_KEY != 0xf0))  //延时,确保没有按键再按下
  {
   delay(1000);
   a ++;
  }

  }

  

  }

   void main()
   {
   while(1)
   {
   KeyDown();//检测按键是否按下
   GPIO_DIG = ~smgduan[KeyLineValue*4 + KeyColValue];//根据按键的行列值,静态数码管显示相应的值
   }
   }

     

 

2019-08-09 14:57:17 qq_26106317 阅读数 226
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4001 人正在学习 去看看 朱有鹏

51单片机驱动 矩阵键盘原理及简单实现


原理:将P1赋值为0x0f, 如果某行被按下,P1.0 ~ P1.3 的某一个电平会被拉低,单片机就可以检查到电平变化,此时P1赋值为0xf0,再检测P1.4 ~ P1.7的电平,最后将前后值相或,得到的值就是等同于键盘某个键的“坐标”,例如以键盘左上角第一个键为坐标原点,键盘(0,0)的值为0000 1110 | 1110 0000 = 1110 1110,即0xee


贴代码:

/*

	4x4矩阵键盘驱动
	
	单片机:STC12C5A08S2  机械周期1T
	晶振:13.37Mhz左右
	功能:驱动4x4矩阵键盘
	
	原理:
		将P1赋值为0x0f, 如果某行被按下,P1.0 ~ P1.3 的某一个
		电平会被拉低,单片机就可以检查到电平变化,此时P1赋值
		为0xf0,再检测P1.4 ~ P1.7的电平,最后将前后值相或,得
		到的值就是等同于键盘某个键的“坐标”,例如以键盘左上角
		第一个键为坐标原点,键盘(0,0)的值为0000 1110 | 1110 0000 = 1110 1110,即0xee

	抖动处理:4x4矩阵键盘无法接外设去抖动,所以设计100ms延时进行抖动处理

	按键对应值:
		(0,0) -> 0xee		   (2,0) -> 0xeb
		(0,1) -> 0xde		   (2,1) -> 0xdb					 
		(0,2) -> 0xbe		   (2,2) -> 0xbb
		(0,3) -> 0x7e		   (2,3) -> 0x7b
		(1,0) -> 0xed		   (3,0) -> 0xe7
		(1,1) -> 0xdd		   (3,1) -> 0xd7
		(1,2) -> 0xbd		   (3,2) -> 0xb7
		(1,3) -> 0x7d		   (3,3) -> 0x77

*/

#include " stc12c5a.h "		 //	 stc12c5a系列单片机头文件,stc-isp有提供
#include < intrins.h >

unsigned char Data;
sbit po20 = P2^0;

void delay100ms(){		   //延时100ms
	unsigned int i;
	unsigned char k;
	for ( i = 0; i < 14285; i++ ){
		for ( k = 0; k < 10; k++ ){
			_nop_();
		}
	} 
}

void FxFInit(){			//4x4键盘初始化

	P1 = 0x0f;			//0000 1111	
	P2 = 0x01;			//0000 0001	  我习惯将P2口做测试口
	
}

void judge(){	   //判断按键

	switch( Data ){
	/*可按照上面的对照表添加case*/
		case 0xee:
			po20 = ~po20;
			break;
		default:
			break;
	}

}

void main(){
	FxFInit();						   

	while(1){
		if(P1 != 0x0f){	  //0x0f是初始化的值,如果不等于这个值,代表用户按下4行按键的某一行
			delay100ms();	  //延时100ms
			if(P1 != 0x0f){		//延时检测,去抖动
				Data = P1;	  //检测是哪行被按下,并存入信息
				P1 = 0xf0;	  //0000 1111	-> 	1111 0000
				Data |= P1;	  //Data 与P1值相或 存入Data   最终Data会有4 x 4 = 16种结果,对应4x4键盘的每一位

				judge();
				P1 = 0x0f;	   //重新赋值
			}
		}
	}
}
没有更多推荐了,返回首页