目录
1.设计要求
以单片机为核心,设计一个8位竞赛抢答器:同时供8名选手或8个代表队比赛,分别用8个按钮S0~S7表示。
设置一个系统清除和抢答控制开关S,开关由主持人控制。
抢答器具有锁存与显示功能。即选手按按钮,锁存相应的编号,并在优先抢答选手的编号一直保持到主持人将系统清除为止。
抢答器具有定时抢答功能,且一次抢答的时间由主持人设定(如30秒)。
当主持人启动“开始”键后,定时器进行减计时,同时扬声器发出短暂的声响,声响持续的时间为0.5s左右。
参赛选手在设定的时间内进行抢答,抢答有效,定时器停止工作,显示器上显示选手的编号和抢答的时间,并保持到主持人将系统清除为止。
如果定时时间已到,无人抢答,本次抢答无效,系统报警并禁止抢答,定时显示器上显示00。
2. 实验原理
通过键盘改变抢答的时间,原理与闹钟时间的设定相同,将定时时间的变量置为全局变量后,通过键盘扫描程序使每按下一次按键,时间加1(超过30时置0)。同时单片机不断进行按键扫描,当参赛选手的按键按下时,用于产生时钟信号的定时计数器停止计数,同时将选手编号(按键号)和抢答时间分别显示在LED上。
3. 电路设计(Proteus仿真通过)
4. 单片机运行结果
5.流程图展示
6.代码展示
#include <reg52.h> // 包含reg52头文件
#define uchar unsigned char // 定义uchar为无符号字符型
#define uint unsigned int // 定义uint为无符号整型
#define GPIO_DIG P0 // 定义GPIO_DIG为P0
#define GPIO_KEY P1 // 定义GPIO_KEY为P1
sbit en = P2^7; // 使能引脚
sbit rs = P2^6; // 寄存器选择引脚
sbit rw = P2^5; // 读/写选择引脚
sbit beep = P1^5; // 蜂鸣器引脚
sbit K0 = P3^0; // K0引脚
sbit K1 = P3^1; // K1引脚
sbit K2 = P3^2; // K2引脚
sbit K3 = P3^3; // K3引脚
sbit P20 = P2^0; // P20引脚
// 数码管显示字符的编码
uchar code start[] = "Welcome to game"; // 开始游戏提示语
uchar code time[] = "0123456789";
uchar name_time[] = "00";
uchar name_time1[] = "00";
uchar timer[] = {0, 0}; // 计时器数组,用于显示时间
uint count = 0; // 定时器中断计数器
uchar KeyValue = 0; // 键值
uchar name1 = 0; // 名称
uchar str1[] = {"012345678"};
uchar str0[] = {"name"};
uchar str2[] = {"replied"};
void delay(uint z); // 延时函数声明
void write_com(uchar com); // 写命令函数声明
void init(); // 初始化函数声明
void write_data(uchar date); // 写数据函数声明
void LCD_SetCursor(uchar Line, uchar Columu); // 设置LCD光标函数声明
void LCD_ShowString(unsigned char Line, unsigned char Columu, unsigned char* String); // LCD显示字符串函数声明
void T0_init(); // 定时器T0初始化函数声明
void T1_init(); // 定时器T1初始化函数声明
void KeyDown(); // 按键检测函数声明
void sound(); // 蜂鸣器函数声明
void start_game(); // 游戏开始函数声明
void timer_init(); // 计时器初始化函数声明
void shijian();
void main()
{
init(); // LCD初始化
T0_init(); // 初始化定时器0
T1_init(); // 初始化外部中断
timer_init();
write_com(0x80); // 设置LCD显示位置
while(1)
{
start_game(); // 游戏开始
}
}
// 延时函数
void delay(uint z)
{
uint x, y;
for (x = z; x > 0; x--)
for (y = 110; y > 0; y--);
}
void delay1(unsigned int i) //延时函数
{
while (i--)
;
}
// 写命令
void write_com(uchar com) {
rs = 0; // 设置为命令寄存器
rw = 0; // 写操作
P0 = com; // 将命令写入数据口
delay(10);
en = 1; // 使能LCD
en = 0; // 禁用LCD
}
// LCD初始化
void init() {
rw = 0; // 写操作
en = 0; // 禁用LCD
write_com(0x38); // 设置为8位数据接口,显示模式为2行显示,字符大小为5x8
write_com(0x0c); // 开启显示,关闭光标
write_com(0x06); // 光标右移,字符不移动
write_com(0x01); // 清屏
}
// 写数据
void write_data(uchar date) {
rs = 1; // 设置为数据寄存器
rw = 0; // 写操作
P0 = date; // 将数据写入数据口
delay(10);
en = 1; // 使能LCD
en = 0; // 禁用LCD
}
// 1s定时器
void timer0() interrupt 1 {
TH0 = (65536 - 50000) / 256; // 设置定时器初值高位
TL0 = (65536 - 50000) % 256; // 设置定时器初值低位
count++;
if (count >= 20) { // 每计数20次(约1秒)
count = 0;
if (timer[0] == 0 && timer[1] == 0)
{
TR0 = 0;
init(); // 清屏
LCD_ShowString(1 , 1 , "no replied");
} else
{
if (timer[1] == 0)
{
if (timer[0] > 0)
{
timer[0]--; // 减少十位
timer[1] = 9; // 设置个位为9
}
} else
{
timer[1]--; // 仅减少个位
}
}
}
}
//停止与继续中断函数
void INT_1() interrupt 2 {
if (!K3) {
delay(10);
if (!K3) {
TR0 = !TR0; // 切换定时器启停状态
while (!K3);
}
}
}
// 定时器T0初始化
void T0_init() {
TMOD = 0x01; // 定时器0工作在方式1(16位定时器)下
TH0 = (65536 - 50000) / 256;
TL0 = (65536 - 50000) % 256;
EA = 1; // 允许中断
ET0 = 1; // 允许定时器0中断
}
// 定时器T1初始化
void T1_init() {
EA = 1; // 开启总中断
EX1 = 1; // 开启1号外部中断
IT1 = 1; // 设置外部中断0为下降沿触发
}
// lcd显示函数
void LCD_ShowString(uchar Line, uchar Columu, uchar* String)
{
unsigned char i = 0;
// 设置LCD光标位置
LCD_SetCursor(Line, Columu);
// 遍历字符串并逐个显示字符
for (i = 0; String[i] != '\0'; i++)
{
write_data(String[i]);
}
}
// 设置LCD光标位置
void LCD_SetCursor(uchar Line, uchar Columu)
{
if (Line == 1)
{
write_com(0x80 | (Columu - 1));
}
else
{
write_com(0x80 | (Columu - 1) + 0x40);
}
}
// 蜂鸣器
void sound()
{
uchar i = 50;
// 循环控制蜂鸣器的开关以产生声音
while (i--)
{
beep = ~beep;
delay(200);
}
}
// 矩阵键盘扫描
void KeyDown()
{
GPIO_KEY = 0x0f;
if (GPIO_KEY != 0x0f) // 如果按键按下
{
delay(10); // 延时10ms消抖
if (GPIO_KEY != 0x0f) // 按键确实按下
{
// 判断按键所在列,以所在列的第一行的按键键值赋给KeyValue
GPIO_KEY = 0X0F;
switch (GPIO_KEY)
{
case (0X07): KeyValue = 1; break; // 第一列按下
case (0X0b): KeyValue = 2; break; // 第二列按下
case (0X0d): KeyValue = 3; break; // 第三列按下
case (0X0e): KeyValue = 4; break; // 第四列按下
}
// 判断按键所在行
GPIO_KEY = 0XF0;
switch (GPIO_KEY)
{
case (0X70): KeyValue = KeyValue; break; // 第一行按下
case (0Xb0): KeyValue = KeyValue + 4; break; // 第二行按下
case (0Xd0): KeyValue = KeyValue + 8; break; // 第三行按下
case (0Xe0): KeyValue = KeyValue + 12; break; // 第四行按下
}
while (GPIO_KEY != 0xf0); // 按键松手后退出
}
}
}
//蜂鸣器函数
void beep1()
{
unsigned char i;
for (i = 0; i < 100; i++)
{
delay1(100);
beep = ~beep;
}
}
//抢答器函数
void xianshiqiangda()
{
unsigned int i = 0;
unsigned int j;
j = KeyValue;
write_com(0x80);
delay(1000);
// 遍历字符串str0并逐个显示字符
while (str0[i] != '\0')
{
write_data(str0[i]);
i++;
}
TR0 = 0;
// 计算name_time[0]的值
name1 = name_time[0] * 10 - timer[0] * 10 - timer[1];
name_time[0] = name1 / 10;
name_time[1] = (name1 % 10)-1;
write_com(0x80 + 0x06);
delay(10);
write_data(str1[j]);
delay(10);
// 在LCD上显示"name_number:"
LCD_ShowString(2, 1, "name_number:");
// 在LCD上显示name_time[0]和name_time[1]对应的时间
write_data(time[name_time[0]]);
delay(5);
write_data(time[name_time[1]]);
while (1);
}
// 开始函数
void start_game()
{
if (!K2)
{
delay(5);
if (!K2)
{
// 在LCD上显示"start"
LCD_ShowString(1, 1, start);
delay(10);
// 在LCD上显示"time:"
LCD_ShowString(2, 1, "time:");
TR0 = 1;
while (1)
{
// 在LCD上显示当前计时器的值
write_com(0x80 + 0x40 + 0x07);
write_data(time[timer[0]]);
delay(5);
write_data(time[timer[1]]);
delay(5);
if (GPIO_KEY != 0xf0)
{
break;
}
}
}
while (!K2);
}
if (timer[0] != 0 && timer[1] != 0)
{
write_com(0x01);
KeyDown();
beep1();
if (KeyValue < 8)
{
while (1)
{
xianshiqiangda();
}
}
}
}
//设置初时函数
void timer_init()
{
// 在LCD上显示"set the time:"
LCD_ShowString(1, 1, "set the time:");
// 初始化计时器数组
timer[0] = 0; // 初始化第一个元素
timer[1] = 0; // 初始化第二个元素
while (1)
{
KeyDown();
// 获取按键值并赋给timer[0]
timer[0] = KeyValue;
name_time[0] = KeyValue;
name_time[1] = 0;
if (timer[0] != 0)
{
init();
// 在LCD上显示"Countdown time:"
LCD_ShowString(1, 1, "Countdown time:");
// 在LCD上显示设置的倒计时时间
write_com(0x80 + 0x40 + 0x03);
write_data(time[timer[0]]);
delay(5);
write_data(time[timer[1]]);
delay(5);
KeyValue = 0;
break;
}
}
}