ADS1015驱动程序

ADS1015.c

/**************************************************************************/
/*!
    @file     Adafruit_ADS1015.c
    @author   K.Townsend (Adafruit Industries)
    @license  BSD (see license.txt)

    Driver for the ADS1015/ADS1115 ADC

    This is a library for the Adafruit MPL115A2 breakout
    ----> https://www.adafruit.com/products/???

    Adafruit invests time and resources providing this open source code,
    please support Adafruit and open-source hardware by purchasing
    products from Adafruit!

    @section  HISTORY

    v1.0 - First release
    Edit by FangZheng 2014-12-11
*/

#include "global.h"

static Uint16 m_i2cAddress;
static Uint16 m_conversionDelay;
static Uint16 m_bitShift;
static adsGain_t m_gain;
static Uint16 m_chn;
#if 0
/**************************************************************************/
/*!
    @brief  Writes 16-bits to the specified destination register
*/
/**************************************************************************/
static void writeRegister(Uint16 i2cAddress, Uint16 reg, Uint16 value)
{
    Wire.beginTransmission(i2cAddress);
    i2cwrite((Uint16)reg);
    i2cwrite((Uint16)(value >> 8));
    i2cwrite((Uint16)(value & 0xFF));
    Wire.endTransmission();
}

/**************************************************************************/
/*!
    @brief  Writes 16-bits to the specified destination register
*/
/**************************************************************************/
static Uint16 readRegister(Uint16 i2cAddress, Uint16 reg)
{
    Wire.beginTransmission(i2cAddress);
    i2cwrite(ADS1015_REG_POINTER_CONVERT);
    Wire.endTransmission();
    Wire.requestFrom(i2cAddress, (Uint16)2);
    return ((i2cread() << 8) | i2cread());
}
#endif
/**************************************************************************/
/*!
    @brief  Instantiates a new ADS1015 class w/appropriate properties
*/
/**************************************************************************/
void ads1015_init(void)
{
    m_i2cAddress = ADS1015_ADDRESS;
    m_conversionDelay = ADS1015_CONVERSIONDELAY;
    m_bitShift = 4;
    m_gain = GAIN_ONE; /* +/- 6.144V range (limited to VDD +0.3V max!) */
    m_chn = 0;
}

#if 0
/**************************************************************************/
/*!
    @brief  Sets the gain and input voltage range
*/
/**************************************************************************/
void setGain(adsGain_t gain)
{
    m_gain = gain;
}

/**************************************************************************/
/*!
    @brief  Gets a gain and input voltage range
*/
/**************************************************************************/
adsGain_t getGain()
{
    return m_gain;
}
#endif

/**************************************************************************/
/*!
    @brief  Gets a single-ended ADC reading from the specified channel
*/
/**************************************************************************/
Uint16 readADC_SingleEnded(Uint16 channel)
{
    Uint16  config;
    if (channel > 3)
    {
        return 0;
    }

    // Start with default values
    config = ADS1015_REG_CONFIG_CQUE_NONE    | // Disable the comparator (default val)
            ADS1015_REG_CONFIG_CLAT_NONLAT  | // Non-latching (default val)
            ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
            ADS1015_REG_CONFIG_CMODE_TRAD   | // Traditional comparator (default val)
            ADS1015_REG_CONFIG_DR_1600SPS   | // 1600 samples per second (default)
            ADS1015_REG_CONFIG_MODE_SINGLE;   // Single-shot mode (default)

    // Set PGA/voltage range
    config |= m_gain;

    // Set single-ended input channel
    switch (channel)
    {
    case (0):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_0;
        break;
    case (1):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_1;
        break;
    case (2):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_2;
        break;
    case (3):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_3;
        break;
    }

    // Set 'start single-conversion' bit
    config |= ADS1015_REG_CONFIG_OS_SINGLE;

    // Write config register to the ADC
    writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);

    // Wait for the conversion to complete
    //DELAY_US(m_conversionDelay);
    DELAY_US(350);

    // Read the conversion results
    // Shift 12-bit results right 4 bits for the ADS1015
    return readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
}


void read_adc_config(void)
{
    Uint16  config;

    // Start with default values
    config = ADS1015_REG_CONFIG_CQUE_NONE    | // Disable the comparator (default val)
            ADS1015_REG_CONFIG_CLAT_NONLAT  | // Non-latching (default val)
            ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
            ADS1015_REG_CONFIG_CMODE_TRAD   | // Traditional comparator (default val)
            ADS1015_REG_CONFIG_DR_1600SPS   | // 1600 samples per second (default)
            ADS1015_REG_CONFIG_MODE_SINGLE;   // Single-shot mode (default)

    // Set PGA/voltage range
    config |= m_gain;

    // Set single-ended input channel
    switch (m_chn)
    {
    case (0):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_0;
        break;
    case (1):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_1;
        break;
    case (2):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_2;
        break;
    case (3):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_3;
        break;
    }

    // Set 'start single-conversion' bit
    config |= ADS1015_REG_CONFIG_OS_SINGLE;

    // Write config register to the ADC
    writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);
    
}

void read_adc_reg(volatile Uint16 *res0, volatile Uint16 *res1)
{
    if(m_chn)
    {
        *res1 = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
        m_chn = 0;
    }
    else
    {
        *res0 = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
        m_chn = 1;
    }
}
void get_adc(volatile Uint16 *res0, volatile Uint16 *res1)
{
    #define PER_CNT 6
    static volatile Uint16 i = 0;

    if(i == 0)
    {
        read_adc_config();
    }

    if(i == PER_CNT)
    {
        read_adc_reg(res0, res1);
    }
    if(i++ > PER_CNT) i = 0;
}
#if 0
/**************************************************************************/
/*!
    @brief  Reads the conversion results, measuring the voltage
            difference between the P (AIN0) and N (AIN1) input.  Generates
            a signed value since the difference can be either
            positive or negative.
*/
/**************************************************************************/
int16_t readADC_Differential_0_1(void)
{
    Uint16 res, config;
    // Start with default values
    config = ADS1015_REG_CONFIG_CQUE_NONE    | // Disable the comparator (default val)
             ADS1015_REG_CONFIG_CLAT_NONLAT  | // Non-latching (default val)
             ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
             ADS1015_REG_CONFIG_CMODE_TRAD   | // Traditional comparator (default val)
             ADS1015_REG_CONFIG_DR_1600SPS   | // 1600 samples per second (default)
             ADS1015_REG_CONFIG_MODE_SINGLE;   // Single-shot mode (default)

    // Set PGA/voltage range
    config |= m_gain;

    // Set channels
    config |= ADS1015_REG_CONFIG_MUX_DIFF_0_1;          // AIN0 = P, AIN1 = N

    // Set 'start single-conversion' bit
    config |= ADS1015_REG_CONFIG_OS_SINGLE;

    // Write config register to the ADC
    writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);

    // Wait for the conversion to complete
    DELAY_US(m_conversionDelay);

    // Read the conversion results
    res = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
    if (m_bitShift == 0)
    {
        return (int16)res;
    }
    else
    {
        // Shift 12-bit results right 4 bits for the ADS1015,
        // making sure we keep the sign bit intact
        if (res > 0x07FF)
        {
            // negative number - extend the sign to 16th bit
            res |= 0xF000;
        }
        return (int16)res;
    }
}

/**************************************************************************/
/*!
    @brief  Reads the conversion results, measuring the voltage
            difference between the P (AIN2) and N (AIN3) input.  Generates
            a signed value since the difference can be either
            positive or negative.
*/
/**************************************************************************/
int16_t readADC_Differential_2_3(void)
{
    Uint16 res, config;
    // Start with default values
    config = ADS1015_REG_CONFIG_CQUE_NONE    | // Disable the comparator (default val)
             ADS1015_REG_CONFIG_CLAT_NONLAT  | // Non-latching (default val)
             ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
             ADS1015_REG_CONFIG_CMODE_TRAD   | // Traditional comparator (default val)
             ADS1015_REG_CONFIG_DR_1600SPS   | // 1600 samples per second (default)
             ADS1015_REG_CONFIG_MODE_SINGLE;   // Single-shot mode (default)

    // Set PGA/voltage range
    config |= m_gain;

    // Set channels
    config |= ADS1015_REG_CONFIG_MUX_DIFF_2_3;          // AIN2 = P, AIN3 = N

    // Set 'start single-conversion' bit
    config |= ADS1015_REG_CONFIG_OS_SINGLE;

    // Write config register to the ADC
    writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);

    // Wait for the conversion to complete
    DELAY_US(m_conversionDelay);

    // Read the conversion results
    res = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
    if (m_bitShift == 0)
    {
        return (int16)res;
    }
    else
    {
        // Shift 12-bit results right 4 bits for the ADS1015,
        // making sure we keep the sign bit intact
        if (res > 0x07FF)
        {
            // negative number - extend the sign to 16th bit
            res |= 0xF000;
        }
        return (int16)res;
    }
}

/**************************************************************************/
/*!
    @brief  Sets up the comparator to operate in basic mode, causing the
            ALERT/RDY pin to assert (go from high to low) when the ADC
            value exceeds the specified threshold.

            This will also set the ADC in continuous conversion mode.
*/
/**************************************************************************/
void startComparator_SingleEnded(Uint16 channel, int16_t threshold)
{
    // Start with default values
    Uint16 config = ADS1015_REG_CONFIG_CQUE_1CONV   | // Comparator enabled and asserts on 1 match
                    ADS1015_REG_CONFIG_CLAT_LATCH   | // Latching mode
                    ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
                    ADS1015_REG_CONFIG_CMODE_TRAD   | // Traditional comparator (default val)
                    ADS1015_REG_CONFIG_DR_1600SPS   | // 1600 samples per second (default)
                    ADS1015_REG_CONFIG_MODE_CONTIN  | // Continuous conversion mode
                    ADS1015_REG_CONFIG_MODE_CONTIN;   // Continuous conversion mode

    // Set PGA/voltage range
    config |= m_gain;

    // Set single-ended input channel
    switch (channel)
    {
    case (0):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_0;
        break;
    case (1):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_1;
        break;
    case (2):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_2;
        break;
    case (3):
        config |= ADS1015_REG_CONFIG_MUX_SINGLE_3;
        break;
    }

    // Set the high threshold register
    // Shift 12-bit results left 4 bits for the ADS1015
    writeRegister(m_i2cAddress, ADS1015_REG_POINTER_HITHRESH, threshold << m_bitShift);

    // Write config register to the ADC
    writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);
}

/**************************************************************************/
/*!
    @brief  In order to clear the comparator, we need to read the
            conversion results.  This function reads the last conversion
            results without changing the config value.
*/
/**************************************************************************/
int16_t getLastConversionResults(void)
{
    Uint16 res;
    // Wait for the conversion to complete
    DELAY_US(m_conversionDelay);

    // Read the conversion results
    res = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
    if (m_bitShift == 0)
    {
        return (int16)res;
    }
    else
    {
        // Shift 12-bit results right 4 bits for the ADS1015,
        // making sure we keep the sign bit intact
        if (res > 0x07FF)
        {
            // negative number - extend the sign to 16th bit
            res |= 0xF000;
        }
        return (int16)res;
    }
}
#endif

ADS1015.h

/**************************************************************************/
/*!
    @file     Adafruit_ADS1015.h
    @author   K. Townsend (Adafruit Industries)
    @license  BSD (see license.txt)

    This is a library for the Adafruit ADS1015 breakout board
    ----> https://www.adafruit.com/products/???

    Adafruit invests time and resources providing this open source code,
    please support Adafruit and open-source hardware by purchasing
    products from Adafruit!

    @section  HISTORY

    v1.0  - First release
    v1.1  - Added ADS1115 support - W. Earl
*/
/**************************************************************************/

#ifndef _ADS_1015_H_
#define _ADS_1015_H_

/*=========================================================================
    I2C ADDRESS/BITS
    -----------------------------------------------------------------------*/
#define ADS1015_ADDRESS                 (0x90)    // 1001 000 (ADDR = GND)
/*=========================================================================*/

/*=========================================================================
    CONVERSION DELAY (in uS)
    -----------------------------------------------------------------------*/
#define ADS1015_CONVERSIONDELAY         (10)
#define ADS1115_CONVERSIONDELAY         (8)
/*=========================================================================*/

/*=========================================================================
    POINTER REGISTER
    -----------------------------------------------------------------------*/
#define ADS1015_REG_POINTER_MASK        (0x03)
#define ADS1015_REG_POINTER_CONVERT     (0x00)
#define ADS1015_REG_POINTER_CONFIG      (0x01)
#define ADS1015_REG_POINTER_LOWTHRESH   (0x02)
#define ADS1015_REG_POINTER_HITHRESH    (0x03)
/*=========================================================================*/

/*=========================================================================
    CONFIG REGISTER
    -----------------------------------------------------------------------*/
#define ADS1015_REG_CONFIG_OS_MASK      (0x8000)
#define ADS1015_REG_CONFIG_OS_SINGLE    (0x8000)  // Write: Set to start a single-conversion
#define ADS1015_REG_CONFIG_OS_BUSY      (0x0000)  // Read: Bit = 0 when conversion is in progress
#define ADS1015_REG_CONFIG_OS_NOTBUSY   (0x8000)  // Read: Bit = 1 when device is not performing a conversion

#define ADS1015_REG_CONFIG_MUX_MASK     (0x7000)
#define ADS1015_REG_CONFIG_MUX_DIFF_0_1 (0x0000)  // Differential P = AIN0, N = AIN1 (default)
#define ADS1015_REG_CONFIG_MUX_DIFF_0_3 (0x1000)  // Differential P = AIN0, N = AIN3
#define ADS1015_REG_CONFIG_MUX_DIFF_1_3 (0x2000)  // Differential P = AIN1, N = AIN3
#define ADS1015_REG_CONFIG_MUX_DIFF_2_3 (0x3000)  // Differential P = AIN2, N = AIN3
#define ADS1015_REG_CONFIG_MUX_SINGLE_0 (0x4000)  // Single-ended AIN0
#define ADS1015_REG_CONFIG_MUX_SINGLE_1 (0x5000)  // Single-ended AIN1
#define ADS1015_REG_CONFIG_MUX_SINGLE_2 (0x6000)  // Single-ended AIN2
#define ADS1015_REG_CONFIG_MUX_SINGLE_3 (0x7000)  // Single-ended AIN3

#define ADS1015_REG_CONFIG_PGA_MASK     (0x0E00)
#define ADS1015_REG_CONFIG_PGA_6_144V   (0x0000)  // +/-6.144V range = Gain 2/3
#define ADS1015_REG_CONFIG_PGA_4_096V   (0x0200)  // +/-4.096V range = Gain 1
#define ADS1015_REG_CONFIG_PGA_2_048V   (0x0400)  // +/-2.048V range = Gain 2 (default)
#define ADS1015_REG_CONFIG_PGA_1_024V   (0x0600)  // +/-1.024V range = Gain 4
#define ADS1015_REG_CONFIG_PGA_0_512V   (0x0800)  // +/-0.512V range = Gain 8
#define ADS1015_REG_CONFIG_PGA_0_256V   (0x0A00)  // +/-0.256V range = Gain 16

#define ADS1015_REG_CONFIG_MODE_MASK    (0x0100)
#define ADS1015_REG_CONFIG_MODE_CONTIN  (0x0000)  // Continuous conversion mode
#define ADS1015_REG_CONFIG_MODE_SINGLE  (0x0100)  // Power-down single-shot mode (default)

#define ADS1015_REG_CONFIG_DR_MASK      (0x00E0)
#define ADS1015_REG_CONFIG_DR_128SPS    (0x0000)  // 128 samples per second
#define ADS1015_REG_CONFIG_DR_250SPS    (0x0020)  // 250 samples per second
#define ADS1015_REG_CONFIG_DR_490SPS    (0x0040)  // 490 samples per second
#define ADS1015_REG_CONFIG_DR_920SPS    (0x0060)  // 920 samples per second
#define ADS1015_REG_CONFIG_DR_1600SPS   (0x0080)  // 1600 samples per second (default)
#define ADS1015_REG_CONFIG_DR_2400SPS   (0x00A0)  // 2400 samples per second
#define ADS1015_REG_CONFIG_DR_3300SPS   (0x00C0)  // 3300 samples per second

#define ADS1015_REG_CONFIG_CMODE_MASK   (0x0010)
#define ADS1015_REG_CONFIG_CMODE_TRAD   (0x0000)  // Traditional comparator with hysteresis (default)
#define ADS1015_REG_CONFIG_CMODE_WINDOW (0x0010)  // Window comparator

#define ADS1015_REG_CONFIG_CPOL_MASK    (0x0008)
#define ADS1015_REG_CONFIG_CPOL_ACTVLOW (0x0000)  // ALERT/RDY pin is low when active (default)
#define ADS1015_REG_CONFIG_CPOL_ACTVHI  (0x0008)  // ALERT/RDY pin is high when active

#define ADS1015_REG_CONFIG_CLAT_MASK    (0x0004)  // Determines if ALERT/RDY pin latches once asserted
#define ADS1015_REG_CONFIG_CLAT_NONLAT  (0x0000)  // Non-latching comparator (default)
#define ADS1015_REG_CONFIG_CLAT_LATCH   (0x0004)  // Latching comparator

#define ADS1015_REG_CONFIG_CQUE_MASK    (0x0003)
#define ADS1015_REG_CONFIG_CQUE_1CONV   (0x0000)  // Assert ALERT/RDY after one conversions
#define ADS1015_REG_CONFIG_CQUE_2CONV   (0x0001)  // Assert ALERT/RDY after two conversions
#define ADS1015_REG_CONFIG_CQUE_4CONV   (0x0002)  // Assert ALERT/RDY after four conversions
#define ADS1015_REG_CONFIG_CQUE_NONE    (0x0003)  // Disable the comparator and put ALERT/RDY in high state (default)
/*=========================================================================*/

typedef enum
{
    GAIN_TWOTHIRDS    = ADS1015_REG_CONFIG_PGA_6_144V,
    GAIN_ONE          = ADS1015_REG_CONFIG_PGA_4_096V,
    GAIN_TWO          = ADS1015_REG_CONFIG_PGA_2_048V,
    GAIN_FOUR         = ADS1015_REG_CONFIG_PGA_1_024V,
    GAIN_EIGHT        = ADS1015_REG_CONFIG_PGA_0_512V,
    GAIN_SIXTEEN      = ADS1015_REG_CONFIG_PGA_0_256V
} adsGain_t;

extern void ads1015_init(void);

extern Uint16 readADC_SingleEnded(Uint16 channel);

extern void get_adc(volatile Uint16 *res0, volatile Uint16 *res1);

#endif

i2c

static void IIC_Delay(void)
{
    DELAY_US(1);
}
static void IIC_Delay_Half(void)
{
    DELAY_US(1);
}
static Uint16 IsCharOne(void)
{
    Uint16 retcode = 0;
    if (SDA_IN())
    {
        retcode = 1;
    }
    return retcode;
}

static void StartRom(void)
{
    SET_SDA_OUT();
    SET_SCL_OUT();
    SetSDA();
    IIC_Delay();
    SetSCL();
    IIC_Delay();
    ClrSDA();
    IIC_Delay();
    ClrSCL();
    IIC_Delay();
    
}

static void StopRom(void)
{
    ClrSDA();
    IIC_Delay();
    SetSCL();
    IIC_Delay();
    SetSDA();
    IIC_Delay();
    ClrSCL();
    IIC_Delay();
}

static Uint16 SendToRom(Uint16 data)
{
    Uint16 count, retcode = 0;
    for (count = 0; count < 8; count++)
    {
        if ((data & BIT7) == BIT7)
        {
            SetSDA();
        }
        else
        {
            ClrSDA();
        }
        IIC_Delay();
        SetSCL();
        data = data << 1;
        IIC_Delay();
        ClrSCL();
        //IIC_Delay();
    }
    SET_SDA_IN();
    IIC_Delay();
    SetSCL();
    //IIC_Delay();
    IIC_Delay_Half();
    retcode = IsCharOne();
    IIC_Delay_Half();
    //IIC_Delay();
    ClrSCL();
    IIC_Delay();
    SET_SDA_OUT();
    
    return retcode;
}

static Uint16 ReceiveFromRom(void)
{
    Uint16 m = 0, count;
    SET_SDA_IN();
    IIC_Delay();
    for (count = 0; count < 8; count++)
    {
        m = m << 1;
        SetSCL();
        //IIC_Delay();
        //DELAY_US(3);
        IIC_Delay_Half();
        if (IsCharOne())
        {
            m |= BIT0;
        }
        //IIC_Delay();
        //DELAY_US(2);
        IIC_Delay_Half();
        ClrSCL();
        IIC_Delay();
    }
    SET_SDA_OUT();
    
    return m;
}

static void mack(void)
{
    ClrSDA();
    IIC_Delay();
    SetSCL();
    IIC_Delay();
    ClrSCL();
    IIC_Delay();
}

static void nmack(void)
{
    SetSDA();
    IIC_Delay();
    SetSCL();
    IIC_Delay();
    ClrSCL();
    IIC_Delay();
}
void writeRegister(Uint16 i2cAddress, Uint16 reg, Uint16 value) 
{
    Uint16 i;

    EALLOW;
    for(i = 0; i < ADS_RE_COUNT; i++)
    {               
        StopRom();
        //IIC_Delay();
        IIC_Delay();
        StartRom();
        if(SendToRom(i2cAddress) == 1)
            continue;
        if(SendToRom(reg&0xFF) == 1)
            continue;
        if(SendToRom(value>>8) == 1)
            continue;
        if(SendToRom(value&0xFF) == 1)
            continue;
        
        break;        
    }    
    StopRom(); 
    IIC_Delay();
    EDIS;
    
    //return i;
}

Uint16 readRegister(Uint16 i2cAddress, Uint16 reg) 
{
    Uint16 i, ret;

    EALLOW;
    for(i = 0; i < ADS_RE_COUNT; i++)
    {               
        StopRom();
        IIC_Delay();
        StartRom();
        if(SendToRom(i2cAddress) == 1)
            continue;
        if(SendToRom(ADS1015_REG_POINTER_CONVERT) == 1)
            continue;
        StopRom();
        IIC_Delay();
        StartRom();
        if(SendToRom(i2cAddress|BIT0) == 1)
            continue;
        ret = ReceiveFromRom() << 8;
        mack();

        ret |= ReceiveFromRom();
        mack();

        break;        
    }    
    StopRom(); 
    IIC_Delay();
    EDIS;
    
    return ret;  
}

做个备份以后遇到底层的东西就可以省的心了

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值