RTL视图
配置摄像头
本设计选用豪威科技(Omnivison)的OV5640模组
在配置OV5640 CMOS之前,需要观察该CMOS的上电时序,上电之后需要等待t3过后在复位,这段时间有标注为:大于等于1MS。复位之后,需要等待t4时间才可以开始配置。
delay from RESETB pull high to SCCB initialization 等待时间在手册里也有标记大概为20MS。
上述逻辑即满足该摄像头的上电时序。
通常我们通过SIOC,SIOD两根信号线对OV5640进行配置。配置CMOS时我们采用的协议是SCCB。SCCB是串行摄像机控制总线协议的英文名简称,相当于一个简易的I2C协议。
SIOC为时钟线 SIOD为数据线
SCCB时序
下面将列举一些我们需要配置的寄存器,其他寄存器我们就让它保持默认值即可。 下面这两组寄存器(0X3820/0X3821)是用来调整图片的镜像和翻转效果。OV5640是一款BSI图像传感器,成像光线是从芯片背面射入的,所以原始生成的图像看起来是左右相反的,故此需要对图像做镜像处理使其显示正常。
在这里关于图像尺寸大小设置的寄存器,我们需要通过20组寄存器进行配置(0X3800到0X3813)。其中输出图像尺寸需要通过0X3808,0X3809,0X380A,以及0X380B这四组寄存器进行配置。
该寄存器0X4300为配置图像输出格式,这里我们设置该寄存器的值为0X6F,即输出图像为RGB565,且输出顺序为{g[2:0],b[4:0]},(r[4:0],g[5:3])
在这里我们例化了一个I2C控制器,其真正的用途就是可以实现I2C的通信协议。我们可以通过在顶层逻辑中改变addr/wrdata/rddata的值,使底层逻辑实现对OV5640不同寄存器配置相应的数值,以实现对该摄像头的配置。
图像采集
DVP时序
Digital video port(DVP): VSYNC,帧同步信号,标志着一帧图像数据输出的开始。 HREF,行同步信号,标着着一行像素点数据输出的开始。 PCLK,DVP PCLK output,数据输出同步时钟。 D[9:2],数据输出(一个PCLK输出8位数据,而一个像素点是由16位数据组成的,故两个PCLK输出的数据拼接为一个像素点)。
本设计是需要将采集到的数据,通过以太网传输到上位机进行处理。这里我们在设计上需要处理两个问题:一是图像采集模块(PCLK/48M)和以太网数据包发送模块(GMII_CLK/125M)之间的跨时钟域数据交互,二是如何确保上位机能够正确的解析数据,因为UDP协议发包没有握手信号,所以无法保证数据是否丢失,那么如果数据丢失我们还按照原有的行顺序进行图像还原,就会造成图像失真。 故,需要对发送的数据进行编号。
左图是设计中的图像采集模块(并具有数据编号功能)。 在这个模块中,通过两个信号(VSYNC/HREF)来对data进行编号,设计中采用UDP组包发送数据,而数据长度是一行数据,故此通过href这个信号的变化,来控制vcnt的值,使vcnt成为数据的标记位(行号)。由于此设计图像的分辨率为640*480,总行数为480行,所以将vcnt设计为16位。故此解决了设计中的第二个难题,数据编号问题。
FIFO时序图
FPGA设计中常用FIFO解决跨时钟域数据交互的问题。 在FIFO的设计中,常见的信号如左图所示, 其中可以看出wrreq/rdreq为FIFO的使能信号,并且在异步FIFO的设计中,可以将wrclk/rdclk以及data width/q width设计为不同的值。 除此之外还有两个常见的信号:wrusedw/rdusedw 。wrusedw:表示已经写了多少数据 rdusedw: 表示还可以读多少数据 在FPGA的设计中常用这两个信号控制wrreq以及rdreq。
Quartus II中FIFO配置界面
对配置的fifo进行例化,wrclk连接在pclk(OC5640数据输出时钟),rdclk连接在GMII_GTXC,即将在pclk和GMII_GTXC这两个时钟域进行数据交换,也就是图像采集模块(Camera_ETH_Formator.v/pclk)和以太网数据发送模块(UDP_Send.v/GMII_GTXC)
wrreq 是FIFO中写使能信号,在本设计中的FIFO一行缓存的数据量是一帧图像中一行的像素点,而在DVP协议中HREF信号的上升沿也标志着新的一行像素点,因此选用HREF信号的跳变来触发wrreq(写使能信号)。
在设计中fifo_rdreq是FIFO中的读使能信号,而该信号的触发条件是:
数据报包头部分发送完毕&&FIFO中可读数据量大于data_length(IMAGE_WIDTH*2+2),其中data_length指FIFO中一行的数据量,但是为什么不是一行的像素点呢(IMAGE_WIDTH*2)?因为在设计中采用了两个字节的数据标记行数,因此为(IMAGE_WIDTH*2+2)。 这里解决了数据采集的一个难题,跨时域数据交互。
灰度化处理
灰度处理就是将视频图像中的RGB565,16位色的彩色图像转换成YCbCr信号,关于YCbCr是什么?简单的说:“Y”表示明亮度,也就是灰阶值;而“Cb”和“Cr”表示的则是色度。在彩色转黑白图像中,我们需要的就是这个明亮度的值Y,把颜色部分去掉,Y值越大,颜色越白,Y值越小,颜色越暗。如果输出的RGB的值都等于这个亮度Y的值,VGA显示的图像就成了黑白图像。 那如何来产生这个“亮度”Y信号呢?“亮度”Y是通过RGB输入信号来创建的,方法是将RGB信号的特定部分叠加到一起。“色度”则定义了颜色的两个方面-色调与饱和度,分别用Cr和Cb来表示。其中Cr反映了RGB输入信号红色部分与RGB信号亮度值之间的差异。而Cb反映的是RGB输入信号蓝色部分与RGB信号亮度值之间的差异。
RGB和YUV的转换公式如下:
Y=0.183R+0.614G+0.062B+16;
CB=-0.101R-0.338G+0.439B+128;
CR=0.439R-0.399G-0.040B+128;
上图是在FPGA设计中一个常见的逻辑,两个Flip flops之间插入了Algorithm。在static timing analysis中会发现Combinatorial logic是影响这个系统工作频率的关键因素。 在灰度化处理中会通过Algorithm来将RGB565转换为YCbCr,虽然只是数学上简单的加减乘除,但相对于整个系统来说,它是一个庞大的combinatorial logic。从系统的稳定性角度上去看这个问题,它可能会让整个系统存在亚稳态。从电路的工作速率看这个问题, Y=0.183R+0.614G+0.062B+16; CB=-0.101R-0.338G+0.439B+128; CR=0.439R-0.399G-0.040B+128;
每次进行这个Algorithm,系统消耗三个时钟周期。即每三个时钟,输出一组灰度值。
FPGA设计,常用Pipeline Design来提升系统工作速率。Pipeline Design就是将组合逻辑系统地分割,并在各个部分之间插入寄存器,并暂存中间数据的方法。目的是将一个大操作分解成若干的小操作每一步小操作的时间较小,所以能提高频率,各个小操作能并行执行,所以能提高数据吞吐率。 Y=0.183R+0.614G+0.062B+16; CB=-0.101R-0.338G+0.439B+128; CR=0.439R-0.399G-0.040B+128; 对这个算法进行流水线设计,第一级是进行乘法运算(verilog HDL进行浮点运算时,首先左移8位,再右移八位),第二级是进行加法运算,第三级是进行除法运算。 通过流水线设计,输出的第一组数据虽然也是需要等待三个时钟周期,但是之后的数据是每隔一个时钟周期输出一组数据。
图像边缘处理
sobel边缘处理效果图
边缘是图像最基本的特征,其在计算机视觉、图像分析等应用中起着重要的作用,这是因为图像的边缘包含了用于识别的有用信息,是图像分析和模式识别的主要特征提取手段。在图像中,“边缘”指的是临界的意思。一幅图像的临界表示为图像上亮度显著变化的地方,边缘指的是一个区域的结束,也是另一个区域的开始。“边缘点”指的是图像中具有坐标[X,y],且处在强度显著变化的位置上的点。常用的边缘检测算法大多是以原始图像灰度值为基础,通过考察图像的每个像素的某个邻域内灰度的变化,利用边缘一阶或二阶导数的规律来检测边缘。实现边缘检测有很多不同的方法,也一直是图像处理中的研究热点,人们期望找到一种抗噪强、定为准、不漏检、不误检的检测算法。其中sobel算子效果较好,边缘检测算法比较简单,实际应用中效率比canny边缘检测效果要高,但是边缘canny检测的准确,但是很多实际应用的场合,sobel边缘却是首选,尤其是对效率要求较高,而对细纹理不太关心的时候。本项目采用Sobel的算法来实现视频图像的边缘检测。
Sobel卷积因子
公式①
公式②
公式①是sobel算法的标准公式,而公式②是精简版的,可以提升效率,但是图像精度不高。因为本项目对图像精度要求极高,故选用公式①,但是在FPGA中求开方是比较困难的,所幸altera提供了一个IP核,可以供开发人员使用,如下图所示。
关于实现Sobel边缘检测还有另一个技术难点,就是如何在一帧图像中获取一个3*3的像素点矩阵,本设计中参考了crazy bingo的方法实现了3*3矩阵的获取。具体实现原理如下: Altera的cyclone IV系列存在shift_ram的IP,它正适合用于3*3像素点矩阵的提取。 首先将shiftin/shiftout的位宽设置为8位,由于在上个模块中将RGB565图像转为Yuv422,故输出图像的单位像素点的位宽为8位。 其次是需要配置taps,翻阅手册查看:
其次需要配置taps,taps指的是shift_ram已缓存的数据深度,由于在做sobel边缘处理时,需要3*3的矩阵,所以在这里设置为2,并把Great groups for each tap output勾选上,在生成的IP可视引脚里可以看到三组输出号: shiftout[7...0]/taps1x[7...0]/taps0x[7...0]。 How wide should the distance between taps be?这里需要根据项目中需要配置多大分辨率的图像配置,本设计是640*480分辨率的,故在这里配置640。
shift_ram IP配置
通过仿真可以得出3*3的像素点矩阵
RGMII通信
PHY芯片接口电路
本次设计选用的PHY芯片是KSZ9031,其数据传输接口是RGMII。RGMII(Reduced Gigabit Media Independent Interface)是Reduced GMII(吉比特介质独立接口)。RGMII均采用4位数据接口,工作时钟125MHz,并且在上升沿和下降沿同时传输数据,因此传输速率可达1000Mbps,即如果采用GMII接口(单时钟沿传输),则需要8位数据接口。之所以在UDP发送模块采用单沿传输,是因为在这种传输模式下的数据发送逻辑相对简单些,只需要考虑posedge clk,而不需要考虑negedge clk。这也是为什么需要在工程中加入GMII 2 RGMII。 在这个模块中最大的难点就是怎么由单沿转换为双沿发送? 这里,本模块运用了ALTERA公司自带的IP,DDIO_OUT。
ALTDDIO_OUT配置图
RGMII的数据宽度为4位,故width配置为4,其余只需要配置输出时钟使能,输出数据使能就OK了
DDIO_OUT timing waveform
上图为DDIO_OUT的时序图,datain_h/datain_l均在outclk的上升沿被采样(但是在FPGA芯片内部是不需要对齐数据中间位置,而仅仅需要满足setup time以及hold time即可,而芯片之间通信,无论是源同步还是系统同步,时钟都需对齐数据的中间位置),datain_h是指要在outclk这个时钟域中上升沿输出的数据,而datain_l指在outclk这个时钟域下降沿输出的数据。这里解决了单沿到双沿传输的问题! 但是还要解决一个问题,输出数据与时钟对齐的问题,即RGMII_CLK与RGMII_DATA。如上图所示,转换为双沿输出时,时钟上升沿输出D0,下降沿输出D1,而如果clock与data传输路径一致时,就会导致几乎同时到达PHY芯片,导致采样数据存在误码。大体有两种解决方案: 1.在PCB走线时,延长时钟线。 2.设置PHY芯片时,设置芯片内部延迟。本设计采用第二种方式,因为第一种设计成本偏高。
RTL 8211E PHY
RTL 88E1512 PHY
ALTDDIO仿真波形
UDP组包
以太网帧格式
UDP协议报格式
ARP请求/应答
在UDP组包这个模块的设计中,采用了FPGA系统设计的另一种常用设计方法,即状态机。由于FPGA是一种并行的系统设计,各个always模块都是随着clock同步执行的,但是某些模块总需要去顺序执行(如以太网组包/iic协议等),这时便可以选用状态机的设计方法。 本模块中大体分为几个state:IDLE(空闲状态)、SEND_HEADER(发送前导码)、SEND_DES_MAC(发送目的MAC地址)、SEND_SRC_MAC(发送源MAC地址)、SEND_FRAME_TYPE(发送帧类型)、SEND_IP_HEADER(发送IP首部)、SEND_UDP_HEADER(发送UDP首部)、SEND_DATA(发送数据包)、SEND_CRC(发送CRC校验码)。 此外在状态机的设计中,需要添加每个state的计数器,用来对该状态发送的字节数计数,用来判断是否可以进入下一个state。
上图是整个状态机中的SEND_UDP_HEADER,即发送UDP包头状态。其中就是通过case语句以及cnt_udp_header的计数来使GMII_TXD_reg发送对应的数据,最后再通过cnt_udp_header的数值来判断是否可以进入下一个状态SEND_DATA。
通过网络调试助手以及wireshark,测试FPGA逻辑部分,以及板子到上位机的链路层是否出现问题。
项目完毕,最后分享老科的一段话:Those times when you stay up late and you work hard; those times when don't feel like working — you're too tired, you don't want to push yourself — but you do it anyway. That is actually the dream. That's the dream!!!