STM32 AM2320 温湿度万年历 微信小程序显示及控制

功能描述:

使用STM32F103R8T6,红外遥控器,数码管,串口,预留ADC(4~20mA输入、0~10V输入)、485、以太网、WiFi、SD卡、USB_OTG等功能。单总线的方式采集温湿度(因整个系统时序要求,所以使用状态机采集),ST自带的RTC时钟。单片机采集到温湿度数值通过串口自制的4G模块通讯,上传数据到指定服务器,然后在微信小程序关注蓝星物联,就可以看到设备数据,我们还做了一个网站,专门管理底层设备。现在分享单片机程序和部分原理图。程序源码在一个资源里面,下载需要积分,没有积分的可以私信我。源码地址:https://download.csdn.net/download/L_e_c/14029632              

硬件设计:

1)电源及MCU部分

使用LM2596DC-DC,降压后放上一个LDO输出3.3V。预留SWD下载,纽扣电池,BOOT脚选择,红外接收头,温湿度模块,多预留一路时钟模块,防止内部RTC随温漂的误差。以及其他一些外设接口预留。

2)通讯及数据保存部分

预留485接口,232接口,USB接口,WiFi接口,以太网接口。通信接口只要做数据汇总、数据上传使用。数据保存有SD卡保存和flash保存两种,SD卡保存使用模块外接的形式,可以把采集到的数据直接保存成文件的形式。flash芯片可以保存需要的字库等等。

3)输入输出部分

预留ADC采集4~20mA及0~10V模拟信号。按键输入,不是全隔离,只是防止客户使用的时候会接错烧坏MCU引脚。继电器输出,可带12V声光报警器。

4)数码管显示部分

使用驱动芯片JXI5020级联,做到三线带多位数码管。

5)PCB展示

软件设计:

1)main.c

main函数进来,先是各种外设初始化,第一个while(1)的循环是发送消息给服务器或者上层设备注册设备用的,设备长时间不按照协议通讯的话,设备会软复位,重新注册。在第二个while(1)的循环是处理注册后的数据,包括温湿度和时间的显示,按键(红外遥控和轻触按键)设置,报警状态设置,设置定时,每1S钟发送一次实时数据到服务器,处理串口2接收到服务器发送的数据(JSON格式,使用CJSON库)。

/*********************************************************************************
 * 文件名  :main.c
 * 描述    :      
 * 实验平台:
 * 库版本  :ST3.5.0
 * 作者    : 
 * 论坛    :
 * 版本    ?VV1.0
 * 时间	   :2017.6.6
   备注    : STM32F103_R8T6 工程模版 
**********************************************************************************/

#include "Header_File.h"

u8 Send_Buff1[114];
u16   Crc_Send_Num=0;
u8 Crc_Send_H=0;
u8 Crc_Send_L=0;

u32 Set_MS_Num=0;


#define  SW_RESET()	       NVIC_SystemReset()

int processMessage(char *msg) {
	cJSON *jsonObj=cJSON_Parse(msg);
	cJSON *method;
	cJSON *typ;
//	cJSON *temmin;
//	cJSON *temmax;
//	cJSON *temcalibrate;
//	cJSON *tembkwd;
//	cJSON *hummin;
//	cJSON *hummax;
//	cJSON *humcalibrate;
//	cJSON *humbkwd;
    char *m;
	int x;
	
    
    //json字符串解析失败,直接退出
    if(!jsonObj)
    {
		cJSON_Delete(jsonObj);
        return 0;
    }
    method = cJSON_GetObjectItem(jsonObj, "TYPE");
    m = method->valuestring;
	if(strncmp(m, "AUTHERROR", 9) == 0 ) //设备未注册,重新注册
    {
		cJSON_Delete(jsonObj);
		SW_RESET();
        return 1;
//        char *content = cJSON_GetObjectItem(jsonObj, "DATA")->valuestring;	
//        if(strncmp(content, "No identity", 4) == 0)
//        {
//			
//        }
    }
    if(strncmp(m, "HSOK", 4) == 0 ) //注册信息返回
    {
        char *content = cJSON_GetObjectItem(jsonObj, "DATA")->valuestring;	
        if(strncmp(content, "HSOK", 4) == 0)
        {
			cJSON_Delete(jsonObj);
            return 1;
        }
    }
	if(strncmp(m, "DATAOK", 6) == 0 ) //
    {
        char *content = cJSON_GetObjectItem(jsonObj, "TYPE")->valuestring;	
        if(strncmp(content, "DATAOK", 6) == 0)
        {
			Resert_Cnt_10S=0; //重启时间清零
			cJSON_Delete(jsonObj);
            return 1;
        }
    }
	if(strncmp(m, "CDATA", 5) == 0 ) //
    {
		typ = cJSON_GetObjectItem(jsonObj, "ctype");
		x=typ->valueint;
//		if(strncmp(m, "1", 1) == 0 ) 
		if(x==1) 
		{
			int ye = cJSON_GetObjectItem(jsonObj, "y")->valueint;	
			int mo = cJSON_GetObjectItem(jsonObj, "m")->valueint;
			int da = cJSON_GetObjectItem(jsonObj, "d")->valueint;	
			int ho = cJSON_GetObjectItem(jsonObj, "h")->valueint;	
			int mi = cJSON_GetObjectItem(jsonObj, "i")->valueint;	
			int se = cJSON_GetObjectItem(jsonObj, "s")->valueint;	
			RTC_Set(ye,mo,da,ho,mi,se);
//			Set_MS_Num=atoi(ye);
		}
		else if(x==2) //
		{
			double temmin = cJSON_GetObjectItem(jsonObj, "tem_min")->valuedouble;
			double temmax = cJSON_GetObjectItem(jsonObj, "tem_max")->valuedouble;
			double temcalibrate = cJSON_GetObjectItem(jsonObj, "tem_calibrate")->valuedouble;
			double tembkwd = cJSON_GetObjectItem(jsonObj, "tem_bkwd")->valuedouble;
			double hummin = cJSON_GetObjectItem(jsonObj, "hum_min")->valuedouble;	
			double hummax = cJSON_GetObjectItem(jsonObj, "hum_max")->valuedouble;	
			double humcalibrate = cJSON_GetObjectItem(jsonObj, "hum_calibrate")->valuedouble;	
			double humbkwd = cJSON_GetObjectItem(jsonObj, "hum_bkwd")->valuedouble;	
//			Temperature_Down=atoi(temmin);
//			Temperature_Up=atoi(temmax);
			Temperature_Up=(s16)(temmax*10);
			Temperature_Down=(s16)(temmin*10);
			Temperature_Temp=(s16)(temcalibrate*10);
			Temperature_hc=(s16)(tembkwd*10);
			Humidity_Up=(s16)(hummax*10);
			Humidity_Down=(s16)(hummin*10);
			Humidity_Temp=(s16)(humcalibrate*10);
			Humidity_hc=(s16)(humbkwd*10);
		}
    }
	
//    if(jsonObj)
		cJSON_Delete(jsonObj);
    return 0;
}


int main(void)
{	
//    char* out="{\"TYPE\":\"fengxin\",\"passwd\":123,\"y\":2020,\"m\":6,\"d\":3,\"h\":23,\"minu\":33}"; //  ,\"miao\":20
	u8 ack_Usart1=0;
	u8 cclen=0;
	u16 Cnt_500S=0; 
	u16 Cnt_500Ms=0;
	SystemInit(); 			 	//系统时钟初始化为72M	  SYSCLK_FREQ_72MHz	
	TIM3_Cnt_Init(999,71);      //配置定时器3  1Ms中断一次 
	RTC_Init();	  				//RTC时钟初始化
	IO_Init(); 					//GPIO初始化
	Uart1_Init(115200);			//串口1初始化
//	Uart3_Init(9600);
	HC595_IO_Init();			//74HC595使用初始化
	Init_AOSONG();				//温湿度模块初始化
	Init_Infrared();			//红外模块初始化,开一个定时器和一个外部中断
//	Adc_Init();
	Key_IO();					//按键输入初始化 就是这么优秀
	Uart2_Init(115200);			//数据上传	
//	Interface_Changes=1;
	W25QXX_Init();				//W25QXX初始化
	FLASH_SIZE=32*1024*1024;	//FLASH 大小为xxM字节
	Read_Data_From_Flash();	
	Time_Num[8]=calendar.week%10;
	delay_ms(100);
	show_led12();
	while(1)	//注册设备信息,待服务器返回后退出
	{
		if(System1S==1)
		{
			System1S=0;
			cclen=sprintf((char *)Send_Server,"{\"TYPE\":\"HS\",\"CID\":\"WJVSAIL1\"}");
			Usart2_Printf((uint8_t *)Send_Server,cclen);
		}
		if(USART2_RX_STA == 1)
        {
            USART2_RX_STA = 0;
            ack_Usart1=processMessage((char*)Usart2_RxBuff);
			Clear_Buffer((uint8_t*)Usart2_RxBuff,strlen((char*)Usart2_RxBuff));
            rx_counter_Usart2 = 0;
			if(ack_Usart1==1)	break;
        }
	}
	while(1)
	{ 		
		show_led12();	//数码管显示
		Key_use();		//按键处理
		Data_Change();
		if(Display_Complete_Flag==1)
		{
			if(((Temp_001>Temperature_Up)||(Temp_001<Temperature_Down)||(Humi_001>Humidity_Up)||(Humi_001<Humidity_Down))&&(Mute_Flag==0)&&(Set_Position==0)&&(Key_Set_Position==0))
			{
				if(Temp_001>Temperature_Up)	Temperature_Up_Flag=1;
				if(Humi_001>Humidity_Up)	Humidity_Up_Flag=1;
				if(Temp_001<Temperature_Down)	Temperature_Down_Flag=1;
				if(Humi_001<Humidity_Down)	Humidity_Down_Flag=1;
				Out2=1;	
			}
			else if((Set_Position==0)&&(Key_Set_Position==0))	
			{
				if(Temp_001<=(Temperature_Up-Temperature_hc))	Temperature_Up_Flag=0;
				if(Temp_001>=(Temperature_Down+Temperature_hc))	Temperature_Down_Flag=0;
				if(Humi_001<=(Humidity_Up-Humidity_hc))			Humidity_Up_Flag=0;
				if(Humi_001>=(Humidity_Down+Humidity_hc))		Humidity_Down_Flag=0;
				if((Temperature_Up_Flag==0)&&(Temperature_Down_Flag==0)&&(Humidity_Up_Flag==0)&&(Humidity_Down_Flag==0))
					Out2=0;
				if(Mute_Flag==1)	Out2=0;
				
			}	
		}
		
		if((Set_Position==0)&&(Key_Set_Position==0))	
		{
			Time_Num[8]=calendar.week%10;
			if(Time_Num[8]==0)	Time_Num[8]=7;
		}
		if(System10Ms==1)
		{
			System10Ms=0;  
			if(++Cnt_500Ms>=50)
			{
				Cnt_500Ms=0;
				LED1=~LED1;
				if((Set_Position==0)&&(Key_Set_Position==0))
				{
					RTC_LED=~RTC_LED;
				}
				else 
					RTC_LED=0;
					
			}
		}	
		if(System1S==1)
		{
			System1S=0;
			Clear_Buffer((uint8_t*)Send_Server,strlen((char*)Send_Server));
			cclen=sprintf((char *)Send_Server,"{\"TYPE\":\"DATA\",\"Tmp\":\"%d.%01d\",\"Hum\":\"%d.%01d\"}",Temp_001/10,Temp_001%10,Humi_001/10,Humi_001%10); //,Temp_001/10,Temp_001%10,Humi_001/10,Humi_001%10
			Usart2_Printf((uint8_t *)Send_Server,cclen);
			if(++Resert_Cnt_10S>=20) //10s后没有接到来自服务器的数据进行软重启,再次注册
			{
				Resert_Cnt_10S=0;
				SW_RESET();
			}
		}	
		if(USART2_RX_STA == 1)
		{
			USART2_RX_STA = 0;
			processMessage((char*)Usart2_RxBuff);
			Clear_Buffer((uint8_t*)Usart2_RxBuff,strlen((char*)Usart2_RxBuff));
			rx_counter_Usart2 = 0;
		}
	}
}

2)Infrared.c

红外接收模块处理文件。在主函数里面只需要直接访问InfraredCode变量就可以知道是否有按键按下。开启一个定时器和一个外部中断,检测接收头(HS0038B)是否收到遥控器的信号,并按照固定的格式给信号做一个处理。识别对应的按钮按下对应的码值,做一个赋值,供主函数读取使用。

#include "infrared.h"
//#include "Timer2_Cnt.h"

u8 timercnt_up=0;
u8 timercnt_down=0;
u8 Infraredbuf[4];
u8 InfraredCode=0;
// 红外遥控器 编码格式
// START + 8Bit厂商编码 + 8Bit反码 + 8Bit数据 + 8Bit反码

// START L=9.00ms   H=4.500ms  引导码
// BIT0  L=0.56ms   H=0.565ms  数据0
// BIT1  L=0.56ms   H=1.690ms  数据1
// PRESS L=9.00ms   H=2.250ms  重复码


u8 irdata[33];
u8 irok;   
u16  irtime;
static u8 ir_flag;     //是否开始处理标志位

void TIM2_Cnt_Init(u16 arr,u16 psc)	 // TIM2_Cnt_Init(999,71);  1ms中断一次   
{
		TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 
		NVIC_InitTypeDef NVIC_InitStructure; 
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);         //①时钟TIM2使能 
			 
		//定时器TIM2初始化 
		TIM_TimeBaseStructure.TIM_Period = arr;                      //设置自动重装载寄存器周期的值   
		TIM_TimeBaseStructure.TIM_Prescaler =psc;                    //设置时钟频率除数的预分频值 
		TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;      //设置时钟分割 
		TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数 
		TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);              //②初始化TIM2   
		TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE );                    //③允许更新中断   
	 
		//中断优先级NVIC设置 
		NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;              //TIM2中断 
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;    //抢占优先级1级 
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;           //从优先级0级 
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              //IRQ 通道被使能 
		NVIC_Init(&NVIC_InitStructure);                              //④初始化NVIC寄存器 
	 	TIM_Cmd(TIM2, ENABLE);                                       //⑤使能TIM2 
}	

//定时器2中断服务程序 
void TIM2_IRQHandler(void)      //TIM2中断 
{ 
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)               //检查TIM2更新中断发生与否   
    { 
	    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);             	 //清除TIM2更新中断标志   
		irtime++; 			//用于计数2个下降沿之间的时间,0.1ms进一次定时器中断
		if(irtime>=1000)	//irtime>=1000说明按键已经松开有1000*0.1ms=100ms了,故可以使能红外接收功能
		{
			ir_flag=1;		//使能红外接收功能
		}
//		if(INIR) timercnt_up++;	
//		else 	 timercnt_down++;
//	    System1Ms=1;
//		if(++i>=10)
//		{
//			i=0;
//			System10Ms=1;
//		}
    } 
}

void EXTIX_Init(void)
{
 	EXTI_InitTypeDef EXTI_InitStructure;
 	NVIC_InitTypeDef NVIC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;

  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能PORTA,PORTC时钟

	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭jtag,使能SWD,可以用SWD模式调试
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_4;//PC5
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
 	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOC5
 
	
    //GPIOC.1 中断线以及中断初始化配置
  	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource4);

  	EXTI_InitStructure.EXTI_Line=EXTI_Line4;
  	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
  	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
  	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  	EXTI_Init(&EXTI_InitStructure);	 	//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

 
  	NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;			//使能按键所在的外部中断通道
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;	//抢占优先级2 
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;					//子优先级1
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;								//使能外部中断通道
  	NVIC_Init(&NVIC_InitStructure);  	  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
		
}


void EXTI4_IRQHandler(void)
{
	static u8  RecvCnt;          //接收红外信号处理
	u8 a,b,c,d,i,e,f,tmp;
	u32 RecvData = 0;
	u16  VS1838_KEY_VALUE;
	
	if(EXTI_GetITStatus(EXTI_Line4)!=RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line4);  //清除EXTI0线路挂起位
		if(ir_flag==0)
		{
			irtime=0;                           //清零计数器
			return;
		}	
		else if(ir_flag==1)
		{
			if((irtime>IR_START) && (irtime<(IR_START + DataTolerance_IR)))	//接收到引导码 9ms+4.5ms
			{
				RecvCnt=0;
			}
			irdata[RecvCnt]=irtime;	//存储每个电平的持续时间,用于以后判断是0还是1
			irtime=0;		//清零irtime变量
			RecvCnt++;		//数组下标自增1
			if(RecvCnt>=33)
			{
				irok=1;		//接收完33个红外数据
				RecvCnt=0;	//数组下标清零
				ir_flag=0;	//失能红外接收功能
				VS1838_KEY_VALUE=0;

				for(i=1;i<33;i++){
					if((irdata[i] > IR_BIT1) && (irdata[i] < (IR_BIT1 + DataTolerance_IR))){   		// BIT1
						RecvData <<= 1;
						RecvData  |= 1;
					}else if((irdata[i] > IR_BIT0) && (irdata[i] < (IR_BIT0 + DataTolerance_IR))){ 	// BIT0
						RecvData <<= 1;
					}
				}

				a=(RecvData>>24)&0xFF;
				b=(RecvData>>16)&0xFF;
				c=(RecvData>>8)&0xFF;
				d= RecvData&0xFF;
				e=~b;
				f=~d;
				if((a==e)&&(c==f))
				{
					VS1838_KEY_VALUE=(a<<8)|c;
				}
			
				switch(VS1838_KEY_VALUE){
					case 0x0098:InfraredCode=Infrared_0;break;	// 按键 0
					case 0x00A2:InfraredCode=Infrared_1;break;	// 按键 1
					case 0x0062:InfraredCode=Infrared_2;break;	// 按键 2
					case 0x00E2:InfraredCode=Infrared_3;break;	// 按键 3
					case 0x0022:InfraredCode=Infrared_4;break;	// 按键 4
					case 0x0002:InfraredCode=Infrared_5;break;	// 按键 5
					case 0x00C2:InfraredCode=Infrared_6;break;	// 按键 6
					case 0x00E0:InfraredCode=Infrared_7;break;	// 按键 7
					case 0x00A8:InfraredCode=Infrared_8;break;	// 按键 8
					case 0x0090:InfraredCode=Infrared_9;break;	// 按键 9
					case 0x0068:InfraredCode=Infrared_Xing;break;	// 按键 *
					case 0x00B0:InfraredCode=Infrared_Jing;break;	// 按键 #
					case 0x0010:InfraredCode=Infrared_Left;break;	// 按键 <
					case 0x005A:InfraredCode=Infrared_Right;break;	// 按键 >
					case 0x0018:InfraredCode=Infrared_Up;break;	// 按键 ^
					case 0x004A:InfraredCode=Infrared_Down;break;	// 按键 v
					case 0x0038:InfraredCode=Infrared_OK;break;	// 按键 OK
					default: tmp=0;break;	// NO_KEY	
				}
			}
		}		
		
		
//		irok=0;	
	}
}

/* 红外解码,返回按键键值 */
void HS0038B_DeCode(void)
{
//	u8 a,b,c,d,i,tmp;
//	u32 RecvData = 0;
//	u16  VS1838_KEY_VALUE;
//	
//	if(irok){
//		irok=0;
//		VS1838_KEY_VALUE=0;

//		for(i=1;i<33;i++){
//			if((irdata[i] > IR_BIT1) && (irdata[i] < (IR_BIT1 + DataTolerance_IR))){   		// BIT1
//		        RecvData <<= 1;
//		        RecvData  |= 1;
//		    }else if((irdata[i] > IR_BIT0) && (irdata[i] < (IR_BIT0 + DataTolerance_IR))){ 	// BIT0
//		        RecvData <<= 1;
//		    }
//		}

//		a=(RecvData>>24)&0xFF;
//		b=(RecvData>>16)&0xFF;
//		c=(RecvData>>8)&0xFF;
//		d= RecvData&0xFF;
//		if((a==~b)&&(c==~d))
//			VS1838_KEY_VALUE=(a<<8)|c;
//	
//		switch(VS1838_KEY_VALUE){
//			case 0x0098:InfraredCode=Infrared_0;break;	// 按键 0
//			case 0x00A2:InfraredCode=Infrared_1;break;	// 按键 1
//			case 0x0062:InfraredCode=Infrared_2;break;	// 按键 2
//			case 0x00E2:InfraredCode=Infrared_3;break;	// 按键 3
//			case 0x0022:InfraredCode=Infrared_4;break;	// 按键 4
//			case 0x0002:InfraredCode=Infrared_5;break;	// 按键 5
//			case 0x00C2:InfraredCode=Infrared_6;break;	// 按键 6
//			case 0x00E0:InfraredCode=Infrared_7;break;	// 按键 7
//			case 0x00A8:InfraredCode=Infrared_8;break;	// 按键 8
//			case 0x0090:InfraredCode=Infrared_9;break;	// 按键 9
//			case 0x0068:InfraredCode=Infrared_Xing;break;	// 按键 *
//			case 0x00B0:InfraredCode=Infrared_Jing;break;	// 按键 #
//			case 0x0010:InfraredCode=Infrared_Left;break;	// 按键 <
//			case 0x005A:InfraredCode=Infrared_Right;break;	// 按键 >
//			case 0x0018:InfraredCode=Infrared_Up;break;	// 按键 ^
//			case 0x004A:InfraredCode=Infrared_Down;break;	// 按键 v
//			case 0x0038:InfraredCode=Infrared_OK;break;	// 按键 OK
//			default: tmp=0;break;	// NO_KEY	
//		}
		return tmp;
//	}							   
//	return 0;	
}


void Init_Infrared(void)
{
	TIM2_Cnt_Init(99,71);       //100us
	EXTIX_Init();
	
}



3)其他

具体使用到的可以看下面的工程的目录,下面有源码分享的链接。

协议及调试效果

1)硬件实物图

2)微信小程序检测界面

3)演示视频

感兴趣的朋友可以关注一下抖音账号,分享了一些做的东西的视频在上面,不是太多

总结

博客就只是分享了原理图和部分程序,一些注释也不全。有需要的可以下载源码。底层的设备都是做好和服务器直接对接的,要是连接服务器的话可以直接使用。或者加上中转设备,有WiFi,以太网或者4G。协议也是有的,因最近比较忙,等我回苏之后贴出来。谢谢阅览。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值