基于51单片机的篮球计分器设计

一、系统总体设计

1.1 系统架构

复制代码
┌─────────────────────────────────────────────────────────┐
│                    篮球计分器系统架构                  │
├─────────────────────────────────────────────────────────┤
│  ┌──────────┐   ┌──────────┐   ┌──────────┐       │
│  │  按键模块 │   │  显示模块 │   │  报警模块 │       │
│  │ 4×4矩阵  │◄──┤ 4位LED  │◄──┤ 蜂鸣器  │       │
│  │ 键盘输入 │   │ 数码管显示 │   │ 声音提示 │       │
│  └────┬─────┘   └────┬─────┘   └────┬─────┘       │
│       │                │                │                  │
│  ┌────▼────────────────▼────────────────▼─────┐       │
│  │              AT89C51 主控芯片              │       │
│  │  • 按键扫描处理      • 分数计算逻辑          │       │
│  │  • 显示刷新控制      • 24秒计时管理         │       │
│  │  • 比赛时间管理      • 报警控制             │       │
│  └─────────────────────────────────────────────┘       │
└─────────────────────────────────────────────────────────┘

1.2 功能需求

功能模块 具体功能 说明
计分功能 A队/B队加分/减分 支持1分、2分、3分
计时功能 比赛时间、24秒计时 精确到秒
显示功能 分数、时间、节次显示 4位LED数码管
控制功能 开始/暂停/复位 比赛流程控制
报警功能 24秒违例提示 蜂鸣器报警

二、硬件设计

2.1 核心元件选型

模块 型号 数量 说明
主控 AT89C51 1 8位单片机,4KB Flash
显示 4位共阳数码管 2组 A队/B队分数显示
按键 轻触按键 16个 功能按键输入
驱动 74HC245 2片 数码管段选驱动
锁存 74HC573 2片 数码管位选锁存
晶振 11.0592MHz 1 系统时钟
复位 10uF电容+10K电阻 1 上电复位
报警 有源蜂鸣器 1 声音提示

2.2 硬件连接表

c 复制代码
/* 51单片机篮球计分器引脚定义 */
#include <reg52.h>
#include <intrins.h>

// 系统时钟定义
#define FOSC 11059200L   // 晶振频率11.0592MHz

// 数码管段选定义(P0口)
#define SEG_PORT P0        // 段选数据端口

// 数码管位选定义(P2口低4位)
sbit BIT1 = P2^0;        // 第1位数码管(A队十位)
sbit BIT2 = P2^1;        // 第2位数码管(A队个位)
sbit BIT3 = P2^2;        // 第3位数码管(B队十位)
sbit BIT4 = P2^3;        // 第4位数码管(B队个位)

// 按键定义(P1口)
#define KEY_PORT P1        // 按键端口

// 控制引脚定义(P3口)
sbit START_PAUSE = P3^0;  // 开始/暂停按键
sbit RESET = P3^1;        // 复位按键
sbit BEEP = P3^2;         // 蜂鸣器控制
sbit LED_A = P3^3;        // A队得分指示灯
sbit LED_B = P3^4;        // B队得分指示灯

// 数码管显示缓冲区
unsigned char disp_buf[4] = {0, 0, 0, 0};  // 显示数据缓存

// 系统变量
unsigned char team_a_score = 0;    // A队分数
unsigned char team_b_score = 0;    // B队分数
unsigned char game_time_min = 10;   // 比赛时间(分钟)
unsigned char game_time_sec = 0;    // 比赛时间(秒)
unsigned char shot_clock = 24;      // 24秒计时
bit game_running = 0;              // 比赛进行标志
bit shot_clock_running = 0;        // 24秒计时运行标志
unsigned long system_tick = 0;     // 系统节拍

三、软件系统设计

3.1 延时函数

c 复制代码
/* 延时函数 */
void DelayMs(unsigned int ms) {
    unsigned int i, j;
    for(i = 0; i < ms; i++)
        for(j = 0; j < 110; j++);
}

void DelayUs(unsigned int us) {
    while(us--) {
        _nop_(); _nop_(); _nop_(); _nop_();
    }
}

/* 定时器0初始化(1ms中断) */
void Timer0_Init(void) {
    TMOD &= 0xF0;    // 清除T0控制位
    TMOD |= 0x01;    // 设置T0为模式1(16位定时器)
    TH0 = 0xFC;      // 装载初值(1ms@11.0592MHz)
    TL0 = 0x66;
    ET0 = 1;         // 允许T0中断
    EA = 1;          // 允许总中断
    TR0 = 1;         // 启动T0
}

/* 定时器0中断服务函数 */
void Timer0_ISR(void) interrupt 1 {
    TH0 = 0xFC;     // 重装初值
    TL0 = 0x66;
    
    system_tick++;    // 系统节拍递增
    
    // 比赛时间计时
    if(game_running) {
        if(system_tick % 1000 == 0) {  // 每秒更新一次
            if(game_time_sec > 0) {
                game_time_sec--;
            } else {
                if(game_time_min > 0) {
                    game_time_min--;
                    game_time_sec = 59;
                } else {
                    game_running = 0;  // 比赛结束
                    BEEP = 0;           // 蜂鸣器报警
                    DelayMs(500);
                    BEEP = 1;
                }
            }
        }
    }
    
    // 24秒计时
    if(shot_clock_running) {
        if(system_tick % 1000 == 0) {  // 每秒更新一次
            if(shot_clock > 0) {
                shot_clock--;
                if(shot_clock == 0) {
                    shot_clock_running = 0;
                    BEEP = 0;           // 24秒违例报警
                    DelayMs(1000);
                    BEEP = 1;
                }
            }
        }
    }
}

3.2 数码管显示驱动

c 复制代码
/* 数码管显示驱动 */
// 数码管字形码(共阳极)
unsigned char code seg_tab[10] = {
    0xC0,  // "0" - 11000000
    0xF9,  // "1" - 11111001
    0xA4,  // "2" - 10100100
    0xB0,  // "3" - 10110000
    0x99,  // "4" - 10011001
    0x92,  // "5" - 10010010
    0x82,  // "6" - 10000010
    0xF8,  // "7" - 11111000
    0x80,  // "8" - 10000000
    0x90   // "9" - 10010000
};

// 显示扫描函数
void Display_Scan(void) {
    static unsigned char scan_bit = 0;
    
    // 关闭所有位选
    BIT1 = 1; BIT2 = 1; BIT3 = 1; BIT4 = 1;
    
    switch(scan_bit) {
        case 0:
            SEG_PORT = seg_tab[disp_buf[0]];  // A队十位
            BIT1 = 0;  // 选中第1位
            scan_bit = 1;
            break;
        case 1:
            SEG_PORT = seg_tab[disp_buf[1]];  // A队个位
            BIT2 = 0;  // 选中第2位
            scan_bit = 2;
            break;
        case 2:
            SEG_PORT = seg_tab[disp_buf[2]];  // B队十位
            BIT3 = 0;  // 选中第3位
            scan_bit = 3;
            break;
        case 3:
            SEG_PORT = seg_tab[disp_buf[3]];  // B队个位
            BIT4 = 0;  // 选中第4位
            scan_bit = 0;
            break;
    }
}

/* 更新显示缓冲区 */
void Update_Display_Buffer(void) {
    disp_buf[0] = team_a_score / 10;    // A队十位
    disp_buf[1] = team_a_score % 10;    // A队个位
    disp_buf[2] = team_b_score / 10;    // B队十位
    disp_buf[3] = team_b_score % 10;    // B队个位
}

/* 显示比分 */
void Display_Score(void) {
    Update_Display_Buffer();
}

/* 显示时间 */
void Display_Time(void) {
    disp_buf[0] = game_time_min / 10;   // 分钟十位
    disp_buf[1] = game_time_min % 10;   // 分钟个位
    disp_buf[2] = game_time_sec / 10;   // 秒钟十位
    disp_buf[3] = game_time_sec % 10;   // 秒钟个位
}

3.3 按键扫描程序

c 复制代码
/* 按键扫描程序 */
unsigned char Key_Scan(void) {
    unsigned char key_value = 0xFF;  // 无按键
    unsigned char temp;
    
    // 扫描第1行(P1.0-P1.3)
    KEY_PORT = 0xFE;  // 11111110
    temp = KEY_PORT & 0x0F;
    if(temp != 0x0F) {
        DelayMs(10);  // 消抖
        temp = KEY_PORT & 0x0F;
        if(temp != 0x0F) {
            switch(temp) {
                case 0x0E: key_value = 0x01; break;  // 第1行第1列 - A队+1分
                case 0x0D: key_value = 0x02; break;  // 第1行第2列 - A队+2分
                case 0x0B: key_value = 0x03; break;  // 第1行第3列 - A队+3分
                case 0x07: key_value = 0x04; break;  // 第1行第4列 - A队-1分
            }
            while((KEY_PORT & 0x0F) != 0x0F);  // 等待按键释放
            return key_value;
        }
    }
    
    // 扫描第2行(P1.4-P1.7)
    KEY_PORT = 0xFD;  // 11111101
    temp = KEY_PORT & 0x0F;
    if(temp != 0x0F) {
        DelayMs(10);
        temp = KEY_PORT & 0x0F;
        if(temp != 0x0F) {
            switch(temp) {
                case 0x0E: key_value = 0x05; break;  // 第2行第1列 - B队+1分
                case 0x0D: key_value = 0x06; break;  // 第2行第2列 - B队+2分
                case 0x0B: key_value = 0x07; break;  // 第2行第3列 - B队+3分
                case 0x07: key_value = 0x08; break;  // 第2行第4列 - B队-1分
            }
            while((KEY_PORT & 0x0F) != 0x0F);
            return key_value;
        }
    }
    
    // 扫描第3行
    KEY_PORT = 0xFB;  // 11111011
    temp = KEY_PORT & 0x0F;
    if(temp != 0x0F) {
        DelayMs(10);
        temp = KEY_PORT & 0x0F;
        if(temp != 0x0F) {
            switch(temp) {
                case 0x0E: key_value = 0x09; break;  // 开始/暂停
                case 0x0D: key_value = 0x0A; break;  // 复位
                case 0x0B: key_value = 0x0B; break;  // 24秒复位
                case 0x07: key_value = 0x0C; break;  // 节次切换
            }
            while((KEY_PORT & 0x0F) != 0x0F);
            return key_value;
        }
    }
    
    // 扫描第4行
    KEY_PORT = 0xF7;  // 11110111
    temp = KEY_PORT & 0x0F;
    if(temp != 0x0F) {
        DelayMs(10);
        temp = KEY_PORT & 0x0F;
        if(temp != 0x0F) {
            switch(temp) {
                case 0x0E: key_value = 0x0D; break;  // 显示切换(比分/时间)
                case 0x0D: key_value = 0x0E; break;  // 保留功能
                case 0x0B: key_value = 0x0F; break;  // 保留功能
                case 0x07: key_value = 0x10; break;  // 蜂鸣器测试
            }
            while((KEY_PORT & 0x0F) != 0x0F);
            return key_value;
        }
    }
    
    return 0xFF;  // 无按键
}

3.4 计分逻辑处理

c 复制代码
/* 计分逻辑处理 */
void Score_Process(unsigned char key) {
    switch(key) {
        case 0x01:  // A队+1分
            if(team_a_score < 99) {
                team_a_score++;
                LED_A = 0;  // A队指示灯亮
                BEEP = 0;   // 蜂鸣器响
                DelayMs(100);
                LED_A = 1;
                BEEP = 1;
            }
            break;
            
        case 0x02:  // A队+2分
            if(team_a_score <= 97) {
                team_a_score += 2;
                LED_A = 0;
                BEEP = 0;
                DelayMs(200);
                LED_A = 1;
                BEEP = 1;
            }
            break;
            
        case 0x03:  // A队+3分
            if(team_a_score <= 96) {
                team_a_score += 3;
                LED_A = 0;
                BEEP = 0;
                DelayMs(300);
                LED_A = 1;
                BEEP = 1;
            }
            break;
            
        case 0x04:  // A队-1分
            if(team_a_score > 0) {
                team_a_score--;
            }
            break;
            
        case 0x05:  // B队+1分
            if(team_b_score < 99) {
                team_b_score++;
                LED_B = 0;  // B队指示灯亮
                BEEP = 0;
                DelayMs(100);
                LED_B = 1;
                BEEP = 1;
            }
            break;
            
        case 0x06:  // B队+2分
            if(team_b_score <= 97) {
                team_b_score += 2;
                LED_B = 0;
                BEEP = 0;
                DelayMs(200);
                LED_B = 1;
                BEEP = 1;
            }
            break;
            
        case 0x07:  // B队+3分
            if(team_b_score <= 96) {
                team_b_score += 3;
                LED_B = 0;
                BEEP = 0;
                DelayMs(300);
                LED_B = 1;
                BEEP = 1;
            }
            break;
            
        case 0x08:  // B队-1分
            if(team_b_score > 0) {
                team_b_score--;
            }
            break;
    }
    
    Display_Score();  // 更新显示
}

/* 比赛控制处理 */
void Game_Control(unsigned char key) {
    switch(key) {
        case 0x09:  // 开始/暂停
            if(game_time_min > 0 || game_time_sec > 0) {
                game_running = !game_running;
                shot_clock_running = game_running;
                if(game_running) {
                    BEEP = 0;
                    DelayMs(50);
                    BEEP = 1;
                }
            }
            break;
            
        case 0x0A:  // 复位
            team_a_score = 0;
            team_b_score = 0;
            game_time_min = 10;
            game_time_sec = 0;
            shot_clock = 24;
            game_running = 0;
            shot_clock_running = 0;
            Display_Score();
            break;
            
        case 0x0B:  // 24秒复位
            shot_clock = 24;
            shot_clock_running = 1;
            break;
            
        case 0x0D:  // 显示切换
            static bit show_time = 0;
            show_time = !show_time;
            if(show_time) {
                Display_Time();
            } else {
                Display_Score();
            }
            break;
            
        case 0x10:  // 蜂鸣器测试
            BEEP = 0;
            DelayMs(500);
            BEEP = 1;
            break;
    }
}

四、主程序

4.1 完整主程序

c 复制代码
/* 篮球计分器主程序 */
#include <reg52.h>
#include <intrins.h>

// 函数声明
void Timer0_Init(void);
void Display_Scan(void);
void Display_Score(void);
void Update_Display_Buffer(void);
unsigned char Key_Scan(void);
void Score_Process(unsigned char key);
void Game_Control(unsigned char key);

/* 系统初始化 */
void System_Init(void) {
    // 初始化端口
    P0 = 0xFF;    // P0口上拉
    P1 = 0xFF;    // P1口上拉(按键输入)
    P2 = 0xFF;    // P2口输出高电平
    P3 = 0xFF;    // P3口上拉
    
    // 初始化变量
    team_a_score = 0;
    team_b_score = 0;
    game_time_min = 10;
    game_time_sec = 0;
    shot_clock = 24;
    game_running = 0;
    shot_clock_running = 0;
    
    // 初始化定时器
    Timer0_Init();
    
    // 初始化显示
    Display_Score();
    
    // 开机提示音
    BEEP = 0;
    DelayMs(200);
    BEEP = 1;
}

/* 主程序 */
void main(void) {
    unsigned char key_value;
    
    // 系统初始化
    System_Init();
    
    while(1) {
        // 按键扫描
        key_value = Key_Scan();
        
        if(key_value != 0xFF) {
            // 处理计分按键
            if(key_value <= 0x08) {
                Score_Process(key_value);
            }
            // 处理控制按键
            else if(key_value >= 0x09 && key_value <= 0x0D) {
                Game_Control(key_value);
            }
            // 处理其他功能按键
            else {
                Game_Control(key_value);
            }
        }
        
        // 显示扫描(放在主循环中持续刷新)
        Display_Scan();
        
        // 短暂延时,防止刷新过快
        DelayMs(1);
    }
}

五、功能扩展

5.1 节次管理功能

c 复制代码
/* 节次管理 */
unsigned char quarter = 1;  // 当前节次(1-4节)

void Quarter_Control(void) {
    if(quarter < 4) {
        quarter++;
    } else {
        quarter = 1;  // 回到第一节
    }
    
    // 重置比赛时间
    game_time_min = 10;
    game_time_sec = 0;
    team_a_score = 0;
    team_b_score = 0;
    
    // 显示节次
    disp_buf[0] = 0xBF;  // 显示"Q"(自定义字符)
    disp_buf[1] = quarter;
    disp_buf[2] = 0x00;  // 熄灭
    disp_buf[3] = 0x00;  // 熄灭
}

5.2 暂停时间管理

c 复制代码
/* 暂停时间管理 */
unsigned char timeout_count = 0;  // 暂停次数
bit timeout_active = 0;           // 暂停状态

void Timeout_Control(void) {
    if(!game_running && !timeout_active) {
        timeout_active = 1;
        game_running = 0;  // 停止比赛计时
        shot_clock_running = 0;  // 停止24秒计时
        
        // 显示暂停时间(30秒)
        game_time_min = 0;
        game_time_sec = 30;
        Display_Time();
    } else if(timeout_active) {
        timeout_active = 0;
        game_running = 1;  // 恢复比赛
        shot_clock_running = 1;  // 恢复24秒计时
        Display_Score();
    }
}

5.3 犯规次数统计

c 复制代码
/* 犯规次数统计 */
unsigned char foul_a = 0;  // A队犯规次数
unsigned char foul_b = 0;  // B队犯规次数

void Foul_Process(unsigned char team) {
    if(team == 0) {  // A队犯规
        if(foul_a < 5) {
            foul_a++;
        } else {
            // A队犯规满5次,对方罚球
            BEEP = 0;
            DelayMs(1000);
            BEEP = 1;
        }
    } else {  // B队犯规
        if(foul_b < 5) {
            foul_b++;
        } else {
            // B队犯规满5次,对方罚球
            BEEP = 0;
            DelayMs(1000);
            BEEP = 1;
        }
    }
}

参考代码 基于51单片机的篮球计分器 www.youwenfan.com/contentcst/133192.html

六、调试与测试

6.1 测试程序

c 复制代码
/* 系统测试程序 */
void System_Test(void) {
    unsigned char test_step = 0;
    
    while(1) {
        switch(test_step) {
            case 0:  // 数码管测试
                disp_buf[0] = 0x88;  // 显示"8."(带小数点)
                disp_buf[1] = 0x88;
                disp_buf[2] = 0x88;
                disp_buf[3] = 0x88;
                DelayMs(1000);
                test_step = 1;
                break;
                
            case 1:  // 按键测试
                disp_buf[0] = 0x86;  // 显示"E."
                disp_buf[1] = 0x86;
                disp_buf[2] = 0x86;
                disp_buf[3] = 0x86;
                
                if(Key_Scan() != 0xFF) {
                    BEEP = 0;
                    DelayMs(100);
                    BEEP = 1;
                    test_step = 2;
                }
                break;
                
            case 2:  // 计时器测试
                game_time_min = 0;
                game_time_sec = 5;
                game_running = 1;
                shot_clock = 5;
                shot_clock_running = 1;
                Display_Time();
                test_step = 3;
                break;
                
            case 3:  // 等待计时结束
                if(game_time_min == 0 && game_time_sec == 0) {
                    test_step = 4;
                }
                break;
                
            case 4:  // 测试完成
                disp_buf[0] = 0x8C;  // 显示"P."
                disp_buf[1] = 0x8C;
                disp_buf[2] = 0x8C;
                disp_buf[3] = 0x8C;
                BEEP = 0;
                DelayMs(500);
                BEEP = 1;
                DelayMs(500);
                test_step = 0;  // 循环测试
                break;
        }
        
        Display_Scan();
        DelayMs(1);
    }
}

七、总结

基于51单片机的篮球计分器实现了以下功能:

核心功能:

  1. 实时计分:支持A/B两队1分、2分、3分加减
  2. 比赛计时:10分钟比赛倒计时,精确到秒
  3. 24秒计时:进攻时间管理,违例报警
  4. 显示控制:4位LED数码管显示比分和时间
  5. 声音提示:蜂鸣器提供操作反馈和报警

技术亮点:

  1. 矩阵按键扫描:16键键盘,功能齐全
  2. 动态显示扫描:4位数码管分时显示,节省I/O口
  3. 精确定时:定时器中断实现精确的时间管理
  4. 低功耗设计:合理使用延时和资源
  5. 扩展性强:预留节次、犯规、暂停等功能接口

应用场景:

  • 校园篮球赛:班级联赛、校际比赛
  • 社区篮球赛:业余联赛、友谊赛
  • 训练比赛:球队训练、技能考核
  • 娱乐活动:公司团建、生日派对
  • 教学演示:单片机课程设计、电子竞赛

改进建议:

  1. 增加LCD显示:显示更多信息(节次、犯规、暂停)
  2. 无线遥控:添加红外遥控功能
  3. 数据存储:记录比赛数据到EEPROM
  4. 语音播报:添加ISD1820语音模块
  5. LED大屏:驱动更大尺寸的LED显示屏

这个篮球计分器系统成本低、功能实用、操作简单,非常适合学校、社区等场所的篮球比赛使用!

相关推荐
编程之升级打怪2 小时前
什么是PWM
嵌入式硬件
zmj3203243 小时前
单片机内存在C 语言编译后的 “逻辑分区”
c语言·单片机·内存分区
小柯博客4 小时前
STM32MP2 Secure Boot实战
stm32·单片机·嵌入式硬件
Ww.xh4 小时前
STM32调用AI接口完整教程
stm32·单片机·嵌入式硬件
ZYNQRFSOC4 小时前
基于安路PH2A系列FPGA的JESD204B接口测试
嵌入式硬件·fpga开发
LCG元5 小时前
STM32实战:基于STM32F407的LWIP以太网通信(TCP Server)
stm32·嵌入式硬件·tcp/ip
项目題供诗5 小时前
STM32-按键控制LED&光敏传感器控制蜂鸣器(五)
stm32·单片机·嵌入式硬件
猪八戒1.05 小时前
Allergo Cadence学习笔记
嵌入式硬件
危桥带雨5 小时前
WDG理论知识
stm32·单片机·嵌入式硬件