当前位置: 首页 > news >正文

第十五届蓝桥杯单片机组4T模拟赛三(第二套)

本套试题在4T平台中的名字为第15届蓝桥杯单片机组模拟考试三,不知道哪套是4T的模拟赛,所以两套都敲一遍练练手感。

为了代码呈现美观,本文章前面的各个模块在main函数中的处理函数均未添加退出处理,在最后给出的完整代码中体现。

附件:第15届蓝桥杯单片机组模拟考试三
在这里插入图片描述

一、准备工作

1.阅读系统框架

由系统框架图得知,等会创建工程时需要编写的底层模块有:

  • 初始化模块(Init.h,Init.c)
  • 指示灯模块(Led.h,Led.c)
  • 数码管模块(Seg.h,Seg.c)
  • 温度传感器模块(ds18b20.h,ds18b20.c)
  • 按键模块(key.h,key.c)
    这些底层函数在作者的专栏《蓝桥杯单片机模板搭建系列》中都详细地讲解了怎么去实现,如果读者有不懂的可以点击下面的链接。
    蓝桥杯单片机模板搭建系列

2.阅读题目了解题意

  • 按键只使用了S4、S5和S8,并且没有错误按键次数的判断,所以写按键时可以只写S4、S5和S8的。
  • 数码管显示字母用到PC的段码,在数码管底层函数记得添加。
  • 页面显示只有两个页面,所以为了更简单地实现页面流转,可以定义bit型变量SegMode控制页面流转(为0时对应信号界面,为1时对应温度界面)
  • 由于涉及NE555,所以需要使用定时器0去计数,定时器1去计时,同时屏蔽掉按键模块中对P34的赋值

二、数码管模块

1.Seg.h

#ifndef __Seg_H__
#define __Seg_H__

void SegDisp(unsigned char wela, unsigned char dula, unsigned char point);

#endif

2.Seg.c

#include <STC15F2K60S2.H>

code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0xff, //空
0x8c, //P
0xc6, //C
};

void SegDisp(unsigned char wela, unsigned char dula, unsigned char point)
{
	P0 = 0xff;
	P2 = P2 & 0x1f | 0xe0;
	P2 &= 0x1f;
	
	P0 = (0x01 << wela);
	P2 = P2 & 0x1f | 0xc0;
	P2 &= 0x1f;
	
	P0 = Seg_Table[dula];
	if(point)
		P0 &= 0x7f;
	P2 = P2 & 0x1f | 0xe0;
	P2 &= 0x1f;
}

3.NE555

NE555在底层搭建系列也已经讲过了,这边直接给出代码

void Timer0_Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0x7F;			//定时器时钟12T模式
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x05;			//设置定时器模式
	TL0 = 0;				//设置定时初始值
	TH0 = 0;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
}

void Timer1_Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0xBF;			//定时器时钟12T模式
	TMOD &= 0x0F;			//设置定时器模式
	TL1 = 0x18;				//设置定时初始值
	TH1 = 0xFC;				//设置定时初始值
	TF1 = 0;				//清除TF1标志
	TR1 = 1;				//定时器1开始计时
	ET1 = 1;				//使能定时器1中断
	EA = 1;
}

void Timer1_Isr(void) interrupt 3
{
	//systick++;调度器使用
	if(++SegPos == 8)
		SegPos = 0;
	SegDisp(SegPos,SegBuf[SegPos],SegPoint[SegPos]);
	if(++Time_1s == 1000)
	{
		Time_1s = 0;
		f = (TH0 << 8) | TL0;
		TH0 = TL0 = 0;
	}
}

4.main.c中的数码管处理函数SegProc

在这里插入图片描述

信号界面

用bit型变量SegMode来控制页面流转,当SegMode等于0时,对应显示界面为NE555频率测量,NE555的上限值大概在31000多,所以第一位和第二位数码管直接熄灭即可(最左边的数码管为第零位数码管),显示界面还需要进行高位为0熄灭的判断,这个也是很简单的,代码如下:

void SegProc()
{
	unsigned char i;
	if(!SegMode)
	{
		SegBuf[0] = 11;
		SegBuf[1] = 10;
		SegBuf[2] = 10;
		SegBuf[3] = f / 10000 % 10;
		SegBuf[4] = f / 1000 % 10;
		SegBuf[5] = f / 100 % 10;
		SegBuf[6] = f / 10 % 10;
		SegPoint[6] = 0;
		SegBuf[7] = f % 10;
		//高位熄灭处理
		i = 3;
		while(SegBuf[i] == 0)
		{
			SegBuf[i] = 10;
			if(++i == 7)
				break;
		}
	}
}

温度界面

ds18b20底层代码
#ifndef __ds18b20_H__
#define __ds18b20_H__

float TemRead();

#endif
#include <STC15F2K60S2.H>

sbit DQ = P1^4;

void Delay_OneWire(unsigned int t)  
{
	unsigned char i;
	while(t--){
		for(i=0;i<12;i++);
	}
}

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

//
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(5);
	}
	return dat;
}

//
bit init_ds18b20(void)
{
  	bit initflag = 0;
  	
  	DQ = 1;
  	Delay_OneWire(12);
  	DQ = 0;
  	Delay_OneWire(80);
  	DQ = 1;
  	Delay_OneWire(10); 
    initflag = DQ;     
  	Delay_OneWire(5);
  
  	return initflag;
}

float TemRead()
{
	idata unsigned char tem_low, tem_hig;
	
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0x44);
	
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0xbe);
	
	tem_low = Read_DS18B20();
	tem_hig = Read_DS18B20();
	
	return ((tem_hig << 8) | tem_low) / 16.0;
}

SegMode为1时,对应温度读取界面,由于作者个人习惯不喜欢使用float型变量,所以在这边使用的是unsigned int型变量Tem_10x(将温度放大10倍),这边也是给出两种写法。
写法一:定义float型变量

//取余变量声明、函数声明、头文件引用均省略
idata float Tem;

void SegProc()
{
	if(!SegMode)
	{
		//省略
	}
	else
	{
		SegBuf[0] = 12;
		SegBuf[1] = 10;
		SegBuf[2] = 10;
		SegBuf[3] = 10;
		SegBuf[4] = 10;
		SegBuf[5] = (unsigned char)Tem / 10;
		SegBuf[6] = (unsigned char)Tem % 10;
		SegPoint[6] = 1;
		SegBuf[7] = (unsigned char)(Tem * 10) % 10;
	}
}

void TemProc()
{
	Tem = TemRead();
}

写法二:定义unsigned int型变量

idata unsigned int Tem;

void SegProc()
{
	if(!SegMode)
	{
		//省略
	}
	else
	{
		SegBuf[0] = 12;
		SegBuf[1] = 10;
		SegBuf[2] = 10;
		SegBuf[3] = 10;
		SegBuf[4] = 10;
		SegBuf[5] = Tem_10x / 100;
		SegBuf[6] = Tem_10x / 10 % 10;
		SegPoint[6] = 1;
		SegBuf[7] = Tem_10x % 10;
	}
}

void TemProc()
{
	Tem_10x = TemRead() * 10;
}

采集频率

  • 在信号页面按下S5为频率数据采集,此时数码管显示周期。
  • 周期 = 1 频率 \frac{1}{频率} 频率1 * 10 6 ^6 6(1s=10 6 u s ^6us 6us
    定义bit型变量FreMode控制信号页面的数据显示
  • FreMode为0时,显示频率,为1时,显示周期。
  • 如果频率值为1时,周期为100 0000us,所以高位熄灭的判断要从数码管第一位开始判断。
void SegProc()
{
	unsigned char i;
	if(!SegMode)
	{
		SegBuf[0] = 11;
		if(!FreMode)
		{
			//频率显示页面
		}
		else
		{
			T = 1000000 / f;
			SegBuf[1] = T / 1000000 % 10;
			SegBuf[2] = T / 100000 % 10;
			SegBuf[3] = T / 10000 % 10;
			SegBuf[4] = T / 1000 % 10;
			SegBuf[5] = T / 100 % 10;
			SegBuf[6] = T / 10 % 10;
			SegPoint[6] = 0;
			SegBuf[7] = T % 10;
			//高位数据为0熄灭判断
			i = 1;
			while(SegBuf[i] == 0)
			{
				SegBuf[i] = 10;
				if(++i == 7)
					break;
			}
		}
	}
}

温度取整

  • 温度界面按下S5时将温度保留整数后在数码管中显示,这个时候应在数码管的低二位(最右边两个数码管)显示温度数据。
  • 同样的,定义bit型变量TemMode来控制温度显示页面的数据显示。
else//温度页面
	{
		SegBuf[0] = 12;
		SegBuf[1] = 10;
		SegBuf[2] = 10;
		SegBuf[3] = 10;
		SegBuf[4] = 10;
		/*
		if(!TemMode)
		{
			SegBuf[5] = Tem_10x / 100;
			SegBuf[6] = Tem_10x / 10 % 10;
			SegPoint[6] = 1;
			SegBuf[7] = Tem_10x % 10;
		}
		*/
		else//温度整数显示
		{
			SegBuf[5] = 10;
			SegBuf[6] = Tem_10x / 100;
			SegBuf[7] = Tem_10x / 10 % 10;
			SegPoint[6] = 0;
		}
	}

5.数码管模块完整代码

void SegProc()
{
	unsigned char i;
	if(!SegMode)
	{
		SegBuf[0] = 11;
		if(!FreMode)
		{
			SegBuf[1] = 10;
			SegBuf[2] = 10;
			SegBuf[3] = f / 10000 % 10;
			SegBuf[4] = f / 1000 % 10;
			SegBuf[5] = f / 100 % 10;
			SegBuf[6] = f / 10 % 10;
			SegPoint[6] = 0;
			SegBuf[7] = f % 10;
			i = 3;
			while(SegBuf[i] == 0)
			{
				SegBuf[i] = 10;
				if(++i == 7)
					break;
			}
		}
		else
		{
			T = 1000000 / f;
			SegBuf[1] = T / 1000000 % 10;
			SegBuf[2] = T / 100000 % 10;
			SegBuf[3] = T / 10000 % 10;
			SegBuf[4] = T / 1000 % 10;
			SegBuf[5] = T / 100 % 10;
			SegBuf[6] = T / 10 % 10;
			SegPoint[6] = 0;
			SegBuf[7] = T % 10;
			i = 1;
			while(SegBuf[i] == 0)
			{
				SegBuf[i] = 10;
				if(++i == 7)
					break;
			}
		}
	}
	
	else
	{
		SegBuf[0] = 12;
		SegBuf[1] = 10;
		SegBuf[2] = 10;
		SegBuf[3] = 10;
		SegBuf[4] = 10;
		if(!TemMode)
		{
			SegBuf[5] = Tem_10x / 100;
			SegBuf[6] = Tem_10x / 10 % 10;
			SegPoint[6] = 1;
			SegBuf[7] = Tem_10x % 10;
		}
		else
		{
			SegBuf[5] = 10;
			SegBuf[6] = Tem_10x / 100;
			SegBuf[7] = Tem_10x / 10 % 10;
			SegPoint[6] = 0;
		}
	}
}

三、按键模块

1.按键底层代码

#ifndef __Key_H__
#define __Key_H__

unsigned char KeyDisp();

#endif
#include <STC15F2K60S2.H>

unsigned char KeyDisp()
{
	unsigned char temp = 0x00;
	
	P44 = 0;
	P42 = 1;
	P35 = 1;
	if(P32 == 0) temp = 5;
	if(P33 == 0) temp = 4;
	
	P44 = 1;
	P42 = 0;
	P35 = 1;
	if(P32 == 0) temp = 9;
	
	return temp;
}

2.按键实现

在这里插入图片描述
按键模块特别简单,直接给出代码
这边注意的是,SegMode、FreMode、TemMode在前文中都已经定义成bit型变量,bit型变量是嵌入式系统设计中特有的变量,它的取值只有0和1,所以在按键S4、S5的变量取反可以使用下面两种方法实现:

SegMode = !SegMode;
SegMode ^= 1;

3.按键模块完整代码:

void KeyProc()
{
	KeyVal = KeyDisp();
	KeyDown = KeyVal & ~KeyOld;
	KeyDown = ~KeyVal & KeyOld;
	KeyOld = KeyVal;
	
	switch(KeyDown)
	{
		case 4:
			SegMode ^= 1;
		break;
		
		case 5:
			if(!SegMode)
			{
				/*在下文的Led模块实现
				if(!FreMode)
					CountFlag = 1;
				*/
				FreMode ^= 1;
			}
			else
				TemMode ^= 1;
		break;
			
		case 9:
			SegMode = 0;
			FreMode = 0;
			TemMode = 0;
		break;
	}
}

四、Led模块

1.Led底层代码

#ifndef __Led_H__
#define __Led_H__

void LedDisp(unsigned char *ucLed);

#endif
#include <STC15F2K60S2.H>

void LedDisp(unsigned char *ucLed)
{
	unsigned char i, temp = 0x00;
	static unsigned char temp_old = 0xff;
	
	for(i = 0; i < 8; i++)
		temp |= (ucLed[i] << i);
	
	if(temp != temp_old)
	{
		P0 = ~temp;
		P2 = P2 & 0x1f | 0x80;
		P2 &= 0x1f;
		temp_old = temp;
	}
}

在这里插入图片描述

2.L1、L2

L1、L2的点亮逻辑很简单
读者可以使用下面的代码也可以使用if-else语句或者三目运算符。

void LedProc()
{
	ucLed[0] = !SegMode;
	ucLed[1] = SegMode;
}

3.L3

  • L3的点亮逻辑是在信号页面激活采集功能后,L3点亮1s后熄灭,这边要注意的是,L3点亮是当采集功能激活后点亮的,而且1s后就要熄灭,没有进行其他操作时不能重复点亮。
  • 定时点亮、定时熄灭逻辑:定义时间变量以及指示灯标志位,时间变量在定时器内计时,当时间变量达到指定时间时,时间变量复位,指示灯标志位开启/关闭。
  • 本题实现方法有很多,下面给出我能想到的最简单的方法,如果读者有更简单的方法,欢迎评论区讨论。
/*有关于L3的变量定义*/
idata unsigned int Time_1s_Led;

/*
*CountFlag两个作用:
*1.CountFlag置1时,Time_1s_Led开始计时
*CountFlag置0时,Time_1s_Led复位
*2.ucLed[2] = CountFlag,即L3是否点亮决定于CountFlag
*/
idata bit CountFlag;

//CountFlag激活地
void KeyProc()
{
	switch(KeyDown)
	{
		case 5:
			if(!SegMode)
			{
				if(!FreMode)
					CountFlag = 1;//只有跳转到采集界面才激活CountFlag
				FreMode ^= 1;
			}
	}
}

//Led
void LedProc()
{
	ucLed[2] = CountFlag;
}

//定时器1中断服务函数
void Timer1_Isr(void) interrupt 3
{
	//其它省略
	
	if(CountFlag)//开灯计时
	{
		if(++Time_1s_Led == 1000)
		{
			Time_1s_Led = 0;//复位
			CountFlag = 0;//关灯
		}
	}
}

4.L4

  • L4的点亮逻辑是实时温度超过40°时L4以100ms为间隔闪烁
  • 指示灯闪烁的逻辑很简单,定义一个计时变量和一个Led点亮标志位,定时器中断中计时变量达到特定时间时,将Led点亮标志位取反,让ucLed[2] = Led点亮标志位,这样就可以实现指示灯闪烁
idata unsigned char Time_100ms;

idata bit LedFlah;//当实时温度>40°时,标志位置1
idata bit LedFlash;//L4使能标志位

void TemProc()
{
	Tem_10x = TemRead() * 10;
	LedFlag = (Tem_10x > 300);
}

void LedProc()
{
	ucLed[7] = LedFlash;
}

void Timer1_Isr(void) interrupt 3
{
	if(LedFlag)
	{
		if(++Time_100ms == 100)
		{
			Time_100ms = 0;
			LedFlash ^= 1;
		}
	}
	else
	{
		Time_100ms = 0;
		LedFlash = 0;
	}
}

五、完整代码

完整代码的思路是没有问题的,已经通过4T评测了,完整代码是根据以上代码片段拼凑起来的,如果读者用以下代码搭建的工程不能完整实现工程或者报错,请及时联系我。

#include <STC15F2K60S2.H>
#include "Init.h"
#include "Key.h"
#include "Seg.h"
#include "Led.h"
#include "ds18b20.h"

typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long int u32;

idata u8 Key_Slow; //按键减速变量 10ms 
idata u8 Seg_Slow; //数码管减速变量 500ms
idata u8 SegPos;
idata u8 KeyVal, KeyDown, KeyUp, KeyOld;
idata u8 Time_100ms;

pdata u8 SegBuf[8] = {10,10,10,10,10,10,10,10};
pdata u8 SegPoint[8] = {0,0,0,0,0,0,0,0};
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};

idata u16 Time_1s, f;
idata u16 Tem_10x;
idata u16 T;
idata u16 Time_1s_Led;

idata bit SegMode;
idata bit FreMode;
idata bit TemMode;
idata bit CountFlag;
idata bit LedFlag;
idata bit LedFlash;

void KeyProc()
{
	if(Key_Slow) return;
		Key_Slow = 1; //按键减速
	KeyVal = KeyDisp();
	KeyDown = KeyVal & ~KeyOld;
	KeyDown = ~KeyVal & KeyOld;
	KeyOld = KeyVal;
	
	switch(KeyDown)
	{
		case 4:
			SegMode ^= 1;
		break;
		
		case 5:
			if(!SegMode)
			{
				if(!FreMode)
					CountFlag = 1;
				FreMode ^= 1;
			}
			else
				TemMode ^= 1;
		break;
			
		case 9:
			SegMode = 0;
			FreMode = 0;
			TemMode = 0;
		break;
	}
}

void SegProc()
{
	unsigned char i;
	if(Seg_Slow) return;
	Seg_Slow = 1; //数码管减速
	
	Tem_10x = TemRead() * 10;
	LedFlag = (Tem_10x > 300);
	
	if(!SegMode)
	{
		SegBuf[0] = 11;
		if(!FreMode)
		{
			SegBuf[1] = 10;
			SegBuf[2] = 10;
			SegBuf[3] = f / 10000 % 10;
			SegBuf[4] = f / 1000 % 10;
			SegBuf[5] = f / 100 % 10;
			SegBuf[6] = f / 10 % 10;
			SegPoint[6] = 0;
			SegBuf[7] = f % 10;
			i = 3;
			while(SegBuf[i] == 0)
			{
				SegBuf[i] = 10;
				if(++i == 7)
					break;
			}
		}
		else
		{
			T = 1000000 / f;
			SegBuf[1] = T / 1000000 % 10;
			SegBuf[2] = T / 100000 % 10;
			SegBuf[3] = T / 10000 % 10;
			SegBuf[4] = T / 1000 % 10;
			SegBuf[5] = T / 100 % 10;
			SegBuf[6] = T / 10 % 10;
			SegPoint[6] = 0;
			SegBuf[7] = T % 10;
			i = 1;
			while(SegBuf[i] == 0)
			{
				SegBuf[i] = 10;
				if(++i == 7)
					break;
			}
		}
	}
	
	else
	{
		SegBuf[0] = 12;
		SegBuf[1] = 10;
		SegBuf[2] = 10;
		SegBuf[3] = 10;
		SegBuf[4] = 10;
		if(!TemMode)
		{
			SegBuf[5] = Tem_10x / 100;
			SegBuf[6] = Tem_10x / 10 % 10;
			SegPoint[6] = 1;
			SegBuf[7] = Tem_10x % 10;
		}
		else
		{
			SegBuf[5] = 10;
			SegBuf[6] = Tem_10x / 100;
			SegBuf[7] = Tem_10x / 10 % 10;
			SegPoint[6] = 0;
		}
	}
}

void LedProc()
{
	ucLed[0] = !SegMode;
	ucLed[1] = SegMode;
	ucLed[2] = CountFlag;
	ucLed[7] = LedFlash;
	LedDisp(ucLed);
}

void Timer0_Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0x7F;			//定时器时钟12T模式
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x05;			//设置定时器模式
	TL0 = 0;				//设置定时初始值
	TH0 = 0;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
}

void Timer1_Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0xBF;			//定时器时钟12T模式
	TMOD &= 0x0F;			//设置定时器模式
	TL1 = 0x18;				//设置定时初始值
	TH1 = 0xFC;				//设置定时初始值
	TF1 = 0;				//清除TF1标志
	TR1 = 1;				//定时器1开始计时
	ET1 = 1;				//使能定时器1中断
	EA = 1;
}

void Timer1_Isr(void) interrupt 3
{
	if(++Key_Slow == 10) Key_Slow = 0; //按键延迟
	if(++Seg_Slow == 200) Seg_Slow = 0; //数码管延迟
	if(++SegPos == 8) SegPos = 0;
	SegDisp(SegPos,SegBuf[SegPos],SegPoint[SegPos]);
	if(++Time_1s == 1000)
	{
		Time_1s = 0;
		f = (TH0 << 8) | TL0;
		TH0 = TL0 = 0;
	}
	if(CountFlag)
	{
		if(++Time_1s_Led == 1000)
		{
			Time_1s_Led = 0;
			CountFlag = 0;
		}
	}
	if(LedFlag)
	{
		if(++Time_100ms == 100)
		{
			Time_100ms = 0;
			LedFlash ^= 1;
		}
	}
	else
	{
		Time_100ms = 0;
		LedFlash = 0;
	}
}

void main()
{
	SystemInit();
	Timer0_Init();
	Timer1_Init();
	while(1)
	{
		KeyProc();
		SegProc();
		LedProc();
	}
}

相关文章:

  • 区县级水司一体化抄表营业收费系统设计思路
  • Datawhale 数学建模导论二 笔记5 多模数据与智能模型
  • Rk3568驱动开发_新字符设备驱动原理_7
  • sessioncookie 2.0(示例篇)
  • 数据结构与算法-图论-最短路和其他的结合
  • 学睿德毅AI 赋能教育拓展短视频创作边界
  • 基于Electron的应用程序安全测试基础 — 提取和分析.asar文件的案例研究
  • 【tplink】校园网接路由器如何单独登录自己的账号,wan-lan和lan-lan区别
  • 深入xtquant:Python量化交易的桥梁与核心
  • 网络安全虚拟化组成
  • 散户情绪周期模型(情绪影响操作)
  • 16.1 LangChain 表达式语言(LCEL)深度解析:构建灵活高效的大模型应用流水线
  • Nginx处理http的流程
  • 【AI+智造】基于SKF IMAX-16+PT1000与Odoo18工业物联网架构智慧生产诊断系统集成方案
  • Prompt Engineering for Large Language Models
  • 软件工程:软件需求之需求编写规则
  • 【六祎 - Note】Redis缓存设计模型,备忘录;
  • WPF-ReactiveUi
  • .bash_profile一些笔记
  • 在kubernetes集群中持续压测 SpringCloud 应用,pod 的 memory cache 持续增长问题
  • 网站制作的公司/提升关键词排名有哪些方法
  • 网站建设的社会/seo站内优化技巧
  • 网站客服系统在线/石家庄网络营销网站推广
  • 沭阳各乡镇做网站/今天热搜前十名
  • 上城网站建设/自己如何开网站
  • 有什么正网站做兼职的/windows优化大师要会员