Keil uVision3:嵌入式开发工具安装与实践指南

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Keil uVision3是一个全面的嵌入式开发环境,由ARM公司维护,适用于ARM和Cortex-M微控制器的程序开发。本指南涵盖安装步骤、关键知识点、以及如何使用其提供的各种工具,从项目管理到代码调试,确保开发者能够高效地进行嵌入式系统的设计与开发。 keil3安装包

1. Keil uVision IDE功能概述

Keil uVision 是一款功能强大的集成开发环境(IDE),专为基于ARM处理器的嵌入式系统设计。它集成了代码编辑、编译、链接、调试等功能,并提供了丰富的库和工具支持。开发者可以在uVision中进行高效的应用程序开发、程序编译、硬件调试等操作,使嵌入式软件开发过程更加简单和高效。本章将详细介绍uVision的功能,为后续章节的深入探讨打下基础。

graph LR
    A[Keil uVision IDE] --> B[编辑器]
    A --> C[编译器]
    A --> D[调试器]
    A --> E[项目管理器]
  • 编辑器 :提供代码编写、高亮显示、自动补全等方便编码的功能。
  • 编译器 :将源代码编译成可执行的二进制文件。
  • 调试器 :支持代码级调试,包括断点、单步执行、变量检查等。
  • 项目管理器 :管理工程配置、依赖关系以及整个软件开发流程。

uVision IDE通过集成这些组件,让开发者能够在单一界面上完成软件的开发、测试和调试,极大地提高了开发效率。随着本章的阅读,您将对Keil uVision有一个基础的认识,为后续章节的深入了解做好准备。

2. MDK-ARM工具集详解

2.1 MDK-ARM的架构和组成

2.1.1 工具链核心组件

MDK-ARM是ARM公司提供的针对ARM处理器的一套完整的软件开发工具集,它包含了一系列的软件组件,共同构成了强大的开发环境。这些组件包括ARM编译器、链接器、库以及调试器等,每个组件都有其独特的功能和作用。MDK-ARM工具集的核心组件可以细分为以下几个部分:

  • ARM Compiler : 这是MDK-ARM的核心编译器,它支持从C到汇编的高级语言代码转换,并且生成高效的ARM处理器代码。ARM Compiler基于ARM公司的优化技术,可以提供良好的代码性能和尺寸优化。
  • ARM Linker : 链接器负责将编译生成的目标文件(Object files)和库文件(Library files)合并成一个单一的可执行映像。ARM Linker提供了强大的内存映射和符号解析功能,使得开发者可以精确控制程序的加载和执行。
  • Runtime Libraries : 运行时库为应用程序提供了各种标准函数实现,如输入输出、数学运算等。这些库是高度优化的,能够在保持代码小尺寸的同时提供必要的功能支持。

2.1.2 扩展软件包和模块

除了核心组件外,MDK-ARM还提供了一系列的扩展软件包和模块,这些扩展能够增强开发者的开发能力,并且适应更多特定场景的需求。这些扩展包主要包括:

  • Middleware Components : 中间件组件提供了实现常见功能的库,如TCP/IP网络通信、USB设备协议栈、图形用户界面(GUI)等。使用这些库可以大幅减少开发时间,简化复杂功能的实现。
  • Device Family Packs (DFPs) : 设备族包为特定系列的ARM微控制器提供完整的支持。它包括芯片制造商提供的设备特定的外设配置、启动代码、以及必要时的驱动程序。
  • CMSIS (Cortex Microcontroller Software Interface Standard) : CMSIS是ARM提供的一个硬件抽象层,为处理器核心和外设提供统一的访问接口,使软件更加可移植。

通过这些核心组件和扩展软件包的结合使用,MDK-ARM能够为开发者提供一个功能强大、易于使用的开发平台,支持从简单的程序编写到复杂的系统开发的全过程。

2.2 MDK-ARM的软件开发环境

2.2.1 环境配置与安装要求

MDK-ARM软件开发环境的搭建需要遵循一定的步骤和条件,以确保软件运行的稳定性和效率。在安装MDK-ARM之前,必须确认系统满足以下安装要求:

  • 操作系统支持 : MDK-ARM支持Windows 7/10/11操作系统,对于Linux和macOS,需要使用虚拟机或双引导系统。
  • 硬件要求 : 推荐至少2GB的RAM和足够大的硬盘空间来存放编译生成的文件和中间文件。
  • 安装介质 : 官方下载的安装包包含了所有必要的安装程序和许可证管理工具。

安装步骤如下:

  1. 访问ARM官方下载页面,下载最新版本的MDK-ARM安装程序。
  2. 执行安装程序,接受软件许可协议。
  3. 选择安装路径和组件,包括MDK核心工具和扩展软件包。
  4. 完成安装并根据提示重启系统。

在安装过程中,MDK-ARM会自动配置系统的环境变量,以便可以在命令行中直接调用MDK-ARM的工具。安装完成后,建议运行安装程序中提供的验证工具,以确保MDK-ARM环境的正确配置。

2.2.2 集成开发环境的定制

MDK-ARM的集成开发环境(IDE)是非常灵活的,开发者可以根据个人喜好和项目需求进行定制。MDK-ARM IDE提供了丰富的插件机制、窗口布局和快捷键设置,使得开发者能够创建个性化的开发环境。

要定制MDK-ARM IDE,可以按照以下步骤操作:

  1. 界面布局调整 : 在IDE的主窗口中,可以拖动各个面板到理想的位置,也可以保存当前布局作为一个新的工作空间(workspace)。
  2. 快捷键设置 : 菜单“Options” -> “Key Configuration...”允许用户为常用的命令设置快捷键,提高开发效率。
  3. 插件安装 : 通过“Pack Installer”可以安装额外的插件,如代码格式化工具、版本控制工具等。
  4. 模板和代码片段 : 使用代码模板和代码片段(code snippets)可以快速插入常用的代码片段,提高编码效率。

通过上述定制,开发者可以打造一个适合自身项目和习惯的MDK-ARM开发环境,从而在开发过程中更加高效和舒适。

3. uVision项目管理设置

3.1 uVision项目文件结构

3.1.1 工程文件与配置文件

在 Keil uVision 中,项目文件结构是组织和管理项目的关键。uVision 项目文件通常包含工程文件(.uvproj)和配置文件(.uvopt)。工程文件是项目的核心,其中包含了项目的配置、设置和源文件引用。配置文件是可选的,但它能够保存调试配置、目标设置以及调试器的特定参数。

当创建一个新项目时,uVision 会引导你通过一个向导来创建一个工程文件。在向导中,你需要指定目标微控制器型号,并根据需要添加源文件。工程文件结构包括以下几个主要部分:

  • 目标配置 : 包含有关项目目标微控制器的配置信息。
  • 源文件组 : 列出了项目中的所有源代码文件(如 .c 和 .s)和头文件(如 .h)。
  • 编译器设置 : 包含编译器的设置选项,如编译警告级别、优化选项等。
  • 调试器设置 : 包括调试符号和调试器特定的配置,如内存映射和外设模拟。

3.1.2 源代码管理和版本控制

管理源代码是软件开发中至关重要的环节。uVision 支持集成版本控制系统,如 Git,让开发者可以轻松地追踪代码变更,并与团队成员协作。配置源代码管理系统后,可以执行以下操作:

  • 版本控制 : 在源代码文件中记录更改,允许开发者可以回退到之前的状态。
  • 分支管理 : 允许开发人员同时在不同的分支上工作,从而进行特性开发、修复问题等。
  • 合并 : 在完成特定任务或特性后,可以将分支合并回主代码库。

在实际操作中,可以在 uVision 的设置中启用版本控制集成,然后使用 Git 命令(如 git commit git push )来进行版本控制操作。

3.2 uVision的编译和调试配置

3.2.1 编译参数的设置

uVision IDE 提供了一个灵活的编译过程,允许用户根据项目的需要调整编译参数。编译参数可以在项目设置中进行配置,位于 "Options for Target" 对话框中的 "C/C++" 标签页。一些重要的编译参数包括:

  • 优化级别 : 可以选择从 -O0 (无优化)到 -O3 (高级优化)不等的级别。
  • 警告级别 : 设置编译器警告的严格程度,有助于识别代码中的潜在问题。
  • 包含目录 : 指定额外的头文件搜索路径。
  • 宏定义 : 允许在编译时定义宏。

示例代码块展示如何在 uVision 中设置编译参数:

# 示例:项目设置中的编译器选项
Compiler: C51 (8051 Compiler)  
Optimization: Level 3 (-O3)  
Include Paths: ../Libraries/Include  
Macro Definitions: BOARDNAME=MyBoard DEBUG=1  

3.2.2 调试会话的配置和启动

调试是 uVision 中不可或缺的一个步骤。配置和启动调试会话需要对调试器进行适当的设置,然后编译项目并启动调试器。调试设置可以在项目选项对话框的 "Debug" 标签页中找到,这包括但不限于以下设置:

  • 选择调试器 : 可以选择使用软件仿真器、ULink 仿真器或特定的硬件调试器。
  • 调试指令 : 设置调试器在启动时执行的初始化指令。
  • 运行到 : 设置在调试会话中启动时自动执行到的程序位置。

启动调试会话后,可以设置断点、单步执行代码、监视变量或观察内存。调试过程中的动态分析对于识别和修复软件中的问题至关重要。

# 示例:项目设置中的调试器选项
Debugger: Use: Simulator  
Run to: main()  

请注意,调试选项的配置应与开发团队的特定需求相匹配,以确保可以高效地诊断和解决问题。

4. C和汇编编程方法

4.1 C语言在uVision中的应用

4.1.1 C语言编程基础

C语言是嵌入式系统开发中广泛使用的编程语言之一,它结合了高级语言的便捷性和低级语言对硬件的控制能力。在Keil uVision环境中,C语言编程的基础涉及多个方面,包括基本语法、数据类型、控制结构、函数以及模块化编程等。编译器支持标准C语言的所有特性,同时还提供了针对嵌入式开发的扩展功能。

一个典型的C程序在uVision IDE中的基本结构包括:

  • 预处理指令(例如 #include #define 等);
  • 函数原型声明;
  • 全局变量和常量;
  • 主函数( main );
  • 自定义函数。

下面是一个简单的C语言程序示例,展示了如何编写一个“Hello World”程序:

#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

在Keil uVision中编写C语言程序时,推荐的实践包括:

  • 使用函数来组织代码,提高重用性;
  • 使用编译器特有的宏定义来控制特定平台的特性;
  • 保持代码的可移植性,尽量避免依赖于特定平台的特性。

4.1.2 C语言项目编程规范

在开发较为复杂的应用时,遵循一致的编程规范至关重要。这有助于提高代码的可读性和维护性,同时也可以减少潜在的bug。uVision支持的C语言项目编程规范通常包括以下几个方面:

  • 命名约定 :定义变量、函数和类型时采用一致的命名规则,如驼峰命名法或者下划线分隔法。
  • 注释规则 :确保代码有适当的注释,解释不易理解的部分或者复杂逻辑。
  • 代码格式化 :统一代码格式,比如括号的位置、缩进和空格使用。
  • 文件组织 :对源文件和头文件进行合理的分类与组织。

例如,一个命名约定的例子:

  • 变量命名: int employeeAge;
  • 函数命名: void printEmployeeDetails();
  • 宏定义: #define MAX_EMPLOYEES 100

在uVision项目中实施编程规范后,团队成员可以更高效地协作,同时也有利于后期的维护和升级。

4.2 汇编语言基础与应用

4.2.1 汇编语言的特点

汇编语言是一种低级编程语言,它几乎与机器语言直接相关,但是使用了人类可读的符号和单词来代替二进制代码。与C语言相比,汇编语言提供了对硬件更细粒度的控制。然而,编写汇编代码较为复杂且易于出错,所以通常只有在性能非常关键的场合才会使用汇编语言。

汇编语言的特点包括:

  • 直接控制硬件资源;
  • 执行速度快;
  • 程序大小通常较小;
  • 需要对目标处理器架构有深入的了解。

由于每个处理器架构都有自己独特的指令集,因此汇编语言是针对特定架构的。例如,ARM架构和x86架构的汇编语言就有很大的差异。

下面是一个ARM汇编语言的简单示例,演示了如何使用汇编来完成一个简单的算术操作:

    AREA RESET, DATA, READONLY
    ENTRY                           ; Mark the entry point for the linker

    LDR R0, =0x12345678             ; Load address of data into R0
    LDR R1, [R0]                    ; Load the value at address into R1
    ADD R2, R1, R1                  ; R2 = R1 + R1
    END                             ; Mark the end of the file

4.2.2 汇编语言项目实例

在实际项目中,使用汇编语言进行编程可能涉及到编写启动代码(Bootloader)或是优化关键路径上的性能。下面将通过一个汇编语言编写启动代码的实例,来展示汇编语言在项目中的应用。

    AREA RESET, CODE, READONLY       ; Define a code section named RESET
    ENTRY                           ; Entry point for the program

start
    LDR SP, =0x8000                  ; Initialize stack pointer
    BL main                          ; Call the main function

    ; Infinite loop to keep processor running
hang
    B hang                           ; Branch to hang (infinite loop)
    END                             ; Mark the end of the file

在这个例子中,我们定义了一个名为 RESET 的代码区域,设置了入口点,并初始化了堆栈指针。随后,调用了 main 函数来开始程序的执行。最后,一个无限循环用于防止处理器执行未定义的指令。

汇编语言虽然使用起来较为困难,但在嵌入式系统开发中,其性能和资源控制的优势使其成为不可或缺的一部分。尤其是在系统启动代码编写,或者对性能要求极高的关键算法优化场景中,汇编语言的应用依然十分广泛。

5. 编译和链接流程设置

5.1 编译过程详解

5.1.1 编译器的工作原理

编译器在软件开发中扮演着核心角色,其工作原理可以概括为以下几个步骤:

  1. 词法分析 :编译器首先将源代码分解成一系列的标记(tokens),这包括关键字、标识符、字面量等。
  2. 语法分析 :分析标记流,构建出抽象语法树(AST),这棵树反映了代码的语法结构。
  3. 语义分析 :编译器会检查AST是否有意义,例如类型检查和变量是否已声明。
  4. 中间代码生成 :将AST转化为中间代码,这是一种与机器无关的低级语言,通常是三地址代码。
  5. 优化 :中间代码经过优化,以提高程序的运行效率,例如死码消除、常量传播等。
  6. 目标代码生成 :最后,编译器将优化后的中间代码转化为机器代码或者汇编代码。

5.1.2 优化策略和编译警告

编译器优化是一个复杂的过程,包括但不限于以下几种策略:

  • 循环优化 :比如循环展开、循环不变式移除等,提高循环执行效率。
  • 函数内联 :将函数调用替换为函数体,减少函数调用开销。
  • 指令调度 :调整指令执行顺序以减少延迟和提高并行性。
  • 公共子表达式消除 :重用已经计算过的表达式结果,避免重复计算。

编译警告在编译过程中也非常重要。它们通常是编译器发现代码中可能存在的问题,但不足以导致编译失败的提示信息。例如,未使用的变量、可能的逻辑错误等。在优化过程中,编译器也会生成相应的警告,提醒开发者注意代码中的某些行为可能不符合预期。

5.2 链接过程详解

5.2.1 链接器的作用和配置

链接器的作用是将一个或多个编译后的目标文件(通常是.o或.obj文件)和库文件链接成一个单独的可执行文件或库。链接过程中,链接器执行如下主要任务:

  • 符号解析 :将源代码中引用的外部函数和变量与定义匹配起来。
  • 重定位 :为地址相关的代码和数据分配实际内存地址。
  • 符号决议 :解决重复符号,这可能会产生多重定义错误或多重引用错误。

链接器的配置通常通过链接脚本(Linker Script)或编译器的链接选项来指定。链接选项可以控制如何处理库文件(如静态库或动态库)、如何设置内存区域的布局等。

5.2.2 静态库与动态库链接

静态库和动态库是链接过程中需要考虑的两种不同类型的库:

  • 静态库 :在链接时,库中相关的函数和数据直接被拷贝到最终的可执行文件中。这意味着可执行文件会较大,但无需在运行时依赖库文件。
  • 动态库 :函数和数据保留在库文件中,在运行时由系统加载。这使得可执行文件较小,但需要确保所需的动态库在运行时可用。

了解这两种链接方式对于控制程序的大小和性能至关重要。选择静态链接还是动态链接依赖于特定的应用需求和环境。

graph LR
A[开始编译过程] --> B[词法分析]
B --> C[语法分析]
C --> D[语义分析]
D --> E[中间代码生成]
E --> F[优化]
F --> G[目标代码生成]
G --> H[链接过程]
H --> I[符号解析]
I --> J[重定位]
J --> K[符号决议]
K --> L[链接静态库或动态库]
L --> M[生成可执行文件或库]
flowchart TD
    classDef default fill:#f9f,stroke:#333,stroke-width:4px;
    classDef link fill:#ccf,stroke:#f66,stroke-width:2px;

    A[/词法分析/] -->|产生| B[标记流]
    B --> C[/语法分析/]
    C -->|构建| D[抽象语法树]
    D --> E[/语义分析/]
    E -->|检查并转换| F[中间代码]
    F --> G[/优化/]
    G -->|改进性能| H[优化后的中间代码]
    H --> I[/目标代码生成/]
    I -->|转化为| J[机器或汇编代码]

    subgraph 编译过程
    A
    B
    C
    D
    E
    F
    G
    H
    I
    end

    J --> K[/链接过程/]
    K --> L[符号解析]
    L --> M[重定位]
    M --> N[符号决议]
    N --> O[/静态库与动态库链接/]
    O --> P[生成可执行文件]

    subgraph 链接过程
    K
    L
    M
    N
    O
    end

    class A default;
    class B default;
    class C default;
    class D default;
    class E default;
    class F default;
    class G default;
    class H default;
    class I default;
    class J default;
    class K link;
    class L link;
    class M link;
    class N link;
    class O link;
    class P link;

在本章节中,我们深入探讨了编译和链接流程的设置。对编译器的工作原理有了清晰的了解,以及如何通过各种优化策略来改进性能,同时对链接过程中的关键步骤进行了详细的解释。接下来,我们将继续深入探讨如何在特定的开发环境中有效地使用这些知识。

6. uVision内置调试器使用

6.1 调试器的基本操作

6.1.1 断点设置和单步执行

在软件开发过程中,断点是调试器中一个非常有用的功能,它允许开发人员在程序执行到断点时暂停,以便检查程序状态、内存内容或变量值。在 Keil uVision 中使用断点,开发者可以单击代码行号旁边的边缘区域来设置或清除断点。当程序执行到该行时,它会自动暂停,这允许程序员逐步执行代码(单步执行)、观察变量或内存值的变化。

graph TD
    A[开始调试] --> B[程序执行]
    B --> C{是否遇到断点?}
    C -->|是| D[暂停执行]
    C -->|否| E[继续执行]
    D --> F[单步执行]
    F -->|步入| G[进入函数内部]
    F -->|跳过| H[越过函数执行]
    F -->|跳至| I[执行到指定行]

在 uVision 中,单步执行有三种模式:

  • 步入(Step Into):当执行到一个函数调用时,步入将进入函数内部执行。
  • 跳过(Step Over):跳过当前行的函数调用,仅执行调用该函数的代码行。
  • 跳至(Step Out):继续执行直到当前函数返回。

6.1.2 变量监视和内存查看

调试过程中,观察变量的值是判断程序状态是否正常的关键。在 uVision 的调试器窗口,可以添加变量监视列表,监视特定变量的变化。此外,开发者还可以查看内存地址的内容,这在调试低级代码或者解决指针问题时非常有帮助。

int main() {
    int a = 10;
    int b = 20;
    int sum = a + b;
    // 在此处可以设置变量监视:变量 "sum"
    // 在此处可以查看内存地址:变量 "sum" 的地址
    return 0;
}

在实际操作中,为了查看变量 sum 的值,可以右键点击变量,选择 "Add to Watch"。若要查看内存地址,可以使用 "Memory" 窗口,输入内存地址即可查看相应内存中的内容。

6.2 高级调试技巧

6.2.1 性能分析和资源监测

性能分析是确定程序性能瓶颈的重要手段。在 uVision 中,性能分析工具可以记录程序运行期间各种函数调用的次数和耗时,帮助开发者发现效率低下的代码段。

资源监测则关注程序在执行期间对处理器、内存、I/O 等资源的使用情况。Keil uVision 提供的资源监测工具可以实时显示这些资源的使用情况,从而允许开发者在不改变程序逻辑的前提下进行优化。

6.2.2 调试脚本和自动化测试

为了提高调试效率,uVision 允许用户编写和运行调试脚本,这些脚本可以自动执行一系列调试操作,例如设置断点、单步执行、变量监视等。通过脚本自动化常见的调试任务,开发者可以节省大量时间,特别是在进行回归测试时。

# 示例调试脚本
import pyuvis
pyuvis.debugger.start()

# 设置断点
pyuvis.debugger.set_breakpoint('main.c', 12)

# 运行到断点
pyuvis.debugger.run_to_breakpoint()

# 获取变量的值
variable_value = pyuvis.debugger.get_variable('sum')

# 检查变量的值是否符合预期
if variable_value != 30:
    print('Unexpected value')
else:
    print('Variable check passed')

pyuvis.debugger.stop()

上述 Python 示例脚本演示了如何在使用 Keil uVision 的 Python 调试接口(假设存在,当前版本可能需要适配)进行自动化调试。这包括启动调试器、设置断点、运行至断点、获取变量值并进行检查,最后停止调试器的整个过程。这种自动化能力对于测试和调试大型项目尤为关键。

使用这些高级调试技巧,开发者可以在复杂的应用程序中有效地定位和解决问题,大幅度提高开发效率和程序质量。

7. 模拟器和仿真功能

模拟器和仿真在嵌入式开发中扮演着重要角色,它们允许开发者在没有实际硬件的情况下测试和验证他们的软件。利用模拟器,可以在软件层面上评估整个系统的性能和功能,而无需担心硬件的限制。

7.1 模拟器的使用方法

7.1.1 模拟器的启动与配置

模拟器启动首先需要选择合适的模拟器配置文件。通常,这个配置文件包含了目标硬件的模拟信息,比如处理器类型、内存大小、外设配置等。在 Keil uVision 中,启动模拟器的步骤如下:

  1. 打开 uVision IDE。
  2. 加载一个项目,该项目包含了需要模拟的目标设备配置。
  3. 点击工具栏上的“Start/Stop Debug Session”按钮或使用快捷键 Ctrl+F5 启动模拟器。
  4. 在出现的“Select Startup”对话框中选择合适的模拟器配置文件。

7.1.2 外设模拟与性能评估

模拟器能够模拟各种外设,比如串口、定时器、ADC、DAC等。利用这些模拟外设,开发者可以测试程序对这些外设的操作。通过模拟器,性能评估也是可能的,因为你可以观察CPU利用率、内存使用情况等关键性能指标。

7.2 仿真在开发中的应用

7.2.1 仿真模型的构建

为了进行有效的仿真测试,构建准确的仿真模型是关键。这包括对处理器、内存、以及所有相关的外设进行建模。在 Keil uVision 中,开发者可以使用其自带的模型或者自定义模型。

7.2.2 仿真实验的设计与分析

仿真实验设计需要明确实验目标、测试条件、预期结果等。执行仿真实验时,开发者需要观察程序行为,并记录关键数据,比如执行时间、内存占用等。实验结果的分析对于优化和调整软件行为至关重要。

仿真实验的一个简单例子是测试一个数据处理算法的性能。开发者可以记录算法执行前后的时间戳,并计算出执行时间。然后,可以通过改变数据集的大小来评估算法的可扩展性。

// 示例代码片段,用于计算执行时间
#include <stdio.h>

int main() {
    clock_t start, end;
    double cpu_time_used;

    start = clock();
    // 执行数据处理算法
    // ...

    end = clock();
    cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
    printf("Algorithm took %f seconds to execute\n", cpu_time_used);

    return 0;
}

在上述代码中,使用 clock() 函数来记录时间戳,并计算执行算法所需的时间。这样的实验可以多次进行,以获取算法性能的平均值和变异性分析。

通过这些仿真实验,开发者可以对软件行为有更深入的理解,并据此进行必要的调整和优化,确保在实际硬件上的运行效果达到预期目标。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Keil uVision3是一个全面的嵌入式开发环境,由ARM公司维护,适用于ARM和Cortex-M微控制器的程序开发。本指南涵盖安装步骤、关键知识点、以及如何使用其提供的各种工具,从项目管理到代码调试,确保开发者能够高效地进行嵌入式系统的设计与开发。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值