Keil MDK-ARM从零到精通:STM32开发环境搭建与工程实践全攻略

大家好,我是专注于嵌入式开发的技术博主。在学习和开发STM32、51单片机等项目时,Keil(MDK-ARM或C51)几乎是绕不开的集成开发环境。然而,从软件安装、工程创建到代码调试、固件生成,每一步都可能遇到各种“坑”,比如环境变量配置错误、芯片包缺失、编译报错、无法下载等。网上的资料虽然多,但往往零散不成体系,新手容易迷失在细节里。

本文旨在为你提供一份从零开始、手把手式的Keil MDK使用全攻略。无论你是刚接触单片机的新手,还是需要快速搭建特定芯片(如STM32F103)开发环境的老手,都能在这里找到清晰的步骤、完整的代码示例以及避坑指南。我们将覆盖软件安装、工程创建、代码编写、编译调试、生成Hex/Bin文件等核心环节,并解答一系列高频问题,让你能真正“照着做,就能跑通”。

1. Keil MDK 核心概念与版本辨析

在开始动手之前,我们首先要理清几个关键概念,这能帮助你避免后续很多混淆。

Keil是什么? Keil是一家提供微控制器开发工具的公司,现已被ARM收购。我们常说的“Keil软件”通常指的是其µVision IDE(集成开发环境),它提供了一个编写、编译、调试嵌入式C/C++代码的统一平台。

MDK-ARM, C51, C251, C166 有什么区别? 这是Keil针对不同处理器内核提供的不同工具链(Toolchain)和软件包(Device Family Pack)的组合,它们都运行在µVision这个IDE外壳下。

  • MDK-ARM (Microcontroller Development Kit for ARM): 用于开发基于ARM Cortex-M、Cortex-R等内核的微控制器,如意法半导体(ST)的STM32系列、恩智浦(NXP)的LPC系列等。这是目前使用最广泛的版本。
  • C51: 用于开发基于Intel 8051架构的微控制器,如常见的STC89C52等。
  • C251/C166: 用于开发相应的16位/32位微控制器,相对小众。

µVision 和 Keil MDK 是什么关系? 你可以把µVision理解为一个“空壳”或“前台”,它提供了编辑器、项目管理器、调试器界面等。而MDK-ARM、C51等是“后台引擎”或“工具链”,包含了针对特定芯片的编译器(ARMCC或AC5/AC6)、汇编器、链接器以及调试驱动。安装MDK-ARM时,会自动安装对应版本的µVision IDE。

关于“Keil注册机”和“Keil uVision5 crack” 在搜索热词中,这些词汇出现频率很高,这反映了部分开发者面临的现实。Keil MDK是一款商业软件,对于个人学习和小规模应用,官方提供代码大小限制(例如32KB)的评估版(Eval Version),基本功能完整,足够学习使用。使用非授权方式破解软件存在法律风险、安全风险(可能携带病毒或后门)以及稳定性风险(可能导致编译异常或调试崩溃)。 本文强烈建议读者使用官方评估版进行学习,或通过正规渠道购买授权用于商业开发。 我们将完全基于官方评估版进行操作讲解。

本文环境说明:

  • 操作系统: Windows 10/11 64位
  • Keil 版本: µVision5 (MDK-ARM Version 5.xx)。µVision5是目前最稳定、资源最丰富的版本,µVision6尚在普及中,其基本操作逻辑一致。
  • 目标芯片: 以最流行的 STM32F103C8T6 (Cortex-M3内核)为例进行工程创建和演示。
  • 核心工具链: ARM Compiler 5 (AC5) 或 ARM Compiler 6 (AC6) 均可,文中会说明区别。

2. 环境准备:Keil MDK 安装与芯片支持包管理

这是万里长征第一步,也是最容易出错的一步。正确的安装是后续所有工作的基础。

2.1 获取与安装 Keil MDK

  1. 访问官网: 打开ARM Keil官方网站,找到MDK-ARM产品页面。
  2. 下载评估版: 寻找“Download MDK-ARM”或“Eval Version”链接。你需要填写简单的注册信息(邮箱、国家等)来获取下载链接。
  3. 运行安装程序: 下载完成后,以管理员身份运行安装程序(如 mdkxxx.exe )。
  4. 选择安装路径: 建议安装路径不要包含中文和空格,例如 D:\Keil_v5 。这将避免很多潜在的路径解析问题。
  5. 填写用户信息: 安装过程中会要求填写姓名、公司名和邮箱,评估版可随意填写。
  6. 等待安装完成: 安装程序会自动安装µVision IDE、ARM Compiler、基本示例和文档。 安装完成后,先不要急于运行软件。

2.2 安装设备支持包(Device Family Pack, DFP)

这是新手最常卡住的地方。Keil安装包本身不包含具体芯片(如STM32F103)的启动文件、外设寄存器定义、链接脚本等。这些内容以“包”的形式在线安装。

  1. 运行 Pack Installer: 安装完成后,可以在开始菜单找到 Keil µVision5 ,桌面上也会有快捷方式。首次运行,软件可能会自动打开 Pack Installer 。如果没有,你可以在µVision的菜单栏点击 Project -> Manage -> Pack Installer 来打开它。
  2. 更新包列表: 在Pack Installer界面,点击左上角的 Refresh 按钮,确保获取到最新的包列表。
  3. 搜索并安装芯片包: Device 选项卡或 Packs 选项卡中,找到你需要的芯片厂商和系列。例如,找到 STMicroelectronics ,展开后找到 STM32F1 Series ,再找到 STM32F103 。你会看到多个变种,选择 STM32F103C8 (对应STM32F103C8T6)。点击右侧的 Install 按钮。软件会自动下载并安装该芯片的所有必要文件(启动文件、外设库、Flash算法等)。
  4. 验证安装: 安装成功后,对应的版本号会显示为绿色。关闭Pack Installer。

为什么必须安装DFP? 没有DFP,你在新建工程时根本找不到你的芯片型号,编译时也会因为缺少核心的 .s 启动文件和 .h 头文件而报错。

2.3 安装J-Link/ST-Link等调试器驱动

如果你使用J-Link、ST-Link、DAPLink等硬件调试器下载和调试程序,需要确保其USB驱动已正确安装。

  • J-Link: 前往SEGGER官网下载并安装最新的J-Link软件包。
  • ST-Link: 前往ST官网下载STM32CubeProgrammer或ST-LINK Utility,安装时会包含驱动。
  • DAPLink: 通常被系统识别为CMSIS-DAP设备,Windows 10/11通常能自动安装,也可从ARM官网获取驱动。

安装后,将调试器通过USB连接电脑,在设备管理器中应能看到对应的设备,无感叹号。

至此,你的Keil MDK开发环境就基本准备好了。

3. 创建你的第一个STM32工程:从零到点灯

理论说再多不如动手一试。我们以创建一个控制LED闪烁的STM32F103工程为例,贯穿整个流程。

3.1 新建工程与选择设备

  1. 启动µVision5,创建新工程: Project -> New µVision Project...
  2. 选择工程路径和名称: 新建一个文件夹(如 STM32F103_LED ),进入该文件夹,为工程命名(如 LED ),点击保存。
  3. 选择设备(Device): 在弹出的 Select Device for Target 对话框中,左侧找到 STMicroelectronics -> STM32F1 Series -> STM32F103 -> STM32F103C8 。右侧会显示该芯片的基本信息。 确认无误后点击 OK 如果这里找不到你的芯片,说明之前的DFP没有安装成功。
  4. 管理运行时环境(RTE - Manage Run-Time Environment): 点击 OK 后,会弹出RTE配置窗口。这是一个用于快速添加中间件(如RTOS、文件系统、网络协议栈)和软件组件(如标准外设库STM32F1xx_DFP或HAL库)的图形化工具。对于初学者,我们暂时不使用这个功能,直接点击 Cancel 。我们将手动添加必要的文件,这有助于理解工程结构。

3.2 手动添加工程文件与目录结构

一个清晰的工程结构是良好习惯的开始。我们在工程根目录下创建几个文件夹:

  • User/ - 存放用户编写的应用层代码(main.c, led.c等)
  • Libraries/ - 存放芯片厂商提供的库文件(标准外设库或HAL库)
  • Startup/ - 存放启动文件(startup_stm32f103xb.s)
  • MDK-ARM/ - Keil自动生成的文件夹,存放编译输出文件和工程配置。

如何找到启动文件? 启动文件在DFP安装目录下。例如,如果你安装在D盘,路径可能类似于: D:\Keil_v5\ARM\PACK\Keil\STM32F1xx_DFP\2.4.0\Device\Source\ARM 。找到 startup_stm32f103xb.s 文件,复制到你的工程 Startup/ 文件夹下。

如何添加文件到工程?

  1. 在µVision左侧的 Project 窗口中,右键点击 Target 1 ,选择 Manage Project Items...
  2. Project Items 选项卡,可以修改 Target 名称(如改为 LED ),并创建 Groups (组),对应我们的文件夹。
    • 点击 New (Insert) 按钮,创建 User , Libraries , Startup 组。
  3. 为每个组添加文件:
    • 选中 Startup 组,点击右侧 Add Files ,导航到你的 Startup/ 文件夹,选择 startup_stm32f103xb.s 文件。
    • 其他组暂时没有文件,可以先不添加。

3.3 编写核心代码:main.c 与 led.c

现在我们来编写最简单的点灯程序。假设LED连接在PC13引脚(这是很多STM32最小板上的用户LED)。

1. 创建 main.c (位于 User/ 文件夹):

/* 文件:User/main.c */
#include "stm32f10x.h" // 这是STM32F10x系列的标准外设库头文件,包含了所有寄存器定义
#include "led.h"       // 我们自定义的LED驱动头文件

// 简单的延时函数(循环延时,不精确,仅用于示例)
void Delay_ms(uint32_t nCount) {
    for(; nCount != 0; nCount--) {
        for (volatile uint32_t i = 0; i < 8000; i++);
    }
}

int main(void) {
    LED_Init(); // 初始化LED GPIO

    while (1) {
        LED_Toggle(); // 翻转LED状态
        Delay_ms(500); // 延时约500ms
    }
    // 永远不会执行到这里
    return 0;
}

// 中断服务函数示例(如果需要)
void SysTick_Handler(void) {
    // 系统滴答定时器中断,可用于实现更精确的延时
}

2. 创建 led.h (位于 User/ 文件夹):

/* 文件:User/led.h */
#ifndef __LED_H
#define __LED_H

#include "stm32f10x.h"

void LED_Init(void);
void LED_On(void);
void LED_Off(void);
void LED_Toggle(void);

#endif /* __LED_H */

3. 创建 led.c (位于 User/ 文件夹):

/* 文件:User/led.c */
#include "led.h"

// 定义LED连接的GPIO端口和引脚
#define LED_GPIO_PORT    GPIOC
#define LED_GPIO_PIN     GPIO_Pin_13
#define LED_GPIO_CLK     RCC_APB2Periph_GPIOC

void LED_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;

    // 1. 开启GPIOC的时钟
    RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);

    // 2. 配置PC13为推挽输出,低速(驱动LED足够)
    GPIO_InitStructure.GPIO_Pin = LED_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; // 低速
    GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure);

    // 3. 初始状态:关闭LED (假设低电平点亮)
    LED_Off();
}

void LED_On(void) {
    // 根据你的硬件连接,可能是低电平点亮,也可能是高电平点亮
    // 假设LED阳极接3.3V,阴极接PC13,则PC13输出低电平时LED点亮
    GPIO_ResetBits(LED_GPIO_PORT, LED_GPIO_PIN);
}

void LED_Off(void) {
    GPIO_SetBits(LED_GPIO_PORT, LED_GPIO_PIN);
}

void LED_Toggle(void) {
    // 读取当前输出状态,然后取反
    if (GPIO_ReadOutputDataBit(LED_GPIO_PORT, LED_GPIO_PIN) == Bit_RESET) {
        LED_Off();
    } else {
        LED_On();
    }
}

4. 将 main.c led.c 添加到工程: 再次打开 Manage Project Items ,在 User 组下,添加 User/main.c User/led.c 文件。

3.4 关键工程配置:魔术棒设置

点击工具栏的 Options for Target (魔术棒图标)进行关键配置。

1. Target 选项卡:

  • Xtal (MHz) : 输入你的外部晶振频率,例如 8.0
  • Use MicroLIB 勾选 。这是一个针对嵌入式系统优化的简化C标准库,可以显著减少代码体积,建议勾选。

2. Output 选项卡:

  • Select Folder for Objects... : 选择输出文件夹,如 .\MDK-ARM\Obj
  • Name of Executable : 可执行文件名称,如 LED
  • Create HEX File 勾选 。生成用于下载到芯片的HEX文件。
  • Create Batch File : 可选,用于批处理编译。

3. Listing 选项卡:

  • 可以指定列表文件(如汇编列表)的输出目录。

4. User 选项卡:

  • 可以配置编译前/后执行的脚本命令。例如,在编译后自动生成BIN文件(后面会讲)。

5. C/C++ 选项卡:

  • Define : 定义全局宏。对于STM32标准外设库,通常需要添加 USE_STDPERIPH_DRIVER 。如果你的芯片是STM32F103C8T6,可能还需要 STM32F10X_MD (中容量)。具体宏定义需要参考你使用的库文件。
  • Include Paths 这是重中之重! 添加头文件搜索路径。点击末尾的 ... 按钮,添加以下路径(根据你的实际存放位置调整):
    • .\User (用于 led.h
    • .\Libraries\CMSIS (如果你放了CMSIS核心文件)
    • .\Libraries\STM32F10x_StdPeriph_Driver\inc (如果你使用了标准外设库)
    • 最重要的是,添加DFP中的设备头文件路径,例如: D:\Keil_v5\ARM\PACK\Keil\STM32F1xx_DFP\2.4.0\Device\Include

6. Debug 选项卡:

  • 选择你的调试器。例如,使用 ST-Link Debugger J-LINK / J-TRACE Cortex
  • 点击旁边的 Settings ,在 Debug 子选项卡中确认SWD接口和速度设置,在 Flash Download 子选项卡中确认已正确加载了对应芯片的Flash编程算法(通常安装DFP后会自动添加)。

7. Utilities 选项卡:

  • 设置编程算法,通常与Debug选项卡关联即可。

配置完成后,点击 OK 保存。

3.5 编译、下载与调试

  1. 编译: 点击工具栏的 Build (F7)或 Rebuild (全部重新编译)。下方的 Build Output 窗口会显示编译过程。如果一切配置正确,最后会显示 “LED” - 0 Error(s), 0 Warning(s)
  2. 下载: 将开发板通过ST-Link/J-Link连接电脑,并上电。点击 Load (F8)按钮,程序将被编译并下载到芯片的Flash中。看到 “Load “.\MDK-ARM\Obj\LED.axf” completed successfully.” 表示下载成功。
  3. 调试: 点击 Debug (Ctrl+F5)进入调试模式。你可以单步执行、设置断点、查看变量和寄存器。点击 Run (F5)全速运行,应该能看到LED开始闪烁。点击 Stop 退出调试模式。

恭喜!你已经完成了第一个Keil STM32工程的创建、编码、编译、下载和调试的全过程。

4. 进阶操作与高频问题详解

掌握了基本流程后,我们来看一些更具体和常见的问题。

4.1 如何生成Bin文件?

HEX文件是Intel格式的十六进制文件,包含地址信息。BIN文件是纯二进制镜像,体积更小,常用于批量生产或某些Bootloader。Keil默认不生成BIN文件,但可以通过配置用户命令实现。

  1. 打开 Options for Target -> User 选项卡。
  2. After Build/Rebuild 区域,勾选 Run #1
  3. 在命令输入框中,填入以下命令(路径根据你的Keil安装位置调整):
    D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o ./MDK-ARM/Obj/LED.bin ./MDK-ARM/Obj/LED.axf
    
    • D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe fromelf 工具路径,它随ARM编译器安装。
    • --bin : 指定输出格式为BIN。
    • -o ./MDK-ARM/Obj/LED.bin : 指定输出文件路径和名称。
    • ./MDK-ARM/Obj/LED.axf : 输入文件,即编译链接后生成的ELF格式文件。
  4. 重新编译工程后,在 MDK-ARM/Obj 文件夹下就能找到 LED.bin 文件。

4.2 如何将常量数据定位到Flash固定地址?

这在存储配置表、字体库等场景非常有用。这需要修改链接脚本( .sct 文件)和代码。

1. 修改链接脚本:

  • 编译一次工程后,会在 MDK-ARM 文件夹下生成一个 LED.sct 文件(Scatter-Loading Description File)。
  • 打开它,在 RW_IRAM1 区域后,可以添加一个自定义的只读数据区。例如,我们想在 0x0800F000 地址存放数据:
    LR_IROM1 0x08000000 0x00010000  {    ; 加载区域,起始地址0x08000000,大小64KB
      ER_IROM1 0x08000000 0x00010000  {  ; 执行区域,代码
       *.o (RESET, +First)
       *(InRoot$$Sections)
       .ANY (+RO)
      }
      RW_IRAM1 0x20000000 0x00005000  {  ; 内存区域
       .ANY (+RW +ZI)
      }
      MY_CONST_SECTION 0x0800F000 0x00001000 { ; 自定义区域,起始0x0800F000,大小4KB
        * (MyConst) ; 将所有放在 `MyConst` 段的数据放到这里
      }
    }
    

2. 在代码中定义变量并指定段:

/* 文件:User/flash_data.c */
#include “stm32f10x.h”

// 使用 __attribute__((section(“段名”))) 将常量数组定位到自定义段
const uint8_t my_config_table[1024] __attribute__((section(“MyConst”))) = {
    0x01, 0x02, 0x03, // ... 你的数据
};

// 在代码中访问
void read_config(void) {
    uint8_t value = my_config_table[0];
    // ...
}

3. 在工程配置中指定使用自定义链接脚本: Options for Target -> Linker 选项卡,取消勾选 Use Memory Layout from Target Dialog ,然后在 Scatter File 框中指定你修改后的 .sct 文件路径。 重新编译后,该常量数组就会被固定在Flash的 0x0800F000 地址。

4.3 如何删除Keil工程中的所有注释?

这是一个文本处理需求,Keil IDE本身没有一键删除所有注释的功能。但可以通过以下方法实现:

  • 使用外部工具: sed (Linux shell或Windows下的Git Bash/MSYS2)、 gcc -fpreprocess ,或编写Python/Perl脚本,使用正则表达式匹配并删除 // /* ... */ 注释。
  • 使用编辑器插件: 在VS Code、Notepad++等高级编辑器中打开工程文件,使用其查找替换功能(支持正则表达式)进行批量处理。
  • 注意: 直接删除注释可能破坏字符串内的 // /* ,操作前务必备份,并仔细检查结果。

4.4 ARM Compiler 5 (AC5) 与 ARM Compiler 6 (AC6) 如何选择?

Options for Target -> Target 选项卡的 ARM Compiler 下拉框中可以选择。

  • AC5: 传统编译器,成熟稳定,社区资源丰富,与旧版代码兼容性好。是大多数现有项目的选择。
  • AC6: 基于LLVM/Clang的新一代编译器,编译速度可能更快,生成的代码更优化,并且支持更新的C/C++标准(如C++14)。但某些为AC5编写的特定语法或内联汇编可能需要调整。
  • 建议: 新项目可以尝试AC6。维护旧项目或遇到兼容性问题时,使用AC5。切换后可能需要 Rebuild All

5. 常见编译与调试错误排查

遇到报错不要慌,大部分问题都有固定套路可循。

问题现象 可能原因 排查思路与解决方案
fatal error: ‘stm32f10x.h’ file not found 头文件路径未正确包含。 检查 Options for Target -> C/C++ -> Include Paths ,确保包含了芯片头文件所在目录。
undefined symbol SystemInit (referred from startup_stm32f10x.o). 启动文件调用了 SystemInit 函数,但用户未定义。 main.c 之前(如在 main 函数前)定义一个空的 SystemInit 函数,或从标准外设库中拷贝一个。
error: #5: cannot open source input file “core_cm3.h”: No such file or directory CMSIS核心头文件缺失或路径错误。 确保CMSIS文件已放入工程(通常来自DFP或库),并在 Include Paths 中添加其路径。
warning: #223-D: function “XXX” declared implicitly 函数在使用前未声明或未包含其头文件。 检查函数原型,确保包含了正确的头文件( .h )。
No ULINK2/3 Device found 调试器未连接、驱动未安装或型号选择错误。 1. 检查调试器USB连接和板子供电。
2. 检查设备管理器驱动状态。
3. 在 Debug 设置中选择正确的调试器型号。
Flash Download failed - “Cortex-M3” Flash编程算法未添加或芯片型号不匹配。 Debug -> Settings -> Flash Download 中,检查是否添加了正确的Flash算法(如 STM32F10x Med-density )。
程序下载后不运行 1. 启动模式(BOOT0/BOOT1)设置错误。
2. 复位电路或晶振有问题。
3. 中断向量表地址错误。
1. 确保BOOT0跳线帽接地(从主Flash启动)。
2. 检查硬件。
3. 检查 Options for Target -> Target IROM1 的起始地址是否为 0x08000000
代码大小超过限制 使用了评估版,代码超过32KB限制。 1. 优化代码,使用 -O1 / -O2 优化等级。
2. 勾选 Use MicroLIB
3. 移除不用的库函数。评估版限制是编译后的代码大小。

6. 工程管理与最佳实践

养成良好的工程管理习惯,能让团队协作和长期维护事半功倍。

  1. 清晰的目录结构: 如前所述,将用户代码、库文件、启动文件、文档、输出文件分门别类存放。避免所有文件堆在根目录。
  2. 使用版本控制: 务必使用Git等版本控制系统管理你的代码。将 User/ Libraries/ (如果是自己维护的库)、 Startup/ 以及工程文件( .uvprojx )纳入版本控制。将输出目录( MDK-ARM/ )和临时文件加入 .gitignore
  3. 合理管理库文件:
    • 方式一(推荐): 将官方库文件(如StdPeriph_Lib或HAL库)放在工程目录外的某个公共位置,所有工程通过相对路径或绝对路径引用。这样库文件只需一份,易于更新。
    • 方式二: 将库文件拷贝到每个工程的 Libraries/ 目录下。独立性好,但占用空间,更新麻烦。
    • 切忌: 不要直接修改库文件中的源代码,除非你非常确定自己在做什么。自定义修改应通过覆写函数或宏定义实现。
  4. 头文件包含规范:
    • .c 文件中,首先包含对应的 .h 文件,然后是其他必要的头文件。
    • 使用 #ifndef/#define/#endif 防止头文件被重复包含。
    • 头文件中只放声明(函数原型、外部变量声明、宏定义、类型定义),不放定义(函数体、变量初始化)。
  5. 编译优化选择: Options for Target -> C/C++ 选项卡中, Optimization 等级选择:
    • -O0 : 不优化,调试最方便,但代码体积大、速度慢。
    • -O1 : 适度优化,在代码大小、速度和调试性之间取得平衡, 推荐日常开发使用
    • -O2 / -O3 : 更激进优化,代码更小或更快,但可能影响调试(变量被优化掉)。
    • -Os : 优化代码大小。
  6. 调试技巧:
    • 熟练使用断点(F9)、单步(F11/F10)、运行到光标处(Ctrl+F10)。
    • 善用观察窗口(Watch)和内存窗口(Memory) 查看变量和内存数据。
    • 使用 printf 重定向到调试器(ITM)或串口进行日志输出 ,这是非常强大的调试手段。
  7. 备份与文档: 定期备份工程。在代码中使用注释记录关键逻辑、参数含义和修改历史。维护一个简单的 README.md 说明工程用途、硬件连接和编译环境。

从软件安装、芯片包管理,到工程创建、代码编写、配置调整,再到编译下载、调试排错,本文详细梳理了Keil MDK开发STM32的完整闭环流程。掌握这些基础操作和问题排查方法,你就能独立应对大部分开发场景。嵌入式开发重在实践,多动手、多思考、多查阅数据手册和官方文档,是提升能力的不二法门。希望这篇长文能成为你手边可靠的参考,助你在嵌入式开发的道路上走得更稳、更远。如果在实践中遇到新的问题,欢迎在评论区交流探讨。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值