基于51单片机的液晶LCD1602的抢答器

LCD1602

 

 液晶判忙

        LCD 1602的响应速度相对于单片机的速度来说是偏慢的。
        举个简单的例子,把一桶油通过漏斗向一个瓶子里倒,倒油的速度,即流量必须维持在一定范围之内,倒得太快油会从漏斗顶部溢出来,这样就浪费掉了。我们通过眼睛可以判断并使油面保持在顶面以下,以漏斗的额定流量来倒油,这样效率最高。
        而对于单片机来说,1602好比那个瓶子漏斗,写入1602中要显示的数据好比油,如果以单片机的高运行速度向1602写数据就很可能造成上面所说的溢出,比如连续写入abc,结果只显示出了a,这是因为1602的显示芯片每次都要花时间来处理输入的ascii码数据,并把它显示出来。而我们却不容易主动地去控制写入数据的速度,所以1602使用忙信号就有必要了,每次单片机只有检测到忙信号为0,即不忙时,才向1602发数据。比如要显示abc,则这样操作,写a---判忙---写b---判忙---写c---判忙。这样就不会出错了。

/*等待液晶准备*/
void Lcdready()
{
	unsigned char sta;
	P0 = 0xFF;  //P0在使用时要规定置1
	RS = 0;     //数据/指令选择位 1为数据,0为指令
	RW = 1;     //读/写位 1为读,0为写
	
	do
	{
		EN = 1;  //使能位
		sta = P0;//读取状态字,即把P0口的数据赋值给sta
		EN = 0;

	}while(sta & 0x80);//当sta最高位为0时则跳出循环;若为1时则继续循环,相当于单片机停止让液晶先工作
}

液晶初始化,写入指令,数据

 

/* 初始化 1602 液晶 */
void init1602()
{
	WriteCmd(0x38);
	WriteCmd(0x0C);
	WriteCmd(0x06);
	WriteCmd(0x01);
}

/* 向 LCD1602 液晶写入命令,cmd-待写入命令值 */
void WriteCmd(unsigned char cmd)
{
	Lcdready();
	RS = 0;
	RW = 0;
	P0 = cmd;
	EN = 1;
	EN = 0;
}
/* 向 LCD1602 液晶写入数据,dat-待写入数据 */
void WriteData(unsigned char dat)
{
	Lcdready();
	RS = 1;
	RW = 0;
	P0 = dat;
	EN = 1;
	EN = 0;
}

液晶显示行,列的位置

/* 设置显示 RAM 起始地址(x,y)-对应屏幕上的字符坐标 */
void Lcdaddr(unsigned char x,unsigned char y)
{
	unsigned char m;
	if(y==0)
		m = 0x00+x;	//第一行字符地址从 0x00 起始
	else 
		m = 0x40+x;	//第二行字符地址从 0x40 起始
	WriteCmd(m | 0x80);//设置 RAM 地址
} 

液晶数据转换成字符串,以及显示

/* 整型数转换为字符串,str-字符串指针,dat-待转换数,返回值-字符串长度 */
unsigned char IntToString(unsigned char *str, int dat)
{
    unsigned char i = 0;
    unsigned char len = 0;
    unsigned char buf[6];
    
    if (dat < 0)  //如果为负数,首先取绝对值,并在指针上添加负号
    {
        dat = -dat;
        *str++ = '-';
        len++;
    }
    do 			   //先转换为低位在前的十进制数组
	{         
        buf[i++] = dat % 10;  //取最低位
        dat /= 10;     
    } while (dat > 0);
    len += i;     //i最后的值就是有效字符的个数
    while (i-- > 0)   //将数组值转换为ASCII码反向拷贝到接收指针上
    {
        *str++ = buf[i] + '0';  //加0其实是加ASCII码中的0x30
    }
    *str = '\0';  //添加字符串结束符
     
    return len;   //返回字符串长度
}

/* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */
void Lcdshow(unsigned char x,unsigned char y,unsigned char *str)
{
	Lcdaddr(x,y);
	while(*str != '\0')      //表示一直循环到字符串结尾
	{
		WriteData(*str++);   //表示从高到低依次写入str数组的值
	}
}

注:ASCII码中,数字0地址为0x30,1为0x31,2为0x32......因此,在指针*str=buf[i]中,为了表示数字0,1,2.......要加上0x30的地址('0')。

控制键按钮

选手按钮:1号,2号,3号,4号

主持人按钮:开始抢答,抢答成功,作答正确,作答错误,查询积分

/*控制键扫描函数,开始结束*/
void Keycontrol()   
{
		if(K5==0)	//开始抢答键
    {
				
        if(K5==0)
        {
					while(!K5);//摁下按键的一瞬间会一直循环,直到松手才完成这一个过程
					WriteCmd(0x01);
					sec=20;    //重新计时
					TR0=~TR0;  //TRO=1,定时器开始计时
					s_flag = 1;//抢答开始键,直到抢答成功键摁下才会置0
					e_flag=0;  //抢答结束键,直到抢答成功键摁下才会置1
					flag=1;    //抢答标志,直到有人抢答才会置0
				}
    }
		if(K6==0) //抢答成功键
		{
			
			if(K6==0 && flag==0)  //有人抢答时flag会置0
			{
				while(!K6);
				e_flag =1;//表示结束
				LED1=1;   //熄灭LED灯
				LED2=1;
				LED3=1;
				LED4=1;
				Buzz = 0; //蜂鸣器响应
				Lcdshow(9,0,"Successed");
			}	
		}
		if(K9==0)	 //查分键
		{
			unsigned char ch;
			
			if(K9==0 && e_flag==1) //按键按下且此时已结束
			{
				while(!K9);
				WriteCmd(0x01);      //清屏
				
				Lcdshow(0,0,"P1:");  //当lcdshow显示"P1:"时,光标会在":"号后面
				ch=(char) (score[1]+'0');
				WriteData(ch);       //此时写入数据ch即可继续在lcd上显示
						
				Lcdshow(10,0,"P2:");
				ch=(char) (score[2]+'0');
				WriteData(ch);
				
				Lcdshow(0,1,"P3:");
				ch=(char) (score[3]+'0');
				WriteData(ch);
				
				Lcdshow(10,1,"P4:");
				ch=(char) (score[4]+'0');
				WriteData(ch);
			}	
		}
}
//四路抢答器按键扫描
void KeyScan()               
{
	if( K1==0 && flag != 0 && s_flag==1)  //有按键摁下,抢答标志键为1及存在,抢答结束键为1及还没确认
    {
    	
        if(K1==0)
        {
            while(!K1);
            num=1;           //LCD显示1号"1"
            TR0=0;           //关闭定时器0,计时停止       
            TR1=1;				   //打开定时器1,蜂鸣器响应 
						LED1=0;					 //按下抢答器,LED亮  
            flag=0;          //关闭开始键标志位,使再按其他三个键不会响应                                   
        }        
    }
	if( K2==0 && flag != 0 && s_flag==1)	  
    {
    
        if(K2==0)
        {
            while(!K1);
            num=2;          
            TR0=0;                    
            TR1=1;  
						LED2=0;					
            flag=0;                                        
        }        
    }
	if( K3==0 && flag != 0 && s_flag==1)	  
    {
    
        if(K3==0)
        {
            while(!K1);
            num=3;          
            TR0=0;                    
            TR1=1;  
						LED3=0;					
            flag=0;                                        
        }        
    }
	if( K4==0 && flag != 0 && s_flag==1)	  
    {
    	
        if(K4==0)
        {
            while(!K1);
            num=4;          
            TR0=0;                    
            TR1=1; 
						LED4=0;					
            flag=0;                                        
        }        
    }
        
}
void Judge()
{
	if( K7==0 )//抢答正确
    {
  
        if(K7==0)
        {
            while(!K7);         
            WriteCmd(0x01); //清屏
						Lcdshow(0, 0, "Your answer is:");  
						Lcdshow(0, 1, "Right"); 
						score[num]++;   //为选手加分                                    
        }         
    }
	if( K8==0 )	//抢答错误
    {
    	
        if(K8==0)
        {
            while(!K8);         
            WriteCmd(0x01);
						Lcdshow(0, 0, "Your answer is:");  
						Lcdshow(0, 1, "Worry");                                     
        }        
    }	
}

主函数

void main()
{
	unsigned char str1[12];
	unsigned char str2[12];
	unsigned char len,len1;
	EA = 1;      //开启总中断
	TMOD = 0x11; //打开定时器T0,T1
	
	ET0 = 1;     //开启定时器0中断
	TR0 = 0;     //定时器0关闭计时
	TH0 = 0xFC;  //定时1ms
	TL0 = 0x67;
	
	ET1 = 1;     //开启定时器1中断
	TR1 = 0     ;//定时器1关闭计时
	TH1 = 0xFC;  //定时1ms
	TL1 = 0x67;
	
	TCON=0x05;   //外部中断0设置为边沿触发
	EX0=1;       //开外部中断0
	
	//初始化
	init1602();
	Lcdshow(0,0,str1);
	LED1=1;
	LED2=1;
	LED3=1;
	LED4=1;
	while(1)
	{
		Keycontrol();//扫描主持人按键
		KeyScan();//扫描选手按键
		len = IntToString(str1,sec);//倒计时转化为字符串
		len1= IntToString(str2,num);//选手代号转化为字符串
		if(e_flag==0)//抢答未结束
		{
			Lcdshow(0,0,"Time:");
			Lcdshow(6,0,str1);//显示倒计时
		}
		if(num!=0&&e_flag==0&&flag==0)//没有结束且有人按下按键 有人抢答时flag会置0
		{
			Lcdshow(0, 1, "answerer:");	
			Lcdshow(9, 1, str2);//显示抢答的选手
		}
		if(e_flag==1)//抢答结束进行评分
		{
			Judge();
		}			
	}
}


unsigned int cnt1= 0;   //记录中断次数
void Time1 () interrupt 3
{
	TH1=(65536-2000)/256;
    TL1=(65536-2000)%256;
    Buzz = ~Buzz;
    cnt1++;
    if(cnt1==1000)      //定时1s
    {
      cnt1=0;
      TR1=0;
    }
}

unsigned int cnt0 = 0;  //记录中断次数
void Time0 () interrupt 1
{
	TH0 = 0xFC;
	TL0 = 0x67;
	cnt0++;
	if(cnt0>=1000)      //定时1s
	{
		cnt0 = 0;
		sec--;
		if(sec==0)
		TR0 = 0;
	}
}

proteus仿真

注:蜂鸣器的驱动电流较大,IO口无法直接驱动,所以需要经过三极管放大电流驱动。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值