一、系统总体设计
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单片机的篮球计分器实现了以下功能:
核心功能:
- 实时计分:支持A/B两队1分、2分、3分加减
- 比赛计时:10分钟比赛倒计时,精确到秒
- 24秒计时:进攻时间管理,违例报警
- 显示控制:4位LED数码管显示比分和时间
- 声音提示:蜂鸣器提供操作反馈和报警
技术亮点:
- 矩阵按键扫描:16键键盘,功能齐全
- 动态显示扫描:4位数码管分时显示,节省I/O口
- 精确定时:定时器中断实现精确的时间管理
- 低功耗设计:合理使用延时和资源
- 扩展性强:预留节次、犯规、暂停等功能接口
应用场景:
- 校园篮球赛:班级联赛、校际比赛
- 社区篮球赛:业余联赛、友谊赛
- 训练比赛:球队训练、技能考核
- 娱乐活动:公司团建、生日派对
- 教学演示:单片机课程设计、电子竞赛
改进建议:
- 增加LCD显示:显示更多信息(节次、犯规、暂停)
- 无线遥控:添加红外遥控功能
- 数据存储:记录比赛数据到EEPROM
- 语音播报:添加ISD1820语音模块
- LED大屏:驱动更大尺寸的LED显示屏
这个篮球计分器系统成本低、功能实用、操作简单,非常适合学校、社区等场所的篮球比赛使用!