【Protues仿真】基于AT89C52单片机的数码管驱动事例
目录
1 AT89C52单片机驱动单个数码管
1.1 数码管基础知识
1.1.1外观与引脚
1.1.2 共阴(CC) vs 共阳(CA)
1.1.3段码表(以数字1为例)
1.1.4驱动方式A. 直连IO(最简单,占用IO多)一个段一根线,共阴或共阳公共端固定接GND/VCC。适合单个数码管、教学实验。
1.1.6 快速排查清单
1.2 完整例子1——AT89C52单片机驱动单个数码管0~9循环计数
1.2.1 电路原理图
1.2.2 控制程序
1.3 完整例子2——AT89C52单片机实现比分计数器
1.3.1 74HC573透明锁存器
1.3.2 引脚与封装
1.3.3 真值表(简化)
1.3.4 典型应用:51 单片机驱动 4 位共阴七段数码管
1.3.5 最小示例(Keil C,12 MHz 晶振)
1.3.6 常见问题速查
1.4 AT89C52单片机实现比分计数器电路原理图
1.5 控制程序
摘要:本文介绍了AT89C52单片机驱动数码管的基础知识及实现方法。主要内容包括:1.数码管工作原理,包括共阴/共阳结构、段码表、驱动方式;2.两个具体应用实例:单个数码管0-9循环计数和比分计数器;3.详细硬件电路设计(含74HC573锁存器应用)和配套C语言程序实现;4.常见问题排查指南。通过定时器中断实现精确控制,采用动态扫描技术实现多位显示,并提供了完整的代码示例和电路原理图。
1 AT89C52单片机驱动单个数码管
1.1 数码管基础知识
7段数码管(7-segment display)是最常见的数字显示器件之一,广泛用于仪表、时钟、计数器等场合。
1.1.1外观与引脚
如上图所示,7个发光段分别命名为 a、b、c、d、e、f、g(顺时针方向排列),再加一个小数点 dp。
单个数码管常见封装:
直插 DIP-10(2.54 mm)
贴片 SMD-10
引脚排列没有统一标准,务必看数据手册或用万用表「二极管档」量:红笔固定接公共端,黑笔依次碰其它脚,某一段亮就说明该脚对应段。
1.1.2 共阴(CC) vs 共阳(CA)
共阴:所有 LED 阴极并在一起 → 公共脚接 GND,要点亮某段就给该段阳极送高电平(+V)。
共阳:所有 LED 阳极并在一起 → 公共脚接 +V,要点亮某段就给该段阴极送低电平(GND)。
记忆口诀:
共阴 = “给1亮”
共阳 = “给0亮”
1.1.3段码表(以数字1为例)
数字1需要点亮 b、c 两段。
类型 | a b c d e f g dp | 十六进制 | 说明 |
共阴 | 0 1 1 0 0 0 0 0 | 0x06 | b、c=1,其余=0 |
共阳 | 1 0 0 1 1 1 1 1 | 0xF9 | b、c=0,其余=1 |
完整段码速查:
0 1 2 3 4 5 6 7 8 9
共阴:0x3F 0x06 0x5B 0x4F 0x66 0x6D 0x7D 0x07 0x7F 0x6F
共阳:取反即可(~共阴 & 0xFF)
1.1.4驱动方式
A. 直连IO(最简单,占用IO多)
一个段一根线,共阴或共阳公共端固定接GND/VCC。
适合单个数码管、教学实验。
B. 译码/驱动芯片
74HC47(BCD→7段,共阳)
CD4511(BCD→7段,带锁存,共阴)
TM1637、MAX7219(集成扫描与亮度调节)
C. MCU+三极管/MOS(动态扫描多个位)
节省IO:段线共用,位选线轮流导通。
需要定时刷新(>100 Hz 无闪烁)。
1.1.5 多连排数码管动态扫描
以4位共阴为例:
12个IO:8段+4位选
流程:
关所有位(位选高)
送第N位段码
打开第N位(位选低)
延时1~2 ms
回到1,循环
伪代码:
const uint8_t seg_cc[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void display_4digit(uint16_t val){for(uint8_t pos=0; pos<4; pos++){set_digit_OFF();output_SEG(seg_cc[val%10]); // 输出段码set_digit_ON(pos); // 打开位delay_ms(2);val /= 10;}
}
1.1.6 快速排查清单
不亮:检查公共端电压、限流电阻(每段330 Ω~1 kΩ)。
乱亮:段码高低反了(把共阴/共阳搞反)。
闪烁:动态扫描频率太低,提高刷新或加定时器中断。
1.2 完整例子1——AT89C52单片机驱动单个数码管0~9循环计数
1.2.1 电路原理图
如上图所示,AT89C52单片机驱动单个数码管0~9循环计数电路原理图由AT89C52单片机、上拉排阻、数码管、晶振和复位电路组成,通过AT89C52单片机控制P0的输出电平实现数码管数字显示的控制。
1.2.2 控制程序
//头文件与位定义
# include <reg51.h>
# include <intrins.h>
//数码管对应数字
// 0:10000000 1:11110010 2:01001000 3:01100000 4:00110010 5:00100100 6:00000100 7:11110000 8:00000000 9:00100000
unsigned char display_table[] ={ 0x80, 0xf2, 0x48, 0x60, 0x32, 0x24, 0x04, 0xf0, 0x00, 0x20 }; // 共阳极数码管显示编码16进制
unsigned int i=0;
//定时器T0初始化
void inittimer()
{TMOD = 0x01; // T0 方式 1TH0 = 0xFC; // 1 msTL0 = 0x18;TR0 = 1; ET0 = 1; EA = 1;//中断设置值
}
//定时器延时函数
void Delay(unsigned int ms)
{unsigned int i;for (i = 0; i < ms; i++){while (!TF0); // 等待定时器T0溢出TF0 = 0; // 清除溢出标志}
}
//定时器T0模式1中断服务函数
void Timer0_ISR() interrupt 1
{TH0 = 0xFC; TL0 = 0x18;}
void main() // 主程序
{inittimer();while(1){P0 = 0x00;for(i = 0; i < 10; i++){if (i == 10 ) // 当i=10时,将0清零。就是当数码管显示9后,再从0开始{i = 0;}P0 = display_table[i];Delay(1000);}}
}
如上程序所示,程序使用定时器T0的T1模式实现精确延时控制,从而实现控制数码管的显数显示。
1.3 完整例子2——AT89C52单片机实现比分计数器
1.3.1 74HC573透明锁存器
74HC573是一块“8 位透明锁存器(Octal D-Type Transparent Latch)”,带三态输出,最常被拿来做“IO口扩展+数据保持”,比如用3片74HC573 就能让 8 位单片机口线分时输出 24 位数据,正好驱动 8×16 LED 点阵或多位七段数码管
1.3.2 引脚与封装
封装:DIP-20、SOP-20、TSSOP-20 等
关键脚位(俯视图左下角圆点逆时针数):
1 OE̅ 输出使能(低电平有效,拉高则 Q0~Q7 高阻)
11 LE 锁存使能(高=透明,低=锁存)
2~9 D0~D7 数据输入
19~12 Q0~Q7 三态输出
20 VCC,10 GND
1.3.3 真值表(简化)
OE̅ | LE | D | Q |
0 | 1 | X | 随D(透明) |
0 | 0 | X | 保持上次数据 |
1 | X | X | 高阻态 |
1.3.4 典型应用:51 单片机驱动 4 位共阴七段数码管
接线示意
MCU-P0 → 74HC573(段码) → 330 Ω 电阻 → a~g,dp
MCU-P2.0~P2.3 → 三极管 → 位选线(共阴端)
锁存器:
OE̅ 接地(一直使能)
LE → 单片机 P3.6(段锁存)
时序
1把段码送 P0
2给 LE 一个高脉冲(锁存段码)
3打开对应位选线,延时 2 ms
4关位选、重复扫描
1.3.5 最小示例(Keil C,12 MHz 晶振)
#include <reg52.h>
sbit HC573_LE = P3^6; // 段锁存
unsigned char code seg7[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void delay_ms(unsigned char t)
{unsigned int i;while(t--) for(i=0;i<123;i++);
}
void main()
{unsigned char i=0;while(1){P0 = seg7[i]; // 送段码HC573_LE = 1; // 锁存HC573_LE = 0;P2 = ~(1<<0); // 点亮第 1 位delay_ms(2);P2 = 0xFF; // 关闭所有位if(++i>9) i=0;}
}
1.3.6 常见问题速查
输出全高阻:OE̅ 被拉高,请接地。
数据写不进去:LE 没有拉到高电平或脉冲宽度不足(>10 ns 即可)。
电流不够:段码端只能提供 ±4 mA,驱动大电流 LED 请加三极管或 ULN2803。
1.4 AT89C52单片机实现比分计数器电路原理图
如上图所示,电路原理图由AT89C52单片机、上拉排阻、控制按键、晶振和复位电路、锁存器和6个数码管组成。通过五个独立按键控制高低电平,可以实现数值增加、减少或清零,并通过数码管显示数值。
1.5 控制程序
//头文件与位定义
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit AJJ= P2^1;//A-
sbit BJJ= P2^2;//B-
sbit CLC= P2^3;//A+
sbit AJ= P2^4;//A+
sbit BJ= P2^5;//B+
sbit LE_D = P2^6;//段选
sbit LE_W = P2^7;//位选uchar code table_D[] = {0xC0,0xF9,0xA4,0XB0,0x99,0x92,0x82,0xF8,0x80,0x98};//段值0-9
uchar code table_W[] = {0x01,0x02,0x04,0x08,0x10,0x20}; //位值1-6 uint time = 0;
uint Num_D = 0;
uint Num_W = 0;
uchar ge,shi,bai;//个 十 百位
uint num = 0;
uint num1 = 0;void init();//定时器T0初始化
void delay(uint dtime);//延时函数
void display1(uint num);//显示函数
void display2(uint num);//显示函数//定时器T0段选位选初始化
void init(){ LE_D = 0;LE_W = 0;AJ=1;BJ=1;TMOD = 0x01; //0000 0001TH0 = 0xFC; //1msTL0 = 0x17;EA = 1;ET0 = 1;TR0 = 1;
}//定时器T0延时函数
void delay(unsigned int dtime)
{unsigned int i;for (i = 0; i <dtime; i++){while (!TF0); // 等待定时器T0溢出TF0 = 0; // 清除溢出标志}
}
//定时器T0模式1中断服务函数
void Timer0_ISR() interrupt 1
{TH0 = 0xFC; TL0 = 0x17;time++;
}void main(){init();num1 = 0;num1 = 0;while(1){if((time >= 300)&&(AJ==0)){time=0;num1++;}else if((time >= 300)&&(BJ==0)){time=0;num++;}else if((time >= 300)&&(AJJ==0)&&(num>0)){time=0;num1--;}else if((time >= 300)&&(BJJ==0)&&(num>0)){time=0;num--;}else if((time >= 300)&&(CLC==0)){time=0;num=0;num1=0;}display1(num1);display2(num);}
}//后三个数码管显示数字的函数
void display1(uint num){ bai = num / 100; //百位应显示的数字shi = num % 100 /10; //十位应显示的数字ge = num % 10; //个位应显示的数字//数码管显示个位数字LE_D = 1;P0 = table_D[ge]; //段——>位 会在段所指的位置上显示段LE_D = 0; //位——>段 新的数字会显示到之前的位置上LE_W = 1;P0 = 0x20; //0010 0000LE_W = 0;delay(1);LE_W = 1; //消隐P0 = 0;LE_W = 0;//数码管显示十位数字LE_D = 1;P0 = table_D[shi]; LE_D = 0; LE_W = 1;P0 = 0x10; //0001 0000LE_W = 0;delay(1);LE_W = 1; //消隐P0 = 0;LE_W = 0;//数码管显示百位数字LE_D = 1;P0 = table_D[bai];LE_D = 0;LE_W = 1;P0 = 0x08; //0000 1000LE_W = 0;delay(1);LE_W = 1; //消隐P0 = 0;LE_W = 0;
}//前三个数码管显示数字的函数
void display2(uint num){ bai = num / 100; //百位应显示的数字shi = num % 100 /10; //十位应显示的数字ge = num % 10; //个位应显示的数字//数码管显示个位数字LE_D = 1;P0 = table_D[ge]; //段——>位 会在段所指的位置上显示段LE_D = 0; //位——>段 新的数字会显示到之前的位置上LE_W = 1;P0 = 0x04; //0000 0100LE_W = 0;delay(1);LE_W = 1; //消隐P0 = 0;LE_W = 0;//数码管显示十位数字LE_D = 1;P0 = table_D[shi]; LE_D = 0; LE_W = 1;P0 = 0x02; //0000 0010LE_W = 0;delay(1);LE_W = 1; //消隐P0 = 0;LE_W = 0;//数码管显示百位数字LE_D = 1;P0 = table_D[bai];LE_D = 0;LE_W = 1;P0 = 0x01; //0000 0001LE_W = 0;delay(1);LE_W = 1; //消隐P0 = 0;LE_W = 0;
}