蓝桥杯----锁存器、LED、蜂鸣器、继电器、Motor

发布于:2025-08-07 ⋅ 阅读:(13) ⋅ 点赞:(0)

   (七)、锁存器

 1、原理

      蓝桥杯中数据传入口都是P0,也就是数码管段选、位选数据、LED亮灭的数据、蜂鸣器启动或禁用的数据,外设启动或者关闭都需要通过P0写入数据,那么如何这样共用一个端口会造成冲突嘛,答案是肯定的。所以蓝桥杯加入了锁存器---通过P2高三位(22、21、20)操作。如图 三十LED锁存器对应地址为Y4C,凑成高三位数据100,后五位自动补零,P2对应数据1000 0000(用十六进制表示0x80)。Y5C对应数据就是1010 0000(0xa0)…

  2、代码示范与解读

 如 图 三十一,首先定义一个temp临时变量,再在P0口传入数据

P2 & 0x1f就是保留低五位,单独使高三位全为0,因为高三位才是锁存器的选择位。

再单独改变temp = temp | 0x80,0x80是刚刚推出来的数据,这样|上,就是单独修改了高三位,低五位 保持不变。再将数据放入P2中,此时P0中数据就已经传入了。

最后我们temp = P2 & 0x1f就是清空高三位,P2 = temp关闭锁存器。注意如果此时不关闭锁存器,那么一旦有数据传入P0就会被立即写入。

对这个代码进行推广,只需改变0x80那个位置的数据即可,假设想操作数码管位选的锁存器Y6C,可知为1100 0000(即为0xc0),将0x80改成0xc0就可以传入数码管位选数据了。

                        图 三十 LED原理图

            

                 图 三十一  借助锁存器写入数据

(八)、LED

 1、原理(图 三十)

        在P00-P07口传入数据,对应第一盏灯到第八盏灯,打开锁存器,就可以实现LED亮灭了。由原理图可知,为共阳极接法,当P0端口传入低电平数据时,就点亮LED了。注意51单片机LED亮灭可能不同。

 2、代码解读(图 三十二)

        定义一个数组,传入LED亮灭的数据,1为亮表示使能,0为灭表示失能

       定义两个变量,temp_1与temp_1_old,只有这两者不相等时操作寄存器(改变)写入,temp_1存放亮灭的八位数据。

       LED_Buf[0]<<0就是将第一个灯数据左移0位,放在temp_1的最低位,就是操作P00第一个灯。LED_Buf[1]<<1就是把第二个灯数据按二进制左移1位,放在temp_1的Bit2位…

       有改变发生,两者不同了,写入数据,记住低电平点亮灯,所以要取反,在打开LED的锁存器,更新temp_1_old的值。

      在主程序中我们可以打开定时器0,写一个1ms中断函数,在中断中扫描这个函数。

                   图 三十二 LED代码解读

(九)、继电器、蜂鸣器、Motor

  1、原理(图 三十三)

      最左边P01~P07是数据输入端口,最右边Relay就是对应继电器,Motor就是发动机,Buzz对应蜂鸣器,低电平0就是打开这个设备,但是ULN2003设备在中间会对数据进行取反,所以打开Relay数据为 空一位011 1111,对应最左边数据为 0001 0000,即为0x10。同理打开蜂鸣器即为0x40,打开Motor即为0x20。

                    图 三十三 外设原理图

               

 2、代码解读(图 三十四 )

     开局必须定义两个全局变量为蜂鸣器、继电器、发动机共用,防止不同变量操作对其他设备影响。

     当Flag为1时,必须单独变化第四位为1,其余的不变,用 | 操作符来实现,|上0这一位就是保持不变,|上1就是强制为1。当Flag为1时,第四位为0,其余的不变,用 & 符来实现。

    temp_2就是临时数据值,temp_2_Old就是记录上一个状态的值,不相同说明状态发生了改变,将数据传入P0端,打开锁存器即可。

                图 三十四 代码解读(继电器)

 3、代码推广(图 三十五、图 三十六)

   将0x10改为0x40就是单独打开蜂鸣器代码,改成0x20就是启动Motor电机代码,电机适用于设计PWM波形的。一般来说只考继电器在特定条件下打开与关闭。

                  图 三十五 蜂鸣器推广代码

                      图 三十六 Motor推广代码

4、提供参考代码,希望对读者有帮助

#include <STC15F2K60S2.H>

idata unsigned char temp_1 = 0x00;
idata unsigned char temp_old_1 = 0xff;
void LED_Disp(unsigned char *LED_Buf)   //传入LED数据数组,1亮、0灭
{
	unsigned char temp; 
	temp_1 = 0x00;
	temp_1 = (LED_Buf[0]<<0) | (LED_Buf[1]<<1) |(LED_Buf[2]<<2) |
	(LED_Buf[3]<<3) |(LED_Buf[4]<<4) |(LED_Buf[5]<<5) |(LED_Buf[6]<<6) |
	(LED_Buf[7]<<7);    //通过一个数组传入8个LED灯的数据  
	//LED_Buf[0]<<0,假设第一个数据为1,数据<<(按位左移)0就是让Bit1为1
	//LED_Buf[1]<<1,假设第一个数据为1,数据<<(按位左移)1就是让Bit2为1
	
	if(temp_1 != temp_old_1)  //数据改变
	{
		P0 = ~temp_1;    //一定记得取反
		temp = P2 & 0x1f;
		temp = temp | 0x80;   //操作Led灯的锁存器
		P2 = temp;
		temp = P2 & 0x1f;
		P2 = temp;
		
		temp_old_1 = temp_1;   //更新temp_old_1
	}
}


idata unsigned char temp_2 = 0x00;
idata unsigned char temp_2_old = 0xff;

//继电器操作函数
void Relay_Disp(unsigned char Flag)
{
	unsigned char temp;
	if(Flag)
	temp_2 |= 0x10;
	else
	temp_2 &= ~0x10;
	
	if(temp_2 != temp_2_old)
	{
		P0 = temp_2;
		
		temp = P2 & 0x1f;
		temp = temp | 0xa0;
		P2 = temp;
		temp = P2 & 0x1f;
		P2 = temp;
		
		temp_2_old = temp_2;
	}
}

//蜂鸣器操作函数
void Beep_Disp(unsigned char Flag)
{
	unsigned char temp;
	if(Flag)
	temp_2 |= 0x40;
	else
	temp_2 &= ~0x40;
	
	if(temp_2 != temp_2_old)
	{
		P0 = temp_2;
		
		temp = P2 & 0x1f;
		temp = temp | 0xa0;
		P2 = temp;
		temp = P2 & 0x1f;
		P2 = temp;
		
		temp_2_old = temp_2;
	}
}


//Motor操作函数
void Motor_Disp(unsigned char Flag)
{
	unsigned char temp;
	if(Flag)
	temp_2 |= 0x20;
	else
	temp_2 &= ~0x20;
	
	if(temp_2 != temp_2_old)
	{
		P0 = temp_2;
		
		temp = P2 & 0x1f;
		temp = temp | 0xa0;
		P2 = temp;
		temp = P2 & 0x1f;
		P2 = temp;
		
		temp_2_old = temp_2;
	}
}

5、提供定时器1代码与中断

//定时器一初始化,自己加上EA = 1;ET1 = 1;
void Timer1_Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0xBF;			//定时器时钟12T模式
	TMOD &= 0x0F;			//设置定时器模式
	TL1 = 0x18;				//设置定时初始值
	TH1 = 0xFC;				//设置定时初始值
	TF1 = 0;				//清除TF1标志
	TR1 = 1;				//定时器1开始计时
	EA = 1;                 //打开总中断
	ET1 = 1;                //打开定时器一中断允许位
}

void Timer1_Routine() interrupt 3
{
LED_Disp(LED_Buf);         //LED扫描
}