ESP32-S3 IO 扩展方案(I/O 扩展芯片)

AI助手已提取文章相关产品:

ESP32-S3 的 IO 扩展之道:如何用 I²C 芯片“无中生有”出几十个 GPIO 🧠🔌

你有没有遇到过这种窘境?手里的 ESP32-S3 看似有 48 个 GPIO,结果一上电才发现——
十几个被 Flash 占了,几个是 JTAG 调试口,PSRAM 又吃掉一串,ADC 和触摸引脚还得留着……
最后真正能自由支配的?可能就二十多个。😅

可项目偏偏要接一个 16 键矩阵键盘、驱动 8 路继电器、读取 5 个数字传感器,还要留灯位指示状态……
这不巧了吗?GPIO 直接告急!🚨

这时候,与其换主控、改架构、重画板子,不如换个思路: 我们为什么不“借”点 IO 来用?

没错,今天我们就来聊聊嵌入式开发里那个低调却关键的“幕后英雄”—— I/O 扩展芯片
它就像一个外挂的“IO快递员”,通过仅两根线(I²C)就能给你送来整整 16 甚至 32 个新引脚,成本不过几毛到一块钱。

重点是,整个过程对 ESP32-S3 来说轻量又透明。你想轮询也好,想事件驱动也行,它都能配合得妥妥帖帖。


为什么选 I²C?而不是 SPI 或直接并行?

在讲具体芯片前,先解决一个灵魂问题:
既然要扩展 IO,为啥大家都爱用 I²C?SPI 不是更快吗?并行总线不是更直接?

其实答案很简单: 布线成本和资源占用

  • 并行方案 (比如用 74HC595 移位寄存器):虽然便宜,但你要 8 根数据线 + 时钟 + 锁存,占主控资源多,PCB 布局也头疼。
  • SPI 方案 :速度确实快,但每增加一片就得额外一个 CS 片选线,三五片还行,十片八片?光片选线就能让你 GPIO 再次崩溃 😵‍💫
  • I²C 方案 :只需要 SDA 和 SCL 两根线,理论上可以挂 128 个设备(实际受限于总线负载),地址靠引脚配置即可区分,简直是“以少控多”的典范!

所以,在 ESP32-S3 这种资源宝贵、追求高集成度的场景下,I²C 成为了 IO 扩展的首选通路。

而且别忘了,ESP32-S3 自带两个 I²C 控制器,支持主从模式,速率最高可达 1 Mbps(标准模式)甚至更高(超快模式),完全够用大多数中低速外设。


PCAL6416A:不只是“多16个IO”,它是懂系统的那种选手 ⚙️💡

说到 I²C IO 扩展芯片,很多人第一反应是 MCP23017 —— 没错,它很经典。
但我们先来看看一位“后起之秀”: NXP 的 PCAL6416A

它名字看着冷门,实则是个狠角色,尤其适合那些对功耗、可靠性、响应实时性要求更高的项目。

它到底强在哪?

咱们拆开看:

16 位可编程 GPIO,独立配置方向、上下拉、中断触发方式

每一脚都能单独设置为输入或输出,还能决定是否启用内部上拉/下拉电阻。这意味着你可以直接连接按键而无需外部电阻,省空间又省钱。

更重要的是,每个输入引脚都可以配置为上升沿、下降沿或双边沿触发中断。也就是说,只要某个按钮被按下或释放,它就会主动告诉你:“嘿,我变了!”而不是让你不停地去查。

真正的中断机制,告别轮询地狱

这是 PCAL6416A 最亮眼的一点。很多老式 IO 扩展芯片(比如 PCF8574)压根没有中断功能,你只能每隔几毫秒去读一次状态,白白消耗 CPU 时间。

而 PCAL6416A 支持 INT 输出引脚 ,当任意一个使能了中断的输入发生变化时,INT 引脚会被拉低(开漏输出),你可以把它接到 ESP32-S3 的任意一个外部中断引脚上(比如 GPIO34)。

这样一来,你的主循环就可以安心睡觉( delay() vTaskDelay() ),只有事件发生时才被唤醒处理,极大提升系统效率与响应速度。

双电压域设计,兼容 1.8V ~ 5.5V

它的 VDDIO 引脚支持宽压供电,意味着你可以让它一边连着 3.3V 的 ESP32-S3,另一边轻松对接 5V 的继电器模块或者老式 TTL 设备,中间不需要电平转换芯片!

这在混合电压系统中简直是救星。再也不用担心“这个传感器是 5V 的,我能接吗?”这种问题。

低至 1μA 的静态电流,电池供电设备最爱

如果你做的是便携式设备、无线门铃、环境监测节点这类靠电池运行的产品,那这个特性就太重要了。

PCAL6416A 在待机状态下几乎不耗电,配合 ESP32-S3 的深度睡眠模式,整机能做到月级甚至年級续航。

支持 SMBus Alert 和热复位

它不仅能上报事件,还能接收来自主机的远程复位命令。比如你在软件中检测到通信异常,可以直接发一条指令让它软重启,不用断电重来。

这对工业现场或无人值守设备来说,是非常实用的容错机制。


实战代码示例:用 Arduino 风格玩转 PCAL6416A 🔧

虽然官方没有提供专门的库,但好消息是: PCAL6416A 寄存器结构和 PCA9555 完全兼容!

所以我们完全可以借用 Adafruit 的 Adafruit_PCA9555 库来驱动它,零修改就能跑起来。

#include <Wire.h>
#include <Adafruit_PCA9555.h>

#define IO_EXPANDER_ADDR 0x20  // ADDR 接 GND → 地址 0x20

Adafruit_PCA9555 io_expander;

void setup() {
    Serial.begin(115200);
    Wire.begin(21, 22);  // SDA=21, SCL=22 (ESP32-S3 默认)

    if (!io_expander.begin(IO_EXPANDER_ADDR)) {
        Serial.println("❌ 无法连接到 PCAL6416A,请检查接线和地址");
        while (1);
    }

    Serial.println("✅ 成功初始化 PCAL6416A");

    // 配置:前8位为输出(控制LED),后8位为输入(读按键)
    for (int i = 0; i < 8; i++) {
        io_expander.pinMode(i, OUTPUT);
        io_expander.digitalWrite(i, LOW);  // 初始关闭
    }

    for (int i = 8; i < 16; i++) {
        io_expander.pinMode(i, INPUT_PULLUP);  // 启用内部上拉
    }

    // 设置中断:任一输入变化即触发,INT 输出为开漏低有效
    io_expander.setupInterrupts(true, false, LOW);

    // 开启 PIN8 的 CHANGE 中断检测
    io_expander.interruptPin(8, CHANGE);

    // 将 INT 引脚连接到 ESP32-S3 的 GPIO35,并注册中断服务程序
    pinMode(35, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(35), handle_io_interrupt, FALLING);
}

// 中断标志
volatile bool io_event_triggered = false;

void handle_io_interrupt() {
    io_event_triggered = true;  // 标记事件发生
}

void loop() {
    if (io_event_triggered) {
        noInterrupts();  // 关中断防止竞争
        io_event_triggered = false;
        interrupts();

        // 读取所有输入状态
        uint16_t input_state = io_expander.readGPIOAB();

        // 如果 PIN8 按下(低电平),点亮 LED0
        bool button_pressed = !(input_state & (1 << 8));
        io_expander.digitalWrite(0, button_pressed ? HIGH : LOW);

        Serial.printf("📥 输入状态变化: 0x%04X | 按钮状态: %s\n", 
                      input_state, button_pressed ? "PRESSED" : "RELEASED");
    }

    // 其他任务正常执行...
    delay(10);
}

📌 关键细节提示
- readGPIOAB() 一次性读取全部 16 位状态,效率高。
- 使用 interruptPin() 启用特定引脚的中断监测。
- INT 引脚必须连接到 ESP32 的外部中断引脚(如 GPIO34~39),这些引脚支持深度睡眠唤醒。
- 实际项目中建议加入软件消抖逻辑,例如延迟 10~20ms 再确认状态。


MCP23017:久经沙场的老将,生态王者 👑💪

如果说 PCAL6416A 是“性能派新生代”,那 MCP23017 就是当之无愧的“江湖元老”。

Microchip 出品,十多年活跃在各类 Arduino、Raspberry Pi 项目中,资料多、教程全、库完善,几乎是开源社区的标配 IO 扩展方案。

它凭什么这么稳?

✔ 广泛支持,拿来就用

无论你是用 Arduino IDE、PlatformIO 还是 ESP-IDF,都有成熟的库可用:

  • Arduino: Adafruit_MCP23017 Wire
  • ESP-IDF:自带 I²C 驱动 + 自定义封装
  • Python(树莓派): smbus2 , gpiozero

这意味着你几乎不用从头写底层通信协议,几分钟就能让芯片跑起来。

✔ 最大支持 8 片级联,共 128 个扩展 IO!

MCP23017 提供三个地址选择引脚 A0/A1/A2,组合出 8 个不同地址(0x20 ~ 0x27)。
也就是说,一根 I²C 总线上最多能挂 8 片,带来 128 个额外 GPIO

想象一下:
- 控制 64 个 LED 灯阵?
- 扫描 8×8 按键矩阵 × 4 层?
- 驱动多个数码管、继电器板、传感器组?

统统不在话下。这才是真正的“IO自由”。

✔ 寄存器丰富,控制精细

相比一些“傻瓜型”扩展芯片,MCP23017 提供了非常完整的寄存器体系:

寄存器 功能
IODIRA/B 设置端口方向(输入/输出)
IPOLA/B 极性反转(高电平变低电平触发)
GPINTENA/B 中断使能开关
DEFVALA/B 默认比较值(用于差异中断)
INTCONA/B 中断触发模式(默认 vs 变化)
IOCON 配置 BANK 模式、中断类型等

特别是 DEFVAL INTCON 的组合,可以实现“只有当我从高变低”才中断,避免频繁打扰主控。

✔ 支持自动地址递增,批量操作更高效

当你连续读写多个寄存器时,MCP23017 支持地址自动递增模式。
只需发送起始地址,后续数据会按顺序填入下一个寄存器,大大减少通信次数。

比如你想同时设置 IODIRA 和 IODIRB,可以用一次写操作完成:

i2c_start();
i2c_write(MCP23017_ADDR << 1 | I2C_WRITE);
i2c_write(REG_IODIRA);        // 起始地址
i2c_write(0x00);              // IODIRA = 输出
i2c_write(0xFF);              // IODIRB = 输入
i2c_stop();

效率比逐个写高出不少。


ESP-IDF 原生驱动 MCP23017:贴近硬件的操作体验 🔩

下面这段代码展示如何在 ESP-IDF 环境中使用原生 I²C API 驱动 MCP23017,适合追求性能与可控性的开发者。

#include "driver/i2c.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define I2C_PORT     I2C_NUM_0
#define MCP_ADDR     0x20
#define SDA_PIN      21
#define SCL_PIN      22
#define INT_GPIO     35

static void i2c_init() {
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = SDA_PIN,
        .scl_io_num = SCL_PIN,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = 400000
    };
    i2c_param_config(I2C_PORT, &conf);
    i2c_driver_install(I2C_PORT, I2C_MODE_MASTER, 0, 0, 0);
}

static esp_err_t mcp_write(uint8_t reg, uint8_t value) {
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (MCP_ADDR << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg, true);
    i2c_master_write_byte(cmd, value, true);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(I2C_PORT, cmd, pdMS_TO_TICKS(100));
    i2c_cmd_link_delete(cmd);
    return ret;
}

static esp_err_t mcp_read(uint8_t reg, uint8_t *value) {
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (MCP_ADDR << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg, true);
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (MCP_ADDR << 1) | I2C_MASTER_READ, true);
    i2c_master_read_byte(cmd, value, I2C_MASTER_NACK);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(I2C_PORT, cmd, pdMS_TO_TICKS(100));
    i2c_cmd_link_delete(cmd);
    return ret;
}

void mcp_setup() {
    // Port A 输出,Port B 输入
    mcp_write(0x00, 0x00);  // IODIRA
    mcp_write(0x01, 0xFF);  // IODIRB

    // 启用 Port B 输入中断
    mcp_write(0x04, 0xFF);  // GPINTENB
    mcp_write(0x07, 0x00);  // IOCON: Bank=0, SEQOP=1 (禁用地址递增锁)
}

void app_main(void) {
    i2c_init();
    mcp_setup();

    gpio_config_t int_conf = {
        .pin_bit_mask = BIT64(INT_GPIO),
        .mode = GPIO_MODE_INPUT,
        .pull_up_en = 1,
        .pull_down_en = 0,
        .intr_type = GPIO_INTR_NEGEDGE
    };
    gpio_config(&int_conf);

    uint8_t last_btn = 0xFF;

    while (1) {
        uint8_t current;
        if (gpio_get_level(INT_GPIO) == 0) {  // 中断触发
            mcp_read(0x13, &current);  // 读 GPIOB
            mcp_read(0x10, &current);  // 也可读 INTCAPB 获取中断瞬间状态

            if ((last_btn & 0x01) != (current & 0x01)) {
                vTaskDelay(pdMS_TO_TICKS(15));  // 简单消抖
                mcp_read(0x13, &current);
                bool pressed = !(current & 0x01);

                // 更新 Port A 输出
                mcp_write(0x12, pressed ? 0x01 : 0x00);

                printf("🎯 按键状态更新: %s\n", pressed ? "ON" : "OFF");
            }
            last_btn = current;
        }
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

📌 亮点说明
- 使用 INTCAPB 寄存器可获取中断发生那一刻的输入快照,避免因延迟读取导致的状态偏差。
- IOCON.SEQOP=1 关闭地址递增限制,允许连续访问非相邻寄存器。
- 实际应用中可结合 FreeRTOS 创建独立任务处理 IO 事件,进一步解耦主逻辑。


如何搭建一个多芯片协同工作的系统?🧠🔗

现在我们手里有了 PCAL6416A 和 MCP23017 两张牌,怎么打好这套组合拳?

来看一个真实场景:
你要做一个智能家居面板,功能包括:

  • 16 个背光按键(输入)
  • 8 路继电器控制(输出)
  • 4 个温湿度传感器(I²C)
  • OLED 显示屏(I²C)
  • Wi-Fi 上报事件

如果全靠 ESP32-S3 原生 GPIO,根本不够分。怎么办?

架构设计如下:

                         +------------------+
                         |   ESP32-S3       |
                         |                  |
                         | I2C_SCL ─────┬───┼────────────┐
                         | I2C_SDA ─────┼───┼────────────┤
                         +--------------+   |            |
                                            ▼            ▼
                                  +----------------+ +---------------+
                                  | PCAL6416A      | | MCP23017      |
                                  | Addr: 0x20     | | Addr: 0x21    |
                                  | Ports:         | | Ports:        |
                                  | - PA0-7: Relay | | - PA0-7: Keys |
                                  | - PB0-7: Spare | | - PB0-3: Sensors En |
                                  +----------------+ +---------------+
                                            │               │
                                            ▼               ▼
                                     [Relay Board]    [Key Matrix + Sensors]
                                            │               │
                                            └─────┬─────────┘
                                                  │
                                                  ▼
                                             [Event Handler]
                                             → Send to MQTT / HTTP
  • PCAL6416A 负责驱动继电器(大电流负载分离),利用其低功耗特性配合睡眠模式。
  • MCP23017 负责扫描按键和使能传感器电源,中断触发唤醒主控。
  • 两者共用同一 I²C 总线,地址错开即可和平共处。
  • INT 引脚汇总后接入 ESP32-S3 的同一个中断引脚(可用 OR 门电路合并,或分别注册)。

这样,原本需要 24 个 GPIO 的任务,现在只用了 2 根 I²C 线 + 1~2 根中断线就搞定了。


实际工程中的那些“坑”,我们都踩过了 ⚠️🔧

别以为接上线就能跑,实战中有很多细节容易翻车:

❌ 问题 1:I²C 总线不稳定,偶尔通讯失败

原因
- 上拉电阻太弱(>10kΩ)或太强(<1kΩ)
- 总线太长(>30cm)导致信号反射
- 多设备导致总线电容超标(>400pF)

解决方案
- 使用 4.7kΩ 上拉电阻(推荐)
- 每个 IO 扩展芯片电源引脚旁加 0.1μF 陶瓷电容 去耦
- 若设备超过 3 个或距离较远,考虑加 I²C 缓冲器 (如 PCA9515、TCA9517)

❌ 问题 2:中断误触发,CPU 被疯狂打断

原因
机械按键抖动、电源噪声、浮空引脚干扰

解决方案
- 硬件:加 RC 滤波(如 10kΩ + 100nF)
- 软件:中断内只设标志位,主循环延时 10~20ms 后再读状态
- 对于 PCAL6416A,可通过寄存器设置 去抖时间 (内置 100μs ~ 100ms 可调)

❌ 问题 3:地址冲突,找不到设备

常见错误 :两片芯片都接地,地址都是 0x20

正确做法
| 芯片 | A0 | A1 | A2 | 地址 |
|------|----|----|----|------|
| 第一片 | GND | GND | GND | 0x20 |
| 第二片 | VCC | GND | GND | 0x21 |
| 第三片 | GND | VCC | GND | 0x22 |

用万用表测一下各引脚电平,确保配置正确。

❌ 问题 4:驱动能力不足,灯不亮或继电器吸合无力

注意 :IO 扩展芯片输出电流有限!一般单脚最大 25mA,总和不超过 150mA。

所以:
- 直接驱动 LED 可以,但要串限流电阻(≥220Ω)
- 驱动继电器、电机、蜂鸣器?必须通过三极管或 MOSFET 放大!


选型建议:PCAL6416A vs MCP23017,怎么选?🤔📊

维度 PCAL6416A MCP23017
中断精度 ✅ 支持每脚独立边沿配置 ❌ 仅支持端口级中断
去抖功能 ✅ 内置可编程去抖滤波器 ❌ 依赖外部或软件处理
功耗 ✅ 典型 1μA(静态) ⚠️ 约 5~10μA
电压兼容性 ✅ 1.65V~5.5V 双域 ✅ 1.8V~5.5V
生态支持 ⚠️ 较少专用库,依赖兼容方案 ✅ 社区强大,库齐全
价格 💰 稍贵(约 ¥3~5) 💰 便宜(¥1~2)
适用场景 电池设备、高可靠性系统 工业控制、快速原型开发

📌 一句话总结
- 想做 低功耗穿戴设备、远程传感节点 ?→ 选 PCAL6416A
- 想快速搭个 中控面板、教学实验平台 ?→ 选 MCP23017


更进一步:你能用它们做什么酷炫的事?🚀✨

别只想着按键和灯,这些芯片还能玩出花来:

🔹 用 MCP23017 实现 16×16 LED 点阵扫描

将两个 MCP23017 分别作为行驱动和列驱动,通过动态扫描方式控制 256 个 LED,成本不到 10 块钱。

🔹 把 PCAL6416A 当作“智能配电开关”

配合 MOSFET,实现对多个传感器的独立供电控制。不用时彻底断电,真正做到零待机功耗。

🔹 构建多层按键系统(带组合键识别)

利用中断快速响应按键按下,再通过读取完整端口状态判断是否为 Ctrl+Alt+Del 式组合操作,适用于 HMI 设计。

🔹 与 FreeRTOS 结合,实现事件队列机制

中断触发 → 发送消息到队列 → 任务处理 → 回调通知云端,形成完整的异步事件链。


写在最后:扩展的不只是 IO,更是设计思维 🌱🧠

你看,表面上我们在讲怎么多几个引脚,
但实际上,我们是在学习一种 资源复用、分层解耦、模块化设计 的思维方式。

I/O 扩展芯片的存在提醒我们:

“不要试图让主控干所有事。”

把简单重复的工作交给外设,让 ESP32-S3 专注于它最擅长的事:联网、计算、调度、交互。

这才是现代嵌入式系统的优雅所在。

下次当你面对 GPIO 不足的困境时,不妨停下来想想:
是不是真的需要更多引脚?
还是我们只是没找对方法?

有时候, 两根线,就能打开新世界的大门。 🔓⚡

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

您可能感兴趣的与本文相关内容

内容概要:本文设计了一种基于PLC的全自动洗衣机控制系统内容概要:本文设计了一种,采用三菱FX基于PLC的全自动洗衣机控制系统,采用3U-32MT型PLC作为三菱FX3U核心控制器,替代传统继-32MT电器控制方式,提升了型PLC作为系统的稳定性与自动化核心控制器,替代水平。系统具备传统继电器控制方式高/低水,实现洗衣机工作位选择、柔和过程的自动化控制/标准洗衣模式切换。系统具备高、暂停加衣、低水位选择、手动脱水及和柔和、标准两种蜂鸣提示等功能洗衣模式,支持,通过GX Works2软件编写梯形图程序,实现进洗衣过程中暂停添加水、洗涤、排水衣物,并增加了手动脱水功能和、脱水等工序蜂鸣器提示的自动循环控制功能,提升了使用的,并引入MCGS组便捷性与灵活性态软件实现人机交互界面监控。控制系统通过GX。硬件设计包括 Works2软件进行主电路、PLC接梯形图编程线与关键元,完成了启动、进水器件选型,软件、正反转洗涤部分完成I/O分配、排水、脱、逻辑流程规划水等工序的逻辑及各功能模块梯设计,并实现了大形图编程。循环与小循环的嵌; 适合人群:自动化套控制流程。此外、电气工程及相关,还利用MCGS组态软件构建专业本科学生,具备PL了人机交互C基础知识和梯界面,实现对洗衣机形图编程能力的运行状态的监控与操作。整体设计涵盖了初级工程技术人员。硬件选型、; 使用场景及目标:I/O分配、电路接线、程序逻辑设计及组①掌握PLC在态监控等多个方面家电自动化控制中的应用方法;②学习,体现了PLC在工业自动化控制中的高效全自动洗衣机控制系统的性与可靠性。;软硬件设计流程 适合人群:电气;③实践工程、自动化及相关MCGS组态软件与PLC的专业的本科生、初级通信与联调工程技术人员以及从事;④完成PLC控制系统开发毕业设计或工业的学习者;具备控制类项目开发参考一定PLC基础知识。; 阅读和梯形图建议:建议结合三菱编程能力的人员GX Works2仿真更为适宜。; 使用场景及目标:①应用于环境与MCGS组态平台进行程序高校毕业设计或调试与运行验证课程项目,帮助学生掌握PLC控制系统的设计,重点关注I/O分配逻辑、梯形图与实现方法;②为工业自动化领域互锁机制及循环控制结构的设计中类似家电控制系统的开发提供参考方案;③思路,深入理解PL通过实际案例理解C在实际工程项目PLC在电机中的应用全过程。控制、时间循环、互锁保护、手动干预等方面的应用逻辑。; 阅读建议:建议结合三菱GX Works2编程软件和MCGS组态软件同步实践,重点理解梯形图程序中各环节的时序逻辑与互锁机制,关注I/O分配与硬件接线的对应关系,并尝试在仿真环境中调试程序以加深对全自动洗衣机控制流程的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值