【C51】关于 LED 数码管的一些事

开始更新 C51 单片机的学习笔记,直接从 LED 数码管开始讲起吧
本文基于普中 51- 单核 -A2 开发板

基础

LED 数码管的工作原理

常见的 LED 数码管是由 8 个发光二极管组成,分别是 a, b, c, d, e, f, g, dp(小数点 Decimal Point),顺时针

有两种接法,共阴和共阳,顾名思义,共阴就是阴极相接,公共阴极接地,阳极接高电平时,二极管点亮
共阳则相反,为阳极相接,公共阳极接正电压,阴极接低电平时,二极管点亮

如果按照共阳接法则单片机可以直接驱动显示,如果按照共阴接法则单片机不能直接驱动显示,因为单片机的 IO 提供的电流大小不够驱动数码管内部的 LED 显示,需要外部电路来提供一个大电流驱动的芯片来解决(74HC245 芯片就是起的这个作用)

普中 51- 单核 -A2 开发板

板子上的数码管是四位一体数码管,也就是将四个数码管封装到一起的。每个数码管的 a, b, c, d, e, f, g, dp 都是并联在一起的,称为段口,四个公共端单独引出,称为位选。两个四位一体数码管并联,组成了一个能显示八位数的大数码管

所以我们需要先控制显示在哪里,再控制显示什么,也就需要通过段码和位码来分别控制

但是位码需要控制 8 个数码管的亮灭,那我们就需要 8 个引脚,我们可以看到上图中其实还有一个 74LS138 译码器,它的作用就是可以把三位地址数转成 8 位地址,这样就可以省 5 个 IO 引脚

数码管段码

默认情况下,位码就是 1111 1111 显示在第八个数码管上,0 的位置就是显示的数码管的位置

74138 功能表

电路板上,A2A1A0 对应着 P2 口的 234 引脚

电路图

两种显示方式

静态显示就是一个数码管单独由一个 8 位 IO 口来控制,如果是只有一个数码管,那肯定没得选,肯定是静态显示

如果多个数码管吗,显然静态显示不合适,那得多少个 IO 端口啊,这板子上满打满算总共才 4 个

动态显示则是,将多个数码管并联,通过一个 IO 口来控制,虽然不能同时点亮 8 个数码管,但是可以一次点亮一个啊,由于人眼的余晖效应,也就是视觉暂停,会持续一段时间,只要低于 1/24 秒,人眼根本发现不了 LED 灯曾经熄灭过

我们就可以循环点亮 8 个数码管,延时小于1/24/n-1s 即可,太短亮度低,有重影,太长就会闪,好像这么算没啥用,随便延个 5ms 左右,看着没问题就行了

实例

数码管显示任意数字

要求

显示任意给定的数字

思路

给定的数直接拆开放入一个数组中

把段码放进一个数组中,这样我们就可以用数组下标来表示这个数了

74138 译码器使用了 P2 口的 234 引脚,我选择使用 switch 为三个引脚单独赋值,从而控制显示位置

用一个 for 循环来控制数码管显示,由于是从右到左显示,还需要倒序输出出来

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <reg52.h>
#define u8 unsigned char
#define u16 unsigned int

sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

void delay(u16 n)
{
u16 i,j;
for(i=0; i<n; i++)
for(j=0; j<123; j++);
}

// 共阴接法
u8 code duanMa[16] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 };
u8 string[8] = {1, 2, 3, 4, 5, 6, 7, 8};

void setWeiMa(u8 i)
{
switch(i) //位选,选择点亮的数码管,
{
case(0):
LSA=0;LSB=0;LSC=0; break;//显示第0位
case(1):
LSA=1;LSB=0;LSC=0; break;//显示第1位
case(2):
LSA=0;LSB=1;LSC=0; break;//显示第2位
case(3):
LSA=1;LSB=1;LSC=0; break;//显示第3位
case(4):
LSA=0;LSB=0;LSC=1; break;//显示第4位
case(5):
LSA=1;LSB=0;LSC=1; break;//显示第5位
case(6):
LSA=0;LSB=1;LSC=1; break;//显示第6位
case(7):
LSA=1;LSB=1;LSC=1; break;//显示第7位
}
}

void main()
{
u8 i;
while(1)
{
for(i=0; i<8; i++)
{
setWeiMa(i);
P0 = duanMa[string[7-i]]; //逆序输出
delay(1);
}
}
}

数码管计数

要求

key1 按下数值加一,key2 按下数值减一,key3 按下复位,数值为 0

思路

按键进行数值的加减很简单,得到这个数字后把每一位拆分开,放进一个数组中

用一个 for 循环来控制数码管显示,第一个数码管显示第一位,第二个数码管显示第二位,依次类推,最多可以显示 8 位数字

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <reg52.h>
#define u8 unsigned char
#define u16 unsigned int
#define dataPort P0 // 定义数据端口

// 定义按键输入端口
sbit KEY_ADD = P3 ^ 0; // 加
sbit KEY_DEC = P3 ^ 1; // 减
sbit KEY_REC = P3 ^ 2; // 复位

// 控制显示的位置
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

u8 code duanMa[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f}; // 显示段码值 0~9
u8 tempData[8]; // 存储显示值的全局变量

void display(u8 firstBit, u8 num);
void delay(u16 n);

// 主函数
void main(void)
{
u16 num = 0;
u16 temp;
u8 i;
KEY_ADD = 1;
KEY_DEC = 1;
while (1)
{
if (!KEY_ADD)
{
delay(10);
if (!KEY_ADD)
{
while (!KEY_ADD) ;
num++;
}
}

if (!KEY_DEC)
{
delay(10);
if (!KEY_DEC)
{
while (!KEY_DEC) ;
if (num > 0)
num--;
}
}

if (!KEY_REC)
{
delay(10);
if (!KEY_REC)
{
while (!KEY_REC) ;
num = 0;
}
}

// num 不为 0
if(num)
{
temp = num;
for(i =0; temp > 0; i++)
{
tempData[i] = duanMa[temp % 10];
temp /= 10;
}
}
else // num 为 0
{
for(i = 0; i<8; i++)
tempData[i] = 0; // 熄灭所有数码管
tempData[0] = duanMa[0]; // 第一个数码管显示为 0
}

display(0, i); // 从第一数码管开始显示,显示 i 位数,可以显示最多 8 位数
}
}

// 延时函数
void delay(u16 n)
{
u16 i, j;
for (i = 0; i < n; i++)
for (j = 0; j < 123; j++)
;
}

void setWeiMa(u8 i)
{
switch(i) // 位选,选择点亮的数码管,
{
case(0):
LSA=0;LSB=0;LSC=0; break;// 显示第 0 位
case(1):
LSA=1;LSB=0;LSC=0; break;// 显示第 1 位
case(2):
LSA=0;LSB=1;LSC=0; break;// 显示第 2 位
case(3):
LSA=1;LSB=1;LSC=0; break;// 显示第 3 位
case(4):
LSA=0;LSB=0;LSC=1; break;// 显示第 4 位
case(5):
LSA=1;LSB=0;LSC=1; break;// 显示第 5 位
case(6):
LSA=0;LSB=1;LSC=1; break;// 显示第 6 位
case(7):
LSA=1;LSB=1;LSC=1; break;// 显示第 7 位
}
}

// firstBit 表示显示的初始位置,0 就从第一个数码管开始开始显示
// num 就是数的长度
void display(u8 firstBit, u8 len)
{
u8 i;
for (i = 0; i < len; i++)
{
dataPort = 0; // 清空数据,防止有交替重影
setWeiMa(i + firstBit); // 显示的位置
dataPort = tempData[i]; // 取段码
delay(2); // 扫描间隙延时,时间太长会闪烁,太短会造成重影
}
}
文章作者: ourongxing
文章链接: https://orxing.top/post/8d929ede.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 OURONGXING

评论