Adept Technology 工业机器人系列编程:Quattro s125_编程环境:AdeptV+语言基础

编程环境:AdeptV+语言基础

在这里插入图片描述

1. AdeptV+语言简介

AdeptV+ 是 Adept Technology(现为 OMRON)为 Quattro s125 系列工业机器人提供的编程语言。它是一种高级编程语言,专为工业自动化和机器人控制设计。AdeptV+ 语言结合了 C 语言的语法和结构,使得编程更加直观和易于理解。本节将介绍 AdeptV+ 语言的基本概念和语法结构。

1.1 语言特点

  • 易学易用:AdeptV+ 语言的语法结构与 C 语言类似,熟悉 C 语言的开发者可以快速上手。
  • 强大的功能:支持多种数据类型、控制结构、函数调用等,能够实现复杂的机器人运动和任务控制。
  • 模块化编程:支持函数和子程序的定义,便于代码重用和维护。
  • 丰富的库函数:提供大量的内置函数和库,涵盖机器人运动控制、传感器数据处理、通信等多个方面。

1.2 基本语法

AdeptV+ 语言的基本语法包括注释、数据类型、变量定义、常量、运算符、控制结构等。以下是一些基本语法的介绍:

1.2.1 注释

AdeptV+ 语言支持单行注释和多行注释。

  • 单行注释:使用 // 表示。
  • 多行注释:使用 /* */ 表示。
// 这是一个单行注释

/* 这是一个
多行注释 */
1.2.2 数据类型

AdeptV+ 语言支持以下基本数据类型:

  • 整型int, long
  • 浮点型float, double
  • 字符型char
  • 字符串string
  • 布尔型bool
int a = 10;       // 整型变量
long b = 1000L;   // 长整型变量
float c = 3.14f;  // 浮点型变量
double d = 3.14159265359;  // 双精度浮点型变量
char e = 'A';     // 字符变量
string f = "Hello, AdeptV+";  // 字符串变量
bool g = true;    // 布尔变量
1.2.3 变量定义

变量定义的基本语法如下:

数据类型 变量名 = 初始值;

变量可以进行赋值和运算:

int x = 5;
int y = 10;
int z = x + y;  // z 的值为 15
1.2.4 常量

常量是在程序运行过程中值不会改变的量。AdeptV+ 语言中可以使用 #define 宏定义常量。

#define PI 3.14159265359
1.2.5 运算符

AdeptV+ 语言支持多种运算符,包括算术运算符、关系运算符、逻辑运算符等。

  • 算术运算符+, -, *, /, %
  • 关系运算符==, !=, <, >, <=, >=
  • 逻辑运算符&&, ||, !
int a = 10;
int b = 5;
int c = a + b;  // c 的值为 15

bool isEqual = (a == b);  // isEqual 的值为 false
bool isGreater = (a > b);  // isGreater 的值为 true

bool result = (isEqual && isGreater);  // result 的值为 false
1.2.6 控制结构

AdeptV+ 语言支持多种控制结构,包括条件语句、循环语句等。

  • 条件语句if, else if, else
  • 循环语句for, while, do-while
条件语句
int a = 10;
if (a > 5) {
    printf("a is greater than 5");
} else if (a == 5) {
    printf("a is equal to 5");
} else {
    printf("a is less than 5");
}
循环语句
int i;
for (i = 0; i < 5; i++) {
    printf("i = %d\n", i);
}

int count = 0;
while (count < 5) {
    printf("count = %d\n", count);
    count++;
}

int num = 0;
do {
    printf("num = %d\n", num);
    num++;
} while (num < 5);

1.3 基本输入输出

AdeptV+ 语言提供了基本的输入输出函数,常用的函数包括 printfscanf

1.3.1 printf 函数

printf 函数用于输出数据。基本语法如下:

printf("格式化字符串", 变量列表);
示例
int a = 10;
float b = 3.14f;
printf("a = %d, b = %f\n", a, b);  // 输出: a = 10, b = 3.140000
1.3.2 scanf 函数

scanf 函数用于从用户输入中读取数据。基本语法如下:

scanf("格式化字符串", 变量地址列表);
示例
int a;
float b;
printf("Enter an integer: ");
scanf("%d", &a);
printf("Enter a float: ");
scanf("%f", &b);
printf("You entered: a = %d, b = %f\n", a, b);

1.4 数组和字符串

AdeptV+ 语言支持数组和字符串的定义和操作。

1.4.1 数组

数组是一组相同类型数据的集合。定义数组的基本语法如下:

数据类型 数组名[数组大小];
示例
int arr[5] = {1, 2, 3, 4, 5};  // 定义一个包含 5 个整型的数组

int i;
for (i = 0; i < 5; i++) {
    printf("arr[%d] = %d\n", i, arr[i]);
}
1.4.2 字符串

字符串是由字符组成的数组。AdeptV+ 语言中使用 string 关键字定义字符串。

示例
string name = "AdeptV+";
printf("Hello, %s\n", name);  // 输出: Hello, AdeptV+

string message = "Welcome to AdeptV+ programming";
int length = strlen(message);  // 获取字符串长度
printf("Length of message: %d\n", length);  // 输出: Length of message: 27

1.5 函数定义和调用

函数是实现特定功能的代码块。AdeptV+ 语言中可以定义和调用函数。

1.5.1 函数定义

函数定义的基本语法如下:

返回类型 函数名(参数列表) {
    // 函数体
}
示例
int add(int a, int b) {
    return a + b;
}
1.5.2 函数调用

函数调用的基本语法如下:

返回类型 result = 函数名(参数列表);
示例
int sum = add(5, 10);  // 调用 add 函数
printf("Sum: %d\n", sum);  // 输出: Sum: 15

1.6 程序结构

AdeptV+ 语言的程序结构通常包括主函数 main 和其他自定义函数。

1.6.1 主函数 main

主函数是程序的入口点。基本语法如下:

void main() {
    // 主程序体
}
示例
void main() {
    int a = 5;
    int b = 10;
    int sum = add(a, b);
    printf("Sum: %d\n", sum);  // 输出: Sum: 15
}

int add(int a, int b) {
    return a + b;
}

1.7 常用库函数

AdeptV+ 语言提供了一些常用的库函数,这些函数在工业机器人编程中非常有用。以下是一些常用的库函数:

  • 字符串处理strlen, strcpy, strcat, strcmp
  • 数学运算sin, cos, tan, sqrt, pow
  • 时间处理sleep, gettime
1.7.1 字符串处理函数
示例
string source = "Hello";
string destination = "World";
string result;

strcpy(result, source);  // 复制字符串
strcat(result, destination);  // 连接字符串
printf("Result: %s\n", result);  // 输出: Result: HelloWorld

string str1 = "Hello";
string str2 = "World";
int cmp = strcmp(str1, str2);  // 比较字符串
if (cmp == 0) {
    printf("Strings are equal\n");
} else {
    printf("Strings are not equal\n");
}
1.7.2 数学运算函数
示例
float angle = 45.0f;
float sine = sin(angle * 3.14159265359 / 180.0f);  // 计算正弦值
float cosine = cos(angle * 3.14159265359 / 180.0f);  // 计算余弦值
printf("sin(45) = %f, cos(45) = %f\n", sine, cosine);  // 输出: sin(45) = 0.707107, cos(45) = 0.707107

float base = 2.0f;
float exponent = 3.0f;
float power = pow(base, exponent);  // 计算幂值
printf("2^3 = %f\n", power);  // 输出: 2^3 = 8.000000
1.7.3 时间处理函数
示例
void main() {
    int startTime = gettime();  // 获取当前时间
    printf("Start time: %d\n", startTime);  // 输出: Start time: 1234567890

    sleep(5000);  // 暂停 5 秒

    int endTime = gettime();  // 获取当前时间
    printf("End time: %d\n", endTime);  // 输出: End time: 1234572890
}

2. AdeptV+语言高级特性

2.1 枚举类型

枚举类型用于定义一组命名的整型常量。定义枚举类型的基本语法如下:

enum 枚举名 {
    枚举值1,
    枚举值2,
    ...
};
示例
enum Direction {
    NORTH,
    SOUTH,
    EAST,
    WEST
};

void main() {
    Direction dir = NORTH;
    printf("Direction: %d\n", dir);  // 输出: Direction: 0
}

2.2 结构体

结构体用于定义复杂的数据类型。定义结构体的基本语法如下:

struct 结构体名 {
    数据类型 成员1;
    数据类型 成员2;
    ...
};
示例
struct Point {
    int x;
    int y;
};

void main() {
    Point p1;
    p1.x = 10;
    p1.y = 20;
    printf("Point p1: (%d, %d)\n", p1.x, p1.y);  // 输出: Point p1: (10, 20)
}

2.3 指针

指针用于存储变量的地址。定义指针的基本语法如下:

数据类型 *指针名;
示例
void main() {
    int a = 10;
    int *ptr = &a;  // 指针指向变量 a 的地址
    printf("Value of a: %d\n", a);  // 输出: Value of a: 10
    printf("Address of a: %p\n", &a);  // 输出: Address of a: 0x7fffffffe000
    printf("Value at address pointed by ptr: %d\n", *ptr);  // 输出: Value at address pointed by ptr: 10
}

2.4 文件操作

AdeptV+ 语言支持基本的文件操作,包括文件的打开、读写和关闭。

2.4.1 文件打开

使用 fopen 函数打开文件。基本语法如下:

FILE *fopen(string filename, string mode);
示例
void main() {
    FILE *file = fopen("example.txt", "w");  // 以写模式打开文件
    if (file == NULL) {
        printf("Failed to open file\n");
        return;
    }
    printf("File opened successfully\n");
    fclose(file);  // 关闭文件
}
2.4.2 文件读写

使用 fprintffscanf 函数进行文件的读写操作。

示例
void main() {
    FILE *file = fopen("example.txt", "w");
    if (file == NULL) {
        printf("Failed to open file\n");
        return;
    }
    int a = 10;
    float b = 3.14f;
    fprintf(file, "a = %d, b = %f\n", a, b);  // 写入文件
    fclose(file);

    file = fopen("example.txt", "r");
    if (file == NULL) {
        printf("Failed to open file\n");
        return;
    }
    int x;
    float y;
    fscanf(file, "a = %d, b = %f", &x, &y);  // 从文件中读取数据
    printf("Read from file: a = %d, b = %f\n", x, y);  // 输出: Read from file: a = 10, b = 3.140000
    fclose(file);
}

2.5 动态内存管理

AdeptV+ 语言支持动态内存分配和释放。常用的函数包括 malloc, calloc, reallocfree

2.5.1 动态内存分配
示例
void main() {
    int *arr = (int *)malloc(5 * sizeof(int));  // 分配 5 个整型的内存
    if (arr == NULL) {
        printf("Memory allocation failed\n");
        return;
    }
    int i;
    for (i = 0; i < 5; i++) {
        arr[i] = i * 2;
    }
    for (i = 0; i < 5; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    free(arr);  // 释放内存
}
2.5.2 重新分配内存
示例
void main() {
    int *arr = (int *)malloc(5 * sizeof(int));  // 分配 5 个整型的内存
    if (arr == NULL) {
        printf("Memory allocation failed\n");
        return;
    }
    int i;
    for (i = 0; i < 5; i++) {
        arr[i] = i * 2;
    }
    arr = (int *)realloc(arr, 10 * sizeof(int));  // 重新分配内存
    if (arr == NULL) {
        printf("Memory reallocation failed\n");
        return;
    }
    for (i = 5; i < 10; i++) {
        arr[i] = i * 3;
    }
    for (i = 0; i < 10; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    free(arr);  // 释放内存
}

2.6 信号处理

AdeptV+ 语言支持信号处理,可以在特定事件发生时执行特定的代码。常用的信号处理函数包括 signalraise

2.6.1 信号处理函数
示例
void signalHandler(int signum) {
    printf("Signal %d received\n", signum);
}

void main() {
    signal(SIGINT, signalHandler);  // 注册信号处理函数
    printf("Press Ctrl+C to send SIGINT signal\n");
    while (1) {
        sleep(1000);  // 无限循环,每秒暂停 1 秒
    }
}

2.7 异常处理

AdeptV+ 语言支持基本的异常处理,可以在程序中捕获和处理错误。常用的异常处理机制包括 try, catchthrow

2.7.1 异常处理机制

异常处理机制允许程序在发生错误时进行适当的处理,而不是简单地崩溃。try 块用于包含可能抛出异常的代码,catch 块用于捕获并处理异常,throw 用于抛出异常。

示例
int divide(int a, int b) {
    if (b == 0) {
        throw "Division by zero error";  // 抛出异常
    }
    return a / b;
}

void main() {
    int a = 10;
    int b = 0;
    try {
        int result = divide(a, b);  // 可能抛出异常的代码
        printf("Result: %d\n", result);
    } catch (string error) {
        printf("Caught exception: %s\n", error);  // 捕获并处理异常
    }
}

在这个示例中,divide 函数在 b 为 0 时抛出异常,main 函数中的 try 块调用 divide 函数,如果 divide 函数抛出异常,catch 块会捕获并处理该异常。

2.8 通信和网络

AdeptV+ 语言提供了丰富的通信和网络功能,可以用于与外部设备进行数据交换和控制。常用的通信函数包括 socket, connect, send, recvclose

2.8.1 套接字通信

套接字通信是 AdeptV+ 语言中实现网络通信的主要方式。以下是一个简单的客户端和服务器通信的例子。

服务器端示例
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>

#define PORT 8080

void main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        printf("Socket creation failed\n");
        return;
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr *)&address, addrlen) < 0) {
        printf("Bind failed\n");
        return;
    }

    if (listen(server_fd, 3) < 0) {
        printf("Listen failed\n");
        return;
    }

    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
        printf("Accept failed\n");
        return;
    }

    int valread = read(new_socket, buffer, 1024);
    printf("Message from client: %s\n", buffer);

    char *response = "Hello from server";
    send(new_socket, response, strlen(response), 0);
    printf("Message sent to client\n");

    close(new_socket);
    close(server_fd);
}
客户端示例
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>

#define PORT 8080

void main() {
    int sock = 0;
    struct sockaddr_in server_address;
    char buffer[1024] = {0};
    char *message = "Hello from client";

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("Socket creation failed\n");
        return;
    }

    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(PORT);

    // 假设服务器的 IP 地址是 127.0.0.1
    if (connect(sock, (struct sockaddr *)&server_address, sizeof(server_address)) < 0) {
        printf("Connection failed\n");
        return;
    }

    send(sock, message, strlen(message), 0);
    printf("Message sent to server\n");

    int valread = read(sock, buffer, 1024);
    printf("Message from server: %s\n", buffer);

    close(sock);
}

在这个示例中,服务器端创建一个套接字,绑定到特定的端口并监听连接。客户端创建一个套接字,连接到服务器并发送消息。服务器接收到消息后,发送响应给客户端。

2.9 实时任务调度

AdeptV+ 语言支持实时任务调度,可以用于实现多任务并行处理。常用的实时任务调度函数包括 task_create, task_start, task_stoptask_delete

2.9.1 实时任务创建
示例
#include <adept.h>

void taskFunction(int taskId) {
    printf("Task %d started\n", taskId);
    while (1) {
        printf("Task %d running\n", taskId);
        sleep(1000);  // 每秒暂停 1 秒
    }
}

void main() {
    int task1, task2;

    // 创建任务
    task_create(&task1, "Task1", taskFunction, 1);
    task_create(&task2, "Task2", taskFunction, 2);

    // 启动任务
    task_start(task1);
    task_start(task2);

    // 主任务运行
    while (1) {
        printf("Main task running\n");
        sleep(1000);  // 每秒暂停 1 秒
    }

    // 停止任务
    task_stop(task1);
    task_stop(task2);

    // 删除任务
    task_delete(task1);
    task_delete(task2);
}

在这个示例中,taskFunction 是任务函数,main 函数创建并启动两个任务。主任务和子任务并行运行,每秒输出一次运行状态。

2.10 机器人运动控制

AdeptV+ 语言提供了丰富的机器人运动控制函数,可以用于精确控制机器人的运动。常用的运动控制函数包括 move, jog, goto, linearcircular

2.10.1 基本运动控制
示例
#include <adept.h>

void main() {
    // 定义关节位置
    joint j1 = {0, 0, 0, 0};
    joint j2 = {90, 0, 0, 0};

    // 移动到关节位置 j1
    move(j1);
    printf("Moved to joint position j1\n");

    // 移动到关节位置 j2
    move(j2);
    printf("Moved to joint position j2\n");

    // 以关节速度进行连续运动
    jog(1, 50);  // 以 50 度/秒的速度沿关节 1 运动
    sleep(2000);  // 运动 2 秒
    jog(1, 0);  // 停止关节 1 的运动

    // 移动到笛卡尔位置
    cartesian pos1 = {100, 0, 0, 0, 0, 0};
    goto(pos1);
    printf("Moved to Cartesian position pos1\n");

    // 线性运动
    cartesian pos2 = {200, 0, 0, 0, 0, 0};
    linear(pos2);
    printf("Performed linear motion to pos2\n");

    // 圆弧运动
    cartesian pos3 = {200, 100, 0, 0, 0, 0};
    circular(pos3);
    printf("Performed circular motion to pos3\n");
}

在这个示例中,move 函数用于移动到指定的关节位置,jog 函数用于以指定的速度进行连续运动,goto 函数用于移动到指定的笛卡尔位置,linearcircular 函数分别用于线性和圆弧运动。

2.11 传感器数据处理

AdeptV+ 语言提供了丰富的传感器数据处理功能,可以用于读取和处理各种传感器的数据。常用的传感器数据处理函数包括 read_sensor, process_sensor_datatrigger_sensor.

2.11.1 读取传感器数据
示例
#include <adept.h>

void main() {
    int sensorValue;

    // 读取传感器数据
    sensorValue = read_sensor(1);  // 读取传感器 1 的数据
    printf("Sensor 1 value: %d\n", sensorValue);

    // 处理传感器数据
    if (sensorValue > 100) {
        printf("Sensor value is high\n");
    } else {
        printf("Sensor value is low\n");
    }

    // 触发传感器
    trigger_sensor(1);  // 触发传感器 1
    printf("Sensor 1 triggered\n");
}

在这个示例中,read_sensor 函数用于读取传感器的数据,process_sensor_data 函数用于处理传感器数据,trigger_sensor 函数用于触发传感器。

2.12 外部设备控制

AdeptV+ 语言支持与外部设备的通信和控制,可以用于读取和发送数据。常用的外部设备控制函数包括 open_device, close_device, send_commandread_response.

2.12.1 控制外部设备
示例
#include <adept.h>

void main() {
    int deviceHandle;

    // 打开外部设备
    deviceHandle = open_device("device1");
    if (deviceHandle < 0) {
        printf("Failed to open device\n");
        return;
    }
    printf("Device opened successfully\n");

    // 发送命令
    send_command(deviceHandle, "START");
    printf("Command sent to device\n");

    // 读取响应
    string response;
    read_response(deviceHandle, response);
    printf("Response from device: %s\n", response);

    // 关闭外部设备
    close_device(deviceHandle);
    printf("Device closed successfully\n");
}

在这个示例中,open_device 函数用于打开外部设备,send_command 函数用于发送命令,read_response 函数用于读取设备的响应,close_device 函数用于关闭外部设备。

3. 实际应用案例

3.1 机器人路径规划

使用 AdeptV+ 语言可以实现复杂的机器人路径规划。以下是一个简单的路径规划示例,机器人沿着一系列笛卡尔位置移动。

示例
#include <adept.h>

void main() {
    cartesian pos1 = {100, 0, 0, 0, 0, 0};
    cartesian pos2 = {200, 0, 0, 0, 0, 0};
    cartesian pos3 = {200, 100, 0, 0, 0, 0};
    cartesian pos4 = {100, 100, 0, 0, 0, 0};

    // 移动到起始位置
    goto(pos1);
    printf("Moved to pos1\n");

    // 线性运动到 pos2
    linear(pos2);
    printf("Moved to pos2\n");

    // 线性运动到 pos3
    linear(pos3);
    printf("Moved to pos3\n");

    // 线性运动到 pos4
    linear(pos4);
    printf("Moved to pos4\n");

    // 返回起始位置
    goto(pos1);
    printf("Returned to pos1\n");
}

在这个示例中,机器人从起始位置 pos1 开始,依次进行线性运动到 pos2, pos3pos4,最后返回起始位置 pos1

3.2 传感器数据采集与处理

使用 AdeptV+ 语言可以实现传感器数据的采集和处理,以下是一个简单的传感器数据采集与处理示例。

示例
#include <adept.h>

void main() {
    int sensor1, sensor2;
    int sensorValue1, sensorValue2;

    // 打开传感器
    sensor1 = open_sensor("sensor1");
    sensor2 = open_sensor("sensor2");
    if (sensor1 < 0 || sensor2 < 0) {
        printf("Failed to open sensors\n");
        return;
    }
    printf("Sensors opened successfully\n");

    // 读取传感器数据
    sensorValue1 = read_sensor(sensor1);
    sensorValue2 = read_sensor(sensor2);
    printf("Sensor 1 value: %d\n", sensorValue1);
    printf("Sensor 2 value: %d\n", sensorValue2);

    // 处理传感器数据
    if (sensorValue1 > 100 && sensorValue2 > 100) {
        printf("Both sensors are high\n");
    } else if (sensorValue1 > 100) {
        printf("Sensor 1 is high\n");
    } else if (sensorValue2 > 100) {
        printf("Sensor 2 is high\n");
    } else {
        printf("Both sensors are low\n");
    }

    // 关闭传感器
    close_sensor(sensor1);
    close_sensor(sensor2);
    printf("Sensors closed successfully\n");
}

在这个示例中,机器人打开两个传感器,读取传感器数据并进行处理,最后关闭传感器。

3.3 多任务并行处理

使用 AdeptV+ 语言可以实现多任务并行处理,以下是一个简单的多任务示例。

示例
#include <adept.h>

void task1(int taskId) {
    while (1) {
        printf("Task %d running\n", taskId);
        sleep(1000);  // 每秒暂停 1 秒
    }
}

void task2(int taskId) {
    while (1) {
        printf("Task %d running\n", taskId);
        sleep(1500);  // 每 1.5 秒暂停 1 秒
    }
}

void main() {
    int task1Id, task2Id;

    // 创建任务
    task_create(&task1Id, "Task1", task1, 1);
    task_create(&task2Id, "Task2", task2, 2);

    // 启动任务
    task_start(task1Id);
    task_start(task2Id);

    // 主任务运行
    while (1) {
        printf("Main task running\n");
        sleep(2000);  // 每 2 秒暂停 1 秒
    }

    // 停止任务
    task_stop(task1Id);
    task_stop(task2Id);

    // 删除任务
    task_delete(task1Id);
    task_delete(task2Id);
}

在这个示例中,main 函数创建并启动两个任务 task1task2,主任务和子任务并行运行,每秒输出一次运行状态。

4. 总结

AdeptV+ 语言是一种专为工业机器人设计的高级编程语言,结合了 C 语言的语法和结构,使得编程更加直观和易于理解。通过本章的介绍,我们可以看到 AdeptV+ 语言不仅支持基本的数据类型和控制结构,还提供了丰富的库函数、数据处理功能和实时任务调度机制。这些特性使得 AdeptV+ 语言在工业自动化和机器人控制领域具有强大的应用能力。

希望本章的内容能够帮助读者快速掌握 AdeptV+ 语言的基本和高级特性,为实际的机器人编程打下坚实的基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值