
AI8051U_数码管时钟
简介
基于国芯 AI8051U单片机设计的一款数码管时钟学习板
简介:基于国芯 AI8051U单片机设计的一款数码管时钟学习板开源协议
:Public Domain
描述
🚀 AI8051U 六位数码管、按键与蜂鸣器综合开发板
💡 项目简介
本项目是基于国芯 AI8051U 8位单片机设计的一款数码管时钟学习板。它集成了3个2位共阴数码管动态显示、4个独立按键、有源蜂鸣器以及双冒号LED指示等功能,是学习AI8051U(兼容传统8051)单片机编程、练习C语言代码编写以及理解单片机外围驱动电路的理想平台。
板载资源丰富,电路设计经典,代码开源且注释详尽,无论您是单片机初学者还是希望了解新型AI8051U芯片的开发者,都能从中获益。

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

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
- 🛠️ 项目开发者 - 需要显示与控制核心板的创客
🚦 快速开始
📦硬件准备
- PCB打样:使用
GERBER/目录下的文件进行电路板制作 - 物料采购:参考
BOM/清单采购所有元器件 - 焊接组装:按原理图焊接各元器件
🖥️软件准备
- 开发环境:安装 Keil C51 开发环境
- 程序下载:准备AI8051U编程器/下载器
- 编译烧录:
# 打开 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亮
}
}

✨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++);
}
}
}

✨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 冒号闪烁
我们可以在定时器中断中同时控制冒号,但是要注意,冒号的亮灭应该与时钟的秒同步,通常冒号会每秒闪烁一次。
修改思路:
- 在定时器中断中,除了扫描数码管,还要控制冒号。
- 定义一个变量来记录冒号的状态(亮或灭),并且每秒改变一次状态(闪烁)。
- 在中断中,根据冒号状态控制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 << currentDigit;
currentDigit++;
if(currentDigit >= 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 &= ~0x20;
updateDisplay();
timer0_init();
while(1)
{
// 使用精确延时
Delay1000ms();
// 冒号每秒闪烁一次
colonState = !colonState;
// 更新时间
seconds++;
if(seconds >= 60)
{
seconds = 0;
minutes++;
if(minutes >= 60)
{
minutes = 0;
hours++;
if(hours >= 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 & 0x0F;
// 状态机处理按键
switch(keyState)
{
case 0: // 等待按键按下
if(currentKey != 0)
{
keyState = 1;
keyDebounceCount = 0;
}
break;
case 1: // 按键消抖
if(currentKey != 0)
{
keyDebounceCount++;
if(keyDebounceCount >= 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 > 3) clockMode = 0;
blinkCount = 0;
blinkState = 1;
break;
case 0x02: // KEY2: 数值增加
switch(clockMode)
{
case 1: // 设置小时
hours++;
if(hours >= 24) hours = 0;
break;
case 2: // 设置分钟
minutes++;
if(minutes >= 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 && !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 >= 100) // 持续1秒 (100 * 10ms = 1000ms)
{
beepCount = 0;
P14 = 0; // 关闭蜂鸣器
if(beepTimes < 3)
{
beepState = 2; // 进入间隔状态
}
else
{
beepActive = 0; // 完成三次鸣叫
beepState = 0;
}
}
break;
case 2: // 间隔状态
beepCount++;
if(beepCount >= 50) // 间隔500ms (50 * 10ms = 500ms)
{
beepCount = 0;
beepState = 1; // 进入下一次鸣叫
P14 = 1; // 打开蜂鸣器
beepTimes++; // 增加计数
}
break;
}
}
// 整点报时检查
void checkHourBeep(void)
{
// 只在正常模式下检查整点报时
if(clockMode != 0) return;
// 检查是否是整点(分钟和秒都为0)
if(minutes == 0 && seconds == 0)
{
// 避免重复报时
if(lastMinute != 0)
{
lastMinute = 0;
// 只在蜂鸣器开启状态下报时
if(beepEnable && !beepActive)
{
beepActive = 1; // 激活蜂鸣器
beepState = 0; // 从初始状态开始
beepCount = 0;
beepTimes = 0;
}
}
}
else
{
// 重置lastMinute,为下一次整点报时做准备
if(seconds > 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 && 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] < 10)
{
P2 = segTable[displayDigits[currentDigit]];
}
else
{
P2 = 0x00;
}
P0 = 1 << currentDigit;
currentDigit++;
if(currentDigit >= 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 &= ~0x20;
// 按键端口设置为准双向
P1M0 = 0x00;
P1M1 = 0x00;
// 蜂鸣器端口设置为推挽输出,并初始化为关闭状态
P1M0 |= 0x10;
P1M1 &= ~0x10;
P14 = 0; // 确保蜂鸣器初始状态为关闭
updateDisplay();
timer0_init();
while(1)
{
// 按键处理
keyProcess();
// 闪烁处理(500ms切换一次)
blinkCount++;
if(blinkCount >= 50)
{
blinkCount = 0;
blinkState = !blinkState;
}
// 只有在正常模式下才更新时间
if(clockMode == 0)
{
// 使用精确延时
Delay1000ms();
// 冒号每秒闪烁一次
colonState = !colonState;
// 更新时间
seconds++;
if(seconds >= 60)
{
seconds = 0;
minutes++;
if(minutes >= 60)
{
minutes = 0;
hours++;
if(hours >= 24)
{
hours = 0;
}
}
}
// 检查整点报时
checkHourBeep();
}
else
{
// 在设置模式下,使用较短的延时保持响应性
Delay10ms();
}
// 更新显示
updateDisplay();
}
}
设计图
未生成预览图,请在编辑器重新保存一次BOM
暂无BOM
克隆工程知识产权声明&复刻说明
本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。
请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。










