简介:这是一份面向硬件开发与图像传感器应用工程师的线阵CCD驱动电路设计资料,围绕数码相机及非接触式工件尺寸测量中的CCD硬件设计问题展开。资料重点介绍采用可编程逻辑器件EPM7032C44动态控制CCD光积分时间的新方法,在系统主频不变的前提下,根据背景光强自动缩短或延长积分时间,从而避免输出信号饱和失真、提高复杂光照环境下的测量精度,并配有TCD1206UD型线阵CCD结构与驱动波形说明。所提方案已在带钢纵切机组自动对中系统中验证,适合从事CCD驱动、图像采集和嵌入式测控系统设计的工程师参考。压缩包内共1个doc文档,大小约120KB;文档包含摘要、工作原理、驱动波形设计及应用实例,便于直接阅读和复用。该资源已有268人学习/下载,可作为相关项目前期选型与电路设计的参考。
1. 线阵 CCD 驱动电路:为什么要动态控制光积分时间
做非接触尺寸测量的工程师,大多被 CCD 输出信号的饱和失真和背景分割问题折磨过。环境光一变,固定驱动时序下的 CCD 信号要么饱和溢出,要么低到把工件淹没在背景里。本文要拆的这套线阵 CCD 驱动电路设计方案,核心就一句话:在不改系统主频的前提下,用可编程逻辑器件动态调整光积分时间,让 CCD 输出始终落在正常工作区间。它解决了传统直接数字电路、单片机驱动和计数分频法共有的静态驱动缺陷,已经用在带钢纵切机组自动对中系统里。这个思路对做 CCD 硬件设计、数码相机成像前端、各类在线测量系统的从业者都有参考价值,尤其是被环境光干扰折腾过的,可以直接沿用这套逻辑。
2. 三种传统驱动法为什么不够用:从静态到动态的选型逻辑
2.1 直接数字电路、单片机、计数分频的各自天花板
先说结论:这三种方法在原理上都是“静态驱动”,光积分时间被锁死,适应不了光照突变的环境。
直接数字电路驱动法,用门电路、触发器、计数器硬搭时序。优点是输出波形干净、速率快,但元件数量大。一张驱动板几十个芯片,排线绕成蜘蛛网,调试时拿示波器逐点对波形,改一个时序参数就要换电阻电容甚至重新布线。量产时的一致性也差,两块板子同样的元件焊出来,波形沿的抖动可能就是两回事。
单片机驱动法,用 I/O 口软件翻转产生驱动脉冲。改动灵活,时序想怎么调就怎么调。但瓶颈在 I/O 翻转速率,51 内核的单片机跑 12MHz 晶振,一条翻转指令好几个周期。TCD1206UD 这类 2000 多像素的线阵 CCD,φ1、φ2 需要兆赫兹级别的时钟,单片机产生的频率根本喂不饱,高速数据采集场景直接淘汰。
计数分频法比前两者进了一步,通过改变主频或计数器预置值来改变积分时间。但问题是:改主频时,模拟通道的低通滤波器、相关双采样电路的参数都跟着变,频带和信噪比全乱套;改计数初值呢,直接扰动驱动波形,φ1 和 φ2 的相位关系可能错位,CCD 输出就花屏。
这三种方法共同的问题是:光积分时间一固定,光照强了就饱和失真,光照弱了信号就淹没在噪声里,景物和背景的灰度差拉不开。我拆过一个类似的测量项目,当时的做法是手动调镜头光圈,晴天拧小光圈,阴天拧大,车间里光照变化快的时候完全跟不上。
2.2 动态控制的设计目标:主频不动,只动空驱动数量
这套设计方案的核心思路是引入“空驱动”的概念。TCD1206UD 在一个 φSH 周期内,读完 2236 个像素后其实还可以继续打时钟,这段多余的时钟就叫空驱动。空驱动期间 CCD 不输出有效像素信号,但它延后了下一个转移脉冲 φSH 的到来时间,相当于拉长了光积分窗口。
积分时间拉长,感光单元收集的电荷就多,输出电压幅度就高;积分时间缩短,电荷少,幅度低。所以动态控制逻辑就清楚了:背景光强时,少插空驱动,光积分时间短,避免饱和;背景光弱时,多插空驱动,光积分时间拉长,让信号抬起来。
关键操作是保持系统主频 T0 不变,只改变空驱动脉冲数量。这样模拟通道的工作条件始终稳定,信号处理电路的频带和信噪比不受影响,而 CCD 的光积分时间却在一个大范围内可调。最小光积分时间是 2236 个主频脉冲周期,最大可以到 2236+4096 个周期,动态范围接近一倍。这套逻辑用 CPLD(EPM7032C44)实现,逻辑规模不大,但时序要求严苛,接下来看具体的实现方法。
3. 基于 EPM7032C44 的动态驱动电路:Verilog 实现与参数设置
3.1 硬件架构与信号分配
先理清这 5 路信号的分工,它们是整个驱动电路的基础:
- T0:统一主频时钟,所有时序从这个时钟派生,保证同步
- φSH:转移脉冲,高电平期间把感光区的电荷转移到模拟移位寄存器
- φ1、φ2:移位时钟,互补驱动,把寄存器里的电荷逐级搬向输出端
- φR:复位脉冲,每个像素输出前复位一次输出电容
从实现角度说,φ1 由 T0 二分频得到,φ2 是 φ1 的取反,频率相同、相位互补。φR 直接由 T0 经过逻辑整形生成,保证每个 φ1 周期内完成一次复位和输出。φSH 则不是固定分频得到的,它靠计数器组合产生,是动态控制的关键节点。
整个逻辑的核心是两个计数器:基本计数器负责数 2236 个像素周期,计满后触发一个信号,让预置计数器开始工作;预置计数器负责空驱动延时,预置初值由微处理器根据 CCD 输出信号幅度反推的光强信息动态设定,取值范围 1~4095。预置计数器计满后,清除基本计数器,同时产生一个 φSH 脉冲,开始下一轮积分。
这个设计的好处是:φSH 的周期被拆成了“固定像素周期 + 可变空驱动周期”两段,改积分时间只动预置值,不动像素读出节奏。
3.2 核心 Verilog 代码逐段拆解
借用原文给出的 Verilog 程序框架,补上可读性注释,拆一下关键逻辑:
module Drive(φSH, φ1, φ2, φR, Data, T0); input T0; // 系统主频时钟 input [11:0] Data; // 微处理器写入的空驱动数量预置值 1~4095 output φSH, φ1, φ2, φR; // CCD 四路驱动信号 reg φ1, φ2, φR; reg φSH, Clr, Start_signal, Signal_del; reg [11:0] Data_reg, Base_count; reg [8:0] Sh_count; parameter n = 2236; // TCD1206UD 有效像素 + 暗像素总数 // φ1 二分频,φ2 取反,形成互补移位时钟 always @(posedge T0) begin φ1 = ~φ1; φ2 = ~φ1; end // φR 直接用主频时钟整形,保证与 φ1 同步 always @(T0) φR = T0; // 基本计数器:数到 2236 产生进位信号,拉高 Start_signal always @(posedge T0) begin if (Base_count == n) begin Signal_del = 1; Base_count = Base_count + 1; Start_signal = 1; end else if (Base_count > n+2) Base_count = n+3; // 防止计数器跑飞 else begin Signal_del = 0; Base_count = Base_count + 1; end if (Clr) begin Base_count = 0; Start_signal = 0; end end // 预置计数器:空驱动延时计数,计满 4095 产生清零信号 always @(posedge T0) begin if (Signal_del) Data_reg = Data; if (Start_signal) begin if (Data_reg == 4095) begin Clr = 1; end else Data_reg = Data_reg + 1; end if (Clr) begin if (Sh_count == 500) begin Sh_count = 0; Clr = 0; end else Sh_count = Sh_count + 1; end end // φSH 在 Clr 期间拉高,形成转移脉冲 always @(Clr) begin if (Clr) φSH = 1; else φSH = 0; end endmodule这段代码的关键参数:
- n=2236:TCD1206UD 的像素总数,其中 2160 个有效像元,其余是暗像元和虚设单元。这个值跟传感器型号绑定,换传感器必须改
- Data[11:0]:12 位预置值,取值范围 0~4095,对应空驱动脉冲数。这个值的来源是微处理器分析 CCD 输出信号的幅度后做出的判断
- Sh_count=500:φSH 高电平持续 500 个 T0 周期,这个脉宽要保证电荷完全转移到移位寄存器,不能太短
从时序角度理解这段逻辑:基本计数器数到 2236 后,不是马上产生 φSH,而是拉起 Start_signal,让预置计数器开始数空驱动脉冲。预置计数器从微处理器写入的 Data 值开始往上加,加到 4095 时产生 Clr 信号,Clr 拉高 φSH,同时清零基本计数器。所以 φSH 的周期等于 2236 加上(4095 减去 Data)个 T0 周期。Data 越大,空驱动越少,积分时间越短;Data 越小,空驱动越多,积分时间越长。
注意这段代码有一个细节:信号名用了希腊字母 φ,这在某些编译环境中可能不识别,实际工程中我一般会改成 SH、P1、P2、PR 这种 ASCII 命名,或者用 phiSH、phi1 这样安全的名字,避免综合工具报错。这是拿原代码直接跑仿真最容易踩的坑之一。
3.3 动态控制的闭环逻辑
驱动电路本身不决策,它只执行。决策在微处理器那边。
系统工作流程是:CCD 输出模拟信号,经过抗干扰滤波和 A/D 转换后送入微处理器。微处理器分析当前帧的灰度分布,如果看到整体电平偏高、接近饱和,就增大 Data 值减小空驱动,缩短下一帧的积分时间;如果看到整体电平偏低、动态范围不够,就减小 Data 值增加空驱动,延长积分时间。这个反馈回路的基本原理是 CCD 输出电压与光照强度和积分时间的乘积成正比,控制积分时间本质上是把曝光量拉回线性区。
这套逻辑在工程上跟自动曝光算法是同一套思路,只是实现载体换成了 CPLD。做数码相机成像前端的朋友看到这里应该觉得眼熟,就是快门优先和光圈优先的硬件版本。
注意:Data 值的反馈周期不能太短。如果你每个像素都做一次判定,CCD 还没读完一行呢,预置值就变了好几次,画面会出现条带状亮度不均。一般至少要等完整扫完一帧再做一次判定,这属于控制周期的工程权衡。
4. 系统集成与实测应用:带钢纵切机组自动对中系统
4.1 系统框图与信号链路
应用环境是带钢纵切机组的自动对中系统。现场环境对光电传感器极不友好:轧线上有蒸汽、冷却水飞溅、钢粉飘落,背景光照随时在变。用固定积分时间的传统方案,经常出现板材边缘和背景灰度接近,边缘检测算法找不到边界的情况。
系统的信号链路这样走:
CCD 输出端先接抗干扰电路。这级的关键是滤掉电机变频器和周边大功率设备带来的传导干扰,否则模拟信号上叠加的毛刺会被 A/D 误判成有效像素。抗干扰电路输出接 A/D 转换器,采样率要跟 φ1 时钟匹配才能保证每个像素一个有效采样点。转换结果存入缓冲区,微处理器做边缘检测和灰度统计,根据统计结果计算新的空驱动预置值,写入 CPLD 的 Data 寄存器。
微处理器在其中的角色是控制闭环。它每隔固定帧数做一次灰度统计,取 CCD 输出有效像素区的最大灰度值和平均灰度值,跟预设的上下阈值比较。超过上限就把 Data 往大了调,低于下限就往小了调。调整步长要按现场情况设,我习惯先用大步长快速逼近,再小步长细调,避免振荡。
4.2 现场参数整定与效果
具体参数整定给出一个参考模板:
表 1 动态驱动电路的现场整定参数
| 参数 | 典型值 | 整定方法 |
|---|---|---|
| 系统主频 T0 | 1MHz | 由传感器最高驱动频率决定,留 20% 裕量 |
| 基本计数值 n | 2236 | 对应 TCD1206UD 像素总数,固定 |
| φSH 脉宽 | 500 个 T0 周期 | 示波器观测 OS 端输出信号,确保电荷转移干净 |
| Data 预置值范围 | 500~3000 | 根据现场光照变化区间标定 |
| 反馈判定周期 | 每帧一次 | 带钢运行速度越快,周期要越短 |
整定过程我踩过不少坑。最典型的是把 Data 调到 4095 附近时,CCD 输出出现顶部削平。原因不是饱和,而是 φSH 脉宽不够——预置计数器计满到 Clr 产生的这段组合逻辑延时,导致 φSH 高电平持续时间勉强够用,但温度一变,CPLD 内部延时漂移,时序裕量不够了。后来把 Sh_count 从 500 拉到 700,问题消失。
另一个实际效果值得说:这套动态驱动方案解决了饱和失真和背景分割问题,但带钢边缘检测的稳定性还取决于光源布局。现场用的是红外背景光源加 CCD 的透射式方案,光源强度波动本来就小,动态积分时间主要补偿的是镜头污染和钢粉遮挡带来的缓慢衰减,不是光源突变。换句话说,这套方法擅长处理慢变的背景光,反对快变的光源频闪。
系统的检测精度在实验数据上,带钢边缘定位误差控制在 0.3mm 以内,完全满足纵切机组的对中要求。这套方案能成立的关键是反馈环路有足够的帧率余量——CCD 一帧读出时间在毫秒级,微处理器做主控,DSP 或 ARM 都跑得动这个闭环。
5. 避坑与常见问题排查:动态驱动电路的现场实战记录
5.1 现象:CCD 输出出现周期性条纹
现象:调试时用示波器看 OS 端信号,发现输出波形叠加了低频条纹,频率约几百赫兹,肉眼可见亮度不均匀。
原因:Data 预置值的更新时机不对。微处理器在 CCD 正在读出一帧的过程中写入新值,CPLD 在 Signal_del 信号到达时锁存数据,如果这个时机恰好落在有效像素输出区间,就会导致前半帧用旧积分时间、后半帧用新积分时间,一帧内部亮度不连续。
解决:把 Data 更新和帧同步绑定。微处理器只有在检测到 φSH 上升沿之后才写入新预置值,给 CPLD 足够的时钟周期完成锁存。代码层面把 Signal_del 的触发条件加一个限定:在 Base_count 等于 n 后的第一个 T0 上升沿才锁存数据,保证数据写入发生在空驱动区间的起点。
5.2 现象:高温环境下驱动时序漂移,边缘出现毛刺
现象:车间夏季温度到 40℃ 以上,CCD 输出信号边缘出现毛刺,A/D 采样偶尔采到错误值,边缘检测结果抖动。
原因:EPM7032C44 作为 CPLD,内部走线延时随温度漂移。φR 由组合逻辑产生,跟 φ1 的相位差在高温下微小偏移,导致 A/D 采样窗口落在信号上升沿上。这不是 CPLD 本身坏了,而是时序裕量不够。
解决:从时序设计上解决,不要在关键路径上依赖组合逻辑。把 φR 改成由 T0 直接分频同步产生,保证和 φ1 同源同相;A/D 采样时钟改成用 φ1 的下降沿触发,这样数据稳定时采样;再在 PCB 走线上把 φR 信号线加粗做等长。改完以后高温环境下边缘毛刺消失。
5.3 现象:更换 CCD 后输出全黑
现象:把 TCD1206UD 换成一个引脚兼容的型号,结果输出信号全黑,一个有效像素都没有。
原因:不同型号的线阵 CCD,虽然像素数相近,但暗像元数量、虚设像元位置、转移脉冲上升沿的要求都不同。基本计数器 n 还停留在 2236,实际芯片需要的转移时序不匹配,电荷转移不完整。换了传感器,直接用旧的配置参数,这是硬件工程师最容易犯的经验主义错误。
解决:换 CCD 后第一件事是查数据手册,确认三个参数:总像素数(决定 n 值)、暗像元数量(决定有效输出起始位置)、φSH 最小脉宽(决定 Sh_count)。把这三个参数全部改成新传感器对应的值,再上示波器看波形。从那以后我形成了习惯,任何涉及传感器替换的项目,先列一个寄存器参数对照表,新旧型号逐项比对,参数核对完才上电。
5.4 现象:微处理器写入的 Data 值不生效
现象:通过接口向 CPLD 写 Data 值,发现写入后 CCD 驱动波形没变化,光积分时间一直不变。
原因:CPLD 里 Data_reg 的锁存逻辑只在 Signal_del 为高时采样一次。如果微处理器的写入时序比 Signal_del 晚,就会等到下一帧才生效。如果下一帧 Signal_del 持续窗口太短,微处理器接口的时序延迟导致数据没被采到,就会一直维持旧值。这是典型的异步接口握手问题。
解决:在微处理器和 CPLD 之间加一个握手信号。微处理器先拉高写请求,CPLD 等到 Signal_del 有效时锁存数据并回一个应答信号,微处理器收到应答才撤销写请求。没有硬件握手的话,就用查询方式:等检测到 φSH 上升沿后再写数据,保证时序窗口足够。工程上更省事的办法是让 CPLD 把 Data_reg 做成随时可写、Signal_del 到达时再同步到计数寄存器,这样微处理器什么时间写都行,只要保证一帧内写完一次就行。
5.5 现象:A/D 采样率不够,像素丢失
现象:系统跑起来后,CCD 像素数对不上,每行少了几个像素,边缘位置偶发跳动。
原因:A/D 转换器的采样率和 φ1 频率没匹配。CCD 输出是串行的,每个 φ1 周期输出一个像素。如果 A/D 的采样率低于 φ1 频率,或者采样时钟抖动大,部分像素被漏采。更隐蔽的是采样率高于 φ1 频率好几倍,结果同一个像素被采多次,虽然总数不变但灰度值被重复平均了,边缘精度反而下降。
解决:A/D 采样率应该等于 φ1 频率,并且采样时钟直接由 φ1 分频产生,用锁相环保证同相。采样窗口放在像素信号的平台期,避开上升沿和下降沿。从效率角度,A/D 数据每行存满后直接 DMA 搬运到内存,不要在中断服务里逐像素读,否则微处理器负载过高,反馈控制周期就被拉长,动态响应的优势就体现不出来了。
6. 进阶调试技巧:用一段仿真代码验证驱动时序的正确性
在投入硬件调试之前,先在仿真环境里把时序跑通,能省下大量示波器前折腾的时间。这套驱动电路的核心是计数器和状态机的配合,仿真脚本可以直接复用原 Verilog 代码做 testbench,重点盯三个时间点:最小积分时间、最大积分时间、Data 中途变化时的过渡行为。
`timescale 1ns/1ps module tb_Drive; reg T0; reg [11:0] Data; wire SH, P1, P2, PR; integer pulse_count; Drive uut( .φSH(SH), .φ1(P1), .φ2(P2), .φR(PR), .Data(Data), .T0(T0) ); // 50MHz 主频时钟 initial begin T0 = 0; Data = 12'd2048; // 中等积分时间 forever #10 T0 = ~T0; end // 统计 T0 周期数 always @(posedge T0) begin if (SH == 1) pulse_count = 0; else pulse_count = pulse_count + 1; end initial begin $dumpfile("drive_wave.vcd"); $dumpvars; #10000; Data = 12'd1000; // 模拟光变强,缩短积分时间 #10000; Data = 12'd3500; // 模拟光变弱,延长积分时间 #20000; $display("pulse_count at SH low: %0d", pulse_count); $finish; end endmodule跑完仿真重点看两个指标:一是 φSH 的周期跟理论值 2236 加上(4095 减去 Data)是否一致;二是 Data 变更后,下一帧是否按新值工作,有没有出现半帧旧的半帧新的一帧两制现象。这两点过关了,硬件调试压力就小得多。
仿真通过后还有一个验证细节值得做:测量 φSH 低电平期间包含的 T0 脉冲总数,检查是否落在理论范围内。用 VCD 波形文件导入查看,比直接看波形直观得多。如果计数结果跟理论值差一两个周期,多半是组合逻辑的竞争冒险导致计数器多走了一拍,可以在敏感列表里补事件,或者把计数条件改成严格边沿触发。
这套仿真流程是我在某个图像处理 Demo 里的习惯做法,从那以后每次调 CCD 驱动时序,都强制先在 ModelSim 或 Vivado 里跑一遍全时序仿真,然后才是上板。硬件调试一旦烧板,CPLD 要重新擦写,来回成本比仿真高太多。希望这套流程对你也有帮助——先仿真、后上板、再标定,三步走完,动态 CCD 驱动电路基本不会跑偏。
本文还有配套的精品资源,点击获取