stm32之硬件I2C读写MPU6050陀螺仪、加速度传感器应用案例

系列文章目录

1. stm32之I2C通信协议
2. stm32之软件I2C读写MPU6050陀螺仪、加速度传感器应用案例
3. stm32之I2C通信外设



前言

提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者

本案例实现了一个stm32使用硬件I2C外设通信读写MPU6050陀螺仪、加速度传感器的功能,最终我们将MPU6050的实时数据显示在了OLED上。OLED最上面显示的是设备ID号,左下角三个是加速度传感器的输出数据,分别为X轴、Y轴和Z轴的加速度。右边三个是陀螺仪传感器的输出数据,分别为X轴、Y轴和Z轴的角速度。当我们改变MPU6050传感器的姿态这六个数据就会相应地变化。


一、电路接线图

在这里,stm32作为主机,MPU6050作为从机,是一主一从的模型。

接线方面,MPU6050模块的VCC和GND分别接到电源的正负极进行供电,SCL接到stm32的PB10口,SDA接到stm32的PB11口。XCL和XDA用于扩展的接口这里暂时用不到就先不接。AD0引脚可用于修改从机地址的最低位,如果有需要可以接上,这里由于模块内置了下拉电阻,所以引脚悬空的话相当于接地。最后INT,中断信号输出脚,我们暂时用不到先不接。

由于本次我们使用的是I2C2外设进行硬件I2C通信,经查阅引脚定义表可知I2C2_SCL接在了PB10,I2C2_SDA接在了PB11上,这个注意不要接错了。

注意:根据I2C协议的硬件电路规定SCL和SDA都应该外挂一个上拉电阻的,但是由于MPU6050模块本身内部就已经集成了上拉电阻了,所以这里就不需要再外挂上拉电阻了。

在这里插入图片描述

二、应用案例代码

MPU6050_Reg.h:

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H

#define	MPU6050_SMPLRT_DIV		0x19
#define	MPU6050_CONFIG			0x1A
#define	MPU6050_GYRO_CONFIG		0x1B
#define	MPU6050_ACCEL_CONFIG	0x1C

#define	MPU6050_ACCEL_XOUT_H	0x3B
#define	MPU6050_ACCEL_XOUT_L	0x3C
#define	MPU6050_ACCEL_YOUT_H	0x3D
#define	MPU6050_ACCEL_YOUT_L	0x3E
#define	MPU6050_ACCEL_ZOUT_H	0x3F
#define	MPU6050_ACCEL_ZOUT_L	0x40
#define	MPU6050_TEMP_OUT_H		0x41
#define	MPU6050_TEMP_OUT_L		0x42
#define	MPU6050_GYRO_XOUT_H		0x43
#define	MPU6050_GYRO_XOUT_L		0x44
#define	MPU6050_GYRO_YOUT_H		0x45
#define	MPU6050_GYRO_YOUT_L		0x46
#define	MPU6050_GYRO_ZOUT_H		0x47
#define	MPU6050_GYRO_ZOUT_L		0x48

#define	MPU6050_PWR_MGMT_1		0x6B
#define	MPU6050_PWR_MGMT_2		0x6C
#define	MPU6050_WHO_AM_I		0x75

#endif

MPU6050.h:

#ifndef __MPU6050_H
#define __MPU6050_H

void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);

void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);

#endif

MPU6050.c:

#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"

#define MPU6050_ADDRESS		0xD0

void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint32_t Timeout;
	Timeout = 10000;
	while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)
	{
		Timeout --;
		if (Timeout == 0)
		{
			break;
		}
	}
}

void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	I2C_GenerateSTART(I2C2, ENABLE);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	
	I2C_SendData(I2C2, RegAddress);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
	
	I2C_SendData(I2C2, Data);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
	
	I2C_GenerateSTOP(I2C2, ENABLE);
}

uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	I2C_GenerateSTART(I2C2, ENABLE);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	
	I2C_SendData(I2C2, RegAddress);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
	
	I2C_GenerateSTART(I2C2, ENABLE);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
	
	I2C_AcknowledgeConfig(I2C2, DISABLE);
	I2C_GenerateSTOP(I2C2, ENABLE);
	
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);
	Data = I2C_ReceiveData(I2C2);
	
	I2C_AcknowledgeConfig(I2C2, ENABLE);
	
	return Data;
}

void MPU6050_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_InitTypeDef I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStructure.I2C_ClockSpeed = 50000;
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;
	I2C_Init(I2C2, &I2C_InitStructure);
	
	I2C_Cmd(I2C2, ENABLE);
	
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);
}

uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}

void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	*AccX = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	*AccY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*AccZ = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (DataH << 8) | DataL;
}

主程序main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"

uint8_t ID;
int16_t AX, AY, AZ, GX, GY, GZ;

int main(void)
{
	OLED_Init();
	MPU6050_Init();
	
	OLED_ShowString(1, 1, "ID:");
	ID = MPU6050_GetID();
	OLED_ShowHexNum(1, 4, ID, 2);
	
	while (1)
	{
		MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
		OLED_ShowSignedNum(2, 1, AX, 5);
		OLED_ShowSignedNum(3, 1, AY, 5);
		OLED_ShowSignedNum(4, 1, AZ, 5);
		OLED_ShowSignedNum(2, 8, GX, 5);
		OLED_ShowSignedNum(3, 8, GY, 5);
		OLED_ShowSignedNum(4, 8, GZ, 5);
	}
}

完整工程:stm32之硬件I2C读写MPU6050陀螺仪、加速度传感器

三、应用案例分析

在这里插入图片描述

3.1 基本思路

  • 第一步,开启I2C外设和对应GPIO口的时钟。
  • 第二步,把I2C外设对应的GPIO口初始化为复用开漏输出模式。
  • 第三步,使用结构体对整个I2C进行配置。
  • 第四步,I2C_Cmd,使能I2C。

以上就是I2C外设配置的基本思路了,接下来我们只需要在上一章软件I2C通信模块的基础上进行修改即可,把软件实现的时序用硬件I2C接口替换掉。

3.2 相关库函数介绍

在这里我们只选择部分重点需要掌握的函数进行讲解,一些讲烂了的函数就不讲了,比如初始化什么的,其实写完了就会发现套路都是差不多的,只是外设不一样而已。

1. 生成起始、终止信号

void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);

I2C_GenerateSTART函数用于生成起始信号,I2C_GenerateSTOP函数用于生成终止信号。

2. 应答位配置

void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);

该函数用于配置应答位,ENABLE就是应答,DISABLE就是非应答,原理就是操作控制寄存器ACK位。

3. 发送数据

void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);

该函数用于发送一个字节数据,其实现原理就是将一个字节的数据写入到DR数据寄存器。

4. 接收数据

uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);

该函数用于接收一个字节数据,其实现原理就是从DR寄存器中读取一个字节数据。

5. 发送七位地址专用函数

void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);

该函数用于进行从机寻址时序,即从机地址(7位)+读写标志位(1位),第二个参数I2C_Direction决定读写标志位,当指定为I2C_Direction_Transmitter时表示为写,当指定为I2C_Direction_Receiver时表示为读。

6. I2C事件状态监控函数

函数原型ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);
功能该函数用于检查指定的 I2C 事件是否发生
参数
I2Cx: 指定要使用的 I2C 外设,可以是 I2C1、I2C2 或其他可用的 I2C 实例
I2C_EVENT: 指定要检查的 I2C 事件
返回值typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus;
成功返回SUCCESS,表示指定的 I2C 事件发生。失败返回ERROR,表示指定的 I2C 事件未发生

I2C_EVENT可以是预定义的一些事件常量,如:

事件描述
I2C_EVENT_MASTER_MODE_SELECT主机模式选择事件(EV5)
I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED主机发送模式选择事件(EV6)
I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED主机接收模式选择事件(EV6)
I2C_EVENT_MASTER_BYTE_TRANSMITTING主机字节正在发送中(EV8)
I2C_EVENT_MASTER_BYTE_TRANSMITTED主机字节发送完成(EV8_2)
I2C_EVENT_MASTER_BYTE_RECEIVED主机字节接收完成(EV7)

3.3 MPU6050模块

MPU6050模块主要封装了一个模块初始化函数以及一个指定地址写函数和一个指定地址读函数。

3.1.1 模块初始化

1. 开启I2C外设和对应GPIO口的时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

这里I2C1和I2C2都是APB1的外设,注意不要搞错了。

2. 把I2C外设对应的GPIO口初始化为复用开漏输出模式

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

3. 使用结构体对整个I2C进行配置

I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_ClockSpeed = 50000;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_Init(I2C2, &I2C_InitStructure);

I2C_Mode: I2C 模式配置,这里选择I2C_Mode_I2C,即标准的 I2C 模式。STM32 的 I2C 外设还支持 I2C 模式和 SMBus 模式。

I2C_ClockSpeed: 时钟速度配置,这里选择50KHz。该参数控制 SCL 引脚的频率,进而影响 I2C 总线的通信速率。典型的 I2C 时钟速度可以是 100 kHz(标准模式),400 kHz(快速模式),或更高的速率,具体根据应用需求设置。

I2C_DutyCycle: 占空比配置,这里选择I2C_DutyCycle_2,即设置 I2C 时钟的占空比为 2(即高电平与低电平时间的比例)。该参数仅在 I2C 工作在快速模式(400 kHz 或更高)时有效,用于调整时钟信号的高低电平时间比例。

I2C_Ack: 应答配置,这里选择I2C_Ack_Enable,即启用 I2C 的应答功能(ACK)。当 I2C 接收到数据后,启用 ACK 功能可以让从设备(或主设备)自动发送一个应答信号,以表示数据接收成功。

I2C_AcknowledgedAddress: 应答地址模式配置,这里选择了I2C_AcknowledgedAddress_7bit,即将 I2C 的应答地址模式设置为 7 位地址模式。I2C 设备地址可以是 7 位或 10 位,此处选择 7 位模式。

I2C_OwnAddress1: 设备地址配置,这里选择了0x00,即设置当前 I2C 外设的主地址为 0x00。在从设备模式下,这个地址用于从设备识别自己的地址。在主设备模式下,地址参数的作用较小。

4. I2C_Cmd,使能I2C

I2C_Cmd(I2C2, ENABLE);

到这里,整个I2C2外设就初始化完了,接下来就是进行MPU6050模块的初始化配置了。

5. MPU6050初始化配置

MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//接触睡眠,选择陀螺仪时钟
MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);//六个轴均不待机
MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//采用分频为10
MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//滤波参数给最大
//陀螺仪和加速度计都选择最大量程
MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);
MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);

3.1.2 指定地址写

1. 发送起始信号(EV5)

I2C_GenerateSTART(I2C2, ENABLE);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);

2. 发送从机地址和方向位(EV6)

I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);

3. 发送数据(寄存器地址)(EV8)

I2C_SendData(I2C2, RegAddress);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);

4. 发送数据(写入寄存器数据)(EV8_2)

I2C_SendData(I2C2, Data);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);

5. 发送终止信号

I2C_GenerateSTOP(I2C2, ENABLE);

3.1.3 指定地址读

1. 发送起始信号(EV5)

I2C_GenerateSTART(I2C2, ENABLE);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);

2. 发送从机地址和方向位(EV6)

I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);

3. 发送数据(寄存器地址)(EV8_2)

I2C_SendData(I2C2, RegAddress);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);

注意:这里由于是写时序的最后一个发送数据,所以还是使用I2C_EVENT_MASTER_BYTE_TRANSMITTED等待EV8_2稳妥点。

4. 重新发送起始信号(EV5)

I2C_GenerateSTART(I2C2, ENABLE);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);

5. 发送从机地址和方向位(EV6)

I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);

注意:这里有两个EV6事件,分别是主机作为发送端的I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED和主机作为接收端的I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED,不要搞错了。

6. 接收最后一个字节数据前设置

I2C_AcknowledgeConfig(I2C2, DISABLE);
I2C_GenerateSTOP(I2C2, ENABLE);

在接收最后一个字节之前设置ACK为非应答,设置停止位。

7. 接收数据(EV7)

MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);
Data = I2C_ReceiveData(I2C2);

8. 恢复默认ACK状态

I2C_AcknowledgeConfig(I2C2, ENABLE);
STM32 MPU6050陀螺仪是指在STM32单片机读写MPU6050陀螺仪的软件DEMO例程源码。这个例程可以供学习和设计参考使用。 MPU6050是一款使用I2C通讯的陀螺仪和加速度计。要想获取MPU6050陀螺仪和加速度数据,需要先了解一下MPU6050的使用方法。 在STM32 MPU6050陀螺仪的软件DEMO例程源码中,通过初始化和校验的步骤,成功地完成了MPU6050的初始化。然后在数据读取环节,使用相应的函数来读取MPU6050的加速度数据、角加速度数据和原始温度数据。 具体的读取函数包括: 1. 读取MPU6050的加速度数据的函数MPU6050ReadAcc,将数据存储在accData数组中。 2. 读取MPU6050的角加速度数据的函数MPU6050ReadGyro,将数据存储在gyroData数组中。 3. 读取MPU6050的原始温度数据的函数MPU6050ReadTemp,将数据存储在tempData变量中。 通过调用这些函数,可以获取MPU6050陀螺仪数据。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [STM32单片机 MPU6050陀螺仪串口屏显示&匿名上位机软件例程源码](https://download.csdn.net/download/GJZGRB/86775224)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【stm32stm32学习笔记(江科大)-详解stm32获取Mpu6050陀螺仪和加速度](https://blog.csdn.net/m0_74086611/article/details/129171737)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [模块介绍之六轴陀螺仪MPU6050篇(STM32基本使用)](https://blog.csdn.net/weixin_42193239/article/details/102979420)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值