找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索

小玩意,3个普通IO识别22个按键试验。有实物和程序

查看数: 17562 | 评论数: 34 | 收藏 25
关灯 | 提示:支持键盘翻页<-左 右->
    组图打开中,请稍候......
发布时间: 2013-7-9 23:08

正文摘要:

在51hei论坛吸取各位前辈的经验,将之前二极管用量多的问题优化一下,目前不用二极管能接6键,2只二极管能接12键,6只二极管能接18键,9只二极管能接21键,第22键要单独占用3只二极管最不化算。 实验用89S51作试 ...

回复

ID:237471 发表于 2024-10-15 09:06
厉害呀,受教了
ID:163139 发表于 2024-10-14 20:29
由于还是新人 只有跟在高手后面慢慢做了!
ID:74252 发表于 2019-6-9 03:28
这是深夜逛论坛翻到宝了!!!
ID:368541 发表于 2018-7-10 11:21
楼主辛苦  回复的很仔细
ID:297381 发表于 2018-3-26 17:17
6膜拜大佬
ID:89386 发表于 2015-10-14 19:48
超级赞! 新手 目前写个 点亮一个LED都带写错的。。。。直接膜拜!
ID:92052 发表于 2015-10-14 07:20
高手啊,掌握了精髓,所以可以随心所欲了。
ID:91611 发表于 2015-10-5 11:18
这真是大神啊
ID:60246 发表于 2014-4-2 09:50
谢谢分享
ID:59341 发表于 2014-3-1 15:55
高手呀,收藏一下
ID:59151 发表于 2014-2-18 01:59
好东西,揽下试试,谢谢!
ID:58796 发表于 2014-2-14 00:00
学习。。。。。。。。。。。。。
ID:58267 发表于 2013-12-29 20:49
支持一下
ID:52286 发表于 2013-10-13 23:52
支持支持!!
ID:55734 发表于 2013-10-9 02:12
看看学习学习。。
ID:53315 发表于 2013-8-21 02:07
楼主,我没看见按键啊?
ID:52177 发表于 2013-7-23 22:24
有套件卖吗?我要
ID:51777 发表于 2013-7-13 20:32
由于还是新人 只有跟在高手后面慢慢做了!
ID:51654 发表于 2013-7-10 01:31
今天太晚了,下次再弄吧
ID:50574 发表于 2013-7-10 01:28

const unsigned char Key_tab[]=     //键码映射表
{//  0  1  2  3  4  5  6  7  8  9   
        22, 0, 2, 0, 0, 0, 0, 0, 4, 0, //0
         0, 0, 0, 0, 0,18, 0, 0, 0, 0, //1X
         0, 0, 0, 0, 0, 0, 3,14, 0, 0, //2X
        20,10, 6, 0, 0, 0, 0, 0, 1,19, //3X
         0, 5, 0, 0, 0,15, 0,11, 0, 0, //4X
         0,17, 0, 0,13, 8, 0,21, 0, 9, //5X
        16,12, 7, 0                    //6X
};

//=============== 检测按键 =================
void Key_scan()
{   
    unsigned char i;
    Key_count --;                        //扫描次序
    Key_count &= 3;
    switch (Key_count)                //按次序处理
    {
        case 2:                //第一轮扫描                                   
        if (PINA & 0x02) Key_state |= 0x01;    //读入PINA.1,记录于Key_state.0  
        if (PINA & 0x04) Key_state |= 0x02;    //读入PINA.2,记录于Key_state.1
                DDRA  &= ~(1<<0);  //A口位0先改为输入  
        PORTA |=  (1<<0);  //A口位0上拉有效
                DDRA  &= ~(1<<2);  //A口位2先改为输入  
        PORTA |=  (1<<2);  //A口位2上拉有效  
        PORTA &=  ~(1<<1);  //A口位1设定为0  
        DDRA  |=  (1<<1);  //A口位1改为输出0         
        break;
     
        case 1:                                //每二轮扫描
        if (PINA & 0x01) Key_state |= 0x04;    //读入PINA.0,记录于Key_state.2  
        if (PINA & 0x04) Key_state |= 0x08;    //读入PINA.2,记录于Key_state.3
        PORTA |= (1<<1);
                DDRA &= ~(1<<1);
        PORTA &= ~(1<<2);
                DDRA  |= (1<<2);
                DDRA  &= ~(1<<0);   
        PORTA |=  (1<<0);  
        break;
     
        case 0:                                //每三轮扫描
        if (PINA & 0x02) Key_state |= 0x10;    //读入PINA.1,记录于Key_state.4  
        if (PINA & 0x01) Key_state |= 0x20;    //读入PINA.0,记录于Key_state.5
        PORTA |= (1<<2);
                DDRA &= ~(1<<2);
        PORTA &= ~(1<<0);
                DDRA  |= (1<<0);
                DDRA  &= ~(1<<1);   
        PORTA |=  (1<<1);  
        break;
     
        default:                        //每四轮扫描
        if (!(PINA&0X01)) Key_state &= ~(1<<0);
        if (!(PINA&0X02)) Key_state &= ~(1<<1);
        if (!(PINA&0X04)) Key_state &= ~(1<<2);
               
        break;
     }

        //======更新显示缓冲区=======
        i = Key_tab[Key_state];                 
        if (i == 0)
        {
            Disp_buf[2] = 0x11;                //显示三横
            Disp_buf[1] = 0x11;
            Disp_buf[0] = 0x11;
        }
        else
        {
            Disp_buf[2] = 0x0c;     //字符"C"
            Disp_buf[1] = i / 10;   //键码十位
            Disp_buf[0] = i % 10;      //键码个位                        
        }
                //Key_state = 0;                        
}
Key_state什么时候清零才合适?要是4IO口的话,键码又如何扩展呢?
ID:51654 发表于 2013-7-10 01:14
你没细看程序,Key_state只用了6位,defalt那次的位用压缩方或与前面6位中的3位相与。如果真的用到9位,键码表占512字节就不好了。
至于dafalt后的break,在这里可用可不用,因为defalt后已经是switch的未端。
你可以把文件传到这里,或发至cowboy3@163.com
ID:50574 发表于 2013-7-10 01:11
四个IO扫3次,那Key_state就不止8位了,咋办呢?
另外,default语句是不是漏了break?
楼主,能否把QQ号给我?我直接把仿真文件给你,就能直观地看到错误了
ID:51654 发表于 2013-7-10 01:07
h333 发表于 2013-7-10 00:43
4个IO口,能扫多少键呢? ^_^

51的四个IO能扫65个键(或可以再多6个,但这6个可靠性不太好).
AVR的,2个IO就可以扫15键,4个IO可能超100了,你可以搜索一下h2feo4的贴子。
ID:51654 发表于 2013-7-10 01:06
先改变IO状态,立刻回读IO,有时会出错,IO外部受分布参数影响会有延时,特别是AVR上拉电阻较大,如果立该回读可能出错。
这里看似先读再改变IO输出,其实读的时候,上一轮扫描中已经把IO改变,到现在已过了5ms,IO状态足够稳定了,这时读入就可靠。
读完了,再改变IO状态,其实是为下一次做好准备。

Key_state用组合生成,效果是一样,只是处理繁琐且占内存也多。你先前的程序IO切换过程不正确才可能导致没效果。
ID:50574 发表于 2013-7-10 00:43

4个IO口,能扫多少键呢? ^_^
ID:50574 发表于 2013-7-10 00:43
为什么 不是:
        case 2:                //第一轮扫描  
        DDRA  &= ~(1<<0);  //A口位0先改为输入
        PORTA |=  (1<<0);  //A口位0上拉有效
        PORTA &=  ~(1<<1);  //A口位1设定为0
        DDRA  |=  (1<<1);  //A口位1改为输出0                               //第一轮扫描  
        if (PINA & 0x02) Key_state |= 0x01;    //读入PINA.1,记录于Key_state.0
        if (PINA & 0x04) Key_state |= 0x02;    //读入PINA.2,记录于Key_state.1        
        break;  
       先改变IO口状态,再做键值判断?
       我那个 Key_state = (KB5<<5 + KB4<<4 + KB3<<3 + KB2<<2 + KB1<<1 + KB0);效果也应该是一样呀,只不过繁琐了一些
ID:51654 发表于 2013-7-10 00:43
楼上还是没搞明白,举例说明一下

        case 2:                                //第一轮扫描
        if (PINA & 0x02) Key_state |= 0x01;    //读入PINA.1,记录于Key_state.0
        if (PINA & 0x04) Key_state |= 0x02;    //读入PINA.2,记录于Key_state.1
        DDRA  &= ~(1<<0);  //A口位0先改为输入
        PORTA |=  (1<<0);  //A口位0上拉有效
        PORTA &=  (1<<1);  //A口位1设定为0
        DDRA  |=  (1<<1);  //A口位1改为输出0
        break;             //以上端口变化顺序不要更改,否则可以出现短路
ID:50574 发表于 2013-7-10 00:42

unsigned char KB0,KB1,KB2,KB3,KB4,KB5;


//=============== 检测按键 =================
void Key_scan()
{   
    unsigned char i;
    Key_count --;                        //扫描次序
    Key_count &= 3;
    switch (Key_count)                //按次序处理
    {
        case 2:                                //第一轮扫描
        KB0 = PINA&0X02;  
        KB1 = PINA&0X04;  
        PORTA |= (1<<0);
                DDRA |= (1<<0);
        PORTA &= ~(1<<1);
                DDRA &= ~(1<<1);
        break;
     
        case 1:                                //每二轮扫描
        KB2 = PINA&0X04;
        KB3 = PINA&0X01;
        PORTA |= (1<<1);
                DDRA |= (1<<1);
        PORTA &= ~(1<<2);
                DDRA &= ~(1<<2);
        break;
     
        case 0:                                //每三轮扫描
        KB4 = PINA&0X01;
        KB5 = PINA&0X02;
        PORTA &= ~(1<<0);
                DDRA &= ~(1<<0);
        PORTA |= (1<<2);
                DDRA |= (1<<2);
        break;
     
        default:                        //每四轮扫描
        if (!(PINA&0X01)) KB0 = 0;
        if (!(PINA&0X02)) KB2 = 0;
        if (!(PINA&0X04)) KB4 = 0;
         

        //======更新显示缓冲区=======
        Key_state = (KB0<<5 + KB1<<4 + KB2<<3 + KB3<<2 + KB4<<1 + KB5);
        i = Key_tab[Key_state];
        if (i == 0)
        {
            Disp_buf[2] = 0x11;                //显示三横
            Disp_buf[1] = 0x11;
            Disp_buf[0] = 0x11;
        }
        else
        {
            Disp_buf[2] = 0x0c;     //字符"C"
            Disp_buf[1] = i / 10;   //键码十位
            Disp_buf[0] = i % 10;      //键码个位
        }
        Key_state = 0;
    }
}     

void main()
{
   // TMOD = 0x10;            //定时器1,16位模式
    //TCON = 0xc0;            //TR1=1;TF1=1;
        init_devices();
    while(1)                //主循环
    {
        //Bus_drive();        //显示总线驱动
        PORTB = LED_font[Disp_buf[2]];
        PORTC = LED_font[Disp_buf[1]];
        PORTD = LED_font[Disp_buf[0]];
        Key_scan();         //检测按键
        delay_ms(5);        //延时5MS     
    }
}

怎么按,都是显示C22
ID:51654 发表于 2013-7-10 00:24
h333 发表于 2013-7-10 00:21
楼主,两点请求:
1、能否给我单总线数码管显示那块的电路图
2、我移植到AVR下,按键值不变,错在哪里 ...

首先,AVR的IO不是准双向口,在作为输入时,需要开上拉电阻,当切换到输出0时,需要改变PORTA和DDRA,你程序里没有更改DDRA,因而不能动作。
其次,没看到你对KB0~KB5的变量类型声明,51中声明为bit,AVR中的位操作比较麻烦,建议读入时直接更新Key_state,取消KB0~KB5。
至于显示的问题,我打算迟些开个新帖,因为和这主题关系不大。键码已从 i= Key_tab[Key_state] 获得,需要送显示可自行处理。
ID:50574 发表于 2013-7-10 00:21

楼主,两点请求:
1、能否给我单总线数码管显示那块的电路图
2、我移植到AVR下,按键值不变,错在哪里呢?
   //=============== 检测按键 =================
void Key_scan()
{   
    unsigned char i;
    Key_count --;                        //扫描次序
    Key_count &= 3;
    switch (Key_count)                //按次序处理
    {
        case 2:                                //第一轮扫描
        KB0 = PINA&0X02;  
        KB1 = PINA&0X04;  
        PORTA |= (1<<0);
        PORTA &= ~(1<<1);
        break;
     
        case 1:                                //每二轮扫描
        KB2 = PINA&0X04;
        KB3 = PINA&0X01;
        PORTA |= (1<<1);  
        PORTA &= ~(1<<2);
        break;
     
        case 0:                                //每三轮扫描
        KB4 = PINA&0X01;
        KB5 = PINA&0X02;
        PINA &= ~(1<<0);
        PORTA |= (1<<0);  
        break;
     
        default:                        //每四轮扫描
        if (!(PINA&0X01)) KB0 = 0;
        if (!(PINA&0X02)) KB2 = 0;
        if (!(PINA&0X04)) KB4 = 0;
         

        //======更新显示缓冲区=======
        Key_state = KB0<<5 + KB1<<4 + KB2<<3 + KB3<<2 + KB4<<1 + KB5;
        i = Key_tab[Key_state];
        if (i == 0)
        {
            Disp_buf[2] = 0x11;                //显示三横
            Disp_buf[1] = 0x11;
            Disp_buf[0] = 0x11;
        }
        else
        {
            Disp_buf[2] = 0x0c;     //字符"C"
            Disp_buf[1] = i / 10;   //键码十位
            Disp_buf[0] = i % 10;      //键码个位
        }
        Key_state = 0;
    }
}

还有,你那个笔段代码是BCD码吗?
ID:51654 发表于 2013-7-9 23:21
对于这种方式的按键识别方法,很多朋友担心编程会很复杂,其实仔细分析后也很简单.比如上面例子,其本的思路是依次把三个IO拉低,然后记录另外两个IO的状态,最后三个IO都不下拉,再记录一次,就可得出的结果.对于按下不同的按键,就有不同的结果.如果只扫18键,那么最后一次扫描可以省掉,即扫描三次即可.实际应用时5MS的扫描间隔可以用定时中断来实现,这样就只占用很少的MCU时间.
ID:51654 发表于 2013-7-9 23:15

作为试验目的,没有接按键,只焊了个键盘框架,用镊子短路相应的节点来当按键。图中接二极管阵列的三根线是3个IO,单独的一根是地线。MCU发送串行数据给HC595驱动数码管作键码显示。
ID:51654 发表于 2013-7-9 23:15
下次焊个四面体的4IO玩玩看

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

二极管数量

6条楞,每楞两个,12个

中间星型,4个

共16个

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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