按键数码管显示复用后冲突问题
用的51单片机,附电路图。按键KEY与数码管COM端是复用的。
现在可以知道,把数码管的数据输出端设置为高阻输入。
按键即(COM)端,设置为标准IO口,可以检测到按键输入。
数码管显示时,
数码管的数据输出端设置为推挽输出。
按键即(COM)端,同时要设置为推挽才可以显示正常,
数码管显示用的是定时器中断显示。(6ms)。
按键在主循环,扫描输入(100ms)扫描1次。
用了按键程序,LED显示不正常。
用了LED显示程序,按键程序不正常。
不知道为什么?有没有人指一下点?
没有人看,自己顶一下 显示的时候不要扫描键盘
共阴数码管,则com1~com4会循环输出低电平.R1接地端改为接VCC,R1的另一端接单片机IO,当comX输出低电平后,读取R1连接的IO状态,如果为低电平,则有按键按下,并且这个按键就是连接comX的那个按键.此电路的缺点是不能同时按下两个按键,如果要支持同时按下两个按键,则每个按键与R1连接间加入一个二极管进行隔离,二极管方向是从R1流向按键. 这个电路不需要同时按下两个按键的。
按键的时候,我已经把定时器关闭了的。
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;
}
按理说,扫描和按键,我都是重新设定端口方向的。为什么还是会相互有影响? 貌似这样不行吧,用个74hc595试试啊 硬件是这样做的,别人这样用可以。 扫描时间太长了吧,显示的时间也长,按键的时间也长
6MS*4=24MS了,也算可以吧
按键100MS就太长了,要是10次消抖动的话,岂不是1秒钟才能反应过来?
说一下我的设计习惯,把按键的COM接到一个IO上,每个按键串联二极管(防止两个同时按下时影响显示),在扫描显示输出的时候,读一下这个IO就行了,这样不用来回倒腾什么推挽啥的了,简单方便。我用的定时中断是2MS的,消抖动是计数10次有效,否则视为无效。 本帖最后由 xiaobendan001 于 2013-5-16 09:57 编辑
wenking99 发表于 2013-5-16 09:24 static/image/common/back.gif
硬件是这样做的,别人这样用可以。
你要这样用也可以,显示用共阴的,然后不要分开操作,也不要去搞什么推挽了,就标准IO,就是定时器中断里面扫描输出后其他三个点是高电平的,此时检测这三个按键即可,这个开关下面的电阻有可能是高亮的数码管产生一点亮度,估计不会很明显。
看你的程序应该就是共阴的吧。 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();
}
}
}
} 这个是长短按键处理吧。
因为共阴极没有用三极管驱动,要点亮数码管,必须要设定公共端为推挽输出,数码管数据端也必须设定为推挽输出才能点亮。
其它方式都不行。
要按键检测也必须设定数码管数据端为高阻输入,数码管公共端,即按键检测端为标准IO,才可以检测到按键。
其它方式都不行。
所以上述程序为什么要先设定端口模式的原因。 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 ( );
}
} 怎么不正常法?说来看看
我刚刚整了个140多个数码管,30多个按键,数据IO口复用的,显示跟按键扫描都很正常。 LED显示程序不能跟下面Get_Key按键检测同时,用了数码管会闪,没有按键也会按下输出数据。 我现在单独用按键测试
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;
}
如果只调试按键部分,很奇怪,为什么把后面注释部分加上去,按键都检测不到了。
扫描的时候,开始时不是都已经重新赋值了吗?
不明白怎么回事,高手指点。 attach://112793.png
哎呀呀,把电路改成这样吧 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 ( );
}
}
接下来,消抖,按下,弹开,长按,多按键,都是原来的思路
ADC采集按键,轻松又愉快,想多少就搞多少 这样设计就是为了节省成本,所以用到这样方式。 我现在在扫描按键的时候,先把LED数码管公共端状态,还有数据端状态数据都保存,按键完成后,再恢复原来的状态。
又出现一个问题,按键检测不了。显示可以了。 上述电路如果改成那种方式,还需要多一个IO口来检测按键输入吧。 wenking99 发表于 2013-5-17 09:51 static/image/common/back.gif
上述电路如果改成那种方式,还需要多一个IO口来检测按键输入吧。
我说的没看懂啊
都说了不要把COM设置成推挽,你用共阴的数码管,接COM的脚只是灌电流,干嘛非得推挽?是否推挽的灌电流都一样的
唉 如果COM端不设置为推挽数码管根本点不亮。因为IO口没有驱动电路。 进入Display_Led()函数时,首先调用Get_Key()函数,然后执行Display_Led()函数其它内容. 本帖最后由 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中了,这样程序不是很麻烦?效率很低? 我知道了,你说的没有驱动是把IO设置为输入了,高阻的那种了吧
还有一种方法你可以试试,你是4个数码管的,把你的扫描次数设定为5个,在第五次的时候关闭所有的数码管,令4个COM都处于输入状态,然后去读取你的按键吧。 我不是说没有驱动。一般情况下,要点亮数码管,公共端要加三极管等驱动电路。
这个电路没有什么三极管放大,只有把端口设置为推挽方式。因为推挽方式驱动电流有15-20MA.
是的,跟你那个STC一样的芯片 我打算改一下电路,把按键输入接到数码管的数据端。 我觉得不管用哪种方式,还是分时复用的问题。按键跟LED扫描冲突了。 推挽只是说其拉电流也可以达到这个值的,灌电流是一样的,仔细看看那个所谓的手册吧。 wenking99 发表于 2013-5-17 16:56 static/image/common/back.gif
我觉得不管用哪种方式,还是分时复用的问题。按键跟LED扫描冲突了。
进入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();/
}
我觉得这样子做,显示和按键都是分开的。为什么还按键还是没有检测到? 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秒闪一次。
不知道为什么要连续扫描两次按键才有数据输出,
而且数码管会闪烁。 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; //设置按键输入引脚为高电平
进一步发现这个按键模式,及电平要设置两次,才行。
难道是延时不够?
进一步发现,只要在按键检测前加一个延时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;
}
wenking99 发表于 2013-5-17 14:58 static/image/common/back.gif
我不是说没有驱动。一般情况下,要点亮数码管,公共端要加三极管等驱动电路。
这个电路没有什么三极管放大 ...
接数码管阳极的管脚才需要设置成推挽输出,数码管阴极的,默认的输出就可以了。
你的是共阴数码管,不需要设置成推挽输出,程序里也就没这么多PxM0,PxM1的操作了。
wenking99 发表于 2013-5-17 19:32 static/image/common/back.gif
进一步发现,只要在按键检测前加一个延时1us就可以正常按键,LED显示也正常了。
void delay_us ( BYTE tim ...
你用多少频率的晶振?
还是那个问题,没仔细看PDF文件,加延时的问题,在PDF文件里面有提及。
要踏实,不要浮躁! 接数码管阳极的管脚才需要设置成推挽输出,数码管阴极的,默认的输出就可以了。
你的是共阴数码管,不需要设置成推挽输出,程序里也就没这么多PxM0,PxM1的操作了。
我试过,数码管驱动和数据端必须设置为推挽输出,才可以点亮。
我晶振是24MHZ的 我也经常是这样子用,主程序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;
}
显示的时候要设置成推挽输出,做按键检测的时候,要先关闭显示,把复用做按键检测的设置为输入带上拉
wenking99 发表于 2013-5-17 21:05 static/image/common/back.gif
接数码管阳极的管脚才需要设置成推挽输出,数码管阴极的,默认的输出就可以了。
你的是共阴数码管,不需要 ...
数据端是要推挽的,因为你要用他的拉电流,COM就不用了吧,因为你只用他的灌电流。
24M是要加延时读取IO的,在PDF里面有,我用的6M的,似乎没有用过延时读取,不过我不是一次读取的,而是分几次的,而且我读取时往往在之前先做点别的什么,这样就不要专门的去延时了。
硬件不用改,软件很多办法可以用的,仔细琢磨一下吧。
还有,再重复一下,要仔细看PDF文件,尽管那个是有点乱。 刚才再试了一下,数码管数据端IO口设置为推挽输出,公共端设置为标准IO口,
这样是可以点亮数码管,也可以不加延时就可以检测到按键输入。
但电路中按键加了一个5.1K的下拉电阻,按键的时候,对应的数码管显示会闪烁。 或者说按键的时候,出现低亮度现象。 已经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 ( ); //这里要连续扫描两次才能输出键值
}
}
}
谢谢大家。 今天又优化了一下,效果很好,真正实现了分时复用。
按上面那位楼主xiaobendan001说的,
还有一种方法你可以试试,你是4个数码管的,把你的扫描次数设定为5个,在第五次的时候关闭所有的数码管,令4个COM都处于输入状态,然后去读取你的按键吧。
路过.学习一下.... bbssilverkey 发表于 2013-5-17 08:59
ADC采集按键,轻松又愉快,想多少就搞多少
ADC能不能实现多个按键一起按?
页:
[1]