简介:本资源面向FPGA与ARM协同开发工程师,聚焦USB3.0高速数据传输场景下的软硬件联调实践,解决USB3014芯片在嵌入式系统中稳定接入、大流量数据上传与跨时钟域可靠交互等核心问题。压缩包共11个文件(71KB),含8个Verilog源码(实现AXI4总线双存储器+BRAM接口、跨时钟域同步、FIFO控制及USB从机逻辑)、1个ARM可烧写img镜像文件(100MB,已实测通过)、1个IP配置XML及1个Tcl脚本,结构完整、即拿即用。已有265人学习下载,所有模块均经实际板级验证,支持连续读写与高速数据上传,实测上传速率高达392MB/s;读者可直接复用FPGA端IP核、快速部署ARM侧固件,并参考多时钟域协同设计范式优化自身USB高速外设项目。
1. 项目概述:当USB3014遇上FPGA与ARM
最近在折腾一个数据采集板卡,核心是赛普拉斯的USB3014这颗高性能USB 3.0集线器控制器。我的任务是把FPGA采集到的海量数据,通过USB3014稳定地送到上位机,同时还得搞定ARM核心的固件烧写与更新。这听起来像是两个独立的任务——FPGA侧的接口调试和ARM侧的嵌入式开发,但实际上,它们被USB3014这颗芯片紧密地耦合在了一起。如果你也在做类似的高速数据通道设计,或者正在为如何协调FPGA的逻辑时序与ARM的固件管理而头疼,那这次踩坑和填坑的经历,或许能给你一些直接的参考。
USB3014远不止是一个简单的USB HUB。它内置了一个ARM9核心,这意味着你可以在上面跑自己的嵌入式程序,去灵活配置下游的USB端口、管理数据流,甚至实现一些协议转换。而FPGA这边,则需要通过并行或FIFO接口与USB3014通信,把数据“灌”进去。所以,整个项目的核心就变成了两部分:一是编写和调试FPGA与USB3014通信的硬件描述语言(HDL)代码,确保数据能正确、高速地写入USB3014的缓冲区;二是为USB3014内部的ARM核心编译、生成并烧写固件镜像文件,让这个“智能集线器”能按照我们的意愿工作。这个过程涉及FPGA开发、嵌入式交叉编译、USB协议栈理解以及调试手段的综合运用,任何一个环节卡住,整个数据流就断了。
2. 核心需求与方案选型解析
2.1 为什么是USB3014?项目需求深度拆解
选择USB3014,根本上是源于项目对带宽、灵活性和集成度的三重苛刻要求。我们需要将FPGA预处理后的图像数据(每秒数百MB)实时上传到PC。普通的USB-UART或USB-FIFO芯片(如FTDI系列)带宽吃紧,而原生USB 3.0 PHY的FPGA方案成本高、开发周期长。USB3014提供了一个折中且强大的方案:它自带USB 3.0物理层和链路层,提供高达5Gbps的理论带宽;其内置的ARM核心和可编程数据通路(GPIF II接口)允许我们深度定制数据流控制逻辑,实现类似DMA的块传输,极大减轻FPGA侧的控制逻辑负担。
具体到本项目的需求:
- 高速数据流:FPGA需通过16位或32位并行接口,以最高100MHz以上的时钟速率,将数据持续写入USB3014。
- 可靠的控制通道:除了数据流,还需要一个低速、可靠的控制通道,用于PC向FPGA发送采集参数(如曝光时间、增益),或读取FPGA的状态寄存器。这个通道通常通过USB3014的ARM核心转发实现。
- 固件可更新性:产品出厂后,可能需要修复BUG或升级功能,因此必须设计一套可靠的固件烧写与更新机制,最好能通过USB口完成,无需拆机。
- 开发便捷性:需要成熟的开发套件(如CYUSB3KIT-003)和软件工具链(SDK、编译器)支持,以加速调试进程。
基于这些需求,方案的核心构成如下:FPGA作为数据生产者,通过GPIF II接口与USB3014对接;USB3014内部的ARM核心运行定制固件,管理数据从GPIF II接口到USB 3.0主机端的搬运;PC端则使用赛普拉斯提供的CyAPI或libusb库编写应用程序,进行数据接收和控制命令下发。
2.2 FPGA接口方案:GPIF II vs. Slave FIFO的选择考量
USB3014与FPGA的物理接口主要支持两种模式:GPIF II(通用可编程接口)和Slave FIFO。这两种模式的选择,直接决定了FPGA侧代码的复杂度和数据吞吐效率。
Slave FIFO模式:这种模式下,USB3014作为“从设备”,其内部缓冲区对FPGA呈现为一个或多个FIFO。FPGA像操作普通异步FIFO一样,通过简单的读写使能、满空标志信号来控制数据传输。它的优点是接口简单,时序容易控制,非常适合FPGA初学者或对吞吐率要求不是极端高的场景(例如,持续传输速率在200MB/s以下)。赛普拉斯提供的SDK中也有对应的Slave FIFO示例工程,可以快速上手。
GPIF II模式:这种模式下,USB3014的接口更像一个可编程的状态机。FPGA工程师需要根据赛普拉斯提供的“GPIF II Designer”工具生成的接口时序,编写与之严格匹配的HDL代码。GPIF II模式能实现更复杂的数据流控制(如突发传输、握手信号更灵活),理论上可以榨干USB 3.0的带宽,达到更高的传输效率。但代价是开发难度大,时序调试复杂,需要深入理解GPIF II的状态转换图。
实操心得:对于大多数首次接触USB3014的工程师,我强烈建议从Slave FIFO模式开始。先让整个数据链路跑通,验证硬件设计和基础功能。在Slave FIFO稳定后,如果确实遇到带宽瓶颈,再考虑迁移到GPIF II模式进行优化。不要一开始就追求极限性能而陷入复杂的时序调试泥潭。我的项目初期就采用了Slave FIFO模式,用32位接口、100MHz时钟,实测稳定传输速率超过320MB/s,已满足大部分高速采集需求。
3. FPGA侧代码设计与调试要点
3.1 接口时序设计与代码实现
在Slave FIFO模式下,FPGA侧的代码核心是一个状态机,用于响应USB3014的FIFO控制信号。关键信号通常包括:
FLAGA, FLAGB, FLAGC:这些是USB3014输出的FIFO状态标志(如可编程的空、满、半满等)。SLCS(片选)、SLOE(输出使能)、SLRD(读使能)、SLWR(写使能):控制信号。FD[31:0]:32位双向数据总线。PKTEND:包结束信号,用于手动提交一个USB数据包。
我的代码结构主要分为两个模块:
- 数据写入状态机:持续监控
FLAGA(这里配置为“FIFO未满”标志)。当FPGA内部数据缓存(例如一个大的RAM或另一个FIFO)有数据,且FLAGA为高时,状态机进入写数据状态,拉低SLWR信号,将数据放到FD总线上。这里要特别注意SLWR的建立和保持时间,必须严格遵循数据手册的时序要求。 - 控制命令解析模块:USB3014也可以向FPGA发送数据(如下发控制命令)。这需要FPGA监控
SLOE和SLRD信号,当它们有效时,从FD总线上读取数据。读取的数据可以解析为寄存器地址和写入值,用于配置FPGA内部的采集参数。
// 简化的Slave FIFO写数据状态机片段(Verilog示例) localparam IDLE = 2‘b00, WRITE = 2’b01; reg [1:0] state; reg [31:0] data_to_send; wire fifo_not_full = FLAGA; // 假设FLAGA配置为未满标志 always @(posedge ifclk or negedge rst_n) begin if (!rst_n) begin state <= IDLE; SLWR <= 1‘b1; end else begin case(state) IDLE: begin if (internal_fifo_has_data && fifo_not_full) begin data_to_send <= internal_fifo_data; state <= WRITE; end end WRITE: begin SLWR <= 1’b0; // 拉低写使能 FD <= data_to_send; // 输出数据 // 保持一个时钟周期后,回到IDLE state <= IDLE; SLWR <= 1‘b1; end endcase end end注意:上述代码是高度简化的概念模型。实际应用中,必须根据你所使用的FPGA型号和USB3014的Slave FIFO接口时序图,添加精确的时钟延迟控制,以满足
SLWR相对于数据的建立/保持时间。通常需要仔细调整FD数据输出的时机。
3.2 仿真与上板调试的关键步骤
代码写完后,仿真和调试是确保成功的关键。
仿真阶段:
- 创建仿真模型:赛普拉斯SDK中通常不提供USB3014的HDL仿真模型。一个实用的方法是创建一个“行为级”仿真模型。这个模型模拟USB3014 FIFO的行为:当收到
SLWR有效信号时,将FD上的数据存入一个数组;同时根据数组的填充程度,模拟FLAGA(满标志)的变化。 - 编写测试激励:在仿真中,让FPGA模块模拟产生连续的数据(如递增计数器),并观察其写入时序是否正确。同时,可以模拟USB3014发送控制命令,测试FPGA的解析逻辑。
- 使用SignalTap II/ChipScope:对于Altera/Intel或Xilinx的FPGA,一定要在代码中预先埋入SignalTap或ChipScope逻辑分析仪核。这是上板调试最强大的武器。你需要抓取所有与USB3014接口相关的信号:
FLAGA、SLWR、FD、SLCS等。通过对比抓取的波形和数据手册的时序图,可以精准定位是建立时间违规、还是状态机跳转错误。
上板调试阶段:
- 先静态后动态:首先确保电源、时钟、复位等基础信号正常。用示波器测量提供给USB3014的
IFCLK(接口时钟)是否稳定、幅值正确。 - 连接性测试:使用赛普拉斯提供的“Control Center”软件。在ARM固件正确运行的前提下,你可以在Control Center中看到USB3014设备。通过其提供的“FIFO”标签页,可以手动进行Loopback测试:向一个FIFO端点写入数据,再从另一个端点读出,验证FPGA与USB3014之间的物理连接和基础通信是否正常。
- 协同调试:让FPGA开始发送特定的数据模式(例如,每包数据的前4个字节是包计数器)。在PC端用简单的CyAPI程序接收数据,检查数据是否连续、有无丢失。如果发现丢数,立刻触发SignalTap,抓取丢数瞬间的波形,重点看
FLAGA是否在数据写入期间突然变低(表示FIFO满),以及FPGA的状态机是否因此异常。
4. 嵌入式侧:ARM固件开发与烧写文件生成
4.1 开发环境搭建与固件框架
USB3014的ARM核心开发,通常使用赛普拉斯提供的EZ-USB FX3 SDK。它基于一个名为“Framework”的实时操作系统(RTOS)精简内核,提供了USB协议栈、GPIF II驱动、DMA管理器等核心组件。
环境搭建步骤:
- 安装工具链:下载并安装FX3 SDK。它内部集成了ARM的交叉编译工具链(通常是GCC for ARM)。
- 导入示例工程:SDK中包含了Slave FIFO和GPIF II的完整示例工程。以
slavefifo示例为起点是最佳选择。这个工程位于SDK安装路径\1.3\firmware\slavefifo_example。 - 理解工程结构:
cyfxslffifo.c/.h:这是主应用文件,包含了main函数和最重要的CyFxApplicationDefine函数。你需要在这里初始化USB描述符、配置端点(Endpoint)、启动DMA通道,并连接GPIF II接口到这些DMA通道。cyfx_gpif2config.h:GPIF II接口的配置头文件。即使使用Slave FIFO模式,也需要这个文件来定义引脚映射和基础状态机。你可以使用“GPIF II Designer”工具图形化配置后生成此文件,但对于Slave FIFO,直接使用示例工程中的文件即可。cyfxtx.c:RTOS的底层移植和启动代码,一般无需修改。
固件初始化流程解析: 在CyFxApplicationDefine函数中,固件按以下顺序初始化:
- GPIF II初始化:调用
CyFxGpif2Init,加载cyfx_gpif2config.h中的配置,将USB3014的IO引脚配置为Slave FIFO模式。 - USB描述符设置:定义设备的VID/PID、厂商字符串、配置描述符、接口描述符和端点描述符。对于高速数据流,你需要配置一个或多个BULK IN端点(例如EP6)。
- DMA通道创建与绑定:这是数据流的核心。创建一个从GPIF II接口(
CY_FX_EP_PRODUCER)到USB IN端点(CY_FX_EP_CONSUMER)的DMA通道。固件中的DMA管理器会自动处理数据从FPGA接口到USB端点的搬运,无需CPU干预。 - 启动DMA:初始化完成后,启动DMA通道。此后,只要FPGA开始向FIFO写数据,DMA就会自动将其打包成USB数据包发送给主机。
4.2 编译、链接与生成烧写文件
固件代码修改完成后,需要在SDK提供的Eclipse环境中进行编译。
- 编译:确保项目配置正确,选择正确的编译目标(通常是
Debug或Release)。编译成功后,会在项目目录下生成一个.elf文件,这是ARM核心的可执行文件。 - 生成镜像文件:USB3014上电时,需要从外部I2C EEPROM或通过USB从主机加载固件。因此,我们需要将
.elf文件转换成特定的二进制格式。SDK提供了一个名为elf2img的工具(位于SDK安装路径\util\elf2img\bin)。- 打开命令提示符,导航到该目录。
- 执行命令:
elf2img.exe -i .\你的固件.elf -o .\fw.img - 这条命令会生成一个
fw.img文件,这就是最终的固件烧写镜像。-i指定输入,-o指定输出。
- 镜像文件格式解析:生成的
.img文件并非纯粹的二进制代码。它的开头包含一个固件头(Firmware Header),其中包含了固件长度、CRC校验、入口地址等信息。USB3014的BootROM会解析这个头,然后将后续的代码段和数据段加载到内部SRAM中执行。
4.3 固件烧写方法与生产流程考虑
有了fw.img文件,接下来就是将其“灌入”USB3014。根据产品阶段,有几种不同的烧写方式:
1. 开发调试阶段:通过USB Bootloader烧写这是最常用的方式。USB3014芯片内部有一个ROM Bootloader,当检测到外部EEPROM为空或无效时,它会进入“USB Boot”模式,等待主机通过USB发送固件。
- 操作步骤: a. 确保板卡上电,且未连接有效的EEPROM。 b. 使用USB线连接板卡和PC。 c. 打开赛普拉斯“Control Center”软件。 d. 软件会识别到一个“Cypress EZ-USB FX3 BootLoader”设备。 e. 在Control Center中,选择“Program” -> “FX3”,然后选择你生成的
fw.img文件。 f. 点击“Program”,软件会将固件通过USB下载到USB3014的内部RAM并直接运行。注意:这种方式是易失性的,断电后固件丢失。
2. 小批量生产/原型固化:烧写外部EEPROM为了断电后固件不丢失,需要将固件烧录到板载的I2C EEPROM(通常是24LCxx系列)中。
- 操作步骤: a. 在Control Center中,对已运行固件的设备,选择“Program” -> “EEPROM”。 b. 加载
fw.img文件,设置EEPROM的I2C地址(通常为0x50)。 c. 点击“Program”,Control Center会通过USB3014作为桥梁,将固件写入连接的EEPROM。 d. 断电重启后,USB3014的BootROM会自动从EEPROM中读取并加载固件。
3. 量产阶段:考虑更高效的方案对于大批量生产,通过USB口一个个烧写效率太低。
- 方案一:使用EEPROM编程器:在贴片前,先用专用的EEPROM编程器将
fw.img文件烧录到空白EEPROM芯片中,然后再将已编程的EEPROM贴装到板卡上。这是最主流的方式。 - 方案二:使用USB3014的“量产模式”:赛普拉斯SDK提供了一种“量产镜像”生成工具,可以生成一个包含VID/PID等定制信息的特殊镜像。结合特定的硬件设计(如上拉/下拉电阻配置启动模式),可以实现更灵活的生产流程,但复杂度较高。
实操心得:在开发初期,我强烈建议在板子上预留一个EEPROM烧录插座(如SOIC-8的夹子座)和一个用于强制进入Bootloader模式的测试点(例如,将USB3014的
I2C_SCL引脚通过电阻上拉,在启动时拉低该引脚可强制进入USB Boot模式)。这能极大方便调试和生产切换。我曾遇到固件有BUG导致设备“变砖”(无法被识别),就是通过强制进入Bootloader模式重新烧写才救活的。
5. 系统联调与性能优化实战
5.1 PC端应用程序与数据接收
FPGA和ARM固件都准备好后,最后一步是在PC上编写应用程序来接收数据。赛普拉斯提供了CyAPI(C++)和Python绑定(pyUSB后端)等多种库。
一个简单的C++数据接收循环核心代码如下:
#include <cyusb.h> // ... 初始化CyUSB库,打开设备,找到BULK IN端点(例如0x86)... CCyUSBEndPoint *InEndPt = ...; // 获取端点指针 LONG bytesToRead = 1024 * 1024; // 每次读取1MB PUCHAR buffer = new UCHAR[bytesToRead]; LONG bytesRead = 0; OVERLAPPED ov; ov.hEvent = CreateEvent(NULL, false, false, NULL); while (bStreaming) { // 提交异步读请求 InEndPt->BeginDataXfer(buffer, bytesToRead, &ov); // ... 可以在这里处理其他事务 // 等待传输完成 bool success = InEndPt->WaitForXfer(&ov, 1000); // 超时1秒 if (success) { InEndPt->FinishDataXfer(buffer, bytesToRead, &ov, bytesRead); // 处理接收到的数据,bytesRead为实际读到的字节数 processData(buffer, bytesRead); // 重新提交异步请求,形成连续流 InEndPt->BeginDataXfer(buffer, bytesToRead, &ov); } else { // 超时或错误处理 CancelIo(...); ResetEvent(ov.hEvent); // 可能需要重新初始化端点 } }关键点:一定要使用异步传输(Overlapped I/O)并配合多缓冲区轮转。同步读取会因等待USB数据而阻塞主线程,极易导致数据丢失。上述代码展示了“提交请求-等待完成-处理数据-提交新请求”的典型流水线操作,能最大化吞吐率。
5.2 性能瓶颈分析与优化策略
当系统跑通后,你可能会发现实际带宽远低于理论值。这时就需要进行系统级的性能瓶颈分析。
常见瓶颈点及排查顺序:
- FPGA时钟与数据连续性:用逻辑分析仪确认FPGA提供给USB3014的接口时钟(IFCLK)是否稳定,数据总线(FD)是否在每个有效时钟沿都有数据写入。如果FPGA内部数据处理跟不上,会导致写入断流,USB带宽利用率自然上不去。
- USB3014固件DMA配置:检查固件中创建的DMA通道大小(
dmaSize)。这个值定义了每次DMA搬运的数据量。设置过小会增加CPU中断频率,降低效率;设置过大会增加传输延迟。通常设置为16KB或32KB是一个不错的起点。同时,确保创建了足够多的DMA缓冲区(dmaCount),例如8个或16个,以实现乒乓操作。 - PC端接收程序:这是非常常见的瓶颈。确保使用异步I/O,并且数据处理线程(
processData函数)的耗时必须小于USB传输一缓冲区数据的时间。如果处理太慢,缓冲区会被迅速填满,导致后续传输阻塞。如果处理不过来,考虑将数据直接存入内存队列,由另一个线程进行后续处理(如存盘、显示)。 - USB传输参数:在PC端,尝试调整每次读取的数据包大小(
bytesToRead)。理论上,越大越好,但会受到USB主机控制器驱动限制。可以尝试64KB、128KB、256KB等不同值进行测试。 - 系统环境:关闭PC的USB选择性暂停设置(在电源管理选项中),使用USB 3.0(蓝色)端口,并确保主板芯片组驱动已安装。
优化案例:在我的项目中,初始速率只有约120MB/s。经过排查,发现瓶颈在PC端:我使用了同步读取,且数据处理(简单的格式转换)在主线程进行。改为异步I/O并将数据处理移到独立工作线程后,速率提升到280MB/s。随后,我将FPGA的写入时钟从80MHz提升到100MHz,并优化了FPGA内部FIFO的“几乎满”阈值,减少等待,最终速率稳定在320MB/s左右。
6. 开发中的典型问题与排查实录
6.1 设备枚举失败与固件加载问题
这是最令人头疼的起步问题。插上USB线,电脑没反应,或者只识别为“未知设备”。
- 问题现象:设备管理器中出现“Cypress EZ-USB FX3 BootLoader”或“Unknown Device”。
- 排查步骤:
- 检查硬件:首先用万用表测量USB3014的VDD、VDDIO、复位信号是否正常。用示波器检查24MHz主晶振是否起振。
- 检查启动配置:USB3014通过
I2C_SCL和I2C_SDA引脚的上拉/下拉状态决定启动方式。请对照数据手册的“Boot Options”章节,确认你的硬件电路配置与预期一致(例如,是否为了进入USB Boot模式而正确配置了)。 - 检查固件镜像:使用
hexdump或二进制查看工具检查生成的fw.img文件,确认文件头是否正常(通常开头是CY等魔术字)。尝试用SDK中未经修改的示例工程固件进行烧写,以排除自己代码的问题。 - 使用Cypress SuiteUSB Control Center:这是官方调试神器。如果设备能被识别为BootLoader,就通过它烧写固件。如果烧写失败,注意看错误信息。常见的错误是“CRC mismatch”或“File format error”,这通常意味着镜像文件损坏或生成工具版本不匹配。
6.2 数据传输不稳定、丢包或CRC错误
系统能识别,也能传输数据,但传输一段时间后出错、停止,或PC端收到大量CRC错误的数据包。
- 问题现象:传输中途停止,PC端软件报超时错误;或接收到的数据中夹杂乱码,校验和不通过。
- 排查步骤:
- 电源完整性:这是高速数字电路的头号杀手。用示波器探头(带宽至少500MHz)的尖端接地弹簧,测量USB3014核心电源(如1.2V)和IO电源(如3.3V)上的噪声。噪声峰峰值应小于电源电压的5%。如果噪声过大,检查电源滤波电容(特别是高频去耦电容)的布局和焊接。
- 信号完整性:重点检查FPGA与USB3014之间的高速并行总线。确保布线等长,阻抗匹配。使用示波器测量数据线(如
FD[0])和时钟线(IFCLK)的波形。观察是否有严重的过冲、振铃或边沿退化。眼图闭合是导致间歇性错误的常见原因。 - 时序违例:回到FPGA的时序约束和逻辑分析仪抓取的波形。确认
SLWR/SLRD等控制信号相对于IFCLK和数据信号的建立保持时间是否满足。在FPGA开发工具中,仔细检查时序报告(Timing Report),看是否有红色的“Setup Hold”违规。 - 软件流控:确认FPGA是否正确地响应了
FLAGA(满标志)。如果FPGA无视FLAGA为低(FIFO满)仍强行写入,会导致数据被覆盖。同样,PC端应用程序读取数据的速度必须跟上FPGA发送的速度,否则USB3014内部的缓冲区会溢出。
6.3 调试工具与技巧汇总
工欲善其事,必先利其器。以下是我在调试过程中认为最有效的工具组合:
| 工具/手段 | 用途 | 关键技巧 |
|---|---|---|
| Cypress Control Center | 基础设备管理、EEPROM/内存读写、端点测试、固件烧写。 | 善用“Debug”标签页查看芯片内部寄存器和端点状态,用于验证USB枚举和端点配置是否正确。 |
| 逻辑分析仪 (如Saleae) | 抓取FPGA与USB3014之间的并行总线时序。 | 设置足够的采样深度(如100M samples),同时抓取时钟、使能、标志位和关键数据线,触发条件设置为“当FLAGA变低时”,用于捕捉FIFO满的瞬间。 |
| FPGA片内逻辑分析仪 (SignalTap/ChipScope) | 实时抓取FPGA内部信号,精度最高。 | 在关键状态机状态跳转、计数器溢出或错误标志拉高时触发。这是定位FPGA侧逻辑错误的终极手段。 |
| 示波器 (高带宽) | 测量电源噪声、时钟质量、信号完整性。 | 使用带宽至少为信号频率3-5倍的示波器。测量电源时,务必使用探头的尖端接地弹簧,而非长接地线。 |
| Wireshark (USB抓包) | 在PC端捕获USB协议层的通信数据包。 | 需要安装USBPcap驱动。可以清晰地看到USB的SETUP、IN、OUT事务,用于分析协议层问题,例如描述符请求是否正常响应。 |
| 自定义调试信息输出 | 通过USB3014的UART或USB虚拟串口,从ARM固件中打印日志。 | 在固件关键流程(如DMA回调函数、USB事件处理函数)中加入CyU3PDebugPrint语句,将日志输出到PC的串口助手,是追踪固件运行状态的利器。 |
整个调试过程,就是一个从系统到模块、从软件到硬件的逐层隔离和定位过程。保持耐心,用好工具,大部分问题都能找到根源。这个从FPGA逻辑到ARM固件,再到PC软件的完整链条调试经历,让我对高速数据采集系统的协同工作有了更深刻的理解,其核心在于每个环节的稳定和环节间握手信号的精确同步。
本文还有配套的精品资源,点击获取