wenking99 发表于 2013-5-15 08:42:02

按键数码管显示复用后冲突问题

用的51单片机,附电路图。

按键KEY与数码管COM端是复用的。

现在可以知道,把数码管的数据输出端设置为高阻输入。
按键即(COM)端,设置为标准IO口,可以检测到按键输入。

数码管显示时,
数码管的数据输出端设置为推挽输出。
按键即(COM)端,同时要设置为推挽才可以显示正常,

数码管显示用的是定时器中断显示。(6ms)。
按键在主循环,扫描输入(100ms)扫描1次。

用了按键程序,LED显示不正常。
用了LED显示程序,按键程序不正常。

不知道为什么?有没有人指一下点?

wenking99 发表于 2013-5-15 17:19:44

没有人看,自己顶一下

lxa0 发表于 2013-5-15 20:36:55

显示的时候不要扫描键盘

summarize 发表于 2013-5-15 21:49:42

共阴数码管,则com1~com4会循环输出低电平.R1接地端改为接VCC,R1的另一端接单片机IO,当comX输出低电平后,读取R1连接的IO状态,如果为低电平,则有按键按下,并且这个按键就是连接comX的那个按键.此电路的缺点是不能同时按下两个按键,如果要支持同时按下两个按键,则每个按键与R1连接间加入一个二极管进行隔离,二极管方向是从R1流向按键.

wenking99 发表于 2013-5-15 22:37:19

这个电路不需要同时按下两个按键的。
按键的时候,我已经把定时器关闭了的。

void Display_Led ( void )
{
        Byte idata value;
       
        g_byLedCount++;
        if(g_byLedCount >= 4)                                                //重新开始
        {
                g_byLedCount = 0;
        }       
        value = Display_Tab];       

       
        P1M0 &= ~0xfc;        //数据端设置推挽输出
        P1M1 |= 0xfc;          
        P2M0 &= ~0x06;        //数据端设置推挽输出
        P2M1 |= 0x06;   
       
        P2M1 |= 0x20;         //公共端设置推挽输出
        P3M1 |= 0x1c;
       
        P16 = (bit) (value & 0x80);
        P21 = (bit) (value & 0x40);
        P13 = (bit) (value & 0x20);
        P14 = (bit) (value & 0x10);
        P15 = (bit) (value & 0x08);
        P17 = (bit) (value & 0x04);
        P12 = (bit) (value & 0x02);
        P22 = (bit) (value & 0x01);

        switch(g_byLedCount)//循环扫描
        {       
                case 0:
                {               
                        COM1 = 0;
                        COM2 = 1;
                        COM3 = 1;
                        COM4 = 1;
                } break;
               
                case 1:
                {
                        COM1 = 1;
                        COM2 = 0;
                        COM3 = 1;
                        COM4 = 1;
                } break;                       
                case 2:
                {
                        COM1 = 1;
                        COM2 = 1;
                        COM3 = 0;
                        COM4 = 1;
                } break;               
                case 3:
                {
                        COM1 = 1;
                        COM2 = 1;
                        COM3 = 1;
                        COM4 = 0;
                } break;
               
                default : break;
        }
}

Byte Get_Key ( void )
{
        BYTE byKey = 0;

       ET0 = 0;                //关闭中断(即关闭led扫描)

        P1M0 |= 0xfc;   //按键时,数据端高阻输入
        P1M1 &= ~0xfc;       
        P2M0 |= 0x06;   //按键时,数据端高阻输入
        P2M1 &= ~0x06;
       
        P2M1 &= ~0x20;        //按键时,公共端标准IO口
        P3M1 &= ~0x1c;
       
        if ( KEY_MENU == 0 )//是否按键了。
        {
               byKey = 1;
               gotoKEY_EXIT;
        }
        if ( KEY_UP == 0 ) //是否按键了。
        {
               byKey = 2;
               gotoKEY_EXIT;
        }
        if ( KEY_DOWN == 0 )//是否按键了。
        {
               byKey = 3;
               gotoKEY_EXIT;
        }
        if ( KEY_ENTER == 0 )//是否按键了。
        {
               byKey = 4;
        }

KEY_EXIT:               
        ET0 = 1;                                           //打开扫描显示

        return byKey;
}

按理说,扫描和按键,我都是重新设定端口方向的。为什么还是会相互有影响?

zjk 发表于 2013-5-16 09:06:30

貌似这样不行吧,用个74hc595试试啊

wenking99 发表于 2013-5-16 09:24:29

硬件是这样做的,别人这样用可以。

xiaobendan001 发表于 2013-5-16 09:42:00

扫描时间太长了吧,显示的时间也长,按键的时间也长
6MS*4=24MS了,也算可以吧
按键100MS就太长了,要是10次消抖动的话,岂不是1秒钟才能反应过来?
说一下我的设计习惯,把按键的COM接到一个IO上,每个按键串联二极管(防止两个同时按下时影响显示),在扫描显示输出的时候,读一下这个IO就行了,这样不用来回倒腾什么推挽啥的了,简单方便。我用的定时中断是2MS的,消抖动是计数10次有效,否则视为无效。

xiaobendan001 发表于 2013-5-16 09:56:09

本帖最后由 xiaobendan001 于 2013-5-16 09:57 编辑

wenking99 发表于 2013-5-16 09:24 static/image/common/back.gif
硬件是这样做的,别人这样用可以。

你要这样用也可以,显示用共阴的,然后不要分开操作,也不要去搞什么推挽了,就标准IO,就是定时器中断里面扫描输出后其他三个点是高电平的,此时检测这三个按键即可,这个开关下面的电阻有可能是高亮的数码管产生一点亮度,估计不会很明显。
看你的程序应该就是共阴的吧。

lmt50211 发表于 2013-5-16 19:39:21

void key_scan(void)
{
    P2IO &=0xe1;                        //*1110 0001*//

    delay(100);
    key_buf=0;                            //清按键值
    P1&=0x02;                           //关显示
    key_com=0;                            //开启按键公共端
    delay(100);

    if(key_2==0){key_buf|=0x01;}          //读按键1
    if(key_3==0){key_buf|=0x02;}          //读按键2
    if(key_4==0){key_buf|=0x04;}          //读按键3
    if(key_5==0){key_buf|=0x08;}          //读按键4

    P2IO |=0x1e;                        //*0001 1110*//
    key_com=1;                            //释放按键公共端

    if(key_buf==0)                        //无键
    {
      keyup_bit=0;                      //按键释放标志
      flash_not_bit=0;
      key_cnt_short=0;                  //清去抖计数
      key_cnt_long=0;                   //清长按计数
    }
    else                                  //有键处理
    {
      if(keyup_bit)                     //按键按下标志
      {
            if(++key_cnt_long>=200)      //长按 40->200
            {
                key_cnt_long-=50;         //4Hz   15->50
                flash_not_bit=1;          //快速调节不闪烁
                key_pro();                //按键处理
            }
            else
            {
                return;
            }
      }
      else                              //
      {
            if(++key_cnt_short>=20)       //去抖5->20
            {
                key_cnt_short=0;
                keyup_bit=1;            //短按
                short_buz(1,short_buz_time);
                key_pro();
            }
      }
    }
}

wenking99 发表于 2013-5-16 22:45:51

这个是长短按键处理吧。
因为共阴极没有用三极管驱动,要点亮数码管,必须要设定公共端为推挽输出,数码管数据端也必须设定为推挽输出才能点亮。
其它方式都不行。

要按键检测也必须设定数码管数据端为高阻输入,数码管公共端,即按键检测端为标准IO,才可以检测到按键。
其它方式都不行。

所以上述程序为什么要先设定端口模式的原因。

wenking99 发表于 2013-5-16 23:19:01

        while ( 1 )
        {
                WDT_action();//喂狗
                if ( g_time_5MS_Fg )//5ms数码管显示
                {
                        g_time_5MS_Fg = 0;
                        Display_Led();//LED显示程序不能跟下面Get_Key按键检测同时,用了数码管会闪,没有按键也会按下输出。
                        g_key_scan_count++;
                       
                        if ( g_key_scan_count >= 20 )//100ms
                        {
                                g_key_scan_count = 0;
                                //key = Get_Key ( );
                        }
                }

68336016 发表于 2013-5-16 23:29:53

怎么不正常法?说来看看

我刚刚整了个140多个数码管,30多个按键,数据IO口复用的,显示跟按键扫描都很正常。

wenking99 发表于 2013-5-16 23:43:31

LED显示程序不能跟下面Get_Key按键检测同时,用了数码管会闪,没有按键也会按下输出数据。

wenking99 发表于 2013-5-17 00:11:09

我现在单独用按键测试

Byte Get_Key ( void )
{
        BYTE byKey = 0;

        P1M0 |= 0xfc;    //数码管数据端高阻输入
        P1M1 &= ~0xfc;       
        P2M0 |= 0x06;   ///数码管数据端高阻输入
        P2M1 &= ~0x06;               

        P2M0 &= ~0x20;;        //数码管公共端设置为标准IO口,即按键输入端
        P2M1 &= ~0x20
      P3M0 &= ~0x1c;              //数码管公共端设置为标准IO口,即按键输入端
        P3M1 &= ~0x1c;
        P2 |= 0X20;            //设置按键输入引脚为高电平
        P3 |= 0X1C;             //设置按键输入引脚为高电平
       
        if ( P25 == 0 )//KEY_MENU按键按下
        {
              UartSendByte(1);        //测试
              byKey = 1;
        }

        //恢复推挽输出
        //P1M0 &= ~0xfc;        //数码管数据端设置为推挽输出
        //P1M1 |= 0xfc;          
        //P2M0 &= ~0x06; //数码管数据端设置为推挽输出
        //P2M1 |= 0x06;          

      //P2M0 &= ~0x20;
        //P2M1 |= 0x20;   //数码管公共端设置为推挽输出
      //P3M0 &= ~0x1c;
        //P3M1 |= 0x1c;    //数码管公共端设置为推挽输出

        return byKey;
}

如果只调试按键部分,很奇怪,为什么把后面注释部分加上去,按键都检测不到了。
扫描的时候,开始时不是都已经重新赋值了吗?

不明白怎么回事,高手指点。

xiaowu191 发表于 2013-5-17 01:11:23

attach://112793.png

哎呀呀,把电路改成这样吧

xiaowu191 发表于 2013-5-17 01:29:31

key_int任意姐一个IO口吧

定时器一直开着;

while ( 1 )
      {
                WDT_action();//喂狗
                if ( g_time_5MS_Fg )//5ms数码管显示
                {
                        g_time_5MS_Fg = 0;
                                               
                                                ////读按键
                                                if(!key_io)
                                                {
                                                        key_value |= (0x01 << g_byLedCount);
                                                }
                                                else
                                                {
                                                        key_value &= ~(0x01 << g_byLedCount);
                                                }
                                               
                        Display_Led();//LED显示程序不能跟下面Get_Key按键检测同时,用了数码管会闪,没有按键也会按下输出。
                        g_key_scan_count++;
                     
                        if ( g_key_scan_count >= 20 )//100ms
                        {
                              g_key_scan_count = 0;
                              //key = Get_Key ( );
                        }
                }

接下来,消抖,按下,弹开,长按,多按键,都是原来的思路

bbssilverkey 发表于 2013-5-17 08:59:35

ADC采集按键,轻松又愉快,想多少就搞多少

wenking99 发表于 2013-5-17 09:41:41

这样设计就是为了节省成本,所以用到这样方式。

wenking99 发表于 2013-5-17 09:49:44

我现在在扫描按键的时候,先把LED数码管公共端状态,还有数据端状态数据都保存,按键完成后,再恢复原来的状态。
又出现一个问题,按键检测不了。显示可以了。

wenking99 发表于 2013-5-17 09:51:21

上述电路如果改成那种方式,还需要多一个IO口来检测按键输入吧。

xiaobendan001 发表于 2013-5-17 10:01:14

wenking99 发表于 2013-5-17 09:51 static/image/common/back.gif
上述电路如果改成那种方式,还需要多一个IO口来检测按键输入吧。

我说的没看懂啊
都说了不要把COM设置成推挽,你用共阴的数码管,接COM的脚只是灌电流,干嘛非得推挽?是否推挽的灌电流都一样的

wenking99 发表于 2013-5-17 10:42:28

如果COM端不设置为推挽数码管根本点不亮。因为IO口没有驱动电路。

summarize 发表于 2013-5-17 11:21:12

进入Display_Led()函数时,首先调用Get_Key()函数,然后执行Display_Led()函数其它内容.

xiaobendan001 发表于 2013-5-17 13:43:33

本帖最后由 xiaobendan001 于 2013-5-17 13:50 编辑

wenking99 发表于 2013-5-17 10:42 static/image/common/back.gif
如果COM端不设置为推挽数码管根本点不亮。因为IO口没有驱动电路。

又有新发现,IO没有驱动?你用的神马单片机?
如此给你古老的89C51就更加不能使用了?

看脚的功能,似乎和我正在使用的STC12C5204AD是一样的,不知道对不对。
让我感到奇怪的是,你为什么不使用一整组IO驱动LED的8段,而是分到两组IO中了,这样程序不是很麻烦?效率很低?

xiaobendan001 发表于 2013-5-17 13:55:29

我知道了,你说的没有驱动是把IO设置为输入了,高阻的那种了吧

还有一种方法你可以试试,你是4个数码管的,把你的扫描次数设定为5个,在第五次的时候关闭所有的数码管,令4个COM都处于输入状态,然后去读取你的按键吧。

wenking99 发表于 2013-5-17 14:58:14

我不是说没有驱动。一般情况下,要点亮数码管,公共端要加三极管等驱动电路。
这个电路没有什么三极管放大,只有把端口设置为推挽方式。因为推挽方式驱动电流有15-20MA.
是的,跟你那个STC一样的芯片

wenking99 发表于 2013-5-17 15:11:28

我打算改一下电路,把按键输入接到数码管的数据端。

wenking99 发表于 2013-5-17 16:56:29

我觉得不管用哪种方式,还是分时复用的问题。按键跟LED扫描冲突了。

xiaobendan001 发表于 2013-5-17 17:10:15

推挽只是说其拉电流也可以达到这个值的,灌电流是一样的,仔细看看那个所谓的手册吧。

xiaobendan001 发表于 2013-5-17 17:13:31

wenking99 发表于 2013-5-17 16:56 static/image/common/back.gif
我觉得不管用哪种方式,还是分时复用的问题。按键跟LED扫描冲突了。

wenking99 发表于 2013-5-17 17:27:17

进入Display_Led()函数时,首先调用Get_Key()函数,然后执行Display_Led()函数其它内容.

while ( 1 )
      {
                WDT_action();//喂狗
                if ( g_time_5MS_Fg )//5ms数码管显示
                {
                        g_time_5MS_Fg = 0;
                        
                        g_key_scan_count++;
                        
                        if ( g_key_scan_count >= 20 )//100ms
                        {
                              g_key_scan_count = 0;
                              key = Get_Key ( );                     
                        }
                         Display_Led();/
                }
我觉得这样子做,显示和按键都是分开的。为什么还按键还是没有检测到?

wenking99 发表于 2013-5-17 18:45:30

while ( 1 )
      {
                WDT_action();//喂狗
                if ( g_time_5MS_Fg )//5ms数码管显示
                {
                        g_time_5MS_Fg = 0;
                        
                        g_key_scan_count++;                        
                     
                         Display_Led();//
                        if ( g_key_scan_count >= 20 )//100ms扫描一次按键
                         {
                              g_key_scan_count = 0;
                              key = Get_Key ( );      //这里要连续扫描两次才能输出键值                  
                              key = Get_Key ( );      //这里要连续扫描两次才能输出键值   
                         }
                }
}

但是上面会出现一个问题。按下时4个数码管会闪,长按会不停闪,频率大概是1秒闪一次。
不知道为什么要连续扫描两次按键才有数据输出,
而且数码管会闪烁。

wenking99 发表于 2013-5-17 19:15:20

P2M0 &= ~0x20;;      //数码管公共端设置为标准IO口,即按键输入端
P2M1 &= ~0x20
P3M0 &= ~0x1c;                //数码管公共端设置为标准IO口,即按键输入端
P3M1 &= ~0x1c;
P2 |= 0X20;            //设置按键输入引脚为高电平
P3 |= 0X1C;             //设置按键输入引脚为高电平

P2M0 &= ~0x20;;      //数码管公共端设置为标准IO口,即按键输入端
P2M1 &= ~0x20
P3M0 &= ~0x1c;                //数码管公共端设置为标准IO口,即按键输入端
P3M1 &= ~0x1c;
P2 |= 0X20;            //设置按键输入引脚为高电平
P3 |= 0X1C;             //设置按键输入引脚为高电平

进一步发现这个按键模式,及电平要设置两次,才行。

难道是延时不够?


wenking99 发表于 2013-5-17 19:32:01

进一步发现,只要在按键检测前加一个延时1us就可以正常按键,LED显示也正常了。

void delay_us ( BYTE time )
{
        BYTE i;
        for (i=0; i<time; i++)
        {
                ;
        }
}

Byte Get_Key ( void )
{
      BYTE byKey = 0;

      P1M0 |= 0xfc;    //数码管数据端高阻输入
      P1M1 &= ~0xfc;      
      P2M0 |= 0x06;   ///数码管数据端高阻输入
      P2M1 &= ~0x06;               

      P2M0 &= ~0x20;;      //数码管公共端设置为标准IO口,即按键输入端
      P2M1 &= ~0x20
      P3M0 &= ~0x1c;                //数码管公共端设置为标准IO口,即按键输入端
      P3M1 &= ~0x1c;
      P2 |= 0X20;            //设置按键输入引脚为高电平
      P3 |= 0X1C;             //设置按键输入引脚为高电平
      
       delay_us(1);             //延时1uS再读引脚电平。
      
      if ( P25 == 0 )//KEY_MENU按键按下
      {
            UartSendByte(1);      //测试
            byKey = 1;
      }
      return byKey;
}

canback 发表于 2013-5-17 19:58:09

wenking99 发表于 2013-5-17 14:58 static/image/common/back.gif
我不是说没有驱动。一般情况下,要点亮数码管,公共端要加三极管等驱动电路。
这个电路没有什么三极管放大 ...

接数码管阳极的管脚才需要设置成推挽输出,数码管阴极的,默认的输出就可以了。
你的是共阴数码管,不需要设置成推挽输出,程序里也就没这么多PxM0,PxM1的操作了。

xiaobendan001 发表于 2013-5-17 20:55:40

wenking99 发表于 2013-5-17 19:32 static/image/common/back.gif
进一步发现,只要在按键检测前加一个延时1us就可以正常按键,LED显示也正常了。

void delay_us ( BYTE tim ...

你用多少频率的晶振?
还是那个问题,没仔细看PDF文件,加延时的问题,在PDF文件里面有提及。
要踏实,不要浮躁!

wenking99 发表于 2013-5-17 21:05:37

接数码管阳极的管脚才需要设置成推挽输出,数码管阴极的,默认的输出就可以了。
你的是共阴数码管,不需要设置成推挽输出,程序里也就没这么多PxM0,PxM1的操作了。

我试过,数码管驱动和数据端必须设置为推挽输出,才可以点亮。

我晶振是24MHZ的

ijlc1314 发表于 2013-5-17 21:05:43

我也经常是这样子用,主程序2ms刷新一次,每次刷新一位数码管,或者说显示COM,数码管刷完了,再一个周期去做按键检测就可以了

static uint8_t mDisplayIndex;

switch ( mDisplayIndex )
{
case 0:...;mDisplayIndex = 1;break;
case 1:...;mDisplayIndex = 2;break;
case 2:...;mDisplayIndex = 3;break;
case 3:...;mDisplayIndex = 4;break;
case 4: Key_Read();;mDisplayIndex = 0;break;
}

显示的时候要设置成推挽输出,做按键检测的时候,要先关闭显示,把复用做按键检测的设置为输入带上拉

xiaobendan001 发表于 2013-5-18 08:18:31

wenking99 发表于 2013-5-17 21:05 static/image/common/back.gif
接数码管阳极的管脚才需要设置成推挽输出,数码管阴极的,默认的输出就可以了。
你的是共阴数码管,不需要 ...

数据端是要推挽的,因为你要用他的拉电流,COM就不用了吧,因为你只用他的灌电流。
24M是要加延时读取IO的,在PDF里面有,我用的6M的,似乎没有用过延时读取,不过我不是一次读取的,而是分几次的,而且我读取时往往在之前先做点别的什么,这样就不要专门的去延时了。
硬件不用改,软件很多办法可以用的,仔细琢磨一下吧。
还有,再重复一下,要仔细看PDF文件,尽管那个是有点乱。

wenking99 发表于 2013-5-18 09:31:20

刚才再试了一下,数码管数据端IO口设置为推挽输出,公共端设置为标准IO口,
这样是可以点亮数码管,也可以不加延时就可以检测到按键输入。
但电路中按键加了一个5.1K的下拉电阻,按键的时候,对应的数码管显示会闪烁。

wenking99 发表于 2013-5-18 09:32:03

或者说按键的时候,出现低亮度现象。

wenking99 发表于 2013-5-18 14:32:58

已经OK了。
数码显示正常,按键按下,数码管无任何闪烁,鬼影。
如果采用其他方式,比如数码管数据输出端采用推挽,数码管公共端采用标准I0模式,数码管可以正常显示,但是长按键时,有鬼影(即可以看到对应按键按下的数码管有低亮现象)。

程序流程如下:

按键扫描的时候数码管数据端采用高阻方式。
数码管公共端,即按键输入端采用标准IO方式。置按键电平为高。
然后延时1us.
再读取低电平,判断是否有键按下。

LED扫描的时候,数码管数据端与公共端都设置为推挽输出。

数码管扫描5MS扫描一次,
扫描扫描40MS扫描一次。


while ( 1 )
      {
                WDT_action();//喂狗
                if ( g_time_5MS_Fg )//5ms数码管显示
                {
                        g_time_5MS_Fg = 0;
                        
                        g_key_scan_count++;                        
                     
                         Display_Led();//
                        if ( g_key_scan_count >=8 )//40Ms扫描一次按键(小于40MS,LED显示会出现闪烁,估计是是高阻到推挽输出有延迟,或者从标准IO到推挽有延迟。)
                         {
                              g_key_scan_count = 0;
                              key = Get_Key ( );      //这里要连续扫描两次才能输出键值                  
                         }
                }
}

谢谢大家。

wenking99 发表于 2013-5-19 12:36:59

今天又优化了一下,效果很好,真正实现了分时复用。

按上面那位楼主xiaobendan001说的,

还有一种方法你可以试试,你是4个数码管的,把你的扫描次数设定为5个,在第五次的时候关闭所有的数码管,令4个COM都处于输入状态,然后去读取你的按键吧。

jz701209李 发表于 2013-5-19 15:59:18

路过.学习一下....

MCU开发2014 发表于 2014-5-19 11:24:31

bbssilverkey 发表于 2013-5-17 08:59
ADC采集按键,轻松又愉快,想多少就搞多少

ADC能不能实现多个按键一起按?
页: [1]
查看完整版本: 按键数码管显示复用后冲突问题