蓝桥杯基础技能训练

                                   51单片机系统浓缩图

1. HC138译码器

· 用3个输入引脚,实现8个输出引脚,而且这个八个输出引脚中只要一个低电平,所以我们只需要记住真值表就行

 

#include "reg52.h"  



sbit HC138_A = P2^5;        

sbit HC138_B = P2^6;        

sbit HC138_C = P2^7;   


void Init74HC138(unsigned char n)

{

        switch(n)

        {

                case 4:  //LED

                        HC138_A = 0;

                        HC138_B = 0;

                        HC138_C = 1;

                        break;

                case 5:  //蜂鸣器与译码器

                        HC138_A = 1;

                        HC138_B = 0;

                        HC138_C = 1;

                        break;

                case 6:  //数码管位置

                        HC138_A = 0;

                        HC138_B = 1;

                        HC138_C = 1;

                        break;

                case 7:  //数码管内容

                        HC138_A = 1;

                        HC138_B = 1;

                        HC138_C = 1;

                        break;

                case 8:  //关闭所有设备

                        HC138_A = 0;

                        HC138_B = 0;

                        HC138_C = 0;

                        break;

        }

}

由高人指点,这种方法并不好用,因为在译码器切换的时候,他是加上去的,也就是会打开到别的寄存器,所以我们直接使用下面的方法。

2. HC573

 573锁存器有20个引脚,D1~D8是数据输入端,Q1~Q8是数据输出端,LE为锁存控制端。当锁存使能端LE为高时,573的锁存对于数据是透明的(也就是说输出同步)。当锁存使能变低时,符合建立时间和保持时间的数据会被锁存。使用其可以替换HC138,两个功能相同

#include "reg52.h"
void SelectHC573(unsigned 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 InitHC138 (unsigned char n)  
{
	 switch(n)
	{
		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;
	}
}

 3. 控制LED与继电器

两者都是直接给对应引脚给上电平即可

// 任务:
/* 先让奇数的灯闪,再让偶数的灯闪,然后所有的灯闪3下,最后依次点亮所有的灯,然后再依次熄灭,然后循环 */

void LEDRunning()

{
        char i = 0;
        P0 = 0xaa;
        Delay(60000);
        P0 = 0x55;
        Delay(60000);    

        for(i = 0; i < 3; i++)

        {

                P0 = 0x00; //全灭
                Delay(60000);
                P0 = 0xff; //全亮
                Delay(60000);
        }

        for(i = 0; i < 8; i++)

        {
                P0 <<= 1; //按顺序依次闪过
                Delay(60000);
        }

        for(i = 0; i < 8; i++)

        {
                P0 <<= 1;
                P0 |= 1;  //熄灭
                Delay(60000);
        }
}



main()

{

        Init74HC138(4);   //打开LED的138                            
        while(1)
        {
                LEDRunning();                                
        }

}

蜂鸣器与继电器

#include "reg52.h"

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

void InitHC138(unsigned char n)
{
	switch(n)
	{
		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;
	}
}

void OutPutP0(unsigned char channel, unsigned char dat)
{
	InitHC138(channel);
	P0 = dat;
}

void LEDRunning()
{
	unsigned char i;
	
	for(i = 0; i < 3; i++)
	{
		OutPutP0(4,0x00);
		Delay(60000);
		Delay(60000);
		OutPutP0(4,0xff);
		Delay(60000);
		Delay(60000);
	}
	
	for(i = 1; i <= 8; i++)
	{
		OutPutP0(4,(0xff << i));
		Delay(60000);
	}
	
	OutPutP0(5,0x10);
	Delay(60000);
	Delay(60000);
	OutPutP0(5,0x00);
	
	InitHC138(4);
	for(i = 1; i <= 8; i++)
	{
		OutPutP0(4,~(0xff << i));
		Delay(60000);
	}
	
	OutPutP0(5,0x40);
	Delay(60000);
	Delay(60000);
	OutPutP0(5,0x00);
}

void InitSystem()
{
	OutPutP0(5,0x00);
}

void main()
{
	InitSystem();
	while(1)
	{
		LEDRunning();
	}
}

4. 数码管

 

 其中a、b、c、d、e、f、g、dp引脚分别对应8个段码,该8个引脚通过74HC573锁存器与单片机的P0端口相连。另外有com1~com4四个公共控制脚,该应用为高电平则使能对应位的数码管。两个F3461BH一共有8个com控制引脚,也是通过74HC573锁存器与单片机的P0端口相连的。因此,在操控数码管显示的过程中也离不开74HC138译码器和74HC573锁存器。

任务:

在 8 位数码管中,左边 4 位数码管显示 年份“2018 ”,接着 2 位是分隔符“--”,靠右的2 位数码管显示月份。从 1 月份开始,每隔一段时间加 1 个月,到 12 月之后又从 1 月开始递增, 如此循环往复。
#include "reg52.h"

unsigned char code SMG_duanma[18]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
     0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};
unsigned char yue=1;
void Delay(unsigned int t)
{
	while(t--);
}


void InitHC138 (unsigned char n)  //ͨµÀÑ¡Ôñ
{
	switch(n)
	{
		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;
	}
}

void ShowSMG_Bit(unsigned char dat,unsigned int pos)
{
	InitHC138(6);   //ÊýÂë¹ÜµÄλÖÃ
	P0=0X01<<pos;
	InitHC138(7);   //ÊýÂë¹ÜµÄÄÚÈÝ
	P0=dat;
}

void Display_SMG()
{
	
	ShowSMG_Bit(SMG_duanma[2],0);
	Delay(500);
	ShowSMG_Bit(SMG_duanma[0],1);
  Delay(500);
	ShowSMG_Bit(SMG_duanma[1],2);
	Delay(500);
	ShowSMG_Bit(SMG_duanma[8],3);
  Delay(500);
	ShowSMG_Bit(SMG_duanma[16],4);
	Delay(500);
	ShowSMG_Bit(SMG_duanma[16],5);
  Delay(500);
	ShowSMG_Bit(SMG_duanma[yue/10],6);
	Delay(500);
	ShowSMG_Bit(SMG_duanma[yue%10],7);
  Delay(500);

}

void Delay2(unsigned int t)
{
	while(t--)
	{
		Display_SMG();
	}
}

void InitSystem()
{
	InitHC138(5);  //¹Ø±Õ¼ÌµçÆ÷
  P0=0x00;
	InitHC138(4);  //´ò¿ªµÆ
	P0=0xff;
 	P2=0x00;  //¹Ø±ÕHC138
}

void main()
{
	 InitSystem();
	while(1)
	{
	  Display_SMG();
		yue++;
		if(yue>12)
			yue=1;
	 Delay2(100);
	}
}

5. 独立按键

一般情况下,独立按键有两个引脚,其中一个通过上拉电阻接到单片机的I/O端口,另外一端接地。也就是说,平时按键没有动作的时候,输出的是高电平,如果有按下动作发生,则输出的是低电平。那么,我们在程序设计的时候,只要扫描跟按键引脚相连的I/O端口,如果发现有低电平产生,则判定该按键处于按下状态。有些时候,电路或者外围有电磁干扰,也会使单片机的I/O端口产生低电平,这种干扰信号会让单片机误认为是按键动作。所以,在扫描按键的时候应该做去抖动处理,把干扰信号过滤掉,从而获得准确的按键状态信号。

任务:
1、将 CT107D 上 J5 处跳帽接到 2~3 引脚,使 S4、S5、S6 和 S7 成为 4 个独立按键。
2、系统上电后,关闭蜂鸣器,关闭继电器,关闭 8 个 LED 灯。
3、循环扫描按键状态,在确认按键按下时,进行去抖动处理。当 S7 按键按下时,点亮
L1 指示灯,松开后熄灭;当 S6 按键按下时,点亮 L2 指示灯,松开后熄灭;当 S5 按键按下
时,点亮 L3 指示灯,松开后熄灭;当 S4 按键按下时,点亮 L4 指示灯,松开后熄灭
#include "reg52.h"
// 按键定义
sbit S7=P3^0;
sbit S6=P3^1;
sbit S5=P3^2;
sbit S4=P3^3;


//灯光定义
sbit L1=P0^0;
sbit L2=P0^1;
sbit L3=P0^2;
sbit L4=P0^3;
sbit L5=P0^4;
sbit L6=P0^5;

void SelectHC138(unsigned char n)
{
	switch(n)
	{
		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;
	}
}
void Delay(unsigned int t)
{
  while(t--);
}
unsigned char flag=0;
void KeyWorking()
{
	if(S7==0)
	{
		Delay(100);
		if(S7==0)
		{
			if(flag==0)
			{
				L1=0;
				flag=1;
			}
			else if(flag==1)
			{
				L1=1;
				flag=0;
			}
			while(S7==0);
		}
	   
	}
	if(S6==0)
	{
		Delay(100);
		if(S6==0)
		{
			if(flag==0)
			{
			  L2=0;
				flag=2;
			}
			else if(flag==2)
			{
				L2=1;
				flag=0;
			}
			while(S6==0);
		}
	   
	}
	if(S5==0)
	{
		Delay(100);
		if(S5==0)
		{
			if(flag==1)
			{
				L3=0;
				while(S5==0);
				L3=1;
			}
			else if(flag==2)
			{
				L5=0;
				while(S5==0);
				L5=1;
			}
		}
	   
	}
	if(S4==0)
	{
		Delay(100);
		if(S4==0)
		{
			if(flag==1)
			{
			 L4=0;
				while(S4==0);
			 L4=1;
			}
		else if(flag==2)
			{
			 L6=0;
			 while(S4==0);
			 L6=1;
			}
			
		}

	}


}
void InitSystem()
{
	SelectHC138(5);
	P0=0x00;
	SelectHC138(4);
	P0=0xff;
}
void main()
{
	InitSystem();
	while(1)
	{
	 KeyWorking();
	
	
	}
	
}

6 .矩阵键盘的使用

与独立按键不同的是,按键的两个引脚都分别连接的单片机的I/O端口,一个作为行信号,另外一个作为列信号。

 对与矩阵键盘,我们只能逐行扫描,然后读取列的状态信号。如果R3行输出低电平,那么黄色按键如果有按下动作的话,那读取C2列信号也应该为低电平,而该行上其他没有按下动作的按键的列信号则为高电平。因此,我们可以得到矩阵键盘的基本扫描步骤:

 <1> R1输出低电平,R2、R3、R4输出高电平,逐个读取判断列信号,如果都为高电平则R1行上没有按键按下。
 <2> R2输出低电平,R1、R3、R4输出高电平,逐个读取判断列信号。
 <3> R3输出低电平,R1、R2、R4输出高电平,发现C2列信号为低电平,那么可以判断得R3行的C2列的按键有按下动作。
 <4> R4输出低电平,R1、R3、R4输出高电平,逐个读取判断列信号。

任务:
1、将 CT107D 上 J5 处跳帽接到 1~2 引脚,使 S4 到 S19 成为 4X4 的矩阵键盘。
2、系统上电后,关闭蜂鸣器,关闭继电器,关闭 8 个 LED 灯。
3、循环扫描矩阵键盘状态,发现有按键按下,等待其松开后,在数码管的最左边 1 位
显示相应的数字。从左至右,从上到下,依次显示“ 0 ”到“ F ”。即按下 S7 ,显示“ 0 ”,
按下 S11 显示“ 1 ”,按下 S15 显示“ 2 ”,按下 S6 显示“ 4 ”...依次类推。
#include "reg52.h"

sfr P4=0xc0;
sbit S1=P3^0;
sbit S2=P3^1;
sbit S3=P3^2;
sbit S4=P3^3;

sbit C1=P4^4;
sbit C2=P4^2;
sbit C3=P3^5;
sbit C4=P3^4;


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


void Delay(unsigned char t)
{
  while(t--);
}
void SelectHC138(unsigned char n)
{
	switch(n)
	{
		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;
	}
}
unsigned char Key=0;

void SMG_Bit(unsigned char dat)
{
	SelectHC138(6);
	P0=0x01;
	SelectHC138(7);
	P0=dat;
}
void ScanKey()
{
	//==========µÚÒ»ÐÐ
	S1=0;S2=1;S3=1;S4=1;
	if(C1==0)
	{
		Delay(100);
		if(C1==0)
			Key=0;
		while(C1==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C2==0)
	{
		Delay(100);
		if(C2==0)
			Key=1;
		while(C2==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C3==0)
	{
		Delay(100);
		if(C3==0)
			Key=2;
		while(C3==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C4==0)
	{
		Delay(100);
		if(C4==0)
			Key=3;
		while(C4==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	//==========µÚÒ¶þÐÐ
	S1=1;S2=0;S3=1;S4=1;
	if(C1==0)
	{
		Delay(100);
		if(C1==0)
			Key=4;
		while(C1==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C2==0)
	{
		Delay(100);
		if(C2==0)
			Key=5;
		while(C2==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C3==0)
	{
		Delay(100);
		if(C3==0)
			Key=6;
		while(C3==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C4==0)
	{
		Delay(100);
		if(C4==0)
			Key=7;
		while(C4==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	//==========µÚÈýÐÐ
	S1=1;S2=1;S3=0;S4=1;
	if(C1==0)
	{
		Delay(100);
		if(C1==0)
			Key=8;
		while(C1==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C2==0)
	{
		Delay(100);
		if(C2==0)
			Key=9;
		while(C2==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C3==0)
	{
		Delay(100);
		if(C3==0)
			Key=10;
		while(C3==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C4==0)
	{
		Delay(100);
		if(C4==0)
			Key=11;
		while(C4==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	//==========µÚËÄÐÐ
	S1=1;S2=1;S3=1;S4=0;
	if(C1==0)
	{
		Delay(100);
		if(C1==0)
			Key=12;
		while(C1==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C2==0)
	{
		Delay(100);
		if(C2==0)
			Key=13;
		while(C2==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C3==0)
	{
		Delay(100);
		if(C3==0)
			Key=14;
		while(C3==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
	if(C4==0)
	{
		Delay(100);
		if(C4==0)
			Key=15;
		while(C4==0)
		{
			SMG_Bit(SMG_DM[Key]);
		}
	}
}
void InitSystem()
{
	SelectHC138(5);
	P0=0x00;
	SelectHC138(4);
	P0=0xff;
}
void main()
{
	InitSystem();
	while(1)
	{
		//SMG_Bit(SMG_DM[Key]);
		ScanKey();
	}
}

7. 中断相关寄存器

一般来说,51单片机有5个中断源(忽略定时/计数器2),分2个优先级,这个5个中断源按照自然优先级从高到低依次为:
    外部中断0INT0
    定时/计数器0TF0
    外部中断1INT1
    定时/计数器1TF1    
    串口中断RI/TI

中断相关的寄存器有4个,每个寄存器都是可以位寻址的,这该编程带来了方便。    其中2个为控制寄存器:IE寄存器IP寄存器

 另外2个为中断请求标志:TCON寄存器SCON寄存器

 

 一般情况下,中断的处理函数有两个,其一为中断初始化函数,其二为中断服务函数。初始化函数就是一个普通的函数,而中断服务函数却有特殊的格式要求:
    <1> 中断函数没有返回值,也不能带参数。
    <2> 函数名后面要跟一个关键字interrupt,说明这是一个中断服务函数。
    <3> 在关键字interrupt后面要跟上中断号,说明这个中断服务函数是为那个中断服务的。 

8.定时器

 51单片机有两个定时/计数器T0和T1,为16位加法计数器,由低8位TLx和高8位THx两个寄存器组成,最大计数值为65535个计数脉冲
    该加1计数器的计数脉冲来源有2个:
    <1> 系统时钟振荡器输出的12分频
    <2> T0或T1引脚输入的外部脉冲信号
     每接收到一个计数脉冲,计数器就会加1,当计数值累计至全为1时(8位255,13位8191,16位65535),再输入一个计数脉冲,计数器便会溢出回零,并且计数器的溢出是TCON寄存器的TF0或TF1位置1,同时向内核提出中断请求。如果定时/计数器工作于定时模式,则表示间隔定时时间到,如果工作与计数模式,则表示计数值已满。
     假设单片机的外部晶振为12MHz,那么,经过12分频后输入计数器的计数脉冲为1MHz,即每个脉冲的周期为1us。因此定时器T0的16位工作模式最大的定时时间为65535us,65.5ms。如果要定时10ms的话,计数器就不能够从0开始计数了,必须给它一个计数初值。怎么计算这个初值呢?
    要定时10ms,则相当于计数10000个脉冲后计数器的值就到达65535了,那么开始计数的这个地方就是计数初值。
    65535 - 10000 = 55535 = 0xd8ef
    把这个计算得到的初值写入TH0和TL0寄存器即可:
    TH0 = 0xd8;或者 TH0 = (65535 - 10000) / 256;
    TL0 = 0xef; 或者  TL0 = (65535 - 10000) % 256;

定时/计数器相关的寄存器除了计数初值寄存器THxTLx之外,就是TMOD寄存器和TCON寄存器,务必掌握。
    <1> TMOD模式控制寄存器,不能进行位寻址,只能字节操作。

    <2> TCON中断标志寄存器

定时/计数器的程序设计中,通常有两个函数:初始化函数中断服务函数
    在初始化函数中,一般需要进行以下几个配置:
    <1> 配置工作模式,即对TMOD寄存器编程。
    <2> 计算技术初值,即对THx和TLx寄存器进行赋值。
    <3> 使能定时/计数器中断,即ET0或ET1置1。
    <4> 打开总中断,即EA =1。
    <5> 启动定时器,即TR0或TR1置1。
    在中断服务函数中,一般需要进行以下的编程:
    <1> 如果不是自动重装模式,需要对THx和TLx重新赋值。
    <2> 进行间隔定时到达的逻辑处理(越少越好)。

任务:

1、系统上电后,关闭蜂鸣器,关闭继电器,关闭 8 个 LED 灯。
2、利用定时/计数器 T0 的模式 1 实现 50ms 的间隔定时。
3、在 50ms 间隔定时的基础上,每隔 1 秒 L1 指示灯闪烁一次,即 L1 指示灯循环点亮
0.5 秒,熄灭 0.5 秒。
4、每隔 10 秒 L8 指示灯闪烁 1 次,即 L1 指示灯循环点亮 5 秒,熄灭 5 秒
#include "reg52.h"  

sbit HC138_A = P2^5;        
sbit HC138_B = P2^6;        
sbit HC138_C = P2^7;        

sbit LED1 = P0^0;        
sbit LED2 = P0^1;        

void Init74HC138(unsigned char n)
{
        switch(n)
        {
                case 4:
                        HC138_A = 0;
                        HC138_B = 0;
                        HC138_C = 1;
                        break;
                case 5:
                        HC138_A = 1;
                        HC138_B = 0;
                        HC138_C = 1;
                        break;
                case 6:
                        HC138_A = 0;
                        HC138_B = 1;
                        HC138_C = 1;
                        break;
                case 7:
                        HC138_A = 1;
                        HC138_B = 1;
                        HC138_C = 1;
                        break;
                case 8:
                        HC138_A = 0;
                        HC138_B = 0;
                        HC138_C = 0;
                        break;
        }
}
/*===============初始化定时器0==================*/
void Init_Timer0()
{
        TMOD = 0x01;                    //16位定时模式
        TH0 = (65536 - 50000) / 256;      //定时50ms
        TL0 = (65536 - 50000) % 256;
        ET0 = 1;                       //使能定时器T0中断
        EA = 1;                       //使能总中断        
        TR0 = 1;                       //启动定时器T0
}
/*============定时器0中断服务函数===============*/
unsigned char count = 0;
void SeviceTimer0() interrupt 1
{
        TH0 = (65536 - 50000) / 256;
        TL0 = (65536 - 50000) % 256;
        count++;
        if(count == 10)                    //0.5秒定时到
        {
                LED1 = ~LED1;
        }
        if(count == 20)                    //1秒定时到
        {
                LED2 = ~LED2;
                count = 0;
        }
}
/*==================主函数======================*/
main()
{
        Init74HC138(4);
        Init_Timer0();
        while(1);
}

9. PWM控制

1、系统上电后,关闭蜂鸣器,关闭继电器,关闭 8 个 LED 灯。
2、PWM 脉宽信号的频率为 100Hz。
3、L1 指示灯有 4 种亮度,分别是:完全熄灭、10%的亮度、50%的亮度和 90%的亮度。
4、按下 S7 按键,循环切换 L1 指示灯的四种亮度模式
#include "reg52.h"
#include "HC573.h"
#includd "SMG.h"

sbit L1 = P0^0;
sbit S7 = P3^0;

/*¶¨Ê±Æ÷Ïà¹Ø*/
unsigned char count = 0;

void InitTimer0()
{
	TMOD = 0x01; //ʹÓö¨Ê±Æ÷1µÄ16λģʽ
	TH0 =  (65535 - 100)/256      // ¼ÆÊý³õÖµ
	TL0 =  (65535 - 100)%256
	
	ET0 = 1;
	EA = 1;
	TR0 = 1;
}

void ServiceTimer0() interrupt 1
{
	TH0 =  (65535 - 100)/256      // ¼ÆÊý³õÖµ
	TL0 =  (65535 - 100)%256
	
	count++;
	if(count == pwm_duty)
	{
		L1 = 1;
	}
	if(count == 100)
	{
		L1 = 0;
		count = 0;
	}
}


// °´¼ü
unsigned char stat = 0;
void Scankeys()
{
	if(S7 == 0)
	{
		Delay(200)
		if(S7 == 0)
		{
			switch(stat)
			{
				case 0:
					L1 = 0;  //¿ªµÆ
				  pwm_duty = 10;
				  stat =1;
				break;
				
				case 1:
				  pwm_duty = 50;
				  stat =2;
				break;
				
				case 1:
				  pwm_duty = 90;
				  stat =3;
				break;
				
				case 3:
					L1 = 0;
				  stat = 0;
				break;	
			}
		}
	}
}

10. 串行接口

在串口通信的程序设计中,主要有串口初始化数据收发两个部分
    在初始化函数中,基本步骤如下:
    <1> 设置定时器1的工作模式,也就是对TMOD寄存器赋值。
    <2> 计算波特率参数,并赋值给TH1和TL1寄存器。
    <3> 打开定时器1。
        如果使用的是STC 12系统单片机,则要设置AUXR寄存器。
    <4> 设置SCON寄存器。
    <5> 使能串口中断ES。
    <6> 使能总中断EA。

1、初始化串口为模式 1,即 8 位 UART 模式,波特率 9600,允许接收。
2、数据发送采用查询方式,数据接收采用中断方式。
3、系统上电初始化之后,单片机向上位机发送两个字节:
0x5a 和 0xa5(串口助手以十六进制 HEX 发送和显示)。
4、串口每成功接收到一个字节后,在该字节基础上加 1,
然后通过串口发送回上位机。
5、注意 89C52 单片机和 IAP15F2K61S2 单片机串口应用的
差别,使用 9600 波特率时,晶振时钟选择 11.0592MHz。
#include "reg52.h" 

sfr AUXR=0x8e;

unsigned char tmpRecv;
void Init_Uart()
{
        TMOD=0x20; 
        TH1=0xfd;
        TL1=0xfd; 
        AUXR=0x00;
        TR1=1; 
        SCON = 0x50; 
        ES=1; 
        EA=1; 
}

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


main()
{
        Init_Uart();
        SendByte(0x5a);
        SendByte(0xa5);
        while(1);
}

一般情况下,上位机的命令可能不是一个字节,而是多个字节组成的命令帧,有的长度固定,有的长度变化;而且要求返回的数据可能也不是一个字节,可能是一个数组,也有可能是一个字符串等。在蓝桥杯的比赛中,也不可能让你只是收发一个字节而已,因此,在串口这一个单元中,必须多加一个强化环境,掌握多字节的数据帧收发应用。

1、初始化串口为模式 1,即 8 位 UART 模式,波特率 9600,允许接收。
2、数据发送采用查询方式,数据接收采用中断方式。
3、系统上电后,关闭蜂鸣器,关闭继电器,关闭 8 个 LED 灯,通过串口向上位机发送
字符串:“Welcome to XMF system!”,回车换行。
4、上位机通过串口发送单字节命令,控制单片机的 8 个 LED 灯开关,单片机响应正确
的控制命令后,完成相应的灯光操作。
5、上位机通过串口发送单字节命令,读取单片机运行信息,单片机响应正确的读取命
令后,向上位机返回指定的信息。

串口初始化函数Init_Uart()和单字节发送函数SendByte()就不需要修改,拷过来就能用

<1> 字符发送

<2>字符接收

 <3>命令解析与执行

11.DS18B20温度传感器

在蓝桥杯“单片机设计与开发”赛项中,会提供一个关于DS18B20的库文件,里面有传感器复位、写字节和读字节三个函数。所以,你不一定要把单总线的时序搞清楚,但你一定要把DS18B20的基本操作流程弄明白。

通过单线总线端口访问DS18B20的协议如下:
    步骤1: 复位初始化  
    步骤2: ROM操作指令
    步骤3: DS18B20功能指令

三个重要的指令:

    <1> CCH:跳过ROM指令,忽略64位ROM地址,直接向DS18B20发起各种温度转换指令。
    <2> 44H:温度转换指令,启动DS18B20进行温度转换,转换时间最长为500ms(典型值为200ms),结果保存在高速RAM中。
    <3> BEH:读暂存器指令,读取高速暂存存储器9个字节的内容。

读取一次温度传感器数值的操作:


    <1> 主机对DS18B20进行复位初始化。
    <2> 主机向DS18B20写0xCC命令,跳过ROM。
    <3> 主机向DS18B20写0x44命令,开始进行温度转换。   

    <4> 等待温度转换完成。
    <5> 主机对DS18B20进行复位初始化。
    <6> 主机向DS18B20写0xCC命令,跳过ROM。
    <7> 主机向DS18B20写0xBE命令,依次读取DS18B20发出的从第0一第8,共九个字节的数据。如果只想读取温度数据,那在读完第0和第1个数据后就不再理会后面DS18B20发出的数据即可,或者通过DS18B20复位,停止数据的输出。


如果你利用大赛提供的DS18B20的库文件,也就是onewire.conewire.h,进行程序设计的时候,没能正确的读出温度传感器的数值,对库文件中代码的时序进行适当的调整即可。)、

onewire.h


#include "reg52.h"

sbit DQ = P1^4;  


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


void Write_DS18B20(unsigned char dat)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		DQ = dat&0x01;
		Delay_OneWire(50);
		DQ = 1;
		dat >>= 1;
	}
	Delay_OneWire(50);
}

unsigned char Read_DS18B20(void)
{
	unsigned char i;
	unsigned char dat;
  
	for(i=0;i<8;i++)
	{
		DQ = 0;
		dat >>= 1;
		DQ = 1;
		if(DQ)
		{
			dat |= 0x80;
		}	    
		Delay_OneWire(50);
	}
	return dat;
}


bit init_ds18b20(void)
{
  	bit initflag = 0;
  	
  	DQ = 1;
  	Delay_OneWire(120);
  	DQ = 0;
  	Delay_OneWire(800);
  	DQ = 1;
  	Delay_OneWire(100); 
    initflag = DQ;     
  	Delay_OneWire(50);
  
  	return initflag;
}
温度传感器任务:
1、将 DS18B20 的底层驱动代码文件正确移植到工程中。
2、循环采样启动 DS18B20 进行温度转换。
3、将 DS18B20 的温度转换结果读出,进行换算,保留 1 位
小数 ,并显示在数码管靠右端,显示格式如图。
4、注意,在进行 DS18B20 底层驱动代码文件移植时,需确
认单总线的 时序参数是否匹配

12. DS1302时钟系统

 DS1302将地址和读写控制放到一个字节里面,形成一个控制字。

为了方便程序设计,我们把读寄存器地址、写寄存器地址和日历时钟寄存器方面用三个数组定义。

任务如下:
1、将 DS1302 的底层驱动代码文件正确移植到工程中。
2、初始化 DS1302 的默认启动参数为:
20 4 19 23 58 24 秒, 周六
3、系统上电后, DS1302 实时时钟从默认参数启动运行,并
将当前的 时、分、秒 显示在数码管上,时分秒之间用“ - ”分隔。
显示格式如图

#include <reg52.h>
#include <intrins.h>

sbit SCK=P1^7;		
sbit SDA=P2^3;		
sbit RST = P1^3;   // DS1302¸´Î»												

void Write_Ds1302(unsigned  char temp) 
{
	unsigned char i;
	for (i=0;i<8;i++)     	
	{ 
		SCK=0;
		SDA=temp&0x01;
		temp>>=1; 
		SCK=1;
	}
}   

void Write_Ds1302_Byte( unsigned char address,unsigned char dat )     
{
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1; 	_nop_();  
 	Write_Ds1302(address);	
 	Write_Ds1302(dat);		
 	RST=0; 
}

unsigned char Read_Ds1302_Byte ( unsigned char address )
{
 	unsigned char i,temp=0x00;
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1;	_nop_();
 	Write_Ds1302(address);
 	for (i=0;i<8;i++) 	
 	{		
		SCK=0;
		temp>>=1;	
 		if(SDA)
 		temp|=0x80;	
 		SCK=1;
	} 
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
	SCK=1;	_nop_();
	SDA=0;	_nop_();
	SDA=1;	_nop_();
	return (temp);			
}

DS1302时钟模块

#include "reg52.h"
#include "ds1302.h"
unsigned char Write_DS1302[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c}; //写寄存器
unsigned char Read_DS1302[7]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};  //读寄存器
unsigned char Timer[7]={0x50,0x59,0x12,0x18,0x04,0x06,0x22};        //日历寄存器

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

void SelectHC138(unsigned char n)
{
	switch(n)
	{
		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;
	}
}
//时钟初始化
void DS1302_Config()
{
	unsigned char i;
	Write_Ds1302_Byte(0x8e,0x00);
	for(i=0;i<7;i++)
	{
	Write_Ds1302_Byte(Write_DS1302[i], Timer[i]);
	}
	Write_Ds1302_Byte(0x8e,0x80);

}
void Read_DS1302_Timer()
{
	unsigned char i;
	for(i=0;i<7;i++)
	{
	Timer[i]=Read_Ds1302_Byte(Read_DS1302[i]);
	}
	if(Timer[2]>0x12)
	{
		Timer[2]-=0x12;
	}
}
void Delay_SMG(unsigned int t)
{
  while(t--);
}
void SMG_Bit(unsigned char dat,unsigned char pos)
{
	SelectHC138(6);
	P0=0x01<<pos;
	SelectHC138(7);
	P0=SMG_DM[dat];
}
void Display_SMG()
{
	SMG_Bit(Timer[2]/16,0);
	Delay_SMG(100);
	SMG_Bit(Timer[2]%16,1);
	Delay_SMG(100);
	SMG_Bit(16,2);
	Delay_SMG(100);
	SMG_Bit(Timer[1]/16,3);
	Delay_SMG(100);
	SMG_Bit(Timer[1]%16,4);
	Delay_SMG(100);
	SMG_Bit(16,5);
	Delay_SMG(100);
	SMG_Bit(Timer[0]/16,6);
	Delay_SMG(100);
	SMG_Bit(Timer[0]%16,7);
	Delay_SMG(100);
	
	
}

void InitSystem()
{
	SelectHC138(5);
	P0=0x00;
	SelectHC138(4);
	P0=0xff;
}
void main()
{
	InitSystem();
	DS1302_Config();
	while(1)
	{
		Read_DS1302_Timer();
		Display_SMG();
	
	
	}
}

13. 频率测试

#include "reg52.h"
unsigned char code SMG_DM[18]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,
	0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};
unsigned int count_f=0;
unsigned int dat=0;
unsigned char count_s=0;
void SelectHC138(unsigned char n)
{
	switch(n)
	{
		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;
	}
}
//============================ÊýÂë¹ÜÏÔʾ
void Delay_SMG(unsigned int t)
{
  while(t--);
}
void SMG_Bit(unsigned char dat,unsigned char pos)
{
	SelectHC138(6);
	P0=0x01<<pos;
	SelectHC138(7);
	P0=dat;
}
void Display_SMG()
{
	SMG_Bit(SMG_DM[15],0);
	Delay_SMG(500);
	SMG_Bit(0xff,1);
	Delay_SMG(500);
	SMG_Bit(0xff,2);
	Delay_SMG(500);
	if(dat>9999)
	{
	  SMG_Bit(SMG_DM[dat/10000],3);
	  Delay_SMG(500);
	}
	if(dat>999)
	{
	  SMG_Bit(SMG_DM[(dat/1000)%10],4);
	  Delay_SMG(500);
	}
	if(dat>99)
	{
	  SMG_Bit(SMG_DM[(dat/100)%10],5);
	  Delay_SMG(500);
	}
	if(dat>9)
	{
	  SMG_Bit(SMG_DM[(dat/10)%10],6);
	  Delay_SMG(500);
	}
	  SMG_Bit(SMG_DM[dat%10],7);
	  Delay_SMG(500);
}
//===============================¶¨Ê±Æ÷
void Init_Timer()
{
	TMOD=0x16;
	//¶¨Ê±Æ÷0ÓÃ×÷¼ÆÊý  0110
	TH0=0xff;
	TL0=0xff;
	//¶¨Ê±Æ÷1ÓÃ×÷¶¨Ê±   0001
	TH1=(65535-50000)/256;
	TL1=(65535-50000)%256;
	ET0=1;
	ET1=1;
	EA=1;
	
	TR0=1;
	TR1=1;
	
}

void Service_T0() interrupt 1
{
  count_f++;
}
void Service_T1() interrupt 3
{
	
	TH1=(65535-50000)/256;
	TL1=(65535-50000)%256;
	count_s++;
	if(count_s==20)
	{
		dat=count_f;
		count_f=0;
		count_s=0;
	
	}
}
void InitSystem()
{
	SelectHC138(5);
	P0=0x00;
	SelectHC138(4);
	P0=0xff;
}

void main()
{
	InitSystem();
	Init_Timer();
	while(1)
	{
		Display_SMG();

	}
}

14. 2402C存储器使用

在使用前,我们得先明白其是通过IIC总线通信的

没有硬件IIC外设的微处理器中,需要根据总线时序设计IIC接口的驱动程序。包括:起始信号停止信号产生应答等待应答发送数据接收数据6个函数。下面以51单片机为例,阐述IIC总线驱动程序的设计。

//IIC

#include "reg52.h"
#include "intrins.h"

#define DELAY_TIME 5

#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1


sbit SDA = P2^1;  /* Êý¾ÝÏß */
sbit SCL = P2^0;  /* ʱÖÓÏß */

// 延时函数
void IIC_Delay(unsigned char i)
{
    do{_nop_();}
    while(i--);        
}

// 起始信号
void IIC_Start(void)
{
    SDA = 1;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 0;
    IIC_Delay(DELAY_TIME);
    SCL = 0;	
}

//停止信号
void IIC_Stop(void)
{
    SDA = 0;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 1;
    IIC_Delay(DELAY_TIME);
}

//产生答应
void IIC_SendAck(bit ackbit)
{
    SCL = 0;
    SDA = ackbit;  					// 0£ºÓ¦´ð£¬1£º·ÇÓ¦´ð
    IIC_Delay(DELAY_TIME);
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SCL = 0; 
    SDA = 1;
    IIC_Delay(DELAY_TIME);
}

//等待答应
bit IIC_WaitAck(void)
{
    bit ackbit;
	
    SCL  = 1;
    IIC_Delay(DELAY_TIME);
    ackbit = SDA;
    SCL = 0;
    IIC_Delay(DELAY_TIME);
    return ackbit;
}

//发送数据
void IIC_SendByte(unsigned char byt)
{
    unsigned char i;

    for(i=0; i<8; i++)
    {
        SCL  = 0;
        IIC_Delay(DELAY_TIME);
        if(byt & 0x80) SDA  = 1;
        else SDA  = 0;
        IIC_Delay(DELAY_TIME);
        SCL = 1;
        byt <<= 1;
        IIC_Delay(DELAY_TIME);
    }
    SCL  = 0;  
}

//接受数据
unsigned char IIC_RecByte(void)
{
    unsigned char i, da;
    for(i=0; i<8; i++)
    {   
    	SCL = 1;
	IIC_Delay(DELAY_TIME);
	da <<= 1;
	if(SDA) da |= 1;
	SCL = 0;
	IIC_Delay(DELAY_TIME);
    }
    return da;    
}

一般情况下,所提供的IIC总线底层驱动代码有“ **.c ”和“ **.h ”两个文件,你需要懂得它们,至少需要了解“ **.h” 头文件,才能正确应用。虽然不再需要编写IIC总线的底层驱动代码,但是对于具体设备的操作还需要结合数据手册来进一步实现,而IIC是需要用在我们的这个24C02存储器当中的

24C02存储器

#include "reg52.h"
#include "iic.h"
unsigned char code SMG_DM[18]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,
	0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};
unsigned char dat1 = 0, dat2 = 0, dat3 = 0;

//字节写操作
void SelectHC138(unsigned char n)
{
	switch(n)
	{
		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;
	}
}
void Delay(unsigned int t)
{
 while(t--);
}
void Write_24C02(unsigned char addr, unsigned char dat)
{
	IIC_Start();		
	IIC_SendByte(0xa0); 	
	IIC_WaitAck();		
	IIC_SendByte(addr); 	
	IIC_WaitAck(); 		
	IIC_SendByte(dat); 	
	IIC_WaitAck();		
	IIC_Stop();		
}

//字节读操作
unsigned char Read_24C02(unsigned char addr)
{
	unsigned char tmp;

	IIC_Start();			
	IIC_SendByte(0xa0); 	
	IIC_WaitAck();		
	IIC_SendByte(addr); 	
	IIC_WaitAck(); 		

	IIC_Start();			
	IIC_SendByte(0xa1); 	
	IIC_WaitAck();		
	tmp = IIC_RecByte();	
	IIC_SendAck(1); 		
	IIC_Stop();				
	return tmp;
}

//数据读写函数
void Read_Write()
{
	dat1 = Read_24C02(0x01);
	dat2 = Read_24C02(0x03);
	dat3 = Read_24C02(0x05);
	
	dat1 = dat1 + 1;
	dat2 = dat2 + 2;
	dat3 = dat3 + 3;
	
	if(dat1 > 10)
		dat1 = 0;
	if(dat2 > 20)
		dat2 = 0;
	if(dat3 > 30)
		dat3 = 0;
	
	Write_24C02(0x01, dat1);
	Delay(1100);
	Write_24C02(0x03, dat2);
	Delay(1100);
	Write_24C02(0x05, dat3);
	Delay(1100);
}
//数码管显示函数
void SMG_Bit(unsigned char dat,unsigned char pos)
{
	SelectHC138(6);
	P0=0x01<<pos;
	SelectHC138(7);
	P0=SMG_DM[dat];
}

//显示函数
void Display_24c02()
{
	SMG_Bit(dat1/10,0);
	Delay(500);
	SMG_Bit(dat1%10,1);
	Delay(500);
	SMG_Bit(16,2);
	Delay(500);
	SMG_Bit(dat2/10,3);
	Delay(500);
	SMG_Bit(dat2%10,4);
	Delay(500);
	SMG_Bit(16,5);
	Delay(500);
	SMG_Bit(dat3/10,6);
	Delay(500);
	SMG_Bit(dat3%10,7);
	Delay(500);
}

// 初始化函数
void InitSystem()
{
	SelectHC138(5);
	P0=0x00;
	SelectHC138(4);
	P0=0xff;
}

void main()
{
	InitSystem();
	Read_Write();
	while(1)
	{
		Display_24c02();
		
	}
	
}

15.PWM呼吸灯

任务如下:
1、将 J5 的 23 脚短接,把 S4 S7 设置为 独立按键
2、系统上电后,关闭蜂鸣器和继电器, L4 L5 指示灯 点亮 ,其余的指示灯熄灭。
3、按下 S4 按键,松开后, L1 L8 八个指示灯进行每隔 1 秒的 呼吸流水点亮 。控制流
程为: L1 缓慢 点亮 -> L1 缓慢熄灭-> L2 缓慢 点亮 -> L2 缓慢熄灭... L8 缓慢 点亮 -> L8 缓慢熄
灭-> L1 缓慢 点亮 -> L1 缓慢熄灭... 如此循环往复。
4、再次按下 S4 按键,松开后,L1 到 L8 八个指示灯从当前状态开始 逆向呼吸流水点亮
如果当前的水方向为: L1 缓慢 点亮 -> L1 缓慢熄灭-> L2 缓慢 点亮 - >L2 缓慢熄灭... 那么,按
S4 按键松开后为:
L2 缓慢 点亮 -> L2 缓慢熄灭-> L1 缓慢 点亮 -> L1 缓慢熄灭-> L8 缓慢 点亮
-> L8 缓慢熄灭-> L7 缓慢 点亮 -> L7 缓慢熄灭... 如此循环往复。
5、对于每个 LED 指示灯,缓慢点亮的时长为 0.5 秒 ,缓慢熄灭的时长为 0.5 秒
6、按下 S4 按键时,当前的指示灯暂停流水变化并保持现有的亮度,直到按键松开后,
亮度才开始恢复变化。待当前的亮度变化完成后,才开始改变呼吸流水控制的方向。
7、按下 S7 按键时,在数码管上显示 当前 LED 指示灯的
位置 PWM 脉宽调制信号的 占空比 。按键松开后,数码管熄
灭。显示格式如图,在数码管左边的第 1 位数码管显示 LED
指示灯的位置,在数码管右边的 2 位数码管显示 PWM 信号占
空比。例如:当前点亮 L6 指示灯,PWM 信号的占空比为 30%,
那么,数码管最左边的 1 位显示“6”,在最右边的 2 位显示
“30”,其余没有使用的数码管熄灭。
8、按下 S7 按键时,当前的指示灯暂停流水变化并保持现有的亮度,直到按键松开后,
亮度才开始恢复变化
#include "regx52.h"
#include "absacc.h"
 
sbit S7 = P3^0;
sbit S4 = P3^3;
 
unsigned char pwm = 0;          
unsigned char pwm_duty = 0;              
unsigned char times = 0;        
unsigned char led_go = 0;       
unsigned char stat_go = 0;      
unsigned char stat = 0;         
unsigned char key_puse = 0;     
 
unsigned char code SMG_duanma[18]=
		{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
		 0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};
 
void DelaySMG(unsigned int t)
{
	while(t--);
}
 
void DisplaySMG_Bit(unsigned char pos, unsigned char value)
{
	XBYTE[0xE000] = 0xff;
	XBYTE[0xC000] = 0x01 << pos;
	XBYTE[0xE000] = value;
}
 
void Display_Info(unsigned char pos, unsigned char duty)
{
	DisplaySMG_Bit(0, SMG_duanma[pos]);
	DelaySMG(500);
 
	DelaySMG(500);
	DisplaySMG_Bit(6, SMG_duanma[duty / 10]);
	DelaySMG(500);
	DisplaySMG_Bit(7, SMG_duanma[duty % 10]);	
	DelaySMG(500);
	
	DisplaySMG_Bit(0, 0xff);
	DisplaySMG_Bit(6, 0xff);
	DisplaySMG_Bit(7, 0xff);
}
 
void Init_Timer0()
{
	TMOD = 0x01;
	TH0 = (65535 - 1000) / 256;			
	TL0 = (65535 - 1000) % 256;
	
	ET0 = 1;
	EA = 1;
	TR0 = 1;
}
 
void Service_Timer0() interrupt 1
{
	TH0 = (65535 - 1000) / 256;
	TL0 = (65535 - 1000) % 256;
	
	if(stat_go == 0)      						
      {
        XBYTE[0x8000] = 0xe7;						
        return;
      } 
	
        pwm++;                
  
      if(pwm <= pwm_duty)   
      {
	XBYTE[0x8000] = ~(0x01 << stat);
      }
      else if(pwm <= 10)
      {
        XBYTE[0x8000] = 0xff;
      }
      else
      {
        XBYTE[0x8000] = ~(0x01 << stat);
    
        pwm = 0;
        if(key_puse == 0)     
        {
          times++;
        }
      }
}
 
void LED_Control()
{
  if(times == 5)          
  {
    times = 0;
    if(led_go == 0)       
    {
      pwm_duty = pwm_duty + 1;
      if(pwm_duty == 11)
      {
				pwm_duty = 10;
        led_go = 1;
      }
    }
    else if(led_go == 1)  
    {
      pwm_duty = pwm_duty - 1;
      if(pwm_duty == 255)
      {
				pwm_duty = 0;
        led_go = 0;
        
        if(stat_go == 1)      
        {
          stat++;
          if(stat == 8)
          {
            stat = 0;
          }
        }
        else if(stat_go == 2) 
        {
          stat--;
          if(stat == 255)
          {
            stat = 7;
          }
        }
      }
    }
  }
}
 
void Scan_Keys()
{
	if(S4 == 0)
	{
		DelaySMG(100);
		if(S4 == 0)
		{
			while(S4 == 0)
			{
				key_puse = 1;
			}
			key_puse = 0;
			
      stat_go++;            
      if(stat_go == 3)
      {
        stat_go = 1;
      }
		}
	}
	
	if(S7 == 0)
	{
		DelaySMG(100);
		if(S7 == 0)
		{
			while(S7 == 0)
			{
				key_puse = 1;
				Display_Info(stat + 1, pwm_duty * 10);
			}
			key_puse = 0;
		}
	}
}
 
void Init_System()
{
	//XBYTE[0xA000] = 0xff;
	XBYTE[0xA000] = 0x00;
	XBYTE[0xE000] = 0xff;
	XBYTE[0xC000] = 0xff;
	
	Init_Timer0();
}
 
main()
{
	Init_System();
	while(1)
	{
		LED_Control();
		Scan_Keys();
	}
}

16.超声波测距模块的使用

1、超声波模块的 TX 引脚 接到单片机的 P1.0 引脚, RX 引脚 接到单片机的 P1.1 引脚。
2、利用超声波传感器测量前端障碍物的距离,测量结果
厘米 作为单位,显示在数码管 最右边 3 位
3、测量距离最大约 100 厘米,当障碍物超出测量范围或
前方无障碍物,以“ F ”为标志,显示在数码管 最左边 1 位
4、 超声波传感器测距的工作原理:
首先产生 8 个 40KHz 的超声波信号,通过 TX 引脚 发射出
去,同时 启动定时器 ,计数计数脉冲,接着等待超声波信号的返回。如果超声波传感器接收
到反射回来的信号,则 RX 引脚 变为 低电平 ,这时候 停止定时器 ,读取计数脉冲个数,计算
超声波发射出去到反射回来的时间长度 T。最后根据公式: L = V*T/2 ,计算距离。
其中 V 为 20 摄氏度时的声速,其值约为: 344 米/秒
#include "reg52.h"
#include "intrins.h"

sbit TX = P1^0;
sbit RX = P1^1;

unsigned char code SMG_DuanMa[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,
	0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf};
unsigned int distance = 0;
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 DisplaySMG_Bit(unsigned char pos,unsigned char dat)
{
	SelectHC573(7);
	P0 = 0xff;
	SelectHC573(6);
	P0 = 0x01 << pos;
	SelectHC573(7);
	P0 = dat;
	SelectHC573(0);
}

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

void DisplaySMG()
{
	if(distance == 999)
	{
		DisplaySMG_Bit(0,SMG_DuanMa[15]);
		DelaySMG(500);
	}
	else
	{
		DisplaySMG_Bit(5,SMG_DuanMa[distance / 100]);
		DelaySMG(500);
		DisplaySMG_Bit(6,SMG_DuanMa[distance / 10 % 10]);
		DelaySMG(500);
		DisplaySMG_Bit(7,SMG_DuanMa[distance % 10]);
		DelaySMG(500);	
	}
}
void Delay_12us()
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 33;
	while (--i);
}
void SendWave()
{
	unsigned char j=0;
	for(j=0;j<8;j++)
	{
		TX =1;
		Delay_12us();
		TX=0;
		Delay_12us();
	}
}
void MeasureDistance()
{
	unsigned int time=0;
	TMOD=0x00;
	TH1=0;
	TL1=0;
	SendWave();
	TR1=1;
 	while((RX==1)&&(TF1==0));
  TR1=0;
	if(TF1==0)
	{
		time=TH1;
		time=(time<<8)|TL1;
		distance=time*0.0172;
	}
	else
	{
		TF1=0;
		distance=999;
	}



}


void Delay(unsigned char n)
{
	while(n--)
	{
		DisplaySMG();
	}

}

void main()
{
	InitSystem();
	while(1)
	{
		MeasureDistance();
		Delay(5);
	}
}


17. DAC转换

1、将 IIC 总线 的底层驱动代码文件正确移植到工程中。
2、循环采样 PCF8591 AIN3 通道中可调电阻的信号,将
采样结果转换成电压值,保留 2 位小数 ,显示在数码管右侧。
3、将可调电阻的当前采样数据作为 PCF8591 D/A 转换
数输出模拟电压。
5、使用万用表测量 J3 19 20 引脚,即 OUT GND
比较可调电阻的采样电压和 DAC 输出的实际电压。
#include "reg52.h"
#include "iic.h"

sbit S4 = P3^3;
sbit S5 = P3^2;
int dat = 0; 
unsigned char code SMGNodot[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char code SMGdot[] = {0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF};
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(4);
	P0 = 0XFF;
	SelectHC573(5);
	P0 = 0X00;
	SelectHC573(7);
	P0 = 0XFF;
	SelectHC573(6);
	P0 = 0XFF;
	SelectHC573(7);
	P0 = 0XFF;
	SelectHC573(0);
}

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

void DisplayAll_Off()
{
	SelectHC573(7);
	P0 = 0xff;
	SelectHC573(6);
	P0 = 0xff;
	SelectHC573(7);
	P0 = 0xff;
}

unsigned char Show[] = {0,0,0,0,0,0,0,0};

void CatchShow()
{
	Show[7] = ~SMGNodot[dat % 10];
	Show[6] = ~SMGNodot[(dat %100) / 10];
	Show[5] = ~SMGNodot[dat / 100];
	Show[4] = 0xff;
	Show[3] = 0xff;
	Show[2] = 0XFF;
	Show[1] = 0xff;
	Show[0] = 0xff;
}

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

void DisplayValue()
{	
	unsigned char i;
	CatchShow();
	for(i = 0; i < 8 ; i++)
	{
		DisplaySMG_bit(i,Show[i]);
		DelaySMG(2000);
	}
	DisplayAll_Off();
}

void WriteDA(unsigned char dat)
{
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(0x40);
	IIC_WaitAck();
	IIC_SendByte(dat);
	IIC_WaitAck();
	IIC_Stop();	
}

void ScanKeys()
{
	if(S4 == 0)
	{
		DisplayValue();
		if(S4 == 0)
		{
			while(S4 == 0)
			{
				DisplayValue();
				dat += 1;
				if(dat > 255)
				{
					dat = 255;
				}
			}				
		}
	}
	
	if(S5 == 0)
	{
		DisplayValue();
		if(S5 == 0)
		{
			while(S5 == 0)
			{
				DisplayValue();
				dat -= 1;
				if(dat < 0)
				{
					dat = 0;
				}
			}				
		}
	}
}


void main()
{
	InitSystem();
	WriteDA(2*255/5);
	while(1);
}
	


整体模块技能训练

// 头文件包含
# include "reg52.h" 
# include "iic.h"
# include "onewire.h"     //单总线头文件包含
# include "ds1302.h"      //DS1032时钟模块包含
# include "intrins.h"		 


typedef unsigned char uchar;
typedef unsigned int uint;

sfr AUXR = 0x8e;

//按键引脚定义
sbit S4 = P3^3;
sbit S5 = P3^2;
sbit S6 = P3^1;
sbit S7 = P3^0;

//信号发送与接收
sbit TX = P1^0;				 
sbit RX = P1^1;

//数码管段码
uchar duanma[10] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
//无点断码
uchar duanmadot[10] = {0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};				 

//DS1302 地址
uchar Writeaddr [7] = {0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
uchar Readaddr [7] = {0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
uchar Timer[7] = {0x00}; //时间地址自己写入

uchar rb2 = 0;
uint count = 0;         		 
bit smg_f = 0;


//矩阵键盘引脚定义
sfr P4 = 0xC0;

sbit R1 = P3^0;
sbit R2 = P3^1;
sbit R3 = P3^2;
sbit R4 = P3^3;

sbit C1 = P4^4;
sbit C2 = P4^2;
sbit C3 = P3^5;
sbit C4 = P3^4;

//==================573锁存器与138译码器===========================
void SelectHC573(uchar n)
{
	switch (n)
	{
		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;
	}
}
//============================================================


//====================LED=====================

//============================================================


//=====================系统初始化函数=============================
void InitSystem ()
{	
	SelectHC573(4);
	P0 = 0xff;
	SelectHC573(5);
	P0 = 0x00;
	SelectHC573(0);	
}
//============================================================

//=========================PCF8591============================

void ReadRd1 ()
{
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(0x03);	
	IIC_WaitAck();

   	IIC_Start();
	IIC_SendByte(0x91);
	IIC_WaitAck();
	rb2 = IIC_RecByte();
	IIC_SendAck(1);
	IIC_Stop();
}

/
void PCFADC ()
{
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(0x43);	
	IIC_WaitAck();

   	IIC_Start();
	IIC_SendByte(0x91);
	IIC_WaitAck();
	rb2 = IIC_RecByte();	
	IIC_SendAck(1);
	IIC_Stop();	
}

void PCFDAC (uchar dat)
{
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(0x40);	
	IIC_WaitAck();
	IIC_SendByte(dat);	  
	IIC_WaitAck();
	IIC_Stop();	
}
//============================================================


//=======================AT24C02==============================
uchar Read24c02 (uchar addr)
{
	uchar temp;

	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(addr);	
	IIC_WaitAck();

   	IIC_Start();
	IIC_SendByte(0xa1);
	IIC_WaitAck();
	temp = IIC_RecByte();
	IIC_SendAck(1);
	IIC_Stop();
	
	return temp;		
}

void Write24c02 (uchar addr,uchar dat)
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(addr);	
	IIC_WaitAck();
	IIC_SendByte(dat);
	IIC_WaitAck();
	IIC_Stop();		
}
//============================================================

//=======================DS18B20==============================
void Read_Temp ()								
{
	uchar LSB;
	uchar MSB;

	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0x44);
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0xbe);
	
	LSB = Read_DS18B20();
	MSB = Read_DS18B20();
	tem = (MSB << 8) | LSB;
	
	if ((tem & 0xf800) == 0x0000)	  
	{
		tem = tem >> 4;
	}
	
}
//============================================================

//=======================DS1302===============================
void Write_Timer ()								  
{
	uchar i;
	Write_Ds1302_Byte(0x8e,0x00);
	for (i = 0;i <= 7;i++)
	{
		Write_Ds1302_Byte(Writeaddr[i],Timer[i]);		
	}
	Write_Ds1302_Byte(0x8e,0x80);	
}

void Read_Timer ()
{
	uchar i;
	for (i = 0;i <= 7;i++)
	{
		Timer[i] = Read_Ds1302_Byte(Readaddr[i]);		
	}	
}


uchar BCD_add(uchar dat)
{
	dat = dat + 1;
	switch (dat)
	{
		case 10:dat = 16;break;
		case 26:dat = 32;break;
		case 42:dat = 48;break;
		case 58:dat = 64;break;
		case 74:dat = 80;break;
		case 90:dat = 0;break;	
	}
	return dat;
}

//??BCD??
uchar BCD_minus(uchar dat)
{
	dat = dat - 1;
	switch (dat)
	{
		case -1:dat =89;break;
		case 79:dat = 73;break;
		case 63:dat = 57;break;
		case 47:dat = 41;break;
		case 31:dat = 25;break;
		case 15:dat = 9;break;	
	}
	return dat;
}
//============================================================

//=========================NE555频率计=============================

void InitTime ()
{
	TMOD = 0x16;

	TH1 = (65535 - 100) / 256;
	TL1 = (65535 - 100) % 256;
	TH0 = 0xff;
	TL0 = 0xff;
	
	TR0 = 1;
	TR1 = 1;
	ET0 = 1;
	ET1 = 1;
	EA = 1;	
}

void ServiceT0 () interrupt 1
{
	f_c ++;
}

void ServiceT1 () interrupt 3
{
	TH1 = (65535 - 100) / 256;
	TL1 = (65535 - 100) % 256;
	
	count++;
	if (count >= 10000)
	{
		dat_f = f_c;
		count = 0;
		f_c = 0;
	}

	count++;
	if (count >= 10000)
	{
		count = 0;
		smg_f = ~smg_f;
	}
	
				   
	pwm_c ++;
	if (pwm_c >= pwm_t)
	{
		pwm_f = 0;
		if (pwm_c >= 100)
		{
			pwm_c = 0;
			pwm_f = 1;
		}
	}	
}
//============================================================

//=======================中断服务函数=============================
void InitINT0 ()
{
	IT0 = 1;
	EX0 = 0;
	EA = 1;	
}

void ServiceINT0 () interrupt 0
{
	//L8 = 0;
	//Delay (60000);
	//L8 = 1;	
}
//============================================================

//========================串口通信========================		  //=================================================
void InitUart ()
{
	TMOD = 0x21;
	TH1 = 0xfd;					  
	TL1 = 0xfd;
	TR1 = 1;
	ES = 1;
	EA = 1;

	AUXR = 0x00;
	SCON = 0x50;
}

void ServiceUart ()	interrupt 4
{
	if (RI == 1)
	{
		command = SBUF;
		RI = 0;
	}
}

void SendByte (uchar dat)
{
	SBUF = dat;
	while (TI == 0);
	TI = 0;
}

void SendString (uchar* addr)
{
	uchar i = 0;
	while (*addr != '\0')
	{
		SendByte(*addr);
		addr = addr + 1;
	}
}

void ExecuteCommand ()
{
	if (command != 0x00)
	{
		switch (command & 0xf0)
		{
			case: 0xa0:
				//
				command = 0x00;
				break;
			case: 0xb0:
				//
				command = 0x00;
				break;
				//...
		}
	}
}
//============================================================

//====================数码管显示==========================
void Delay_SMG (uint t)
{
	while (t--);	
} 

void ShowSMG_Bit (uchar pos,uchar dat)
{
	SelectHC573(7);
	P0 = 0xff;
	SelectHC573(6);
	P0 = 0x01 << pos - 1;
	SelectHC573(7);
	P0 = dat;
	SelectHC573(0);		
}

void All_SMG (uchar dat)
{
	SelectHC573(6);
	P0 = 0xff;
	SelectHC573(7);
	P0 = dat;	
}

void ShowSMG ()
{
	if (k4 == 0)
	{
		ShowSMG_Bit(1,0xc1);
		Delay_SMG (500);
		ShowSMG_Bit(6,duanmadot[dat_u / 100]);
		Delay_SMG (500);
		ShowSMG_Bit(7,duanma[(dat_u / 10) % 10]);
		Delay_SMG (500);
		ShowSMG_Bit(8,duanma[dat_u % 10]);
		Delay_SMG (500);	
	}
	else if (k4 == 1)
	{
		ShowSMG_Bit(1,0x8e);
		Delay_SMG (500);
		if (dat_f > 99999)
		{
			ShowSMG_Bit(3,duanma[(dat_f / 100000) % 10]);
			Delay_SMG (500);	
		}
		if (dat_f > 9999)
		{
			ShowSMG_Bit(4,duanma[(dat_f / 10000) % 10]);
			Delay_SMG (500);	
		}
		if (dat_f > 999)
		{
			ShowSMG_Bit(5,duanma[(dat_f / 1000) % 10]);
			Delay_SMG (500);
		}
		if (dat_f > 99)
		{
			ShowSMG_Bit(6,duanma[(dat_f / 100) % 10]);
			Delay_SMG (500);
		}
		if (dat_f > 9)
		{
			ShowSMG_Bit(7,duanma[(dat_f / 10) % 10]);
			Delay_SMG (500);
		}
		ShowSMG_Bit(8,duanma[(dat_f / 1) % 10]);
		Delay_SMG (500);	
	}
	All_SMG(0xff);	
}
//============================================================

//==================按键延时========================
void DelayKey (uchar t)
{
	while (t--);
}

void ScanKey ()
{
	if (S4 == 0)
	{
		DelayKey (100);
		if (S4 == 0)
		{
			while (S4 == 0)
			{
			}
		}
	}
	if (S5 == 0)
	{
		DelayKey (100);
		if (S5 == 0)
		{
			while (S5 == 0)
			{
			}
		}
	}
	if (S6 == 0)
	{
		DelayKey (100);
		if (S6 == 0)
		{
			while (S6 == 0)
			{
			}
		}
	}
	if (S7 == 0)
	{
		DelayKey (100);
		if (S7 == 0)
		{
			while (S7 == 0)
			{
			}
		}
	}
}
//============================================================

//====================矩阵键盘==========================
void Scanjuzhenjianpan (void)
{
	R1 = 0;
	R2 = R3 = R4 = 1;
	C1 = C2 = C3 = C4 = 1;
	if (C1 == 0)
    {}
	else if (C2 == 0)
	{}
	else if (C3 == 0)
	{}
	else if (C4 == 0)
	{}
	
	R2 = 0;
	R1 = R3 = R4 = 1;
	C1 = C2 = C3 = C4 = 1;
	if (C1 == 0)
	{
		
	}
	else if (C2 == 0)
	{}
	else if (C3 == 0)
	{}
	else if (C4 == 0)
	{}
	
}

//============================================================

//==========================???=============================
void Delay12us()	
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 33;
	while (--i);
}

void SendWave ()									  //40KHZ,8?????,TX??
{
	uchar i;
	for (i = 0;i < 8;i++)
	{
		TX = 1;
		Delay12us();
		TX = 0;
		Delay12us();
	}
}

void DistanceMeasure ()								
{
	TMOD &= 0x0f;	
	TH1 = 0x00;	
	TL1 = 0x00;

	SendWave ();

	TR1 = 1;
	while ((RX == 1) && (TF1 == 0)); 
	TR1 = 0;

	if (TF1 == 1)
	{
		distance = 999;
		TF1 = 0;					
	}
	else 
	{
		time = (TH1 << 8) | TL1;
		distance = time * 0.0172;	   
	}
}

void Delay (uchar n)								  
{
	while (n--)
	{
	   ShowSMG ();
	}
}
//=========================================================

//====================主函数========================
void main ()
{
	while (1)
	{
		Delay(10);
	}
}
/

——————————————————分界线————————————————

░░░░░░░░░▄▄

░░░░░░░░░█░█

░░░░░░░░░█░█

░░░░░░░░█░░█

░░░░░░░█░░░█

█████▄▄█░░░████

▓▓▓▓█░░░░░░░░░░░░█

▓▓▓▓█░░░░░░░░░░░░█

▓▓▓▓█░░░░░░░░░░░░█

▓▓▓▓█░░░░░░░░░░░░█

▓▓▓▓█░░░░░░░░░░░░█

▓▓▓▓█████░░░░░░░░

████▀░░░▀▀██████▀​

                                                                      ------------------------------------------->> To Be Continue

猜你喜欢

转载自blog.csdn.net/ArtoriaLili/article/details/129533095