ARTICLE DETAIL

资讯详情

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

STM32F405驱动TCD1304:时序控制与ADC同步采集实践

STM32F405驱动TCD1304:时序控制与ADC同步采集实践 简介STM32F405-TCD1304驱动程序是一份专门用于STM32F405微控制器控制TCD1304线性CCD传感器的固件工程适用于光强检测、光学扫描、图像传感等场景面向嵌入式开发者和毕业设计学生解决信号采集、数据处理与上位机通信问题。压缩包共37个文件大小约209KB以12个头文件与9个C源码为核心并包含Makefile、链接脚本、hex/bin烧录镜像及编译中间文件便于直接编译或烧录验证也可辅助理解嵌入式工程构建流程。目前已有2363人浏览学习适合正在开展相关课题或希望深入了解CCD驱动与USB CDC协议的人群。资源内部分模块清晰主程序、USB虚拟串口、ADC配置、定时器控制等相互分离方便移植和二次开发。附带的hex/bin固件镜像可快速验证驱动效果代码结构可为毕业设计提供一套可运行的参考实现。1. 项目概述与整体设计思路调过 CCD 的人都知道所谓“STM32F405-TCD1304 驱动程序”核心难点不在初始化那几行寄存器代码而在于一整套时序关系、电荷转移过程、模拟信号采集和数据同步方案。TCD1304 是东芝的线性 CCD3648 个有效像素像元尺寸 8μm × 200μm常出现在光谱仪、AOI 检测、条码扫描这类场景里。我这次是在给一台近红外光谱仪做前端采样板主控选了 STM32F405传感器就是 TCD1304最终要把 CCD 输出的串行模拟信号变成一帧一帧的数字行数据交给上位机去做波长标定和光谱分析。1.1 这个“驱动程序”到底在驱动什么很多人第一次接触这个项目时容易把“驱动程序”理解成 Windows 里的设备驱动。其实这里说的是 MCU 固件是一段跑在 STM32F405 里的底层代码负责完成三件事产生 TCD1304 需要的时钟时序、控制曝光积分时间、把传感器输出的模拟信号用 ADC 同步采集下来并拼成完整帧。TCD1304 和常见的数字传感器完全不一样。数字传感器内部自带时序控制器和 ADCMCU 只要通过 I2C/SPI 配置寄存器就能读数据。而 TCD1304 是纯模拟输出没有内部控制器所有动作都靠外部给时钟驱动。也就是说传感器每个像素的读出、每一帧的曝光节奏全部由主控芯片“掐着点”控制稍有偏差轻则图像错位重则完全采不到有效信号。1.2 方案选型为什么是 STM32F405我当初评估过几种方案STM32F103、FPGA、树莓派加外部 ADC。FPGA 做时序确实最稳但开发周期长而且光谱仪后续还要跑滤波算法和通信协议没必要为了一颗 CCD 上 FPGA。树莓派或者 Linux 板卡做数据采集延迟太大很难保证微秒级的时钟同步。STM32F103 的 ADC 最快约 1Msps跑 1MHz 像素时钟时非常勉强几乎没有余量。STM32F405 的优势在于主频 168MHz内置 3 个 12 位 ADC采样率最高约 2.4Msps而且定时器之间支持主从同步触发硬件上可以直接用 PWM 产生像素时钟用另一个定时器触发 ADCDMA 自动搬运数据CPU 全程不参与高速采样。这颗芯片在电子市场摸爬滚打多年资料多、参考设计多、成本也低做仪器仪表非常合适。1.3 软件架构一句话总结整套驱动代码可以分成三层最底层是时序层负责输出 Pixel Clock、SH、ICG 三路信号中间是采集层负责 ADC 触发、DMA 转换、帧数据缓存最上层是应用层处理像素有效区定位、暗电流矫正和积分时间调整。架构想清楚之后后面的工作就是按部就班地填细节。2. TCD1304 时序原理与关键参数2.1 TCD1304 的工作机制TCD1304 内部结构可以理解成一条感光阵列加一个模拟移位寄存器。光照射到感光区后每个像元产生光生电荷电荷量的大小和光照强度成正比这就是积分过程。积分结束后需要把这些电荷整列转移到移位寄存器里然后逐个读出。整个过程由三路信号控制ΦM像素时钟连续方波决定每个像素的读出速率。SH移位/积分控制低电平期间是曝光积分时间高电平期间把感光区电荷转移到移位寄存器。ICG积分清除门在 SH 高电平期间给出一个清除脉冲把感光区残留电荷清掉防止上一帧的电荷混入下一帧。从宏观上看一帧数据就是“曝光积累电荷 → 转移 → 逐个读出 → 再曝光”的循环。SH 低电平时间越长曝光量越大输出信号幅度越高但暗电流噪声也会跟着涨。这个传感器没有自动曝光功能曝光时间完全靠程序控制 SH 的电平宽窄来实现。2.2 三路核心信号之间的相位关系这三路信号不是相互独立的它们之间有严格的时间顺序。我调试时习惯用逻辑分析仪同时抓三路信号再对照数据手册逐项核对。最正常的一帧是这样的SH 拉高进入转移窗口此时 ΦM 继续自由运行在 SH 为高期间的某个时刻ICG 产生一个高脉冲把感光区清空随后 SH 拉低感光区重新开始积分再从 SH 下降沿之后的若干时钟周期开始Vout 引脚就会串行输出一整行像素电压。有一个非常关键的细节ICG 信号必须在 SH 为高电平时到达不能提前也不能滞后太多。如果 ICG 脉冲落在了 SH 低电平区间就会在积分过程中把电荷清掉一部分输出波形会出现明显的凹陷或者整行缺失。这一点用示波器观察最直观我当时就因为在中断里改 ICG 的翻转时机导致前几十行数据全是乱的。2.3 像素时钟、数据率与积分时间的计算TCD1304 的像素时钟一般工作在 0.5MHz 到 4MHz 之间。如果 ΦM 取常用的 1MHz每个像素周期就是 1μs。TCD1304 有效像素 3648 个加上前端的 dummy 像素和尾部的过渡像素实际读出一行大约需要 3700 到 3800 个时钟周期也就是 3.7ms 到 3.8ms。一帧的总时间 积分时间 读出时间。比如曝光 5ms读出 3.8ms那么理论最高帧率大约在 110fps 左右。实际应用中光谱仪一般不需要这么快的帧率反而更看重低噪声和长曝光动态范围所以帧率通常设置在 10fps 到 30fps积分时间可以留出很大调整空间。像素时钟的选择还要考虑 ADC 的采样能力。1MHz 像素时钟意味着每微秒要完成一次 ADC 采样和转换STM32F405 完全跑得动。如果超频到 4MHz每个像素周期只有 250nsF405 的 ADC 就非常吃力了通常需要牺牲分辨率或者使用交替采样模式。我的建议是没有特殊需求就把 ΦM 控制在 1MHz稳定性和代码复杂度都最友好。3. 硬件连接与 STM32F405 资源分配3.1 引脚与定时器资源规划我画板子时把时序信号的生成任务全部交给定时器硬件没有用 GPIO 翻转加延时的方式因为软件延时无法保证和像素时钟的相位关系而且中断容易被打断。STM32F405 的定时器资源很丰富我用 TIM1 做主时钟发生器利用它的多个通道输出不同信号。信号STM32F405 资源说明ΦMTIM1_CH1PWM 模式输出 1MHz 像素时钟SHTIM1_CH2输出比较模式控制积分时间和帧周期ICGTIM1_CH3输出比较模式在 SH 高电平期间产生清除脉冲VoutADC1_IN0 或任意可用通道采集 CCD 模拟输出帧同步EXTI 或定时器捕获辅助标记一帧数据的起点把三路信号放在同一个定时器里最直接的好处是它们的时钟基准完全一致不会出现多定时器之间累积误差的问题。TIM1 是高级定时器支持带死区的互补输出虽然我们不用互补输出但它的事件触发能力刚好可以喂给 ADC。3.2 模拟输出调理与 ADC 接入TCD1304 的 Vout 引脚输出的是模拟电压内部有采样保持电路输出电压带有直流偏置。直接把这个信号接到 STM32F405 的 ADC 引脚上有风险一方面阻抗不匹配另一方面偏置电压可能超出 ADC 的输入范围。我用的方法是加一级运放缓冲同时做电平搬移。具体来说先用一个高输入阻抗的运放跟随器隔离 CCD 输出再通过电阻分压和加法器把信号搬到 ADC 可以安全采样的范围。如果只是做功能验证也可以先用电位器分压把信号压到 0 到 3.3V 之间再接一个 RC 低通滤波。有一点要特别注意STM32F405 的 ADC 输入引脚耐压不能超过供电电压超压会直接烧引脚。TCD1304 的 Vout 在某些供电条件下可能接近 3.5V 甚至更高所以调理电路不是可选项强烈建议加上。3.3 电源与布线的几处细节TCD1304 对电源纹波比较敏感尤其是 Vdd 引脚。开关电源的纹波会直接串进输出信号表现为一帧数据上的周期性横条纹。我实际测试下来在 CCD 的 Vdd 引脚放一个 10μF 钽电容加一个 100nF 陶瓷电容噪声改善非常明显。数字地和模拟地建议单点汇合不要让 CCD 的模拟回流路径和 STM32 的数字地混在一起。另外ΦM 这类时钟信号如果走线太长会产生振铃进而干扰 Vout。PCB 布局时尽量把 TCD1304 靠近 STM32F405Vout 走线远离时钟线。4. 驱动代码架构与关键实现4.1 代码模块怎么划分驱动代码我分成了ccd_timing.c、ccd_acq.c、ccd_app.c三个模块。ccd_timing.c只负责初始化 TIM1输出 ΦM、SH、ICG 三路波形ccd_acq.c负责 ADC 和 DMA 的配置以及帧完成回调ccd_app.c放上层逻辑比如设置积分时间、计算曝光量、处理最终数据。这样的分层对调试很友好。比如想调整曝光时间只需要改ccd_timing.c里 SH 信号的比较值完全不影响采样代码。我刚写完第一版时也想把逻辑都塞进一个文件后来发现一旦出现数据错位定位问题会非常痛苦。拆开之后每个模块都能单独测。4.2 用定时器硬件产生三路时序TIM1 输出像素时钟的配置很直接。假设系统时钟 168MHzPSC 设为 0ARR 设为 167PWM 频率就是 168MHz / (167 1) 1MHz。占空比通过 CCR1 控制我设成 84得到 50% 方波。// TIM1 产生 1MHz 像素时钟 TIM1-PSC 0; TIM1-ARR 167; // 168MHz / 168 1MHz TIM1-CCR1 84; // 50% 占空比 TIM1-CCER TIM_CCER_CC1E; // 使能 CH1 输出 TIM1-CR1 TIM_CR1_CEN; // 启动定时器SH 和 ICG 用 TIM1 的 CH2、CH3 输出比较模式实现。比较模式的好处是当计数器值到达设定比较值时引脚电平自动翻转不需要 CPU 干预。SH 的周期决定帧率SH 的脉宽决定转移窗口ICG 则要求在 SH 高电平区间的特定位置翻转一次。具体比较值要根据积分时间和期望帧率先算好我习惯做成两个变量sh_period和icg_delay方便在调试时动态修改。4.3 ADC 触发与 DMA 搬运配置像素时钟产生之后最关键的一步是让 ADC 和像素时钟同步。我采用的是 TIM1 的 CC1 事件触发 ADC 转换。TIM1 每隔一个像素时钟周期产生一次比较事件这个事件直接作为 ADC 的外部触发源保证每个像素到来时 ADC 刚好开始采样。// ADC1 由 TIM1_CC1 事件触发 ADC1-CR2 | ADC_CR2_EXTEN_RISING; // 上升沿触发 ADC1-CR2 | (0 ADC_CR2_EXTSEL_Pos); // TIM1_CC1 作为触发源DMA 用循环模式还是正常模式取决于你的帧处理逻辑。我建议用正常模式每帧启动一次 DMA 传输搬完一整行数据后触发完成中断在中断里把帧标志位置 1。这样保证一帧数据在内存里是完整的不会被下一次写入覆盖。另一个容易踩的坑是 ADC 采样时间的设置。TCD1304 输出源阻抗不算低如果采样时间设得太短ADC 内部的采样电容来不及充满采集到的幅度会偏低。我选了 28 个 ADC 时钟周期的采样时间在 1MHz 像素时钟下完全来得及而且精度比最短采样时间要稳定很多。4.4 有效像素区定位与数据校验TCD1304 的 Vout 不是一上来就是有效像素。手册上写得很清楚SH 下降沿之后会先输出一段 dummy 像素然后是有效像素区。我第一次调试时没仔细看直接把 DMA 搬回来的 3648 个点当成有效数据结果光谱图看起来像被切掉了一截。正确的做法是先确定起点再截取有效像素。我用的办法是把镜头遮住记录一整帧的暗输出波形找到波形中第一个明显跳变的位置那个位置就是有效像素区起点。对同一颗传感器来说这个偏移量在相同像素时钟下基本固定可以写成一个常量也可以在上电校准阶段自动扫描。如果输出信号中有明显异常多半是时序问题可以先检查序号 5 的排查表。5. 实测问题与排查技巧实录5.1 有波形没数据先查触发相位我遇到过一种很诡异的现象示波器看 Vout 波形完整漂亮但 DMA 搬回来的数组全是一个固定值。用逻辑分析仪一查发现 ADC 的触发时刻和像素时钟的上升沿完全错开了ADC 老是采到两个像素之间的复位干扰区采回来的全是无意义的基线。这其实就是触发相位的问题。解决办法很简单调整 ADC 的采样时刻让采样点落在像素电压稳定的区间而不是变化沿附近。我最终是把采样点设在 ΦM 的下降沿之后约 200ns效果立刻改善。碰到类似问题先别急着改代码用示波器看两三个周期的波形确定合适的采样窗口。5.2 数据毛刺和电源噪声的博弈另一块板子测试了一段时间后调高积分时间数据里开始出现明显的横向条纹。单独看单帧数据不觉得连续滚动查看时非常规律间隔固定。最终定位到是电源走线太长CCD 的 Vdd 上叠加了高频噪声。处理措施有两个一是增加去耦电容二是在 Vout 输出端加一阶 RC 滤波。RC 截止频率不要压得太低否则有效信号也会被滤掉。我用的 100Ω 电阻加 100pF 电容截止频率大约 16MHz足够滤掉高频毛刺又不会影响 1MHz 的像素信号。5.3 像素错位问题可能出在 DMA 启动时机如果一帧数据看起来还是错位的而且错位数量不固定那大概率是 DMA 启动时机和帧信号没有严格对齐。早期版本里我在 SH 上升沿的中断中启动 DMA但中断本身有延迟导致每次开始采集的点都不一样。后来我把方案改成不用中断启动 DMA而是让 DMA 事先配置好由定时器的帧信号硬件触发启动。或者干脆每次都采样一整帧长度的数据包含 dummy 像素然后在软件里做偏移对齐。后一种方法牺牲一点内存但稳定性高很多。一帧数据长度大约 3700 个 ADC 值每个值占 2 字节用不到 8KBF405 的 RAM 完全承担得起。5.4 常见问题速查表现象可能原因排查方向Vout 恒为直流无帧波形PHM、SH、ICG 时序未配对逻辑分析仪抓三路信号输出全是白电平积分时间过长CCD 饱和缩短 SH 低电平时间数据有暗条纹CCD 电源纹波加去耦电容、检查接地数据错位且不固定DMA 启动时刻不稳改用硬件触发或整帧偏移每个像素边沿有毛刺ADC 采样点在时钟跳变沿调整采样相位、加 RC 滤波图像亮度逐行衰减ICG 脉冲未正常清除电荷检查 ICG 和 SH 相位关系调试 TCD1304 这类模拟输出 CCD最大的感受就是“慢工出细活”。代码本身并不复杂复杂的是把每个环节的时序都校准到位。我最终把 ADC 触发从 SH 沿改成基于 ΦM 的硬件事件之后整帧数据稳定了非常多。最后再补一个多数人会忽略的细节TCD1304 的输出负载能力很弱不能直接驱动长线缆也不能直接并联到 ADC 引脚。我试过用杜邦线把 Vout 引到另一个板子的 ADC 上结果波形严重畸变。后来老老实实在传感器旁边放了一级运放缓冲输出阻抗降到几十欧问题才彻底解决。如果你也在调这颗 CCD先把供电、时序、缓冲放大这三件事处理干净再碰 ADC 代码都不迟。本文还有配套的精品资源点击获取
返回列表