数码管T0计数器设计(基于51单片机)

基于51单片机的T0计数器系统设计,使用定时器0作为计数器对外部脉冲进行计数,并通过数码管显示计数值。

一、系统设计

1.1 功能要求

  • T0作为计数器,对P3.4(T0)引脚的外部脉冲计数
  • 4位共阳数码管显示计数值(0-9999)
  • 支持加计数和减计数两种模式
  • 按键控制:清零、模式切换、暂停/继续
  • 计数溢出处理(0-9999循环)
  • 计数频率可达50kHz

1.2 硬件资源

资源 型号/规格 数量
单片机 STC89C52/AT89C52 1
数码管 4位共阳数码管 1
按键 轻触开关 3
电阻 220Ω(限流) 8
电阻 10kΩ(上拉) 3
晶振 11.0592MHz 1
脉冲源 按键/信号发生器 1

二、硬件设计

2.1 电路原理图

+5V
 |
 +-+ 数码管显示
 |  段选:P0.0-P0.7 (经220Ω电阻)
 |  位选:P2.0-P2.3 (经220Ω电阻)
 |
 +-+ 按键控制
 |  K1(清零) -> P3.0 -> GND
 |  K2(模式) -> P3.1 -> GND
 |  K3(暂停) -> P3.2 -> GND
 |  (各接10kΩ上拉电阻到+5V)
 |
 +-+ 计数输入
 |  P3.4(T0) <- 外部脉冲输入
 |
 +-+ 单片机最小系统
    XTAL1/XTAL2 -> 11.0592MHz晶振
    RST -> 10kΩ电阻+10μF电容
    EA -> +5V
    VCC -> +5V
    GND -> GND

2.2 引脚分配表

单片机引脚 连接设备 功能说明
P0.0-P0.7 数码管段选 a,b,c,d,e,f,g,dp
P2.0-P2.3 数码管位选 千位、百位、十位、个位
P3.0 按键K1 清零
P3.1 按键K2 模式切换
P3.2 按键K3 暂停/继续
P3.4 外部脉冲 T0计数输入
P1.0 LED指示 计数状态指示

三、软件设计

3.1 主程序 (main.c)

#include <reg52.h>
#include "display.h"
#include "keyboard.h"
#include "timer0_counter.h"

// 全局变量
unsigned int count_value = 0;      // 当前计数值
unsigned int max_count = 9999;    // 最大计数值
unsigned int min_count = 0;       // 最小计数值
bit count_mode = 0;               // 0:加计数 1:减计数
bit count_enable = 1;             // 计数使能
bit overflow_flag = 0;            // 溢出标志

void main(void)
{
    System_Init();
    
    while(1)
    {
        Key_Process();              // 按键处理
        Display_Update();           // 显示更新
        Count_Check();             // 计数范围检查
    }
}

void System_Init(void)
{
    // 初始化定时器0为计数器模式
    Timer0_Counter_Init();
    
    // 初始化数码管
    Display_Init();
    
    // 显示初始值
    Display_Number(count_value);
    
    // 开启总中断
    EA = 1;
    
    // 启动计数
    TR0 = 1;
}

void Key_Process(void)
{
    unsigned char key = Key_Scan();
    
    switch(key)
    {
        case KEY_CLEAR:    // 清零
            count_value = 0;
            Display_Number(count_value);
            break;
            
        case KEY_MODE:     // 切换模式
            count_mode = !count_mode;
            if(count_mode)
                Display_Show_Mode("DEC");  // 显示减计数模式
            else
                Display_Show_Mode("INC");  // 显示加计数模式
            break;
            
        case KEY_PAUSE:    // 暂停/继续
            count_enable = !count_enable;
            if(count_enable)
            {
                TR0 = 1;   // 继续计数
                P1_0 = 0;  // LED亮表示计数中
            }
            else
            {
                TR0 = 0;   // 暂停计数
                P1_0 = 1;  // LED灭表示暂停
            }
            break;
            
        default:
            break;
    }
}

void Count_Check(void)
{
    // 检查溢出
    if(overflow_flag)
    {
        overflow_flag = 0;
        if(count_mode == 0)  // 加计数溢出
        {
            count_value = min_count;  // 回到最小值
        }
        else  // 减计数溢出
        {
            count_value = max_count;  // 回到最大值
        }
        Display_Number(count_value);
    }
    
    // 检查是否达到极限
    if(count_value >= max_count && count_mode == 0)
    {
        count_enable = 0;  // 禁止计数
        TR0 = 0;
        P1_0 = 1;          // LED灭
    }
    
    if(count_value <= min_count && count_mode == 1)
    {
        count_enable = 0;  // 禁止计数
        TR0 = 0;
        P1_0 = 1;          // LED灭
    }
}

// 定时器0中断服务程序(计数器溢出中断)
void Timer0_ISR(void) interrupt 1
{
    overflow_flag = 1;  // 设置溢出标志
    
    // 根据计数模式更新计数值
    if(count_mode == 0)  // 加计数
    {
        count_value++;
        if(count_value > max_count)
            count_value = min_count;  // 循环
    }
    else  // 减计数
    {
        count_value--;
        if(count_value < min_count)
            count_value = max_count;  // 循环
    }
    
    Display_Number(count_value);
}

3.2 定时器0计数器 (timer0_counter.h)

#ifndef __TIMER0_COUNTER_H__
#define __TIMER0_COUNTER_H__

#include <reg52.h>

// 函数声明
void Timer0_Counter_Init(void);
void Timer0_Start(void);
void Timer0_Stop(void);
void Timer0_Set_Value(unsigned int value);
unsigned int Timer0_Get_Value(void);

#endif

3.3 定时器0计数器 (timer0_counter.c)

#include "timer0_counter.h"

void Timer0_Counter_Init(void)
{
    // 设置T0为计数器模式,工作方式1(16位计数)
    TMOD &= 0xF0;  // 清除T0控制位
    TMOD |= 0x05;  // 0000 0101: T0为计数器,方式1(16位)
    
    // 设置计数初值(0)
    TH0 = 0x00;
    TL0 = 0x00;
    
    // 允许T0中断
    ET0 = 1;
    
    // 启动T0(由TR0控制)
    TR0 = 0;  // 先不启动
}

void Timer0_Start(void)
{
    TR0 = 1;  // 启动计数器
}

void Timer0_Stop(void)
{
    TR0 = 0;  // 停止计数器
}

void Timer0_Set_Value(unsigned int value)
{
    TH0 = value >> 8;      // 高8位
    TL0 = value & 0xFF;    // 低8位
}

unsigned int Timer0_Get_Value(void)
{
    unsigned int value;
    value = (TH0 << 8) | TL0;
    return value;
}

3.4 数码管显示 (display.h)

#ifndef __DISPLAY_H__
#define __DISPLAY_H__

#include <reg52.h>

// 数码管段码定义(共阳)
#define SEG_A 0x01
#define SEG_B 0x02
#define SEG_C 0x04
#define SEG_D 0x08
#define SEG_E 0x10
#define SEG_F 0x20
#define SEG_G 0x40
#define SEG_DP 0x80

// 数字0-9段码表
extern unsigned char code SegCode[10];

// 位选定义
#define DIGIT_1 P2_0  // 千位
#define DIGIT_2 P2_1  // 百位
#define DIGIT_3 P2_2  // 十位
#define DIGIT_4 P2_3  // 个位

// 函数声明
void Display_Init(void);
void Display_Number(unsigned int num);
void Display_Scan(unsigned char pos);
void Display_Clear(void);
void Display_Show_Mode(char *mode);

// 显示缓冲区
extern unsigned char Display_Buffer[4];

#endif

3.5 数码管显示 (display.c)

#include "display.h"

// 共阳数码管段码表(0-9)
unsigned char code SegCode[10] = {
    0xC0,  // 0: 1100 0000
    0xF9,  // 1: 1111 1001
    0xA4,  // 2: 1010 0100
    0xB0,  // 3: 1011 0000
    0x99,  // 4: 1001 1001
    0x92,  // 5: 1001 0010
    0x82,  // 6: 1000 0010
    0xF8,  // 7: 1111 1000
    0x80,  // 8: 1000 0000
    0x90   // 9: 1001 0000
};

// 字母段码表
unsigned char code LetterCode[26] = {
    0x88,  // A
    0x83,  // b
    0xC6,  // C
    0xA1,  // d
    0x86,  // E
    0x8E,  // F
    0xC2,  // G
    0x89,  // H
    0xF9,  // I
    0xF1,  // J
    0xC7,  // L
    0xC8,  // M
    0xC1,  // n
    0xC0,  // O
    0x8C,  // P
    0x98,  // q
    0x92,  // r
    0x87,  // S
    0x83,  // t
    0xC1,  // u
    0x91,  // v
    0xD1,  // w
    0x89,  // X
    0x49,  // y
    0x90   // Z
};

unsigned char Display_Buffer[4] = {0xC0, 0xC0, 0xC0, 0xC0};

void Display_Init(void)
{
    P0 = 0xFF;   // 段选全灭
    P2 = 0x0F;   // 位选全灭
}

void Display_Number(unsigned int num)
{
    // 分解各位数字
    unsigned char thousand, hundred, ten, unit;
    
    thousand = num / 1000;
    hundred = (num % 1000) / 100;
    ten = (num % 100) / 10;
    unit = num % 10;
    
    // 存储显示缓冲区
    Display_Buffer[0] = SegCode[thousand];
    Display_Buffer[1] = SegCode[hundred];
    Display_Buffer[2] = SegCode[ten];
    Display_Buffer[3] = SegCode[unit];
}

void Display_Scan(unsigned char pos)
{
    // 关闭所有位选
    P2 = 0x0F;
    
    // 送段码
    switch(pos)
    {
        case 0:  // 千位
            DIGIT_1 = 0;  // 选中千位
            P0 = Display_Buffer[0];
            break;
        case 1:  // 百位
            DIGIT_2 = 0;  // 选中百位
            P0 = Display_Buffer[1];
            break;
        case 2:  // 十位
            DIGIT_3 = 0;  // 选中十位
            P0 = Display_Buffer[2];
            break;
        case 3:  // 个位
            DIGIT_4 = 0;  // 选中个位
            P0 = Display_Buffer[3];
            break;
    }
}

void Display_Clear(void)
{
    P0 = 0xFF;   // 段选全灭
    P2 = 0x0F;   // 位选全灭
}

void Display_Show_Mode(char *mode)
{
    // 显示模式标识
    if(mode[0] == 'I')  // INC
    {
        Display_Buffer[0] = LetterCode['I'-'A'];
        Display_Buffer[1] = LetterCode['N'-'A'];
        Display_Buffer[2] = LetterCode['C'-'A'];
        Display_Buffer[3] = 0xFF;  // 熄灭
    }
    else if(mode[0] == 'D')  // DEC
    {
        Display_Buffer[0] = LetterCode['D'-'A'];
        Display_Buffer[1] = LetterCode['E'-'A'];
        Display_Buffer[2] = LetterCode['C'-'A'];
        Display_Buffer[3] = 0xFF;  // 熄灭
    }
}

3.6 键盘扫描 (keyboard.h)

#ifndef __KEYBOARD_H__
#define __KEYBOARD_H__

#include <reg52.h>

// 按键定义
#define KEY_CLEAR   0x01
#define KEY_MODE    0x02
#define KEY_PAUSE   0x03
#define KEY_NONE    0xFF

// 按键端口定义
sbit KEY_CLR_PIN = P3^0;
sbit KEY_MOD_PIN = P3^1;
sbit KEY_PAU_PIN = P3^2;

// 函数声明
void Key_Init(void);
unsigned char Key_Scan(void);

#endif

3.7 键盘扫描 (keyboard.c)

#include "keyboard.h"
#include "delay.h"

void Key_Init(void)
{
    // 按键端口初始化
}

unsigned char Key_Scan(void)
{
    // 检测清零键
    if(KEY_CLR_PIN == 0)
    {
        Delay_Ms(10);  // 消抖
        if(KEY_CLR_PIN == 0)
        {
            while(KEY_CLR_PIN == 0);  // 等待按键释放
            return KEY_CLEAR;
        }
    }
    
    // 检测模式键
    if(KEY_MOD_PIN == 0)
    {
        Delay_Ms(10);
        if(KEY_MOD_PIN == 0)
        {
            while(KEY_MOD_PIN == 0);
            return KEY_MODE;
        }
    }
    
    // 检测暂停键
    if(KEY_PAU_PIN == 0)
    {
        Delay_Ms(10);
        if(KEY_PAU_PIN == 0)
        {
            while(KEY_PAU_PIN == 0);
            return KEY_PAUSE;
        }
    }
    
    return KEY_NONE;
}

3.8 延时函数 (delay.h)

#ifndef __DELAY_H__
#define __DELAY_H__

#include <reg52.h>

// 函数声明
void Delay_Ms(unsigned int ms);
void Delay_Us(unsigned int us);

#endif

3.9 延时函数 (delay.c)

#include "delay.h"

// 毫秒延时
void Delay_Ms(unsigned int ms)
{
    unsigned int i, j;
    for(i = ms; i > 0; i--)
        for(j = 110; j > 0; j--);
}

// 微秒延时
void Delay_Us(unsigned int us)
{
    while(us--)
    {
        _nop_();
        _nop_();
        _nop_();
        _nop_();
    }
}

四、Proteus仿真电路

4.1 Proteus元件清单

1. AT89C52 (单片机)
2. 7SEG-MPX4-CA (4位共阳数码管)
3. BUTTON (按键×3)
4. RES (电阻220Ω×8)
5. RES (电阻10kΩ×3)
6. CRYSTAL (11.0592MHz)
7. CAP (电容30pF×2)
8. POWER (电源)
9. GROUND (地)
10. SIGNAL_GENERATOR (信号发生器,用于产生脉冲)

4.2 仿真连接图

AT89C52:
  P0.0-P0.7 -> 数码管段选a-g,dp
  P2.0-P2.3 -> 数码管位选1-4
  P3.0 -> 按键K1(清零)
  P3.1 -> 按键K2(模式)
  P3.2 -> 按键K3(暂停)
  P3.4 -> 信号发生器输出(T0计数输入)
  XTAL1/XTAL2 -> 11.0592MHz晶振
  RST -> 10kΩ电阻+10μF电容
  VCC -> +5V
  GND -> GND

五、功能扩展

5.1 增加计数频率测量

// 测量计数频率
void Measure_Frequency(void)
{
    static unsigned int last_count = 0;
    static unsigned long last_time = 0;
    unsigned int current_count;
    unsigned long current_time;
    unsigned int frequency;
    
    current_time = Get_System_Time();  // 获取系统时间(ms)
    current_count = Timer0_Get_Value();
    
    if(current_time - last_time >= 1000)  // 每秒测量一次
    {
        frequency = current_count - last_count;
        last_count = current_count;
        last_time = current_time;
        
        // 显示频率
        Display_Frequency(frequency);
    }
}

5.2 增加计数预置值

// 设置预置值
void Set_Preset_Value(unsigned int preset)
{
    count_value = preset;
    Timer0_Set_Value(preset);
    Display_Number(count_value);
}

5.3 增加串行通信

// 串口初始化
void UART_Init(void)
{
    TMOD &= 0x0F;  // 清除T1控制位
    TMOD |= 0x20;  // 设置T1为模式2(8位自动重载)
    TH1 = 0xFD;    // 波特率9600
    TL1 = 0xFD;
    TR1 = 1;       // 启动T1
    SM0 = 0;       // 设置串口为模式1
    SM1 = 1;
    REN = 1;       // 允许接收
    EA = 1;        // 允许总中断
    ES = 1;        // 允许串口中断
}

// 串口中断服务程序
void UART_ISR(void) interrupt 4
{
    unsigned char received_data;
    
    if(RI)  // 接收中断
    {
        RI = 0;  // 清除接收中断标志
        received_data = SBUF;
        
        // 根据接收到的数据设置预置值
        if(received_data >= '0' && received_data <= '9')
        {
            unsigned int digit = received_data - '0';
            count_value = count_value * 10 + digit;
            if(count_value > 9999)
                count_value = 9999;
            Display_Number(count_value);
        }
        else if(received_data == 'C' || received_data == 'c')
        {
            count_value = 0;
            Display_Number(count_value);
        }
    }
}

参考代码 数码管T0计数器设计 www.youwenfan.com/contentcnv/118036.html

六、使用说明

6.1 操作步骤

  1. 接通5V电源
  2. 数码管显示初始值"0000"
  3. 将脉冲源连接到P3.4(T0)引脚
  4. 按"模式"键切换加/减计数模式
  5. 按"暂停"键暂停/继续计数
  6. 按"清零"键将计数值清零

6.2 注意事项

  1. 外部脉冲幅度需在0-5V之间
  2. 脉冲宽度需大于2个机器周期
  3. 最大计数频率不超过50kHz
  4. 计数溢出时会自动循环

6.3 调试技巧

  1. 先用按键模拟脉冲测试基本功能
  2. 再用信号发生器测试高频计数
  3. 观察LED指示灯判断计数状态
  4. 使用示波器观察T0引脚波形

七、性能指标

参数 指标
计数范围 0-9999
计数模式 加计数/减计数
最大计数频率 50kHz
显示方式 4位静态显示
按键响应 <50ms
功耗 <50mA @5V
posted @ 2026-06-10 16:37  晃悠人生  阅读(5)  评论(0)    收藏  举报