单片机实现数码管动态显示(0~99999999)并使用74HC138驱动
在本项目中,我们将介绍如何在单片机上实现一个八位数码管(显示0到99999999)的动态显示,并使用74HC138解码器来控制数码管的选择。74HC138是一种常见的3线至8线解码器,可以通过它来控制8个数码管的显示位置,使得我们能够动态地切换每个数码管并显示相应的数字。
1. 项目需求分析
目标:
- 八位数码管显示:在8个数码管上动态显示一个8位数字,支持0到99999999的数字显示。
- 使用74HC138解码器驱动:通过74HC138解码器控制8个数码管的位置,实现动态显示。
- 数码管显示内容:每个位数显示0-9的数字,通过动态扫描显示多个数字。
功能需求:
- 动态扫描显示:使用定时器中断周期性地切换数码管显示的位置。
- 数字显示:控制数码管显示对应的数字。
- 使用74HC138控制显示:通过74HC138的选择端(A、B、C)控制数码管的显示位置。
- 简易数字递增:为了方便观察效果,模拟数字从0到99999999递增并在数码管上显示。
2. 硬件设计
2.1 单片机选择
选择51系列单片机(如AT89C51)作为开发平台,原因是该单片机具有足够的I/O端口,适合控制多个数码管,并且资源较为丰富,便于开发。
2.2 数码管与74HC138解码器连接
- 74HC138解码器:它有3个输入端(A、B、C)用于选择8个输出端(Y0至Y7)。每个输出端对应一个数码管,输入端控制哪个数码管被选中。
- 数码管:使用8个共阴或共阳的七段数码管,通过单片机的端口控制每个数码管的段选。
- 连接设计 :
- 74HC138的输出端连接到每个数码管的位选端。
- 每个数码管的段选端连接到单片机的GPIO口。
2.3 数码管段选数据表
每个数字(0-9)对应的七段数码管的显示,可以使用一个数组来表示每个数字的段选状态(即每个数字的显示对应于7个段的开关状态)。
cpp
// 数码管段选数据(共阴,0表示亮,1表示灭)
unsigned char code digit[] = {
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F // 9
};
3. 软件设计
3.1 定时器中断
使用定时器中断来实现数码管的动态扫描。定时器定时触发中断,每次中断发生时,我们依次切换不同的数码管进行显示。
3.2 代码实现
cpp
#include <reg51.h> // 包含51系列单片机的寄存器定义文件
// 数码管段选数据(共阴,0表示亮,1表示灭)
unsigned char code digit[] = {
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F // 9
};
// 定义74HC138的输入端口(A, B, C)
sbit A = P3^0; // 74HC138的A端连接到P3.0
sbit B = P3^1; // 74HC138的B端连接到P3.1
sbit C = P3^2; // 74HC138的C端连接到P3.2
// 数码管的段选端口(假设连接在P2端口)
sbit SEG_A = P2^0;
sbit SEG_B = P2^1;
sbit SEG_C = P2^2;
sbit SEG_D = P2^3;
sbit SEG_E = P2^4;
sbit SEG_F = P2^5;
sbit SEG_G = P2^6;
// 定义一个8位数字
unsigned long display_number = 0; // 当前显示的数字,最大可显示到99999999
// 数码管显示函数
void display_digit(unsigned char position, unsigned char number) {
// 根据数字选择相应的段选数据
unsigned char seg = digit[number]; // 获取数字对应的段选数据
SEG_A = (seg >> 0) & 0x01;
SEG_B = (seg >> 1) & 0x01;
SEG_C = (seg >> 2) & 0x01;
SEG_D = (seg >> 3) & 0x01;
SEG_E = (seg >> 4) & 0x01;
SEG_F = (seg >> 5) & 0x01;
SEG_G = (seg >> 6) & 0x01;
}
// 选择要显示的数码管位置(通过74HC138)
void select_digit(unsigned char position) {
// 根据位置选择对应的数码管
switch (position) {
case 0: A = 0; B = 0; C = 0; break;
case 1: A = 0; B = 0; C = 1; break;
case 2: A = 0; B = 1; C = 0; break;
case 3: A = 0; B = 1; C = 1; break;
case 4: A = 1; B = 0; C = 0; break;
case 5: A = 1; B = 0; C = 1; break;
case 6: A = 1; B = 1; C = 0; break;
case 7: A = 1; B = 1; C = 1; break;
}
}
// 定时器中断服务函数
void Timer0_ISR(void) interrupt 1 {
static unsigned char pos = 0; // 当前显示的数码管位置
static unsigned char digits[8]; // 存储显示的每一位数字
// 将当前数字转换为8位数字数组
digits[0] = display_number % 10;
digits[1] = (display_number / 10) % 10;
digits[2] = (display_number / 100) % 10;
digits[3] = (display_number / 1000) % 10;
digits[4] = (display_number / 10000) % 10;
digits[5] = (display_number / 100000) % 10;
digits[6] = (display_number / 1000000) % 10;
digits[7] = (display_number / 10000000) % 10;
// 选择并显示当前数码管
select_digit(pos); // 选择当前数码管
display_digit(pos, digits[pos]); // 显示对应的数字
// 更新显示的数码管位置
pos++;
if (pos == 8) pos = 0; // 8个数码管显示完后重新从第一个开始
}
// 定时器初始化
void Timer0_Init() {
TMOD = 0x01; // 定时器0模式1,16位定时器
TH0 = 0xFC; // 设置定时器初值
TL0 = 0x66;
ET0 = 1; // 使能定时器0中断
EA = 1; // 开启总中断
TR0 = 1; // 启动定时器0
}
void main() {
// 初始化定时器0
Timer0_Init();
// 主循环,模拟数字递增
while (1) {
display_number++; // 模拟数字递增
if (display_number > 99999999) {
display_number = 0; // 数字达到最大值后重置
}
}
}
4. 代码解释
-
数码管段选数据 :
digit
数组包含0到9对应的七段数码管显示数据,每个数字对应一个字节,每个字节的7个位表示一个七段数码管的段(a-g)的开关状态。0表示该段亮,1表示该段灭。 -
display_digit
函数:此函数负责根据给定的位置(0-7)和数字,控制相应的段选端口(a-g)显示对应的数字。 -
select_digit
函数:此函数控制74HC138解码器的选择端A、B、C的状态,从而选择哪个数码管进行显示。 -
Timer0_ISR
函数 :定时器中断服务函数,每次定时器中断时,它都会更新当前显示的数码管,并通过select_digit
和display_digit
函数来显示数字。 -
Timer0_Init
函数:初始化定时器,设置为16位定时器模式,定时器溢出时会产生中断,从而周期性地更新数码管的显示。 -
主程序 :在主程序中,我们通过递增
display_number
来模拟数字从0到99999999的递增,并在数码管上动态显示。
5. 总结
通过定时器中断与74HC138解码器,我们实现了8个数码管的动态显示,显示范围为0到99999999的数字。每次定时器中断触发时,我们通过select_digit
选择显示的数码管,并通过display_digit
显示数字。这个方法有效地实现了多个数码管的动态显示,同时利用74HC138简化了数码管的控制。