基于nrf52832 mpu6050应用实例(7)

1 基于平台

1.硬件平台:nRF52832
2:软件sdk:nRF5_SDK_14.2.0_17b948a
3:参考的博客链接
地址1
地址2
地址3
地址4

2 使用I2C接口驱动

参考之前博客,已编写完成且测试,可以直接移植调用
模拟I2C地址

3 MPU6050运动中断相关寄存器

由于这样或那样的原因,MPU6050的技术文档中省略了很多寄存器,而这些被刻意省略的寄存器往往涉及到各类高级应用,如较为常用的运动中断(自由落体中断、加速度中断和静止中断)、内部DMP数据融合和内部FIFO等。在此感谢运用反编译等技术手段整理出相关寄存器和DMP驱动的各位大神。
除基本寄存器(PWR_MGMT、CONFIG、ACCEL_CONFIG等)之外,运动中断应用所涉及到的相关内部寄存器主要包括:

1: 中断使能寄存器:INT_ENABLE ,寄存器地址0x38,为MPU6050所有中断输出的开关寄存器,用于使能运动中断、FIFO溢出中断和数据中断等;
2 FIFO控制寄存器:USER_CTRL,寄存器地址0x6A,该寄存器用于使能FIFO,并可控制MPU6050的I2C主机。应用运动中断功能时,应关闭FIFO和I2C主机;
3 FIFO使能寄存器:FIFO_EN,寄存器地址0x23,该寄存器用于使能各个FIFO功能,在应用运动中断功能时应关闭;
4 中断引脚配置寄存器:INT_PIN_CFG,寄存器地址0x37,该寄存器用于设置INT中断引脚的电平标准和驱动方式(推挽、开漏)等;
5 运动状态寄存器:MOT_DETECT_STATUS,寄存器地址0x61,该寄存器用于在触发运动中断(自由落体中断、加速度中断和静止中断)时标示中断的类型。
6 自由落体中断阈值寄存器:FF_THR,寄存器地址0x1D,当使能自由落体中断时,该寄存器的值决定中断的触发阈值,数值越高,对自由落体的检测越不灵敏;
7 自由落体中断时间寄存器:FF_DUR,寄存器地址0x1E,当使能自由落体中断时,该寄存器的值决定中断的持续时间阈值,当自由落体的持续时间达到设定阈值时触发中断。数值越高,对自由落体检测的时间阈值越长;
8 加速度中断阈值寄存器:MOT_THR,寄存器地址0x1F,当使能加速度中断时,该寄存器的值决定中断的触发阈值,数值越高,触发中断所需的加速度越大。
9 加速度中断时间寄存器:MOT_DUR,寄存器地址0x20,当使能加速度中断时,该寄存器的值决定中断的持续时间阈值,当加速度值持续时间达到设定阈值时触发中断。数值越高,触发中断所需的加速度持续时间越长;
10 静止中断阈值寄存器:ZRMOT_THR,寄存器地址0x21,当使能静止中断时,若当前加速度计输出的三轴值均小于静止中断阈值,则产生静止中断。数值越高,触发静止中断的要求越宽松。
11 静止中断时间寄存器:ZRMOT_DUR,寄存器地址0x22,当使能静止中断时,该寄存器的值决定触发静止中断所需的持续时间,数值越高,触发中断所需的静止持续时间越长。
除特殊说明外,各个寄存器均为逻辑1使能

中断使能寄存器:INT_ENABLE
在这里插入图片描述
FIFO控制寄存器:USER_CTRL
在这里插入图片描述
FIFO使能寄存器:FIFO_EN
在这里插入图片描述
中断引脚配置寄存器:INT_PIN_CFG
在这里插入图片描述
运动状态寄存器:MOT_DETECT_STATUS
在这里插入图片描述
自由落体中断阈值寄存器:FF_THR
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4代码示例

.h文件

#ifndef mpu6050_h_
#define mpu6050_h_


#include "stdint.h"



#define MPU6050_SDA_PIN  16
#define MPU6050_SCL_PIN  15



#define MPU6050_ADDRESS_AD0_LOW 0xD0 
 
#define MPU6050_ADDRESS_AD0_HIGH 0XD1 
 
#define MPU_ADDR 0xD0 



/*lint ++flb "Enter library region" */

#define ADDRESS_WHO_AM_I          (0x75U) // !< WHO_AM_I register identifies the device. Expected value is 0x68.
#define ADDRESS_SIGNAL_PATH_RESET (0x68U) // !<

#define MPU6050_ADDRESS         0x69
#define MPU6050_GYRO_OUT        0x43
#define MPU6050_ACC_OUT         0x3B

#define MPU_SELF_TESTX_REG		0X0D	//×Ô¼ì¼Ä´æÆ÷X
#define MPU_SELF_TESTY_REG		0X0E	//×Ô¼ì¼Ä´æÆ÷Y
#define MPU_SELF_TESTZ_REG		0X0F	//×Ô¼ì¼Ä´æÆ÷Z
#define MPU_SELF_TESTA_REG		0X10	//×Ô¼ì¼Ä´æÆ÷A
#define MPU_SAMPLE_RATE_REG		0X19	//²ÉÑùƵÂÊ·ÖƵÆ÷
#define MPU_CFG_REG				0X1A	//ÅäÖüĴæÆ÷
#define MPU_GYRO_CFG_REG		0X1B	//27:ÍÓÂÝÒÇÅäÖüĴæÆ÷
#define MPU_ACCEL_CFG_REG		0X1C	//28¼ÓËٶȼÆÅäÖüĴæÆ÷


#define MPU_FF_THR		        0X1D	//×ÔÓÉÂäÌå¼ì²âÖжÏãÐÖµ
#define MPU_FF_DUR		        0X1E	//×ÔÓÉÂäÌåÖжÏʼþ

#define MPU_MOTION_DET_REG		0X1F	//Ô˶¯¼ì²â·§ÖµÉèÖüĴæÆ÷
#define MPU_MOT_DUR_REG	        0X20	//Ô˶¯ÖжÏʱ¼ä

#define MPU_ZRMOT_THR_REG		0X21	//¾²Ö¹¼ì²âãÐÖµ
#define MPU_ZRMOT_DUR_REG	    0X22	//¾²Ö¹¼ì²âʱ¼ä


#define MPU_FIFO_EN_REG			0X23	//FIFOʹÄܼĴæÆ÷
#define MPU_I2CMST_CTRL_REG		0X24	//IICÖ÷»ú¿ØÖƼĴæÆ÷
#define MPU_I2CSLV0_ADDR_REG	0X25	//IIC´Ó»ú0Æ÷¼þµØÖ·¼Ä´æÆ÷
#define MPU_I2CSLV0_REG			0X26	//IIC´Ó»ú0Êý¾ÝµØÖ·¼Ä´æÆ÷
#define MPU_I2CSLV0_CTRL_REG	0X27	//IIC´Ó»ú0¿ØÖƼĴæÆ÷
#define MPU_I2CSLV1_ADDR_REG	0X28	//IIC´Ó»ú1Æ÷¼þµØÖ·¼Ä´æÆ÷
#define MPU_I2CSLV1_REG			0X29	//IIC´Ó»ú1Êý¾ÝµØÖ·¼Ä´æÆ÷
#define MPU_I2CSLV1_CTRL_REG	0X2A	//IIC´Ó»ú1¿ØÖƼĴæÆ÷
#define MPU_I2CSLV2_ADDR_REG	0X2B	//IIC´Ó»ú2Æ÷¼þµØÖ·¼Ä´æÆ÷
#define MPU_I2CSLV2_REG			0X2C	//IIC´Ó»ú2Êý¾ÝµØÖ·¼Ä´æÆ÷
#define MPU_I2CSLV2_CTRL_REG	0X2D	//IIC´Ó»ú2¿ØÖƼĴæÆ÷
#define MPU_I2CSLV3_ADDR_REG	0X2E	//IIC´Ó»ú3Æ÷¼þµØÖ·¼Ä´æÆ÷
#define MPU_I2CSLV3_REG			0X2F	//IIC´Ó»ú3Êý¾ÝµØÖ·¼Ä´æÆ÷
#define MPU_I2CSLV3_CTRL_REG	0X30	//IIC´Ó»ú3¿ØÖƼĴæÆ÷
#define MPU_I2CSLV4_ADDR_REG	0X31	//IIC´Ó»ú4Æ÷¼þµØÖ·¼Ä´æÆ÷
#define MPU_I2CSLV4_REG			0X32	//IIC´Ó»ú4Êý¾ÝµØÖ·¼Ä´æÆ÷
#define MPU_I2CSLV4_DO_REG		0X33	//IIC´Ó»ú4дÊý¾Ý¼Ä´æÆ÷
#define MPU_I2CSLV4_CTRL_REG	0X34	//IIC´Ó»ú4¿ØÖƼĴæÆ÷
#define MPU_I2CSLV4_DI_REG		0X35	//IIC´Ó»ú4¶ÁÊý¾Ý¼Ä´æÆ÷


//#define MPU_PWR_MGMT1_REG		0X6B	//µçÔ´¹ÜÀí¼Ä´æÆ÷1
//#define MPU_PWR_MGMT2_REG		0X6C	//µçÔ´¹ÜÀí¼Ä´æÆ÷2 

#define MPU_I2CMST_STA_REG		0X36	//IICÖ÷»ú״̬¼Ä´æÆ÷
#define MPU_INTBP_CFG_REG		0X37	//ÖжÏ/ÅÔ·ÉèÖüĴæÆ÷
#define MPU_INT_EN_REG			0X38	//ÖжÏʹÄܼĴæÆ÷
#define MPU_INT_STA_REG			0X3A	//ÖжÏ״̬¼Ä´æÆ÷

#define MPU_I2CMST_DELAY_REG	0X67	//IICÖ÷»úÑÓʱ¹ÜÀí¼Ä´æÆ÷
#define MPU_SIGPATH_RST_REG		0X68	//ÐźÅͨµÀ¸´Î»¼Ä´æÆ÷
#define MPU_MDETECT_CTRL_REG	0X69	//Ô˶¯¼ì²â¿ØÖƼĴæÆ÷
#define MPU_USER_CTRL_REG		0X6A	//Óû§¿ØÖƼĴæÆ÷
#define MPU_PWR_MGMT1_REG		0X6B	//µçÔ´¹ÜÀí¼Ä´æÆ÷1
#define MPU_PWR_MGMT2_REG		0X6C	//µçÔ´¹ÜÀí¼Ä´æÆ÷2 
#define MPU_FIFO_CNTH_REG		0X72	//FIFO¼ÆÊý¼Ä´æÆ÷¸ß°Ëλ
#define MPU_FIFO_CNTL_REG		0X73	//FIFO¼ÆÊý¼Ä´æÆ÷µÍ°Ëλ
#define MPU_FIFO_RW_REG			0X74	//FIFO¶Áд¼Ä´æÆ÷
#define MPU_DEVICE_ID_REG		0X75	//Æ÷¼þID¼Ä´æÆ÷


#define MPU_MOT_DETECT_STATUS		0X61	//Ô˶¯×´Ì¬¼Ä´æÆ÷



void mpu6050_init(void);
void task_read_MPU6050(void);
#endif


.c驱动代码

#include "mpu6050.h"
#include "iic_io.h"
#include "utility.h"

#include "bsp.h"




static uint8_t       m_device_address;   // !< Device address in bits [7:1]



/************************************************
*  mpu6050_register_write
*
******************************************/
bool mpu6050_register_write(uint8_t register_address, uint8_t value)
{
    uint8_t w2_data[2];

    w2_data[0] = register_address;
    w2_data[1] = value;
	return hrs_iic_transfer(m_device_address, w2_data, 2, TWI_ISSUE_STOP);
}


/*****************************************************************************
*@function name:  mpu6050_register_read
*@description  :  write and read reg
*@Para         :  register_address
*******************************************************************************/
bool mpu6050_register_read(uint8_t register_address, uint8_t * destination, uint8_t number_of_bytes)
{
	bool transfer_succeeded;
	transfer_succeeded=hrs_iic_transfer(m_device_address,&register_address,1, TWI_DONT_ISSUE_STOP);
	transfer_succeeded=hrs_iic_transfer(m_device_address|TWI_READ_BIT, destination, number_of_bytes, TWI_ISSUE_STOP);
	
	return transfer_succeeded;
}



/***********************************************
*   mpu6050_verify_product_id
*
***********************************/
 const uint8_t expected_who_am_i = 0x68U; // !< Expected value to get from WHO_AM_I register.
bool mpu6050_verify_product_id(void)
{
    uint8_t who_am_i=0;

    if (mpu6050_register_read(ADDRESS_WHO_AM_I, &who_am_i, 1))
    {
		BSP_RTT("mpu6050_verify_product_id =0x%02x\r\n",who_am_i);
        if (who_am_i != expected_who_am_i)
        {
            return false;
        }
        else
        {
            return true;
        }
    }
    else
    {
        return false;
    }
}


/*********************************************
*  Ô˶¯¼ì²âÖжÏ
*
**************************************/
void Motion_Interrupt(void)             //
{
    mpu6050_register_write(MPU_MOTION_DET_REG,0x01);        
    mpu6050_register_write(MPU_MOT_DUR_REG,0x14);          

	
	mpu6050_register_write(MPU_CFG_REG,0x04);  //DLPF
    mpu6050_register_write(MPU_ACCEL_CFG_REG,0x1C); //
	
    mpu6050_register_write(MPU_INTBP_CFG_REG,0X1C); //INT
    mpu6050_register_write(MPU_INT_EN_REG,0x40); //

	
}


/*********************************************
*  MPU_Fall_Init
*
****************************************************/
void MPU_Fall_Init(void)			//
{
    mpu6050_register_write(MPU_FF_THR,0x14);           //20mg 
    mpu6050_register_write(MPU_FF_DUR,0x0A);           //10ms
	
	
	mpu6050_register_write(MPU_CFG_REG,0x04);  //DLPF
    mpu6050_register_write(MPU_ACCEL_CFG_REG,0x1C); //
	
    mpu6050_register_write(MPU_INTBP_CFG_REG,0X1C); //INT
    mpu6050_register_write(MPU_INT_EN_REG,0x40); //
}


/*********************************************
*  MPU_Zero_Motion_Init
*
****************************************************/
void MPU_Zero_Motion_Init(void)
{

	mpu6050_register_write(MPU_ZRMOT_THR_REG,0x20);           //64mg
    mpu6050_register_write(MPU_ZRMOT_DUR_REG,0x20);           //32ms
	
	mpu6050_register_write(MPU_CFG_REG,0x04);  //DLPF
    mpu6050_register_write(MPU_ACCEL_CFG_REG,0x1C); //

    mpu6050_register_write(MPU_INTBP_CFG_REG,0X1C); //INT
    mpu6050_register_write(MPU_INT_EN_REG,0x40); //
}

/**************************************
* mpu6050_init
*
*****************************/
//	uint8_t inData[7]={0x00,								
//							0x00,												
//							0x03,												
//							0x10,											
//							0x00,												
//							0x32,												
//							0x01};	

/****************************************************************************/
// bit2-bit0
/*
 * * | ??????| ???
 * * DLPF_CFG | ??| ??| ??| ??| ???
 * ------------- + -------- + ------- + -------- + ------ + - -----------
 * 0 | 260Hz | 0ms | 256Hz | 0.98ms | 8kHz
 * 1 | 184Hz | 2.0ms | 188Hz | 1.9ms | 1kHz
 * 2 | 94Hz | 3.0ms | 98Hz | 2.8ms | 1kHz
 * 3 | 44Hz | 4.9ms | 42Hz | 4.8ms | 1kHz
 * 4 | 21Hz | 8.5ms | 20Hz | 8.3ms | 1kHz
 * 5 | 10Hz | 13.8ms | 10Hz | 13.4ms | 1kHz
 * 6 | 5Hz | 19.0ms | 5Hz | 18.6ms | 1kHz
 * 7 | ??| ??| ??
 * */
void mpu6050_init(void)
{
									
	bool transfer_succeeded = 0xff;
	
	uint8_t device_address =0x68;
	
  	hrs_iic_init(MPU6050_SDA_PIN,MPU6050_SCL_PIN);
    delay_ms(100);
	
	m_device_address = (uint8_t)(device_address << 1);
	
	// Do a reset on signal paths
	uint8_t reset_value = 0x04U | 0x02U | 0x01U; // Resets gyro, accelerometer and temperature sensor signal paths
	transfer_succeeded = mpu6050_register_write(ADDRESS_SIGNAL_PATH_RESET, reset_value);


	//ÉèÖòÉÑùÂÊ 							
	//rate:4~1000(Hz)	
	mpu6050_register_write(MPU_SAMPLE_RATE_REG ,0x00);//    -- SMPLRT_DIV = 0  Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)
	
	//-- EXT_SYNC_SET 0 (½ûÓþ§ÕñÊäÈë½Å) ; 
    //default DLPF_CFG = 0 => (µÍͨÂ˲¨)ACC bandwidth = 260Hz  GYRO bandwidth = 256Hz)	
    //0x06-5hz 	
	mpu6050_register_write(MPU_CFG_REG ,0x06);//0x00); //CONFIG       

    //-- SLEEP 0; CYCLE 0; TEMP_DIS 0; CLKSEL 3 (PLL with Z Gyro reference)
	mpu6050_register_write(MPU_PWR_MGMT1_REG , 0x03); //PWR_MGMT_1    
	mpu6050_register_write(MPU_USER_CTRL_REG , 0x00);	// 0x6AµÄ I2C_MST_EN  ÉèÖóÉ0  ĬÈÏΪ0 6050  ʹÄÜÖ÷IIC 	
	
	
	 mpu6050_register_write(MPU_FIFO_EN_REG,0X00); //close fifo
	
/*****************************ÉèÖÃGYRO****************************************************/
    //fsr:0,¡À250dps;1,¡À500dps;2,¡À1000dps;3,¡À2000dps   bit3-bit4
	uint8_t gyro_fsr =3;//
	mpu6050_register_write(MPU_GYRO_CFG_REG,gyro_fsr<<3);// 0x10); //gyroÅäÖà Á¿³Ì  0-1000¶ÈÿÃë

/*****************************ÉèÖÃ ACC****************************************************/	
   //fsr:0,+-2G;1,+-4G;2,+-8G s ;3,+-16G   bit3-bit4
   uint8_t acc_fsr =0;
    mpu6050_register_write(MPU_ACCEL_CFG_REG,acc_fsr<<3);         //ACCÉèÖà  Á¿³Ì +-2G s 		

/*********************************************************************************************/
	// £¬¸ßµçƽÖжϣ¬Ò»Ö±Êä³ö¸ßµçƽֱµ½ÖжÏÇå³ý
	mpu6050_register_write(MPU_INTBP_CFG_REG, 0x1C);//0x32);//	INTµÍµçƽ
	mpu6050_register_write(MPU_INT_EN_REG, 0X40);// 0x01); 

	 // Read and verify product ID
    transfer_succeeded &= mpu6050_verify_product_id();	
	
	
	Motion_Interrupt();
}


/*******************************************************
*  MPU6050_ReadGyro
*  ÍÓÂÝÒÇÖµ
*********************************/
void MPU6050_ReadGyro(int16_t *pGYRO_X , int16_t *pGYRO_Y , int16_t *pGYRO_Z )
{
  uint8_t buf[6];    			
	
  mpu6050_register_read(MPU6050_GYRO_OUT,  buf, 6);
	
  *pGYRO_X = (buf[0] << 8) | buf[1];
	if(*pGYRO_X & 0x8000) *pGYRO_X-=65536;
		
	*pGYRO_Y= (buf[2] << 8) | buf[3];
  if(*pGYRO_Y & 0x8000) *pGYRO_Y-=65536;
	
  *pGYRO_Z = (buf[4] << 8) | buf[5];
	if(*pGYRO_Z & 0x8000) *pGYRO_Z-=65536;
}



/*******************************************************
*  MPU6050_ReadAcc
*  ¼ÓËÙ¶ÈÖµ
*********************************/
void MPU6050_ReadAcc( int16_t *pACC_X , int16_t *pACC_Y , int16_t *pACC_Z )
{
    
	uint8_t buf[6];    		

    
	mpu6050_register_read(MPU6050_ACC_OUT, buf, 6);
  *pACC_X = (buf[0] << 8) | buf[1];
	if(*pACC_X & 0x8000) *pACC_X-=65536;
		
	*pACC_Y= (buf[2] << 8) | buf[3];
  if(*pACC_Y & 0x8000) *pACC_Y-=65536;
	
  *pACC_Z = (buf[4] << 8) | buf[5];
	if(*pACC_Z & 0x8000) *pACC_Z-=65536;
}



void task_read_MPU6050(void)
{
    int16_t AccValue[3],GyroValue[3];


	MPU6050_ReadAcc( &AccValue[0], &AccValue[1] , &AccValue[2] );
	MPU6050_ReadGyro(&GyroValue[0] , &GyroValue[1] , &GyroValue[2] );

	BSP_RTT("6050-ACC:  %d	%d	%d	",AccValue[0],AccValue[1],AccValue[2]);
	BSP_RTT("6050-GYRO: %d	%d	%d	\r\n",GyroValue[0],GyroValue[1],GyroValue[2]);
}

角度算法

// 变量定义
 
#define Kp 100.0f                        // 比例增益支配率收敛到加速度计/磁强计
#define Ki 0.002f                // 积分增益支配率的陀螺仪偏见的衔接
#define halfT 0.001f                // 采样周期的一半
 
float q0 = 1, q1 = 0, q2 = 0, q3 = 0;          // 四元数的元素,代表估计方向
float exInt = 0, eyInt = 0, ezInt = 0;        // 按比例缩小积分误差
 
float Yaw,Pitch,Roll;  //偏航角,俯仰角,翻滚角
 //加速度单位g,陀螺仪rad/s
void IMUupdate(float gx, float gy, float gz, float ax, float ay, float az)
{
        float norm;
        float vx, vy, vz;
        float ex, ey, ez;  
 
        // 测量正常化
        norm = sqrt(ax*ax + ay*ay + az*az);      
        ax = ax / norm;                   //单位化
        ay = ay / norm;
        az = az / norm;      
 
        // 估计方向的重力
        vx = 2*(q1*q3 - q0*q2);
        vy = 2*(q0*q1 + q2*q3);
        vz = q0*q0 - q1*q1 - q2*q2 + q3*q3;
 
        // 错误的领域和方向传感器测量参考方向之间的交叉乘积的总和
        ex = (ay*vz - az*vy);
        ey = (az*vx - ax*vz);
        ez = (ax*vy - ay*vx);
 
        // 积分误差比例积分增益
        exInt = exInt + ex*Ki;
        eyInt = eyInt + ey*Ki;
        ezInt = ezInt + ez*Ki;
 
        // 调整后的陀螺仪测量
        gx = gx + Kp*ex + exInt;
        gy = gy + Kp*ey + eyInt;
        gz = gz + Kp*ez + ezInt;
 
        // 整合四元数率和正常化
        q0 = q0 + (-q1*gx - q2*gy - q3*gz)*halfT;
        q1 = q1 + (q0*gx + q2*gz - q3*gy)*halfT;
        q2 = q2 + (q0*gy - q1*gz + q3*gx)*halfT;
        q3 = q3 + (q0*gz + q1*gy - q2*gx)*halfT;  
 
        // 正常化四元
        norm = sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
        q0 = q0 / norm;
        q1 = q1 / norm;
        q2 = q2 / norm;
        q3 = q3 / norm;
 
        Pitch  = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3; // pitch ,转换为度数
        Roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // rollv
        //Yaw = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3;   //偏移太大,等我找一个好用的
}

可以加入QQ群:687360507
与大伙沟通交流,技术在于分享而进步

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值