基于51单片机的抢答器设计(仿真)

        基于51单片机的抢答器设计,利用AT89C51作为系统主控,数码管进行显示倒计时,独立按键作为主持人按钮,同时为了抢答及时,采用外部中断扩展的方式进行抢答按键的设计,同时为了节省IO的,利用74HC595进行设计,如下图:

一、硬件设计

1、显示器件

        本次使用三个二位数码管进行显示,显示的内容分别是抢答倒计时、抢答选手号、回答倒计时三个数据,数码管的选型为共阴型,即低电平选中。

2、外部中断扩展

        由于抢答有时效性的要求,如果使用简单的循环扫描按键,这势必会导致有时候抢答不及时,甚至抢答卡顿,影响抢答额的公平性,但是AT89C51有且只有两个外部中断,于是需要使用外部中断扩展来实现功能,如下图,每个按键按下的时候都会触发INT0中断,而INT0刚好有最高优先级,且在外部中断引脚处各自连接了一个二极管,目的是为了防止除低电平以外的信号输入在外部中断上。

3、74HC595

        74HC595是一种串转并的芯片,它可以将输入的字节信号转换为八位电平信号,通过该方式,用三根数据线实现八个IO口的控制,大大节省了IO口,针对本次设计IO口紧张的情况是效果显著的。

二、软件设计

1、数码管驱动

        本次设计的数码管是共饮数码管了,因此首先需要获取到共阴数码管的段码表并用数组存储起来,方便日后进行显示,同时共阴数码管是低电平选中,在程序中也是要体现的。

#include <REGX52.H>
#include "74HC595.h"

//10在Data[]里的索引是空0x00
unsigned char nixie_Buf[9]={0,16,16,16,16,16,16,16,16};
unsigned char Data[]={0x3f,0x30,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00,0x40};

//将需要显示的值传递进来
void nixie(unsigned char Location,number)
{
	//把数组nixie_Buf的Location位换成number
	nixie_Buf[Location]=number;
}

void nixie_Scan(unsigned char Location,number)
{
	SendTo595_1(0x00);
	 switch(Location)
	{
		case 1:P0_3=0;P0_4=1;P0_5=1;P0_6=1;P0_7=1;P2_0=1;break;
		case 2:P0_3=1;P0_4=0;P0_5=1;P0_6=1;P0_7=1;P2_0=1;break;
		case 3:P0_3=1;P0_4=1;P0_5=0;P0_6=1;P0_7=1;P2_0=1;break;
		case 4:P0_3=1;P0_4=1;P0_5=1;P0_6=0;P0_7=1;P2_0=1;break;
		case 5:P0_3=1;P0_4=1;P0_5=1;P0_6=1;P0_7=0;P2_0=1;break;
		case 6:P0_3=1;P0_4=1;P0_5=1;P0_6=1;P0_7=1;P2_0=0;break;
		default:break;
	}
	SendTo595_1(Data[number]);
}

void nixie_Loop()
{
	static unsigned char i=1;
	//依次扫描数码管(通过遍历数组nixie_Buf的值)
	nixie_Scan(i,nixie_Buf[i]);
	i++;
	if(i>=9){i=1;}
}

2、74HC595驱动

        本次设计中,74HC595是进行数码管段选的,74HC595有一套简单的步骤,只需要根据该步骤书写代码,便可以实现功能。

#include "74HC595.h"
#include <intrins.h>

sbit SH_CP1 = P0^0;    //串行输入时钟,上升沿有效
sbit DS1 = P0^1;    //p3.4串行数据输入   DS
sbit ST_CP1 = P0^2;    //串行寄存器时钟,上升沿有效
 
//发送一个字节数据给595再并行输出
void SendTo595_1(unsigned char Data)
{
	char i=0;
	ST_CP1 = 0;
	for(i;i<8;i++)
	{
		SH_CP1 = 0; 
		 
		DS1=0x80&Data;//&为按位运算符,即全1为1,有0为0,上式也就是 (1000 0000)&(1111 1111)=1000 0000,若高位为1则是1高位为0则这个式子为0
		 
		Data=_crol_(Data,1); //左移一位 将高位补给低位,如果二进制数为01010101 那么_crol_(1) 为10101010

		SH_CP1 = 1;          //上升沿让串行输入时钟变成高电平 并延时一个时钟周期
		_nop_();

	}  

	/*位移寄存器完毕,转移到存储寄存器*/
	ST_CP1 = 1;         //上升沿,存储寄存器变为高电平 延迟两个时钟周期
	_nop_();
	_nop_();
 
}

3、外部中断扩展驱动

        外部中断扩展主要体现在硬件上,程序设计上只需要配置中断触发方式为低电平触发,并且在外部中断中判断触发电平的IO口,便可以实现外部中断的扩展。

//外部中断0初始化
void EXTI0_Init()
{
    IT0 = 1; // 设置外部中断 0 的触发方式为下降沿触发
    EX0 = 1; // 使能外部中断 0
    EA = 1;  // 全局中断使能	
}

//外部中断0的中断服务函数
void externalInterrupt0() interrupt 0 {
	if(mode == 1){
		if(flag == 0){
			if(P1_0 == 0){Number=1;flag=1;}
			if(P1_1 == 0){Number=2;flag=1;}
			if(P1_2 == 0){Number=3;flag=1;}
			if(P1_3 == 0){Number=4;flag=1;}
			if(P1_4 == 0){Number=5;flag=1;}
			if(P1_5 == 0){Number=6;flag=1;}
			if(P1_6 == 0){Number=7;flag=1;}
			if(P1_7 == 0){Number=8;flag=1;}				
		}
	}
	else if(mode == 0){
		if(P1_0 == 0){Number=1;}
		if(P1_1 == 0){Number=2;}
		if(P1_2 == 0){Number=3;}
		if(P1_3 == 0){Number=4;}
		if(P1_4 == 0){Number=5;}
		if(P1_5 == 0){Number=6;}
		if(P1_6 == 0){Number=7;}
		if(P1_7 == 0){Number=8;}
		error_flag = 1;		
	}
}

最后,附上本人的main函数。如下:

#include <REGX52.H>
#include "delay.h"
#include "Timer0.h"
#include "nixie.h"
#include "KEY.H"

typedef unsigned char u8;
typedef unsigned short u16;


u8 KeyNum,Temp;
char Sec,MiniSec;
u8 RunFlag = 0;
u8 Number = 0;
u8 mode = 0;
u8 flag=0;
u8 error_flag=0;

u8 score1=0, score2=0, score3=0, score4=0, score5=0, score6=0, score7=0, score8=0;

//外部中断0初始化
void EXTI0_Init()
{
    IT0 = 1; // 设置外部中断 0 的触发方式为下降沿触发
    EX0 = 1; // 使能外部中断 0
    EA = 1;  // 全局中断使能	
}

//外部中断0的中断服务函数
void externalInterrupt0() interrupt 0 {
	if(mode == 1){
		if(flag == 0){
			if(P1_0 == 0){Number=1;flag=1;}
			if(P1_1 == 0){Number=2;flag=1;}
			if(P1_2 == 0){Number=3;flag=1;}
			if(P1_3 == 0){Number=4;flag=1;}
			if(P1_4 == 0){Number=5;flag=1;}
			if(P1_5 == 0){Number=6;flag=1;}
			if(P1_6 == 0){Number=7;flag=1;}
			if(P1_7 == 0){Number=8;flag=1;}				
		}
	}
	else if(mode == 0){
		if(P1_0 == 0){Number=1;}
		if(P1_1 == 0){Number=2;}
		if(P1_2 == 0){Number=3;}
		if(P1_3 == 0){Number=4;}
		if(P1_4 == 0){Number=5;}
		if(P1_5 == 0){Number=6;}
		if(P1_6 == 0){Number=7;}
		if(P1_7 == 0){Number=8;}
		error_flag = 1;		
	}
}


void main()
{
	//定时器初始化
    Timer0_Init();
	//外部中断初始化
	EXTI0_Init();
	while(1)
	{	
		KeyNum = KEY();
		
		//清除按键按下,状态归0
		if(KeyNum == 3){
			flag=0;
			mode=0;
			error_flag=0;
			Number=0;
	
			nixie(1,16);
			nixie(2,16);			
			nixie(3,16);
			nixie(4,16);	
			nixie(5,16);
			nixie(6,16);
		}
		
		//抢答未开始前
		if(mode == 0){
			
			//按下抢答前选手按下,触发扣分
			if(error_flag == 1){
				nixie(3,15);
				nixie(4, Number);	
				nixie(5,17);
				nixie(6, 5);
			}

			//按键1,按下可以开始抢答
			if(KeyNum == 1){
				mode = 1;
				RunFlag = 1;
				Sec = 10;
				flag = 0;
			}
		}
		//抢答开始
		if(mode == 1){
			
			//有选手抢答成功
			if(flag == 1){
				nixie(3, Number/10);
				nixie(4, Number%10);
				RunFlag = 0;
			}
			
			//开始作答
			if(KeyNum == 2){
				Sec = 30;
				RunFlag = 1;
				mode = 2;
			}
				
			//显示10s抢答倒计时
			nixie(1, Sec/10);
			nixie(2, Sec%10);
		}
		if(mode == 2){
			//主持人按钮打分
			if(KeyNum == 4){
				nixie(5, 1);
				nixie(6, 0);
				RunFlag = 0;				
			}
			else if(KeyNum == 5){
				nixie(5, 0);
				nixie(6, 0);
				RunFlag = 0;				
			}

			//30s倒计时作答显示
			nixie(1, Sec/10);
			nixie(2, Sec%10);			
		}
    }
}
void Sec_Loop()
{
	if(RunFlag)
	{
		MiniSec++;
		if(MiniSec>=100)
		{
			MiniSec=0;
			if(--Sec<0)
			{
				Sec=0;
				RunFlag = 0;
			}		
		}		
	}	
}	

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count1,T0Count2,T0Count3;
	TL0=0X18;
	TH0=0xfc;
	T0Count1++;
	if(T0Count1>=20)
	{
		T0Count1=0;
		Key_Loop();
	}
	T0Count2++;
	if(T0Count2>=2)
	{
		T0Count2=0;
		nixie_Loop();
	}
	T0Count3++;
	if(T0Count3>=10)
	{
		T0Count3=0;
		Sec_Loop();
	}		
}

三、功能演示

1、程序运行,首先没有任何显示,因为没进行任何操作

2、按下抢答开始,进行10s倒计时

3、10s内按键按下,计时停止,同时显示抢答成功的ID号

4、按下计时,系统30s倒计时,此时选手可作答,专家给出评分后,系统才停止。

四、项目总结

        本次设计利用三个二位数码管设计一个简单的抢答器,使用外部中断扩展技术,实现了抢答的时效性。参考视频课参考本人哔哩哔哩,如下:

51单片机proteus抢答器仿真_哔哩哔哩_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值