2013-05-17 23:09:52 yunwen3344 阅读数 881
  • 单片机控制第一个外设-LED灯-第1季第6部分

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

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

1.  51单片机的P1、P2、P3口自有上拉电阻,P0口要想当做I/O口使用,必须也加个上拉电阻。

2.  如果用51单片机的I/O口用于输入,必须先将I/O口输出1,即置1。

3.  单片机组成的系统中,用的最多的是非编码键盘,其又分为独立式非编码键盘和行列式非编码键盘。

4.  数字电路中线与、线或的概念:


如上图,线与两端电平做与运算,即当一端为高电平,另一端为低电平时,开关闭合,整个电路为低电平。

线或,Z为高阻态,当另一端为高电平时,开关闭后,整个电路为高电平;若另一端为低电平,开关闭后,整个电路为低电平。

5.   按键消抖(软件方法和物理方法),本文只介绍软件方法:按键按下的实际波形如下图


    整个按键过程大约20ms,我们利用软件只在按下抖动期间检测一次,若确实有按键按下,则延时5ms,此时到了稳定闭合阶段,接着再检测,还是低电平,则确实有按键按下;检测到按键释放时,同样要延迟5ms后再次检测,此次仍是检测到释放时则真的是释放了。

2014-11-09 12:53:52 u011984088 阅读数 507
  • 单片机控制第一个外设-LED灯-第1季第6部分

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

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

一、理论

a) 为什么出现矩阵式键盘

    在实际开发过程中,使用的按键太多时,为了减少I/O口的占用,这个时候我们采取将按键排列成矩阵形式。

b) 矩阵键盘的原理图如下图

    

     在每条水平线和垂直线的交叉处不直接连通,而是通过一个减价加以连接。这一 

     个端口就可以构成4 * 4 = 16个的按键。

 

c) 用行扫描法识别矩阵式键盘的按键,两步走:

i. 判断有没有按键按下:

           1. 将全部行线置低电平;

           2. 检测列线的状态:

              a) 只要有一列的电平为低,则表示有键被按下,而且闭合键位于低电平与4根线相交叉的4个键之中。

               b) 所有的列线均为高电平时,表示没有键按下。

ii. 判断闭合键所在的位置:

           1. 依次将行线置为低电平,也就是说在某根航线为低电平时,其他线为高电平。一直到确定到某根行线。

            2. 依次检测个列线的电平状态,如果某列为低,则该列线与置为低电平的行线交叉处的按键就是闭合的按键。


其实找这个按键的过程,就像在二维平面上确定点。

1、确定这点的横坐标:行的位置。

2、确定这点的纵坐标:列的位置。

3、找到该点:键值 行号 列数 列号。


二、代码:

//P2:0~3 行 4~7列

unsigned char key_scan(void)
{
    unsigned char vkey;
    unsigned char kdata;
    unsigned char key_number;
    bit key_flage = 0;

    P2 = 0xf0; //行线置0

    kdata = P2;
    kdata = kdata & 0xf0;

    printf("loop3\n");

    if(0xf0 == kdata)
    {

        printf("loop kdata:%d\n",kdata);
        return 0xff;
    }
    else
    {
        delay(30);

        kdata = 0xfe;

        printf("loop5 kdata:%d\n",kdata);

        while(!key_flage)
        {
            vkey = P2 = kdata;

            kdata = P2;
            kdata = kdata & 0xf0;

            if(0xf0 == kdata)
            {
                
                kdata = vkey;
                kdata = (kdata << 1) | 0x01;
                printf("loop6 kata << 1:%d\n",kdata);                     
            }
            else
            {
                
                kdata = (kdata >> 4) ^ 0x0f;
                printf("loop7 kdata %d\n", kdata);

                switch(kdata)
                {
                    case 1: key_number = 0; break;
                    case 2: key_number = 1; break;
                    case 4: key_number = 2; break;
                    case 8: key_number = 3; break;
                }
                 printf("loop7 key_number %d\n", key_number);
                key_flage = 1;
            }//end else 0xf0 == kdata;
        }//end whle(key_flage)

        vkey = vkey ^ 0x0f;
        printf("loop8 vkey %d\n",vkey);

        switch(vkey)
        {
             case 1: key_number += 0; break;
             case 2: key_number += 4; break;
             case 4: key_number += 8; break;
             case 8: key_number += 12; break;
        }
    }//end else (key_flage)

    do
    {
        printf("loop7 key_number %d\n", key_number);
         kdata = P2;
        kdata = kdata & 0xf0;
    }while( 0xf0 != kdata);

    return key_number;
}

注:如有雷同,纯属巧合!

2019-11-30 16:10:59 qq_37631068 阅读数 31
  • 单片机控制第一个外设-LED灯-第1季第6部分

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

    4006 人正在学习 去看看 朱有鹏
  1. 实验目的和任务

目的:利用“模块化单片机教学实验平台”,加深对单片机与矩阵键盘的接口电路设计以及程序设计的理解。

任务:1、学习独立式按键的查询识别方法。

2、非编码矩阵键盘的行反转法识别方法。

3、掌握键盘接口的基本特点,了解独立键盘和矩阵键盘的应用方法。

4、掌握键盘接口的硬件设计方法,软件程序设计和贴士排错能力。

5、掌握利用Keil51软件对程序进行编译。

6、会根据实际功能,正确选择单片机功能接线,编制正确程序。对实验结果

  1. 实验原理

1、MCS51系列单片机的P0~P3口作为输入端口使用时必须先向端口写入“1”。

2、用查询方式检测按键时,要加入延时(通常采用软件延时10~20mS)以消除抖动。

3、识别键的闭合,通常采用行扫描法和行反转法。行扫描法是使键盘上某一行线为低电平,而其余行接高电平,然后读取列值,如读列值中某位为低电平,表明有键按下,否则扫描下一行,直到扫完所有行。

行反转法识别闭合键时,要将行线接一并行口,先让它工作在输出方式,将列线也接到一个并行口,先让它工作于输入方式,程序使CPU通过输出端口在各行线上全部送低电平,然后读入列线值,如此时有某键被按下,则必定会使某一列线值为0。然后,程序对两个并行端口进行方式设置,使行线工作于输入方式,列线工作于输出方式,并将刚才读得的列线值从列线所接的并行端口输出,再读取行线上输入值,那么,在闭合键所在行线上的值必定为0。这样,当一个键被接下时,必定可以读得一对唯一的行线值和列线值。由于51单片机的并口能够动态地改变输入输出方式,因此,矩阵键盘采用行反转法识别最为简便。

行反转法识别按键的过程是:首先,将4个行线作为输出,将其全部置0,4个列线作为输入,将其全部置1,也就是向P1口写入0xF0;假如此时没有人按键,从P1口读出的值应仍为0xF0;假如此时1、4、7、0四个键中有一个键被按下,则P1.6被拉低,从P1口读出的值为0xB0;为了确定是这四个键中哪一个被按下,可将刚才从P1口读出的数的低四位置1后再写入P1口,即将0xBF写入P1口,使P1.6为低,其余均为高,若此时被按下的键是“4”,则P1.1被拉低,从P1口读出的值为0xBE;这样,当只有一个键被按下时,每一个键只有唯一的反转码,事先为12个键的反转码建一个表,通过查表就可知道是哪个键被按下了。

  1. 实验内容

使用扫描法通过矩阵键盘(2行4列,一共八个按键)控制8个LED灯的点亮。按下第一行第一列的按键使LED1点亮,其他LED熄灭;按下第一行第二列的按键使LED2点亮,其他LED熄灭;按下第一行第三列的按键使LED3点亮,其他LED熄灭;按下第一行第四列的按键使LED4点亮,其他LED熄灭;按下第二行第一列的按键使LED5点亮,其他LED熄灭;按下第二行第二列的按键使LED6点亮,其他LED熄灭;按下第二行第三列的按键使LED7点亮,其他LED熄灭;按下第二行第四列的按键使LED8点亮,其他LED熄灭。

图1 LED电路图

图2 按键电路图

注:把拨码开关(DIP)拨到断开的一侧。该矩阵键盘只用到前两行,即2*4的矩阵键盘。

表1 硬件连线表

CPU_CORE_51

MAIN_BOARD

P2:P1.0-P1.7

J14:LINE0-COLUM3

P3:P2.0-P2.7

J48:LED1-LED8

注意:实验箱的AT89S52单片机的晶振频率为11.0592MHz!

  1. 实验过程和结果

①关掉实验电源。将CPU 板插接在JK1、JK2 上,注意CPU 板的插接方向。按照前面连接关系表将硬件连接好。

②将AT89S52 芯片插在CPU 板的CPU 插座上(注意不要插反)。母板上电。

③运行Keil 开发环境,按照“模块化单片机教学实验平台配套的实验指导书1.2.3 节”介绍的方法建立工程exp11.uV2,CPU 为AT89S52,不包含启动文件STARTUP.A51。

④编写汇编语言源程序exp11.asm 完成基本实验的功能。把源程序exp11.asm加入到工程exp11.uV2,并设置工程exp11.uV2 属性,将其晶振频率设置为11.0592MHz,选择输出可执行文件。

⑤构造(Build)工程exp11.uV2。

⑥通过PROGISP 烧写软件,利用ISP 下载线(见“模块化单片机教学实验平台配套的实验指导书1.3 节”)把HEX 文件下载到单片机中。

⑦下载成功后,通过按下不同按键,观察LED1~7 的亮灭。

图3 实验结果图

  1. 实验心得

本次实验需要先判别整个键盘有无按键按下,从第0列开始逐列检测检测,可以检测出哪个按键被按下。然后用到去除按键的抖动。该实验难度较高,相比以往实验更考验综合能力。如何使用扫线法判断所在行是实验的难点所在。实验中因为对扫线法的理解不深入,在实验中没能完成实验。试验后仔细阅读书上的解释和题本提供的案例程序,我知道了自己实验失败的原因所在,并重新设计了程序和实验。通过学习后,对扫描键盘的程序更加了解,将理论上的结果和自己实验做的结果相印证,对书本上知识掌握的更加牢固,对代码程序有更加深刻的理解。在实验中,对模板的使用更加熟练,对电路的连接更加熟练。我对于矩阵键盘的工作方式有了更进一步的认识,也对扫描法查询矩阵键号的方法编写有了更进一步的掌握。单片机提高重在实践,想要学好单片机,软件编程必不可少。

  1. 附录(代码)

(1)基本实验

ORG 0000H

LJMP MAIN

MAIN:MOV P1,#11110000B;给p1口送数11110000

MOV A,P1       ;获取P1口的电平

XRL A,#11110000B

;将P1口电平与11110000异或运算

;若有按键按下,A不为0

JZ MAIN        ;无按键按键则返回

LCALL TIME_    ;跳转到延时程序

MOV P1,#11110000B

MOV A,P1

XRL A,#11110000B   ;该部分操作与上述操作相同

MOV R3,A       ;保存此时A的值

JZ MAIN          ;确认有按键按下

MOV R0,#11110111B;扫描法判断

MOV R1,#0      ;R1存储列的权值

I:    MOV P1,R0      ;将R0值传递给P1

MOV A,P1       ;读取P1的值

AAA0:JNB ACC.4,AAA1

;如果ACC的第四位(即第一列)不为零

MOV R1,#0      ;r1存0

LJMP OK     ;跳转到计算环节

AAA1: JNB ACC.5,AAA2 ;同上,判断第二列

MOV R1,#01

LJMP OK

AAA2:JNB ACC.6,AAA3  ;同上,判断第三列

MOV R1,#02

LJMP OK

AAA3: JNB ACC.7,AAA4 ;同上,判断第四列

MOV R1,#03

LJMP OK

AAA4:MOV A,R0

JNB ACC.2,MAIN ;判断是否结束

RR A        ;环移

MOV R0,A

MOV A,R1

MOV R1,A   

LJMP I

TIME_:MOV R0,#05

TT:   MOV R1,#250

DJNZ R1,$

DJNZ R0,TT

RET

OK:     ;判断所在行并送数

MOV A,R1

ADD A,R0

MOV R0,A

MOV A,#1

INC R0

AAA:CJNE R0,#0,BBB

RL A

INC A

LJMP AAA

BBB:MOV P2,A

LJMP MAIN

END

(2)扩展实验

ORG 0000H

LJMP MAIN

MAIN:MOV P1,#11110000B;送数11110000

MOV A,P1    ;取当前P1的值

XRL A,#11110000B;与11110000与或运算

JZ MAIN     ;如果为0则跳转回

LCALL TIME_ ;延迟子系统

MOV P1,#11110000B

MOV A,P1

XRL A,#11110000B

JZ MAIN     ;再次判断确定非误触

MOV R3,A    ;存储当前A信息

MOV P1,#00001111B  ;判断所在列

MOV A,P1

XRL A,#00001111B

MOV R4,A    ;存至R4

MOV A,R3

JNB ACC.1,NEXT1

MOV R3,#4;若为第二行则若R3存4,否则存0

LJMP NEXT2

NEXT1: MOV R3,#0;判断行,依次为0,1,2,3

NEXT2:MOV A,R4

JNB ACC.4,NEXT3

MOV R4,#0

LJMP NEXT7

NEXT3:JNB ACC.5,NEXT4

MOV R4,#1

LJMP NEXT7

NEXT4:JNB ACC.6,NEXT5

MOV R4,#2

LJMP NEXT7

NEXT5:MOV R4,#3

NEXT7:MOV A,R3

ADD A,R4      ;将两个权值相加

MOV P2,A      ;送至P2

LJMP MAIN

TIME_:MOV R0,#05

TT:   MOV R1,#250

DJNZ R1,$

DJNZ R0,TT

RET

END

2017-06-10 10:44:11 a514371309 阅读数 7938
  • 单片机控制第一个外设-LED灯-第1季第6部分

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

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

       所有的电子产品几乎到涉及到按键操作。所以微控制器是如何识别一个按键是否被按下,按下后又该如何做出反应,又如何防止按键抖动呢?更深入一点,微控制器又是如何识别矩阵键盘的?本文将详细阐述如何用C语言实现独立按键的检测和矩阵键盘操作。

完成本文所需硬件:基于C51系列单片机的开发板(本文是基于STC12C5A60S2处理器的一款开发板),带中文版windows操作系统的电脑。

完成本文所需软件:KEIL系列平台(本文选取Keil uVision4), STC烧写软件-ISP-V6.82E 。


一、独立按键检测

       这里我要实现用按键K1去控制发光二极管LD4。同时为了试验按键过程中与其他事件的冲突性,引入两个事件即LD1与LD8分别以不同频率闪烁。先上程序吧。另外利用keil软件新建工程和文件部分这里就略过了,总之所有的代码都放在main文件里执行。

代码:

-----------------------------------------------------------------------------------------------

#include <reg51.h>
sbit K1=P2^4;       //定义按键K1的检测口
sbit LD4=P1^3;    //定义控制LD4的输出口
sbit LD1=P1^0;   //定义控制LD1的输出口
sbit LD8=P1^7;   //定义控制LD8的输出口

void LD1_flash()  //LD1闪烁
{
	static unsigned int a; //定义a为静态局部变量
	a = a + 1;
	if(a == 20000) //当a达到20000次CPU计数次数
	{
		a=0;  //a归零
		LD1 =! LD1; //LD1取反,如果先前是灭,则取反后亮,反之。
	}
}

void LD8_flash()//LD8 flash
{
	static unsigned int b;
	b++;
	if(b==40000)
	{
		b=0;
		LD8 =! LD8;
	}
}

void delay(unsigned int x) 
{
	while(x)
	{
		x = x - 1;
	}
}

void Key1()
{
	static char st;
	if(K1==0) //按键K1是否被按下
	{
		if(st==0) //按键K1是否是被刚刚按下,假设st为1,则按键还处于被按下的状态,则不用执行if里面语句。
		{
			delay(5000); //延时防抖动
			if(K1==0) //查看按键K1是否还处于被按下状态
			{
				LD4=!LD4;
				st=1;  //等待放手
			}
				
		}
	}
	else
	{
		st=0;
	}
}

void main() //C语言执行入口
{
	while(1)
	{
		LD1_flash(); //ld1 flash 
		LD8_flash(); //ld8 flash
		Key1();
	}
}

要理解上面的代码,我们需要结合C语言的一些特性来分析。

1、static unsigned int a,为什么不把变量a与b定义成局部变量,却定义成静态局部变量?

     我们知道局部变量在函数执行完时候其值归零,而static关键字修饰的局部变量在函数结束时,其值会保留到下一次该函数被调用。

2、为什么a和b没被定义成全局变量?

    全局变量是指该变量可被任何函数使用,而局部变量只有定义该变量的函数可以使用。局部变量的优点是安全,但缺点是函数结束其值随之归零。与之相对,全局变量则不安全。

3、为什么a和b并未赋初始值?

    这是因为startup.A51程序执行时使其赋值为0。C语言执行是从函数开始的,但是真正的程序运行是从汇编语言开始,即startup.A51文件。因为C语言无法访问寄存器如R0, 所以只能由汇编程序来执行。下面截取一段startup.A51的程序进行分析:

IF IDATALEN <> 0
                MOV     R0,#IDATALEN - 1
                CLR     A
IDATALOOP:      MOV     @R0,A
                DJNZ    R0,IDATALOOP
ENDIF

    这段程序会将内存区域清零,其中A代表累加器ACC,即内存中224号地址。

     另外解释下赋初值的情况,例如unsigned int a = 8, 我们知道RAM在断电后数据丢失,因此单片机就利用ROM来保存这个初始值。在下次上电时,进入C语言之前,汇编程序将ROM中的a调到RAM中。这样也就保证了a的初值。

4、LD1与LD8的闪烁为什么不适用delay函数?

     如果使用delay函数,那么单片机在执行到LD1_flash函数时候,只能停滞在delay的这个时间段内,浪费时钟资源。而引入a和b后,程序进入LD1_flash函数时候,只需判断a的值,然后再加一或者清零,几乎不占用CPU时间。

5、key1函数中st的作用?

     首先,st可以判断程序在每次被调用的前后按键状态,这样,可以保证键被按住的时候,发光二极管不会闪烁。另外,也可以避免在操作K1键的时候影响LD1和LD8的运行。另外如果不想在key1函数中使用delay函数,可以采用如下代码:

void Key1()
{
	static unsigned int a,b;
	if(K1==0)
	{
		if(a==0) //检查按键是否被刚刚按下
		{
			b++;
			if(b >= 1000) //看看按键时间是否维持了1000次CPU执行周期
			{
				LD4=!LD4;
				//...
				a=1;
			}
		}
	}
	else
	{
		a=0;
		b=0;
	}
}


6、如何将不同的函数独立到不同的文件?

    为了保证代码的可维护性,如果要将不同的代码区块分配到不同文件,可以使用extern,它的作用是实现不同文件间的函数调用。例如:

    本例中可将main函数精简成:

#include <reg51.h>

extern void delay(unsigned int x);
extern void LD1_flash();
extern void LD8_flash();
extern void Key1();

void main()
{
	while(1)
	{
		LD1_flash(); //ld1 flash 
		LD8_flash(); //ld8 flash
		Key1();
	}
}

二、矩阵键盘



上图中,P1口初始值从高位到低位为11111110,当2和3键被按下,此时P1.0口连通P1.5和P1.6口,即P1口状态变为10011110。由此我们可以用P1口的高四位来判断有没有键被按下,如果没有那么其值为1111,也就是0x0F。那如果被按下了,具体是哪个键呢?这时候我们可以建立一个矩阵表,然后通过每次读取P1口的状态,与矩阵表相对应,便可以知道哪个键被按下。这个矩阵表见下(选用了P0口代替P1口):

原理就解释到这吧,直接上程序:

-----------------------------------------------------------------------------------------------------------------

#include "reg51.h"

void delay(unsigned int x)
{
	while(x)
	{
		x=x-1;
	}
}

void key_exc(unsigned char k)//unsigned 表示最高位不当做符号用
{
	switch(k)
	{
		case 0xee: P1=~P1; break; //1键按下
		case 0xed:  break;        //2键按下
		case 0xeb:  break;
		case 0xe7:  break;		
		
		case 0xde:  break;
		case 0xdd:  break;
		case 0xdb:  break;
		case 0xd7:  break;
		
		case 0xbe:  break;
		case 0xbd:  break;
		case 0xbb:  break;
		case 0xb7:  break;
		
		case 0x7e:  break;
		case 0x7d:  break;
		case 0x7b:  break;
		case 0x77:  P1=~P1; break;
	}
}
void key_scan()
{
	char i;
	char t; //用来存放读取P0口的状态
	
//	if((P0>>4) != 15)
//	{
//		return; //直接跳出函数
//	}
	
	for(i=0;i<4;i++) //for循环执行,首先i赋值,再判断i是否小于4,如果是的话,开始执行下面的程序,程序执行完加1,再判断i是否小于4,这样一直到结束
	{
		P0 = ~(1<<i); //扫描第i行。注意1<<i是一个数,这个数中只有一个1,这个1正好在第i位上。而~则将其取反。
		t=P0; //将P0的值赋给t
		if((t>>4)!=0x0F) //本行是否有按钮被按下
		{
			delay(5000);
			if(P0==t) //如果相等则表示延时前后的按键状态是一致的
			{
				key_exc(t);//将局部变量传给函数key-exc,并分析按下的按钮
				while((P0>>4)!=15)//等待放手
				{
					
				}
			}
		}
	}
}
void main()
{
	while(1)
	{
		key_scan();
	}
}

-----------------------------------------------------------------------------------------------------------------------------------

利用上面的程序,可以判断哪个按钮被按下。例如当K1按下时候,P1口的发光二极管都被点亮。好了就讲到这里吧,如果有什么疑问,请留言,我会及时答复!

2013-05-18 15:24:02 yunwen3344 阅读数 940
  • 单片机控制第一个外设-LED灯-第1季第6部分

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

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

1.  独立键盘由单片机的一个I/O口驱动,如果开发的系统要用到很多按键时,为节省I/O口,我们一般使用4*4键盘,这样驱动16个键盘只需用一个8位I/O口就可以了。


2.  按键检测(检测哪个按键被按下):

    针对上图电路,先让P3输出1111 0000,接着开始读P3,根据线与的概念,检测到P34~P37某个为0(假设P35为0),则可以确定按键所在的列;然后反过来,将P3输出1101 1111(高位保持上次检测的值),同样根据线与的概念,检测到P30~P33某个为0(假设P30为0,则P3为1101 1110),则可以确定按键所在的行,这样就确定了按下的按键的位置。整个检测时间肯定会小于按键按下的时间(约20ms)。

(备注:线与的概念——开关两端的高低电平做与运算,其结果即为开关闭合后整个线路上的电平值)


单片机 矩阵式按键

阅读数 6385

没有更多推荐了,返回首页