简介线阵CCD驱动电路在非接触式工件尺寸测量中应用广泛但传统设计常受限于实现复杂、单片机驱动速度慢、计数分频法灵活性差且难以随环境光强调整光积分时间容易出现输出饱和失真或景物与背景难以区分的问题。这份面向CCD硬件设计与数码相机相关开发者的技术文档给出一种不改变系统主频即可动态控制光积分时间的驱动方案采用EPM7032C44可编程逻辑器件通过调节光转移脉冲周期实现并已应用于带钢纵切机组自动对中系统。文中完整梳理CCD的MOS结构与TCD1206UD光敏元阵列、转移脉冲与复位脉冲等驱动波形时序以及空驱动延长积分时间的具体做法可作为驱动电路设计与调试的参考。资源包为1个doc文件约120KB已有271人学习下载适合需要快速理解动态驱动思路的工程师查阅。1. 从一次饱和失真说起线阵CCD驱动电路为什么要动态控制光积分时间某次纵切机组对中工位反复出现输出波形削顶现场先擦镜头、换光源问题依旧。根因不在光学件而是线阵CCD驱动电路把光积分时间写死了背景光一强CCD输出就饱和背景光一弱物体和背景又糊在一起。这份设计资料给了一条不改变系统主频的思路——用CPLD动态控制光积分时间按背景光强调整空驱动数量让CCD始终落在输出信号的线性区。数码相机里的CCD硬件设计讲究曝光控制搬到工业线阵扫描场景核心同样是“积分窗口”而不是单纯换镜头。适合做CCD硬件设计、CPLD时序开发和工业视觉采集的开发者。2. 方案定位与技术选型CPLD、单片机、计数分频三种CCD驱动路线的取舍2.1 三种传统驱动方案的实际短板线阵CCD的驱动信号不是“给个时钟就能出图”。转移脉冲SH决定电荷从光敏元搬进模拟移位寄存器φ1、φ2负责把电荷一級一級推到输出端复位脉冲φR决定OS端每次输出后的复位节拍。驱动电路一旦把SH周期固定光积分时间就固定环境光变强时输出幅度跟着涨涨到电源轨附近就是饱和失真环境光变弱时信号幅度掉下去背景和物体在A/D量化后挤在同一小段码值里后续算法再厉害也分不开。常见做法里直接数字电路驱动法用门电路和计数器搭时序逻辑一多飞线、改板、调试都麻烦单片机驱动法靠软件翻转IO频率低遇到高速线阵CCD时一个中断抖动就把时序带偏计数分频法改积分时间要动主频或计数器初值主频一动模拟通道的滤波器参数、A/D采样节拍都得跟着重算改计数初值又可能把驱动波形占空比带歪。这三种路线本质都是静态驱动光积分时间不随背景光强走。方案实现方式实际短板适用场景直接数字电路门电路计数器搭时序改一版要重新布线调试困难时序固定、产量极大的简单产品单片机驱动软件翻转IO或定时器输出频率低中断抖动影响时序低速、低成本、非连续采集计数分频法改主频或计数器初值主频影响模拟通道初值影响波形光强稳定的封闭环境CPLD动态控制硬件计数器微处理器预置需要时序仿真和引脚规划光强变化、要求连续扫描的工业场景这张表里最值得关注的是最后一行CPLD把时序发生放在硬件里主频不动只改“空驱动数量”来拉长或缩短SH周期。数码相机里的CCD硬件设计把曝光时间当成可调参数工业线阵把这套思路放到连续扫描场景难点变成时序不能抖、主频不能动、参数要能被微处理器在线改。选CPLD不是为了追新而是因为它刚好卡在“硬件时序稳定”和“参数可在线配置”之间。2.2 动态光积分时间方案的架构拆解这套方案的闭环不复杂线阵CCD采集到的信号先过抗干扰电路再进A/D转换转换结果存储后送给微处理器微处理器对一段数据做峰值、均值或饱和点统计判断当前背景光偏强还是偏弱算出一个光积分延时预置值写到CPLD的Data总线CPLD用统一主频T0产生SH、φ1、φ2、φR其中SH周期由基本计数器和光积分延时预置计数器共同决定。背景光强时预置值让空驱动数量变少积分时间短背景光弱时空驱动数量变多积分时间长。这里的关键约束是“不改变系统主频”。主频T0同时喂给φ1、φ2、φR和SH计数器模拟通道的采样节拍、滤波器的截止频率、A/D的转换窗口都按T0设计。改主频等于把模拟侧重新调一遍现场没有那么多时间。改空驱动数量只动SH周期φ1、φ2的节拍不变A/D照旧采样软件只需要改一个12位预置值。架构上可以把它拆成四块CCD像元与模拟移位寄存器、驱动缓冲与电平转换、CPLD时序核心、微处理器闭环。CPLD的IO驱动能力有限常见做法是加一级缓冲器再进CCD避免长线负载把上升沿拖慢微处理器和CPLD之间用数据总线或SPI这类接口传预置值写入时机放在SH下降沿之后避开正在计数的窗口。这个细节在后面避坑章会展开现场很多“图像随机撕裂”就是写参数写在了错误节拍上。2.3 器件与工具链EPM7032C44、Verilog与主频约束资料里用的CPLD是EPM7032C44资源不算大逻辑不能铺得太散。用它做线阵CCD驱动核心就是几个计数器、几个比较器和一个SH输出刚好够用。开发流程一般是建工程、选器件、写Verilog、做时序仿真、分配引脚、综合、下载、用示波器量四路波形。没有硬件时也可以先跑仿真把基本计数器的2236、延时计数器的4095、SH脉宽的500这几个边界扫一遍比直接上板子盲调省时间。引脚规划可以按下面这种思路列信号方向接法注意点T0输入系统主时钟尽量走全局时钟脚避免普通IO引入抖动Data[11:0]输入微处理器数据总线或寄存器输出写入时加锁存别在计数中途变phiSH输出缓冲器→CCD转移栅先量周期再量脉宽phi1/phi2输出缓冲器→CCD模拟移位寄存器两路反相边沿要齐phiR输出缓冲器→CCD复位端极性和脉宽按器件手册确认复现时先别急着接CCD。把CPLD下载进去示波器探头挂在phiSH上改Data看SH周期有没有跟着变再挂phi1、phi2看两路是不是严格反相最后挂phiR看复位脉冲有没有落在OS输出的节拍上。这个顺序看着笨但能把“时序问题”和“光学问题”分开。现场最容易翻车的是把镜头问题、光源问题和驱动问题混在一起查最后查了一圈发现是SH周期被预置值方向写反了。提示CPLD的IO不要直接长线驱CCD先加缓冲器再把地回流和时钟走线分开。边沿一软后面调图像都是玄学。3. 器件结构与驱动时序TCD1206UD的2236个像元如何被四路脉冲读出3.1 像元阵列、转移栅与模拟移位寄存器的分工TCD1206UD是线阵CCD资料里给的结构是2236个pn结光电二极管构成光敏元阵列。前64个和后12个被遮蔽用作暗电流检测符号记作Dn中间2160个是曝光像敏单元每个单元14μm长、14μm高中心距14μm。光敏元两侧是存储电荷的MOS电容列也就是存储栅存储栅两侧是转移栅电极SH再往外是CCD模拟移位寄存器输出部分由信号输出单元和补偿输出单元构成。这套结构决定了驱动信号的分工SH负责把光敏元里积分出来的电荷搬到模拟移位寄存器φ1、φ2负责在移位寄存器里搬电荷φR负责输出 circuit 的复位。光积分时间不是由φ1、φ2决定的而是由两个SH脉冲之间的间隔决定的。SH来得越密光积分时间越短SH间隔拉长光积分时间变长输出幅度跟着变高。资料里说“CCD输出信号与光强和光积分时间成正比”落到电路上就是调SH周期。输出顺序也要记清楚OS端先出13个虚设单元信号再出51个暗电流信号然后连续输出S1到S2160有效像素信号S2160之后又出9个暗信号再出2个奇偶检测信号之后是空驱动。空驱动数量可以人为延长正好用来拉长光积分时间。这个“空驱动”不是废数据它是这套动态控制方案的调节余量。3.2 四路驱动脉冲的时序关系与最小周期计算φ1、φ2由主频T0二分频得到两路反相φR由T0经逻辑电路产生SH由计数器产生。资料里给了一个关键约束一个SH周期中至少要有1118个φ1脉冲否则上次数据没读完就开始下一次读数输出会失真。这里容易把“SH周期”和“SH脉宽”混成一件事。按“φ1由T0二分频”推φ1的周期是2T01118个φ1脉冲需要2236T0所以SH周期至少2236T0。资料里写“TSH1118T0”是按φ1脉冲数在数不是按T0在数读的时候要把它换算清楚。用公式写就是T_phi1 2 * T0 T_SH_min 1118 * T_phi1 2236 * T0 T_int T_SH 2236 * T0 N_empty * T0其中T_int是光积分时间N_empty是空驱动数量。最小光积分时间是2236T0最大按资料口径是2236T04096T0。实际差一拍取决于计数器装载和清零的时序仿真时要把边界扫出来。参数含义资料取值调试时看什么N基本计数长度2236小于这个值有效像元读不完Data光积分延时预置值14095方向要和光强策略对齐SH脉宽转移脉冲高电平时间代码里约500T0太窄转移不干净太宽浪费周期φ1/φ2移位寄存器时钟T0二分频、反相两路边沿对齐不能有交叠毛刺φR复位脉冲与T0同步极性和脉宽按CCD手册注意SH周期和SH脉宽不是一回事。周期决定光积分时间脉宽决定电荷转移窗口。示波器上先把周期量对再量脉宽别拿脉宽去算积分时间。3.3 空驱动数量与光积分时间的换算方法按资料里的计数器逻辑基本计数器计到2236后产生计数满信号启动光积分延时预置计数器预置计数器计满后清除基本计数器并产生SH。预置初值确定空驱动数目范围14095。按代码里“Data_reg递增到4095”的方向Data越大等待时间越短SH周期越短Data越小等待时间越长SH周期越长。这个方向和“背景光强时积分时间短”能对上光强时给大预置值光弱时给小预置值。现场标定时可以列一张小表把Data和实测SH周期对应起来Data空驱动趋势SH周期趋势适用光强4095最少接近2236T0强背景光防饱和2048中等约2236T02048T0中等光强1最多接近2236T04096T0弱背景光提信号幅度这张表不是让你背数值而是让你在微处理器里做闭环时有方向。光强时如果Data写反饱和不但压不下去还会更严重。调试时先把Data从最小走到最大看SH周期是变长还是变短确认方向后再进闭环。方向确认这一步花不了十分钟但能省掉后面一堆“算法怎么调都不对”的怀疑。3.4 输出信号序列与暗电流参考OS端输出的序列里前13个虚设单元、51个暗电流信号、9个暗信号和2个奇偶检测信号都不是有效像素但它们有用途。暗电流信号随温度变化拿它做参考可以补偿暗电平漂移奇偶检测信号可以用来判断两列并行传输有没有错位。动态控制光积分时间时光强变化会同时影响有效像素和暗电流微处理器如果只看有效像素的峰值容易把暗电平变化误判成光强变化。常见做法是取一段暗像元的均值做基线再用有效像素的峰值或均值减基线拿这个差值去调Data。这个细节在强光场景下特别有用。背景光一强有效像素先饱和暗像元变化没那么快减基线后能更早发现饱和趋势。弱光场景下有效像素幅度低暗电流占比变大不减基线容易把暗电流当成信号。资料里没有展开算法但硬件上已经把暗像元留出来了二次开发时别浪费。4. 核心逻辑拆解Verilog里两个计数器如何生成SH脉冲与动态积分窗口4.1 端口与寄存器定义资料里的Verilog代码用了非ASCII的φ符号直接贴进综合工具容易出问题。下面按原逻辑整理成ASCII信号名方便阅读和综合。端口保留T0、Data、phiSH、phi1、phi2、phiR内部用base_count管2236个基本节拍sh_count管SH脉宽data_reg存预置值clr做周期清除。module ccd_driver ( input wire T0, // 系统主频所有时序的统一节拍 input wire [11:0] Data, // 微处理器下发的光积分延时预置值 output reg phiSH, // 转移脉冲 output reg phi1, // 模拟移位寄存器时钟1 output reg phi2, // 模拟移位寄存器时钟2与phi1反相 output reg phiR // 复位脉冲 ); parameter N 2236; // TCD1206UD一个SH周期的基础节拍数 reg [11:0] base_count; // 基本计数器 reg [11:0] data_reg; // 预置值锁存 reg [8:0] sh_count; // SH脉宽计数器 reg start_signal; // 启动延时计数 reg signal_del; // 装载预置值的延迟标志 reg clr; // 周期清除标志这段端口的重点在Data位宽。12位对应04095刚好覆盖资料里说的预置范围。base_count也用12位因为2236超过8位。sh_count用9位因为SH脉宽计数到500就够。T0是唯一时钟phi1、phi2、phiR、phiSH都从它派生这样主频不动时序关系不会因为改积分时间而散掉。4.2 基本计数器与光积分延时预置计数器基本计数器负责数到2236数满后置start_signal让延时计数器开始工作。延时计数器从data_reg开始递增直到4095或clr到来。下面这段是整理后的核心计数逻辑always (posedge T0) begin if (base_count N) begin signal_del 1b1; start_signal 1b1; base_count base_count 1b1; end else if (base_count N 2) begin base_count N 3; // 防止计数器越界后乱跳 end else begin signal_del 1b0; base_count base_count 1b1; end if (clr) begin // clr优先级最高直接清周期 base_count 12d0; start_signal 1b0; end endbase_count从0数到2236对应一个SH周期里最基础的2236个T0。signal_del在计满那一拍置1用来在下一拍把Data装进data_regstart_signal同时置1让延时计数器开始递增。base_count N 2那个分支是防越界实际综合时可以把这段简化成饱和计数。clr放在最后保证周期清除时不会被前面的自增覆盖。延时计数器部分如下always (posedge T0) begin if (signal_del) begin data_reg Data; // 在基本计数满后装载预置值 end if (start_signal) begin if (data_reg 12d4095) begin clr 1b1; // 预置值走到头准备产生SH end else begin data_reg data_reg 1b1; end end if (clr) begin if (sh_count 9d500) begin // SH高电平维持500个T0 sh_count 9d0; clr 1b0; end else begin sh_count sh_count 1b1; end end end这里的参数方向要盯住data_reg装载Data后往4095走Data越大走到4095越快空驱动越少SH周期越短。Data越小走到4095越慢空驱动越多SH周期越长。这个方向和“强光短积分、弱光长积分”能对上微处理器在强光时给大值弱光时给小值。如果二次开发时改成递减到0方向就反过来标定表也要跟着翻别把两套逻辑混在一起。4.3 转移脉冲与复位脉冲的生成细节SH由clr产生。clr为高时phiSH为高clr维持500个T0后清零SH也就有了约500T0的脉宽。φ1、φ2由T0二分频φR跟随T0。整理后的写法如下always (posedge T0) begin phi1 ~phi1; phi2 phi1; // 非阻塞赋值phi2取到的是翻转前的phi1 end always (*) begin phiR T0; // 复位脉冲与主频同步实际极性按CCD手册确认 end always (clr) begin phiSH clr; // clr高则SH高clr持续500T0则SH脉宽约500T0 endphi1每个T0翻转一次周期是2T0phi2取翻转前的phi1所以和新的phi1反相。这个写法比phi2 ~phi1更贴近硬件两个寄存器在同一时钟沿更新phi2拿到旧值输出端自然反相。phiR这里用组合逻辑跟随T0资料里说复位脉冲由主频经逻辑电路产生实际板子上要按CCD手册确认极性和脉宽别直接照抄。phiSH用clr做敏感信号属于把内部标志当组合输出综合一般能过但时序分析不友好后面第4.4会讲同步化改法。4.4 代码里几个容易忽略的时序边界第一个边界是SH周期和SH脉宽的交界。clr在data_reg到4095时置1下一拍sh_count开始数500SH在这500拍里保持高。周期清掉后base_count从0重新数下一个SH要等2236拍再加延时。仿真时要把Data1和Data4095两个极端都跑一遍看SH周期是不是落在2236T0到2236T04096T0附近别只看中间值。第二个边界是Data的写入时机。微处理器如果在base_count正数到一半时改Datasignal_del还没到data_reg不会立刻更新这还好如果改在signal_del那一拍附近可能装到旧值或新值不同综合结果可能不一样。常见做法是加两级寄存器把Data先锁存到data_shadow等SH下降沿后再更新data_reg。这样写参数不会打断当前周期。第三个边界是把clr当异步时钟。always (clr)能综合但clr是寄存器输出拿它当敏感信号等于自己造了一个内部时钟时序约束不好写后面加逻辑也容易出毛刺。更稳的写法是状态机在T0沿上判断base_count和data_reg到了就置phiSH数够500拍再清phiSH。下面这段是同步化思路localparam S_IDLE 1b0; localparam S_SH 1b1; reg state; always (posedge T0) begin case (state) S_IDLE: begin if (base_count N data_reg) begin phiSH 1b1; sh_count 9d0; state S_SH; end else begin base_count base_count 1b1; end end S_SH: begin if (sh_count 9d500) begin phiSH 1b0; base_count 12d0; state S_IDLE; end else begin sh_count sh_count 1b1; end end endcase end这段把SH的置位和清零都放在T0沿上clr不再当时钟综合和时序分析都干净。代价是周期计算从“两个计数器串联”变成“一个状态机里比较”逻辑量稍大但EPM7032C44这种资源下仍然放得下。二次开发时如果加寄存器配置、饱和检测建议直接从这个同步版本往下改别在异步clr上叠逻辑。5. 避坑与排查动态CCD驱动电路联调时最容易翻车的5个点5.1 输出全白或全黑先量SH周期不要先换镜头现象CCD输出要么一片高电平要么一片低电平A/D采回来全是0或满量程图像上看不出物体轮廓。现场第一反应通常是镜头脏了、光源坏了换完一圈问题还在。原因SH周期被预置值写死或写反光积分时间落在极端。强光下积分时间太长有效像元饱和输出削顶弱光下积分时间太短信号幅度低于A/D的有效位数看起来像全黑。还有一种情况是SH根本没出来CCD没有转移脉冲OS端只输出固定电平。解决示波器先挂phiSH确认有没有脉冲、周期是多少、脉宽是多少。再改Data从最小走到最大看SH周期有没有跟着变。如果周期不变查Data总线有没有写进去、CPLD引脚有没有分配错如果周期变了但方向反了把预置值映射翻过来。镜头和光源放到时序确认之后再查顺序别倒。5.2 SH脉宽和SH周期混淆图像固定条纹或错位现象图像上出现固定位置的条纹、重复错位或者有效像元区中间有一段数据明显不对。改光源亮度条纹位置不变改Data条纹会动但消不掉。原因把SH脉宽当成SH周期来调。SH脉宽太窄转移栅开启时间不够电荷没搬干净上一行残影混到下一行SH周期小于2236T0上次数据没读完就开始下一次读数移位寄存器里数据错位。资料里“TSH1118T0”是按φ1脉冲数在说直接当成T0数就会把周期算短一半。解决先按φ1周期换算。φ1是T0二分频一个φ1周期2T01118个φ1脉冲对应2236T0。示波器同时挂phi1和phiSH数一个SH周期里有多少个phi1脉冲少于1118就把周期拉长。SH脉宽单独看500T0是资料代码里的值实际按CCD手册和转移栅特性微调别拿脉宽去顶周期。5.3 预置值方向写反强光下饱和反而更严重现象现场光变强微处理器按闭环逻辑调Data结果输出饱和更厉害光变弱信号反而更小。看代码觉得闭环方向没错看波形发现SH周期和预期相反。原因预置值计数方向没统一。资料代码里data_reg往4095递增Data越大等待越短二次开发改成递减到0后Data越大等待越长。微处理器里的“强光增大Data”策略没跟着翻方向就反了。还有一种情况是Data总线高低位接反写4095实际进去是3840标定表全乱。解决先做一张Data-SH周期实测表从1到4095取几个点确认单调方向。微处理器闭环里加一个方向标志换计数器方向时只改一个宏。总线高低位用固定测试图案校验比如写0xAAA和0x555读回CPLD内部寄存器看是不是一致。方向确认之前不要开自动闭环手动调几档把趋势看明白。5.4 CPLD直接驱CCD边沿变软、振铃、输出噪声大现象phi1、phi2上升沿变缓示波器上看到振铃CCD输出噪声大暗像元基线不稳。换一片CPLD好一点但问题还在。原因CPLD的IO驱动能力有限CCD的驱动端有电容负载长线又带来电感和反射。直接驱等于让IO去推一个不匹配的负载边沿一软移位寄存器的两个时钟交叠区变大电荷转移不干净。地回流没处理好数字开关噪声会窜到OS输出。解决加一级缓冲器缓冲器靠近CCD放走线短输出串小电阻阻尼振铃CPLD和CCD的地分开走单点汇合时钟线远离模拟输出线。示波器用短地弹簧测别用长地线夹否则看到的振铃有一半是探头带来的。边沿和噪声处理完再去调Data否则调参数像在猜。5.5 微处理器写Data时毛刺图像随机撕裂现象静态场景下图像偶尔撕裂撕的位置随机手碰数据总线或改软件写入频率撕裂概率变化。复位后可能好一阵跑久了又出现。原因Data在计数器计数中途变化data_reg装到半新半旧的值或者写使能和地址译码有毛刺CPLD把一次写看成多次写。SH周期突然跳一拍下一帧读出的像元就对不上。解决Data先进锁存器再在SH下降沿后更新到data_reg写使能用同步设计地址译码加一拍寄存微处理器侧写之前读状态寄存器的busy位避开SH高电平窗口。调试时用逻辑分析仪抓写使能、Data和phiSH看写入是不是落在SH高电平期间。把写入窗口错开撕裂基本就没了。6. 二次开发把预置值做成可标定寄存器并给时序留一个观测口6.1 把预置值做成可配置寄存器直接在顶层用Data总线比较危险建议在CPLD里加一组寄存器控制寄存器放enable、soft_reset、direction预置值低8位和高4位分两个地址状态寄存器回读当前SH周期计数和饱和标志。微处理器写预置值时先写影子寄存器等SH下降沿后生效当前周期不被打断。这样标定表可以存在微处理器里换镜头、换光源只改表不用重新综合CPLD。地址寄存器位定义用途0x00CTRLbit0 enablebit1 soft_resetbit2 dir使能和方向切换0x01DATA_LData[7:0]预置值低8位0x02DATA_HData[11:8]预置值高4位0x03STATUSbit0 busybit1 sat写入窗口和饱和提示6.2 给时序留一个观测口CPLD空闲IO别浪费引两个观测点出来一个输出SH周期计数的分频信号一个输出“base_count计满”的脉冲。逻辑分析仪抓这两个点再加phiSH和Data就能看出预置值什么时候生效、周期有没有跳变。没有逻辑分析仪时用示波器看观测点和phiSH的相位关系也比只看图像猜强。这个口子平时不影响功能出问题时能少熬一晚上。6.3 验证顺序先不接CCD测四路波形和Data的单调关系再接CCD遮光、正常光本文还有配套的精品资源点击获取