【51单片机】3. 数码管大师
1. 单个数码管基本定义
数码管连接方式:
分为共阳极和共阴极连接,如下图所示,上面为共阴极连接,下面为共阳极连接。
数码管引脚定义:
右侧从1开始逆时针到10,依次为1,2,3,…,8,9,10。上面数码管连接方式图中的标号就对应到具体的引脚。数码管定义的ABCDEFG&DP如左图所示,结合这些LED灯的编号和引脚编号可以得出连线图。
连线方式如下图所示:
2. 多个数码管定义
连接方式如下图所示,一共有12个引脚(上下的连接方式只是共阴极连接和共阳极连接的区别)。
则12,9,8,6控制具体亮哪一个LED灯,而其他控制显示的具体数字
每一个独立数码管显示(下方)都连接到了相同的接口。如果我们上方将12,9,8,6全部点亮(即所有数码管都显示数字),则每个数码管显示的都是具体ABCDEFG&DP连接的单片机的8个端口对应的结果。
这样的好处是,原本需要4*8=32个引脚控制的多个数码管,只用12个引脚就简单替代了
3. 动态数码管模块电路图分析
动态数码管模块电路图如下:
图中的COM通常接地或电源负极,这里可以看作这些LED管是共阴极连接,所以具体abcdefg&dp点亮哪一个,就要给哪一个置1。
0-9对应16进制如下(P07→P00):
int LEDNum[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
下边的74HC245是一个双向数据缓冲器。使用这个是因为:
-
高电平点亮灯与低电平点亮灯比起来,低电平会更亮一些(需要具体自己动手实验看)
-
这里的双向数据缓冲器,就是拿单片机的微弱电流作为输入信号,做信号控制,控制右侧B0~B7这部分的结果
-
B0~B7右侧由VCC直流供电,电源更够一些,保证数码管的亮度
-
综上,双向数据缓冲器就是用来增强驱动能力的
上图中LED连接到74H138译码器如下图所示:
译码器连接了VCC,所以在输出的LED中,需要通过置0,和VCC行成电压差点亮具体的LED(即给低电压点灯)。138是低电平译码!
通过CBA(P24,P23,P22)三个口来控制输出,具体如下:
-
C B A Output
-
0 0 0 0(右4)
-
0 0 1 1(右3)
-
0 1 0 2(右2)
-
0 1 1 3(右1)
-
1 0 0 4(左4)
-
1 0 1 5(左3)
-
1 1 0 6(左2)
-
1 1 1 7(左1)
好处就是仅需单片机3个接口控制8个灯具体亮哪个
还需要注意的是,无论是控制哪个单片机亮、还是控制显示什么数字,例如给P0端口输入,输入的顺序是从P07-P00,而译码器也是从P24-P22输入来控制对应的结果
4. 静态输入位置和数字
代码如下:
#include <REGX52.H>
#include <INTRINS.H>void Delay500ms() //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 4;j = 129;k = 119;do{do{while (--k);} while (--j);} while (--i);
}int LEDNum[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};void LEDShow(unsigned int LEDLocation,Number)
{switch(LEDLocation){case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;}P0 = LEDNum[Number];
}int main(void)
{while (1){// 检测所有位置显示是否正确LEDShow(1,8);Delay500ms();LEDShow(2,8);Delay500ms();LEDShow(3,8);Delay500ms();LEDShow(4,8);Delay500ms();LEDShow(5,8);Delay500ms();LEDShow(6,8);Delay500ms();LEDShow(7,8);Delay500ms();LEDShow(8,8);Delay500ms();// 检测所有数字显示是否正确LEDShow(3,0);Delay500ms();LEDShow(3,1);Delay500ms();LEDShow(3,2);Delay500ms();LEDShow(3,3);Delay500ms();LEDShow(3,4);Delay500ms();LEDShow(3,5);Delay500ms();LEDShow(3,6);Delay500ms();LEDShow(3,7);Delay500ms();LEDShow(3,8);Delay500ms();LEDShow(3,9);Delay500ms();}
}
给LEDShow函数传入具体显示的LED位置(板子上从左至右是第1-8个位置)以及要显示的具体数字,就能够得到对应的结果。
以上的代码有点长,简单展示在第5位显示8的结果如下:
5. 动态数码管显示
小节的要求是在多个位置上显示不同的数字。
动态数码管显示实际上是轮流点亮数码管(一个时刻内只有一个数码管是亮的),利用人眼的视觉暂留现象(余晖效应),看起来是所有数码管在同时显示。
基于第四小节的结果,我们可以设计本小节的代码如下(第一版):
#include <REGX52.H>
#include <INTRINS.H>void Delay(unsigned int ms) //@11.0592MHz
{unsigned char i, j;while (ms){_nop_();i = 2;j = 199;do{while (--j);} while (--i);ms--;}
}int LEDNum[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};void LEDShow(unsigned int LEDLocation,Number)
{switch(LEDLocation){case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;}P0 = LEDNum[Number];
}int main(void)
{while (1){LEDShow(1,2);LEDShow(2,0);LEDShow(3,0);LEDShow(4,1);LEDShow(5,0);LEDShow(6,6);LEDShow(7,0);LEDShow(8,6);}
}
结果如下:
看起来很花,我认为是闪烁过快没有延时导致的,于是我在LEDShow函数后面加了一个延时1ms,对应函数修改如下:
void LEDShow(unsigned int LEDLocation,Number)
{switch(LEDLocation){case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;}P0 = LEDNum[Number];Delay(1);
}
效果如下:
上面的现象减轻了一些,但依然存在,这是因为数码管需要消影。数码管的显示过程实际上是:
位选→段选→位选→段选→位选→段选→位选→段选......
但在段选和位选中间就会出现问题,导致把上一个段选的结果带入到下一个位选中,从而导致重影现象,所以在每一个位选→段选
过后将数码管置0。Delay仍然需要保留,因为去掉Delay的话会什么都不显示,因为单片机的执行速度很快,我们要让数字留下来,就要保留Delay
修改对应函数如下:
void LEDShow(unsigned int LEDLocation,Number)
{switch(LEDLocation){case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;}P0 = LEDNum[Number];Delay(1);P0 = 0x00;
}
最终效果如下:
完整代码:
#include <REGX52.H>
#include <INTRINS.H>void Delay(unsigned int ms) //@11.0592MHz
{unsigned char i, j;while (ms){_nop_();i = 2;j = 199;do{while (--j);} while (--i);ms--;}
}int LEDNum[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};void LEDShow(unsigned int LEDLocation,Number)
{switch(LEDLocation){case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;}P0 = LEDNum[Number];Delay(1);P0 = 0x00;
}int main(void)
{while (1){LEDShow(1,2);LEDShow(2,0);LEDShow(3,0);LEDShow(4,1);LEDShow(5,0);LEDShow(6,6);LEDShow(7,0);LEDShow(8,6);}
}