STM32学习心得二十九:I2C通讯实验及相关代码解读

记录一下,方便以后翻阅~

学习板:正点原子 战舰V3
修改内容:
1)2020年11月29日:对代码部分的解释进行了修正,将代码冗余进行删除,代码仅支持24C02与F103通讯;

主要内容:
1) I2C通讯协议;
2) 24C02芯片介绍;
3) 相关实验代码解读。

实验功能:系统启动后,通过KEY1按键来控制24C02的写入,通过另外一个按键KEY0来控制24C02的读取。并在串口调试助手上面显示相关信息。

官方资料:《STM32中文参考手册V10》第24章——I2C接口

1. I2C通讯协议概念
I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS公司开发用于连接微控制器及其外围设备。
I2C是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。
I2C是半双工通信方式(这个在《学习心得十四:串口通信相关知识及配置方法》有讲)。
I2C总线系统结构如下图所示(这里先提一句,I2C协议里,空闲状态时SDA和SCL都是高电平!):
在这里插入图片描述
2. I2C通信协议及相关代码解读
2.1 空闲状态
I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

2.2 开始信号和停止信号
在这里插入图片描述
起始信号:当SCL为高时,SDA由高到低跳变。启动信号是一种电平跳变时序信号,而不是一个电平信号。
停止信号:当SCL为高时,SDA由低到高跳变。停止信号是一种电平跳变时序信号,而不是一个电平信号。

2.3 应答信号
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平(有效应答应这样编写)。
在这里插入图片描述
2.4 数据的有效性:
I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
即:数据在SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定。
在这里插入图片描述
2.5 数据传输:
在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。

3. 开发版的硬件连接
如下图所示:
在这里插入图片描述
PB6对应IIC的时钟线,PB7对应IIC的数据线。
在这里插入图片描述
PB6和PB7接在EEPROM(24C02)的SCL和SDA上。
4. EEPROM(24C02)介绍
EEPROM (Electrically Erasable Programmable read only memory)是指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片。
24C02是一个2Kbit的串行EEPROM存储芯片,可存储256(2K/8)个字节数据。通过I2C总线通讯读写芯片数据,通讯时钟频率可达400KHz。(备注,1Byte=8bit)
4.1 24C02芯片引脚定义如下图所示:
在这里插入图片描述
4.2 A0-A2的引脚用来设置24C02的地址线,由开发版给的硬件连接图可知,默认A0、A1、A2跟GND连接。
24C02的设备地址由下图的第一行所示:
在这里插入图片描述
上图中,24C02地址的前4位为0b1010,即0xA,后三位由A2、A1、A0决定,即0b000,最后一位根据读或写决定,读至1,写至0,因此24CO2的地址读写地址分别为:0b10100001(0xA1)和0b10100000(0xA0)。
4.3 24C02字节写时序/读时序
在这里插入图片描述
在这里插入图片描述
5. 实验相关代码解读
5.1 myiic.h头文件代码解读

/**
 ********************************  STM32F10x  *********************************
 * @文件名称: myiic.h
 * @修改作者: Aaron
 * @库版本号: V3.5.0
 * @工程版本: V1.0.0
 * @开发日期: 2020年11月28日
 * @摘要简述: myiic头文件
 ******************************************************************************/
/*-----------------------------------------------------------------------------
 * @更新日志:
 * @无
 * ---------------------------------------------------------------------------*/
#ifndef __MYIIC_H
#define __MYIIC_H
/* 包含的头文件 --------------------------------------------------------------*/
#include "sys.h"
#include "stm32f10x.h"
/* 寄存器方法控制GPIO管脚输入输出模式 ----------------------------------------*/
#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}   // PB7 上拉输入模式
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}   // PB7 通用推挽输出,50MHz 
/* 位操作方法定义 ------------------------------------------------------------*/
#define IIC_SCL    PBout(6) // SCL
#define IIC_SDA    PBout(7) // 输出SDA  
#define READ_SDA   PBin(7)  // 输入SDA
/* 定义IIC底层驱动程序 -------------------------------------------------------*/
void IIC_Init(void);                // 初始化IIC的IO口,并设为空闲状态     
void IIC_Start(void);               // 发送IIC开始信号
void IIC_Stop(void);                // 发送IIC停止信号
u8 IIC_Wait_Ack(void);              // IIC等待ACK信号
void IIC_Ack(void);                 // IIC发送ACK信号
void IIC_NAck(void);                // IIC不发送ACK信号
void IIC_Send_Byte(u8 txd);         // IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);// IIC读取一个字节
#endif /* __MYIIC_H */
/****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/

5.2 myiic.c文件代码解读

/**
 ********************************  STM32F10x  *********************************
 * @文件名称: myiic.c
 * @修改作者: Aaron
 * @库版本号: V3.5.0
 * @工程版本: V1.0.0
 * @开发日期: 2020年11月28日
 * @摘要简述: myiic源文件,包含IIC底层驱动程序
 ******************************************************************************/
/*-----------------------------------------------------------------------------
 * @更新日志:
 * @无
 * ---------------------------------------------------------------------------*/
/* 包含的头文件 --------------------------------------------------------------*/
#include "myiic.h"
#include "delay.h"
/**********************************************************************
函数名称:IIC_Init()
函数功能:IIC初始化函数,配置底层IO管脚,并设为空闲状态,属IIC底层驱动程序
入口参数:无
返回参数:无
修改作者:Aaron
***********************************************************************/
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_PP ;       // 通用推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOB, &GPIO_InitStructure);
 GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);              // PB6,PB7 输出高,空闲状态
}
/**********************************************************************
函数名称:IIC_Start()
函数功能:IIC起始信号,当SCL为高时,SDA由高到低跳变,属IIC底层驱动程序
入口参数:无
返回参数:无
修改作者:Aaron
***********************************************************************/
void IIC_Start(void)
{
 SDA_OUT();              // SDA线设为输出
 IIC_SDA=1;              // SDA输出高   
 IIC_SCL=1;              // SCL输出高,此时空间状态
 delay_us(4);
 IIC_SDA=0;              // SDA输出低,IIC开始
 delay_us(4);
 IIC_SCL=0;              // 钳住I2C总线,准备发送或接收数据
}   
/*********************************************************************
函数名称:IIC_Stop()
函数功能:IIC停止信号,当SCL为高时,SDA由低到高跳变,属IIC底层驱动程序
入口参数:无
返回参数:无
修改作者:Aaron
**********************************************************************/
void IIC_Stop(void)
{
 SDA_OUT();              // SDA线设为输出
 IIC_SCL=0;              // SCL输出低
 IIC_SDA=0;              // SDA输出低
 delay_us(4);
 IIC_SCL=1;              // 先SCL输出高
 IIC_SDA=1;              // 再SDA输出高,发送I2C总线结束信号
 delay_us(4);           
}
/*********************************************************************
函数名称:u8 IIC_Wait_Ack()
函数功能:IIC等待应答信号函数,属IIC底层驱动程序
    		应答信号为低电平时,为有效应答位,则接收器成功接收该字节;
     	应答信号为高电平时,为无效应答位,则接收器接收该字节失败。 
入口参数:无
返回参数:u8 返回值:1,接收应答失败;0,接收应答成功
修改作者:Aaron
**********************************************************************/
u8 IIC_Wait_Ack(void)
{
 u8 ucErrTime=0;
 SDA_IN();               // SDA设置为输入  
 IIC_SDA=1;delay_us(1);  // SDA设高电平   
 IIC_SCL=1;delay_us(1);  // SCL设高电平
 while(READ_SDA)         // 读取PB7的值,一直读PB7=1,高电平,则不断循环,直到溢出
 {
  ucErrTime++;
  if(ucErrTime>250)
  {
   IIC_Stop();
   return 1;             // 应答失败
  }
 }
 IIC_SCL=0;              // 当PB7=0,则SCL时钟输出低电平,0     
 return 0;               // 成功
} 
/*********************************************************************
函数名称:void IIC_Ack(void)
函数功能:IIC产生有效ACK应答函数,此时作为接收器,属IIC底层驱动程序
     	有效应答位ACK的要求是:
     	接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,
     	并确保在该时钟的高电平期间为稳定的SDA低电平。
入口参数:无
返回参数:无
修改作者:Aaron
**********************************************************************/
void IIC_Ack(void)
{
 IIC_SCL=0;     // SCL设为低电平
 SDA_OUT();
 IIC_SDA=0;     // SDA设为低电平
 delay_us(2);
 IIC_SCL=1;     // SCL设为高电平
 delay_us(2);
 IIC_SCL=0;     // SCL设为低电平,从而SCL第九个高电平期间,SDA一直为低电平
}
/*********************************************************************
函数名称:void IIC_NAck(void)
函数功能:IIC不产生有效ACK应答函数,此时作为接收器,属IIC底层驱动程序
入口参数:无
返回参数:无
修改作者:Aaron
**********************************************************************/
void IIC_NAck(void)
{
 IIC_SCL=0;     // SCL设为低电平
 SDA_OUT();
 IIC_SDA=1;     // SCL设为高电平
 delay_us(2);
 IIC_SCL=1;     // SCL设为高电平
 delay_us(2);
 IIC_SCL=0;     // SCL设为低电平,从而SCL第九个高电平期间,SDA一直为高电平
} 
/*********************************************************************
函数名称:IIC_Send_Byte(u8 txd)
函数功能:IIC发送一个字节函数,属IIC底层驱动程序
入口参数:u8 txd,待发送的数据,8位
返回参数:无
修改作者:Aaron
**********************************************************************/
void IIC_Send_Byte(u8 txd)
{                        
 u8 t;   
 SDA_OUT();              // SDA设为输出    
 IIC_SCL=0;              // SCL为低电平时,此时可改变SDA,以实现数据的有效传输
 for(t=0;t<8;t++)
 {              
  if((txd&0x80)>>7)      // 获取u8 txd的最高位(0或1),并右移7位
   IIC_SDA=1;            // 若为1,则SDA给1,即u8 txd数据从高位发送
  else
   IIC_SDA=0;            // 若为0,则SDA给0,即u8 txd数据从高位发送
  txd<<=1;    
  delay_us(2);           // 对TEA5767这三个延时都是必须的
  IIC_SCL=1;             // SCL设为高电平,传输SDA
  delay_us(2); 
  IIC_SCL=0;             // SCL设为低电平,传输SDA完成
  delay_us(2);
 }  
}
/*********************************************************************
函数名称:u8 IIC_Read_Byte(unsigned char ack)
函数功能:IIC读取一个字节函数,属IIC底层驱动程序
     	读1个字节,ack=1时,发送ACK,ack=0,发送nACK
入口参数:unsigned char ack
返回参数:u8
修改作者:Aaron
**********************************************************************/
u8 IIC_Read_Byte(unsigned char ack)
{
 unsigned char i,receive=0;  // 无符号字符型,对应整数范围0~255
 SDA_IN();          	     // SDA设置为输入
 for(i=0;i<8;i++ )
 {
    IIC_SCL=0; 
    delay_us(2);
  IIC_SCL=1;                 // SCL由低设高后,开始读信号
    receive<<=1;             // 先将之前接收的信号左移,最右位补0
    if(READ_SDA)
   receive++;                // 如果接收SDA为1,则最右位改为1
  delay_us(1);               // 如果接收SDA为0,则不做操作,延迟1us
 }      
 if (!ack)
  IIC_NAck();                // 发送nACK
 else
  IIC_Ack();                 // 发送ACK   
 return receive;
}
/****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/

5.3 24c02.h头文件代码解读

/**
 ********************************  STM32F10x  *********************************
 * @文件名称: 24c02.h
 * @修改作者: Aaron
 * @库版本号: V3.5.0
 * @工程版本: V1.0.0
 * @开发日期: 2020年11月28日
 * @摘要简述: 24c02头文件 
 ******************************************************************************/
/*-----------------------------------------------------------------------------
 * @更新日志:
 * @无
 * ---------------------------------------------------------------------------*/
#ifndef __24C02_H
#define __24C02_H
/* 包含的头文件 --------------------------------------------------------------*/
#include "stm32f10x.h"  
/* 24CXX驱动 -----------------------------------------------------------------*/    
#define AT24C02  255                                                // 开发板采用的芯片
/* 函数申明 ------------------------------------------------------------------*/ 
void AT24C02_Init(void);                                            // 初始化IIC
u8 AT24C02_ReadOneByte(u16 ReadAddr);                               // 指定地址读取一个字节
void AT24C02_WriteOneByte(u16 WriteAddr,u8 DataToWrite);            // 指定地址写入一个字节
void AT24C02_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);    // 指定地址开始写入指定长度的数据,用于写入16bit或者32bit的数据
u32 AT24C02_ReadLenByte(u16 ReadAddr,u8 Len);                       // 指定地址开始读取指定长度数据,用于读出16bit或者32bit的数据
u8 AT24C02_Check(void);                                             // 检查24C02是否通讯正常
void AT24C02_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead);          // 从指定地址开始读出指定长度的数据
void AT24C02_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite);       // 从指定地址开始写入指定长度的数据
#endif /* __24C02_H */
/****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/

5.4 24c02.c文件代码解读

/**
 ********************************  STM32F10x  *********************************
 * @文件名称: 24c02.c
 * @修改作者: Aaron
 * @库版本号: V3.5.0
 * @工程版本: V1.0.0
 * @开发日期: 2020年11月28日
 * @摘要简述: 24c02源文件
 ******************************************************************************/
/*-----------------------------------------------------------------------------
 * @更新日志:
 * @无
 * ---------------------------------------------------------------------------*/
/* 包含的头文件 --------------------------------------------------------------*/
#include "24c02.h" 
#include "myiic.h"
#include "delay.h"
/**************************************************************************************
函数名称:AT24C02_Init()
函数功能:24C02驱动函数
     	因为开发板选择PB6和PB7作为与24C02芯片通信的引脚,所以直接套用IIC_Init()函数
入口参数:无
返回参数:无
修改作者:Aaron
***************************************************************************************/
void AT24C02_Init(void)
{
 IIC_Init();
}
/**************************************************************************************
函数名称:u8 AT24C02_ReadOneByte(u16 ReadAddr)
函数功能:在AT24C02指定地址读出一个字节的数据,由从24C02芯片读取一个字节数据
入口参数:u16 ReadAddr:开始读数的地址
返回参数:u8 读到的数据
修改作者:Aaron
***************************************************************************************/
u8 AT24C02_ReadOneByte(u16 ReadAddr)
{      
 u8 temp=0;                          
 IIC_Start();                   // IIC起始信号
 IIC_Send_Byte(0XA0);           // 发送设备地址0XA0,进入写数据模式   
 IIC_Wait_Ack();                // 等待响应
 IIC_Send_Byte(ReadAddr);       // 发送读取数据的起始地址
 IIC_Wait_Ack();                // 等待响应
 IIC_Start();                   // IIC起始信号
 IIC_Send_Byte(0XA1);           // 进入接收数据模式      
 IIC_Wait_Ack();                // 等待响应
 temp=IIC_Read_Byte(0);         // 读取一个字节的数据
 IIC_Stop();                    // 产生一个停止条件     
 return temp;
}
/**************************************************************************************
函数名称:AT24C02_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
函数功能:在AT24C02指定地址写入一个字节的数据,由开发板写入一个数据至24C02芯片
入口参数:u16 WriteAddr :写入数据的起始地址;
     	u8 DataToWrite:要写入的数据
返回参数:无
修改作者:Aaron
***************************************************************************************/
void AT24C02_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{                                
 IIC_Start();                    // IIC起始信号
 IIC_Send_Byte(0XA0);            // 发送设备地址0XA0,进入写数据模式   
 IIC_Wait_Ack();                 // 等待响应
 IIC_Send_Byte(WriteAddr);       // 发送写入数据的起始地址
 IIC_Wait_Ack();          	    // 等待响应          
 IIC_Send_Byte(DataToWrite);     // 发送一个字节的数据          
 IIC_Wait_Ack();                 // 等待响应
 IIC_Stop();                     // 产生一个停止条件 
 delay_ms(10);  
}
/**************************************************************************************
函数名称:u8 AT24C02_Check()
函数功能:检查AT24C02是否正常,读取最后一个地址(255)来存储数据
入口参数:无
返回参数:u8,返回1:检测失败;返回0:检测成功
修改作者:Aaron
***************************************************************************************/
u8 AT24C02_Check(void)
{
 u8 temp;
 temp=AT24C02_ReadOneByte(255);      // 读取第255个地址的数据    
 if(temp==0X55)
  return 0;     
 else                                // 排除第一次初始化的情况
 {
  AT24C02_WriteOneByte(255,0X55);    // 先在第255个地址写入0x55数据
  temp=AT24C02_ReadOneByte(255);     // 再读取第255个地址的数据
  if(temp==0X55)
   return 0;
 }
 return 1;             
}
/**************************************************************************************
函数名称:AT24C02_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
函数功能:在AT24C02里面的指定地址开始读出指定个数的数据
入口参数:u16 ReadAddr  : 开始读出的地址,对24c02为0~255;
         u8  *pBuffer  : 数据数组首地址;
         u16 NumToRead : 要读出数据的个数
返回参数:无
修改作者:Aaron
***************************************************************************************/
void AT24C02_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
 while(NumToRead)
 {
  *pBuffer++=AT24C02_ReadOneByte(ReadAddr++); 
  NumToRead--;
 }
} 
/**************************************************************************************
函数名称:AT24C02_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
函数功能:AT24C02里面的指定地址开始写入指定个数的数据
入口参数:u16 WriteAddr  : 开始写入的地址 对24c02为0~255;
     	u8  *pBuffer   : 数据数组首地址;
    		u16 NumToWrite : 要写入数据的个数
返回参数:无
修改作者:Aaron
***************************************************************************************/
void AT24C02_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
 while(NumToWrite--)
 {
  AT24C02_WriteOneByte(WriteAddr,*pBuffer);
  WriteAddr++;
  pBuffer++;
 }
}
/****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/

5.5 main.c文件代码解读

/**
 ********************************  STM32F10x  *********************************
 * @文件名称: main.c
 * @修改作者: Aaron
 * @库版本号: V3.5.0
 * @工程版本: V1.0.0
 * @开发日期: 2020年11月28日
 * @摘要简述: 主函数
 ******************************************************************************/
/*-----------------------------------------------------------------------------
 * @更新日志:
 * @无
 * ---------------------------------------------------------------------------*/
/* 包含的头文件 ---------------------------------------------------------------*/
#include "led.h"
#include "key.h"
#include "delay.h"
#include "sys.h"
#include "serial_communication.h"
#include "24c02.h"
/* 定义常量 ------------------------------------------------------------------*/
const u8 TEXT_Buffer[]={"这是从24C02读取的数据"};   // 要写入到24c02的字符串数组
/* 宏定义 --------------------------------------------------------------------*/
#define SIZE sizeof(TEXT_Buffer)                  // 计算TEXT_Buffer[]长度
/*********************************************************************
函数名称:int main()
函数功能:主函数
入口参数:无
返回参数:无
修改作者:Aaron
**********************************************************************/
 int main(void)
 {  
  u8 key;
  u16 i=0;
  u8 datatemp[SIZE];
  delay_init();            // 延时函数初始化   
  My_USART1_Init();        // 串口初始化为115200
  LED_Init();              // LED管脚初始化
  KEY_Init();              // 按键管脚初始化
  AT24C02_Init();          // IIC初始化 
  while(AT24C02_Check())   // 检测24C02
  {
   printf("检测不到值\n");                 
   DS0=!DS0;               // DS0闪烁
   DS1=!DS1;               // DS1闪烁
   delay_ms(100); 
  }
  printf("24C02芯片测试成功!\n");
  while(1)
  {
   key=KEY_Scan(0);
   if(key==KEY1_PRES)      // KEY1按下,写数据
   {
    AT24C02_Write(0,(u8*)TEXT_Buffer,SIZE);
    printf("向24C02写入数据\n");
   }
   if(key==KEY0_PRES)      // KEY0按下,读数据
   {
    AT24C02_Read(0,datatemp,SIZE);
    printf("从24C02读取数据,内容为:%s\n",datatemp);
   }
   i++;
   delay_ms(10);
   if(i==20)
   {
    DS0=!DS0;              // 提示系统正在运行 
    i=0;
   }     
  }
 }
/****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/

6. 实验结果
实验结果如下图所示:
在这里插入图片描述
旧知识点
1)复习如何新建工程模板,可参考STM32学习心得二:新建工程模板
2)复习基于库函数的初始化函数的一般格式,可参考STM32学习心得三:GPIO实验-基于库函数
3)复习寄存器地址,可参考STM32学习心得四:GPIO实验-基于寄存器
4)复习位操作,可参考STM32学习心得五:GPIO实验-基于位操作
5)复习寄存器地址名称映射,可参考STM32学习心得六:相关C语言学习及寄存器地址名称映射解读
6)复习时钟系统框图,可参考STM32学习心得七:STM32时钟系统框图解读及相关函数
7)复习延迟函数,可参考STM32学习心得九:Systick滴答定时器和延时函数解读
8)复习ST-LINK仿真器的参数配置,可参考STM32学习心得十:在Keil MDK软件中配置ST-LINK仿真器
9)复习ST-LINK调试方法,可参考STM32学习心得十一:ST-LINK调试原理+软硬件仿真调试方法
10)复习串口通信相关知识,可参考STM32学习心得十四:串口通信相关知识及配置方法

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天亮继续睡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值