STM32使用模拟I2C读取AS5600(深入讲解:带波形图)

这篇具有很好参考价值的文章主要介绍了STM32使用模拟I2C读取AS5600(深入讲解:带波形图)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、了解I2C

首先我们要了解I2C的基本原理

当IIC处于空闲状态的时候,SDA和SCL都处于高电平状态,

当IIC通信开始信号,SCL保持高电平,SDA从高电平变成低电平(SCL=1,SDA=1->0),

当IIC通信结束信号,SCL保持高电平,SDA从低电平变成高电平(SCL=1,SDA=0->1)。

as5600,stm32,单片机,arm

IIC通信开始后,发送8位数据信号,SCL拉低,

SDA发送数据最高位(7:1则高电平,0则低电平),然后SDA保持不变直到SCL下次低电平;

SDA发送数据(改变电平)后,SCL拉高,接收端读取信号后SCL再拉低。

共8次发送8个数据位【7:0】

as5600,stm32,单片机,arm

 在发送8个数据后,输出端的SDA将释放总线,交给接收端来控制;

SCL拉低,输出端SDA释放总线,接收端如果应答:SDA拉低    不应答:SDA拉高

 as5600,stm32,单片机,arm

二、AS5600的读取

AS5600是12位的霍尔磁编码器,它的地址是0x36,只需要读取0x0C、0x0D这两个寄存器就可以读出角度的原始数据,再将其乘以360,再除以4096,就可以获得角度值。

as5600,stm32,单片机,arm

 1.配置引脚

我们使用io模拟IIC通信,使用的是PB6,PB7;

这里我们直接使用正点原子的F103 IIC实验代码;

as5600,stm32,单片机,arm

首先将sys.h和sys.c复制到我们的程序中(我使用的是模板,带oled驱动,方便调试)

sys两个文件程序我放在文章后面,方便没有下载正点原子例程的读者;

将myiic.h复制过来,再加上三个函数,再定义一下0x0c和0x0d两个寄存器地址;

#ifndef _AS5600_H
#define _AS5600_H

#include "sys.h"


//IO方向设置
 
#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;} //SDA切换为输入模式 
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;} //SDA切换为输出模式 

//IO操作函数	 
#define IIC_SCL    PBout(6) //SCL输出
#define IIC_SDA    PBout(7) //SDA输出	 
#define READ_SDA   PBin(7)  //SDA输入

#define	_raw_ang_hi 0x0c
#define	_raw_ang_lo 0x0d

//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ÐźÅ

void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);	  


u8 AS5600_ReadOneByte(u16 ReadAddr);
void AS5600_WriteOneByte(u16 WriteAddr,u8 WriteData);
u16 AS5600_ReadTwoByte(u16 ReadAddr_hi,u16 ReadAddr_lo);



#endif
#include "as5600.h"
#include "Delay.h"

 
//³õʼ»¯IIC
void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	//ʹÄÜGPIOBʱÖÓ
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD ;   //注意IIC SDA线需要开漏输出,不然读取不到数据
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); 	//PB6,PB7 Êä³ö¸ß
}
//²úÉúIICÆðʼÐźÅ
void IIC_Start(void)
{
	SDA_OUT();     //sdaÏßÊä³ö
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	Delay_us(4);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	Delay_us(4);
	IIC_SCL=0;//ǯסI2C×ÜÏߣ¬×¼±¸·¢ËÍ»ò½ÓÊÕÊý¾Ý 
}	  
//²úÉúIICÍ£Ö¹ÐźÅ
void IIC_Stop(void)
{
	SDA_OUT();//sdaÏßÊä³ö
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	Delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;//·¢ËÍI2C×ÜÏß½áÊøÐźÅ
	Delay_us(4);
}
//µÈ´ýÓ¦´ðÐźŵ½À´
//·µ»ØÖµ£º1£¬½ÓÊÕÓ¦´ðʧ°Ü
//        0£¬½ÓÊÕÓ¦´ð³É¹¦
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDAÉèÖÃΪÊäÈë  
	IIC_SDA=1;Delay_us(1);	   
	IIC_SCL=1;Delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//ʱÖÓÊä³ö0 	   
	return 0;  
} 
//²úÉúACKÓ¦´ð
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	Delay_us(2);
	IIC_SCL=1;
	Delay_us(2);
	IIC_SCL=0;
}
//²»²úÉúACKÓ¦´ð		    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	Delay_us(2);
	IIC_SCL=1;
	Delay_us(2);
	IIC_SCL=0;
}					 				     
//IIC·¢ËÍÒ»¸ö×Ö½Ú
//·µ»Ø´Ó»úÓÐÎÞÓ¦´ð
//1£¬ÓÐÓ¦´ð
//0£¬ÎÞÓ¦´ð			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//À­µÍʱÖÓ¿ªÊ¼Êý¾Ý´«Êä
    for(t=0;t<8;t++)
    {              
        //IIC_SDA=(txd&0x80)>>7;
		if((txd&0x80)>>7)
			IIC_SDA=1;
		else
			IIC_SDA=0;
		txd<<=1; 	  
		Delay_us(2);   //¶ÔTEA5767ÕâÈý¸öÑÓʱ¶¼ÊDZØÐëµÄ
		IIC_SCL=1;
		Delay_us(2); 
		IIC_SCL=0;	
		Delay_us(2);
    }	 
} 	    
//¶Á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=0; 
        Delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		Delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//·¢ËÍnACK
    else
        IIC_Ack(); //·¢ËÍACK   
    return receive;
}

//ÔÚAS5600Ö¸¶¨µØÖ·¶Á³öÒ»¸öÊý¾Ý
u8 AS5600_ReadOneByte(u16 ReadAddr)
{				  
	u8 temp=-1;		  	    																 
  IIC_Start();  
	IIC_Send_Byte((0X36<<1)|0x00);	   //·¢ËÍдÃüÁî
	IIC_Wait_Ack(); 
  IIC_Send_Byte(ReadAddr);   //·¢Ë͵ØÖ·
	IIC_Wait_Ack();	    
	IIC_Start();  	 	   
	IIC_Send_Byte((0X36<<1)|0x01);           //½øÈë½ÓÊÕģʽ			   
	IIC_Wait_Ack();	 
  temp=IIC_Read_Byte(0);		   
  IIC_Stop();//²úÉúÒ»¸öÍ£Ö¹Ìõ¼þ	    
	return temp;
}

//ÔÚAS5600Ö¸¶¨µØÖ··¢ËͳöÒ»¸öÊý¾Ý
void AS5600_WriteOneByte(u16 WriteAddr,u8 WriteData)
{				  	  	    																 
  IIC_Start();  
	IIC_Send_Byte((0X36<<1)|0x00);	   //·¢ËÍдÃüÁî
	IIC_Wait_Ack(); 
  IIC_Send_Byte(WriteAddr);   	//·¢Ë͵ØÖ·
	IIC_Wait_Ack();	    
	IIC_Start();  	 	   
	IIC_Send_Byte(WriteData);          //·¢ËÍÊý¾Ý		   
	IIC_Wait_Ack();	 	   
  IIC_Stop();//²úÉúÒ»¸öÍ£Ö¹Ìõ¼þ	    
	Delay_ms(10);
}

//¶ÁÈ¡Á½Î»Êý¾Ý
u16 AS5600_ReadTwoByte(u16 ReadAddr_hi,u16 ReadAddr_lo)
{
	u16 TwoByte_Data=-1;
	u8 hi_Data=0,lo_Data=0;
	hi_Data=AS5600_ReadOneByte(ReadAddr_hi);
	lo_Data=AS5600_ReadOneByte(ReadAddr_lo);
	TwoByte_Data = (hi_Data<<8)|lo_Data;
	return TwoByte_Data;
}

as5600.c

这里我们主要讲AS5600_ReadOneByte()这个函数

u8 AS5600_ReadOneByte(u16 ReadAddr)
{                  
    u8 temp=-1;                                                                                   
  IIC_Start();  
    IIC_Send_Byte((0X36<<1)|0x00);       //
    IIC_Wait_Ack(); 
  IIC_Send_Byte(ReadAddr);   //
    IIC_Wait_Ack();        
    IIC_Start();              
    IIC_Send_Byte((0X36<<1)|0x01);           //           
    IIC_Wait_Ack();     
  temp=IIC_Read_Byte(0);           
  IIC_Stop();//       
    return temp;
}

as5600,stm32,单片机,arm

 1.先开始IIC信号

IIC_Start(); 

 2. 发送5600的写入地址

0x36是他的地址,但是要左移一位,因为最后一位是读写位,1是读,0是写

00110110<<1 |0 = 01101100 = 0x6C = 0x36<<1|0

    IIC_Send_Byte((0X36<<1)|0x00);       //发送写命令
    IIC_Wait_Ack();  //等待应答

3.发送读取的寄存器地址 0x0C或者0x0D

  IIC_Send_Byte(ReadAddr);   //发送地址
  IIC_Wait_Ack();    //等待应答

4.开始信号 

IIC_Start(); 

5.发送5600的读取地址

0x36是他的地址,但是要左移一位,因为最后一位是读写位,1是读,0是写

00110110<<1 |1 = 01101101 = 0x6D=0x36<<1|1

    IIC_Send_Byte((0X36<<1)|0x01);           //进入接收模式            
    IIC_Wait_Ack();     

6.读取数据

  temp=IIC_Read_Byte(0);           //读取数据
  IIC_Stop();        //     停止信号
  return temp;  //返回读取数据

as5600,stm32,单片机,armas5600,stm32,单片机,arm

 讲解完IIC的读取数据之后,我们来使用 AS5600_ReadTwoByte()函数读取两个寄存器的值

u16 AS5600_ReadTwoByte(u16 ReadAddr_hi,u16 ReadAddr_lo)
{
    u16 TwoByte_Data=-1;
    u8 hi_Data=0,lo_Data=0;
    hi_Data=AS5600_ReadOneByte(ReadAddr_hi);
    lo_Data=AS5600_ReadOneByte(ReadAddr_lo);
    TwoByte_Data = (hi_Data<<8)|lo_Data;
    return TwoByte_Data;
}

此函数是使用读取两个寄存器的值,对位的操作将两个8位的寄存器数据合并为16位数据

因为as5600是12位精度的霍尔传感器,一个8位寄存器存不下,

所以需要两个8位的寄存器来存储他的值

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "sys.h"
#include "as5600.h"



int main(void)
{
	IIC_Init();
	OLED_Init();
	OLED_ShowChar(1, 1, 'A');	
	u16 raw_num = 0;
	u16 JIAODUnum = 0;
	
	while (1)
	{

		raw_num = AS5600_ReadTwoByte(_raw_ang_hi,_raw_ang_lo);  //读取两个寄存器的值
		OLED_ShowNum(2,1,raw_num,5); //在屏幕显示
		JIAODUnum = (raw_num*360)/4096; //对寄存器值进行处理得到角度值
		OLED_ShowNum(3,1,JIAODUnum,3);//在屏幕显示

		Delay_ms(100);
	}
}

使用这个函数之后,我们就可以读取到传感器原始的角度数据

因为是12位精度的传感器,所以我们得到的数据也是12位的,即0~4095;

因为0度即360度所以我们将数据除4096再乘以360,即可得到0-359度的数据

使用合适的调试工具比如屏幕和串口,我们即可得到角度数据。

附:sys.c

#include "sys.h"

void WFI_SET(void)
{
	__ASM volatile("wfi");		  
}
//关闭所有中断
void INTX_DISABLE(void)
{		  
	__ASM volatile("cpsid i");
}
//开启所有中断
void INTX_ENABLE(void)
{
	__ASM volatile("cpsie i");		  
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
    MSR MSP, r0 			//set Main Stack value
    BX r14
}

sys.h文章来源地址https://www.toymoban.com/news/detail-788983.html

#ifndef __SYS_H
#define __SYS_H	
#include "stm32f10x.h"	 

//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS		0		//定义系统文件夹是否支持UCOS
																	    
	 
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    

#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

//以下为汇编函数
void WFI_SET(void);		//执行WFI指令
void INTX_DISABLE(void);//关闭所有中断
void INTX_ENABLE(void);	//开启所有中断
void MSR_MSP(u32 addr);	//设置堆栈地址

#endif

到了这里,关于STM32使用模拟I2C读取AS5600(深入讲解:带波形图)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • STM32软件模拟I2C从机的实现方法

    在使用I2C通信时,一般会用到软件模拟I2C。目前网络上能搜索到的软件模拟I2C一般都是模拟I2C主机,很少有模拟I2C从机的例程。由于I2C主机在进行数据收发时,有明确的可预见性,也就是主机明确知道什么时候要进行数据的收发操作,而且I2C的同步时钟信号也是由主机产生的

    2024年02月01日
    浏览(15)
  • 01_STM32软件+硬件I2C读取MPU6050(HAL库)

    目录 1、I2C简介 2、I2C时序单元 2.1 起始条件 2.2 终止条件 2.3 发送一个字节 2.4 接收一个字节 2.5 发送应答 2.6 接收应答 3、I2C完整时序 3.1 指定地址写一个字节 3.2 当前地址读一个字节 3.2 指定地址读一个字节 4、简单软件I2C代码(HAL) 4.1 软件I2C 4.2 软件I2C读MPU6050寄存器 5、ST

    2024年04月17日
    浏览(21)
  • 电脑传输数据STM32模拟I2C显示实时画面到OLED

    写的不好,还望大家指正,有的地方引用了一下大佬的代码。 一、所需硬件: STM32F103C8T6 USB转串口模块 OLED 128*64显示屏 STLINK 二、代码部分 1.stm32串口部分代码 2.stm32OLED屏幕部分代码 3.主程序 4.电脑通过opencv库截取电脑当前1080p一帧画面,并对图片二值化处理,通过电脑端编写

    2024年02月13日
    浏览(18)
  • 【STM32】I2C练习,HAL库读取MPU6050角度陀螺仪

    MPU-6000(6050)为全球首例整合性6轴运动处理组件,相较于多组件方案,免除了组合陀螺仪与加速器时间轴之差的问题,减少了大量的封装空间。当连接到三轴磁强计时,MPU-60X0提供完整的9轴运动融合输出到其主I2C或SPI端口(SPI仅在MPU-6000上可用)。 寄存器地址 寄存器内容 0X3B

    2024年02月16日
    浏览(21)
  • STM32模拟I2C获取TCS34725光学颜色传感器数据

    TCS34725是RGB三色颜色传感器,和TCS34727都属于TCS3472系列,在电气特性上略有差别,TCS34727相比TCS34725在I2C总线的访问电平上可以更低,而在I2C软件访问地址方面则一致。 TCS3472内部有4个PD(光电二极管),一个接收clear light(净光,未做任何处理),另外三个分别接收Red, Green,

    2024年02月16日
    浏览(17)
  • STM32模拟I2C协议获取HDC1080温度和湿度传感器数据

    HDC1080是一款温湿度传感器,具有如下特点: 其中温度和湿度经过出厂校准。这里介绍STM32模拟I2C总线协议访问HDC1080的HAL库实现范例。 HDC1080的内部原理及电路连接如下: HDC1080具有低功耗特征,每次触发检测转换后进入睡眠状态,另外内部有一个加热电阻,在环境湿度高时,

    2024年02月11日
    浏览(57)
  • STM32学习笔记(十)丨I2C通信(使用I2C实现MPU6050和STM32之间通信)

    ​  本次课程采用单片机型号为STM32F103C8T6。(鉴于笔者实验时身边只有STM32F103ZET6,故本次实验使基于ZET6进行的) ​  课程链接:江协科技 STM32入门教程   往期笔记链接:   STM32学习笔记(一)丨建立工程丨GPIO 通用输入输出   STM32学习笔记(二)丨STM32程序调试

    2024年01月19日
    浏览(27)
  • STM32模拟I2C协议获取HMC5883L电子罗盘磁角度数据 (HAL)

    HMC5883L 传感器采用霍尼韦尔各向异性磁阻(AMR)技术,应用于罗盘和三轴磁场角度检测领域,常用于水平物体转动的角度识别。HMC5883L 采用I2C总线接口,2.16~3.6V供电范围,带有校准测试功能。 HMC5883L的硬件连接有5个管脚,除了VCC和GND,以及I2C的SCK和SDA,还有一根INT中断线,用于

    2024年02月13日
    浏览(24)
  • 基于HAL库的stm32的OLED显示屏显示(模拟I2C,四脚,0.96寸)

    参考视频:江科大oled程序移植stm32hal库,freertos学习,cpu使用率_哔哩哔哩_bilibili ​ STM32入门教程-2023持续更新中_哔哩哔哩_bilibili 高速和低速晶振均选择为陶瓷晶振即可。 不需更改初始化配置,因为模拟I2C初始化时会设置这两个引脚的电平 step1、step2完成后生成工程即可。 代

    2024年02月06日
    浏览(24)
  • STM32 HAL FreeRTOS 硬件I2C 使用

    因为某个项目想要颜色识别,去识别球的颜色,但是又不想多来个摄像头,所以想尝试一下颜色传感器的方案。但是经过尝试,HAL库生成的 FreeRTOS 硬件 I2C 读写一直在报错。 刚好手头上有九轴陀螺仪的例程代码。最后用FreeRTOS 硬件 I2C 读取数据。 这里提到了阻塞式 HAL 函数(

    2024年02月20日
    浏览(22)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包