打印

基于状态机的矩阵键盘驱动,24楼新增“线性反转法”实现

[复制链接]
10718|38
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
XIANSir|  楼主 | 2011-3-31 18:46 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 XIANSir 于 2011-4-1 17:01 编辑

先秀键盘,图片上——实物通过,Proteus仿真没通过






下面是所有工程及代码及电路图及实验数据,有愿意玩的可以下下来,不过得用UV4才行:
keypad.rar (1.46 MB)

相关帖子

沙发
XIANSir|  楼主 | 2011-3-31 18:51 | 只看该作者
本帖最后由 XIANSir 于 2011-3-31 18:52 编辑

这次编程中,再一次遇到了之前经常遇到的问题:
再次被优先级问题给折腾了个死去活来,不过还好最后通过串口打印找到了问题的所在。
问题很简单,一点就透,所以俺就不多说废话了,只是将正确的代码和错误的代码列出来:
一开始错误的代码:
#define KEY_RD()        (P2 >> 4)&0x0F   //读取矩阵键盘输出脚的状态
这里有很多很多代码省略.............
if(KEY_RD() == 0x0F)

...........................
修改后正确的代码:
#define KEY_RD()        ((P2 >> 4)&0x0F)//读取矩阵键盘输出脚的状态
这里有很多很多代码省略.............
if(KEY_RD() == 0x0F)

...........................

唉,又是优先级,又是小括号:'(

使用特权

评论回复
板凳
XIANSir|  楼主 | 2011-3-31 18:54 | 只看该作者
额,忘了,还没有“炫耀”俺的代码呢??:lol
俺这菜鸟,好不容易才写出来这个程序,炫耀一下不过分吧;P——高手请砖下留情:handshake
/******************************************************************************************************************************************
* 文件名称:        keypad.c
* 功能说明:        基于状态机的矩阵键盘的实现代码
* 作者邮箱:        XIANSir@yeah.net
* 注意事项:
* 版本日期: 2011年3月31日
* 升级记录: 2010年3月31日 将KEY_RD()的定义由(P2 >> 4)&0x0F 改为 ((P2 >> 4)&0x0F)
*                        此修改去除了一个严重的BUG,此BUG与C语言的运算符优先级有关
*******************************************************************************************************************************************/
#include "sysconf.h"
#include "typedef.h"
#include "8052Hal.h"

#include "keypad.h"

/*矩阵键盘的输入脚接P2的低四位,输出脚接P2的高四位
* 注意:矩阵键盘的输入是MCU的输出,矩阵键盘的输出是MCU的输入*/
#define KEY_LO()        P2 &= 0xF0                //矩阵键盘所有输入脚都置低
#define KEY_HI()        P2 |= 0x0F                  //矩阵键盘所有输入脚都置高
#define KEY_L(i)        P2 &= ~(1<<i)        //矩阵键盘指定输入脚  置低
#define KEY_RD()        ((P2 >> 4)&0x0F)//读取矩阵键盘输出脚的状态


bit NeedScan = 0;                                    //键盘每10ms扫描一次,扫描间隔由T0产生
s_Key S_Key   = {NO_KEY,K_STAT_0};        //包含键盘所有有用信息的结构体:按键值,键盘状态


/******************************************************************************************************************************************
* 函数名称: PadInit()
* 功能说明: 矩阵键盘初始化例程,配置矩阵键盘引脚并初始化间隔时钟T0
* 输    入: 无
* 输    出: 无
* 注意事项: 1、本矩阵键盘连接在51的P2端口上
*                        2、本矩阵键盘使用了51的定时器资源T0
******************************************************************************************************************************************/
void PadInit(void)
{
        P2 = 0xFF;                                                //51引脚作为输入时必须先置高

        TMOD = 0x01;                                        //配置T0为16 bit定位时器模式
        TH0 = T10ms_LO;                                        //设置定时器溢出中断周期为10ms
    TL0 = T10ms_HI;
                                                                       
        EA = 1;                                                        //全局中断开
        ET0 = 1;                                                //允许T0中断
    PT0 = 0;                                                //T0优先级设为低
    TR0 = 1;                                            //启动T0开始计时
}

/******************************************************************************************************************************************
* 函数名称: OnceKey()
* 功能说明: 进行一次键盘状态的扫描获取
* 输    入: 无
* 输    出: byte        扫描到的按键值
* 注意事项: 1、编码0xFF(NO_KEY)表示没有按键被按下
*                        2、本函数扫描按键没有进行消抖,下面的ScanKey()函数在本函数的基础上进行了基于状态机机制的机械消抖操作
******************************************************************************************************************************************/
byte OnceKey(void)
{
    ui08 i = 0;
        byte key = NO_KEY;

    KEY_LO();
    if(KEY_RD() == 0x0F)
        {         
                KEY_HI();
        return NO_KEY;                         //没有按键按下,直接返回
        }
       
    for(i = 0;i < 4;i++)
    {
        KEY_HI();
            KEY_L(i);
            key = KEY_RD();
            switch(key)
                {
                case ((~1) & 0x0F):
                        key = 4*i + 0;
                        break;
                case ((~2) & 0x0F):
                        key = 4*i + 1;
                        break;
                case ((~4) & 0x0F):
                        key = 4*i + 2;
                        break;
                case ((~8) & 0x0F):
                        key = 4*i + 3;
                        break;
                default:
                        key = 0xFF;
                }

                if(key < 0x10)
                {
                        return key;
                }
    }
       
        return NO_KEY;                                                                   
}

/******************************************************************************************************************************************
* 函数名称:        ScanKey()
* 功能说明: 矩阵键盘扫描,返回得到的代表被按键位置的码值
* 输    入: 无
* 输    出: byte        扫描到的按键值
* 注意事项: 编码0xFF(NO_KEY)表示没有按键被按下
******************************************************************************************************************************************/
byte ScanKey(void)
{
    byte key =NO_KEY;
        byte ret =NO_KEY;

    if(NeedScan == 0)
        {
                return NO_KEY;
        }
        NeedScan = 0;                                                                //防止重复进入

        key = OnceKey();
    switch(S_Key.state)
        {
        case K_STAT_0:
            if(key != NO_KEY)
            {
                        S_Key.v_key = key;
                S_Key.state = K_STAT_1;
            }
                break;
        case K_STAT_1:
            if(key == S_Key.v_key)
            {
                S_Key.state = K_STAT_2;
            }
            else
            {
                        S_Key.v_key = NO_KEY;
                S_Key.state = K_STAT_0;
            }
                break;
        case K_STAT_2:
            if(key == S_Key.v_key)
            {
                        ret = S_Key.v_key;
                S_Key.state = K_STAT_3;
            }
            else
            {
                        S_Key.v_key = NO_KEY;
                S_Key.state = K_STAT_0;
            }
                break;
        case K_STAT_3:
            if(key == NO_KEY)
            {
                        S_Key.v_key = NO_KEY;
                S_Key.state = K_STAT_0;
            }
                break;
        default:
                ret = NO_KEY;  
    }

    return ret;
}

/******************************************************************************************************************************************
* 函数名称: ParseKey()
* 功能说明: 将按键的编码值映射到更易理解的“按键上标识的字符”
* 输    入: byte key         按键的编码值
* 输    出: char                  按键上标识的字符
* 注意事项: 这个映射需要根据自己矩阵键盘上的实际标注来修改,但是这显然很容易
******************************************************************************************************************************************/
char ParseKey(byte key)
{
        char ch = '\0';

        switch(key)
        {
        case 0x00:
                ch = '1';
                break;
        case 0x01:
                ch = '2';
                break;
        case 0x02:
                ch = '3';
                break;
        case 0x03:
                ch = 'A';
                break;
        case 0x04:
                ch = '4';
                break;
        case 0x05:
                ch = '5';
                break;
        case 0x06:
                ch = '6';
                break;
        case 0x07:
                ch = 'B';
                break;
        case 0x08:
                ch = '7';
                break;
        case 0x09:
                ch = '8';
                break;
        case 0x0A:
                ch = '9';
                break;
        case 0x0B:
                ch = 'C';
                break;
        case 0x0C:
                ch = '*';
                break;
        case 0x0D:
                ch = '0';
                break;
        case 0x0E:
                ch = '#';
                break;
        case 0x0F:
                ch = 'D';
                break;
        default:
                ch = '\0';
        }
       
        return ch;
}

/******************************************************************************************************************************************
* 函数名称: T0Isr
* 功能说明: 产生10ms定时器0中断,在中断中置位NeedScan来驱动矩阵键盘扫描
* 输    入: 无
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
void T0Isr(void) interrupt 1
{
        TR0 = 0;
        TH0 = T10ms_LO;
    TL0 = T10ms_HI+10;                                                                        //9:这4条C语句执行消耗的时间
    TR0 = 1;
       
        NeedScan = 1;   
}

使用特权

评论回复
地板
XIANSir|  楼主 | 2011-3-31 18:55 | 只看该作者
唉!怎么发上去排版就乱了呢??真是不给力啊:sleepy:

使用特权

评论回复
评论
interrupt_2013 2013-4-26 11:31 回复TA
顶一下 
5
sysdriver| | 2011-3-31 21:41 | 只看该作者
思路很容易看懂,挺好的,排版的问题,弄个CODE就好,百度一下就知道了。

使用特权

评论回复
6
ye460398672| | 2011-3-31 21:50 | 只看该作者
可以问一下用状态机编程有什么好处吗?它编程有什么特点呢?

使用特权

评论回复
7
424308016| | 2011-3-31 22:15 | 只看该作者
mark下

使用特权

评论回复
8
谈的元| | 2011-3-31 23:10 | 只看该作者
顶一下

使用特权

评论回复
9
程序匠人| | 2011-3-31 23:39 | 只看该作者
酷一下

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
interrupt_2013 + 1 赞一个!
10
古道热肠| | 2011-4-1 00:02 | 只看该作者
呵呵,还不错,就是“ui08”这变量类型很少见,如果是unsigned integer 一般是16或32,如果是0-255的无符号整型,我习惯写成uchar

使用特权

评论回复
11
XIANSir|  楼主 | 2011-4-1 08:27 | 只看该作者
10# 古道热肠
呵呵,俺习惯写成uchr,因为这样和uint、void一样都是四个字母,还有ulng

使用特权

评论回复
12
XIANSir|  楼主 | 2011-4-1 08:28 | 只看该作者
6# ye460398672
有限状态机!俺也不是太懂,但是好像对于比较小的任务,可以使程序更健壮!

使用特权

评论回复
13
思考| | 2011-4-1 08:44 | 只看该作者
确是不错,基本满足模块化的低耦合,高内聚性。
现在只有一个键值缓冲区,实用性不大,建议LZ再做一个小的循环队列放键值。

使用特权

评论回复
14
XIANSir|  楼主 | 2011-4-1 08:50 | 只看该作者
13# 思考
呵呵,大侠想的果然比俺远啊!

俺之前想的是用两个变量存储按键值,这样就能检测连击了——就像手机上键盘那样,大侠直接给升级到了键值缓冲区,这样连组合键都能检测了!

不过,俺现在没这种需求,而且还要学习DS18B20和步进电机,所以就先这样吧,等以后有需求了再实现大侠的建议。:handshake

使用特权

评论回复
15
XIANSir|  楼主 | 2011-4-1 08:53 | 只看该作者
9# 程序匠人
:'(感动啊!!!这是俺在二姨家第一个酷贴啊!!!
感谢匠人前辈、匠人大侠的鼓励!

使用特权

评论回复
16
tcp1985| | 2011-4-1 10:37 | 只看该作者
2# XIANSir

我前段时间写一个小程序,也是被这个运算符的优先级问题搞得半死不活的!
也是觉得按位与"&"的优先级高于等于"=="!

使用特权

评论回复
17
7120223| | 2011-4-1 13:01 | 只看该作者
优先级 我基本都快忘记了,所以 每次必带括号

使用特权

评论回复
18
huangqi412| | 2011-4-1 13:05 | 只看该作者
别纠结于优先级,多用括号。

使用特权

评论回复
19
lwslws201| | 2011-4-1 13:45 | 只看该作者
矩阵键盘 用线性反转发 也好。

使用特权

评论回复
20
mrxum| | 2011-4-1 15:03 | 只看该作者
键盘实时性比较强,建议使用按键中断后扫描,这样还可以剩下个定时器

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:冷暖自知,泰然处之;持之以恒,必有所成!

0

主题

609

帖子

2

粉丝