ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FPGA+CIS+AD9822高速扫描采集链路设计与调试避坑指南

FPGA+CIS+AD9822高速扫描采集链路设计与调试避坑指南 先交代一下项目背景我在做一套文档批量高速扫描的采集前端要求是 300DPI 分辨率下尽量把一页 A4 压到 2 秒内扫完同时图像数据要能在 FPGA 里实时做黑电平矫正、坏点矫正这类预处理而不是像普通扫描仪那样只把成品图丢给上位机。评估一圈之后我放弃了现成的扫描仪模组和专用扫描控制芯片决定用 CIS 传感器、AD9822 模拟前端和 FPGA 自己搭一条完整的采集链路。文章会把从选型、电路、FPGA 逻辑到时序调试的完整过程写出来重点放在那些 datasheet 里不会明说、但实际调试一定会踩的坑上。适合已经会用 FPGA 写逻辑、但第一次接触 CIS/AD9822 这类模拟前端的朋友。1. 项目定位与总体方案1.1 为什么不用现成扫描仪方案很多人问做图像采集直接买一个高速扫描仪模组不就行了如果只是要“扫出一张图”确实没必要自己搭。但项目里有几个硬性要求导致现成方案很难满足第一图像数据要低延迟进入 FPGA方便后续实时做缺陷检测和自动分类而普通扫描仪的数据流是封闭的第二整机结构上要求传感器长度和走纸机构完全匹配CIS 这种接触式传感器可以直接贴近被扫物体光路短、体积小比 CCD 扫描模组更好嵌入第三成本和迭代周期也要考虑专用扫描控制芯片前期开发快但后期想在增益、曝光、点序上做定制就很受限制。用 FPGA 做核心控制等于把整条链路的主动权都拿回来了CIS 的驱动时钟、LED 光源开关、AD9822 的寄存器配置、图像数据的接收与缓存全部由自己定义。后续要调整分辨率、行频、增益曲线只需要改逻辑和寄存器不用换硬件。对于一个需要长期迭代的产品方向这个灵活性的价值远超前期多花的开发时间。1.2 硬件链路选型CIS AD9822 FPGA先看传感器。CIS接触式图像传感器本质上是一条线的光电二极管阵列配着内置的 LED 光源和自聚焦透镜。和 CCD 相比它不需要高压驱动和复杂的光学系统传感器直接贴在走纸通道上就能成像模块本身又薄又轻特别适合做紧凑型扫描设备。选型时要盯几个参数有效像素数要覆盖目标幅面在指定 DPI 下的像素需求输出方式有单路模拟输出和 RGB 三路独立输出两种想要高速彩色扫描一定要选三路输出主时钟频率决定了行频上限我这边选的这颗 CIS 最大支持 10MHz 主时钟有效像素 2664 个覆盖 A4 幅面在 300DPI 下的 2480 像素需求还多出一些余量。再看模拟前端。CIS 输出的是一路模拟电压幅度大概在 0.2V 到 1.6V 之间这个信号不能直接进 FPGA 的 GPIO。AD9822 是 ADI 为扫描仪类应用做的三通道 16bit 模拟前端内部集成了相关双采样CDS/采样保持放大器SHA、可编程增益放大器PGA、偏移 DAC 和 ADC。它最大的价值就是把模拟链路上的调理工作全包了FPGA 只需要通过串口写几个寄存器就能完成增益、偏置、输入模式的配置。三通道同时采样正好匹配 CIS 的 RGB 三路独立输出一次读一行就可以同时拿到三个通道的像素数据。这颗芯片标称最高采样率在 15MSPS 左右我用 10MHz 像素时钟完全在安全范围内。最后是 FPGA。这级系统做的事情看起来多其实逻辑量不大关键是 IO 数量和时钟管理要够用。我用的是一颗 Artix-7 级别的芯片逻辑资源用了不到一半主要消耗在行缓存、坏点矫正和 DMA 搬运上。如果你手头只有 Cyclone IV 或者国产高云这类中小规模片子只要资源超过 5000 个 LUT、有 PLL/MMCM、有足够的单端 IO也能跑起来。整条链路就是CIS 的 RGB 三路模拟输出进 AD9822AD9822 的三通道 16bit 数字总线进 FPGAFPGA 先做矫正和行缓存再写进 DDR3最后通过 USB3.0 或千兆以太网把图像数据包传给上位机。链路不算长但每一段的时序都得对上。2. 硬件电路与信号完整性设计2.1 CIS 模块接口与 LED 照明设计CIS 的接口看着简单但电气设计上很容易踩坑。一般 CIS 模块会引出这么几类信号CLK 移位时钟、SP 行起始脉冲、模拟视频输出单路或 RGB 三路、LED 供电可能分开的 R/G/B LED 引脚和地。CLK 和 SP 都是低速数字信号FPGA 的普通 IO 就能驱动但要注意驱动能力。有的 CIS 内部移位寄存器对时钟上升沿和下降沿都敏感CLK 的边沿一旦出现抖动像素位置就会整体偏移。我这边在 FPGA 内部对 CIS_CLK 和 SP 做了 ODELAY 级联调整并串了一个 33 欧姆电阻实测边沿质量好了很多。LED 照明是 CIS 系统里最容易忽略但又影响最大的部分。CIS 内置的都是大电流 LED瞬时电流几百毫安很常见如果直接用 FPGA 引脚去拉电平根本扛不住必须外接 MOS 管开关。更关键的是供电LED 电源一定不能和模拟电源共用一路否则 LED 导通瞬间的地弹会直接串进模拟输出图像上就会出现随行频变化的水平条纹。我在首版 PCB 上就吃过这个亏后来把 LED 供电单独用一路 DC-DC输出端再加 LC 滤波CIS 模拟电源用独立 LDO两者最后在板子入口附近单点汇地条纹才彻底消掉。2.2 AD9822 最小系统与配置要点AD9822 外围电路相对常规模拟输入 VINR/VING/VINB 分别接 CIS 的 RGB 三路输出输入端对地加一个 10nF 电容滤高频参考电压 REF 用低噪声基准源数字电源 AVDD/DVDD 分开去耦AGND 和 DGND 在芯片下方单点连接。有一点要特别注意AD9822 的每个模拟输入通道可以独立配置输入范围而 CIS 输出幅度不一定能覆盖 ADC 满量程所以要通过 PGA 把信号放大到接近满量程的位置。我这边把 PGA 设置在 6dB 左右再把偏移 DAC 校准到黑电平输出接近 0这样图像动态范围基本能打满 16bit。AD9822 支持 CDS 和 SHA 两种输入模式。CDS 模式主要针对 CCD 那种带复位电平的输出CIS 大多数情况下没有标准的复位电平结构直接配置成 SHA 模式更合适也就是每个像素稳定后采一次样。这个选择直接决定了后面的时序设计SHA 模式下采样点只要落在像素信号平台区就行判定条件比 CDS 宽松不少。配置接口是三线串行方式SCLK、SDATA、SLOAD。三个配置寄存器分别控制模式、PGA 增益和偏移量。上电之后不要急着写先等电源稳定再等至少十几毫秒让内部基准稳定然后逐字节写入。写入完成后要留出 PGA 和偏移的建立时间通常几个毫秒就够。早期版本我是在上电后立刻配置结果读到图像有半个小时的漂移其实就是内部没稳定就开始采了。2.3 FPGA 板级设计与接口约束FPGA 板卡方面IO 规划主要集中在两组一组是低速控制信号包括 CIS 的 CLK/SP、LED 开关、AD9822 的串口信号这些对时序要求不高只要避开噪声源就行另一组是 ADC 数据总线包括 16bit 数据、通道选择信号、数据时钟这组信号建议全部放在同一个 Bank并且尽量不走长线。数据总线的走线长度不一致会导致建立保持时间余量变小我在布线时做了等长约束。另外系统里存在两个时钟域FPGA 内部系统时钟域和 AD9822 返回的数据时钟域。ADC 数据是跟着它自己的 DATACLK 回来的不能直接用系统时钟去采必须先做跨时钟域处理后面逻辑部分会细说。DDR3 颗粒我用的是板载 1GB 容量实际上存储一整页 A4 的 300DPI 彩色图像只需要 40MB 左右256MB 的颗粒都绰绰余选大容量纯粹是为了给多页连续扫描留缓冲。3. FPGA 逻辑实现从行驱动到数据流3.1 顶层模块划分写 FPGA 代码之前我先在纸上画了模块框图整个采集逻辑按数据流拆成六块时钟管理模块、CIS 行驱动模块、AD9822 配置接口模块、ADC 数据接收模块、图像预处理模块、缓存与传输模块。这样拆的好处是每一块都能单独仿真、单独调试出问题时不用在几百行代码里翻。实际工程里我建议你也保持这个习惯模块边界越清晰后面用手持逻辑分析仪抓信号时越方便。时钟管理模块用 PLL/MMCM 生成三路时钟系统时钟、CIS 主时钟、ADC 采样时钟。这三路不一定同频但源一定要同源否则累计相位误差会让图像出现像素宽度的抖动。CIS 主时钟和 ADC 采样时钟在需要的情况下可以先用同源时钟分频再通过相位偏移找到最佳采样点这个在调试心得里详细讲。3.2 CIS 行驱动状态机CIS 的驱动逻辑核心是一个行状态机状态转换大概是空闲 → 点亮 LED → 等待光源稳定 → 拉高 SP → 连续输出 CLK → 关闭 LED → 进入行间隙 → 等待下一行触发。SP 只需要一个时钟周期的高脉冲之后每个 CLK 上升沿CIS 内部就会把像素阵列的电荷包逐个移到输出端。注意有些 CIS 在 SP 之后的前 N 个 CLK 输出的是无效像素或暗参考像素不能直接当成有效图像的开始后面讲行首对齐时还会再提。CLK 的产生有两种方式一种是用系统时钟直接分频一种是用 PLL 产生的独立时钟。我在首版用分频方式后来为了调相位换成了 PLL 输出。下面这段是行驱动的精简状态机实际工程里还需要加入 LED 稳定计时、超时保护这些辅助逻辑。// 行驱动状态机精简版重点看结构 localparam IDLE 3d0, LED_ON 3d1, LED_STABLE 3d2, LINE_OUT 3d3, LINE_GAP 3d4; reg [2:0] state; reg cis_sp_r, led_en_r; reg [15:0] clk_cnt; always (posedge clk_sys or negedge rst_n) begin if (!rst_n) begin state IDLE; cis_sp_r 1b0; led_en_r 1b0; clk_cnt 16d0; end else begin case (state) IDLE: begin if (line_trig) state LED_ON; end LED_ON: begin led_en_r 1b1; state LED_STABLE; end LED_STABLE: begin if (led_stable_cnt LED_STABLE_CYCLES) begin cis_sp_r 1b1; clk_cnt 16d0; state LINE_OUT; end end LINE_OUT: begin cis_sp_r 1b0; // 用使能信号分频产生 CIS_CLK if (clk_cnt TOTAL_CLK_PER_LINE - 1) begin clk_cnt 16d0; state LINE_GAP; end else begin clk_cnt clk_cnt 16d1; end end LINE_GAP: begin led_en_r 1b0; if (gap_cnt LINE_GAP_CYCLES) state IDLE; end default: state IDLE; endcase end end这段代码里唯一要提醒的是CIS_CLK 本身建议单独用 PLL 输出而不是在状态机里翻转寄存器因为普通 IO 翻转产生的时钟抖动可能会比 PLL 的时钟输出大一个量级。如果必须用寄存器分频至少要在约束文件里给这条时钟路径加上时钟约束让布线工具尽量平衡延时。3.3 AD9822 串行配置接口AD9822 的串行配置接口实现起来就是一个 SPI 写入状态机。每个寄存器是 8bit 数据前面跟 3bit 地址三线接口没有片选但要靠 SLOAD 的低有效窗口来锁存。写入顺序我习惯按照模式寄存器 → PGA 寄存器 → 偏移寄存器。模式寄存器里最关键的位是输入模式选择必须确认配成了 SHA 模式否则采样点位置会不对。上电后状态机在等待计时结束后开始逐位发送位与位之间给足够的时间裕量。下面是一个简单的配置状态机示意实际工程中可以把寄存器地址和数据定义成参数数组要调整参数时只改数组内容就行不用动逻辑。// AD9822 串口配置状态机精简版 reg [15:0] config_shift; reg [3:0] bit_cnt; reg [1:0] reg_cnt; always (posedge clk_sys or negedge rst_n) begin if (!rst_n) begin sclk_r 1b1; sdata_r 1b0; sload_r 1b1; end else begin case (cfg_state) CFG_IDLE: begin if (cfg_start) begin config_shift {cfg_addr, cfg_data}; // 3bit 地址 8bit 数据 bit_cnt 4d11; cfg_state CFG_SEND; end end CFG_SEND: begin sclk_r 1b0; sdata_r config_shift[10]; // 从高位开始 config_shift {config_shift[9:0], 1b0}; bit_cnt bit_cnt - 4d1; cfg_state CFG_SCLK_HIGH; end CFG_SCLK_HIGH: begin sclk_r 1b1; if (bit_cnt 0) begin sload_r 1b0; cfg_state CFG_LOAD; end else begin cfg_state CFG_SEND; end end CFG_LOAD: begin sload_r 1b1; cfg_state CFG_IDLE; end default: cfg_state CFG_IDLE; endcase end end配置过程中最容易出的问题是时序余量太小SCLK 频率太高导致 AD9822 内部移位寄存器误采。我这边 SCLK 用的是分频后的慢时钟大概是 1MHz 左右速度完全够用配置本来就是一锤子买卖没必要追求高速。3.4 ADC 数据接收与多通道解复用ADC 数据接收部分是按 AD9822 输出时序来写的。AD9822 输出是三通道时分复用的 16bit 总线也就是说在一个数据时钟周期内16bit 数据线上会依次出现 CH1、CH2、CH3 的结果同时伴随通道指示信号FPGA 端要把这 16bit 数据按通道分别锁存到三个寄存器里。写法上不复杂核心是一个 case 判断always (posedge adc_dataclk or negedge rst_n) begin if (!rst_n) begin ch1_data 16d0; ch2_data 16d0; ch3_data 16d0; end else begin case (ch_sel) 2b00: ch1_data adc_dout; 2b01: ch2_data adc_dout; 2b10: ch3_data adc_dout; default: ; endcase end end到这里为止我已经拿到三通道像素的 16bit 数据但每个通道的像素值还不一定是准确的。接下来要先做黑电平减去坏点矫正把暗电流偏置和传感器缺陷点去掉。黑电平校正一般取行首暗参考像素的平均值再在流水线上减掉坏点校正则是在标定阶段生成坏点坐标表遇到坏点位置就用相邻像素值替换。这一级处理做完图像底子就比较干净了。3.5 跨时钟域缓存与上行传输ADC 数据在 DATACLK 时钟域而后续的矫正和打包逻辑在系统时钟域中间必须加异步 FIFO 过渡。FIFO 写入端用 ADC 数据时钟读出端用系统时钟深度按两行数据量来算至少能缓存 2664 个像素 × 3 通道 × 16bit我给到了 32K 字深度避免 DDR3 繁忙时把数据憋在源头导致 FIFO 溢出。缓存策略上我的方案是做三缓冲乒乓。FPGA 把矫正后的整行数据写进 DDR3 的 A 区同时上一行的 B 区内容正在被 DMA 搬运到上位机C 区则空闲等待。三块区域轮转下来采集、处理、传输三个环节可以并行工作不会互相等。实测传输带宽大约在 55MB/s 时DDR3 读写占用率不到 20%余量很充足。4. 300DPI 下的关键参数计算4.1 像素数与行频的数学关系把分辨率转换成像素数和行频这一步是系统设计的起点不能拍脑袋定。300DPI 意味着每英寸 300 个像素A4 幅面宽 210mm转换成英寸是 210 除以 25.4约 8.268 英寸乘上 300得到图像有效像素约 2481 个。也就是说选 CIS 时横向有效像素必须大于 2481留出扫描边距和暗参考像素后我选的 2664 像素就比较合适了。纵向 A4 是 297mm约 11.693 英寸同样按 300DPI 换算一页需要约 3508 行。行频的计算闭环是这样的CIS 每输出一行需要送出 2664 个有效像素加上前导消隐、尾消隐、行间隙这些开销我按总时钟数 2930 来算。如果 CIS 主时钟是 5MHz一行需要 2930 除以 5MHz约 586us行频就是 1706 行/秒一页 3508 行耗时约 2.06 秒加上 LED 稳定、机械走纸的余量勉强能进 2 秒。想再提速把主时钟提到 10MHz 就行行频能翻倍到 3412 行/秒单页耗时约 1.03 秒。4.2 AD9822 采样率与数据带宽预算AD9822 的采样率要和 CIS 主时钟匹配不能随便定。CIS 每输出一个像素AD9822 就要采一次所以理论上 MCLK 等于像素时钟就可以。但实际场景里采样点位置可能不在像素稳定区需要额外相位调整空间我倾向于把 AD9822 的采样时钟设成像素时钟的 2 倍也就是 10MHz 或 20MHz这样可以通过内部采样点偏移来找到最佳位置。AD9822 最高采样率在 15MSPS 附近我把像素时钟压在 10MHz 以下采样率 10MSPS留在安全区。图像数据率在这里算一下每像素有 RGB 三个通道每个通道 16bit那一个像素位置就是 48bit约 6 字节。一行 2664 像素乘下来约 15.6KB。行频 1706 行/秒时数据率约 26.6MB/s主时钟翻倍后行频 3412数据率约 54.5MB/s。这个速率对千兆以太网来说压力也不大有效带宽能做到 110MB/s 左右USB3.0 就更宽裕了。DDR3 的带宽远远够用。4.3 缓存深度与实时性分配缓存深度方面我按“至少能缓冲 4 行图像”来算FIFO 内部缓冲 2 行DDR3 的缓存区域按整页划分。一页 A4 彩色图像数据量大概是 3508 行 × 15.6KB约 54.8MB。就算上位机处理速度没跟上DDR3 里也能装下好几页形成一个小的弹性缓冲。实际用下来这套数据流基本不会断流瓶颈反而在走纸机构上。这里有个取舍要说明为什么不在 FPGA 内部 RAM 里直接缓存整页而是外挂 DDR3因为 1 页 55MB 的图像如果用 FPGA 内部 BRAM 存需要至少几十个 18K BRAM中小型芯片根本放不下而且还有 Packet RAM 要留给行缓存和 FIFO。外挂 DDR3 是成本、面积、容量三者的平衡点。5. 时序调试心得与避坑指南5.1 采样点相位调不对图像为什么是全花的整条链路第一次跑通时我拿到的图像完全没法看满屏雪花一样的随机噪声但能隐约看到东西的轮廓。用逻辑分析仪抓 AD9822 的输出数据跳变得很厉害明显是采样点落在像素信号的上沿或下沿上采到的是一个不断变化的中间值。确认是采样点相位问题后就要动采样时钟和像素时钟的相对相位了。我用的办法是用 PLL 产生一路和 CIS_CLK 同频但可调相位的采样时钟然后以固定的步进去扫描相位每换一个相位抓一帧图像用图像对比度或行内像素方差作为评价指标找出最佳相位。整个过程可以用上位机脚本自动跑。最后定位的最佳相位大概在 CIS_CLK 上升沿后 1/4 周期多一点的位置这个点恰好落在像素信号最平缓的平台区。如果采样点落在边沿图像噪声会显著变大这个现象可以作为后期诊断采样相位是否漂移的依据。5.2 LED 电源不隔离水平条纹怎么消第一个版本硬件回来后我发现只要 LED 点亮图像上就会叠一层水平的明暗条纹而且频率和行频一致。用示波器去量 CIS 的模拟输出能看到 LED 导通瞬间叠加了一个尖峰脉冲幅度有几十毫伏。这个尖峰顺着地平面串进了模拟输出就成了每一行的底色波动。解决办法分两头走硬件上把 LED 供电从模拟电源域彻底分开LED 地单独走线再到电源入口单点汇合同时在 LED 驱动管栅极串了电阻降低开关电流的 dI/dt软件上把 LED 点亮和采样窗口错开LED 稳定之后再开始输出 CLK保证模拟采样期间 LED 电流已经进入平稳状态。两头一夹条纹彻底消失。5.3 行首像素错位图像整体偏移图像整体左右偏移、行首出现固定黑线或白线这个现象排查了很久。最后发现是 SP 拉高之后我立刻开始按 CLK 计数有效像素但 CIS 内部在 SP 之后还会输出一段前导无效像素这些像素被我当成了有效数据整行内容整体左移行首多出来的就是无效像素。解决方式很简单在行驱动状态机里SP 拉高后先跳过若干个 CLK再开始采样有效像素。跳过的数量以 datasheet 里的前导时钟数为基准再结合逻辑分析仪实测的波形微调。我把这个参数设成了寄存器方便上位机在线调整。图像开始位置和有效行宽都正确后只需要再做一次标定就能固定下来。5.4 常见问题速查表调试过程中我整理了一张问题速查表每次碰到类似现象直接对着排查效率提升很明显。这里把最关键的一部分列出来供参考。现象可能原因排查方向满屏雪花状杂点采样点落在像素边沿调整采样时钟相位寻找像素稳定区随行频出现的水平条纹LED 电源噪声串入模拟地分离 LED 电源降低 LED 开关电流斜率图像整体左右偏移SP 后前导像素未跳过调整有效像素起始位置跳过前导 CLK 数固定几列出现异常亮/暗CIS 坏点做坏点标定用相邻像素替换图像底色不一左右有渐变光源不均匀或暗电流偏置做黑电平校正统计每行暗像素均值数据偶发丢行异步 FIFO 溢出加大 FIFO 深度检查 DDR3 写带宽占用图像有周期性斜纹时钟和采样时钟相位缓慢漂移检查 PLL 配置确保时钟同源且锁定5.5 调试方法论与工具选择调试这套系统我最大的建议是分阶段验证不要一上来就全链路联调。第一步不接 CIS用信号发生器给 AD9822 注入固定幅值的正弦波或直流电平确认 ADC 的数据读回来是正确的第二步接上 CIS但不要让 LED 点亮采集暗场数据确认行同步和像素计数正常第三步点亮 LED但把增益调小先确认图像形状最后再把所有矫正和传输逻辑加进来。每一步都验证完成出问题时定位范围就非常小。工具上我习惯在 FPGA 工程里保留一组在线读写寄存器所有关键参数——增益、偏置、采样相位、行起始偏置、LED 稳定时间——都能通过上位机直接改。配合逻辑分析仪 IP 抓取内部信号排查问题不用反复编译综合节约的时间非常可观。调试时不需要抓完整行只需要抓行首和行尾关键位置的几十个像素就够。6. 实测结果与可扩展方向6.1 实测效果与性能指标整个系统调通之后我拿标准 ISO 分辨率测试卡和灰阶卡做了几轮验证。300DPI 下黑色文字边缘清晰没有明显粘连现象灰阶卡 12bit 到 16bit 区域能稳定分辨出差异图像上基本看不到前面提到的条纹、雪花和行错位问题。单页 A4 彩色扫描从触发到上位机收到完整图像实测最快约 1.1 秒加上走纸机构动作时间整页处理在 1.5 秒内可以完成达到了最初的设计目标。数据链路的稳定性也做了连续压测。以 55MB/s 的速率连续扫描 100 页DDR3 写带宽占用率稳定在 20% 上下上位机收到的数据没有丢行、错位、缺像素的情况。整体功耗大概在 5W 左右其中 LED 瞬时功耗占了大头这个功耗水平在嵌入式设备里算比较理想的。6.2 这套链路还能怎么扩展做到这一步只是搭好了图像采集的地基上面还能叠加很多东西。比较顺手的方向是完善 ISP 流水线比如把坏点矫正从静态表升级成实时检测加入自动曝光和增益控制甚至跑一个轻量级的 3×3 色彩校正矩阵让 RGB 色彩还原更准接口层面可以把 USB3.0 换成 MIPI CSI-2 输出直接把图像送到移动平台的应用处理器传感器层面如果想覆盖 A3 幅面可以选更长有效像素的 CIS 或者用两片 CIS 做拼接FPGA 这边只需要调整行计数和拼接缝处的重叠补偿逻辑。我个人在实际操作中体会最深的一点是CIS AD9822 这套组合硬件本身并不复杂真正决定项目成败的几乎全在时序“对位”上——每个像素的采样点、每一行的起点、每个通道的解复用一拍的偏差反映到图像上就是完全看不懂的条纹和错位。后面我做任何图像采集项目都会把这类时序参数全部做成寄存器让上位机可以在线调整这个习惯几乎帮我在每个项目里都省下了一到两周的调试时间。如果这篇文章能让你少走一点弯路那就很值了。
返回列表