news 2026/8/17 21:03:38

基于 FPGA 与 USB 2.0 的线阵 CCD 光谱数据采集系统设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 FPGA 与 USB 2.0 的线阵 CCD 光谱数据采集系统设计

基于 FPGA 与 USB 2.0 的线阵 CCD 光谱数据采集系统设计

关键词:FPGA、线阵 CCD、光谱采集、AD9240、FIFO、USB 2.0、FTDI、Vivado、ILA、数据传输

摘要

本文介绍一套基于 FPGA 的线阵光谱数据采集系统。系统从线阵探测器输出的模拟信号开始,经模拟前端和 AD9240 转换为 14 位数字样本;FPGA 负责产生采集时序、识别有效样本、组织帧边界、利用双 FIFO 完成跨时钟缓存,并经 FTDI USB 2.0 接口将原始数据送至 PC。本文重点分析 FPGA 的模块划分、关键数据格式、FIFO/USB 数据通路和 ILA 波形验证,不涉及光学设计、扫描控制、OCT 重建或图像识别。

1. 引言

线阵探测器输出的数据具有两个特点:一是样本随时序连续到来,二是 PC 端 USB 读取并不总是连续。若没有缓存,USB 传输的等待会反过来干扰采集;若没有帧边界,主机也无法判断一段字节流是否是一帧完整光谱。

因此,数据采集系统的核心并不是“把数据传到电脑”,而是保证下面四件事同时成立:

  1. FPGA 在正确时刻驱动探测器和 ADC;
  2. 只有有效窗口内的 ADC 样本进入数据通路;
  3. 每帧数据都具有可识别边界;
  4. 采集时钟域和 USB 时钟域被缓存隔离,数据按顺序送往 PC。

2. 系统总体设计

系统的硬件数据路径很明确:

线阵 CCD / 探测器 ↓ 模拟光谱信号 模拟前端(滤波、幅度匹配) ↓ AD9240(14 位 ADC) ↓ adc_data[13:0] FPGA(时序、有效标志、帧封装、双 FIFO、USB 调度) ↓ FTDI 245 FIFO 型 USB 2.0 接口 ↓ PC(原始帧接收、显示或保存)

FPGA 同时生成ccd_clkstdata_en等时序信号;PC 可以通过 USB 发送启动、停止和采集参数。由此形成“数据上送、控制下发”的双向接口,但正文始终只围绕数据采集展开。

3. FPGA 设计方案

3.1 代码模块如何划分

顶层ccd_usb_top没有把所有功能塞在同一段逻辑中,而是按数据路径拆分:

tra_ccd → ad_dr → data_12_16 → fifo_temp × 2 → fifo_drive → usb_top_ft232h

其中:

模块作用
tra_ccd产生探测器时钟、启动和采集使能
ad_dr接收 AD9240 的 14 位数据并输出有效标志
data_12_16将有效样本转换为带帧边界的 16 位传输字
fifo_temp × 2在采集时钟域和 USB 60 MHz 时钟域之间缓存数据
fifo_drive根据 A/B FIFO 空满状态和 USB 状态调度读写
usb_top_ft232h对接 FTDI 245 FIFO 型 USB 通道并解析控制帧

这样写的好处是:数据不对时看ad_dr,帧不对时看data_12_16,USB 卡顿时看 FIFO 与握手,而不是面对一个无法拆分的大状态机。

3.2 启动和停止,为什么要先清 FIFO

采集系统最容易被忽略的不是“如何启动”,而是“停止后留下了什么”。如果一次采集停止时 FIFO 内还有残留字节,而下一次采集直接启动,PC 端可能会先读到上一轮的帧尾或半帧数据。

代码中的star模块专门处理这一点。它用IDLERUNNINGCLEAR_FIFOWAIT_STOP四个状态控制pll_flagfifo_clean:启动时放行采集时钟;停止时关闭时钟放行、置位 FIFO 清空;下一次启动前一直保持等待状态。这样,采集会话的第一帧从干净的缓存开始。

启动命令 → 放行采集时钟 → 接收一组组新帧 停止命令 → 停止写入 → 清空 FIFO → 等待下一次启动

3.3 时序模块和采样模块为什么分开

tra_ccd不直接处理 ADC 数据,它只产生探测器相关的ccd_clkstdata_en。当前实现中,st_cnt负责周期计数,trig_cnt在指定计数窗口后短暂置位data_en。这样“什么时候让探测器工作”和“什么时候允许数据写入”被分开表达。

ad_dr再把trig输出为ad_clk,并以IDLE → HIGH → LOW状态机产生ad_flag。其中data_en表示采集窗口已经打开,eos用于结束当前有效段。后级并不是见到时钟就写数据,而是依据ad_flag识别有效样本,这一点对排除无效区和避免帧错位非常关键。

3.2 从 14 位 ADC 样本到 USB 帧

AD9240 数据经ccd_data[13:0]进入 FPGA。data_12_16利用ad_flag的边沿确定帧的开始与结束,并采用如下组织方式:

0xAAAA | {2'b00, ccd_data[13:0]} × N | 0x5555

0xAAAA是帧头,0x5555是帧尾,中间为扩展到 16 位的 ADC 样本。这使 PC 接收端可以在连续字节流中寻找边界、检查数据是否截断,并在需要时重新同步。它解决的是数据对齐问题,不是完整的差错校验协议。

data_12_16的状态机逻辑可以理解为:检测到ad_flag上升沿时先写帧头;下一拍开始输出{2'b00, ccd_data};在ad_flag保持期间持续输出样本;检测到下降沿后输出最后一个样本,再单独写帧尾。帧头、首个样本和帧尾因此不会挤在同一个数据字里。

3.3 FIFO 为什么要分成两个

采集数据写入 FIFO 的节奏由采集时钟决定,USB 读数据的节奏由usb_clk_60m和 PC 握手状态决定。两端节奏不同,所以需要把“持续采样”和“按 USB 条件发送”隔开。

当前实现中:

  • FIFO 写侧接收 16 位帧数据;
  • FIFO 读侧输出 8 位字节流;
  • 两个 FIFO 由fifo_drive统一调度;
  • A/B FIFO 的空满状态决定读写是否允许。

这给系统提供了跨时钟域和位宽适配的缓冲位置。后续 FIFO 专题会专门讨论切换条件、缓存深度和连续帧验证。

3.4 A/B FIFO 如何交替工作

fifo_drive实际上有写、读两套状态机。写端运行在采集相关时钟下:一帧开始后选择 FIFO A 或 B 写入,发现eos时记录这一帧已经结束,再切换到另一组 FIFO。读端运行在 USB 60 MHz 时钟下:只有确认某一组 FIFO 已经完成一帧写入、并且尚未读空时,才选择它送往 USB。

采集第 n 帧:写 FIFO A USB:读上一帧的 FIFO B 采集第 n+1 帧:写 FIFO B USB:读上一帧的 FIFO A

这个过程把“采样不能停”和“USB 可能等待”分离开。它不是抽象概念上的乒乓缓存,而是代码中fifo_wrreq_selectfifo_rd_selecteos_a/eos_bfifo_empty_a/b共同完成的帧级调度。

4. USB 2.0 数据传输

数据从 FIFO 读出后进入usb_top_ft232h,再经ftdi_245fifo_top与 FTDI 245 FIFO 风格接口握手。USB 可写时,FPGA 按顺序发送缓存字节;USB 接收方向则用于传入启动、停止和参数控制帧。

对数据采集来说,USB 模块的判断标准不是“接口能枚举”,而是:

  • 有数据时能从正确的 FIFO 读出;
  • FIFO 为空时不发生无效读;
  • USB 不可写时不会覆盖未发送的数据;
  • 一段连续采集后,PC 仍能找到完整帧头和帧尾。

从代码接口可见,上传侧是 8 位字节流,控制接收侧则以 128 位帧进入tx_specified_len。启动标志为16'h0024,全1的 128 位帧用于停止。对数据采集而言,这套控制帧的作用只有一个:把“何时开始写新数据、何时停止并清空缓存”明确地交给 FPGA,而不是依赖 PC 端随意断开读取。

5. 数据通路的时钟域和位宽变化

把一帧数据从 ADC 送往 USB,需要跨过三个边界:

阶段数据宽度主要时钟/标志关键任务
ADC 输入14 位trig/ad_clkdata_enad_flag只保留有效样本
FPGA 帧数据16 位采集相关时钟、帧首尾状态机扩展数据位宽并加入帧边界
FIFO 写侧16 位采集相关时钟连续写入当前帧
FIFO 读侧/USB8 位usb_clk_60m、USB 握手字节化输出并等待接口可写

因此,FIFO 的 16 位写、8 位读不是为了压缩数据,而是把 FPGA 内部的帧字适配为 USB 的字节流。PC 接收端只要按相同字节序重新拼回 16 位字,就可以寻找0xAAAA0x5555来恢复帧边界。

6. ILA 波形如何验证这条链路

这张 ILA 波形将data_en、FIFO A/B 的读写请求与空满状态、USB 相关状态、data_out[13:0]data_16[15:0]放在同一时间轴上。它的价值在于把采样、封帧、缓存和传输放到一次观察中:

观察信号用于确认
data_out[13:0]ADC 样本确实进入 FPGA
data_16[15:0]样本已被组织为传输用数据字
fifo_wrreq_a/b有效帧数据正在进入缓存
fifo_rdreq_a/bUSB 侧正在从缓存取数据
fifo_empty/full_a/b缓存切换没有越过空/满边界
USB 状态读写请求受接口握手条件约束

看这张波形时,建议不要只盯一个信号,而是按顺序检查:先确认data_en出现时data_out[13:0]有连续样本;再确认data_16[15:0]产生对应传输字;接着确认fifo_wrreq_a/b只在当前 FIFO 未满时写入;最后看fifo_rdreq_a/b是否只在 FIFO 非空、USB 条件允许时产生。这样就能把“没有数据”“没有写入缓存”“缓存没有读出”“USB 没有发送”四类问题区分开。

波形中的fifo_full_a/b在当前观察窗口保持低电平,说明这一次捕获窗口内没有写满;fifo_empty_a/b则配合读请求判断缓存是否已经被读空。对外展示时应把它表述为“本次波形观察结果”,不把单个窗口写成长期稳定性的结论。

工程保存的实现报告还显示,该 FPGA 快照的用户时序约束满足,WNS 为 9.900 ns、TNS 为 0。它是 RTL 已完成实现和时序检查的证据,不用于外推整机性能。

7. 总结

这套数据采集系统只做一件事:以 FPGA 为核心,稳定地把线阵探测器经 AD9240 得到的数字光谱样本组织、缓存并传输到 PC。其关键不是单一模块,而是探测器时序 → ADC 有效样本 → 帧边界 → FIFO 跨时钟缓存 → USB 握手的连续关系。

下一篇将从 线阵光谱探测器驱动与采集 开始,继续拆解tra_ccdad_dr的时序和样本有效窗口;之后依次展开 AD9240、FIFO 和 USB 专题。

本文仅分享公开架构、局部波形和设计思路;完整 RTL、约束、IP 配置、原理图、PCB 和原始数据后续分享。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/17 21:03:33

视觉Agent实战:解决AI视觉从实验室到产线的落地难题

1. 项目概述:当Agent遇上真实世界视觉挑战上周调试一个图像分类Agent时遇到了诡异现象:在测试集上准确率高达98%的模型,部署到产线后连最简单的缺陷检测都做不好。这让我意识到当前AI Agent在视觉任务上的表现存在严重"实验室-现实"…

作者头像 李华
网站建设 2026/8/17 21:03:01

小众市场生存之道:从产品矩阵到用户共创的差异化策略

1. 从“嘴硬”到“手硬”:一个汽车品牌的生存逻辑反思最近几年,汽车圈里流行一句话:“不要‘嘴’就是刚”。这话听起来有点糙,但理不糙。它背后反映的,是消费者和市场对一众新品牌、新势力从“PPT造车”、“发布会造车…

作者头像 李华
网站建设 2026/8/17 21:02:05

基于turn.js的3D翻页电子书开发实战:从原理到性能优化

1. 项目概述:从静态到动态的页面革命 在网页设计领域,如何让用户获得超越传统点击和滚动的交互体验,一直是前端开发者追求的目标。静态的图片和文字罗列虽然清晰,但总感觉少了点“灵魂”。直到我遇到了 turn.js ,一个…

作者头像 李华
网站建设 2026/8/17 21:01:56

从深夜紧急发版到一键灰度回滚:Unleash功能开关实战指南

从深夜紧急发版到一键灰度回滚:Unleash功能开关实战指南 【免费下载链接】unleash Open-source feature management platform 项目地址: https://gitcode.com/GitHub_Trending/un/unleash Unleash 是一个开源的"功能开关"(Feature Flag…

作者头像 李华