一、系统总体设计
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 硬件连接表
/* 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 延时函数
/* 延时函数 */
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 数码管显示驱动
/* 数码管显示驱动 */
// 数码管字形码(共阳极)
unsigned char code seg_tab[10] = {0xC0, // "0" - 110000000xF9, // "1" - 111110010xA4, // "2" - 101001000xB0, // "3" - 101100000x99, // "4" - 100110010x92, // "5" - 100100100x82, // "6" - 100000100xF8, // "7" - 111110000x80, // "8" - 100000000x90 // "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 按键扫描程序
/* 按键扫描程序 */
unsigned char Key_Scan(void) {unsigned char key_value = 0xFF; // 无按键unsigned char temp;// 扫描第1行(P1.0-P1.3)KEY_PORT = 0xFE; // 11111110temp = 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; // 11111101temp = 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; // 11111011temp = 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; // 11110111temp = 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 计分逻辑处理
/* 计分逻辑处理 */
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 完整主程序
/* 篮球计分器主程序 */
#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 节次管理功能
/* 节次管理 */
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 暂停时间管理
/* 暂停时间管理 */
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 犯规次数统计
/* 犯规次数统计 */
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/contentcnu/133192.html
六、调试与测试
6.1 测试程序
/* 系统测试程序 */
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显示屏
这个篮球计分器系统成本低、功能实用、操作简单,非常适合学校、社区等场所的篮球比赛使用!
