51单片机综合实训案例

本文详细介绍了如何通过51单片机进行系统初始化,包括锁存器选择和变量定义,以及如何使用定时器T0和T1、UART串口配置。还展示了如何控制数码管动态显示时间和通过键盘和串口控制LED灯,涉及逻辑门操作和数据发送。
摘要由CSDN通过智能技术生成

在前面内容,我们已经学习了大部分的51单片机基础知识,但是,我们缺乏对这块板子的综合引用,现在,让我们一起来做一题综合题,比较彻底的使用51单片机中的各个功能

上面就是题目的要求了,根据题目要求,我将解答简单分为这几部分:

1:初始化系统,写好锁存器选择函数,定义好变量。

2:检查灯和数码管

3:对定时器T0和T1,还有UART串口进行配置

4:让数码管动态显示系统运行时间

5:通过键盘和串口对LED灯进行控制

6:主函数

接下来,我也将以这个顺序来讲解我对这题的体悟

1:初始化系统,写好锁存器选择函数,定义好变量。

这一步比较简单,主要是将后面需要用到的一些变量先定义好,要注意的是,我们在写寄存器选择时,注意要多写一个不选择任何锁存器的选项,在后续代码中,通过这个选择,可以帮我们避开一些错误。

#include "reg52.h"

sfr AUXR = 0x8e;
sbit S5 = P3^2;
sbit S4 = P3^3;

unsigned char code SMG[18]={0xc0,0xf9,
	  0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
    0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};

unsigned int count;
unsigned char stat_LED = 0xff;
unsigned char s;
unsigned char m;
unsigned char h;	
unsigned char command;		

//选择锁存器和初始化系统------------------------
void SelectHC573(unsigned char channel)
{
	switch(channel)
	{
		case 4:
			P2 = (P2 & 0x1f) | 0x80;
			break;
		case 5:
			P2 = (P2 & 0x1f) | 0xa0;
			break;
		case 6:
			P2 = (P2 & 0x1f) | 0xc0;
			break;
		case 7:
			P2 = (P2 & 0x1f) | 0xe0;
			break;
		case 0:
			P2 = (P2 & 0x1f) | 0x00;
			break;		
	}
}

void InitSystem()
{
	SelectHC573(5);
	P0 = 0x00;
	SelectHC573(4);
	P0 = 0xff;
	SelectHC573(0);
}

2:检查灯和数码管

在检查灯和数码管时,我们要注意的是题目要求的一位一位亮起来,也就是到最后是所有的灯(数码管)都亮起来的,所以在这里,我们需要使用左移符号了,通过循环和左移符,我们可以实现让灯(数码管)从1111 1110,到1111 1100然后再一位一位变成0000 0000,每一次的变化中间的时间我们可以用一个delay函数来决定。而且,在这里,我们需要先将灯的状态赋值给stat_LED然后再给P0,不然的话我们可能会出现P0混乱的现象,因为P0是要用在多个地方的。当我们要将灯(数码管)灭掉的时候,我们对他取反就可以了。

void Delay(unsigned int t)
{
	while(t--);
}


void CheckLED()
{
	unsigned char i = 0;
	SelectHC573(4);
	for (i = 1;i <= 8;i++)
	{
		stat_LED = 0xfe <<i;
		P0 = stat_LED;
		Delay(60000);
	}
	
	for (i = 1;i <= 8;i++)
	{
		stat_LED = ~(0xfe <<i);
		P0 = stat_LED;
		Delay(60000);
	}
	SelectHC573(0);
}

void CheckSMG()
{
	unsigned char i = 0;
	for (i = 1;i <= 8;i++)
	{
		SelectHC573(7);
		P0 = 0x00;
		SelectHC573(6);
		P0 = ~(0xfe << i);
		Delay(60000);
	}
	
	for (i = 1;i <= 8;i++)
	{
		SelectHC573(7);
		P0 = 0x00;
		SelectHC573(6);
		P0 = 0xfe << i;
		Delay(60000);		
	}
	SelectHC573(0);
}

3:对定时器T0和T1,还有UART串口进行配置

要实现后面的种种功能,定时器是必不可少的。关于定时器的初始化和定数服务函数的书写,是比较基础的部分,我们直接给出代码吧。注意,在定时服务函数中,我们需要对定时初值进行重组。

void InitT0()
{
	TMOD = 0x21;
	TH0 = (65535 - 50000) / 256;
	TL0 = (65535 - 50000) % 256;
	
	EA = 1;
	TR0 = 1;
	ET0 = 1;
}

void ServeT0()		interrupt 1
{
	TH0 = (65535 - 50000) / 256;
	TL0 = (65535 - 50000) % 256;
	count++;
	if(count == 20)
	{
		s++;
		count = 0;
	}
	
	if(s == 60)
	{
		m++;
		s = 0;
	}
	
	if(m == 60)
	{
		h++;
		m = 0;
	}
}

void InitUart()
{
	TMOD = 0x21;
	TH1 = 0xfd;
	TL1 = 0xfd;
	
	AUXR = 0x00;
	SCON = 0x50;
	
	EA = 1;
	ES = 1;
	TR1 = 1;
}	

void ServeUart() interrupt 4
{
	TH1 = 0xfd;
	TL1 = 0xfd;
	if(RI == 1)
	{
		RI = 0;
		command = SBUF;
	}	
}

void Sending(unsigned char dat)
{
	SBUF = dat;
	while(TI == 0);
	TI = 0;
}

4:让数码管动态显示系统运行时间

接下来,是如何让数码管显示系统的运行时间,在上面,我们已经让系统的时间通过定时器来计算,而我们要动态显示数码管,我们需要运用前面的知识,利用余晖效应我们可以实现数码管的动态显示。

void ShowSMGOnly(unsigned char value,unsigned char location)
{
	P0 = 0xff;
	SelectHC573(7);
	P0 = value;
	SelectHC573(6);
	P0 = 0x01 << location;
	SelectHC573(0);
}

void DisplayTime()
{
	ShowSMGOnly(SMG[s%10],7);
	Delay(500);
	ShowSMGOnly(SMG[s/10],6);
	Delay(500);
	ShowSMGOnly(SMG[16],5);
	Delay(500);	
	
	ShowSMGOnly(SMG[m%10],4);
	Delay(500);
	ShowSMGOnly(SMG[m/10],3);
	Delay(500);
	ShowSMGOnly(SMG[16],2);
	Delay(500);
	
	ShowSMGOnly(SMG[h%10],1);
	Delay(500);
	ShowSMGOnly(SMG[h%10],0);
	Delay(500);	
}

5:通过键盘和串口对LED灯进行控制

这是这一题里面较难的部分,他需要用到逻辑门的运算规则。首先,我们先来写键盘控制L7,L8的代码。为了消除抖动,我们写一个延时函数,但是这个延时函数里的while(t--)不能像之前那样,我们需要在函数里面加入时间显示的函数,以保证在检查键盘的时候,数码管依旧在显示时间不会暂停或停止。然后后面的松手检测也是一样。在这个函数中,我们对stat_LED利用逻辑门直接定义,做到只改变一位而其他位不发生改变。我们就以   stat_LED = (stat_LED & 0xbf) | (~stat_LED & 0x40);    为例子进行解释:       

首先,(stat_LED & 0xbf),0xbf也就是1011 1111,1与上任何数就是任何数,0与上任何数都是0,这就保证这个运算的结果第7位是0而其他位置保持不变,但这也仅仅是开灯而已,我们还需要关灯。后半部分(~stat_LED & 0x40)0x40也就是0100 0000,stat_LED本来第7位是0的时候,取反以后第7位是1,运算以后第7位还是1,当stat_LED本来第7位时1的时候,取反以后第7位时0,运算以后第7位是0,接着最后运算时,第7位就相当于取反,而其他位不变,非常巧妙!另外一个也是相同。

而通过串口控制L1到L4,首先我们先判断传输过来的不是空信号,防止无意义的无限发送数据过来,接着我们将锁存器调为4,然后利用switch语句来判断传输过来的信号高四位是a还是b,通过逻辑门保留高四位,将第四位全部变成0。接着,当传输过来的信号高四位是a时,我们先保留灯原来高四位的状态,由于我们的命令是1开灯0关灯,和电路板刚好相反,所以我们先对命令进行取反,接着利用逻辑门保存低四位,最后综合运算以后,我们就可以做到不影响高四位的情况下,对低四位进行改变,然后来控制灯。接着我们将数值赋给P0,关闭所有锁存器,将命令赋值为0x00,防止无限的发送数据过来。

而下面的数据发送,我们需要用到上面的Sending函数。我需要发送时分秒,高四位显示十位数,低四位显示个位数,所以我们需要用到移位符。将十位数左移4位移到高四位,接着或上个位数,我们就能接收到系统时间了。最后,我们还需要将命令改为0x00,防止无限发送数据。

void DelaySMG(unsigned int t)
{
	while(t--)
	{
		DisplayTime();
	}
}

void ScanKey()
{
	if(S5 == 0)
	{
		DelaySMG(100);
		if(S5 == 0)
		{
			while(S5 == 0)
			{
			DisplayTime();
			}
			SelectHC573(4);
			stat_LED = (stat_LED & 0xbf) | (~stat_LED & 0x40);
			P0 = stat_LED;
			SelectHC573(0);
		}
	}
	
	if(S4 == 0)
	{
		DelaySMG(100);
		if(S4 == 0)
		{
			while(S4 == 0)
			{
				DisplayTime();
			}
			SelectHC573(4);
			stat_LED = (stat_LED & 0x7f) | (~stat_LED & 0x80);
			P0 = stat_LED;
			SelectHC573(0);
		}
	}
}

void ControlLED()
{
	if(command != 0x00)
	{
		SelectHC573(4);
		switch(command & 0xf0)
		{
			case 0xa0:
				stat_LED = (stat_LED | 0x0f) & (~command | 0xf0);
				P0 = stat_LED;
				SelectHC573(0);
				command = 0x00;			
				break;
			case 0xb0:
				Sending(h/10 <<4 | h%10);
				Sending(m/10 <<4 | m%10);
				Sending(s/10 <<4 | s%10);
				SelectHC573(0);
				command = 0x00;
				break;
		}
	}
}

6:主函数

最后就是主函数啦,主函数没什么可以说的,直接看代码吧

void main()
{
	InitSystem();
	CheckLED();
	CheckSMG();
	InitT0();
	InitUart();

	while(1)
	{
		DisplayTime();
		ScanKey();
		ControlLED();
	}	
	
}

最后的最后的全部代码,这一次,我终于大概的了解了逻辑门在51中的应用啦!

#include "reg52.h"

sfr AUXR = 0x8e;
sbit S5 = P3^2;
sbit S4 = P3^3;

unsigned char code SMG[18]={0xc0,0xf9,
	  0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
    0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};

unsigned int count;
unsigned char stat_LED = 0xff;
unsigned char s;
unsigned char m;
unsigned char h;	
unsigned char command;		

//选择锁存器和初始化系统------------------------
void SelectHC573(unsigned char channel)
{
	switch(channel)
	{
		case 4:
			P2 = (P2 & 0x1f) | 0x80;
			break;
		case 5:
			P2 = (P2 & 0x1f) | 0xa0;
			break;
		case 6:
			P2 = (P2 & 0x1f) | 0xc0;
			break;
		case 7:
			P2 = (P2 & 0x1f) | 0xe0;
			break;
		case 0:
			P2 = (P2 & 0x1f) | 0x00;
			break;		
	}
}

void InitSystem()
{
	SelectHC573(5);
	P0 = 0x00;
	SelectHC573(4);
	P0 = 0xff;
	SelectHC573(0);
}

//检查灯和数码管----------------------------------
void Delay(unsigned int t)
{
	while(t--);
}


void CheckLED()
{
	unsigned char i = 0;
	SelectHC573(4);
	for (i = 1;i <= 8;i++)
	{
		stat_LED = 0xfe <<i;
		P0 = stat_LED;
		Delay(60000);
	}
	
	for (i = 1;i <= 8;i++)
	{
		stat_LED = ~(0xfe <<i);
		P0 = stat_LED;
		Delay(60000);
	}
	SelectHC573(0);
}

void CheckSMG()
{
	unsigned char i = 0;
	for (i = 1;i <= 8;i++)
	{
		SelectHC573(7);
		P0 = 0x00;
		SelectHC573(6);
		P0 = ~(0xfe << i);
		Delay(60000);
	}
	
	for (i = 1;i <= 8;i++)
	{
		SelectHC573(7);
		P0 = 0x00;
		SelectHC573(6);
		P0 = 0xfe << i;
		Delay(60000);		
	}
	SelectHC573(0);
}
//定时器与串口配置------------------------------
void InitT0()
{
	TMOD = 0x21;
	TH0 = (65535 - 50000) / 256;
	TL0 = (65535 - 50000) % 256;
	
	EA = 1;
	TR0 = 1;
	ET0 = 1;
}

void ServeT0()		interrupt 1
{
	TH0 = (65535 - 50000) / 256;
	TL0 = (65535 - 50000) % 256;
	count++;
	if(count == 20)
	{
		s++;
		count = 0;
	}
	
	if(s == 60)
	{
		m++;
		s = 0;
	}
	
	if(m == 60)
	{
		h++;
		m = 0;
	}
}

void InitUart()
{
	TMOD = 0x21;
	TH1 = 0xfd;
	TL1 = 0xfd;
	
	AUXR = 0x00;
	SCON = 0x50;
	
	EA = 1;
	ES = 1;
	TR1 = 1;
}	

void ServeUart() interrupt 4
{
	TH1 = 0xfd;
	TL1 = 0xfd;
	if(RI == 1)
	{
		RI = 0;
		command = SBUF;
	}	
}

void Sending(unsigned char dat)
{
	SBUF = dat;
	while(TI == 0);
	TI = 0;
}

//数码管运行时间-------------

void ShowSMGOnly(unsigned char value,unsigned char location)
{
	P0 = 0xff;
	SelectHC573(7);
	P0 = value;
	SelectHC573(6);
	P0 = 0x01 << location;
	SelectHC573(0);
}

void DisplayTime()
{
	ShowSMGOnly(SMG[s%10],7);
	Delay(500);
	ShowSMGOnly(SMG[s/10],6);
	Delay(500);
	ShowSMGOnly(SMG[16],5);
	Delay(500);	
	
	ShowSMGOnly(SMG[m%10],4);
	Delay(500);
	ShowSMGOnly(SMG[m/10],3);
	Delay(500);
	ShowSMGOnly(SMG[16],2);
	Delay(500);
	
	ShowSMGOnly(SMG[h%10],1);
	Delay(500);
	ShowSMGOnly(SMG[h%10],0);
	Delay(500);	
}

//控制LED-----------
void DelaySMG(unsigned int t)
{
	while(t--)
	{
		DisplayTime();
	}
}

void ScanKey()
{
	if(S5 == 0)
	{
		DelaySMG(100);
		if(S5 == 0)
		{
			while(S5 == 0)
			{
			DisplayTime();
			}
			SelectHC573(4);
			stat_LED = (stat_LED & 0xbf) | (~stat_LED & 0x40);
			P0 = stat_LED;
			SelectHC573(0);
		}
	}
	
	if(S4 == 0)
	{
		DelaySMG(100);
		if(S4 == 0)
		{
			while(S4 == 0)
			{
				DisplayTime();
			}
			SelectHC573(4);
			stat_LED = (stat_LED & 0x7f) | (~stat_LED & 0x80);
			P0 = stat_LED;
			SelectHC573(0);
		}
	}
}

void ControlLED()
{
	if(command != 0x00)
	{
		SelectHC573(4);
		switch(command & 0xf0)
		{
			case 0xa0:
				stat_LED = (stat_LED | 0x0f) & (~command | 0xf0);
				P0 = stat_LED;
				SelectHC573(0);
				command = 0x00;			
				break;
			case 0xb0:
				Sending(h/10 <<4 | h%10);
				Sending(m/10 <<4 | m%10);
				Sending(s/10 <<4 | s%10);
				SelectHC573(0);
				command = 0x00;
				break;
		}
	}
}
//主函数-------------
void main()
{
	InitSystem();
	CheckLED();
	CheckSMG();
	InitT0();
	InitUart();

	while(1)
	{
		DisplayTime();
		ScanKey();
		ControlLED();
	}	
	
}

  • 34
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值