站内搜索
发作品签到
AI8051U_数码管时钟
专业版

AI8051U_数码管时钟

简介

基于国芯 AI8051U单片机设计的一款数码管时钟学习板

简介:基于国芯 AI8051U单片机设计的一款数码管时钟学习板
复刻成本:10

开源协议

Public Domain

创建时间:2025-09-13 21:29:18更新时间:2025-10-01 12:09:57

描述

🚀 AI8051U 六位数码管、按键与蜂鸣器综合开发板

💡 项目简介

本项目是基于国芯 AI8051U 8位单片机设计的一款数码管时钟学习板。它集成了3个2位共阴数码管动态显示、4个独立按键有源蜂鸣器以及双冒号LED指示等功能,是学习AI8051U(兼容传统8051)单片机编程、练习C语言代码编写以及理解单片机外围驱动电路的理想平台。

板载资源丰富,电路设计经典,代码开源且注释详尽,无论您是单片机初学者还是希望了解新型AI8051U芯片的开发者,都能从中获益。

image.png

✨ 核心功能与特色

功能模块说明
🧠 主控核心采用强大的国芯AI8051U单片机,兼容传统8051指令集,同时具备更高的运行速度和更丰富的外设资源。
🔢 动态显示驱动3个2位数码管,采用经典的"位选+段选"动态扫描方式,显示稳定无闪烁。
🔘 独立按键板载4个独立12*12按键,电路简单可靠,提供按键扫描与消抖的示例代码。
🔘 下载按键板载1个6*6按键,电路简单可靠,提供按键扫描与消抖的示例代码。
🔊 声光提示集成有源蜂鸣器,可用于告LED**,非常适合制作时钟。
⚙️ 模块化驱动位选、段选、LED、蜂鸣器均采用三极管扩流驱动,设计标准,负载能力强。
📖 开源原理图、PCB、源码完全开源,方便二次开发。

🛠️ 硬件设计详解

🏗️ 系统架构框图

image.png

flowchart TD
    %% === 核心主控 ===
    MCU[🎯 AI8051U 主控芯片]
    
    %% === USB下载与供电系统 ===
    subgraph USBSystem [🔌 USB接口系统]
        TypeC[Type-C接口\n供电+通讯]
        P32[P32一键下载键\n自动下载触发]
        Backup[4Pin排针\n备用供电下载]
    end
    
    TypeC -->|VCC GND| Power[电源管理]
    TypeC -->|D+ D-| MCU
    P32 -->|下载使能| MCU
    Backup -->|VCC GND D+ D-| MCU
    
    %% === 显示系统 ===
    MCU -->|P00-P05\n位选控制| DigitDriver[6×8050三极管\n段选限流]
    DigitDriver --> DigitModule[🔢 6位共阴数码管]
    
    MCU -->|P20-P27\n段选数据| SegmentRes[8×1k电阻\n段选限流]
    SegmentRes --> DigitModule
    
    %% === LED指示系统 ===
    MCU -->|P45\n使能控制| LEDDriver[8550三极管\nLED驱动]
    LEDDriver --> ColonLED[💡 双冒号LED]
    MCU -->|P06 P07\n分组控制| ColonRes[1k电阻\n阴极控制]
    ColonRes --> ColonLED
    
    %% === 输入输出系统 ===
    KeyModule[🔘 4×独立按键] -->|用户输入| KeyRes[1k上拉电阻]
    KeyRes -->|P10-P13| MCU
    
    MCU -->|P14\n音频控制| BuzzerDriver[8050三极管\n电流放大]
    BuzzerDriver --> Buzzer[🔊 有源蜂鸣器]
    
    %% === 样式定义 ===
    classDef mcu fill:#e1f5fe,stroke:#01579b,stroke-width:3px
    classDef usb fill:#fff3e0,stroke:#ff6f00,stroke-width:2px
    classDef display fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
    classDef io fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px
    
    class MCU mcu
    class USBSystem usb
    class DigitModule,ColonLED display
    class KeyModule,Buzzer io

核心部件清单

  • 主控芯片:AI8051U
  • 显示部分
    • 6位共阴数码管(3个2位一体)
    • 6×8050三极管(位选驱动)
    • 1×8550三极管(LED冒号驱动)
    • 8×1k电阻(段选限流)
  • 输入部分:5个轻触按键
  • 输出部分:有源蜂鸣器 + 8050驱动三极管
  • 指示灯:4个LED(用作两个冒号)

📁 文件结构

  • 🗂️ AI8051U数码管时钟.zip - 项目压缩包
    • 📄 sample.uvproj - KEIL工程文件
    • 📄 mian.c - 程序代码
    • 📄 sample.hex - 烧录文件

🎯 目标受众

  • 🧑‍🎓 单片机初学者 - 学习8051系列的学生和电子爱好者
  • 🔄 升级转型开发者 - 从传统STC89C51过渡到AI8051U
  • 🛠️ 项目开发者 - 需要显示与控制核心板的创客

🚦 快速开始

📦硬件准备

  1. PCB打样:使用 GERBER/ 目录下的文件进行电路板制作
  2. 物料采购:参考 BOM/ 清单采购所有元器件
  3. 焊接组装:按原理图焊接各元器件

🖥️软件准备

  1. 开发环境:安装 Keil C51 开发环境
  2. 程序下载:准备AI8051U编程器/下载器
  3. 编译烧录
    # 打开 FW/Project.uvproj
    # 编译代码
    # 连接下载器烧录程序
    

🧪测试代码

✨𝟏 点亮数码管

这个程序讲连梁第一个数码数码管,如图所示,您可以通过修改P0与P2的值让数码管显示不同的数字

#include "AI8051U.h"

void main(void)
{
    // 配置P0和P2为推挽输出
    P0M0 = 0xFF;
    P0M1 = 0x00;
    P2M0 = 0xFF;
    P2M1 = 0x00;
    
    // 固定显示:第一位数码管显示数字8
    while(1)
    {
        P0 = 0x01;  // 0000 0001 - 选择第一位数码管(P00=1)
        //P2 = 0x3F; //0 (0011 1111) -> a,b,c,d,e,f亮
        //P2 = 0x06; //1 (0000 0110) -> b,c亮       
        //P2 = 0x5B; //2 (0101 1011) -> a,b,d,e,g亮       
        //P2 = 0x4F; //3 (0100 1111) -> a,b,c,d,g亮
        //P2 = 0x66; //4 (0110 0110) -> b,c,f,g亮
        //P2 = 0x6D; //5 (0110 1101) -> a,c,d,f,g亮
        //P2 = 0x7D; //6 (0111 1101) -> a,c,d,e,f,g亮
        //P2 = 0x07; //7 (0000 0111) -> a,b,c亮
        P2 = 0x7F; //8 (0111 1111) -> a,b,c,d,e,f,g亮
        //P2 = 0x6F; //9 (0110 1111) -> a,b,c,d,f,g亮
    }
}

image.png

✨2 数码管轮播

这个程序实现第二位数码管0~9变换,效果1视频所示

#include "AI8051U.h"

void Delay100ms(void);	//@40.000MHz

// 数码管段码表
unsigned char code segTable[10] = {
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

void main(void)
{
    unsigned char i;
    unsigned char j;
    
    // 配置P0和P2为推挽输出
    P0M0 = 0xFF;
    P0M1 = 0x00;
    P2M0 = 0xFF;
    P2M1 = 0x00;
    
    while(1)
    {
        P0 = 0x02;  // 选择第二位
        
        for(j = 0; j < 10; j++) 
        {
            P2 = segTable[j];  // 显示数字
            Delay100ms();  // 延时100ms
        }
        
    }
}

void Delay100ms(void)	//@40.000MHz
{
	unsigned char data i, j, k;

	_nop_();
	i = 21;
	j = 75;
	k = 189;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

✨3 数码管动态扫描数字

数码管的段选是连在一起的,所以数码管动态扫描,是利用人眼的“视觉暂留”缺陷,通过快速轮流显示,让你错觉地看到所有数字同时稳定亮起。动态扫描与视觉暂留,是硬件的高效技巧与人类生理特性的完美结合。

#include "AI8051U.h"

unsigned char code segTable[10] = {
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

void main(void)
{
    unsigned char i;
    unsigned char j;
    unsigned char digits[6] = {1, 2, 3, 4, 5, 6};
    
    P0M0 = 0xFF;
    P0M1 = 0x00;
    P2M0 = 0xFF;
    P2M1 = 0x00;
    
    while(1)
    {
        for(i = 0; i < 6; i++)
        {
            P0 = 0x00;
            P2 = segTable[digits[i]];
            P0 = 1 << i;
            
            for(j = 0; j < 20; j++);
        }
    }
}

image.png

✨3 数码管时钟

我们先尝试下这段程序

#include "AI8051U.h"

// 数码管段码表
unsigned char code segTable[10] = {
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

// 系统提供的精确延时函数
void Delay1000ms(void)
{
    unsigned long edata i;
    i = 9999998UL;
    while(i) i--;
}

// 时间变量
unsigned char hours = 12;
unsigned char minutes = 34;
unsigned char seconds = 56;
unsigned char displayDigits[6];

void updateDisplay(void)
{
    displayDigits[0] = hours / 10;
    displayDigits[1] = hours % 10;
    displayDigits[2] = minutes / 10;
    displayDigits[3] = minutes % 10;
    displayDigits[4] = seconds / 10;
    displayDigits[5] = seconds % 10;
}

void main(void)
{
    unsigned char i, j;
    unsigned int scanCount = 0;
    
    // 配置P0和P2为推挽输出
    P0M0 = 0xFF;
    P0M1 = 0x00;
    P2M0 = 0xFF;
    P2M1 = 0x00;
    
    // 初始化显示
    updateDisplay();
    
    while(1)
    {
        // 动态扫描显示
        for(i = 0; i < 6; i++)
        {
            P0 = 0x00;
            P2 = segTable[displayDigits[i]];
            P0 = 1 << i;
            
            for(j = 0; j < 15; j++);
        }
        
        // 时间更新
        scanCount++;
        if(scanCount >= 1000)  // 调整更新频率
        {
            scanCount = 0;
            
            // 使用精确延时
            Delay1000ms();
            
            // 更新时间
            seconds++;
            if(seconds >= 60)
            {
                seconds = 0;
                minutes++;
                if(minutes >= 60)
                {
                    minutes = 0;
                    hours++;
                    if(hours >= 24)
                    {
                        hours = 0;
                    }
                }
            }
            
            // 更新显示
            updateDisplay();
        }
    }
}

由于Delay1000ms是阻塞延时,我们不能在扫描循环中直接使用,否则会导致扫描停止1秒,造成显示熄灭。如效果2视频所示,所以我们需要使用定时器,效果3视频所示

#include "AI8051U.h"

// 数码管段码表
unsigned char code segTable[10] = {
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

// 系统提供的精确延时函数
void Delay1000ms(void)
{
    unsigned long edata i;
    i = 9999998UL;
    while(i) i--;
}

// 全局变量
unsigned char hours = 0;
unsigned char minutes = 0;
unsigned char seconds = 0;
unsigned char displayDigits[6];
unsigned int scanCount = 0;
unsigned char currentDigit = 0;
unsigned char secondFlag = 0;

// 定时器0初始化 - 高频率扫描
void timer0_init(void)
{
    TMOD = 0x01;      // 定时器0,模式1
    TH0 = 0xFE;       // 0.5ms定时初值(40MHz)
    TL0 = 0x0C;
    ET0 = 1;          // 允许定时器0中断
    EA = 1;           // 开启总中断
    TR0 = 1;          // 启动定时器0
}

// 定时器0中断服务函数 - 只负责扫描
void timer0_isr(void) interrupt 1
{
    TH0 = 0xFE;       // 重装初值
    TL0 = 0x0C;
    
    // 只做数码管扫描
    P0 = 0x00;
    P2 = segTable[displayDigits[currentDigit]];
    P0 = 1 << currentDigit;
    currentDigit++;
    if(currentDigit >= 6) currentDigit = 0;
    
    // 扫描计数
    scanCount++;
}

// 更新显示
void updateDisplay(void)
{
    displayDigits[0] = hours / 10;
    displayDigits[1] = hours % 10;
    displayDigits[2] = minutes / 10;
    displayDigits[3] = minutes % 10;
    displayDigits[4] = seconds / 10;
    displayDigits[5] = seconds % 10;
}

void main(void)
{
    WTST = 0x00;
    
    P0M0 = 0xFF;
    P0M1 = 0x00;
    P2M0 = 0xFF;
    P2M1 = 0x00;
    
    updateDisplay();
    timer0_init();
    
    while(1)
    {
        // 使用精确延时控制时间更新
        Delay1000ms();
        
        // 更新时间
        seconds++;
        if(seconds >= 60)
        {
            seconds = 0;
            minutes++;
            if(minutes >= 60)
            {
                minutes = 0;
                hours++;
                if(hours >= 24)
                {
                    hours = 0;
                }
            }
        }
        
        // 更新显示
        updateDisplay();
    }
}

✨4 冒号闪烁

我们可以在定时器中断中同时控制冒号,但是要注意,冒号的亮灭应该与时钟的秒同步,通常冒号会每秒闪烁一次。
修改思路:

  1. 在定时器中断中,除了扫描数码管,还要控制冒号。
  2. 定义一个变量来记录冒号的状态(亮或灭),并且每秒改变一次状态(闪烁)。
  3. 在中断中,根据冒号状态控制P06和P07。

但是注意,我们的P0口在动态扫描时,会不断改变P00-P05,而P06和07也会被改变。因此,在设置P0口的时候,我们不能简单地用1<= 6) currentDigit = 0;

请看效果4

#include "AI8051U.h"

// 数码管段码表
unsigned char code segTable[10] = {
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

// 系统提供的精确延时函数
void Delay1000ms(void)
{
    unsigned long edata i;
    i = 9999998UL;
    while(i) i--;
}

// 全局变量
unsigned char hours = 0;        // 00
unsigned char minutes = 0;      // 00
unsigned char seconds = 0;      // 00
unsigned char displayDigits[6];
unsigned int scanCount = 0;
unsigned char currentDigit = 0;
unsigned char colonState = 1;   // 冒号初始状态为亮

// 定时器0初始化
void timer0_init(void)
{
    TMOD = 0x01;
    TH0 = 0xFE;
    TL0 = 0x0C;
    ET0 = 1;
    EA = 1;
    TR0 = 1;
}

// 定时器0中断服务函数
void timer0_isr(void) interrupt 1
{
    TH0 = 0xFE;
    TL0 = 0x0C;
    
    // 数码管扫描
    P0 = 0x00;
    P2 = segTable[displayDigits[currentDigit]];
    P0 = 1 &lt;&lt; currentDigit;
    currentDigit++;
    if(currentDigit &gt;= 6) currentDigit = 0;
    
    // 冒号控制 - 每秒闪烁
    if(colonState)
    {
        P45 = 1;  // 打开冒号阳极
        P06 = 0;  // 第一组冒号亮
        P07 = 0;  // 第二组冒号亮
    }
    else
    {
        P45 = 0;  // 关闭冒号阳极
        P06 = 1;  // 第一组冒号灭
        P07 = 1;  // 第二组冒号灭
    }
    
    scanCount++;
}

// 更新显示
void updateDisplay(void)
{
    displayDigits[0] = hours / 10;
    displayDigits[1] = hours % 10;
    displayDigits[2] = minutes / 10;
    displayDigits[3] = minutes % 10;
    displayDigits[4] = seconds / 10;
    displayDigits[5] = seconds % 10;
}

void main(void)
{
    WTST = 0x00;
    
    // 配置所有使用到的端口为推挽输出
    P0M0 = 0xFF;
    P0M1 = 0x00;
    P2M0 = 0xFF;
    P2M1 = 0x00;
    P4M0 |= 0x20;
    P4M1 &amp;= ~0x20;
    
    updateDisplay();
    timer0_init();
    
    while(1)
    {
        // 使用精确延时
        Delay1000ms();
        
        // 冒号每秒闪烁一次
        colonState = !colonState;
        
        // 更新时间
        seconds++;
        if(seconds &gt;= 60)
        {
            seconds = 0;
            minutes++;
            if(minutes &gt;= 60)
            {
                minutes = 0;
                hours++;
                if(hours &gt;= 24)
                {
                    hours = 0;
                }
            }
        }
        
        // 更新显示
        updateDisplay();
    }
}

✨5 完整的部分

这次我们将加入按键和蜂鸣器功能,让ta成为一个完整的时钟,我们学习使用状态机来控制按键以及蜂鸣器,请看完整状态视频

状态机是什么?状态机是一种将程序逻辑分解为清晰状态和状态转换的编程范式。它让单片机程序像一部精心编排的剧本: 状态A → 事件发生 → 执行动作 → 状态B

为什么选择状态机?

  • 逻辑清晰度
    传统条件分支往往交织混乱,而状态机为每个状态定义明确的行为边界,使代码具备自解释性。

  • 实时响应能力
    告别阻塞式delay()调用,状态机通过非阻塞设计确保系统时刻保持响应,完美满足实时性要求。

  • 卓越可维护性
    模块化设计:每个状态独立封装,修改不影响整体

  • 易于扩展
    新增功能仅需添加状态,无需重构现有逻辑

#include "AI8051U.h"

// 数码管段码表
unsigned char code segTable[10] = {
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

// 系统提供的精确延时函数
void Delay1000ms(void)
{
    unsigned long edata i;
    i = 9999998UL;
    while(i) i--;
}

void Delay10ms(void)
{
    unsigned int i;
    i = 19998;
    while(i) i--;
}

// 全局变量
unsigned char hours = 0;
unsigned char minutes = 0;
unsigned char seconds = 0;
unsigned char displayDigits[6];
unsigned int scanCount = 0;
unsigned char currentDigit = 0;
unsigned char colonState = 1;

// 按键相关变量
unsigned char keyValue = 0;
unsigned char keyState = 0;
unsigned char keyPressFlag = 0;
unsigned char keyLastState = 0;
unsigned char keyDebounceCount = 0;

// 时钟状态机
unsigned char clockMode = 0;
unsigned char blinkCount = 0;
unsigned char blinkState = 0;

// 蜂鸣器状态机
unsigned char beepState = 0;          // 0:准备, 1:鸣叫, 2:间隔
unsigned int beepCount = 0;           // 改为int类型,避免溢出
unsigned char beepEnable = 1;
unsigned char beepActive = 0;         // 蜂鸣器是否正在响
unsigned char beepTimes = 0;          // 已经响的次数

// 整点报时标志
unsigned char lastMinute = 0xFF;      // 记录上次报时的分钟

// 定时器0初始化
void timer0_init(void)
{
    TMOD = 0x01;
    TH0 = 0xFE;
    TL0 = 0x0C;
    ET0 = 1;
    EA = 1;
    TR0 = 1;
}

// 按键扫描函数
void keyScan(void)
{
    unsigned char currentKey;
    
    // 读取按键状态(P1.0-P1.3)
    currentKey = ~P1 &amp; 0x0F;
    
    // 状态机处理按键
    switch(keyState)
    {
        case 0: // 等待按键按下
            if(currentKey != 0)
            {
                keyState = 1;
                keyDebounceCount = 0;
            }
            break;
            
        case 1: // 按键消抖
            if(currentKey != 0)
            {
                keyDebounceCount++;
                if(keyDebounceCount &gt;= 2) // 20ms消抖
                {
                    keyValue = currentKey;
                    keyPressFlag = 1;
                    keyState = 2;
                }
            }
            else
            {
                keyState = 0;
            }
            break;
            
        case 2: // 等待按键释放
            if(currentKey == 0)
            {
                keyState = 0;
            }
            break;
    }
    
    keyLastState = currentKey;
}

// 按键处理函数
void keyProcess(void)
{
    if(keyPressFlag)
    {
        keyPressFlag = 0;
        
        switch(keyValue)
        {
            case 0x01: // KEY1: 模式切换
                clockMode++;
                if(clockMode &gt; 3) clockMode = 0;
                blinkCount = 0;
                blinkState = 1;
                break;
                
            case 0x02: // KEY2: 数值增加
                switch(clockMode)
                {
                    case 1: // 设置小时
                        hours++;
                        if(hours &gt;= 24) hours = 0;
                        break;
                    case 2: // 设置分钟
                        minutes++;
                        if(minutes &gt;= 60) minutes = 0;
                        break;
                    case 3: // 设置秒钟
                        seconds = 0;
                        break;
                }
                break;
                
            case 0x04: // KEY3: 数值减少
                switch(clockMode)
                {
                    case 1: // 设置小时
                        if(hours == 0) hours = 23;
                        else hours--;
                        break;
                    case 2: // 设置分钟
                        if(minutes == 0) minutes = 59;
                        else minutes--;
                        break;
                    case 3: // 设置秒钟
                        seconds = 0;
                        break;
                }
                break;
                
            case 0x08: // KEY4: 确认/返回正常模式 或 蜂鸣器开关
                if(clockMode == 0)
                {
                    // 在正常模式下,KEY4切换蜂鸣器开关
                    beepEnable = !beepEnable;
                    // 测试蜂鸣器
                    if(beepEnable &amp;&amp; !beepActive)
                    {
                        beepActive = 1;
                        beepState = 0;
                        beepCount = 0;
                        beepTimes = 0;
                    }
                }
                else
                {
                    // 在设置模式下,KEY4返回正常模式
                    clockMode = 0;
                }
                break;
        }
    }
}

// 蜂鸣器控制函数
void beepControl(void)
{
    // 如果蜂鸣器未启用或不在整点报时状态,直接返回
    if(!beepActive) return;
    
    switch(beepState)
    {
        case 0: // 准备状态,开始第一声
            P14 = 1; // 打开蜂鸣器
            beepState = 1;
            beepCount = 0;
            beepTimes = 1;
            break;
            
        case 1: // 鸣叫状态
            beepCount++;
            if(beepCount &gt;= 100) // 持续1秒 (100 * 10ms = 1000ms)
            {
                beepCount = 0;
                P14 = 0; // 关闭蜂鸣器
                if(beepTimes &lt; 3)
                {
                    beepState = 2; // 进入间隔状态
                }
                else
                {
                    beepActive = 0; // 完成三次鸣叫
                    beepState = 0;
                }
            }
            break;
            
        case 2: // 间隔状态
            beepCount++;
            if(beepCount &gt;= 50) // 间隔500ms (50 * 10ms = 500ms)
            {
                beepCount = 0;
                beepState = 1; // 进入下一次鸣叫
                P14 = 1; // 打开蜂鸣器
                beepTimes++; // 增加计数
            }
            break;
    }
}

// 整点报时检查
void checkHourBeep(void)
{
    // 只在正常模式下检查整点报时
    if(clockMode != 0) return;
    
    // 检查是否是整点(分钟和秒都为0)
    if(minutes == 0 &amp;&amp; seconds == 0)
    {
        // 避免重复报时
        if(lastMinute != 0)
        {
            lastMinute = 0;
            
            // 只在蜂鸣器开启状态下报时
            if(beepEnable &amp;&amp; !beepActive)
            {
                beepActive = 1; // 激活蜂鸣器
                beepState = 0;  // 从初始状态开始
                beepCount = 0;
                beepTimes = 0;
            }
        }
    }
    else
    {
        // 重置lastMinute,为下一次整点报时做准备
        if(seconds &gt; 5) // 给一点缓冲时间
        {
            lastMinute = 0xFF;
        }
    }
}

// 更新显示(带闪烁功能)
void updateDisplay(void)
{
    displayDigits[0] = hours / 10;
    displayDigits[1] = hours % 10;
    displayDigits[2] = minutes / 10;
    displayDigits[3] = minutes % 10;
    displayDigits[4] = seconds / 10;
    displayDigits[5] = seconds % 10;
    
    // 设置模式下的闪烁处理
    if(clockMode != 0 &amp;&amp; blinkState == 0)
    {
        switch(clockMode)
        {
            case 1: // 小时闪烁
                displayDigits[0] = 10;
                displayDigits[1] = 10;
                break;
            case 2: // 分钟闪烁
                displayDigits[2] = 10;
                displayDigits[3] = 10;
                break;
            case 3: // 秒钟闪烁
                displayDigits[4] = 10;
                displayDigits[5] = 10;
                break;
        }
    }
}

// 定时器0中断服务函数
void timer0_isr(void) interrupt 1
{
    TH0 = 0xFE;
    TL0 = 0x0C;
    
    // 数码管扫描
    P0 = 0x00;
    
    // 处理不显示的情况
    if(displayDigits[currentDigit] &lt; 10)
    {
        P2 = segTable[displayDigits[currentDigit]];
    }
    else
    {
        P2 = 0x00;
    }
    
    P0 = 1 &lt;&lt; currentDigit;
    currentDigit++;
    if(currentDigit &gt;= 6) currentDigit = 0;
    
    // 冒号控制
    if(colonState)
    {
        P45 = 1;
        P06 = 0;
        P07 = 0;
    }
    else
    {
        P45 = 0;
        P06 = 1;
        P07 = 1;
    }
    
    scanCount++;
    
    // 每10ms执行一次按键扫描和蜂鸣器控制
    if(scanCount % 10 == 0)
    {
        keyScan();
        beepControl(); // 蜂鸣器控制
    }
}

void main(void)
{
    WTST = 0x00;
    
    // 配置端口
    P0M0 = 0xFF;
    P0M1 = 0x00;
    P2M0 = 0xFF;
    P2M1 = 0x00;
    P4M0 |= 0x20;
    P4M1 &amp;= ~0x20;
    
    // 按键端口设置为准双向
    P1M0 = 0x00;
    P1M1 = 0x00;
    
    // 蜂鸣器端口设置为推挽输出,并初始化为关闭状态
    P1M0 |= 0x10;
    P1M1 &amp;= ~0x10;
    P14 = 0; // 确保蜂鸣器初始状态为关闭
    
    updateDisplay();
    timer0_init();
    
    while(1)
    {
        // 按键处理
        keyProcess();
        
        // 闪烁处理(500ms切换一次)
        blinkCount++;
        if(blinkCount &gt;= 50)
        {
            blinkCount = 0;
            blinkState = !blinkState;
        }
        
        // 只有在正常模式下才更新时间
        if(clockMode == 0)
        {
            // 使用精确延时
            Delay1000ms();
            
            // 冒号每秒闪烁一次
            colonState = !colonState;
            
            // 更新时间
            seconds++;
            if(seconds &gt;= 60)
            {
                seconds = 0;
                minutes++;
                if(minutes &gt;= 60)
                {
                    minutes = 0;
                    hours++;
                    if(hours &gt;= 24)
                    {
                        hours = 0;
                    }
                }
            }
            
            // 检查整点报时
            checkHourBeep();
        }
        else
        {
            // 在设置模式下,使用较短的延时保持响应性
            Delay10ms();
        }
        
        // 更新显示
        updateDisplay();
    }
}

设计图

未生成预览图,请在编辑器重新保存一次

BOM

暂无BOM

3D模型

序号文件名称下载次数
暂无数据

附件

序号文件名称下载次数
1
效果1.mp4
0
2
效果2.mp4
0
3
效果3.mp4
0
4
效果4.mp4
0
5
完整版.mp4
0
6
AI8051U数码管时钟.zip
13
克隆工程
添加到专辑
0
0
分享
侵权投诉
知识产权声明&复刻说明

本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。

请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。

底部导航