Linux系统读取设备树硬件信息

Linux系统读取设备树硬件信息

1. 设备树简介

设备树(Device Tree)是一种描述硬件信息的数据结构,用于在Linux系统中传递硬件信息给内核。它替代了传统的硬编码方式,使内核能够更好地适应不同的硬件平台。

设备树的主要作用:

  • 描述硬件组件的存在和配置
  • 传递硬件参数给内核驱动
  • 减少内核中的平台特定代码
  • 提高系统的可移植性

2. 读取设备树的方法

2.1 内核空间基础API

2.1.1 OF API(Open Firmware API)

OF API是内核中最常用的设备树访问接口,用于在驱动中获取设备树信息。

设备树示例

my-device {
    compatible = "vendor,my-device";
    reg = <0x10000000 0x1000>;
    interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
    clock-frequency = <24000000>;
    enable = <1>;
    status = "okay";
    
    #address-cells = <1>;
    #size-cells = <1>;
    
    ranges;
    
    sub-device {
        compatible = "vendor,sub-device";
        reg = <0x0 0x100>;
    };
};

主要函数

  • of_find_node_by_name():通过节点名称查找设备树节点
  • of_find_node_by_path():通过路径查找设备树节点
  • of_find_compatible_node():通过compatible属性查找设备树节点
  • of_property_read_u32():读取32位整数值
  • of_property_read_string():读取字符串值
  • of_property_read_bool():读取布尔值
  • of_property_read_u32_array():读取32位整数数组
  • of_property_count_elems_of_size():计算属性元素个数
  • of_get_property():获取属性的原始数据
  • of_device_is_available():检查设备是否可用

示例代码

#include <linux/of.h>

static int my_driver_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *node = dev->of_node;
    u32 value;
    const char *string;
    int ret;
    
    // 读取32位整数值
    ret = of_property_read_u32(node, "reg", &value);
    if (ret) {
        dev_err(dev, "Failed to read reg property: %d\n", ret);
        return ret;
    }
    dev_info(dev, "reg value: %d\n", value);
    
    // 读取字符串值
    ret = of_property_read_string(node, "compatible", &string);
    if (ret) {
        dev_err(dev, "Failed to read compatible property: %d\n", ret);
        return ret;
    }
    dev_info(dev, "compatible: %s\n", string);
    
    // 检查布尔属性
    if (of_property_read_bool(node, "enable")) {
        dev_info(dev, "Enable property is set\n");
    }
    
    return 0;
}
2.1.2 GPIO相关API

专门用于获取GPIO相关信息的API,包括传统的of_gpio_*函数和现代的gpiod_*函数族。

设备树示例

leds {
    compatible = "my-led-driver";
    led1-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;  // 低电平有效
    led2-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>; // 高电平有效
    enable-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
    
    // 多个相同功能的GPIO
    led-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>,
                <&gpio0 5 GPIO_ACTIVE_LOW>,
                <&gpio0 6 GPIO_ACTIVE_LOW>;
};

主要函数

*传统of_gpio_函数

  • of_get_gpio():从设备树中获取GPIO编号
  • of_get_gpio_flags():从设备树中获取GPIO编号和标志
  • of_gpio_named_count():计算指定名称的GPIO数量
  • of_gpio_get():获取指定名称的GPIO

*现代gpiod_函数族

  • gpiod_get():获取单个GPIO描述符
  • gpiod_get_array():获取多个GPIO描述符
  • gpiod_get_index():通过索引获取GPIO描述符
  • gpiod_put():释放GPIO描述符
  • gpiod_put_array():释放多个GPIO描述符
  • gpiod_direction_input():设置GPIO为输入方向
  • gpiod_direction_output():设置GPIO为输出方向并设置初始值
  • gpiod_get_value():读取GPIO的值
  • gpiod_set_value():设置GPIO的值

示例代码

*传统of_gpio_函数示例

#include <linux/of_gpio.h>

static int my_led_driver_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *node = dev->of_node;
    int led1_gpio, led2_gpio, enable_gpio;
    int num_leds, i;
    int ret;
    
    // 获取单个GPIO
    led1_gpio = of_get_gpio_flags(node, "led1-gpios", 0, NULL);
    if (led1_gpio < 0) {
        dev_err(dev, "Failed to get led1-gpios: %d\n", led1_gpio);
        return led1_gpio;
    }
    
    led2_gpio = of_get_gpio_flags(node, "led2-gpios", 0, NULL);
    if (led2_gpio < 0) {
        dev_err(dev, "Failed to get led2-gpios: %d\n", led2_gpio);
        return led2_gpio;
    }
    
    enable_gpio = of_get_gpio_flags(node, "enable-gpios", 0, NULL);
    if (enable_gpio < 0) {
        dev_err(dev, "Failed to get enable-gpios: %d\n", enable_gpio);
        return enable_gpio;
    }
    
    // 获取多个GPIO
    num_leds = of_gpio_named_count(node, "led-gpios");
    dev_info(dev, "Found %d LEDs\n", num_leds);
    
    for (i = 0; i < num_leds; i++) {
        int gpio = of_get_gpio_flags(node, "led-gpios", i, NULL);
        if (gpio < 0) {
            dev_err(dev, "Failed to get led-gpios[%d]: %d\n", i, gpio);
            return gpio;
        }
        dev_info(dev, "LED %d GPIO: %d\n", i, gpio);
    }
    
    return 0;
}

*现代gpiod_函数族示例

#include <linux/gpio/consumer.h>

static struct gpio_desc *led1_gpio;
static struct gpio_desc *led2_gpio;
static struct gpio_desc *enable_gpio;
static struct gpio_descs *led_array;

static int my_led_driver_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    int ret;
    
    // 获取单个GPIO
    led1_gpio = gpiod_get(dev, "led1", GPIOD_OUT_LOW);
    if (IS_ERR(led1_gpio)) {
        ret = PTR_ERR(led1_gpio);
        dev_err(dev, "Failed to get led1 GPIO: %d\n", ret);
        return ret;
    }
    
    led2_gpio = gpiod_get(dev, "led2", GPIOD_OUT_LOW);
    if (IS_ERR(led2_gpio)) {
        ret = PTR_ERR(led2_gpio);
        dev_err(dev, "Failed to get led2 GPIO: %d\n", ret);
        gpiod_put(led1_gpio);
        return ret;
    }
    
    enable_gpio = gpiod_get(dev, "enable", GPIOD_OUT_LOW);
    if (IS_ERR(enable_gpio)) {
        ret = PTR_ERR(enable_gpio);
        dev_err(dev, "Failed to get enable GPIO: %d\n", ret);
        gpiod_put(led1_gpio);
        gpiod_put(led2_gpio);
        return ret;
    }
    
    // 获取多个GPIO
    led_array = gpiod_get_array(dev, "led", GPIOD_OUT_LOW);
    if (IS_ERR(led_array)) {
        ret = PTR_ERR(led_array);
        dev_err(dev, "Failed to get led array: %d\n", ret);
        gpiod_put(led1_gpio);
        gpiod_put(led2_gpio);
        gpiod_put(enable_gpio);
        return ret;
    }
    
    dev_info(dev, "Found %zu LEDs in array\n", led_array->ndescs);
    
    // 使用GPIO
    gpiod_set_value(enable_gpio, 1);  // 启用设备
    gpiod_set_value(led1_gpio, 1);    // 打开LED1
    gpiod_set_value(led2_gpio, 1);    // 打开LED2
    
    // 使用GPIO数组
    for (int i = 0; i < led_array->ndescs; i++) {
        gpiod_set_value(&led_array->desc[i], 1);  // 打开所有LED
    }
    
    return 0;
}

static int my_led_driver_remove(struct platform_device *pdev)
{
    // 释放GPIO
    if (led_array) {
        gpiod_put_array(led_array);
    }
    if (enable_gpio) {
        gpiod_put(enable_gpio);
    }
    if (led2_gpio) {
        gpiod_put(led2_gpio);
    }
    if (led1_gpio) {
        gpiod_put(led1_gpio);
    }
    
    return 0;
}
2.1.3 平台数据API

通过平台数据结构获取设备树信息。

设备树示例

my-platform-device {
    compatible = "vendor,my-platform-device";
    reg = <0x10000000 0x1000>,
          <0x20000000 0x2000>;
    reg-names = "control", "data";
    interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
    clock-frequency = <24000000>;
    status = "okay";
};

主要函数

  • platform_get_resource():获取平台资源
  • platform_get_irq():获取中断号
  • platform_get_resource_byname():通过名称获取平台资源

示例代码

#include <linux/platform_device.h>

static int my_platform_driver_probe(struct platform_device *pdev)
{
    struct resource *res;
    int irq;
    
    // 获取内存资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(&pdev->dev, "Failed to get memory resource\n");
        return -EINVAL;
    }
    dev_info(&pdev->dev, "Memory resource: start=0x%lx, end=0x%lx\n",
             res->start, res->end);
    
    // 获取中断资源
    irq = platform_get_irq(pdev, 0);
    if (irq < 0) {
        dev_err(&pdev->dev, "Failed to get irq\n");
        return irq;
    }
    dev_info(&pdev->dev, "IRQ: %d\n", irq);
    
    // 通过名称获取资源
    res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control");
    if (res) {
        dev_info(&pdev->dev, "Control memory: start=0x%lx, end=0x%lx\n",
                 res->start, res->end);
    }
    
    return 0;
}

2.2 内核空间专用API

2.2.1 时钟相关API

专门用于获取时钟相关信息的API。

设备树示例

my-clock-device {
    compatible = "vendor,my-clock-device";
    reg = <0x10000000 0x1000>;
    clocks = <&clk_sys>, <&clk_peri>;
    clock-names = "core", "peripheral";
    clock-frequency = <24000000>;
    status = "okay";
};

主要函数

  • of_clk_get():从设备树中获取时钟
  • of_clk_get_by_name():通过名称从设备树中获取时钟
  • of_clk_count():计算设备的时钟数量
  • of_clk_add_provider():添加时钟提供者
  • of_clk_set_defaults():设置默认时钟配置

示例代码

#include <linux/clk.h>
#include <linux/of.h>

static int my_driver_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *node = dev->of_node;
    struct clk *clk;
    int num_clocks;
    int ret;
    
    // 计算时钟数量
    num_clocks = of_clk_count(node);
    dev_info(dev, "Found %d clocks\n", num_clocks);
    
    // 获取默认时钟
    clk = of_clk_get(node, 0);
    if (IS_ERR(clk)) {
        ret = PTR_ERR(clk);
        dev_err(dev, "Failed to get clock: %d\n", ret);
        return ret;
    }
    
    // 启用时钟
    ret = clk_prepare_enable(clk);
    if (ret) {
        dev_err(dev, "Failed to enable clock: %d\n", ret);
        clk_put(clk);
        return ret;
    }
    
    dev_info(dev, "Clock rate: %lu Hz\n", clk_get_rate(clk));
    
    // 禁用时钟
    clk_disable_unprepare(clk);
    clk_put(clk);
    
    // 通过名称获取时钟
    clk = of_clk_get_by_name(node, "core");
    if (!IS_ERR(clk)) {
        dev_info(dev, "Core clock rate: %lu Hz\n", clk_get_rate(clk));
        clk_put(clk);
    }
    
    return 0;
}
2.2.2 中断相关API

专门用于获取中断相关信息的API。

设备树示例

my-interrupt-device {
    compatible = "vendor,my-interrupt-device";
    reg = <0x10000000 0x1000>;
    interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 11 IRQ_TYPE_EDGE_RISING>;
    interrupt-names = "rx", "tx";
    status = "okay";
};

主要函数

  • of_irq_get():从设备树中获取中断号
  • of_irq_get_byname():通过名称从设备树中获取中断号
  • of_irq_count():计算设备的中断数量
  • of_irq_to_resource():将中断转换为资源
  • of_msi_map():映射MSI中断

示例代码

#include <linux/of_irq.h>

static int my_driver_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *node = dev->of_node;
    int irq;
    int num_irqs;
    
    // 计算中断数量
    num_irqs = of_irq_count(node);
    dev_info(dev, "Found %d interrupts\n", num_irqs);
    
    // 获取默认中断
    irq = of_irq_get(node, 0);
    if (irq < 0) {
        dev_err(dev, "Failed to get irq: %d\n", irq);
        return irq;
    }
    
    dev_info(dev, "Got interrupt: %d\n", irq);
    
    // 通过名称获取中断
    irq = of_irq_get_byname(node, "rx");
    if (irq > 0) {
        dev_info(dev, "Got RX interrupt: %d\n", irq);
    }
    
    return 0;
}
2.2.3 I2C相关API

专门用于获取I2C总线和设备信息的API。

设备树示例

i2c@1c2ac00 {
    compatible = "allwinner,sun8i-h3-i2c";
    reg = <0x01c2ac00 0x400>;
    clocks = <&ccu CLK_BUS_I2C0>;
    resets = <&ccu RST_BUS_I2C0>;
    interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
    pinctrl-names = "default";
    pinctrl-0 = <&i2c0_pins>;
    status = "okay";
    
    my-i2c-device@50 {
        compatible = "vendor,my-i2c-device";
        reg = <0x50>;
        power-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
        status = "okay";
    };
};

主要函数

  • of_i2c_get_address():从设备树中获取I2C设备地址
  • of_i2c_register_device():注册I2C设备
  • of_find_i2c_device_by_node():通过节点查找I2C设备
  • i2c_new_device():创建新的I2C设备
  • i2c_new_ancillary_device():创建辅助I2C设备

示例代码

#include <linux/i2c.h>

static int my_i2c_driver_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct device *dev = &client->dev;
    struct device_node *node = dev->of_node;
    u32 address;
    int ret;
    
    // 获取I2C设备地址
    ret = of_i2c_get_address(node, 0, &address);
    if (ret) {
        dev_err(dev, "Failed to get I2C address: %d\n", ret);
        return ret;
    }
    
    dev_info(dev, "I2C address: 0x%x\n", address);
    
    // 读取设备树属性
    if (of_property_read_bool(node, "power-gpios")) {
        dev_info(dev, "Power GPIO property found\n");
    }
    
    return 0;
}
2.2.4 SPI相关API

专门用于获取SPI总线和设备信息的API。

设备树示例

spi@1c68000 {
    compatible = "allwinner,sun8i-h3-spi";
    reg = <0x01c68000 0x1000>;
    clocks = <&ccu CLK_BUS_SPI0>;
    resets = <&ccu RST_BUS_SPI0>;
    interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
    pinctrl-names = "default";
    pinctrl-0 = <&spi0_pins>;
    status = "okay";
    
    my-spi-device@0 {
        compatible = "vendor,my-spi-device";
        reg = <0>;
        spi-max-frequency = <10000000>;
        spi-cpha;
        spi-cpol;
        status = "okay";
    };
};

主要函数

  • of_find_spi_device_by_node():通过节点查找SPI设备
  • spi_new_device():创建新的SPI设备
  • spi_parse_dt():解析SPI设备树节点
  • of_spi_max_speed_hz():获取SPI最大速度

示例代码

#include <linux/spi/spi.h>

static int my_spi_driver_probe(struct spi_device *spi)
{
    struct device *dev = &spi->dev;
    struct device_node *node = dev->of_node;
    u32 max_speed;
    int ret;
    
    dev_info(dev, "SPI device registered: %s\n", dev_name(dev));
    dev_info(dev, "SPI mode: %d\n", spi->mode);
    dev_info(dev, "SPI bits per word: %d\n", spi->bits_per_word);
    dev_info(dev, "SPI max speed: %d Hz\n", spi->max_speed_hz);
    
    // 读取设备树属性
    ret = of_property_read_u32(node, "max-speed", &max_speed);
    if (!ret) {
        dev_info(dev, "max-speed from DT: %d Hz\n", max_speed);
    }
    
    return 0;
}
2.2.5 通用资源API

用于获取各种硬件资源的通用API。

设备树示例

my-resource-device {
    compatible = "vendor,my-resource-device";
    reg = <0x10000000 0x1000>,  /* 控制寄存器 */
          <0x20000000 0x4000>;  /* 数据寄存器 */
    reg-names = "control", "data";
    interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clk_sys>;
    clock-names = "core";
    status = "okay";
};

主要函数

  • of_address_to_resource():将设备树地址转换为资源
  • of_iomap():映射设备树中的内存区域
  • of_get_address():获取设备树中的地址信息
  • of_resource_count():计算设备的资源数量
  • of_find_property():查找设备树属性

示例代码

#include <linux/of_address.h>

static int my_driver_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *node = dev->of_node;
    struct resource res;
    void __iomem *base;
    int ret;
    
    // 获取地址资源
    ret = of_address_to_resource(node, 0, &res);
    if (ret) {
        dev_err(dev, "Failed to get address resource: %d\n", ret);
        return ret;
    }
    
    dev_info(dev, "Memory resource: start=0x%lx, end=0x%lx\n",
             res.start, res.end);
    
    // 映射内存区域
    base = of_iomap(node, 0);
    if (!base) {
        dev_err(dev, "Failed to map memory\n");
        return -ENOMEM;
    }
    
    // 使用映射的内存
    dev_info(dev, "Memory mapped successfully\n");
    
    // 取消映射
    iounmap(base);
    
    return 0;
}

3. 设备树示例

3.1 LED GPIO设备树示例

leds {
    compatible = "my-led-driver";
    led1-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;  // 低电平有效
    led2-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>; // 高电平有效
    enable-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
    
    // 多个相同功能的GPIO
    led-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>,
                <&gpio0 5 GPIO_ACTIVE_LOW>,
                <&gpio0 6 GPIO_ACTIVE_LOW>;
};

3.2 读取示例

3.2.1 内核驱动读取示例
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>

static int my_led_driver_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *node = dev->of_node;
    struct gpio_desc *led1, *led2, *enable;
    struct gpio_descs *leds;
    int ret, i;
    
    dev_info(dev, "Probing LED driver\n");
    
    // 获取单个GPIO
    led1 = gpiod_get(dev, "led1", GPIOD_OUT_LOW);
    if (IS_ERR(led1)) {
        ret = PTR_ERR(led1);
        dev_err(dev, "Failed to get led1 GPIO: %d\n", ret);
        return ret;
    }
    
    led2 = gpiod_get(dev, "led2", GPIOD_OUT_LOW);
    if (IS_ERR(led2)) {
        ret = PTR_ERR(led2);
        dev_err(dev, "Failed to get led2 GPIO: %d\n", ret);
        gpiod_put(led1);
        return ret;
    }
    
    enable = gpiod_get(dev, "enable", GPIOD_OUT_LOW);
    if (IS_ERR(enable)) {
        ret = PTR_ERR(enable);
        dev_err(dev, "Failed to get enable GPIO: %d\n", ret);
        gpiod_put(led1);
        gpiod_put(led2);
        return ret;
    }
    
    // 获取多个GPIO
    leds = gpiod_get_array(dev, "led", GPIOD_OUT_LOW);
    if (IS_ERR(leds)) {
        ret = PTR_ERR(leds);
        dev_err(dev, "Failed to get led GPIOs: %d\n", ret);
        gpiod_put(led1);
        gpiod_put(led2);
        gpiod_put(enable);
        return ret;
    }
    
    dev_info(dev, "Found %zu LEDs in array\n", leds->ndescs);
    
    // 测试GPIO操作
    gpiod_set_value(enable, 1);  // 启用LEDs
    
    gpiod_set_value(led1, 1);    // 打开LED1
    msleep(1000);
    gpiod_set_value(led1, 0);    // 关闭LED1
    
    gpiod_set_value(led2, 1);    // 打开LED2
    msleep(1000);
    gpiod_set_value(led2, 0);    // 关闭LED2
    
    // 测试LED数组
    for (i = 0; i < leds->ndescs; i++) {
        gpiod_set_value(leds->desc[i], 1);
        msleep(500);
    }
    
    for (i = 0; i < leds->ndescs; i++) {
        gpiod_set_value(leds->desc[i], 0);
        msleep(500);
    }
    
    gpiod_set_value(enable, 0);  // 禁用LEDs
    
    // 释放资源
    gpiod_put_array(leds);
    gpiod_put(enable);
    gpiod_put(led2);
    gpiod_put(led1);
    
    return 0;
}

static int my_led_driver_remove(struct platform_device *pdev)
{
    dev_info(&pdev->dev, "Removing LED driver\n");
    return 0;
}

static const struct of_device_id my_led_driver_of_match[] = {
    { .compatible = "my-led-driver" },
    { }
};

static struct platform_driver my_led_driver = {
    .driver = {
        .name = "my-led-driver",
        .of_match_table = my_led_driver_of_match,
    },
    .probe = my_led_driver_probe,
    .remove = my_led_driver_remove,
};

module_platform_driver(my_led_driver);

MODULE_DESCRIPTION("LED driver using device tree");
MODULE_LICENSE("GPL");
3.2.2 用户空间读取示例
#!/bin/bash

# 查看LED设备树节点
LED_NODE="/sys/firmware/devicetree/base/leds"

if [ -d "$LED_NODE" ]; then
    echo "Found LEDs node"
    
    # 读取compatible属性
    if [ -f "$LED_NODE/compatible" ]; then
        echo -n "compatible: "
        cat "$LED_NODE/compatible"
        echo
    fi
    
    # 读取GPIO属性
    for gpio in led1-gpios led2-gpios enable-gpios led-gpios; do
        if [ -f "$LED_NODE/$gpio" ]; then
            echo -n "$gpio: "
            hexdump -C "$LED_NODE/$gpio"
            echo
        fi
    done
else
    echo "LEDs node not found"
fi

4. 常见问题与解决方案

问题原因解决方案
节点查找失败节点路径或名称错误检查设备树结构,确保路径正确
属性读取失败属性不存在或类型错误检查设备树中的属性定义,确保类型匹配
GPIO获取失败GPIO编号错误或被占用检查设备树中的GPIO定义,确保GPIO可用
权限错误权限不足使用root权限或正确设置文件权限
设备树格式错误设备树语法错误使用dtc工具检查设备树语法

5. 总结

Linux系统提供了多种方法来读取设备树硬件信息,包括内核空间的OF API、GPIO相关API、平台数据API,以及用户空间的sysfs接口、dtc工具和libfdt库。

正确使用这些方法可以帮助开发人员:

  • 更好地理解硬件配置
  • 编写更通用的设备驱动
  • 快速调试硬件相关问题
  • 提高系统的可移植性

随着嵌入式系统的不断发展,设备树在Linux系统中的作用越来越重要,掌握设备树的读取方法对于嵌入式开发人员来说是一项必备技能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值