基于单片机的智能风扇设计

这篇具有很好参考价值的文章主要介绍了基于单片机的智能风扇设计。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

一、基本元器件说明

单片机类型:51单片机(普中)

传感器的使用:

DS18B20温度传感器、LD3320语音模块、JQ8900-16P语音播报、LCD1602显示屏、hc-sr501人体感应模块等

二、具体功能说明

设计有三个模式:

模式1:根据温度变化进行风扇速度的调节

模式2:自主控制温度的风扇变化

模式3:采用人体感应模块,监测风扇前是否有人,进行风扇开启与关闭

三种控制方式:语音控制、按键控制、红外遥控控制

三、设计说明

本设计是根据百家之所长,有很多参考的地方,写本次文章的目的是在于记录与分享在设计中遇见的问题,与自己的一些理解,供大家参考与交流,网上很多模块的使用都有很详细的解释,我这只说一些我使用的理解,描述不准确,多指正。

四、分享说明

这是我第一次写这样的内容,肯定会有很多表达不清楚或者不准确的地方,如果有指正一下,共同进步,我主要是针对主函数的讲解,分享我的设计思路,具体模块的头文件部分并没有展示,因为这些模块很多地方都可以找到。

内容展示

设计流程

我的大体设计流程是:

(1)按键控制风扇的速度调节

(2)LCD1602显示温度的变化

(3)风扇和温度相互结合

(4)模式2部分就是按键控制风扇的旋转,模式1中已经解决

(5)模式3部分就是加上人体感应模块即可

(6)加入JQ9600-16P进行语音播报

(7)操作控制的设计是逐个增加:按键控制-----红外遥控-------语音控制

具体设计

【1】按键控制风扇的速度调节

1. 按键部分

我是采用的51单片机,上面有矩阵按键与独立按键,因为我需要用到的按键比较多,我在设计中使用的是矩阵按键,按键这部分不详细说明。

 2. 对于风扇速度的调节,使用的是PWM调速

对于使用注意几点:

 2.1 采用的是直流电机,51已经有了驱动模块,所以直接对端口赋值1(开启)、0(关闭)即可

 2.2 对于PWM调速,单片机的IO口输出的是数字信号,IO口只能输出高电平和低电平,理论上通过连接和断开,可以输出在范围内的任意模拟电压

2.3 对于PWM,其核心就是占空比:可以理解为高电平在周期中占据多少时间,转换成单片机就是电平1占据的时间,占空比50%,那就是高低电平各占一半

2.4 为什么采用定时器产生PWM,我的理解是,快、反复,只要开启定时器,就可以一直反复快速操作

3.相关代码

3.1定时器代码(采用的是定时器2,中断号别搞错了
void Timer2_Routine() interrupt 5
{
	RCAP2L = 0x66;		//设置定时重载值
	RCAP2H = 0xFC;		//设置定时重载值
	TF2=0;
	Counter++;
	Counter%=100;	//计数值变化范围限制在0~99
	if(Counter<Compare)	//计数值小于比较值
	{
		Motor=1;		//输出1
	}
	else				//计数值大于比较值
	{
		Motor=0;		//输出0
	}
}

比如我们现在设置Compare  =  50,定时器开启后,Counter会迅速的自加,在小于50的部分都是高电平(1),超过50的部分都是低电平(0),这样就产生了占空比50%,那就是高低电平各占一半,高电平占的越多,风扇转的越快,所以我们想改变风扇的档位,将Compare设置的大一点就可以了,这就是调速的方法。因为在定时器中Counter%=100;这样就可以实现反复的进行操作,这就是用定时器的原因

3.2 按键控制代码,以模式2代码为例:

主要看KeyNum 、Compare 、M 这三个值就行

KeyNum就是按键值的获取,Compare改变值,从而来控制速度,M就是档位显示(0-3档)

if(KeyNum==5 || Command==IR_0 || Flag==8)
				{
					Flag=0;
					Delay(200);
					play_song_num(0x04);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=0;
					M=0;
				}					//设置比较值,改变PWM占空比
				if(KeyNum==6 || Command==IR_1 || Flag==9)
				{
					Flag=0;
					Delay(200);
					play_song_num(0x05);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=50;
					M=1;
				}
				if(KeyNum==7 || Command==IR_2 || Flag==10)				
				{
					Flag=0;
					Delay(200);
					play_song_num(0x06);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=75;
					M=2;
				}
				if(KeyNum==8 || Command==IR_3 || Flag==11)//3挡
				{
					Flag=0;
					Delay(200);
					play_song_num(0x07);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=100;
					M=3;
				}
				LCD_ShowNum(2,16,M,1);

【2】DS18b20与LCD1602

这两个资料太多了,其实直接调用就行,这里主要补充一下,对于数据的保存,采用了AT24c02。

1.为什么使用AT24c02  ?

在我的设计中,我希望我设置的温度上下限,并不会因为关机重启而导致我还需要再设置一遍,所以我希望保存我设置的数据,那么就需要掉电不丢失,这就是为什么使用AT24c02

2.相关说明

(1)对于AT24c02,51单片中有这个模块

(2)这部分是针对的模式1,所以将风扇和温度结合

3.模式1相关代码

3.1 读取AT24C02的上次存储的值
	/*****温度初始化***/
	DS18B20_ConvertT();		//上电先转换一次温度,防止第一次读数据错误
	Delay(1000);			//等待转换完成
	THigh=AT24C02_ReadByte(0);	//读取温度阈值数据
	TLow=AT24C02_ReadByte(1);   //读取事先存储的温度
	if(THigh>125 || TLow<-55 || THigh<=TLow)//判断AT24C02数据是否非法
	{
		THigh=20;			//如果阈值非法,则设为默认值
		TLow=15;
	}
3.2温度数据在LCD1602上的显示
        DS18B20_ConvertT();	//转换温度
		T=DS18B20_ReadT();	//读取温度
		if(T<0)				//如果温度小于0
		{
			LCD_ShowChar(1,7,'-');	//显示负号
			TShow=-T;		//将温度变为正数
		}
		else				//如果温度大于等于0
		{
			LCD_ShowChar(1,7,'+');	//显示正号
			TShow=T;
		}
		LCD_ShowNum(1,8,TShow,2);		//显示温度整数部分
		LCD_ShowChar(1,10,'.');		//显示小数点
		LCD_ShowNum(1,11,(unsigned long)(TShow*100)%100,2);//显示温度小数部分
3.3根据温度的变化显示数据以及调整风扇速度

具体说明:这部分代码主要分为三个部分:   /********分割线********/作为区分

3.3.1温度上下限调节与相关显示

 这部分就是调节设置温度上下限

显示部分:如果温度在我们所设定的范围内:

LCD_ShowString(1,13,"  N ");

超过我们所设定的温度范围显示:

LCD_ShowString(1,13,"  H");
3.3.2温度超过上下限风扇转速的变化

        Temp1=THigh+2;//超过上限2度
        Temp2=THigh+4;//超过上限4度

这就是为根据温度变化调整转速的根据,达到Temp1就是2档(Compare = 75),达到Temp2就是3档(compare = 100)

3.3.3数据存储使用AT24C02
		if(KeyNum==1 || Command==IR_Mode1 || Flag==1)//Mode1,温度模式
			{
				Mode=1;
				Flag=0;
				Delay(200);
				play_song_num(0x01);  //先不看这句
				LCD_ShowString(1,13,"    ");
				LCD_ShowString(1,1,"Tep ");//温度模式
				LCD_ShowString(1,5,"T:");//显示采集温度
				LCD_ShowString(2,1,"TH:");//显示温度上限
				LCD_ShowString(2,8,"TL:");//显示温度下限
				LCD_ShowString(2,7," ");
				LCD_ShowSignedNum(2,4,THigh,2);//显示上下限值
				LCD_ShowSignedNum(2,11,TLow,2);
				LCD_ShowString(2,14,"M:");
			}
			if(Mode==1)
			{	
				/***温度高位+***/
				if(KeyNum==5 || Command==IR_THADD || Flag==4)//+
				{
					Flag=0;
					THigh++;
					if(THigh>125){THigh=125;}//超过最高范围
				}
				/***温度高位-***/
				if(KeyNum==6 || Command==IR_THMINU || Flag==5)//-
				{
					Flag=0;
					THigh--;
					if(THigh<=TLow){THigh++;}
				}
				/***温度低位+***/
				if(KeyNum==7 || Command==IR_TLADD || Flag==6)//+
				{
					Flag=0;
					TLow++;
					if(TLow>=THigh){TLow--;}//超过最低温度
				}
				/***温度低位-***/
				if(KeyNum==8 || Command==IR_TLMINU || Flag==7)//-
				{
					Flag=0;
					TLow--;
					if(TLow<-55){TLow=-55;}
				}
				/********分割线********/
				Temp1=THigh+2;//超过上限2度
				Temp2=THigh+4;//超过上限4度
				LCD_ShowNum(2,16,0,1);
				if(T>THigh && T<Temp1)			//越界判断
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,1,1);
					Compare=50;
				}
				else if(T>Temp1 && T<Temp2)
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,2,1);
					Compare=75;
				}
				else if(T>Temp2)
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,3,1);
					Compare=100;
				}
				/*else if(T<THigh)			//越界判断
				{
					LCD_ShowString(1,13,"    ");
					Compare=0;
				}*/
				else if(T<TLow)
				{
					LCD_ShowString(1,13,"  L");
					LCD_ShowNum(2,16,0,1);
					Compare=0;
				}
				else 
				{
					LCD_ShowString(1,15," ");
				}
				
				if(T>TLow && T<THigh)
				{
					LCD_ShowString(1,13,"  N ");
					LCD_ShowNum(2,16,1,1);
					Compare=50;
				}
				LCD_ShowSignedNum(2,4,THigh,2);	//显示阈值数据
				LCD_ShowSignedNum(2,11,TLow,2);
				/********分割线********/
				AT24C02_WriteByte(0,THigh);		//写入到At24C02中保存
				Delay(5);
				AT24C02_WriteByte(1,TLow);
				Delay(5);

【3】模式3的设计

模式3就是一个人体感应模块,有人在输出1,没人输出0(吐槽:不知道是我使用的问题,还是这个模块的问题,有时候很不灵敏)

            if(Pep==1 && Mode==3)
			{
				LCD_ShowString(1,1,"ECO T:");
				LCD_ShowString(1,14,"YES");
				LCD_ShowString(2,1,"People Detection");
				Compare=75;
			}
			if(Pep==0 && Mode==3)
			{
				LCD_ShowString(1,1,"ECO T:");
				LCD_ShowString(1,14,"NO ");
				LCD_ShowString(2,1,"People Detection");
				Compare=0;
			}

以上就是三个模块的具体设计

【4】JQ8900-16的介绍

1.JQ8900-16P有三种处理方式:IO口触发播放、双线串口、单线串口

我使用的是IO口触发播放(这个简单)

端口:

sbit sda=P2^7;

主函数代码:

就是主函数中这样的数据

play_song_num(0x01);  

具体使用:

1.生成我们所需要的音频(mp3格式),好像IO口触发最多只支持9条(不太清楚),命名格式为00001.mp3(玄学,按照顺序排下来00001.mp3、00002.mp3这样按顺序排),这里用我画圈那个地方写入,像u盘一样,将文件放进去就行

基于单片机的智能风扇设计,嵌入式硬件,智能家居,51单片机,c语言,驱动开发,硬件工程

2.主函数中play_song_num(0x01);  这样调用即可

【5】语音控制模块

购买LD3320后,淘宝店家会给很详细的资料,按照步骤操作就行文章来源地址https://www.toymoban.com/news/detail-801190.html

1.接收

void UART_Routine() interrupt 4
{
	if(RI==1)					//如果接收标志位为1,接收到了数据
	{  
		RI=0; 
    receive_data = SBUF;  
	}
}

2.接收后

switch(receive_data)
			{
				case 'A':receive_data=0;Delay(300);Flag=1;break;//温度模式语音模块
				case 'B':receive_data=0;Delay(300);Flag=2;break;//正常模式语音模块
				case 'C':receive_data=0;Delay(300);Flag=3;break;//节能模式语音模块
				case 'D':receive_data=0;Delay(300);Flag=4;break;//温度上限+语音模块
				case 'E':receive_data=0;Delay(300);Flag=5;break;//温度上限-语音模块
				case 'F':receive_data=0;Delay(300);Flag=6;break;//温度下限+语音模块
				case 'G':receive_data=0;Delay(300);Flag=7;break;//温度下限-语音模块
				case 'H':receive_data=0;Delay(300);Flag=8;break;//风扇关闭语音模块
				case 'I':receive_data=0;Delay(300);Flag=9;break;//风扇1挡语音模块
				case 'J':receive_data=0;Delay(300);Flag=10;break;//风扇2挡语音模块
				case 'K':receive_data=0;Delay(300);Flag=11;break;//风扇3挡语音模块
			}

【6】按键和红外操作我直接放主函数


#include <REGX52.H>
#include "AT24C02.h"
#include "Delay.h"
#include "DS18B20.h"
#include "I2C.h"
#include "LCD1602.h"
#include "OneWire.h"
#include "Timer0.h"
#include "Timer2.h"
#include "IR.h"
#include "Int0.h"
#include "MatrixKey.h"
#include "UART.h"
#include "jq8900.h"

sbit Motor=P1^0;//风扇驱动
sbit Pep=P3^4;//人体检测
sbit sda=P2^7;


unsigned int RunNum=0;//步进电机摇头
float T,TShow;//T是实际温度,Tshow是显示温度,可以变号
char TLow,THigh;//-127--+127
unsigned char KeyNum;//用于读取按键值
unsigned char Counter,Compare;	//计数值和比较值,用于输出PWM
unsigned char Speed,M,Mode,Temp1,Temp2,Command;
unsigned char receive_data;	//接收串口标志位
unsigned int Flag;//语言标识符
//KeyNum读取按下按键值,Speed风扇模式切换,M档位显示,Mode模式
//Temp1,Temp2高温值设置

void main()
{
	/*****温度初始化***/
	DS18B20_ConvertT();		//上电先转换一次温度,防止第一次读数据错误
	Delay(1000);			//等待转换完成
	THigh=AT24C02_ReadByte(0);	//读取温度阈值数据
	TLow=AT24C02_ReadByte(1);   //读取事先存储的温度
	if(THigh>125 || TLow<-55 || THigh<=TLow)//判断AT24C02数据是否非法
	{
		THigh=20;			//如果阈值非法,则设为默认值
		TLow=15;
	}
	/***显示初始化***/
	LCD_Init(); 
	LCD_ShowString(1,1,"NowT:       ");//初步显示模块
	LCD_ShowString(2,1,"K1:T K2:N K3:E"); 
	Timer0_Init();//定时器初始化,红外线部分
	Timer2_Init();//控制直流电机PWM
	IR_Init();//红外线遥控
	UART_Init();//串口初始化,语音模块
	while(1)
	{
		/****显示模块*****/
		KeyNum=MatrixKey();//矩阵键盘控制
		DS18B20_ConvertT();	//转换温度
		T=DS18B20_ReadT();	//读取温度
		if(T<0)				//如果温度小于0
		{
			LCD_ShowChar(1,7,'-');	//显示负号
			TShow=-T;		//将温度变为正数
		}
		else				//如果温度大于等于0
		{
			LCD_ShowChar(1,7,'+');	//显示正号
			TShow=T;
		}
		LCD_ShowNum(1,8,TShow,2);		//显示温度整数部分
		LCD_ShowChar(1,10,'.');		//显示小数点
		LCD_ShowNum(1,11,(unsigned long)(TShow*100)%100,2);//显示温度小数部分
		/*******语音控制模块*******/
		switch(receive_data)
			{
				case 'A':receive_data=0;Delay(300);Flag=1;break;//温度模式语音模块
				case 'B':receive_data=0;Delay(300);Flag=2;break;//正常模式语音模块
				case 'C':receive_data=0;Delay(300);Flag=3;break;//节能模式语音模块
				case 'D':receive_data=0;Delay(300);Flag=4;break;//温度上限+语音模块
				case 'E':receive_data=0;Delay(300);Flag=5;break;//温度上限-语音模块
				case 'F':receive_data=0;Delay(300);Flag=6;break;//温度下限+语音模块
				case 'G':receive_data=0;Delay(300);Flag=7;break;//温度下限-语音模块
				case 'H':receive_data=0;Delay(300);Flag=8;break;//风扇关闭语音模块
				case 'I':receive_data=0;Delay(300);Flag=9;break;//风扇1挡语音模块
				case 'J':receive_data=0;Delay(300);Flag=10;break;//风扇2挡语音模块
				case 'K':receive_data=0;Delay(300);Flag=11;break;//风扇3挡语音模块
			}
			/****红外线初始化******/
			if(IR_GetDataFlag() || KeyNum || Flag!=0)
			{
			Command=IR_GetCommand();		//获取遥控器命令码
			/******Mode1温度模式******/
			if(KeyNum==1 || Command==IR_Mode1 || Flag==1)//Mode1,温度模式
			{
				Mode=1;
				Flag=0;
				Delay(200);
				play_song_num(0x01);  
				LCD_ShowString(1,13,"    ");
				LCD_ShowString(1,1,"Tep ");//温度模式
				LCD_ShowString(1,5,"T:");//显示采集温度
				LCD_ShowString(2,1,"TH:");//显示温度上限
				LCD_ShowString(2,8,"TL:");//显示温度下限
				LCD_ShowString(2,7," ");
				LCD_ShowSignedNum(2,4,THigh,2);//显示上下限值
				LCD_ShowSignedNum(2,11,TLow,2);
				LCD_ShowString(2,14,"M:");
			}
			if(Mode==1)
			{	
				/***温度高位+***/
				if(KeyNum==5 || Command==IR_THADD || Flag==4)//+
				{
					Flag=0;
					THigh++;
					if(THigh>125){THigh=125;}//超过最高范围
				}
				/***温度高位-***/
				if(KeyNum==6 || Command==IR_THMINU || Flag==5)//-
				{
					Flag=0;
					THigh--;
					if(THigh<=TLow){THigh++;}
				}
				/***温度低位+***/
				if(KeyNum==7 || Command==IR_TLADD || Flag==6)//+
				{
					Flag=0;
					TLow++;
					if(TLow>=THigh){TLow--;}//超过最低温度
				}
				/***温度低位-***/
				if(KeyNum==8 || Command==IR_TLMINU || Flag==7)//-
				{
					Flag=0;
					TLow--;
					if(TLow<-55){TLow=-55;}
				}

				Temp1=THigh+2;//超过上限2度
				Temp2=THigh+4;//超过上限4度
				LCD_ShowNum(2,16,0,1);
				if(T>THigh && T<Temp1)			//越界判断
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,1,1);
					Compare=50;
				}
				else if(T>Temp1 && T<Temp2)
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,2,1);
					Compare=75;
				}
				else if(T>Temp2)
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,3,1);
					Compare=100;
				}
				/*else if(T<THigh)			//越界判断
				{
					LCD_ShowString(1,13,"    ");
					Compare=0;
				}*/
				else if(T<TLow)
				{
					LCD_ShowString(1,13,"  L");
					LCD_ShowNum(2,16,0,1);
					Compare=0;
				}
				else 
				{
					LCD_ShowString(1,15," ");
				}
				
				if(T>TLow && T<THigh)
				{
					LCD_ShowString(1,13,"  N ");
					LCD_ShowNum(2,16,1,1);
					Compare=50;
				}
				LCD_ShowSignedNum(2,4,THigh,2);	//显示阈值数据
				LCD_ShowSignedNum(2,11,TLow,2);
				AT24C02_WriteByte(0,THigh);		//写入到At24C02中保存
				Delay(5);
				AT24C02_WriteByte(1,TLow);
				Delay(5);
			}
			/*****正常模式Mode2*****/
			if(KeyNum==2 || Command==IR_Mode2 || Flag==2)//Mode2,正常模式
			{
				Compare=0;
				Flag=0;
				Mode=2;
				Delay(200);
				play_song_num(0x02);
				LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
				LCD_ShowString(2,1,"Exchange Mode   ");//
				LCD_ShowString(2,15,"  ");//
			}
			if(Mode==2)
			{
				if(KeyNum==5 || Command==IR_0 || Flag==8)
				{
					Flag=0;
					Delay(200);
					play_song_num(0x04);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=0;
					M=0;
				}					//设置比较值,改变PWM占空比
				if(KeyNum==6 || Command==IR_1 || Flag==9)
				{
					Flag=0;
					Delay(200);
					play_song_num(0x05);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=50;
					M=1;
				}
				if(KeyNum==7 || Command==IR_2 || Flag==10)				
				{
					Flag=0;
					Delay(200);
					play_song_num(0x06);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=75;
					M=2;
				}
				if(KeyNum==8 || Command==IR_3 || Flag==11)//3挡
				{
					Flag=0;
					Delay(200);
					play_song_num(0x07);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=100;
					M=3;
				}
				LCD_ShowNum(2,16,M,1);
			}
				if(KeyNum==3 || Command==IR_Mode3 || Flag==3)//Mode3,节能模式
			{
				Flag=0;
				Mode=3;
				Delay(200);
				play_song_num(0x03);
				LCD_ShowString(1,1,"ECO T:          ");
				LCD_ShowString(2,1,"People Detection");
			}
			
		}
			/****节能模式模式3****/
		
			if(Pep==1 && Mode==3)
			{
				LCD_ShowString(1,1,"ECO T:");
				LCD_ShowString(1,14,"YES");
				LCD_ShowString(2,1,"People Detection");
				Compare=75;
			}
			if(Pep==0 && Mode==3)
			{
				LCD_ShowString(1,1,"ECO T:");
				LCD_ShowString(1,14,"NO ");
				LCD_ShowString(2,1,"People Detection");
				Compare=0;
			}
		}
}

void UART_Routine() interrupt 4
{
	if(RI==1)					//如果接收标志位为1,接收到了数据
	{  
		RI=0; 
    receive_data = SBUF;  
	}
}

void Timer2_Routine() interrupt 5
{
	RCAP2L = 0x66;		//设置定时重载值
	RCAP2H = 0xFC;		//设置定时重载值
	TF2=0;
	Counter++;
	Counter%=100;	//计数值变化范围限制在0~99
	if(Counter<Compare)	//计数值小于比较值
	{
		Motor=1;		//输出1
	}
	else				//计数值大于比较值
	{
		Motor=0;		//输出0
	}
}

到了这里,关于基于单片机的智能风扇设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包