搜索
bottom↓
回复: 8

请马老师指点您的书上《AVR单片机嵌入式系统原理及应用实践》的矩阵按键里面的一些

[复制链接]

出0入0汤圆

发表于 2011-6-25 11:29:22 | 显示全部楼层 |阅读模式
书上P292-P297您讲的是矩阵按键,我去年在M48上移植成功了,但前几天想移植到LPC21329(ARM7),一直没出来现象,原因是2132上置高和置低寄存器

是分开了,不能按avr上的那样写了,即使我改了寄存器的设置方式后还是不行,回来看看老师您的书上有以下不明白的地方:

前提;行为输出方式,列为输入方式

1、按您的按键识别的思想,先把行线全部输出为低后判断列是否有低电平出现,有低电平则有按键按下。确认具体哪个按键按下的方法是:把行线逐一

   输出为低(每次循环行线只有一个为低),然后再判断列线上那个输出低则那个按键按下了。问题是:您在解释的时候是按上面对的说法解释的,我

  也觉得是正确的,但您在接下来的程序里就把行线取反了(如,本来应该行线只有一根输出为低的,但您却把三根输出为低,一根输出为高,即
  
  key_line=0B00001000,这是把PD3设为高,而把PD4、PD5、PD6设为低,按前面的解释应该把PD3设为低,而把其他三根设为高的,您这样做的原因是什么呢

  当然这种写法没有错,您后面的按键编码就把行线全部取反了,即保证了按键的正确)

2、我把PORTD设为输出,但我读这个口时PIND会返回什么值呢

3、PORTD =~key_line;这句话是为了延时以读到正确的端口电平值,但是您为什么要取反后再给呢

  PORTD =key_line;PORTD =key_line;写成这样行吗,和您那样写测优缺点是什么呢

先谢谢马老师

出0入0汤圆

 楼主| 发表于 2011-6-25 12:30:39 | 显示全部楼层
附上原代码:

#define No_key         255
#define K1_1        1
#define K1_2        2
#define K1_3        3
#define K2_1        4
#define K2_2        5
#define K2_3        6
#define K3_1        7
#define K3_2        8
#define K3_3        9
#define K4_1        10
#define K4_2        0
#define K4_3        11
#define Key_mask        0b00000111

unsigned char read_keyboard()
{       
        static unsigned char key_state = 0, key_value, key_line;
    unsigned char key_return = No_key,i;
       
        switch (key_state)
        {
                case 0:
                        key_line = 0b00001000;
                        for (i=1; i<=4; i++)                                        // 扫描键盘
                        {       
                                PORTD = ~key_line;                                        // 输出行线电平
                                PORTD = ~key_line;                                        // 必须送2次!!!
                key_value = Key_mask & PIND;                // 读列电平
                                if (key_value == Key_mask)
                                        key_line <<= 1;                                // 没有按键,继续扫描
                                else
                                {
                                        key_state++;                                        // 有按键,停止扫描
                                        break;                                                // 转消抖确认状态
                                }
                        }
                        break;
                case 1:
                        if (key_value == (Key_mask & PIND))                // 再次读列电平,
                        {
                                switch (key_line | key_value)                // 与状态0的相同,确认按键
                                {                                                                // 键盘编码,返回编码值
                                        case 0b00001110:
                                                key_return = K1_1;
                                                break;
                                        case 0b00001101:
                                                key_return = K1_2;
                                                break;
                                        case 0b00001011:
                                                key_return = K1_3;
                                                break;
                                        case 0b00010110:
                                                key_return = K2_1;
                                                break;
                                        case 0b00010101:
                                                key_return = K2_2;
                                                break;
                                        case 0b00010011:
                                                key_return = K2_3;
                                                break;
                                        case 0b00100110:
                                                key_return = K3_1;
                                                break;
                                        case 0b00100101:
                                                key_return = K3_2;
                                                break;
                                        case 0b00100011:
                                                key_return = K3_3;
                                                break;
                                        case 0b01000110:
                                                key_return = K4_1;
                                                break;
                                        case 0b01000101:
                                                key_return = K4_2;
                                                break;
                                        case 0b01000011:
                                                key_return = K4_3;
                                                break;
                                }
                                key_state++;                                // 转入等待按键释放状态
                        }
                        else
                                key_state--;                                // 两次列电平不同返回状态0,(消抖处理)
                        break;                                               
                case 2:                                                        // 等待按键释放状态
                        PORTD = 0b00000111;                        // 行线全部输出低电平
                        PORTD = 0b00000111;                        // 重复送一次
                        if ( (Key_mask & PIND) == Key_mask)
                                key_state=0;                                // 列线全部为高电平返回状态0
                        break;
        }
        return key_return;
}

出0入0汤圆

发表于 2011-6-25 21:43:44 | 显示全部楼层
回复【楼主位】xtaens

书上p292-p297您讲的是矩阵按键,我去年在m48上移植成功了,但前几天想移植到lpc21329(arm7),一直没出来现象,原因是2132上置高和置低寄存器
是分开了,不能按avr上的那样写了,即使我改了寄存器的设置方式后还是不行,回来看看老师您的书上有以下不明白的地方:
前提;行为输出方式,列为输入方式
1、按您的按键识别的思想,先把行线全部输出为低后判断列是否有低电平出现,有低电平则有按键按下。确认具体哪个按键按下的方法是:把行线逐一
   输出为低(每次循环行线只有一个为低),然后再判断列线上那个输出低则那个按键按下了。问题是:您在解释的时候是按上面对的说法解释的,我
  也觉得是正确的,但您在接下来的程序里就把行线取反了(如,本来应该行线只有一根输出为低的,但您却把三根输出为低,一根输出为高,即
   
  key_line=0b00001000,这是把pd3设......
-----------------------------------------------------------------------

我真不知道你的C语言到什么程度,既然都用ARM7了,还问出小儿科的问题。

你在1中分析的很正确,但key_line不是真正的输出,只是一个临时工作变量而以。
真正的输出在在3中提到的,使用了PORTD =~key_line,这个才是真正的输出,这个输出不就符合1中你分析的吗?

key_line=0b00001000
~key_line=0b11110111
而PORD=~key_line,不正是:“把行线逐一输出为低(每次循环行线只有一个为低)”吗?

循环输出,只有一位为低。但C语言的位操作左移是最后补“0”的。仔细体会吧。

可以自豪的说,我编写教材中,最简单的代码,都是比较优化的(“最”字不敢讲),超过市面上80%的垃圾参考书。不管你是使用51、还是AVR,ARM、STM32、M051....,只要你是面对低层的设计,认真学习和参考我的教材,都会有真正的收获。

别的书是说如何做到,而我讲的是如何“最好”的做到。

外行看热闹,内行看门道。从另外的层面上讲,就是你看不出“门道”,你肯定不是“内行”。这是从哲学的高度上看问题。
=====================================
王婆卖瓜了,不好意思。因为刚刚朋友饭局,多喝了几杯。杯具了。

出0入0汤圆

发表于 2011-6-25 23:23:55 | 显示全部楼层
马老师太豪爽了

出0入0汤圆

 楼主| 发表于 2011-6-26 09:32:22 | 显示全部楼层
回复【2楼】machao  
-----------------------------------------------------------------------

谢谢马老师指点,昨天发完帖子吃饭回来后那个键盘就给搞定了,马老师您说的对,我自己觉得我也经常犯一些很小的错误

一直改不掉啊。我太粗心了。。。。我原来的错误理解是:把PORTD = ~key_line;当成PORTD = key_line;(我错看成把key_line取反
2次后还是key_line了,其实~key_line后key_line并没有改变啊)

再次谢谢马老师的教导。。。

下面是我把您书上那个矩阵按键移植到LPC2132上的程序

使用时直接在主函数里包含那个h文件后调用读键盘函数即可。。。。

keyboard_matrix.h文件里的代码:

#ifndef _keyboard_matrix_h_
#define _keyboard_matrix_h_

#define Keyboard_DIR       IO1DIR         //用端口1的P1.16-P1.23 8个管脚
#define Keyboard_PIN       (IO1PIN>>16)   //使用P1[25:16]中的前6个管脚,16-21脚
#define Keyboard_PORT_High  IO1SET         //
#define Keyboard_PORT_Low   IO1CLR

#define No_key  17  //这个数对应0xFF,作用是不按键时数码管什么都不显示
#define K1_1    1
#define K1_2    2
#define K1_3    3
#define K2_1    4
#define K2_2    5
#define K2_3    6
#define K3_1    7
#define K3_2    8
#define K3_3    9
#define K4_1    10
#define K4_2    0
#define K4_3    11
#define Key_mask    0x2A  //列的电平值,0b0010 1010

void Keyboard_Matrix_Init();//矩阵键盘初始化
unsigned char Read_keyboard_Matrix();//矩阵键盘读端口函数接口

#endif

keyboard_matrix.c文件里的代码:

#include <NXP/iolpc2132.h>
#include "keyboard_matrix.h"
#include "..\driver\LPC_Uart.h"  //调试用的

void Keyboard_Matrix_Init()
{
     //P1[25:16]连接到GPIO功能
    PINSEL2=PINSEL2 &(~0x08); //这句话一般不用改      //23   19 16位
    //行输出,列输入,1111 1111 1101 0101 1111 1111 1111 1111
    //先要把低6位置成0后再和0xD5或才能实现行输出,列输入的效果,2011-06-23
    Keyboard_DIR =(Keyboard_DIR & 0xFFC0FFFF)|(0x15<<16);
}
   
unsigned char Read_keyboard_Matrix()
{   
    static unsigned char key_state=0;  //按键的状态
    static unsigned char key_value;    //按键值
    //static unsigned char key_line;     //行线,本程序中不要行线,2011-06-25
    unsigned char i;
    //如果要让按键按下后返回的数只显示一次,把下面的static去掉即可,2011-06-25
    static unsigned char key_return = No_key;
    switch (key_state)
    {
        case 0:
            //key_line = 0x14;//0B00 010100,第0行输出低,其他两行输出为高,本程序中不要行线,2011-06-25
            for(i=1; i<4; i++) //行扫描,扫3遍即3行
            {         
                if(i==1)//下面的几个if语句取代了以前的移位操作
                {
                    Keyboard_PORT_High =0x00140000;// 输出行线电平,第0行输出低,其他两行输出为高
                    Keyboard_PORT_Low  =0x00010000;//0000 0000 0001 0100 ,使其中的那两位输出为0
                }
                else if(i==2)
                {
                    Keyboard_PORT_High =0x00110000;// 输出行线电平,第1行输出低,其他两行输出为高
                    Keyboard_PORT_Low  =0x00040000;//0000 0000 0001 0001 ,使其中的那两位输出为0
                }
                else if(i==3)
                {
                    Keyboard_PORT_High =0x00050000;// 输出行线电平,第2行输出低,其他两行输出为高
                    Keyboard_PORT_Low  =0x00100000;//0000 0000 0000 0101 ,使其中的那两位输出为0
                }
               
                key_value = Key_mask & Keyboard_PIN;// 读列电平
                if (key_value != Key_mask)
                {
                    key_state++;          // 有按键,停止扫描
                    break;                    // 转消抖确认状态
                }
                }
            break;
        case 1:
        if (key_value == (Key_mask & Keyboard_PIN)) // 再次读列电平,
        {   //与状态0的相同,确认按键
            switch ((unsigned char)Keyboard_PIN)
            {       // 键盘编码,返回编码值
              case 0xFC:
                          key_return = K1_1;
                          break;
             case 0xF6:
                          key_return = K1_2;
                          break;
             case 0xDE:
                          key_return = K1_3;
                          break;
              case 0xF9:
                          key_return = K2_1;
                            break;
              case 0xF3:
                          key_return = K2_2;
                          break;
              case 0xDB:
                          key_return = K2_3;
                          break;
              case 0xED:
                          key_return = K3_1;
                          break;
              case 0xE7:
                          key_return = K3_2;
                          break;
              case 0xCF:
                          key_return = K3_3;
                          break;
              case 0x46:
                          key_return = K4_1;
                          break;
              case 0x45:
                          key_return = K4_2;
                          break;
              case 0x43:
                          key_return = K4_3;
                          break;
            }
            key_state++;// 转入等待按键释放状态
         }
         else
            key_state--;//两次列电平不同返回状态0(消抖处理)
            break;                                               
         case 2:                                // 等待按键释放状态
            Keyboard_PORT_Low = 0x15<<16;     // 行线全部输出低电平,0B0001 0101
            if ( (Key_mask & Keyboard_PIN) == Key_mask)
                key_state=0;    // 列线全部为高电平返回状态0
            break;
    }
    return key_return;
}


键盘的连接为:

(原文件名:11.jpg)

出0入0汤圆

发表于 2011-6-27 22:12:21 | 显示全部楼层
"for(i=1; i<4; i++) //行扫描,扫3遍即3行 "

there is no reason to scan the lines - that's just too inefficient.

you can output on the row, read the column; and then output on the column, and then read the row.

After that,  you know precisely which keys are pressed. This works with concurrent key presses too.

your code can be greatly simplified.

出0入0汤圆

 楼主| 发表于 2011-6-30 10:39:57 | 显示全部楼层
回复【5楼】millwood0
-----------------------------------------------------------------------

thank you for your attention!!!

出0入0汤圆

发表于 2011-8-20 09:30:51 | 显示全部楼层
记录一下,备用
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-20 19:11

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表