STM32CubeMX配置LTDC接口:理论分析与限制说明

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

STM32 LTDC显示控制器深度解析:从原理到实战的全链路优化

在智能设备日益普及的今天,一块稳定流畅的显示屏几乎成了嵌入式产品的“门面担当”。无论是工业HMI、医疗仪器还是车载中控,背后都离不开一个关键角色—— LTDC(LCD-TFT Display Controller) 。它不像CPU那样引人注目,却默默承担着将数字信号转化为绚丽图像的重任。

而当我们打开STM32CubeMX,面对LTDC那一堆密密麻麻的参数时,是否曾有过这样的疑问:“这些HBP、VFP到底是啥?为什么改了一个数屏幕就花屏了?” 🤔
别急,今天我们不讲“怎么点按钮”,而是带你真正走进LTDC的内心世界,搞懂它背后的逻辑与玄机。


LTDC的核心机制:不只是像素搬运工

很多人以为LTDC就是个“搬砖”的——把内存里的颜色数据搬到屏幕上。但实际上,它更像是一位交响乐团的指挥家,协调着时钟、同步信号、图层混合和内存访问等多个声部,确保每一帧画面都能精准上演。

像素时钟:整个系统的节拍器 ⏱️

想象一下乐队演奏,如果节拍器不准,再好的乐手也会乱套。对LTDC来说,这个节拍器就是 像素时钟(PCLK)

它的频率决定了每秒能传输多少像素点。比如你要驱动800×480@60Hz的屏幕,总带宽需求是:

800 × 480 × 60 = 每秒2304万像素

也就是说,PCLK至少得跑到23.04MHz以上才能满足基本需求(还不算消隐期)。但在实际工程中,我们通常要留出足够的余量。

那么问题来了:这个PCLK是怎么来的?

在STM32F429/F7/H7系列中,路径如下:

HSE晶振 → PLLSAI锁相环 → LTDC_CLK → 分频器 → PCLK

举个例子,在STM32F429上配置:

RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
PeriphClkInitStruct.PLLSAI.PLLSAIN = 192;   // VCO = 192MHz (基于8MHz输入)
PeriphClkInitStruct.PLLSAI.PLLSAIR = 5;     // 输出 = 192 / 5 = 38.4MHz
PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_8; // 最终PCLK = 38.4 / 8 = 4.8MHz ❌ 太低!

等等……这只有4.8MHz?根本不够用啊!

其实这里有个常见误区: PLLSAIDivR 并不是直接分给PCLK的唯一因素。真正的PCLK还受LTDC内部寄存器 LTDC_LCR 控制,而且部分芯片需要通过额外的时钟树分支。

正确的做法是结合数据手册中的时钟框图来分析。例如在F429中, PLLSAI/PLLR 提供的是 LTDC_CLK ,然后由 LTDC_PCPRE 再次分频得到PCLK。

假设我们要达到38.4MHz:

  • 输入HSE = 8MHz
  • 设置 PLLSAIN=192 , PLLSAIR=5 → 得到307.2MHz?
  • 等等!注意公式应为: (HSE × N) / M / R
  • 若M=8,则 (8MHz × 192)/8/5 = 38.4MHz

所以正确设置应为:

PeriphClkInitStruct.PLLSAI.PLLM = 8;    // 归一化到1MHz基准
PeriphClkInitStruct.PLLSAI.PLLSAIN = 192;
PeriphClkInitStruct.PLLSAI.PLLSAIR = 5;
PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_1; // 不再额外分频

这样就能输出稳定的38.4MHz,刚好匹配典型800×480面板的需求。

💡 小贴士 :如果你发现屏幕闪烁或无法点亮,第一步就应该拿示波器测PA8(PCLK引脚),确认是否有接近理论值的方波输出。没有时钟,一切归零。


同步信号的时空对齐艺术 🎯

除了PCLK,LTDC还需要三个关键信号来告诉显示器“什么时候开始一行”、“什么时候开始一帧”以及“哪些数据是有效的”。

它们分别是:

信号 功能
HSYNC 水平同步脉冲,标识新行开始
VSYNC 垂直同步脉冲,标识新帧开始
DE(Data Enable) 高电平时表示当前传输的是有效像素

这三个信号的时间关系必须严格遵循面板规格书中的Timing Diagram。

以常见的4.3寸800×480屏为例,其水平方向周期由四部分组成:

[ HSYNC ][ HBP ][ ACTIVE WIDTH ][ HFP ]
  • HSYNC :同步脉冲宽度(如2个PCLK)
  • HBP :后肩,同步结束后等待时间(如46个PCLK)
  • ACTIVE WIDTH :可见像素数量(800)
  • HFP :前肩,有效数据显示后的恢复时间(如46个PCLK)

垂直方向同理:

[ VSYNC ][ VBP ][ ACTIVE HEIGHT ][ VFP ]

LTDC寄存器并不是让你直接填这些值,而是要求你填写“累计偏移量”——也就是每个阶段相对于起点的位置。

比如:

hltdc.Init.HorizontalSync = 2 - 1;          // HSW-1
hltdc.Init.VerticalSync = 2 - 1;            // VSW-1
hltdc.Init.AccumulatedHBP = 46 + 2 - 1;     // HBP + HSW -1
hltdc.Init.AccumulatedVBP = 23 + 2 - 1;     // VBP + VSW -1
hltdc.Init.AccumulatedActiveW = 800 + 46 + 2 - 1;  // 总有效宽度前的偏移
hltdc.Init.AccumulatedActiveH = 480 + 23 + 2 - 1;
hltdc.Init.TotalWidth = 800 + 46 + 2 + 46;         // 全周期长度
hltdc.Init.TotalHeigh = 480 + 23 + 2 + 23;

⚠️ 注意所有值都要减1!因为硬件计数器是从0开始的。

你可以把它理解成画坐标轴:
- 第0个PCLK是起点;
- 到第1个(即2-1)结束HSYNC;
- 到第47个(46+2-1)进入有效区域;
- 直到第893个(894-1)完成整行。

如果某个参数错了,轻则图像偏移,重则黑屏。曾经有位工程师把HFP设为0,结果右边边缘被切掉了一大块,折腾半天才发现原来是少了“缓冲时间”。

🔧 调试建议 :使用逻辑分析仪抓取HSYNC/VSYNC波形,观察脉宽和极性是否符合预期。大多数ILI9488/NT35510类IC都要求低电平有效。


图层混合的艺术:Alpha blending如何工作?

LTDC支持最多两个图层叠加,这使得我们可以实现背景+前景、菜单栏浮动窗口等复杂UI效果。

但它的混合方式不是简单的“前景覆盖背景”,而是基于 预乘Alpha(Premultiplied Alpha) 的数学模型:

Output = Foreground + Background × (1 - α)

其中:
- Foreground 是已经乘过α的颜色值(如红色半透明:α=0.5, color=(128,0,0))
- Background 是原始背景色
- α 是透明度系数(0~1)

STM32提供了三种混合模式:

模式 描述 使用场景
Mode 0 固定Alpha混合 半透明遮罩层
Mode 1 像素级Alpha混合 PNG图标叠加
Mode 2 固定×像素Alpha 实现淡入淡出动画

代码配置如下:

LTDC_LayerCfgTypeDef pLayerCfg = {0};

pLayerCfg.WindowX0 = 0;
pLayerCfg.WindowX1 = 800;
pLayerCfg.WindowY0 = 0;
pLayerCfg.WindowY1 = 480;

pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_ARGB8888;
pLayerCfg.Alpha = 128;                    // 固定透明度50%
pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA;  // C = α*C
pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_ONE_MINUS_PAxCA; // (1-α)*C

pLayerCfg.FBStartAdress = (uint32_t)framebuffer_layer1;
pLayerCfg.ImageWidth = 800;
pLayerCfg.ImageHeight = 480;

HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0); // 应用到Layer 0

🧠 经验之谈 :虽然ARGB8888支持透明通道,但代价是内存占用翻倍(相比RGB565)。对于不需要精细透明效果的应用,完全可以使用RGB565 + 固定Alpha模拟半透明,既省资源又高效。

此外,图层还支持裁剪窗口(Windowing),可以只更新局部区域,避免全屏刷新带来的性能损耗。


显示时序的科学计算法 🔢

很多开发者喜欢直接复制别人工程里的数值,但一旦换了屏幕就束手无策。真正掌握LTDC,必须学会自己推导参数。

完整帧周期构成

每一帧包含“可见区”和“非可见区”(即消隐期),后者用于给液晶响应时间和重置电路留出空档。

水平周期(单位:PCLK周期)

$$ T_{\text{horz}} = \text{HSW} + \text{HBP} + \text{WIDTH} + \text{HFP} $$

垂直周期(单位:行数)

$$ T_{\text{vert}} = \text{VSW} + \text{VBP} + \text{HEIGHT} + \text{VFP} $$

由此可得刷新率公式:

$$
\text{Refresh Rate} = \frac{\text{PCLK Frequency}}{T_{\text{horz}} \times T_{\text{vert}}}
$$

以800×480@60Hz为例:

  • HSW = 2
  • HBP = 46
  • HFP = 46
  • VSW = 2
  • VBP = 23
  • VFP = 23

→ $ T_{\text{horz}} = 894 $, $ T_{\text{vert}} = 528 $

若PCLK = 38.4MHz,则:

$$
\frac{38,400,000}{894 × 528} ≈ 81.4\,\text{Hz}
$$

咦?怎么比60Hz高这么多?

哦!原来我记错了标准参数 😅

查证后发现,真实推荐值往往是:

  • HFP = 210
  • HBP = 46
  • HSW = 2
    → 总周期 = 800+2+46+210 = 1058

再算一次:

$$
\frac{38,400,000}{1058 × 528} ≈ 68.5\,\text{Hz}
$$

仍然偏高……

继续调整,最终找到一组合理组合:

  • HFP = 210
  • HBP = 88
  • HSW = 40
    → Total Horz = 800+40+88+210 = 1138
  • VFP = 13
  • VBP = 33
  • VSW = 4
    → Total Vert = 480+4+33+13 = 530

重新计算:

$$
\frac{38,400,000}{1138 × 530} ≈ 63.7\,\text{Hz}
$$

还是略高。看来38.4MHz太高了,试试降频到33.3MHz?

$$
\frac{33,300,000}{1138 × 530} ≈ 55.2\,\text{Hz}
$$

终于接近理想值了!说明原厂推荐的PCLK可能并不是最大值,而是经过权衡后的稳定频率。

结论 :不要盲目追求高频PCLK,稳定性才是第一位。建议误差控制在±2%以内。


如何从数据手册提取参数?

以ATK-4.3’’ TFT模块为例,其Datasheet给出以下信息:

参数 典型值
PCLK 30 MHz
HSW 2 cycles
HBP 46
HFP 46
VSW 2 lines
VBP 23
VFP 23

对照LTDC寄存器映射:

LTDC字段 计算方式
HorizontalSync HSW - 1 1
VerticalSync VSW - 1 1
AccumulatedHBP HSW + HBP - 1 2+46-1 = 47
AccumulatedVBP VSW + VBP - 1 2+23-1 = 24
AccumulatedActiveW WIDTH + HSW + HBP - 1 800+2+46-1 = 847
AccumulatedActiveH HEIGHT + VSW + VBP - 1 480+2+23-1 = 504
TotalWidth WIDTH + HSW + HBP + HFP 800+2+46+46 = 894
TotalHeigh HEIGHT + VSW + VBP + VFP 480+2+23+23 = 528

把这些值填进结构体即可:

hltdc.Init.HorizontalSync = 1;
hltdc.Init.VerticalSync = 1;
hltdc.Init.AccumulatedHBP = 47;
hltdc.Init.AccumulatedVBP = 24;
hltdc.Init.AccumulatedActiveW = 847;
hltdc.Init.AccumulatedActiveH = 504;
hltdc.Init.TotalWidth = 894;
hltdc.Init.TotalHeigh = 528;

只要面板手册没写错,这套参数基本一次成功!


内存管理:你的SDRAM撑得住吗? 💾

LTDC本身没有显存,所有像素数据都来自外部SRAM或SDRAM。这就带来了两个核心问题:

  1. 帧缓冲区有多大?
  2. 带宽够不够用?

帧缓冲大小计算

公式很简单:

$$
\text{Size} = \text{Width} × \text{Height} × \text{BytesPerPixel}
$$

例如:

  • 800×480 ARGB8888 → 800×480×4 = 1,536,000 B ≈ 1.46 MB
  • RGB565 → 800×480×2 = 768 KB

如果是双缓冲,就得准备两份空间,合计约2.92MB。

STM32F429片内SRAM一般只有256KB左右,显然不够。必须外挂SDRAM(如IS42S16400J)。

带宽压力测试 💥

这才是真正的瓶颈所在!

所需带宽:

$$
\text{Bandwidth} = \text{Resolution} × \text{Refresh Rate} × \text{BPP}
$$

仍以上述为例:

  • ARGB8888 @ 60Hz → 800×480×60×4 = 921.6 Mbps ≈ 115.2 MB/s
  • RGB565 → 800×480×60×2 = 76.8 MB/s

而STM32F429的FMC接口在100MHz下理论带宽为800Mbps(100MB/s),已经低于ARGB8888的需求!

这意味着:
- 如果强行使用ARGB8888,系统会频繁出现FIFO underrun(数据供给不上)
- 表现为画面撕裂、卡顿甚至死机

📌 解决方案
1. 降低刷新率至50Hz
2. 改用RGB565格式
3. 启用DMA2D加速局部更新,减少全刷次数
4. 将帧缓冲放在独立SDRAM Bank,提升并发能力

实测对比:

色彩格式 帧率 CPU占用 视觉体验
ARGB8888 ~50fps 75% 色彩丰富但微卡
RGB565 60fps 58% 肉眼难辨差异

所以除非你是做专业图像处理,否则 强烈建议优先选择RGB565


双缓冲 vs 三缓冲:告别画面撕裂 🛠️

当你在动态绘图时,可能会遇到“上半屏是旧内容,下半屏是新内容”的撕裂现象。这是典型的 前后缓冲切换时机不当 导致的。

双缓冲机制

最常用的方法是双缓冲:

  • Front Buffer :正在显示的帧
  • Back Buffer :CPU正在绘制的下一帧
  • 在VSYNC中断中交换指针

代码实现:

uint32_t framebuffer[2][800 * 480] __attribute__((aligned(32)));
volatile uint32_t current_idx = 0;

void LTDC_IRQHandler(void) {
    if (__HAL_LTDC_GET_FLAG(&hltdc, LTDC_FLAG_VSYNC)) {
        HAL_LTDC_SetAddress(&hltdc, 
            (uint32_t)framebuffer[1 - current_idx], 0);
        current_idx = 1 - current_idx;
    }
}

优点:简单可靠,内存开销适中
缺点:若绘制时间超过16.6ms(1/60s),仍可能发生跳帧

三缓冲模拟(软件实现)

为了进一步提高帧一致性,可以引入第三个缓冲区,形成流水线:

Buffer A: 显示中
Buffer B: 已完成,待显示
Buffer C: 正在绘制

仅当VSYNC到来且存在已完成的后备帧时才切换。

虽然LTDC不支持原生三缓冲,但我们可以通过RTOS任务调度模拟:

typedef enum {
    BUF_READY,
    BUF_DRAWING,
    BUF_PENDING
} buf_state_t;

buf_state_t states[3] = {BUF_READY, BUF_READY, BUF_READY};

// 绘制任务
void drawing_task(void *pvParameters) {
    while(1) {
        int idx = find_ready_buffer();
        draw_frame((uint32_t*)framebuffers[idx]);
        states[idx] = BUF_PENDING; // 标记为待显示
        vTaskDelay(pdMS_TO_TICKS(10)); // 控制帧率
    }
}

// 中断服务例程
void HAL_LTDC_LineEvenCallback(LTDC_HandleTypeDef *hltdc) {
    static int last_line = 0;
    if (last_line == 479) { // 每帧最后一行
        int pending = find_pending_buffer();
        if (pending >= 0) {
            HAL_LTDC_SetAddress(hltdc, framebuffers[pending], 0);
            states[pending] = BUF_DRAWING;
        }
    }
    last_line++;
}

这种方式能显著减少丢帧概率,适合动画密集型应用。


CubeMX背后的秘密:它是怎么生成代码的? 🔍

STM32CubeMX极大简化了配置流程,但也让很多人失去了对底层的理解。要知道,它生成的每一个函数调用,背后都有对应的寄存器操作。

GPIO自动配置机制

当你启用LTDC,CubeMX会自动识别所需引脚(R[7:0], G[7:0], B[7:0], HSYNC, VSYNC, DE, CLK),并将其配置为AF14功能。

生成代码如下:

GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF14_LTDC;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

关键点:
- 必须是 复用推挽输出 (AF_PP),开漏无法驱动长线缆
- 速度设为VERY_HIGH,否则高速信号会畸变
- Alternate编号必须准确对应RM0090手册中的定义

⚠️ 常见错误:手动修改引脚后忘记更新Alternate编号,导致功能错乱。

时钟树的影响路径

CubeMX通过图形化界面调节PLL参数,其影响路径为:

HSE → PLLSAI → LTDC_CLK → LTDC_PCPRE → PCLK

但它不会告诉你某些细节,比如:
- PLLSAI必须在使能LTDC之前锁定
- 某些封装下PA8可能被复用为MCO输出
- SDRAM初始化必须早于LTDC启动

因此建议在 main() 中加入自检逻辑:

if (HAL_RCC_GetSysClockFreq() != 180000000UL) {
    Error_Handler(); // 主频未达标
}
if (!__HAL_RCC_PLL_GET_FLAG(RCC_FLAG_PLLRDY)) {
    Error_Handler(); // PLL未锁定
}

配置到HAL的调用链

CubeMX生成的 MX_LTDC_Init() 函数,实际上调用了以下HAL API:

MX_LTDC_Init()
├── HAL_LTDC_Init() 
│   ├── LTDC->SSCR   = (hsync << 16) | vsync
│   ├── LTDC->BPCR   = (accum_hbp << 16) | accum_vbp
│   ├── LTDC->AWCR   = (active_w << 16) | active_h
│   ├── LTDC->TWCR   = (total_w << 16) | total_h
│   └── LTDC->GCR    = polarity_bits
├── HAL_LTDC_ConfigLayer() ×2
└── __HAL_LTDC_ENABLE()

了解这个链条的好处是:你可以脱离CubeMX,在运行时动态修改配置!

例如实现滑动动画:

// 动态移动图层
for(int x = 0; x < 100; x++) {
    HAL_LTDC_SetWindowPosition(&hltdc, x, 0, 0);
    HAL_Delay(10);
}

或者实时切换分辨率:

// 修改时序参数
hltdc.Init.TotalWidth = new_width + hsw + hbp + hfp;
HAL_LTDC_Init(&hltdc);

// 请求在垂直消隐期生效
HAL_LTDC_Reload(&hltdc, LTDC_RELOAD_VERTICAL_BLANKING);

这种灵活性正是高级GUI框架的基础。


实战排错指南:那些年踩过的坑 🚧

黑屏 or 雪花点?

这是最常见的启动失败现象。

黑屏 意味着完全没有数据输出,检查:
- 是否调用了 MX_LTDC_Init()
- PLLSAI是否使能
- PA8有没有PCLK信号(示波器测量)
- 背光是否开启

雪花点 则是有数据但内容随机,通常是:
- 帧缓冲未初始化(SDRAM未清零)
- 地址越界访问
- DMA传输中断

快速验证方法:

// 在右下角点亮一个红点
*(volatile uint32_t*)(fb_addr + (479*800 + 799)*4) = 0xFFFF0000;

如果能看到一个小红点,说明通路是通的。

图像偏移怎么办?

左边黑边太宽?右边被切掉了?

多半是HBP/HFP配错了。

解决办法:
1. 打开数据手册核对Timing Table
2. 用逻辑分析仪抓HSYNC波形
3. 调整AccumulatedHBP直到居中

也可以写个测试程序逐步试探:

for(int hbp = 10; hbp <= 100; hbp += 5) {
    hltdc.Init.AccumulatedHBP = hbp + 2 - 1;
    HAL_LTDC_Init(&hltdc);
    HAL_Delay(2000); // 每次停留2秒观察
}

为什么DMA2D一画就卡?

DMA2D虽快,但和LTDC共用FSMC总线时容易打架。

常见症状:
- 画图时画面卡顿
- 出现短暂黑屏
- FIFO underrun中断频繁触发

优化策略:
1. 提升DMA2D优先级:

HAL_NVIC_SetPriority(DMA2D_IRQn, 0, 0);
  1. 避免在VSYNC期间执行大块传输
  2. 使用 HAL_LTDC_ProgramLineEvent() 分段更新
  3. 开启ART Cache和Prefetch:
__HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();

多屏适配架构设计 🔄

产品系列化开发中,经常需要支持不同尺寸的屏幕。

通用做法是抽象配置结构体:

typedef struct {
    uint16_t w, h;
    uint16_t hsync, hbp, hfp;
    uint16_t vsync, vbp, vfp;
    uint32_t format;
    uint32_t fb_base;
} panel_cfg_t;

const panel_cfg_t panels[] = {
    [PANEL_43] = { .w=480, .h=272, .hsync=41, .hbp=2, .hfp=2, ... },
    [PANEL_70] = { .w=800, .h=480, .hsync=40, .hbp=88, .hfp=40, ... },
};

初始化时根据型号加载:

void init_panel(const panel_cfg_t *cfg) {
    hltdc.Init.HorizontalSync = cfg->hsync - 1;
    hltdc.Init.AccumulatedHBP = cfg->hsync + cfg->hbp - 1;
    // ...其他参数
    HAL_LTDC_Init(&hltdc);

    layer_cfg.ImageWidth = cfg->w;
    layer_cfg.ImageHeight = cfg->h;
    layer_cfg.FBStartAdress = cfg->fb_base;
    HAL_LTDC_ConfigLayer(&hltdc, &layer_cfg, 0);
}

配合编译开关或运行时检测,一套代码跑多屏不再是梦!


未来的路:LTDC的边界与突破 🚀

尽管LTDC功能强大,但仍有不少硬伤:

硬件限制

  • 仅支持并行RGB,无法原生驱动MIPI DSI
  • 最大分辨率受限于1024×768
  • ARGB8888下难以维持60Hz刷新率

目前只能靠桥接芯片(如ICN6211)转协议,但这增加了成本和复杂度。

工具链短板

STM32CubeMX尚不支持:
- 运行时动态修改图层属性
- 可视化双缓冲配置
- 自动带宽估算

不过好消息是,ST已在推动新一代解决方案:

  • STM32U5强化了Chrom-ART加速器
  • TouchGFX Designer支持热重载
  • 未来MPU或将集成DSI主机控制器

也许不久的将来,我们会看到真正的“嵌入式GPU”出现在Cortex-M内核上。


结语:让每一帧都值得期待 🌟

LTDC看似只是一个外设,但它背后凝聚了时序控制、内存管理、信号完整性等多重技术挑战。掌握它,不仅仅是会点几个参数,更是建立起一种系统级思维。

下次当你看到屏幕顺利点亮时,不妨想想:那一个个像素是如何跨越时钟域、穿越总线、最终呈现在你眼前的?

而这,正是嵌入式工程师的魅力所在 ❤️

“优秀的显示系统,从来都不是偶然发生的。”
—— 一位不愿透露姓名的STM32老兵

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值