STM32L151与AHT20通过IIC通讯

STM32L151与AHT20通过IIC通讯

前言

最近在将stm32f103的一个项目移植到l151上,发现AHT20的IIC通讯代码跑不通了,经过一晚上修改后,代码能成功运行并通过串口打印出温湿度。

硬件接线

使用了软件IIC的方式,用软件控制管脚状态以模拟IIC通信波形。
AHT20的VCC,,GND,SCL,SDA分别对应接stm32L151C8T6的5V,GND,B6,B7。

代码实现

首先是主函数:
功能是把AHT采集到的温湿度数据转换为整数,然后通过串口1输出。
main.c:

#include "main.h"
#include "usart.h"
#include "delay.h"
#include "systempz.h"
#include "string.h"
#include "stdio.h"

#include "AHT20.h"

static __IO uint32_t TimingDelay;

struct m_AHT20 AHT20;
u8 temp,humi;

int main(void)
{
	

    uart1_init(115200);

    delay_init();

		
		/*******************************传感器初始化*****************/

		IIC_Init();							//IIC管脚初始化
	  AHT20.alive=!AHT20_Init();	        //AHT20初始化
		printf("初始化结果:%d",AHT20.alive);
		
    while (1)
    {
			
				
				//2.测温湿度
			   
		  if(AHT20.alive)// 如果AHT20传感器存在,则读取温湿度数据
		  {
		   	//读取AHT20的 20Bit原始数据
		   	AHT20.flag = AHT20_ReadHT(AHT20.HT);
				
				
		   	//实际标准单位转换
		   	StandardUnitCon(&AHT20);
				temp =(int)AHT20.Temp;
				humi =(int)AHT20.RH;
				
				printf("温度为:%d",temp);
				printf("湿度·为:%d\r\n",humi);
				
				
					}
                Delay(1000);
				Delay(1000);
				Delay(1000);

    }
}


/**
  * @brief  Decrements the TimingDelay variable.
  * @param  None
  * @retval None
  */
void TimingDelay_Decrement(void)
{
    if (TimingDelay != 0x00)
    {
        TimingDelay--;
    }
}
#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{ 
    /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

    /* Infinite loop */
    while (1)
    {
    }
}
#endif

IIC通讯部分程序移植后需要改动的地方较大
首先,在f103中,模拟IIC的接口可以直接设置为推挽输出GPIO_Mode_Out_PP,而l151不行,在IO口的初始化上有所不同,需要分步配置

//初始化IIC
void IIC_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);	 	
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;        //输出
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6|GPIO_Pin_7;        //输出电平引脚
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  //推挽
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);	//PB6,PB7 输出高
}

其次,IIC通讯需要让SDA引脚的IO模式从输出到输入来回切换,在原先的程序中使用的是操作CRL寄存器的方式,而在L151上没有CRL这个寄存器(可能也是我没找到。)所以采用了土办法去配置IO口。
操作寄存器的方式:

 //IO方向设置
#define SDA_IN()  {GPIOC->CRL&=0X0FFFFFFF;GPIOC->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOC->CRL&=0X0FFFFFFF;GPIOC->CRL|=(u32)3<<28;}

土办法:

 //IO方向设置
void SDA_OUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Pin =  IIC_SDA;        //输出电平引脚
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  //推挽
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

 //IO方向设置
void SDA_IN(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Pin =  IIC_SDA;        //输出电平引脚
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  //推挽
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;    //上拉
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

IIC完整代码如下:
myiic.c

#include "myiic.h"
#include "delay.h"

//IIC延迟函数
void I2C_delay(void) 
{ 
   uint16_t i=160; //Set delay time value
   while(i)  
   {  
     i--;  
   }  
}  
//初始化IIC
void IIC_Init(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;
	  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);	 	
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6|GPIO_Pin_7;        //输出电平引脚
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  //推挽
	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
	  GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);	//PB6,PB7 输出高
}

 //IO方向设置
void SDA_OUT(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Pin =  IIC_SDA;        //输出电平引脚
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  //推挽
	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

 //IO方向设置
void SDA_IN(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Pin =  IIC_SDA;        //输出电平引脚
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  //推挽
		  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;    //上拉
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}



//产生IIC起始信号
void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	 
  IIC_SDA_H;
	IIC_SCL_H;
	I2C_delay();
 	IIC_SDA_L;//START:when CLK is high,DATA change form high to low 
	I2C_delay();
	IIC_SCL_L;//钳住I2C总线,准备发送或接收数据 
}	  
//产生IIC停止信号
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL_L;
	IIC_SDA_L;//STOP:when CLK is high DATA change form low to high
	I2C_delay();
	IIC_SCL_H; 
	IIC_SDA_H;//发送I2C总线结束信号
	I2C_delay();							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	IIC_SDA_H;	I2C_delay();
	IIC_SCL_H;	I2C_delay(); 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL_L;//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL_L;
	SDA_OUT();
	IIC_SDA_L;
	I2C_delay();
	IIC_SCL_H;
	I2C_delay();
	IIC_SCL_H;
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	IIC_SCL_L;
	SDA_OUT();
	IIC_SDA_H;
	I2C_delay();
	IIC_SCL_H;
	I2C_delay();
	IIC_SCL_L;
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{                        
	uint8_t i=8;
	
	SDA_OUT();

  while(i--) 
  { 	
		IIC_SCL_L;
	I2C_delay();
    if((txd&0x80))
		{
     IIC_SDA_H; 
		}			
    else
		{			
     IIC_SDA_L; 
		}			
    txd<<=1; 
	I2C_delay();
		IIC_SCL_H; 
	I2C_delay();
		
  } 
	IIC_SCL_L; 	 
		
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL_L; 
	      I2C_delay();
	    	IIC_SCL_H;
        receive<<=1;
        if(READ_SDA)receive++;   
     	I2C_delay(); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}
 
 
 
/* 对器件读写的封装,从机器件地址为1字节 */
 
 
/**
  * @brief  向I2C设备连续写数据(适用于符合IIC通信协议的寄存器地址为uint8类型的器件)
  * @param  addr:I2C从机器件地址
  * @param  reg: I2C从机寄存器地址
  * @param  len: 写入长度
  * @param  buf: uint8数据数组
  * @retval 0,正常; 其他,错误代码;
  */
uint8_t Soft_I2C_Write(uint8_t dev_addr, uint8_t reg_addr, uint8_t len, unsigned char *data_buf)
{
    uint8_t i;
	
	IIC_Start();
	IIC_Send_Byte(dev_addr << 1 | I2C_Direction_Transmitter);//发送器件地址+写命令
	if(IIC_Wait_Ack())//等待应答
	{
		IIC_Stop();
		return 1;
	}
	
	IIC_Send_Byte(reg_addr);//写寄存器地址
    IIC_Wait_Ack();//等待应答
	
	for(i=0;i<len;i++)
	{
		IIC_Send_Byte(data_buf[i]);//发送数据
		if(IIC_Wait_Ack())//等待ACK
		{
			IIC_Stop();
			return 1;
		}
	}
    IIC_Stop();
	return 0;
}
 
/**
  * @brief  从I2C设备连续读数据(适用于符合IIC通信协议的寄存器地址为uint8类型的器件)
  * @param  addr:I2C从机器件地址
  * @param  reg: I2C从机寄存器地址
  * @param  len: 读出长度
  * @param  buf: uint8数据数组
  * @retval 0,正常; 其他,错误代码;
  */
uint8_t Soft_I2C_Read(uint8_t dev_addr, uint8_t reg_addr, uint8_t len, unsigned char *data_buf)
{
	uint8_t result;
	
	IIC_Start();
	IIC_Send_Byte(dev_addr << 1 | I2C_Direction_Transmitter);//发送器件地址+写命令
	if(IIC_Wait_Ack())//等待应答
	{
		IIC_Stop();
		return 1;
	}
	
	IIC_Send_Byte(reg_addr);//写寄存器地址
    IIC_Wait_Ack();//等待应答
	
	IIC_Start();
	IIC_Send_Byte(dev_addr << 1 | I2C_Direction_Receiver);//发送器件地址+读命令
	IIC_Wait_Ack();//等待应答
	
    while(len)
	{
		if(len==1)*data_buf=IIC_Read_Byte(0);//读数据,发送nACK
		else *data_buf=IIC_Read_Byte(1);//读数据,发送ACK
		len--;
		data_buf++;
	}
    IIC_Stop();//产生一个停止条件
	return 0;
}

myiic.h

#ifndef __MYIIC_H
#define __MYIIC_H
#include "delay.h"

//IIC接口
#define IIC_SCL GPIO_Pin_6
#define IIC_SDA GPIO_Pin_7
//IO方向设置
 


//IO操作函数	 
 
#define READ_SDA       GPIO_ReadInputDataBit(GPIOB, IIC_SDA)

#define IIC_SCL_L     GPIO_ResetBits(GPIOB,IIC_SCL);
#define IIC_SCL_H     GPIO_SetBits(GPIOB,IIC_SCL); 
#define IIC_SDA_L     GPIO_ResetBits(GPIOB,IIC_SDA);
#define IIC_SDA_H     GPIO_SetBits(GPIOB,IIC_SDA);


//IIC所有操作函数
void SDA_OUT(void);             //IO方向设置
void SDA_IN(void);              //IO方向设置

void I2C_delay(void);           //IIC延迟函数
void IIC_Init(void);                //初始化IIC的IO口				 
void IIC_Start(void);				//发送IIC开始信号
void IIC_Stop(void);	  			//发送IIC停止信号
void IIC_Send_Byte(u8 txd);			//IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); 				//IIC等待ACK信号
void IIC_Ack(void);					//IIC发送ACK信号
void IIC_NAck(void);				//IIC不发送ACK信号

uint8_t Soft_I2C_Write(uint8_t dev_addr, uint8_t reg_addr, uint8_t len, unsigned char *data_buf);
uint8_t Soft_I2C_Read(uint8_t dev_addr, uint8_t reg_addr, uint8_t len, unsigned char *data_buf);

#endif

AHT20这部分的程序移植到l151后不用修改
AHT20.C程序:

#include "AHT20.h"
 
/**
  * @brief  读AHT20 设备状态字
  * @param  void
  * @retval uint8_t 设备状态字
  */
static uint8_t AHT20_ReadStatusCmd(void)
{
	uint8_t tmp[1];
	Soft_I2C_Read(ATH20_SLAVE_ADDRESS, AHT20_STATUS_REG, 1, tmp);
	return tmp[0];
}
 
/**
  * @brief  读AHT20 设备状态字 中的Bit3: 校准使能位
  * @param  void
  * @retval uint8_t 校准使能位:1 - 已校准; 0 - 未校准
  */
static uint8_t AHT20_ReadCalEnableCmd(void)
{
	uint8_t tmp;
	tmp = AHT20_ReadStatusCmd();
	return (tmp>>3)&0x01;
}
 
/**
  * @brief  读AHT20 设备状态字 中的Bit7: 忙标志
  * @param  void
  * @retval uint8_t 忙标志:1 - 设备忙; 0 - 设备空闲
  */
static uint8_t AHT20_ReadBusyCmd(void)
{
	uint8_t tmp;
	tmp = AHT20_ReadStatusCmd();
	return (tmp>>7)&0x01;
}
 
/**
  * @brief  AHT20 芯片初始化命令
  * @param  void
  * @retval void
  */
static void AHT20_IcInitCmd(void)
{
	uint8_t tmp[2];
	tmp[0] = 0x08;
	tmp[1] = 0x00;
	Soft_I2C_Write(ATH20_SLAVE_ADDRESS, AHT20_INIT_REG, 2, tmp);
}
 
/**
  * @brief  AHT20 触发测量命令
  * @param  void
  * @retval void
  */
static void AHT20_TrigMeasureCmd(void)
{
	uint8_t tmp[2];
	tmp[0] = 0x33;
	tmp[1] = 0x00;
	Soft_I2C_Write(ATH20_SLAVE_ADDRESS, AHT20_TrigMeasure_REG, 2, tmp);
}
 
/**
  * @brief  AHT20 软复位命令
  * @param  void
  * @retval void
  */
static void AHT20_SoftResetCmd(void)
{
	uint8_t tmp[1];
	Soft_I2C_Write(ATH20_SLAVE_ADDRESS, AHT20_SoftReset, 0, tmp);
}
 
/**
  * @brief  AHT20 设备初始化
  * @param  void
  * @retval uint8_t:0 - 初始化AHT20设备成功; 1 - 初始化AHT20失败,可能设备不存在或器件已损坏
  */
uint8_t AHT20_Init(void)
{
	uint8_t rcnt = 2+1;//软复位命令 重试次数,2次
	uint8_t icnt = 2+1;//初始化命令 重试次数,2次
	
	while(--rcnt)
	{
		icnt = 2+1;
		
		delay_ms(40);//上电后要等待40ms
		// 读取温湿度之前,首先检查[校准使能位]是否为1
		while((!AHT20_ReadCalEnableCmd()) && (--icnt))// 2次重试机会
		{
			delay_ms(10);
			// 如果不为1,要发送初始化命令
			AHT20_IcInitCmd();
			delay_ms(200);//这个时间不确定,手册没讲
		}
		
		if(icnt)//[校准使能位]为1,校准正常
		{
			break;//退出rcnt循环
		}
		else//[校准使能位]为0,校准错误
		{
			AHT20_SoftResetCmd();//软复位AHT20器件,重试
			delay_ms(200);//这个时间不确定,手册没讲
		}
	}
	
	if(rcnt)
	{
		delay_ms(200);//这个时间不确定,手册没讲
		return 0;// AHT20设备初始化正常
	}
	else
	{
		return 1;// AHT20设备初始化失败
	}
}
 
/**
  * @brief  AHT20 设备读取 相对湿度和温度(原始数据20Bit)
  * @param  uint32_t *HT:存储20Bit原始数据的uint32数组
  * @retval uint8_t:0-读取数据正常; 1-读取设备失败,设备一直处于忙状态,不能获取数据
  */
uint8_t AHT20_ReadHT(uint32_t *HT)
{
	uint8_t cnt=3+1;//忙标志 重试次数,3次
	uint8_t tmp[6];
	uint32_t RetuData = 0;
	
	// 发送触发测量命令
	AHT20_TrigMeasureCmd();
	
	do{
		delay_ms(75);//等待75ms待测量完成,忙标志Bit7为0
	}while(AHT20_ReadBusyCmd() && (--cnt));//重试3次
	
	if(cnt)//设备闲,可以读温湿度数据
	{
		delay_ms(5);
		// 读温湿度数据
		Soft_I2C_Read(ATH20_SLAVE_ADDRESS, AHT20_STATUS_REG, 6, tmp);
		// 计算相对湿度RH。原始值,未计算为标准单位%。
		RetuData = 0;
		RetuData = (RetuData|tmp[1]) << 8;
		RetuData = (RetuData|tmp[2]) << 8;
		RetuData = (RetuData|tmp[3]);
		RetuData = RetuData >> 4;
		HT[0] = RetuData;
		
		// 计算温度T。原始值,未计算为标准单位°C。
		RetuData = 0;
		RetuData = (RetuData|tmp[3]) << 8;
		RetuData = (RetuData|tmp[4]) << 8;
		RetuData = (RetuData|tmp[5]);
		RetuData = RetuData&0xfffff;
		HT[1] = RetuData;
		
		return 0;
	}
	else//设备忙,返回读取失败
	{
		return 1;
	}
}
 
/**
  * @brief  AHT20 温湿度信号转换(由20Bit原始数据,转换为标准单位RH=%,T=°C)
  * @param  struct m_AHT20* aht:存储AHT20传感器信息的结构体
  * @retval uint8_t:0-计算数据正常; 1-计算数据失败,计算值超出元件手册规格范围
  */
uint8_t StandardUnitCon(struct m_AHT20* aht)
{
	aht->RH = (double)aht->HT[0] *100 / 1048576;//2^20=1048576 //原式:(double)aht->HT[0] / 1048576 *100,为了浮点精度改为现在的
	aht->Temp = (double)aht->HT[1] *200 / 1048576 -50;
	
	//限幅,RH=0~100%; Temp=-40~85°C
	if((aht->RH >=0)&&(aht->RH <=100) && (aht->Temp >=-40)&&(aht->Temp <=85))
	{
		aht->flag = 0;
		return 0;//测量数据正常
	}
	else
	{
		aht->flag = 1;
		return 1;//测量数据超出范围,错误
	}
}

AHT20.h程序:

#ifndef __AHT20_H
#define __AHT20_H

#include "myiic.h"
#include "delay.h"
 
#define ATH20_SLAVE_ADDRESS		0x38		/* I2C从机地址 */
 
//****************************************
// 定义 AHT20 内部地址
//****************************************
#define	AHT20_STATUS_REG		0x00	//状态字 寄存器地址
#define	AHT20_INIT_REG			0xBE	//初始化 寄存器地址
#define	AHT20_SoftReset			0xBA	//软复位 单指令
#define	AHT20_TrigMeasure_REG	0xAC	//触发测量 寄存器地址
 
// 存储AHT20传感器信息的结构体
struct m_AHT20
{
	uint8_t alive;	// 0-器件不存在; 1-器件存在
	uint8_t flag;	// 读取/计算错误标志位。0-读取/计算数据正常; 1-读取/计算设备失败
	uint32_t HT[2];	// 湿度、温度 原始传感器的值,20Bit
	
	float RH;		// 湿度,转换单位后的实际值,标准单位%
	float Temp;		// 温度,转换单位后的实际值,标准单位°C
};
 
 
uint8_t AHT20_Init(void);
uint8_t AHT20_ReadHT(uint32_t *HT);
uint8_t StandardUnitCon(struct m_AHT20* aht);
 
#endif

delay.c:

#include "delay.h"

static unsigned char  fac_us=0;//us使用
static u16 fac_ms=0;//ms使用
void delay_init()	 
{

    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//选择外部时钟  HCLK/8
    fac_us=SystemCoreClock/8000000;	//为系统时钟的1/8  4
    fac_ms=(u16)fac_us*1000;
}								    

void delay_ms(u16 nms)
{	 		  	  
    uint32_t ui_tmp = 0x00;
    SysTick->LOAD = nms * fac_ms;  
    SysTick->VAL = 0x00;  
    SysTick->CTRL = 0x01;  

    do  
    {  
        ui_tmp = SysTick->CTRL;  
    }while((ui_tmp & 0x01) && (!(ui_tmp & (1 << 16))));  

    SysTick->CTRL = 0x00;  
    SysTick->VAL = 0x00;      
} 

void delay_us(u16 nus)
{	 		  	  
    uint32_t ui_tmp = 0x00;
    SysTick->LOAD = nus * fac_us;  
    SysTick->VAL = 0x00;  
    SysTick->CTRL = 0x01;  

    do  
    {  
        ui_tmp = SysTick->CTRL;  
    }while((ui_tmp & 0x01) && (!(ui_tmp & (1 << 16))));  

    SysTick->CTRL = 0x00;  
    SysTick->VAL = 0x00;      
} 

void Delay(u16 nms)
{	 		  	  
    uint32_t ui_tmp = 0x00;
    SysTick->LOAD = nms * fac_ms;  
    SysTick->VAL = 0x00;  
    SysTick->CTRL = 0x01;  

    do  
    {  
        ui_tmp = SysTick->CTRL;  
    }while((ui_tmp & 0x01) && (!(ui_tmp & (1 << 16))));  

    SysTick->CTRL = 0x00;  
    SysTick->VAL = 0x00;   	    
} 

delay.h:

#ifndef __DELAY_H
#define __DELAY_H 			   
#include <stm32l1xx.h>
#define u8 unsigned char 
#define u16 unsigned short 
#define u32 unsigned int 
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u16 nus);
void Delay(u16 nms);
#endif

总结

目前实现了温湿度信息的实时采集了,之后要尝试使用L151的低功耗模式定时采集数据。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值