51单片机4位秒表 Proteus仿真+完整代码
• 分时点亮4位数码管,每次只显示一位,利用视觉暂留效果实现“同时显示”。• 定时器T0每10ms产生一次中断,通过累加中断次数实现毫秒级计时。• 机械按键按下时会产生抖动,通过延时函数(约5ms)消除误触发。:16位定时模式,最大定时时长约65ms(12MHz晶振)。:依次点亮4位数码管,利用人眼视觉暂留实现“同时显示”。:通过双重循环实现简单延时,用于按键消抖和动态扫描。• 每10ms触发一次
文件下载:
https://pan.baidu.com/s/1_xHZK1pvxi2uWYGDxGLhFQ?pwd=fg26 提取码: fg26
一、代码核心思想
这段代码通过 51单片机 实现了一个 4位秒表 功能,支持 启动、暂停、复位 操作,显示范围 00.00~99.99秒(精确到10ms)。核心思想如下:
- 硬件设计:
• 使用 4位共阳极数码管 显示时间(格式:秒.毫秒)。
• 通过 独立按键 控制秒表的启停和复位。
• 利用 定时器T0 实现10ms精确定时。 - 软件逻辑:
• 按键消抖:通过延时函数消除机械按键抖动。
• 动态扫描:分时刷新4位数码管,避免显示闪烁。
• 中断计时:定时器每10ms触发一次中断,累加时间并更新显示。
二、代码流程解析
1. 头文件与宏定义
#include "reg52.h"
#define uint unsigned int
#define uchar unsigned char
• 头文件:包含51单片机寄存器定义。
• 宏定义:简化代码书写,uint和uchar分别表示无符号整型和字符型。
2. 硬件接口定义
sbit k1 = P1^0; // 启动按键
sbit k2 = P1^1; // 暂停按键
sbit k3 = P1^2; // 复位按键
// 数码管段码表(共阳极,0~9对应编码)
uchar code table[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};
• 按键接口:连接到P1口的低三位。
• 数码管段码:共阳极数码管的0~9编码,例如0xc0对应数字“0”。
3. 延时函数
void delay(uint a) {
uint i, j;
for (i=0; i<a; i++)
for (j=0; j<120; j++);
}
• 作用:通过双重循环实现简单延时,用于按键消抖和动态扫描。
• 参数:a控制延时时间,数值越大延时越长。
4. 按键扫描函数
void Scankey() {
if (k1 == 0) { // 启动按键按下
delay(5); // 消抖
if (k1 == 0) TR0 = 1; // 启动定时器
}
else if (k2 == 0) { // 暂停按键按下
delay(5);
if (k2 == 0) TR0 = 0; // 关闭定时器
}
else if (k3 == 0) { // 复位按键按下
delay(5);
if (k3 == 0) { sec=0; ms=0; } // 时间归零
}
}
• 逻辑:
• 检测按键是否按下,延时消抖后确认操作。
• k1:启动定时器,开始计时。
• k2:暂停定时器,停止计时。
• k3:复位秒和毫秒变量。
5. 数码管显示函数
void Display() {
P2 = 0x01; // 选中第1位(秒十位)
P0 = table[sec/10]; // 显示秒十位
delay(1);
P2 = 0x02; // 选中第2位(秒个位,带小数点)
P0 = table[sec%10] & 0x7f; // 0x7f用于点亮小数点
delay(1);
P2 = 0x04; // 选中第3位(毫秒十位)
P0 = table[ms/10];
delay(1);
P2 = 0x08; // 选中第4位(毫秒个位)
P0 = table[ms%10];
delay(1);
}
• 动态扫描:依次点亮4位数码管,利用人眼视觉暂留实现“同时显示”。
• 小数点处理:秒个位通过&0x7f操作点亮小数点(共阳极数码管的最高位为小数点)。
6. 定时器初始化
void Time0() {
TMOD = 0x01; // 设置定时器T0为模式1(16位定时)
TH0 = (65535-10000)/256; // 10ms定时初值(假设晶振12MHz)
TL0 = (65535-10000)%256;
ET0 = 1; // 允许定时器T0中断
EA = 1; // 开启总中断
TR0 = 0; // 初始状态定时器关闭
}
• 定时器配置:
• 模式1:16位定时模式,最大定时时长约65ms(12MHz晶振)。
• 初值计算:10ms定时需重装载值10000(即65536-10000)。
7. 主函数
void main() {
Time0(); // 初始化定时器
while(1) { // 主循环
Display(); // 刷新显示
Scankey(); // 检测按键
}
}
• 主循环:不断刷新显示和检测按键,确保实时响应。
8. 中断服务函数
void zd() interrupt 1 {
TH0 = (65535-10000)/256; // 重装初值
TL0 = (65535-10000)%256;
ms++; // 毫秒计数+1
if (ms >= 100) { // 满100ms(即1秒)
ms = 0;
sec++;
if (sec >= 99) sec = 0; // 秒最大值99
}
}
• 中断逻辑:
• 每10ms触发一次中断,累计100次后秒加1。
• 秒变量sec范围:0~99,实现最大99.99秒显示。
三、关键实现原理
-
定时器精度
• 定时器T0每10ms产生一次中断,通过累加中断次数实现毫秒级计时。
• 公式:1秒 = 100次中断 × 10ms。 -
动态扫描显示
• 分时点亮4位数码管,每次只显示一位,利用视觉暂留效果实现“同时显示”。 -
按键消抖
• 机械按键按下时会产生抖动,通过延时函数(约5ms)消除误触发。
四、总结
本代码通过 硬件+软件协同设计 实现了高精度秒表功能,核心在于定时器中断和动态扫描技术。初学者可通过以下步骤进一步优化:
- 增加显示范围:修改
sec上限为999,支持更长时间显示。 - 优化消抖算法:使用状态机或硬件电路消抖。
- 添加蜂鸣器提示:在计时完成时触发声音提示(需扩展硬件)。
希望这篇解析能帮助你理解代码逻辑!如果对细节有疑问,欢迎在评论区讨论~
完整代码
#include "reg52.h"
#define uint unsigned int
#define uchar unsigned char
sbit k1=P1^0;
sbit k2=P1^1;
sbit k3=P1^2;
uint num;
uint sec=0;
uint ms=0;
uchar code table[]={0xc0,0xf9,0xa4,0xb0,0x99,
0x92,0x82,0xf8, 0x80,0x90};
void delay(uint a)
{
uint i,j;
for(i=0;i<a;i++)
for(j=0;j<120;j++);
}
void Scankey()
{
if(k1==0)
{
delay(5);
if(k1==0)
{
TR0=1;
}
}
else if(k2==0)
{
delay(5);
if(k2==0)
{
TR0=0;
}
}
else if(k3==0)
{
delay(5);
if(k3==0)
{
sec=0;ms=0;
}
}
}
void Display()
{
P2=0x01;
P0=table[sec/10];
delay(1);
P2=0x02;
P0=table[sec%10]&0x7f;
delay(1);
P2=0x04;
P0=table[ms/10];
delay(1);
P2=0x08;
P0=table[ms%10];
delay(1);
}
void Time0()
{
TMOD=0x01;
TH0=(65535-10000)/256;
TL0=(65535-10000)%256;
ET0=1;
EA=1;
TR0=0;
}
/**************
四位数码管显示
00.00-99.99
1s=1000ms,定时10ms++
**************/
void main()
{
Time0();
while(1)
{
Display();
Scankey();
}
}
void zd() interrupt 1
{
TH0=(65535-10000)/256;
TL0=(65535-10000)%256;
ms++;
if(ms>=100)
{
ms=0;
sec++;
if(sec>=99)
{
sec=0;
}
}
}
仿真图
更多推荐



所有评论(0)