1.功能介绍
抢答器是一种应用非常广泛的设备,在各种竞赛、抢答场合中,它能迅速、客观的分辨出最先获得发言权的选手。早期的抢答器只有几个三极管、可控硅、发光管等组成,能通过发光管的指示辨认出选手号码。现在大多数抢答器均使用单片机(如MCS-51型)和数字集成电路,并增加了许多新功能,如选手号码显示、抢答前或抢答后的计时、选手得分显示等功能。能够实现抢答器功能的方式有多种,可以采用前期的模拟电路、数字电路或模拟与数字电路相结合的方式。近年来随着科技的飞速发展,单片机的应用正在不断深入。
抢答器作为一种电子产品,早已广泛应用于各种智力知识竞赛场合,但目前所使用的抢答器有的电路较复杂不便于制作,可靠性低,实现起来很困难。为适应目前的状况,需要一种电路简单,元件普通,易于购买等优点,并且能够很好的解决各种问题。而目前的多数抢答器存在几个不足之处:第一,现场线路连接复杂,降低了抢答器的可能性,增加了安装难度;第二,电路设计复杂。因为单片机只能完成号码处理、计时、数据运算等动能,其他功能仍只能靠数字集成电路完成,电路延迟时间较大。
本设计利用AT89C51单片机及外围借口实现抢答系统,利用单片机的定时/计数器和计数原理,将软硬件有机的结合起来,使系统能够正确地进行计时,同时能够使LED数码管正确的显示时间和使LCD1602显示选手号码。用按键作为键盘输出,二极管、扬声器发出提示。系统可达到的要求:在抢答中,只有按下开始键抢答才有效,若在之前抢答则记为违规。抢答限定时间及回答限定时间均可调节;可以显示哪位选手抢答,抢答剩余时间及无效抢答,正确按键有声音提示;抢答时间及回答时间倒计时显示,时间计完后可由主持人控制复位;剩余时间为0秒时,选手便不可以进行抢答,须由主持人控制开始下一轮抢答。本设计采用模块化设计智能抢答器,在各种场合下均可应用。各组分别有一抢答按键,主持人有开始键、回答问题开始键、复位键、抢答时间调节键和答题时间调节键。通过设计验证,采用单片机设计,电路连接简单工作可靠,抗干扰性能力强。通过这次设计,可以掌握单片机的原理,了解多功能抢答器的组成原理,提高动手能力,巩固理论知识,建立一套完整的理论实践体系。
2.硬件设计
该抢答器硬件由 6 个部分组成时钟频率电路、复位电路、蜂鸣器电路、按键电路、LCD1602显示电路、数码管显示电路组成。
3.硬件电路图
4.源代码
其他函数
/**************************************************************************
仿真实验3:16路选手抢答器的仿真设计(程序)
由主持人控制开始抢答,主持人可以通过K1控制开始抢答,K2控制倒计时初始时间增加,K3控制倒计时初始时间减少。
若未开始抢答有选手抢答,蜂鸣器将长鸣。开始抢答后若有选手抢答倒计时停止,LCD1602将第一行显示:" celebrate you! " ;
第二行显示:"dat1[]~dat16[]";若无选手抢答数码管显示“FFFF”,由此实现抢答器功能。
实验操作:
S1-S4:dat1[]-dat4[]
S5-S8:dat5[]-dat8[]
S9-S12:dat9[]-dat12[]
S13-S16:dat13[]-dat16[]
***************************************************************************/
#include<reg51.h>
#define GPIO_KEY P1
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
/**********************************
PIN口定义
**********************************/
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;
/**********************************
函数声明
**********************************/
/*在51单片机12MHZ时钟下的延时函数*/
void Lcd1602_Delay1ms(uint c); //误差 0us
/*LCD1602写入8位命令子函数*/
void LcdWriteCom(uchar com);
/*LCD1602写入8位数据子函数*/
void LcdWriteData(uchar dat) ;
/*LCD1602初始化子程序*/
void LcdInit();
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
unsigned char KeyValue;//用来存放读取到的键值
unsigned char KeyState;//用来存放按键状态
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;//设置38译码器的输入端
sbit beep=P2^5;//定义蜂鸣器引脚
sbit start=P3^1;//设置支持人控制开始键
sbit add=P3^0; //设置抢答时间增加键
sbit jian=P3^2;//设置抢答时间减少键
u16 flag=3000,num=0;//设置数码管倒计时初值以及中断次数变量
u8 code smgduan[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
u8 code smgduan1[16]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,
0xff,0xef};//显示数码管第三位“秒”(0.~9.)的值
unsigned char PuZh[]=" celebrate you! ";
unsigned char code dat1[] =" No.1 player ";
unsigned char code dat2[]=" No.2 player ";
unsigned char code dat3[]=" No.3 player ";
unsigned char code dat4[]=" No.4 player ";
unsigned char code dat5[]=" No.5 player ";
unsigned char code dat6[]=" No.6 player ";
unsigned char code dat7[]=" No.7 player ";
unsigned char code dat8[]=" No.8 player ";
unsigned char code dat9[]=" No.9 player ";
unsigned char code dat10[]=" No.10 player ";
unsigned char code dat11[]=" No.11 player ";
unsigned char code dat12[]=" No.12 player ";
unsigned char code dat13[]=" No.13 player ";
unsigned char code dat14[]=" No.14 player ";
unsigned char code dat15[]=" No.15 player ";
unsigned char code dat16[]=" No.16 player ";
/**************************************************************************
* 函 数 名 : Lcd1602_Delay1ms
* 函数功能 : 延时函数,延时1ms
* 输 入 : c
* 输 出 : 无
* 说 名 : 该函数是在12MHZ晶振下,12分频单片机的延时。
***************************************************************************/
void Lcd1602_Delay1ms(uint c) //误差 0us
{
uchar a,b;
for (; c>0; c--)
{
for (b=199;b>0;b--)
{
for(a=1;a>0;a--);
}
}
}
/**************************************************************************
* 函 数 名 : LcdWriteCom
* 函数功能 : 向LCD写入一个字节的命令
* 输 入 : com
* 输 出 : 无
***************************************************************************/
#ifndef LCD1602_4PINS //当没有定义这个LCD1602_4PINS时
void LcdWriteCom(uchar com) //写入命令
{
LCD1602_E = 0; //使能
LCD1602_RS = 0; //选择发送命令
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = com; //放入命令
Lcd1602_Delay1ms(1); //等待数据稳定
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E = 0;
}
#endif
/**************************************************************************
* 函 数 名 : LcdWriteData
* 函数功能 : 向LCD写入一个字节的数据
* 输 入 : dat
* 输 出 : 无
***************************************************************************/
#ifndef LCD1602_4PINS
void LcdWriteData(uchar dat) //写入数据
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 1; //选择输入数据
LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = dat; //写入数据
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5); //保持时间
LCD1602_E = 0;
}
#endif
/**************************************************************************
* 函 数 名 : LcdInit()
* 函数功能 : 初始化LCD屏
* 输 入 : 无
* 输 出 : 无
***************************************************************************/
#ifndef LCD1602_4PINS
void LcdInit() //LCD初始化子程序
{
LcdWriteCom(0x38); //开显示
LcdWriteCom(0x0c); //开显示不显示光标
LcdWriteCom(0x06); //写一个指针加1
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //设置数据指针起点
}
#endif
/**************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
***************************************************************************/
void delay(int i)
{
while(i--);
}
/**************************************************************************
* 函 数 名 : sound
* 函数功能 : 控制蜂鸣器
* 输 入 : 无
* 输 出 : 无
***************************************************************************/
void sound(int dat)
{ int i=0;
while(i<50)
{
beep=~beep;
delay(dat); //延时大约1ms 通过修改此延时时间达到不同的发声效果
i++;
}
}
/**************************************************************************
* 函 数 名 : Timer0Init()
* 函数功能 : 定时器中断函数
* 输 入 : 无
* 输 出 : 无
***************************************************************************/
void Timer0Init(void)
{
TMOD =0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值 1ms
TF0 = 0; //清除TF0标志
ET0 = 1; //定时/计数器T0中断允许开启
EA = 1; //开启总中断
TR0 = 1; //定时器0开始计时
}
/**************************************************************************
* 函 数 名 : DelayMS()
* 函数功能 : 提高倒计时数码管亮度
* 输 入 : 无
* 输 出 : 无
***************************************************************************/
void DelayMS(u8 i)
{
//大约 1ms
u8 j=100;
while(i--)
{
while(j--);
}
}
/**************************************************************************
* 函 数 名 : DIGdisplay()
* 函数功能 : 数码管显示倒计时
* 输 入 : flag(初始化时间)
* 输 出 : 数码管倒计时效果
***************************************************************************/
void DIGdisplay()
{
u8 i;
for (i=0;i<4;i++)
{
switch(i)
{
case(0): LSA = 0 ;LSB = 0 ; LSC = 1;P0 = smgduan[flag%10] ;DelayMS(1);break;//第一位数码管
case(1): LSA = 1 ;LSB = 0 ; LSC = 1 ;P0 = smgduan[flag/10%10] ;DelayMS(1);break;//第二位数码管
case(2): LSA = 0 ;LSB = 1 ; LSC = 1 ;P0 = smgduan1[flag/100%10] ;DelayMS(1);break;//第三位数码管
case(3): LSA = 1 ;LSB = 1 ; LSC = 1 ;P0 = smgduan[flag/1000] ;DelayMS(1);break;//第四位数码管
}
P0 =0X00;//消隐
}
}
/**************************************************************************
* 函 数 名 : time()
* 函数功能 : 初始化T0计时器
* 输 入 : flag(初始化时间)
* 输 出 : 数码管倒计时效果
***************************************************************************/
void time() interrupt 1
{
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值 1ms
num++;
if(num==10) //20ms则跳数字
{
num=0;
flag--;
}
if(flag==0)
{
while(1)
{
LSA = 0 ;LSB = 0 ; LSC = 1 ;P0=0x71;
LSA = 1 ;LSB = 0 ; LSC = 1 ;P0=0X71;
LSA = 0 ;LSB = 1 ; LSC = 1 ;P0=0x71;
LSA = 1 ;LSB = 1 ; LSC = 1 ;P0=0X71;
}
}
}
/**************************************************************************
* 函 数 名 : lcd_string
* 函数功能 : 控制lcd1602字符显示的位置与显示的内容
* 输 入 : 无
* 输 出 : 无
***************************************************************************/
void lcd_string(u8 ad,u8 *s)
{
LcdWriteCom(ad);
while(*s>0)
{
LcdWriteData(*s++) ;
delay(100);
}
}
/**************************************************************************
* 函 数 名 : KeyDown
* 函数功能 : 检测有按键按下并读取键值
* 输 入 : 无
* 输 出 : 无
***************************************************************************/
void KeyDown(void)
{
char a;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)
{
delay(1000);
if(GPIO_KEY!=0x0f)
{
KeyState=1;
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): KeyValue=1,sound(100);break;
case(0X0b): KeyValue=2,sound(100);break;
case(0X0d): KeyValue=3,sound(100);break;
case(0X0e): KeyValue=4,sound(100);break;
}
GPIO_KEY=0XF0;
delay(1000);
switch(GPIO_KEY)
{
case(0X70): KeyValue=KeyValue,sound(100);break;
case(0Xb0): KeyValue=KeyValue+4,sound(100);break;
case(0Xd0): KeyValue=KeyValue+8,sound(100);break;
case(0Xe0): KeyValue=KeyValue+12,sound(100);break;
}
while((a<50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测
{
delay(1000);//消抖
a++;
}
a=0;
}
}
}
LCD1602显示函数
/**************************************************************************
* 函 数 名 : xianshi()
* 函数功能 : 使lcd1602显示第一行与第二行的内容
* 输 入 : PuZh,dat1~dat16
* 输 出 : PuZh,dat1~dat16
***************************************************************************/
void xianshi()
{
KeyDown() ;
switch(KeyValue)
{
case(1):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat1);while(1) ;break;
case(2):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat2);while(1) ;break;
case(3):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat3);while(1) ;break;
case(4):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat4);while(1) ;break;
case(5):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat5);while(1) ;break;
case(6):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat6);while(1) ;break;
case(7):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat7);while(1) ;break;
case(8):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat8);while(1) ;break;
case(9):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat9);while(1) ;break;
case(10):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat10);while(1) ;break;
case(11):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat11);while(1) ;break;
case(12):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat12);while(1) ;break;
case(13):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat13);while(1) ;break;
case(14):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat14);while(1) ;break;
case(15):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat15);while(1) ;break;
case(16):lcd_string(0x80,PuZh),lcd_string(0x80+0x40,dat16);while(1) ;break;
}
}
main函数
/**************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
***************************************************************************/
void main(void)
{
LcdInit();
while(1)
{
LSA = 0 ;LSB = 0 ; LSC = 1 ;P0=0x40;
LSA = 1 ;LSB = 0 ; LSC = 1 ;P0=0X40;
LSA = 0 ;LSB = 1 ; LSC = 1 ;P0=0x40;
LSA = 1 ;LSB = 1 ; LSC = 1 ;P0=0x40;
KeyDown();
if( KeyValue!=0)
{
while(1)
{
sound(100);
}
}
if(start==0)
{
Timer0Init();
delay(1000);
if(start==0)
{
while(1)
{
DIGdisplay();
xianshi();
}
while(!start);
}
}
if(add==0)
{
delay(100);
if(add==0)
{
flag=flag+100;
while(!add);
}
} if(jian==0)
{
delay(1000);
if(jian==0)
{
flag=flag-100;
while(!jian);
}
}
}
}
注:本人水平有限,大神请绕道,不喜勿喷。