从零构建:基于Altera EP2C8Q208的CMV4000图像采集系统实战
最近在做一个机器视觉的项目,客户对图像质量和帧率都有比较高的要求,选型时看中了CMOSIS的CMV4000传感器。这颗传感器2048x2048的分辨率,配合全局快门和LVDS高速接口,确实很适合工业检测这类场景。不过在实际调试过程中,我发现网上关于具体驱动实现的资料比较零散,特别是用老一点的Cyclone II系列FPGA来做的案例更少。今天我就把自己用Altera EP2C8Q208驱动CMV4000的完整过程整理出来,包括硬件连接、VHDL代码编写、时序调试中的那些坑,希望能给正在做类似项目的朋友一些参考。
1. 硬件平台搭建与信号连接
1.1 核心器件选型与特性分析
我选择的FPGA是Altera Cyclone II系列的EP2C8Q208,这款芯片虽然现在看来资源不算丰富,但对于驱动CMV4000来说完全够用。它有8256个逻辑单元,36个M4K RAM块,最大用户I/O引脚182个。更重要的是,它支持LVDS接口,这对于处理CMV4000的高速数据流至关重要。
CMV4000是CMOSIS(现已被ams收购)的一款400万像素全局快门传感器,其主要参数如下:
| 参数 | 规格 | 备注 |
|---|---|---|
| 分辨率 | 2048(H) × 2048(V) | 正方形像素阵列 |
| 像素尺寸 | 5.5μm × 5.5μm | |
| 光学格式 | 1英寸 | |
| ADC精度 | 12位 | 也可配置为10位模式 |
| 最大帧率 | 180 fps (10位) / 37 fps (12位) | 理论值,实际受接口限制 |
| 输出接口 | 16通道LVDS | 可配置为4、8、16通道模式 |
| 数据率 | 最高480 Mbps/通道 | |
| 快门类型 | 流水线全局快门 | 带真正相关双采样 |
注意:CMV4000需要3.3V的I/O电压和1.8V的核心电压,上电时序有严格要求,如果顺序错误可能导致传感器永久损坏。
1.2 关键信号连接方案
EP2C8Q208有4个I/O Bank,我将Bank 1专门用于LVDS接口,其他Bank用于常规的LVTTL信号。这样的分区设计可以避免不同电压标准的信号相互干扰。
CMV4000与FPGA的主要连接信号包括:
-
时钟与控制信号
CMV_CLK:20MHz主时钟输入,由FPGA的PLL产生CMV_RESETn:低电平有效的硬件复位信号FVAL:帧有效信号(输出)LVAL:行有效信号(输出)DVAL:数据有效信号(输出)
-
SPI配置接口
SPI_EN:片选信号,低电平有效SPI_CLK:时钟信号,最高20MHzSPI_MOSI:主出从入数据线SPI_MISO:主入从出数据线
-
LVDS数据接口(4通道模式配置)
LVDS_CLK_P/N:差分时钟输入CH0_P/N~CH3_P/N:4个数据通道的差分对
在实际布线时,LVDS差分对需要严格保持等长,长度差控制在5mil以内。我使用的是4层PCB板,将LVDS信号走在内层,两侧用地平面屏蔽,有效减少了信号完整性问题。
-- 在Quartus II中设置LVDS引脚约束的示例
set_instance_assignment -name IO_STANDARD "LVDS" -to LVDS_CLK_P
set_instance_assignment -name IO_STANDARD "LVDS" -to LVDS_CLK_N
set_instance_assignment -name IO_STANDARD "LVDS" -to CH0_P
set_instance_assignment -name IO_STANDARD "LVDS" -to CH0_N
-- ... 其他通道类似
-- 设置差分对
set_instance_assignment -name INPUT_TERMINATION "PARALLEL 100 OHM WITH CALIBRATION" -to LVDS_CLK_P
set_instance_assignment -name OUTPUT_TERMINATION "SERIES 50 OHM WITH CALIBRATION" -to CH0_P
2. VHDL驱动代码设计与实现
2.1 SPI配置模块的编写
CMV4000上电后需要通过SPI接口配置内部寄存器才能正常工作。我设计了一个通用的SPI主控制器,可以灵活配置时钟分频、数据位宽等参数。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity spi_master is
Generic (
CLK_DIV : integer := 10; -- 系统时钟分频,产生SPI时钟
DATA_WIDTH : integer := 16
);
Port (
clk : in STD_LOGIC;
reset : in STD_LOGIC;
-- 用户接口
start : in STD_LOGIC;
data_to_send : in STD_LOGIC_VECTOR(DATA_WIDTH-1 downto 0);
data_received : out STD_LOGIC_VECTOR(DATA_WIDTH-1 downto 0);
busy : out STD_LOGIC;
-- SPI物理接口
spi_clk : out STD_LOGIC;
spi_mosi : out STD_LOGIC;
spi_miso : in STD_LOGIC;
spi_cs_n : out STD_LOGIC
);
end spi_master;
architecture Behavioral of spi_master is
type state_type is (IDLE, START_BIT, DATA_BITS, STOP_BIT);
signal state : state_type := IDLE;
signal clk_counter : integer range 0 to CLK_DIV-1 := 0;
signal bit_counter : integer range 0 to DATA_WIDTH-1 := 0;
signal shift_reg : STD_LOGIC_VECTOR(DATA_WIDTH-1 downto 0);
signal spi_clk_internal : STD_LOGIC := '0';
begin
-- SPI时钟生成进程
process(clk)
begin
if rising_edge(clk) then
if reset = '1' then
clk_counter <= 0;
spi_clk_internal <= '0';
else
if clk_counter = CLK_DIV-1 then
clk_counter <= 0;
spi_clk_internal <= not spi_clk_internal;
else
clk_counter <= clk_counter + 1;
end if;
end if;
end if;
end process;
-- 主状态机
process(clk)
begin
if rising_edge(clk) then
if reset = '1' then
state <= IDLE;

&spm=1001.2101.3001.5002&articleId=151274110&d=1&t=3&u=243d577cf6bb4836ac22c7682594bb6e)

被折叠的 条评论
为什么被折叠?



