xilinx zynq7020学习笔记

一 .前言

最近在做有关zynq的项目。刚刚入手,以下是一些比较初级的操作,只适合入门道友观摩

将根文件系统通过NFS命令挂载到zynq板

setenv bootargs ‘console=ttyPS0,115200 root=/dev/nfs rw nfsroot=192.168.1.25:/home/growup/linux/nfs/rootfs ip=192.168.1.24:192.168.1.25:192.168.1.1:255.255.255.0::eth0:off‘
saveenv
setenv bootargs 'console=ttyPS0,115200 root=/dev/nfs nfsroot=192.168.1.25:/home/jdcy/linux/nfs/rootfs, proto=tcp,nfsvers=4 ip=dhcp rw'

mount -t nfs -o nolock 192.168.1.27:/home/jdcy/linux/nfs/mod /mnt

setenv root=/dev/nfs rw nfsroot=192.168.1.25:/home/zynq/linux/nfs/rootfs ip=192.168.1.24:192.168.1.25:192.168.1.1:255.255.255.0::eth0:off‘

setenv bootcmd 'tftpboot 0x2080000 zImage; tftpboot 0x2000000 system.dtb; bootz 0x2080000 - 0x2000000 '

制作arm版本Ubuntu根文件系统

下载Ubuntu模拟器与挂载脚本
sudo apt-get install qemu-user-static
cd /home/jdcy/xilinx_zynq/pkg/ubuntu16

sudo cp /usr/bin/qemu-arm-static ./usr/bin/

设置软件源

vi ./etc/apt/sources.list
示例代码 A4.2.1 中科大 Ubuntu arm 软件源
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ xenial-updates main restricted universe multiverse
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ xenial-security main restricted universe multiverse
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ xenial-backports main restricted universe multiverse
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ xenial-proposed main restricted universe multiverse
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ xenial main restricted universe multiverse
示例代码 A4.2.2 mount.sh 脚本文件内容
 #!/bin/bash

sudo mount -t proc /proc /home/zynq/linux/rootfs/ubuntu_rootfs/proc
sudo mount -t sysfs /sys /home/zynq/linux/rootfs/ubuntu_rootfs/sys
sudo mount -o bind /dev /home/zynq/linux/rootfs/ubuntu_rootfs/dev
sudo mount -o bind /dev/pts /home/zynq/linux/rootfs/ubuntu_rootfs/dev/pts
sudo chroot /home/zynq/linux/rootfs/ubuntu_rootfs
示例代码 A4.2.3 unmount.sh 脚本文件内容
 #!/bin/bash

sudo umount /home/zynq/linux/rootfs/ubuntu_rootfs/dev/pts
sudo umount /home/zynq/linux/rootfs/ubuntu_rootfs/dev
sudo umount /home/zynq/linux/rootfs/ubuntu_rootfs/sys
sudo umount /home/zynq/linux/rootfs/ubuntu_rootfs/proc
 ./mount.sh 

退出

exit
 ./unmount.sh 
安装桌面软件:
apt install xubuntu-desktop

利用petalinux快速生成设备树

1.获取xilinx官方设备树仓库
 https://github.com/Xilinx/device-tree-xlnx/releases 
2.拷贝到编译机并且解压运行hsi
3.运行以下命令得到所需设备树
open_hw_design /home/jdcy/xilinx_zynq/proj/Navigator_7020.sdk/navigator_7020_wrapper.hdf

set_repo_path /home/jdcy/xilinx_zynq/pkg/device-tree-xlnx-xilinx-v2018.3

create_sw_design device-tree -os device_tree -proc ps7_cortexa9_0

generate_target -dir /home/jdcy/xilinx_zynq/pkg/work/linux-4.14/arch/arm/boot/dts

得到设备树文件

编译生成BOOT.BIN

petalinux-build -c bootloader
petalinux-build -c u-boot
petalinux-package --boot --fsbl --u-boot --force

(注意zynq的ps端引导第一阶段是fsbl文件先引导u-boot,所以BOOT.BIN是u-boot与fsbl打包在一起生成的,这个步骤只能使用petalinux来生成)
编译 kerne

1.对设备树system-top.dts做适当修改
2.将system-top.dts 添加到arch/arm/boot/dts 目录下的 Makefile 文件

CONFIG_ARCH_ZYNQ选项下

3.设置交叉编译器环境变量
echo 'export PATH=$PATH:'`which arm-linux-gnueabihf-gcc | xargs dirname` | tee -a ~/.bashrc
4.设置编译环境为arm,将下面的语句添加到用户目录的.bashrc
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
5.配置默认编译条件
make xilinx_zynq_defconfig
6.编译linux内核(注意留一份干净的源码给petalinux,不要污染)
make zImage -j8
7.编译设备树

make system-top.dtb -j8

8.编译根文件系统

进入到 Petalinux 工程目录下

petalinux-config -c rootfs

配置无需密码登录后编译Image Features —>debug-tweaks

petalinux-build -c rootfs
9.设置目标机的环境变量,在uboot启动时设置
setenv bootcmd run default_bootcmd
setenv bootargs 'console=ttyPS0,115200 root=/dev/mmcblk0p2 rootwait rw rootfstype=ext4'
env default -a
setenv bitstream_load_address 0x100000
setenv bitstream_image system.bit
setenv bitstream_size 0x300000
setenv kernel_img zImage
setenv dtbnetstart 0x2000000
setenv netstart 0x2080000
setenv cp_dtb2ram 'mmcinfo && fatload mmc 0:1 ${dtbnetstart} ${dtb_img}'

setenv default_bootcmd 'if mmcinfo; then run uenvboot; echo Copying Linux from SD to RAM... &&
load mmc 0 ${bitstream_load_address} ${bitstream_image} &&
fpga loadb 0 ${bitstream_load_address} ${bitstream_size} &&
run cp_kernel2ram && run cp_dtb2ram && bootz ${netstart} - ${dtbnetstart}; fi'
saveenv
boot
setenv bootcmd 'tftpboot 0x2080000 zImage; tftpboot 0x2000000 system.dtb; bootz 0x2080000 - 0x2000000'
mount -t nfs -o nolock 192.168.1.27:/home/jdcy/linux/nfs/mod /mnt
tcl 脚本

新建一个 hsi_test.tcl


# tcl script test
hsi::open_hw_design /home/jdcy/xilinx_zynq/proj/Navigator_7020.sdk/navigator_7020_wrapper.hdf
hsi::set_repo_path /home/jdcy/xilinx_zynq/pkg/device-tree-xlnx-xilinx-v2018.3
hsi::create_sw_design device-tree -os device_tree -proc ps7_cortexa9_0
hsi::generate_target -dir /home/jdcy/xilinx_zynq/pkg/work/linux-4.14/arch/arm/boot/dts
xsct hsi_test.tcl

linux 中的mmap内存映射方案

使用mmap映射DDR到用户空间,直接通过指针操作内存。

使用DMA(Direct Memory Access)技术,通过DMA控制器从DDR中读取数据。

使用内核缓冲区,将DDR中的数据先读入内核缓冲区,再通过用户空间和内核空间之间的数据传输接口(如read/write)将数据传到用户空间。

使用文件系统,将DDR中的数据存储为文件,使用文件读写接口进行操作。

使用共享内存,将DDR中的数据存储在共享内存中,通过共享内存接口进行操作。

使用缓存技术,将DDR中的数据缓存在缓存中,降低读取延迟。

使用多线程技术,将读取数据的操作放在一个单独的线程中,避免阻塞主线程。

对于需要频繁读取的数据,可将其预先加载到缓存中,提高读取效率。

printk 可以根据日志级别对消息进行分
类,一共有 8 个消息级别,这 8 个消息级别定义在文件 include/linux/kern_levels.h 里面
printk(KERN_EMERG “gsmi: Log Shutdown Reason\n”);

描述设备树的使用方式的一些文档路径

在 Linux 内核文档中,有一些文件是描述设备树(Device Tree)的,主要内容包括:

Documentation/devicetree/bindings/:该目录下包含了各个子系统的设备树绑定文档,描述了各个设备节点的属性、绑定方式等。

Documentation/devicetree/booting-without-of.txt:该文件是一个指南,介绍了如何在没有 OF(Open Firmware)的平台上启动 Linux 内核。

Documentation/devicetree/usage-model.txt:该文件介绍了设备树的一般使用模型,包括设备树源码的组织方式、设备树的绑定过程、设备驱动的匹配过程等。

Documentation/devicetree/overlay-notes.txt:该文件介绍了设备树覆盖层的使用方法和限制,包括如何在运行时更新设备树、设备树覆盖层的优缺点等。

Documentation/devicetree/changesets.txt:该文件介绍了设备树变更集的使用方法,包括如何创建变更集、如何使用变更集更新设备树等。

Documentation/devicetree/testing.txt:该文件介绍了如何使用设备树进行系统测试,主要内容包括如何为测试目的构建设备树、如何在测试中使用设备树等。

这些文档主要涵盖了设备树的各个方面,包括设备树的结构、属性和绑定方式,以及如何使用设备树进行系统启动、设备驱动开发和系统测试等。通过阅读这些文档,可以更好地理解和应用设备树技术。

一些开发zynq7000系列的常用文档

Zynq-7000 All Programmable SoC Technical Reference Manual - UG585

该文档介绍了Zynq-7000 All Programmable SoC的架构和组成部分,包括ARM Cortex-A9处理器、PL(Programmable Logic)、PS(Processing System)、中断控制器、DMA控制器等。此外,该文档还介绍了Zynq-7000 SoC的主要功能模块和接口,如GPIO、I2C、SPI、UART、Timer等,以及它们的寄存器映射、状态和操作方法。

Zynq-7000 AP SoC Software Developers Guide - UG821

该文档提供了关于编写Zynq-7000 SoC驱动程序和应用程序的详细信息,包括Linux和Bare-Metal驱动程序的开发方法、硬件抽象层(HAL)库的使用方法、中断处理程序的编写、虚拟文件系统(VFS)的实现、板级支持包(BSP)的开发、设备树的编写等。

Zynq-7000 All Programmable SoC Technical Reference Manual - UG585

该文档介绍了Xilinx Zynq-7000 All Programmable SoC的体系结构,包括外设的接口和操作特点等。对于Zynq SoC的驱动程序开发,了解该文档中的寄存器映射和外设操作方法非常有帮助。

Xilinx Software Development Kit (SDK) User Guide - UG1165

该文档介绍了Xilinx软件开发工具包(SDK)的使用方法,包括创建新工程、添加驱动程序和应用程序、调试程序、生成可执行文件等。

Linux内核文档

Linux内核已经支持Zynq平台,包括了一些常用驱动程序,例如GPIO、I2C、SPI、UART等。可以通过阅读Linux内核文档,了解Zynq驱动程序的使用和开发方法,包括Documentation/devicetree/bindings/arm/xilinx/、Documentation/devicetree/bindings/gpio、Documentation/devicetree/bindings/i2c等。

通过上述文档的学习,可以更深入地了解和应用Zynq平台的驱动程序开发

linux 设备树中的一些常见属性及其含义

Linux设备树(Device Tree)是一种描述硬件信息的文本文件格式,用于指定系统中的硬件设备的连接方式和属性以及驱动程序所需要的信息。以下是Linux设备树中常用的属性:

compatible:指定设备的厂商和型号,通常由厂商提供设备树文件时给出。

reg:指定设备的物理地址和地址范围。

interrupts:指定设备的中断号、优先级和触发方式等。

clocks:指定设备的时钟信息。

gpio:指定设备使用的GPIO信息。

interrupt-parent:指定中断控制器的节点名称。

status:指定设备的状态,如"disabled"、"okay"等。

phandle:指定设备节点的全局句柄。

linux,phandle:指定设备节点的句柄。

label:指定设备的唯一标识符。

pinctrl-0:指定设备的引脚控制信息。

dma-coherent:指定设备是否支持DMA一致性。

interrupts-extended:扩展中断信息。

interrupt-map:中断映射信息。

clock-names:指定设备使用的时钟名称。

这些属性用于描述设备的硬件信息和接口,以及驱动程序需要的信息。通过对设备树的解析,Linux内核可以自动识别硬件和加载相应的驱动程序,从而实现对硬件的控制和管理。

常用的一些设备驱动开发框架

字符设备
传统的创建设备过程
1 alloc_chrdev_region(); /* 申请设备号 */
2 cdev_init(); /* 初始化 cdev */
3 cdev_add(); /* 添加 cdev */
4 class_create(); /* 创建类 */
5 device_create(); /* 创建设备 */
传统的删除设备的过程
1 cdev_del(); /* 删除 cdev */
2 unregister_chrdev_region(); /* 注销设备号 */
3 device_destroy(); /* 删除设备 */
4 class_destroy(); /* 删除类 */
杂项设备
int misc_register(struct miscdevice * misc)

int misc_deregister(struct miscdevice *misc)
平台总线
/* 匹配列表 */
static const struct of_device_id beep_of_match[] = {
	{ .compatible = "alientek,beeper" },
	{ /* Sentinel */ }
};

/* platform驱动结构体 */
static struct platform_driver mybeep_driver = {
	.driver = {
		.name			= "zynq-beep",		// 驱动名字,用于和设备匹配
		.of_match_table	= beep_of_match,	// 设备树匹配表,用于和设备树中定义的设备匹配
	},
	.probe		= mybeep_probe,		// probe函数
	.remove		= mybeep_remove,	// remove函数 这两个函数需要实现
};

module_platform_driver(mybeep_driver);
函数实现如下
/*
 * @description			: platform驱动的probe函数,当驱动与设备
 * 						  匹配成功以后此函数就会执行
 * @param - pdev		: platform设备指针
 * @return				: 0,成功;其他负值,失败
 */
static int mybeep_probe(struct platform_device *pdev)
{
	struct miscdevice *mdev;
	int ret;

	dev_info(&pdev->dev, "BEEP device and driver matched successfully!\n");

	/* 初始化BEEP */
	ret = mybeep_init(pdev);
	if (ret)
		return ret;

	/* 初始化beep_data变量 */
	mdev = &beep_dev.mdev;
	mdev->name	= "zynq-beep";			// 设备名
	mdev->minor	= MISC_DYNAMIC_MINOR;	// 动态分配次设备号
	mdev->fops	= &mybeep_fops;			// 绑定file_operations结构体

	/* 向linux系统misc驱动框架核心层注册一个beep设备 */
	return misc_register(mdev);
}

/*
 * @description			: platform驱动模块卸载时此函数会执行
 * @param - dev			: platform设备指针
 * @return				: 0,成功;其他负值,失败
 */
static int mybeep_remove(struct platform_device *pdev)
{
	/* 注销模块时关闭BEEP */
	gpio_set_value(beep_dev.gpio, 0);

	/* 注销misc设备驱动 */
	misc_deregister(&beep_dev.mdev);

	dev_info(&pdev->dev, "BEEP driver has been removed!\n");
	return 0;
}

中断处理中上半部与下半部的合理规划方案

①、如果要处理的内容不希望被其他中断打断,那么可以放到上半部。
②、如果要处理的任务对时间敏感,可以放到上半部。
③、如果要处理的任务与硬件有关,可以放到上半部
④、除了上述三点以外的其他任务,优先考虑放到下半部。
上半部处理很简单,直接编写中断处理函数就行了,关键是下半部该怎么做呢? Linux 内
核提供了多种下半部机制,接下来我们来学习一下这些下半部机制。

zynq7020的一些常用设备节点记录

/include/ "system-conf.dtsi"
#include <dt-bindings/gpio/gpio.h>
/ {
model = "Navigator Development Board";
compatible = "alientek,zynq-7020","xlnx,zynq-7000";
leds {
compatible = "gpio-leds";
gpio-led1 {
label = "led1";
gpios = <&gpio0 54 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
};
gpio-led2 {
label = "pl_led0";
gpios = <&gpio0 55 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
gpio-led3 {
label = "pl_led1";
gpios = <&gpio0 56 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
gpio-led4 {
label = "ps_led0";
gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
gpio-led5 {
label = "ps_led1";
gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
gpio-led6 {
label = "led2";
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
};
keys {
compatible = "gpio-keys";
autorepeat;
gpio-key1 {
label = "pl_key1";
gpios = <&gpio0 57 GPIO_ACTIVE_LOW>;
linux,code = <105>; // Right
debounce-interval = <20>; // 20ms
};
gpio-key2 {
label = "pl_key2";
gpios = <&gpio0 58 GPIO_ACTIVE_LOW>;
linux,code = <106>; // Left
debounce-interval = <20>;
};
gpio-key3 {
label = "ps_key1";
gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
linux,code = <103>; // Up
debounce-interval = <20>;
};
gpio-key4 {
label = "ps_key2";
gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
linux,code = <108>; // Down
debounce-interval = <20>;
};
touch-key {
label = "touch_key";
gpios = <&gpio0 59 GPIO_ACTIVE_HIGH>;
linux,code = <28>; // ENTER
gpio-key,wakeup;
debounce-interval = <20>;
};
};
beeper {
compatible = "gpio-beeper";
gpios = <&gpio0 60 GPIO_ACTIVE_HIGH>;
};
usb_phy0: phy0@e0002000 {
compatible = "ulpi-phy";
#phy-cells = <0>;
reg = <0xe0002000 0x1000>;
view-port = <0x0170>;
drv-vbus;
};
};
&i2c0 {
clock-frequency = <100000>;
eeprom@50 {
compatible = "24c64";
reg = <0x50>;
pagesize = <32>;
};
rtc@51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
};
};
&usb0 {
dr_mode = "otg";
usb-phy = <&usb_phy0>;
};

&pwm_0 {
compatible = "digilent,axi-pwm";
#pwm-cells = <2>;
clock-names = "pwm";
npwm = <1>;
};
&lcd_vtc {
compatible = "xlnx,v-tc-5.01.a";
};
&lcd_vdma {
dma-ranges = <0x00000000 0x00000000 0x40000000>; // 1GB
};
&hdmi_vtc {
clocks = <&hdmi_dynclk>, <&clkc 15>;
compatible = "xlnx,v-tc-5.01.a";
};
&hdmi_dynclk {
compatible = "digilent,axi-dynclk";
#clock-cells = <0>;
};
&hdmi_vdma {
dma-ranges = <0x00000000 0x00000000 0x40000000>; // 1GB
};
&amba_pl {
xlnx_vdmafb_lcd {
compatible = "xilinx,vdmafb";
status = "okay";
vtc = <&lcd_vtc>;
clocks = <&clk_wiz_0 0>;
clock-names = "lcd_pclk";
dmas = <&lcd_vdma 0>;
dma-names = "lcd_vdma";
pwms = <&pwm_0 0 5000000>;
reset-gpio = <&gpio0 61 GPIO_ACTIVE_LOW>;
lcdID-gpio = <&gpio1 0 0 GPIO_ACTIVE_LOW &gpio1 1 0 GPIO_ACTIVE_LOW &gpio1 2 0
GPIO_ACTIVE_LOW>;
display-timings {
timing_4342: timing0 {
clock-frequency = <9000000>;
hactive = <480>;
vactive = <272>;
hback-porch = <40>;
hfront-porch = <5>;
hsync-len = <1>;
vback-porch = <8>;
vfront-porch = <8>;
vsync-len = <1>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <0>;
};
timing_4384: timing1 {
clock-frequency = <31000000>;
hactive = <800>;
vactive = <480>;
hback-porch = <88>;
hfront-porch = <40>;
hsync-len = <48>;
vback-porch = <32>;
vfront-porch = <13>;
vsync-len = <3>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <0>;
};
timing_7084: timing2 {
clock-frequency = <33300000>;
hactive = <800>;
vactive = <480>;
hback-porch = <46>;
hfront-porch = <210>;
hsync-len = <1>;
vback-porch = <23>;
vfront-porch = <22>;
vsync-len = <1>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <0>;
};
timing_7016: timing3 {
clock-frequency = <51200000>;
hactive = <1024>;
vactive = <600>;
hback-porch = <140>;
hfront-porch = <160>;
hsync-len = <20>;
vback-porch = <20>;
vfront-porch = <12>;
vsync-len = <3>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <0>;
};
timing_1018: timing4 {
clock-frequency = <71100000>;
hactive = <1280>;
vactive = <800>;
hback-porch = <80>;
hfront-porch = <70>;
hsync-len = <10>;
vback-porch = <10>;
vfront-porch = <10>;
vsync-len = <3>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <0>;
};
};
};
xlnx_vdmafb_hdmi {
compatible = "xilinx,vdmafb";
status = "okay";
vtc = <&hdmi_vtc>;
clocks = <&hdmi_dynclk>;
clock-names = "lcd_pclk";
dmas = <&hdmi_vdma 0>;
dma-names = "lcd_vdma";
hdmi;
display-timings {
timing_1080p: timing5 {
clock-frequency = <148500000>;
hactive = <1920>;
vactive = <1080>;
hback-porch = <148>;
hsync-len = <44>;
hfront-porch = <88>;
vback-porch = <36>;
vsync-len = <5>;
vfront-porch = <4>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <1>;
};
};
};
};
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Zynq是赛灵思(Xilinx)推出的一款可编程器件,而Zynq 7020是其中的一种型号。SGMII(SerDes Gigabit Media Independent Interface)是一种高速串行接口标准,用于连接网络设备之间的通信。 要扩展Zynq 7020以支持SGMII接口,需要以下步骤: 1. 首先,确保Zynq 7020设备上有足够的可用引脚来进行SGMII的物理连接。在设备规格中查找SGMII接口所需的引脚数量和位置。 2. 根据SGMII的电气连接要求,将适当的串行电路和信号线连接到Zynq 7020设备的引脚上。这通常涉及到将发送和接收器引脚与适当的信号线连接起来。 3. 确定Zynq 7020上的可编程逻辑资源中是否有足够的资源用于实现SGMII通信。根据SGMII的协议规范,编写适当的逻辑代码用于发送和接收SGMII帧。 4. 在FPGA设计工具(如Vivado)中创建一个新的设计项目,并将SGMII通信逻辑代码添加到该项目中。确保通过引脚约束将适当的引脚与逻辑代码进行关联,以保证正确的物理连接。 5. 在FPGA设计工具中进行综合和实现,生成适用于Zynq 7020设备的比特流文件。使用比特流文件将逻辑代码加载到Zynq 7020设备中。 6. 对于软件开发,根据SGMII的协议规范,在Zynq 7020设备上使用适当的驱动程序和库函数,以便在应用程序中进行SGMII通信控制和数据传输。 总之,要在Zynq 7020上扩展SGMII,需要进行物理连接、逻辑设计和软件开发的相关工作。这需要一定的硬件和软件知识以及使用FPGA设计工具的能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值