51单片机-按键LED-数码管定时

本实验基于STC89C52单片机(兼容AT89C52),实现两个核心功能:按键控制LED亮灭定时器驱动数码管动态显示。通过本实验,读者可以掌握51单片机GPIO输入输出、独立按键检测、定时器中断以及数码管动态扫描的基本原理与编程方法。

2. 硬件原理

2.1 按键电路

本实验使用4个独立按键,分别连接P1.4~P1.7引脚。按键未按下时,引脚通过上拉电阻保持高电平;按下后引脚被拉低为低电平。因此程序通过读取引脚电平即可判断按键状态。

独立按键的典型接法如下:

  • 按键一端接单片机I/O引脚,另一端接地。
  • 引脚外部接10kΩ上拉电阻到VCC,保证未按下时为高电平。
  • 按下按键时引脚被拉低,程序检测到低电平即认为按键被按下。

2.2 LED指示电路

4个LED分别连接P2.0~P2.3引脚,采用低电平点亮方式。当引脚输出低电平时,LED导通发光;输出高电平时,LED熄灭。

2.3 数码管显示电路

数码管采用共阴极接法,段选信号由P0口输出,位选信号由P1.0~P1.3控制。通过动态扫描方式,利用人眼视觉暂留效应实现多位数字同时显示。

3. 晶体管(三极管)驱动原理

在数码管和LED驱动电路中,晶体管(三极管)起到电流放大开关控制的作用。51单片机I/O口驱动能力有限(拉电流约0.5mA,灌电流约20mA),当需要驱动较大电流的负载时,必须借助晶体管进行功率放大。

3.1 NPN三极管开关电路

以NPN型三极管(如S8050)为例,其工作在开关状态时:

  • 截止状态:基极电压低于0.7V,集电极与发射极之间不导通,负载断电。
  • 饱和导通状态:基极电流足够大,集电极与发射极之间近似短路,负载通电。

典型驱动电路:单片机引脚通过限流电阻(1kΩ~10kΩ)连接三极管基极,集电极接负载(如LED或数码管公共端),发射极接地。当引脚输出高电平时,三极管导通,负载工作。

3.2 定时器控制晶体管闪烁

利用定时器中断产生周期性电平翻转,即可控制三极管周期性导通与截止,从而实现LED的闪烁效果。定时器每溢出一次,中断服务程序中将控制引脚电平取反,LED即按固定频率闪烁。

c 复制代码
#include <reg52.h>
#include <intrins.h>

sbit LedEn = P1^0;      // 三极管基极控制引脚
sbit Led = P2^0;        // LED指示灯

// 定时器0中断服务函数:每5ms翻转一次LED状态
void Timer0Handler(void) interrupt 1
{
    static unsigned int count = 0;
    
    TH0 = 0xEE;         // 定时器赋初值,约5ms
    TL0 = 0x00;
    
    count++;
    if (100 == count)   // 累计500ms翻转一次
    {
        count = 0;
        Led = ~Led;     // 翻转LED电平
        LedEn = ~LedEn; // 翻转三极管基极电平
    }
}

void main(void)
{
    // 设置定时器0为16位定时模式
    TMOD &= ~0x3;
    TMOD |= 0x1;
    
    // 定时器赋初值
    TH0 = 0xEE;
    TL0 = 0x00;
    
    // 开启定时器0中断和总中断
    ET0 = 1;
    EA = 1;
    
    // 启动定时器0
    TR0 = 1;
    
    while (1)
    {
        // 主循环可执行其他任务
    }
}

4. 按键控制LED

按键控制LED的核心思路:主循环中不断读取按键引脚电平,根据电平状态控制对应LED的亮灭。按键按下时引脚为低电平,此时点亮LED;松开后引脚恢复高电平,熄灭LED。

c 复制代码
#include <reg52.h>

sbit Key1 = P1^4;
sbit Key2 = P1^5;
sbit Key3 = P1^6;
sbit Key4 = P1^7;

sbit Led1 = P2^0;
sbit Led2 = P2^1;
sbit Led3 = P2^2;
sbit Led4 = P2^3;

void main(void)
{
    while (1)
    {
        if (Key1)
        {
            Led1 = 1;   // 按键松开,LED熄灭
        }
        else
        {
            Led1 = 0;   // 按键按下,LED点亮
        }
        
        if (Key2)
        {
            Led2 = 1;
        }
        else
        {
            Led2 = 0;
        }
        
        if (Key3)
        {
            Led3 = 1;
        }
        else
        {
            Led3 = 0;
        }
        
        if (Key4)
        {
            Led4 = 1;
        }
        else
        {
            Led4 = 0;
        }
    }
}

5. 定时器驱动数码管动态显示

数码管动态显示利用定时器中断实现定时刷新。每隔固定时间切换一位数码管显示,利用视觉暂留效应让4位数码管看起来同时点亮。

5.1 数码管段码表

共阴极数码管显示0~9对应的段码如下:

数字 段码(十六进制) 二进制
0 0x3F 0011 1111
1 0x06 0000 0110
2 0x5B 0101 1011
3 0x4F 0100 1111
4 0x66 0110 0110
5 0x6D 0110 1101
6 0x7D 0111 1101
7 0x07 0000 0111
8 0x7F 0111 1111
9 0x6F 0110 1111

5.2 完整数码管显示程序

c 复制代码
#include <reg52.h>
#include <intrins.h>

sbit Led1En = P1^0;
sbit Led2En = P1^1;
sbit Led3En = P1^2;
sbit Led4En = P1^3;

// 数码管显示对应数字的真值表
unsigned char NumCode[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

// 数码管显示内容的缓存
unsigned char NumBuffer[4] = {0x00, 0x00, 0x00, 0x00};

void DisplayNum(unsigned char wei);

// 定时器0中断服务函数:动态扫描数码管
void Timer0Handler(void) interrupt 1
{
    static unsigned char n = 0;
    
    // 定时器赋初值
    TH0 = 0xEE;
    TL0 = 0x00;
    
    DisplayNum(n);
    n++;
    if (4 == n)
    {
        n = 0;
    }
}

// 延时1ms
void Delay1ms(void)  //@11.0592MHz
{
    unsigned char data i, j;
    _nop_();
    i = 2;
    j = 199;
    do
    {
        while (--j);
    } while (--i);
}

// 延时1000ms
void Delay1000ms(void)  //@11.0592MHz
{
    unsigned char data i, j, k;
    _nop_();
    i = 8;
    j = 1;
    k = 243;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}

// 显示数码管第wei位的数值
void DisplayNum(unsigned char wei)
{
    P1 &= ~0xF;         // 关闭所有位选
    P1 |= (0x1 << wei); // 选中第wei位
    P0 = NumCode[NumBuffer[wei]];  // 输出段码
}

// 将数字设置到显示缓存区中
void SetDisplayNum(int TmpNum)
{
    NumBuffer[0] = TmpNum % 10;
    NumBuffer[1] = TmpNum % 100 / 10;
    NumBuffer[2] = TmpNum % 1000 / 100;
    NumBuffer[3] = TmpNum / 1000;
}

void main(void)
{
    int Num = 0;
    
    // 设置定时器工作模式(16位定时器)
    TMOD &= ~0x3;
    TMOD |= 0x1;
    
    // 定时器赋初值
    TH0 = 0xEE;
    TL0 = 0x00;
    
    // 定时器中断开启
    ET0 = 1;
    EA = 1;
    
    // 开启定时器0
    TR0 = 1;
    
    while (1)
    {
        Num++;
        SetDisplayNum(Num);
        Delay1000ms();
    }
}

6. 按键与定时器综合应用

将按键控制与定时器显示结合,可以实现按键调节数码管显示数值的功能。例如:按下Key1数值加1,按下Key2数值减1,按下Key3清零,按下Key4暂停计数。

c 复制代码
#include <reg52.h>
#include <intrins.h>

sbit Key1 = P1^4;
sbit Key2 = P1^5;
sbit Key3 = P1^6;
sbit Key4 = P1^7;

sbit Led1En = P1^0;
sbit Led2En = P1^1;
sbit Led3En = P1^2;
sbit Led4En = P1^3;

unsigned char NumCode[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
unsigned char NumBuffer[4] = {0x00, 0x00, 0x00, 0x00};

unsigned int DisplayNum = 0;
bit PauseFlag = 0;

void DisplayNumFunc(unsigned char wei);
void SetDisplayNum(int TmpNum);
void Delay10ms(void);

// 定时器0中断:数码管动态扫描
void Timer0Handler(void) interrupt 1
{
    static unsigned char n = 0;
    
    TH0 = 0xEE;
    TL0 = 0x00;
    
    DisplayNumFunc(n);
    n++;
    if (4 == n)
    {
        n = 0;
    }
}

void DisplayNumFunc(unsigned char wei)
{
    P1 &= ~0xF;
    P1 |= (0x1 << wei);
    P0 = NumCode[NumBuffer[wei]];
}

void SetDisplayNum(int TmpNum)
{
    NumBuffer[0] = TmpNum % 10;
    NumBuffer[1] = TmpNum % 100 / 10;
    NumBuffer[2] = TmpNum % 1000 / 100;
    NumBuffer[3] = TmpNum / 1000;
}

// 简易延时消抖
void Delay10ms(void)
{
    unsigned char i, j;
    for (i = 0; i < 20; i++)
    {
        for (j = 0; j < 250; j++);
    }
}

void main(void)
{
    TMOD &= ~0x3;
    TMOD |= 0x1;
    
    TH0 = 0xEE;
    TL0 = 0x00;
    
    ET0 = 1;
    EA = 1;
    TR0 = 1;
    
    while (1)
    {
        // 按键1:数值加1
        if (0 == Key1)
        {
            Delay10ms();
            if (0 == Key1)
            {
                DisplayNum++;
                SetDisplayNum(DisplayNum);
                while (0 == Key1);  // 等待按键松开
            }
        }
        
        // 按键2:数值减1
        if (0 == Key2)
        {
            Delay10ms();
            if (0 == Key2)
            {
                if (DisplayNum > 0)
                {
                    DisplayNum--;
                }
                SetDisplayNum(DisplayNum);
                while (0 == Key2);
            }
        }
        
        // 按键3:清零
        if (0 == Key3)
        {
            Delay10ms();
            if (0 == Key3)
            {
                DisplayNum = 0;
                SetDisplayNum(DisplayNum);
                while (0 == Key3);
            }
        }
        
        // 按键4:暂停/继续计数
        if (0 == Key4)
        {
            Delay10ms();
            if (0 == Key4)
            {
                PauseFlag = ~PauseFlag;
                while (0 == Key4);
            }
        }
        
        // 自动计数
        if (0 == PauseFlag)
        {
            DisplayNum++;
            SetDisplayNum(DisplayNum);
            Delay1000ms();
        }
    }
}

7. 按键消抖处理

机械按键在按下和松开的瞬间会产生抖动,表现为电平在短时间内多次跳变。如果不做消抖处理,一次按键可能被误判为多次触发。常用的消抖方法有两种:

  • 软件延时消抖:检测到按键按下后延时10~20ms再次检测,若仍为按下状态则确认有效。
  • 定时器扫描消抖:利用定时器中断周期性扫描按键,连续多次采样一致才确认状态变化。

本实验综合程序采用软件延时消抖,并在确认按键有效后等待按键松开,避免重复触发。

8. 总结

本实验完整演示了51单片机按键输入、LED输出、定时器中断和数码管动态显示的综合应用。晶体管作为功率驱动器件,在数码管位选和LED驱动中起到关键的电流放大作用。通过定时器中断实现精确的时序控制,配合按键输入实现人机交互,是单片机嵌入式开发的基础技能。

相关推荐
HRTOS1 小时前
HRTOS Debug:8051实时操作系统资源状态监控与分析
单片机·嵌入式硬件·51单片机
神威难绷泪2 小时前
51单片机 ds18b20
51单片机
老当益壮梁奶奶13 小时前
51单片机DS18B20温度传感器详解:单总线时序与C51驱动实现
笔记·stm32·单片机·嵌入式硬件·51单片机
susplus20 小时前
【传感器DS18B20】51单片机上实现温度采集
c语言·51单片机·ds18b20·温度采集
艾芯微科技21 小时前
B16WS SOD-323 肖特基二极管参数、应用电路与选型踩坑指南
网络·单片机·嵌入式硬件·集成测试·51单片机
糖糖单片机设计21 小时前
基于STM32的远程宠物自动投喂系统
人工智能·stm32·51单片机·语音识别·智能硬件
神威难绷泪1 天前
51单片机 day1
51单片机
abc light1 天前
单片机(3)
51单片机
HRTOS1 天前
HRTOS Debug 正式发布:为 8051 实时系统提供可观测、可诊断的运行状态监控能力
单片机·嵌入式硬件·51单片机