电机应用-直流有刷电机(编码器)

目录

编码器

编码器简介

编码器分类

编码器原理

磁电+增量式

光电+增量式

光电+绝对式

磁电+绝对式

编码器参数

STM32编码器接口

编码器接口简介

编码器接口框图

编码器接口计数原理

编码器模式1

计数溢出处理

编码器测速

编码器测试相关参数

编码器接口HAL库函数

关键结构体介绍

课堂代码


编码器

编码器简介

编码器:一种将直线位移、角位移数据转换为脉冲信号、二进制编码的设备。常用于测量物体运动的位置、角度或者速度。

编码器分类

编码器可以按照检测原理编码类型进行分类。

检测原理可分为:光电式、磁电式。

编码类型可分为:绝对式、增量式。

主要以下四大类:

        光电+绝对式

        光电+增量式

        磁电+绝对式

        磁电+增量式

编码器原理

磁电+增量式

利用霍尔效应(磁跟电的关系,磁的变化影响电的变化),将位移转换成计数脉冲,用脉冲个数计算位移和速度

光电+增量式

利用光电系统,将位移转换成计数脉冲,用脉冲个数计算位移和速度

光电+绝对式

不同位置、角度时,光敏元件根据受光转换出相应电平信号,形成二进制数

磁电+绝对式

复杂,略。

编码器参数

分辨率:编码器可以测量的最小距离。对于增量式编码器,分辨率即转轴每旋转一圈所输出的脉冲数(PPR,线)。

精度:编码器输出的信号数据与实际位置之间的误差,常用角分、角秒表示。

最大响应频率(PPS):编码器每秒能输出的最大脉冲数,单位Hz。

最大转速:指编码器机械系统所能承受的最高转速。

STM32编码器接口

编码器接口简介

STM32定时器的编码器接口模式相当于带有方向选择的外部时钟,即外部输入的脉冲信号可以作为计数器的时钟,而计数的方向则是由脉冲相位的先后所决定。

编码器接口框图

A、B两相脉冲信号从TIMx_CH1和TIMx_CH2这两个通道输入,经过滤波器和边沿检测器(可以设置滤波和反相)的处理,进入到编码器接口控制器中。

Tips:TIMx_CH1和TIMx_CH2这两个通道才支持编码器功能,TIMx_CH3和TIMx_CH4这两个通道不支持。

编码器接口计数原理

编码器接口可以利用输入脉冲的边沿进行计数,通过计数值的变化量可以算出输入脉冲信号的变化量,也就可以计算出电机的转速。

TIMx从模式控制寄存器(TIMx_SMCR)的0~2位:SMS[2:0]为从模式选择,控制边沿检测的方式。

选择外部信号时,触发信号(TRGI)的有效边沿与外部输入上所选的极性相关。

001:编码器模式1-计数器根据TI1FP1电平在TI2FP2边沿递增/递减计数。

010:编码器模式2-计数器根据TI2FP2电平在TI1FP1边沿递增/递减计数。

011:编码器模式3-计数器根据TI1FP1和TI2FP2的边沿计数,计数的方向取决于另外一个信号的电平。

Tips:

        选择仅在TI1或TI2处计数,就相当于对脉冲信号进行了2倍频(上身沿和下降沿),如果编码器输出11个脉冲信号,那么就会计数22次。

        选择在TI1和TI2处均计数,就相当于对脉冲信号进行了4倍频(上身沿和下降沿),如果编码器输出11个脉冲信号,那么就会计数44次。

        因此可以我们通过计数次数来计算电机速度时,需要除以相应的倍频系数。至此,A、B两相脉冲信号的变化就转换成了定时器的计数变化。

        可以通过一分钟内计数的变化量来计算电机速度:

                电机转速 = 一分钟内计数的变化量 / 倍频系数 / 编码器线数 / 减速比

编码器模式1:仅在TI1处计数

计数器根据TI1FP1电平在TI2FP2边沿递增/递减计数。

假设把A相接在CH1(TI1),B相接在CH2(TI2),选择仅在TI1处计数(仅检测A相边沿)。脉冲信号会有两种,当编码器正转时A相在前、当编码器反转时B相在前。

正转:当A相上身沿时,B相低电平,表格中对应的是递增计数。当A相下降沿时,B相高电平,表格中对应的是递增计数。即编码器正转对应的计数方向是递增计数。

反转:当A相上身沿时,B相高电平,表格中对应的是递减计数。当A相下降沿时,B相低电平,表格中对应的是递减计数。即编码器反转对应的计数方向是递减计数。

编码器模式2:仅在TI2处计数

计数器根据TI2FP2电平在TI1FP1边沿递增/递减计数。

假设把A相接在CH1(TI1),B相接在CH2(TI2),选择仅在TI2处计数(仅检测B相边沿)。脉冲信号会有两种,当编码器正转时A相在前、当编码器反转时B相在前。

正转:当B相上身沿时,A相高电平,表格中对应的是递增计数。当B相下降沿时,A相低电平,表格中对应的是递增计数。即编码器正转对应的计数方向是递增计数。

反转:当B相上身沿时,A相低电平,表格中对应的是递减计数。当B相下降沿时,A相高电平,表格中对应的是递减计数。即编码器反转对应的计数方向是递减计数。

编码器模式3:在TI1和TI2处均计数

计数器根据TI1FP1和TI2FP2的边沿计数,计数的方向取决于另外一个信号的电平。

假设把A相接在CH1(TI1),B相接在CH2(TI2),选择仅在TI2处计数(仅检测B相边沿)。脉冲信号会有两种,当编码器正转时A相在前、当编码器反转时B相在前。

正转:当A相上身沿时,B相低电平,表格中对应的是递增计数。当B相上身沿时,A相高电平,表格中对应的是递增计数。当A相下降沿时,B相高电平,表格中对应的是递增计数。当B相下降沿时,A相低电平,表格中对应的是递增计数。即编码器正转对应的计数方向是递增计数。

反转:当B相上身沿时,A相低电平,表格中对应的是递减计数。当A相上身沿时,B相高电平,表格中对应的是递减计数。当B相下降沿时,A相高电平,表格中对应的是递减计数。当A相下降沿时,B相低电平,表格中对应的是递减计数。即编码器反转对应的计数方向是递减计数。 

计数溢出处理

编码器在电机运行时会一直旋转并输出脉冲信号,如果时间较长,那么定时器计数就会溢出,我们必须对溢出进行处理,否则电机速度的计算结果就不准了。 

当前总计数值 = 计数器当前值 + 溢出次数(可在溢出中断中记录) * 65536。

溢出时读取TIMx控制寄存器1(TIMx_CR1)的位4(DIR,计数方向),以计算总的计数次数变化量。

        0:计数器递增计数

        1:计数器递减计数

作用:在计数溢出时,读取到溢出方向,后续才能计算总的计数变化量。

编码器测速

编码器测试相关参数

以下参考针对正点原子的直流有刷电机。

编码器分辨率编码器供电电机减速比
11PPRDC 5V30:1

编码器分辨率对于增量式编码器来说,转轴每旋转一圈所输出的脉冲数。即表示每旋转一周能输出11个脉冲数。如果用STM32的编码器接口去检测这个脉冲,假如采用四倍频,则这个编码器旋转一周所对应的脉冲数是11*4,即44个脉冲数。STM32知道了脉冲数,就可以反推旋转速度。

知道了旋转速度,也就是电机原始输出轴(转子的转速),最终经过电机减速比得到输出转速。

编码器接口HAL库函数

函数主要功能
HAL_TIM_Encoder_Init()初始化定时器基础参数及编码器接口
HAL_TIM_Encoder_Start()开启编码器接口通道
HAL_TIM_PeriodElapsedCallback()定时器更新中断回调函数
__HAL_TIM_IS_TIM_COUNTING_DOWN()读取DIR位,判断计数方向

关键结构体介绍

typedef struct
{
    uint32_t EncoderMode;     /* 编码器模式 */

    uint32_t IC11Polarity;    /* 输入极性(边沿检测器) */
    uint32_t IC1Selection;    /* 输入通道选择 */
    uint32_t IClPrescaler;    /* 时钟分频因子 */
    uint32_t IClFilter;       /* 滤波器 */
    
    uint32_t IC21Polarity;
    uint32_t IC2Selection;
    uint32_t IC2Prescaler;
    uint32_t IC2Filter;
}TIM_Encoder_InitTypeDef;

课堂代码

代码功能:

1、利用TIM3的CH1(PC6)、CH2(PC7)作为编码器脉冲信号输入接口。

2、实现简单的电机测速。

主要硬件资源:

        TIM1(PA8)        正常输出通道

        TIM1(PB13)      互补输出通道

        TIM3(PC6)        编码器A相输入通道

        TIM3(PC7)        编码器B相输入通道

        使用TIM6定时计算电机速度,配置溢出时间为1ms,即1ms进入一次定时器6的更新中断。

        ADC1_CH9(PB1)        电压

        ADC1_CH0(PA0)        温度

        ADC1_CH9(PB0)        电流

        SD(PF10)          刹车信号输出

重要函数:

1、定时器初始化函数

初始化定时器通道IO,配置定时器、编码器接口等,增加TIM3更新中断。

2、更新中断回调函数

处理溢出中断,判断计数方向,记录溢出次数(通过TIMx_CR1:DIR判断。向上溢出就加一,向下溢出就减一),计算电机速度。

3、编码器计数函数

计算总的计数值,总计数值 = 计数器当前值 + 溢出次数 * 65536

4、电机速度计算函数

计算单位时间内计数变化量,然后计算电机的转速。

计算电机转速:

1、计算ms毫秒内计数变化量
2、计算1min内计数变化量:g_encode.speed * ((1000 / ms) * 60 ,
3、除以编码器旋转一圈的计数次数(倍频倍数 * 编码器分辨率)
4、除以减速比即可得出电机转速 

直流有刷电机选择磁电增量式编码器,分辨率11线。 

编码器配置参数介绍:

        IC1Polarity:在编码器模式下用于设置输入信号是否反相,它设定的是TIMx_CCER寄存器的CCxNP位和CCxP位。本实验不需要输入信号反相。

        IC1Selection:用来设置通道映射关系,他设定TIMx_CCMRx寄存器的CCxS[1:0]位的值。

/******************************* 第二部分  电机编码器测速 ****************************************************/

/********************************* 1 通用定时器 编码器程序 *************************************/

TIM_HandleTypeDef g_timx_encode_chy_handle;         /* 定时器x句柄 */
TIM_Encoder_InitTypeDef g_timx_encoder_chy_handle;  /* 定时器编码器句柄 */

/**
 * @brief       通用定时器TIMX 通道Y 编码器接口模式 初始化函数
 * @note
 *              通用定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
 *              通用定时器的时钟为APB1时钟的2倍, 而APB1为42M, 所以定时器时钟 = 84Mhz
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft=定时器工作频率,单位:Mhz
 *
 * @param       arr: 自动重装值。
 * @param       psc: 时钟预分频数
 * @retval      无
 */
void gtim_timx_encoder_chy_init(uint16_t arr, uint16_t psc)
{
    /* 定时器x配置 */
    g_timx_encode_chy_handle.Instance = GTIM_TIMX_ENCODER;                      /* 定时器x */
    g_timx_encode_chy_handle.Init.Prescaler = psc;                              /* 定时器分频 */
    g_timx_encode_chy_handle.Init.Period = arr;                                 /* 自动重装载值 */
    g_timx_encode_chy_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;       /* 时钟分频因子 */
    
    /* 定时器x编码器配置 */
    g_timx_encoder_chy_handle.EncoderMode = TIM_ENCODERMODE_TI12;               /* TI1、TI2都检测,4倍频 */
    g_timx_encoder_chy_handle.IC1Polarity = TIM_ICPOLARITY_RISING;              /* 输入极性,非反向 */
    g_timx_encoder_chy_handle.IC1Selection = TIM_ICSELECTION_DIRECTTI;          /* 输入通道选择 */
    g_timx_encoder_chy_handle.IC1Prescaler = TIM_ICPSC_DIV1;                    /* 不分频 */
    g_timx_encoder_chy_handle.IC1Filter = 10;                                   /* 滤波器设置 */
    g_timx_encoder_chy_handle.IC2Polarity = TIM_ICPOLARITY_RISING;              /* 输入极性,非反向 */
    g_timx_encoder_chy_handle.IC2Selection = TIM_ICSELECTION_DIRECTTI;          /* 输入通道选择 */
    g_timx_encoder_chy_handle.IC2Prescaler = TIM_ICPSC_DIV1;                    /* 不分频 */
    g_timx_encoder_chy_handle.IC2Filter = 10;                                   /* 滤波器设置 */
    HAL_TIM_Encoder_Init(&g_timx_encode_chy_handle, &g_timx_encoder_chy_handle);/* 初始化定时器x编码器 */
     
    HAL_TIM_Encoder_Start(&g_timx_encode_chy_handle,GTIM_TIMX_ENCODER_CH1);     /* 使能编码器通道1 */
    HAL_TIM_Encoder_Start(&g_timx_encode_chy_handle,GTIM_TIMX_ENCODER_CH2);     /* 使能编码器通道2 */
    __HAL_TIM_ENABLE_IT(&g_timx_encode_chy_handle,TIM_IT_UPDATE);               /* 使能更新中断 */
    __HAL_TIM_CLEAR_FLAG(&g_timx_encode_chy_handle,TIM_IT_UPDATE);              /* 清除更新中断标志位 */
    
}

/**
 * @brief       定时器底层驱动,时钟使能,引脚配置
                此函数会被HAL_TIM_Encoder_Init()调用
 * @param       htim:定时器句柄
 * @retval      无
 */
void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == GTIM_TIMX_ENCODER)
    {
        GPIO_InitTypeDef gpio_init_struct;
        GTIM_TIMX_ENCODER_CH1_GPIO_CLK_ENABLE();                                 /* 开启通道y的GPIO时钟 */
        GTIM_TIMX_ENCODER_CH2_GPIO_CLK_ENABLE();
        GTIM_TIMX_ENCODER_CH1_CLK_ENABLE();                                      /* 开启定时器时钟 */
        GTIM_TIMX_ENCODER_CH2_CLK_ENABLE();

        gpio_init_struct.Pin = GTIM_TIMX_ENCODER_CH1_GPIO_PIN;                   /* 通道y的GPIO口 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                                 /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_NOPULL;                                     /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                           /* 高速 */
        gpio_init_struct.Alternate = GTIM_TIMX_ENCODERCH1_GPIO_AF;               /* 端口复用 */
        HAL_GPIO_Init(GTIM_TIMX_ENCODER_CH1_GPIO_PORT, &gpio_init_struct);  
        
        gpio_init_struct.Pin = GTIM_TIMX_ENCODER_CH2_GPIO_PIN;                   /* 通道y的GPIO口 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                                 /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_NOPULL;                                     /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                           /* 高速 */
        gpio_init_struct.Alternate = GTIM_TIMX_ENCODERCH2_GPIO_AF;               /* 端口复用 */
        HAL_GPIO_Init(GTIM_TIMX_ENCODER_CH2_GPIO_PORT, &gpio_init_struct);         
       
        HAL_NVIC_SetPriority(GTIM_TIMX_ENCODER_INT_IRQn, 2, 0);                  /* 中断优先级设置 */
        HAL_NVIC_EnableIRQ(GTIM_TIMX_ENCODER_INT_IRQn);                          /* 开启中断 */
    }
}

/**
 * @brief       定时器中断服务函数
 * @param       无
 * @retval      无
 */
void GTIM_TIMX_ENCODER_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_timx_encode_chy_handle);
}


/******************************** 2 基本定时器 编码器程序 ************************************/

TIM_HandleTypeDef timx_handler;         /* 定时器参数句柄 */

/**
 * @brief       基本定时器TIMX定时中断初始化函数
 * @note
 *              基本定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
 *              基本定时器的时钟为APB1时钟的2倍, 而APB1为42M, 所以定时器时钟 = 84Mhz
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft=定时器工作频率,单位:Mhz
 *
 * @param       arr: 自动重装值。
 * @param       psc: 时钟预分频数
 * @retval      无
 */
void btim_timx_int_init(uint16_t arr, uint16_t psc)
{
    timx_handler.Instance = BTIM_TIMX_INT;                              /* 基本定时器X */
    timx_handler.Init.Prescaler = psc;                                  /* 设置预分频器  */
    timx_handler.Init.CounterMode = TIM_COUNTERMODE_UP;                 /* 向上计数器 */
    timx_handler.Init.Period = arr;                                     /* 自动装载值 */
    timx_handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;           /* 时钟分频因子 */
    HAL_TIM_Base_Init(&timx_handler);
    
    HAL_TIM_Base_Start_IT(&timx_handler);                               /* 使能基本定时器x和及其更新中断:TIM_IT_UPDATE */
    __HAL_TIM_CLEAR_IT(&timx_handler,TIM_IT_UPDATE);                    /* 清除更新中断标志位 */
}

/**
 * @brief       定时器底册驱动,开启时钟,设置中断优先级
                此函数会被HAL_TIM_Base_Init()函数调用
 * @param       无
 * @retval      无
 */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == BTIM_TIMX_INT)
    {
        BTIM_TIMX_INT_CLK_ENABLE();                                     /*使能TIM时钟*/
        HAL_NVIC_SetPriority(BTIM_TIMX_INT_IRQn, 1, 3);                 /* 抢占1,子优先级3,组2 */
        HAL_NVIC_EnableIRQ(BTIM_TIMX_INT_IRQn);                         /*开启ITM3中断*/
    }
}

/**
 * @brief       基本定时器TIMX中断服务函数
 * @param       无
 * @retval      无
 */
void BTIM_TIMX_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&timx_handler);                                  /*定时器回调函数*/
}


/******************************** 3 公用部分 编码器程序 ************************************/

volatile int g_timx_encode_count = 0;                                   /* 溢出次数 */

/**
 * @brief       定时器更新中断回调函数
 * @param        htim:定时器句柄指针
 * @note        此函数会被定时器中断函数共同调用的
 * @retval      无
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM3)
    {
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle))   /* 判断CR1的DIR位 */
        {
            g_timx_encode_count--;                                      /* DIR位为1,也就是递减计数 */
        }
        else
        {
            g_timx_encode_count++;                                      /* DIR位为0,也就是递增计数 */
        }
    }
    else if (htim->Instance == TIM6)
    {
        int Encode_now = gtim_get_encode();                             /* 获取编码器值,用于计算速度 */

        speed_computer(Encode_now, 50);                                 /* 中位平均值滤除编码器抖动数据,50ms计算一次速度*/
    }
}

/**
 * @brief       获取编码器的值
 * @param       无
 * @retval      编码器值
 */
int gtim_get_encode(void)
{
    return ( int32_t )__HAL_TIM_GET_COUNTER(&g_timx_encode_chy_handle) + g_timx_encode_count * 65536;       /* 当前计数值+之前累计编码器的值=总的编码器值 */
}

 冒泡排序:略

 一阶低通滤波:适用于对电机速度进行滤波。

公式为:Y(n)= qX(n) + (1-q)Y(n-1)
              其中X(n)为本次采样值;Y(n-1)为上次滤波输出值;Y(n)为本次滤波输出值,q为滤波系数
              q值越小则上一次输出对本次输出影响越大,整体曲线越平稳,但是对于速度变化的响应也会越慢

附链接15-直流有刷电机专题-第13讲 编码器测速(2)_哔哩哔哩_bilibili

通过累计计算10次电机速度,然后进行冒泡排序,把10次电机速度值从小到大排序,接着将中间的6次速度值累加求平均值,最后再进行一阶低通滤波。 

 一阶低通滤波公式:Y(n) = q * X(n) + (1-q) * Y(n-1)。

Y(n):本次滤波输出值

q:滤波系数(0~1)(q越大,响应越快,但曲线不平滑;q越小,曲线越平滑,但响应越慢)

X(n):本次采样值

Y(n-1):上次滤波输出值

/*************************************    第三部分    编码器测速    ****************************************************/

Motor_TypeDef g_motor_data;  /*电机参数变量*/
ENCODE_TypeDef g_encode;     /*编码器参数变量*/

/**
 * @brief       电机速度计算
 * @param       encode_now:当前编码器总的计数值
 *              ms:计算速度的间隔,中断1ms进入一次,例如ms = 5即5ms计算一次速度
 * @retval      无
 */
void speed_computer(int32_t encode_now, uint8_t ms)
{
    uint8_t i = 0, j = 0;
    float temp = 0.0;
    static uint8_t sp_count = 0, k = 0;
    static float speed_arr[10] = {0.0};                     /* 存储速度进行滤波运算 */

    if (sp_count == ms)                                     /* 计算一次速度 */
    {
        /* 计算电机转速 
           第一步 :计算ms毫秒内计数变化量
           第二步 ;计算1min内计数变化量:g_encode.speed * ((1000 / ms) * 60 ,
           第三步 :除以编码器旋转一圈的计数次数(倍频倍数 * 编码器分辨率)
           第四步 :除以减速比即可得出电机转速
        */
        g_encode.encode_now = encode_now;                                /* 取出编码器当前计数值 */
        g_encode.speed = (g_encode.encode_now - g_encode.encode_old);    /* 计算编码器计数值的变化量 */
        
        speed_arr[k++] = (float)(g_encode.speed * ((1000 / ms) * 60.0) / REDUCTION_RATIO / ROTO_RATIO );    /* 保存电机转速 */
        
        g_encode.encode_old = g_encode.encode_now;          /* 保存当前编码器的值 */

        /* 累计10次速度值,后续进行滤波*/
        if (k == 10)
        {
            for (i = 10; i >= 1; i--)                       /* 冒泡排序*/
            {
                for (j = 0; j < (i - 1); j++) 
                {
                    if (speed_arr[j] > speed_arr[j + 1])    /* 数值比较 */
                    { 
                        temp = speed_arr[j];                /* 数值换位 */
                        speed_arr[j] = speed_arr[j + 1];
                        speed_arr[j + 1] = temp;
                    }
                }
            }
            
            temp = 0.0;
            
            for (i = 2; i < 8; i++)                         /* 去除两边高低数据 */
            {
                temp += speed_arr[i];                       /* 将中间数值累加 */
            }
            
            temp = (float)(temp / 6);                       /*求速度平均值*/
            
            /* 一阶低通滤波
             * 公式为:Y(n)= qX(n) + (1-q)Y(n-1)
             * 其中X(n)为本次采样值;Y(n-1)为上次滤波输出值;Y(n)为本次滤波输出值,q为滤波系数
             * q值越小则上一次输出对本次输出影响越大,整体曲线越平稳,但是对于速度变化的响应也会越慢
             */
            g_motor_data.speed = (float)( ((float)0.48 * temp) + (g_motor_data.speed * (float)0.52) );
            k = 0;
        }
        sp_count = 0;
    }
    sp_count ++;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值