【单片机系列】基于51单片机的16路抢答器

   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);

}

}

 }

}

注:本人水平有限,大神请绕道,不喜勿喷。

  • 12
    点赞
  • 115
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南星加油

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值