OLED与8080并口

STM32学习日记——OLED模块

写于8月24日凌晨,该文章仅供自己学习参考,欢迎大家指点交流


image-20240824023854384

1. 硬件驱动接口模式8080并口模式

1.1 信号线

  • CS:OLED 片选信号。

  • WR:向 OLED 写入数据。

  • RD:从 OLED 读取数据。

  • D[7:0]:8 位双向数据线。

  • RST(RES):硬复位 OLED。

  • DC:命令/数据标志(0,读写命令;1,读写数据)。

1.2 时序图

1.2.1 写时序

模块的 8080 并口写的过程为:

  1. 先根据要写入的数据的类型,设置 DC 为高(数据)/低(命令),设置 WR 起始电平为高,然后拉低片选,选中 SSD1306

  2. 接着我们在整个读时序上保持RD 为高电平,然后拉低 WR 的电平准备写入数据,向数据线(D[7:0])上输入要写的信息

  3. 拉高 WR,这样得到一个 WR 的上升沿,在这个上升沿,使数据写入到 SSD1306 里面

    image-20240824025538831

1.2.2 读时序

模块的 8080 并口读的过程为:

  1. 先根据要写入的数据的类型,设置 DC 为高(数据)/低(命令),设置 RD 起始电平为高
  2. 然后拉低片选 CS 信号,选中 SSD1306,接着我们在整个读时序上保持 WR 为高电平
  3. 然后类似写时序,同样的,在 RD 的上升沿,使数据锁存到数据线(D[7:0])上;

image-20240824025900406

2. OLED显存GDDRAM

GDDRAM全称Graphic Display Data RAM,为SSD1306内部的数据内存,SSD1306通过Common Driver驱动OLED显示,可以理解为OLED显示的图像跟随GDDRAM里的内容(当然也可以设置不跟随,用于刷新处理),下图为SSD1306驱动OLED显示屏的逻辑关系:
image-20240824040354142

在上电初始化后,SSD1306默认设置为COM0驱动Row0,COM63驱动Row63(后续可以设置);COM驱动对应内部GDDRAM关系如下:image-20240824040713579

SSD1306 的显存总共为 128*64bit 大小,SSD1306 将这些显存分为了 8 页,不使用显存对应的行列的重映射,其对应关系如表 24.1.2.1 所示:

image-20240824030235964

可以看出,SSD1306 的每页包含了 128 个字节,总共 8 页,这样刚好是 128*64 的点阵大小。当 GRAM 的写入模式为页模式时,需要设置低字节起始的列地址(0x00~ 0x0F)和高字节的起始列地址(0x10~0x1F),芯片手册中给出了写入 GRAM 与显示的对应关系,写入列地址在写完一字节后自动按列增长。

image-20240824030407395

所以我们采用的办法是在 STM32F103 的内部建立一个虚拟的 OLED 的 GRAM(共128*8=1024 个字节),每次修改时,只修改 STM32F103 上的 GRAM(实际上就是 SRAM),在修改完成后一次性把 STM3F103 上的 GRAM写入到OLED 的 GRAM。当然这个方法也有坏处,一个对于那些 SRAM 很小的单片机(比如 51 系列)不太友好,另一个是每次都写入全屏,屏幕刷新率会变低。

总结如下:

  • 显存GDDRAM大小为128*64;
  • SSD1306通过Common Driver驱动OLED每一行的显示;
  • 控制上,每8个COM组成一个Page,共有8个Page,即64行;
  • 每一个COM中有128个Segment,即128列;
  • 通过设置COM与Page的映射关系,可以改变OLED的行刷新方向;
  • 通过设置SEG与Column的映射关系,可以改变OLED的列刷新方向;
  • 在一个Page中,数据刷新总是从低SEG刷新到高SEG;在一个SEG中,数据的低位在上,高位在下,即在一个SEG中数据总是从低COM刷新到高COM。

至此,我们弄清楚了GDDRAM里数据和OLED显示的关系。

3. 指令格式

SSD1306 的命令比较多,这里我们仅介绍几个比较常用的命令

image-20240824035248439

参考SSD1306参考手册可以查到全部的指令,先写指令,再写数据

image-20240824041340272

3.1 基础指令 Fundamental Command Table

3.1.1 Set Contrast Control(设置对比度)
指令/数据D7D6D5D4D3D2D1D0指令作用
0x8110000001设置对比度
A[7:0]A7A6A5A4A3A2A1A0对比度(0~255)
默认值为0x7F(127)

这个命令首先发送命令字节0x81,然后再发送对比度数据。
实际上是设置OLED的驱动电流,对比度设置越大,驱动电流越大,显示效果就越亮。

3.1.2 Entire Display ON(OLED显示跟随GDDRAM)
指令/数据D7D6D5D4D3D2D1D0指令作用
0xA4/0xA51010010X00xA4:OLED显示跟随GDDRAM
0xA5:OLED显示固定,不跟随GDDRAM
默认为0xA4

这个命令只需发送命令字节0xA4或0xA5,可以设置OLED的显示内容是否跟随GDDRAM内容变化

3.1.3 Set Normal/Inverse Display (设置反色)
指令/数据D7D6D5D4D3D2D1D0指令作用
0xA6/0xA71010011X00xA6:正常显示,即GDDRAM中1表示显示,0表示不显
0xA7:反转显示,即GDDRAM中0表示显示,1表示不显
默认为0xA6

这个命令只需发送命令字节0xA6或0xA7,可以设置OLED的显示反色。

3.1.4 Set Display ON/OFF (开启/关闭显示)
指令/数据D7D6D5D4D3D2D1D0指令作用
0xAE/0xAF1010111X00xAE:关闭显示,进入睡眠模式
0xAF:开启显示
默认为0xAE

这个命令只需发送命令字节0xAE或0xAF,可以设置OLED是否开启显示

3.2 Scrolling Command 滚动设置

指令/数据D7D6D5D4D3D2D1D0指令作用
0x26/0x270010011X00x26, X[0]=0, 右水平滚动
0x27, X[0]=1, 左水平滚动
(水平滚动 1 列)
A[7:0]00000000A[7:0] : 虚拟字节(设置为 00h)
B[2:0]*****B2B1B0定义起始页地址外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
C[2:0]*****C2C1C0设置每个滚动步骤之间的时间间隔
帧频率术语image-20240824045430620
D[2:0]*****D2D1D0定义结束页地址image-20240824045539818D[2:0] 的值必须大于或等于至 B[2:0]
E[7:0]00000000虚拟字节(设置为 00h)
F[7:0]11111111虚拟字节(设置为 FFh)

3.3 Addressing Setting Command Table 寻址方式设置

3.3.1 Set Lower Column Start Address for Page Addressing Mode为页面寻址模式设置低列起始地址
指令/数据D7D6D5D4D3D2D1D0指令作用
00~0F0000X3X2X1X0设置列起始地址的下半字节
使用 X[3:0] 注册页面寻址模式
作为数据位。初始显示线路寄存器为
RESET后重置为0000b。

这个命令设置在页寻址(Page Addressing)模式下列起始地址的低4位。
这个命令仅在页寻址模式下有效

3.3.2 Set Higher Column Start Address forPage AddressingMode 为页面寻址设置高列起始地址
指令/数据D7D6D5D4D3D2D1D0指令作用
10~1F0001X3X2X1X0使用 X[3:0] 为页面寻址模式设置列起始地址寄存器的较高半字节
作为数据位。初始显示行寄存器为
RESET 后重置为 0000b。
3.3.3 Set Memory Addressing Mode 设置内存寻址模式
指令/数据D7D6D5D4D3D2D1D0指令作用
0x2000100000设置内存寻址模式
A[1:0]******A1A0A[1:0] = 00b, 水平寻址模式
A[1:0] = 01b, 垂直寻址模式
A[1:0] = 10b, 页面寻址模式 (RESET)
A[1:0] = 11b, 无效

这个命令首先发送0x20命令字节,然后发送寻址模式选择字节,各寻址模式如下:

页寻址模式:

image-20240824051015345

水平/行寻址模式:

image-20240824051029826

垂直/列寻址模式:

3.3.4 Set Column Address设置列地址
指令/数据D7D6D5D4D3D2D1D0指令作用
0x2100100001设置列起始地址和结束地址
此命令仅在行/列寻址模式下有效
A[6:0]*A6A5A4A3A2A1A0A[6:0]:列起始地址,范围:0-127d,
(重置=0d)
B[6:0]*B6B5B4B3B2B1B0B[6:0]:列结束地址,范围:0-127d,
(重置 =127d)

这个命令首先发送0x21命令字节,然后发送2个数据字节用于设定列的起始和结束地址。

3.3.5 Set Page Start Address for Page Addressing Mode为页面寻址模式设置页面开始地址
指令/数据D7D6D5D4D3D2D1D0指令作用
0x2200100010设置页面开始和结束地址
此命令仅适用于水平或垂直寻址模式。
A[2:0]*****A2A1A0A[2:0] : 页面起始地址,范围: 0-7d,
(重置 = 0d)
B[2:0]*****B2B1B0B[2:0]:页面结束地址,范围:0-7d,
(复位 = 7d)

这个命令用于设置页寻址模式下的页起始地址。结合命令4.3.1和4.3.2,可以设置页寻址模式下一页的数据寻址刷新,如下图设置起始页为Page2,起始列为3:

image-20240824055120623

3.3.6 Set Page Start Address for Page Addressing Mode为页面寻址模式设置页面开始地址
指令/数据D7D6D5D4D3D2D1D0指令作用
B0~B710110X2X1X0设置 GDDRAM 页面起始地址
(PAGE0~PAGE7) 用于寻址模式使用 X[2:0]。
此命令仅适用于页面寻址模式

4.OLED 模块的初始化过程

image-20240824060210598

  1. 设置STM32F103OLED 模块相连接的IO

这一步,先将我们与 OLED 模块相连的 IO 口设置为输出,具体使用哪些 IO 口,这里需要根据连接电路以及 OLED 模块所设置的通讯模式来确定。这些将在硬件设计部分向大家介绍。

  1. 初始化OLED模块

其实这里就是上面的初始化框图的内容,通过对 OLED 相关寄存器的初始化,来启动 OLED的显示。为后续显示字符和数字做准备。

  1. 通过函数将字符和数字显示到OLED模块上。

这里就是通过我们设计的程序,将要显示的字符送到 OLED 模块就可以了,这些函数将在软件设计部分向大家介绍。

4.1 硬件连接

这里我用的是正点原子的战舰开发板与OLED屏幕

image-20240824060842066

插入后查文档得出接口对应的GPIO接口

image-20240824060939836

image-20240824061031100

4.2 程序设计

image-20240824061116492

4.2.1 ASCII 字符集内容

首先,用oledfont.h 头文件存储

/* 常用 ASCII 表
* 偏移量 32 
* ASCII 字符集: !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]
^_`abcdefghijklmnopqrstuvwxyz{|}~
* PC2LCD2002 取模方式设置:阴码+逐列式+顺向+C51 格式
* 总共:3 个字符集(12*12、16*16 和 24*24),用户可以自行新增其他分辨率的字符集。
* 每个字符所占用的字节数为:(size/8+((size%8)?1:0))*(size/2),其中 size:是字库生成时的点
* 阵大小(12/16/24...)
*/
/* 12*12 ASCII 字符集点阵 */
const unsigned char oled_asc2_1206[95][12]={ ...这里省略字符集库... };
/* 16*16 ASCII 字符集点阵 */
const unsigned char oled_asc2_1608[95][16]={ ...这里省略字符集库... };
/* 24*24 ASICII 字符集点阵 */
const unsigned char oled_asc2_2412[95][36]={ ...这里省略字符集库... };

该头文件中包含三个大小不同的 ASCII 字符集点阵,其中包括:1212 ASCII 字符集点阵、16 * 16 ASCII 字符集点阵、2424 ASICII 字符集点阵。每个字符集点阵都包含 95 个常用的 ASCII字符集 , 从空格符开始 ( 即 ASCII 码 表 编 号 的 32~127 对 应 的 字 符 ) ,分别

为:

!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
4.2.2 oled.c 文件的驱动源码

oled.c 文件的驱动源码介绍

/**
 * @brief       初始化OLED(SSD1306)
 * @param       无
 * @retval      无
 */
#define OLED_CMD        0       /* 写命令 */
#define OLED_DATA       1       /* 写数据 */
void oled_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    __HAL_RCC_GPIOC_CLK_ENABLE();     /* 使能PORTC时钟 */
    __HAL_RCC_GPIOD_CLK_ENABLE();     /* 使能PORTD时钟 */
    __HAL_RCC_GPIOG_CLK_ENABLE();     /* 使能PORTG时钟 */

    /* PC0 ~ 7 设置 */
    gpio_init_struct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;                
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;        /* 中速 */
    HAL_GPIO_Init(GPIOC, &gpio_init_struct);                /* PC0 ~ 7 设置 */

    gpio_init_struct.Pin = GPIO_PIN_3|GPIO_PIN_6;           /* PD3, PD6 设置 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;        /* 中速 */
    HAL_GPIO_Init(GPIOD, &gpio_init_struct);                /* PD3, PD6 设置 */
    
    gpio_init_struct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;        /* 中速 */
    HAL_GPIO_Init(GPIOG, &gpio_init_struct);                /* WR/RD/RST引脚模式设置 */

    OLED_WR(1);
    OLED_RD(1);
    OLED_CS(1);
    OLED_RS(1);

    OLED_RST(0);
    delay_ms(100);
    OLED_RST(1);

    oled_wr_byte(0xAE, OLED_CMD);   /* 关闭显示 */
    oled_wr_byte(0xD5, OLED_CMD);   /* 设置时钟分频因子,震荡频率 */
    oled_wr_byte(80, OLED_CMD);     /* [3:0],分频因子;[7:4],震荡频率 */
    oled_wr_byte(0xA8, OLED_CMD);   /* 设置驱动路数 */
    oled_wr_byte(0X3F, OLED_CMD);   /* 默认0X3F(1/64) */
    oled_wr_byte(0xD3, OLED_CMD);   /* 设置显示偏移 */
    oled_wr_byte(0X00, OLED_CMD);   /* 默认为0 */

    oled_wr_byte(0x40, OLED_CMD);   /* 设置显示开始行 [5:0],行数. */

    oled_wr_byte(0x8D, OLED_CMD);   /* 电荷泵设置 */
    oled_wr_byte(0x14, OLED_CMD);   /* bit2,开启/关闭 */
    oled_wr_byte(0x20, OLED_CMD);   /* 设置内存地址模式 */
    oled_wr_byte(0x02, OLED_CMD);   /* [1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10; */
    oled_wr_byte(0xA1, OLED_CMD);   /* 段重定义设置,bit0:0,0->0;1,0->127; */
    oled_wr_byte(0xC8, OLED_CMD);   /* 设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数 */
    oled_wr_byte(0xDA, OLED_CMD);   /* 设置COM硬件引脚配置 */
    oled_wr_byte(0x12, OLED_CMD);   /* [5:4]配置 */

    oled_wr_byte(0x81, OLED_CMD);   /* 对比度设置 */
    oled_wr_byte(0xEF, OLED_CMD);   /* 1~255;默认0X7F (亮度设置,越大越亮) */
    oled_wr_byte(0xD9, OLED_CMD);   /* 设置预充电周期 */
    oled_wr_byte(0xf1, OLED_CMD);   /* [3:0],PHASE 1;[7:4],PHASE 2; */
    oled_wr_byte(0xDB, OLED_CMD);   /* 设置VCOMH 电压倍率 */
    oled_wr_byte(0x30, OLED_CMD);   /* [6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc; */

    oled_wr_byte(0xA4, OLED_CMD);   /* 全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏) */
    oled_wr_byte(0xA6, OLED_CMD);   /* 设置显示方式;bit0:1,反相显示;0,正常显示 */
    oled_wr_byte(0xAF, OLED_CMD);   /* 开启显示 */
    oled_clear();
}

该函数的结构比较简单,开始是对 GPIO 口的初始化,这里我们用了宏定义 OLED_MODE来决定要设置的 IO 口,后面的就是一些初始化序列了,我们按照厂家提供的资料来做就可以。

值得注意一点的是,因为 OLED 是无背光的,在初始化之后,我们把显存都清空了,所以我们在屏幕上是看不到任何内容的,就像没通电一样,不要以为这就是初始化失败,要写入数据模块才会显示的。

led_wr_byte函数,实现了向SSD1306写入数据,该流程符合8080的[写时序](# 1.2.1 写时序)与[指令格式](#3. 指令格式)

static void oled_wr_byte(uint8_t data, uint8_t cmd)
{
    oled_data_out(data);
    OLED_RS(cmd);
    OLED_CS(0);
    OLED_WR(0);
    OLED_WR(1);
    OLED_CS(1);
    OLED_RS(1);
}

8080 并口模式下的 oled_wr_byte 函数还调用 oled_data_out 函数,其定义如下:

/**
 * @brief       通过拼凑的方法向OLED输出一个8位数据
 * @param       data: 要输出的数据
 * @retval      无
 */
static void oled_data_out(uint8_t data)
{
    GPIOC->ODR = (GPIOC->ODR & 0XFF00) | (data & 0X00FF);
}

oled_data_out 函数的处理方法,就是我们前面说的,因为 OLED 的 D0~D7 对于战舰开发板来说正好顺序接到了 GPIOC[0:7],所以我们只要向 GPIOC 的低位输出一次数据即可实现并口数据输出。

接着,要介绍的是 oled_refresh_gram 更新显存到 OLED 函数,该函数的作用是把我们在程序中定义的二维数组 g_oled_gram 的值一次性刷新到 OLED 的显存 GRAM 中。我们在 oled.c 文件开头定义了如下一个二维数组:

/* 
* OLED 的显存
* 每个字节表示 8 个像素, 128,表示有 128 列, 8 表示有 64 行, 高位表示高行数. 
* 比如:g_oled_gram[0][0],包含了第一列,第 1~8 行的数据. g_oled_gram[0][0].0,即表示坐标 
* (0,0)
* 类似的: g_oled_gram[1][0].1,表示坐标(1,1), g_oled_gram[10][1].2,表示坐标(10,10), 
* 存放格式如下(高位表示高行数).
* [0]0 1 2 3 ... 127
* [1]0 1 2 3 ... 127
* [2]0 1 2 3 ... 127
* [3]0 1 2 3 ... 127
* [4]0 1 2 3 ... 127
* [5]0 1 2 3 ... 127
* [6]0 1 2 3 ... 127
* [7]0 1 2 3 ... 127
*/
static uint8_t g_oled_gram[128][8];

该数组值与 OLED 显存 GRAM 值一一对应。在操作的时候我们只需要先修改该数组的值,然后再通过调用 oled_refresh_gram 函数把数组的值一次性刷新到 OLED 的 GRAM 上即可。oled_refresh_gram 函数定义如下:

void oled_refresh_gram(void)
{
    uint8_t i, n;

    for (i = 0; i < 8; i++)
    {
        oled_wr_byte (0xb0 + i, OLED_CMD); /* 设置页地址(0~7) */
        oled_wr_byte (0x00, OLED_CMD);     /* 设置显示位置—列低地址 */
        oled_wr_byte (0x10, OLED_CMD);     /* 设置显示位置—列高地址 */

        for (n = 0; n < 128; n++)
        {
            oled_wr_byte(g_oled_gram[n][i], OLED_DATA);
        }
    }
}

oled_refresh_gram 函数先设置页地址,然后写入列地址(也就是纵坐标),然后从 0 开始写入 128 个字节,写满该页,最后循环把 8 页的内容都写入,就实现了整个从 STM32 显存到 OLED显存的拷贝。

/**
 * @brief       OLED画点
 * @param       x  : 0~127
 * @param       y  : 0~63
 * @param       dot: 1 填充 0,清空
 * @retval      无
 */
void oled_draw_point(uint8_t x, uint8_t y, uint8_t dot)
{
    uint8_t pos, bx, temp = 0;

    if (x > 127 || y > 63) return;  /* 超出范围了. */

    pos = y / 8;        /* 计算GRAM里面的y坐标所在的字节, 每个字节可以存储8个行坐标 */

    bx = y % 8;         /* 取余数,方便计算y在对应字节里面的位置,及行(y)位置 */
    temp = 1 << bx;     /* 高位表示低行号, 得到y对应的bit位置,将该bit先置1 */

    if (dot)    /* 画实心点 */
    {
        g_oled_gram[x][pos] |= temp;
    }
    else        /* 画空点,即不显示 */
    {
        g_oled_gram[x][pos] &= ~temp;
    }
}

g_oled_gram [128][8]二维数组中的 128 代表列数(x 坐标),而 8 代表的是页,每页又包含8 行,总共 64 行(y 坐标),从高到低对应行数从小到大,如表 24.3.2.1 所示:

image-20240824063806377

上表中 G 代表 OLED_GRAM,G[0][0]就表示 OLED_GRAM [0][0]。比如,我们要在 x=3,y=9 这个点写入 1,则可以用这个句子实现:

OLED_GRAM [3][1] |= 1<<1;

一个通用的在点(x,y)置 1 表达式为:

OLED_GRAM [x][y/8] |= 1<<(y%8);

其中 x 的范围为:0~127;y 的范围为:0~63。

该函数有 3 个形参,前两个是横纵坐标,第三个 t 为要写入 1 还是 0。该函数实现了我们在OLED 模块上任意位置画点的功能。

5.总结

OLED可应用多种通信协议,8080并口,IIC,SPI等,通过协议规定的时序与命令向SSD1306写入,驱动OLED进行显示

8 行,总共 64 行(y 坐标),从高到低对应行数从小到大,如表 24.3.2.1 所示:

[外链图片转存中…(img-MRvK1pnw-1724453272197)]

上表中 G 代表 OLED_GRAM,G[0][0]就表示 OLED_GRAM [0][0]。比如,我们要在 x=3,y=9 这个点写入 1,则可以用这个句子实现:

OLED_GRAM [3][1] |= 1<<1;

一个通用的在点(x,y)置 1 表达式为:

OLED_GRAM [x][y/8] |= 1<<(y%8);

其中 x 的范围为:0~127;y 的范围为:0~63。

该函数有 3 个形参,前两个是横纵坐标,第三个 t 为要写入 1 还是 0。该函数实现了我们在OLED 模块上任意位置画点的功能。

5.总结

OLED可应用多种通信协议,8080并口,IIC,SPI等,通过协议规定的时序与命令向SSD1306写入,驱动OLED进行显示

  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值