解决数码管和LED显示干扰问题——蓝桥杯经验分享
#数码管显示
数码管就是八个LED灯,针对共阳极数码管,为低电平点亮。a,b,c,d,e,f,g,dp分别对应于P0~P7。因此我们想要让数码管显示0,则让g和dp为高即可,即11000000
,对应十六进制的0xc0
。
##数码管的控制
在蓝桥杯开发板中,数码管的状态通过两个锁存器来控制U8、U7。使能Y6C(置高)将启动位选锁存器,获取点亮数码管的位数据(0x01-0x80),拉低用Y6C(置低)锁存当前值。位数据可通过对P0赋值获得。同理,使能Y7C可以启动锁存器获取段码值(0-9),也是通过P0口输入给锁存器。
因此,假如我们想要使第一个数码管显示0,我们应该先选择数码管,再给段码值。
第一步,使能Y6C:
第二步,向P0赋值0x01
,选择第一个数码管:
第三步,使能Y7C:
第四步,向P0赋值0xc0
(共阳),让数码管显示0:
#LED显示
蓝桥杯开发板上的LED接法为阳极接高电平,阴极接锁存器的输出端。因此我们想要点亮某个LED,必须将对应的端口置低,锁存器的输出和P0的值是一一对应的。
因此,加入我们想要点亮第一个LED,我们应该先使能Y4C,然后给P0赋对应的值。
第一步,使能Y4C:
第二部,向P0赋值0xfe
,即11111110
,第一位为底其他为高,第一个LED将被点亮:
#锁存器的控制
上面说到,无论是显示数码管还是LED,都先要使能对应的锁存器。在蓝桥杯开发板中,锁存器的控制主要通过P25~P27即P2口的高三位来实现。
如果我们想要显示LED,那么我们的第一步就是使能Y4C,如上图,WR默认接地(IO模式),接P42则为MM模式。我们想要让Y4C输出高,对于74HC02或非门来说,WR和Y4只能都为低,WR默认为低。因此我们只需要控制Y4的状态即可,而Y4的状态又通过14HC138来控制,对于38译码器,它的真值表如上。所以我们只需要让P25~P27为001,即可使Y4C为低。
因此我们了解到,如果我们想要显示LED或者数码管,我们只需要操作P2口的高三位(使能锁存器)和P0口(给数据或者段码)即可
代码示例
//第一个数码管显示0
P2 = (P2&0X1F)| 0xc0;//初始化P2口的高三位为0,然后将高三位赋值为110,使能Y6C
P0=0x01; //选择第一个数码管
P2 =(P2&0X1F)| 0xe0;//使能Y7C
P0=0xc0;
//点亮第一个LED
P2= (P2&0X1F) | 0X80; //使能Y4C
P0=0XFE; //把P0的第一位置低
同时我们应该意识到一个问题,数码的显示和LED的显示,都需要P0作为数据的输入端。那么当需要同时显示LED和数码管的时候,会不会出现他们两者互相影响的情况呢。在实际的操作中我也遇到了这种问题:当程序中数码管用中断显示的时候,显示LED的效果往往不好,容易出现不应该亮的LED微微发亮的情况和数码管显示不稳定的情况。
为了解决上面出现的情况,我的解决办法是,当使用完P2后,及时将高三位清零,使锁存器工作在锁存状态,显示完数码管后将P0的值进行一初始化,显示LED的时候,通过声明变量来保存赋值给P0的值
代码示例
代码将实现,全部数码管显示0-7,同时LED在按键控制下从左到右依次被点亮
#include "stc15f2k60s2.h"
#define uchar unsigned char
#define uint unsigned int
sbit S4=P3^3;
void InitTimer0();
void keyscan();
//声明数码管的段码,方便显示调用
uchar code SEG_daun[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
//声明LED显示的段码
uchar code LED_duan[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
uchar led=0xff; //声明变量保存LED的数据
void main()
{
InitTimer0();
while(1)
{
keyscan();
}
}
void Delay10ms() //@12.000MHz
{
unsigned char i, j;
i = 117;
j = 184;
do
{
while (--j);
} while (--i);
}
//为了方便使能各个锁存器,往往把使能条件写在一个函数中供方便使用,输入形参n,即可使能对应的锁存器
void Cro_hc138(uchar n)
{
switch(n)
{
case 4: P2 = (P2&0X1F) | 0X80; break; //使能Y4C
case 5: P2 = (P2&0X1F) | 0XA0; break;
case 6: P2 = (P2&0X1F) | 0XC0; break;
case 7: P2 = (P2&0X1F) | 0XE0; break;
}
}
//按照数码管显示步骤,写一个显示一个数码管的函数,我们可以通过这个函数来选择显示哪一个数码管,显示什么数据,只需要给定两个形参即可
void SEG_bit(uchar p,uchar dat)
{
Cro_hc138(6); //使能Y6C,第一步
P0= 0x01 << p; //选择第几个数码管,若p=0,即1左移零位,还是0x01,为第一位数码管
Cro_hc138(7); //使能Y7C,第三步
P0=dat; //给P0赋值段码,第四步
}
//按键检测程序,按键按下,下一个LED被点亮
void keyscan()
{
static uchar i=0;
if(S4==0)
{
Delay10ms();//消抖
if(S4==0)
{
while(S4==0);//等待按键弹起
led=LED_duan[i];// 初始i为0,按键按下一次,i增加1,实现LED轮流点亮
i++;
i&=0x07; // i等于8的时候,清零,可以自己慢慢想一想,很妙用
}
}
}
//写一个专门的LED显示程序,放到中断里执行
void Dispaly_led()
{
Cro_hc138(4);//使能Y4C,第一步
P0=led; //赋值,第二步
}
//因为要同时显示八个数码管,原理上还是每次只显示一个,但是我可以缩短每一个数码管显示的时间,每个只显示1ms,来达到视觉暂留即看起来全部同时亮了的目的。使用定时器中断,首先先初始化
void InitTimer0()
{
TMOD=0X00; // 配置定时器的工作模式,可参考手册定时器模式控制寄存器IMOD
TH0=(65536-1000)/255; //15定时器默认采用12T模式,即1us记一次数,在此定时1ms
TL0=(65536-1000)%255;
ET0=1;
EA=1; //开中断
TR0=1; //启动计时
}
//T0中断服务函数,在中断里显示数码管和LED
void Timer0() interrupt 1
{
static uchar i=0;//声明静态变量,目的是为了每次进入此函数,i的值不会被清零
Dispaly_led();
SEG_bit(i,SEG_daun[i]);//刚开始i=0,p=0x01,dat=0xc0,第一个数码管亮,显示0,以此类推
i++;
if(i==8) //当显示完最后一个数码管的时候,从头再来,此时全部数码管都显示零
i=0;
P2 &= 0X1F;
P0=0XFF;
}
`