【FPGA】摄像头模块OV5640

本篇文章包含的内容

  • 一、OV5640简介
    • [1.1 基本概述](#1.1 基本概述)
    • [1.2 工作时序](#1.2 工作时序)
      • [1.2.1 DVP Timing(数据传输时序)](#1.2.1 DVP Timing(数据传输时序))
      • [1.2.2 帧曝光工作模式](#1.2.2 帧曝光工作模式)
    • [1.3 OV5640 闪光灯工作模式](#1.3 OV5640 闪光灯工作模式)
      • [1.3.1 Xenon Flash(氙灯闪烁)模式](#1.3.1 Xenon Flash(氙灯闪烁)模式)
      • [1.3.2 LED 1&2 模式](#1.3.2 LED 1&2 模式)
      • [1.3.3 LED 3模式](#1.3.3 LED 3模式)
      • [1.3.4 手动开启闪光灯](#1.3.4 手动开启闪光灯)
    • [1.4 模块硬件设计](#1.4 模块硬件设计)
  • 二、SCCB通信协议
    • [2.1 SCCB读写时序](#2.1 SCCB读写时序)
    • [2.2 SCCB与I2C的区别](#2.2 SCCB与I2C的区别)
  • 三、代码设计
    • [3.1 设置输出像素大小](#3.1 设置输出像素大小)
      • [3.1.1 设置 ISP input size(X/Y_ADDR)](#3.1.1 设置 ISP input size(X/Y_ADDR))
      • [3.1.2 设置 pre-scaling size (OFFSET)](#3.1.2 设置 pre-scaling size (OFFSET))
      • [3.1.3 设置 data output size(OUTPUT)](#3.1.3 设置 data output size(OUTPUT))
    • [3.2 读取摄像头输出像素数据](#3.2 读取摄像头输出像素数据)


一、OV5640简介

1.1 基本概述

OV5640是OV(OMNIVISION)公司设计的一款CMOS图像传感器,最高输出500万像素的图像,最高分辨篇格式为QSXVGA(2592×1944),数据接口采用DVP,控制接口为SCCB。可以输出RGB565/RGB555/RGB444、YUV(422/420)、YCbCr422和JPEG格式,可以对图像进行白平衡、饱和度、色度、锐度、Gamma曲线等调节。图像分辨率、帧率可调。

OV5640支持LED补光、MIPI(移动产业处理器接口,常用于手机摄像头)输出接口和 DVP(数字视频并行,本次实验使用这个接口) 输出接口选择、ISP(图像信号处理)以及AFC(自动聚焦控制)等功能。

控制OV5640的核心在于了解它的时序电路和数据输出之间的关系,在FPGA端需要注意以下标黄的端口,它们都是OV5640的控制输入引脚。下面对时序生成和系统逻辑控制电路的引脚作一些说明:

  • PWDN:休眠控制
  • RESETB:复位信号
  • FREX:帧曝光控制
  • GPIO[3:0]:和自动聚焦和防震动相关配置有关(本次实验没有用到)
  • PCLK:像素同步时钟(由模块板载晶振提供)
  • HREF:行同步信号,可以理解为像素数据有效信号
  • VSYNC:场同步信号
  • STROBE:闪光灯控制(连接到模块板载LED)

OV5640支持2592×1944(QSXVGA)及以下任意分辨率图像的输出。它是通过缩放和修改相关像素定位寄存器实现的。相关配置寄存器的配置地址及意义由下图给出。

  • X/Y_ADDR_ST/END:输出像素的开始位置和结束位置。
  • X/Y_OFFSET:缩放前输出场的偏移。
  • X/Y_OUTPUT_SIZE:输出像素大小。

1.2 工作时序

1.2.1 DVP Timing(数据传输时序)


上图表示了OV5640在DVP模式下的数据输出时序,同时列出了以VGA(640×480)分辨率为例的不同区间的时间。

  • tp:在RGB565格式下,一个 t p tp tp等于两倍的 t P C L K t_{PCLK} tPCLK,因为一个像素的有效数据需要两个时钟周期传输。

1.2.2 帧曝光工作模式

OV5640拥有三种曝光模式,在模式0下,帧曝光控制引脚FREQ作输入,曝光脉冲请求通过FPGA发出;在模式1下,曝光请求通过I2C(SCCB)总线发出,帧曝光控制引脚FREQ作输出,通知外部主机(FPGA)将要开始曝光,LED功能只有在模式0和模式1下才会工作;滚动曝光模式下曝光控制功能失效。

1.3 OV5640 闪光灯工作模式

在了解闪光灯信号strobe信号之前,需要注意,闪光灯的所有工作模式只在帧曝光模式0和模式1下有效。滚动帧曝光下闪光灯不工作。

1.3.1 Xenon Flash(氙灯闪烁)模式

"Xenon"是指氙灯(xenon flash)模式。氙灯是一种基于氙气放电的强光源,当电流通过充满氙气的灯管时,氙气被激发产生亮白色的光。在摄影和摄像中,氙灯闪光灯被广泛用于提供短时间内的高强度照明,以便在拍摄照片或视频时,尤其是在低光环境下,获得更好的曝光效果。在脉冲请求strobe request信号到来后,经历三帧时间,strobe pulse将输出一个极短的脉冲。该功能需要通过寄存器配置。上电后默认工作在该模式下。

1.3.2 LED 1&2 模式

LED1和LED2模式均为LED闪烁模式,在LED曝光期间的帧数据会跳过输出。该功能需要寄存器配置。

1.3.3 LED 3模式

在LED3模式下,LED灯会保持常亮。

1.3.4 手动开启闪光灯

与LED工作模式无关,我们可以通过SCCB总线发送指令修改寄存器的值,手动开启或关闭闪光灯。通过依次配置寄存器0x30160x301C0x3019Bit[1]即可打开或关闭闪光灯。



1.4 模块硬件设计

二、SCCB通信协议

SCCB(Serial Camera Control Bus,串行摄像头控制总线)是由OV(OMNIVISION)公司定义和发展的两线/三线式串行总线。该总线控制OV系列摄像头大部分的功能,包括图像数据格式、分辨篇以及图像处理参数等。两线SCCB只能实现"一主一从"的控制,而三线结构可以实现对多个从机进行控制。OV公司为了减少传感器引脚的封装,现在SCCB大多采用两线式接口总线。

  • SIO_C/SCL:只能由主机(FPGA)配置;
  • SIO_D/SDA:上面有一个三态门,可以实现双向数据传输,可以由主机控制,也可以由从机控制

SCCB协议与I2C协议十分相似,甚至在有些驱动历程中直接将I2C的驱动程序拿来使用。了解I2C协议之前有必要熟悉I2C协议的相关使用,在这里不作过多赘述。

2.1 SCCB读写时序

SCCB 总线跟 I2C 十分类似,起始信号、停止信号与 I2C 一样,SCCB 定义数据传输的基本单元为相(phase),每个相传输一个字节数据。SCCB 只包含三种传输周期:三相写周期、两相写周期和两相读周期。

  1. 三相写周期:依次为(开始信号)设备地址(写命令)、寄存器地址、数据(结束信号)。

  2. 两相写周期:依次为(开始信号)设备地址(写命令)、寄存器地址(结束信号)。

  3. 两相读周期:依次为(开始信号)设备地址(读命令)、数据(结束信号)。

在写入数据时,只执行三相写周期;在读出数据时,先执行两相写周期(虚写),再执行两相读周期。在SCCB协议中,每一相后有一个X信号,表示不需要关心这一位信号,与I2C协议不同,SCCB协议不用等待从器件响应(拉低)。两相读周期最后的NA信号表示不响应,即主机不会拉低信号线给从机发送响应。

2.2 SCCB与I2C的区别

  1. SCCB第九位为不关心位,而I2C传输协议中为应答位。
  2. SCCB每次传输不超过三相(Phase),即不能连续读写;I2C可以连续读写。
  3. SCCB读传输协议中没有重复开始的操作,在虚写完成之后,必须先发出一次停止信号,再重新发出开始信号;I2C协议中不需要在虚写之后添加停止,只需要再发送一次开始信号并执行后续操作即可。

三、代码设计

3.1 设置输出像素大小

OV5640通过SCCB总线写入对应位置寄存器设置输出像素大小。实际上I2C的驱动程序完全可以兼容SCCB通信,所以重点在于OV5640初始化工作中对寄存器值的操作,在正点原子的历程中要配置250个寄存器,还是相当繁琐的。其中首先要注意的就是输出像素大小的定义。

3.1.1 设置 ISP input size(X/Y_ADDR)

clike 复制代码
	/* 部分设置代码...... */

	// 设置感光区域的"开窗大小",开窗区域不是最终的显示区域
	8'd212:
        i2c_data <= {16'h3800,8'h00};
    8'd213:
        i2c_data <= {16'h3801,8'h00};	// 起始点x坐标 16'h0000
    8'd214:
        i2c_data <= {16'h3802,8'h00};
    8'd215:
        i2c_data <= {16'h3803,8'h04};	// 起始点x坐标 16'h0004
    8'd216:
        i2c_data <= {16'h3804,8'h0a};
    8'd217:
        i2c_data <= {16'h3805,8'h3f};	// 终止点x坐标 16'h0a3f = 16'd2623
    8'd218:
        i2c_data <= {16'h3806,8'h07};
    8'd219:
        i2c_data <= {16'h3807,8'h9b};	// 终止点y坐标 16'h079b = 16'd1947
	

3.1.2 设置 pre-scaling size (OFFSET)

clike 复制代码
	/* 部分设置代码...... */

	// 设定画幅OFFSET,在这里X_OFFSET = 0x0010 (16)
	8'd46 :
        i2c_data <= {16'h3810,8'h00}; 	// Timing Hoffset[11:8]
    8'd47 :
        i2c_data <= {16'h3811,8'h10}; 	// Timing Hoffset[7:0]
    8'd48 :
        i2c_data <= {16'h3812,8'h00}; 	// Timing Voffset[10:8]

	/* 部分设置代码...... */

	// OFFSET设置,Y_OFFSET = 0x0006(6)
	8'd228:
        i2c_data <= {16'h3813,8'h06};	 // Timing Voffset[7:0]

3.1.3 设置 data output size(OUTPUT)

OV5640内部的ISP算法可以直接将压缩前获得的图像通过算法压缩成定义的像素大小,只需要在输入中给出像素大小即可。

clike 复制代码
	/* 部分设置代码...... */

	//设置输出像素个数(ISP压缩到800*480)
    //DVP 输出水平像素点数高4位
    8'd220:
        i2c_data <= {16'h3808,{4'd0,cmos_h_pixel[11:8]}};
    //DVP 输出水平像素点数低8位
    8'd221:
        i2c_data <= {16'h3809,cmos_h_pixel[7:0]};
    //DVP 输出垂直像素点数高3位
    8'd222:
        i2c_data <= {16'h380a,{5'd0,cmos_v_pixel[10:8]}};
    //DVP 输出垂直像素点数低8位
    8'd223:
        i2c_data <= {16'h380b,cmos_v_pixel[7:0]};


    //水平总像素大小高5位
    8'd224:
        i2c_data <= {16'h380c,{3'd0,total_h_pixel[12:8]}};
    //水平总像素大小低8位
    8'd225:
        i2c_data <= {16'h380d,total_h_pixel[7:0]};
    //垂直总像素大小高5位
    8'd226:
        i2c_data <= {16'h380e,{3'd0,total_v_pixel[12:8]}};
    //垂直总像素大小低8位
    8'd227:
        i2c_data <= {16'h380f,total_v_pixel[7:0]};
         

3.2 读取摄像头输出像素数据

  • cmos_capture_data.v
clike 复制代码
module cmos_capture_data(
        input rst_n,					// 复位信号

        input cam_pclk,					// coms 数据像素同步时钟
        input cam_vsync,				// coms 场同步信号
        input cam_href,					// cmos 行有效信号(数据输出有效)
        input [7:0] cam_data,

        output cmos_frame_vsync,
        output cmos_frame_href,
        output cmos_frame_valid,
        output [15:0] cmos_frame_data
    );

    // 寄存器全部配置完成后,先等待10帧的时间
	// 待寄存器配置生效后再开始采集图像
    parameter WAIT_FRAME = 4'd10;            // 寄存器等待稳定的帧个数

    // reg define
    reg cam_vsync_d0;
    reg cam_vsync_d1;
    reg cam_href_d0;
    reg cam_href_d1;
    reg [3:0] cmos_ps_cnt;          // 等待帧数稳定计数器
    reg [7:0] cam_data_d0;
    reg [15:0] cmos_data_t;         // 8位转16位临时寄存器
    reg byte_flag;            		// 16位RGB数据转换完成的标志信号
    reg byte_flag_d0;
    reg frame_val_flag;            	// 帧有效标志

    wire pos_vsync;            		// 采输入场同步信号的上升沿

    // 采输入场同步信号的上升沿
    assign pos_vsync = (~cam_vsync_d1) & cam_vsync_d0;

    // 输出帧有效信号和行有效信号
    assign cmos_frame_vsync = frame_val_flag ? cam_vsync_d1 : 1'b0;
    assign cmos_frame_href = frame_val_flag ? cam_href_d1 : 1'b0;

    // 输出数据使能有效信号
    assign cmos_frame_valid = frame_val_flag ? byte_flag_d0 : 1'b0;

    // 输出像素数据
    assign cmos_frame_data = frame_val_flag ? cmos_data_t : 1'b0;

	// 对摄像头的场同步信号和行同步信号进行打拍获取上升沿
	always @(posedge cam_pclk or negedge rst_n) begin
        if(!rst_n) begin
            cam_vsync_d0 <= 1'b0;
            cam_vsync_d1 <= 1'b0;
            cam_href_d0 <= 1'b0;
            cam_href_d1 <= 1'b0;
        end
        else begin
            cam_vsync_d0 <= cam_vsync;
            cam_vsync_d1 <= cam_vsync_d0;
            cam_href_d0 <= cam_href;
            cam_href_d1 <= cam_href_d0;
        end
    end

    // 对帧数进行计数
    always @(posedge cam_pclk or negedge rst_n) begin
        if(!rst_n)
            cmos_ps_cnt <= 4'd0;
        else if(pos_vsync && (cmos_ps_cnt < WAIT_FRAME))
            cmos_ps_cnt <= cmos_ps_cnt + 4'd1;
    end

    // 帧有效标志
    always @(posedge cam_pclk or negedge rst_n) begin
        if(!rst_n)
            frame_val_flag <= 1'b0;
        else if((cmos_ps_cnt == WAIT_FRAME) && pos_vsync)
            frame_val_flag <= 1'b1;
    end

    // 8位数据转16位RGB565数据
    always @(posedge cam_pclk or negedge rst_n) begin
        if(!rst_n) begin
            cmos_data_t <= 16'd0;
            cam_data_d0 <= 8'd0;
            byte_flag <= 1'b0;
        end
        else if(cam_href) begin			// 当数据有效信号
            byte_flag <= ~byte_flag;
            cam_data_d0 <= cam_data;	// 打拍暂存上一个pclk的字节数据
            if(byte_flag)
                cmos_data_t <= {cam_data_d0, cam_data};
        end
        else begin
            byte_flag <= 1'b0;
            cam_data_d0 <= 8'b0;
        end
    end

    // 打一拍产生输出数据有效信号 (cmos_frame_valid)
    always @(posedge cam_pclk or negedge rst_n) begin
        if(!rst_n)
            byte_flag_d0 <= 1'b0;
        else
            byte_flag_d0 <= byte_flag;
    end

endmodule

持续不定期更新完善中......


*  原创笔记,码字不易,欢迎点赞,收藏~ 如有谬误敬请在评论区不吝告知,感激不尽!博主将持续更新有关嵌入式开发、FPGA方面的学习笔记。*


相关推荐
DS小龙哥3 小时前
基于Zynq FPGA的雷龙SD NAND存储芯片性能测试
fpga开发·sd nand·雷龙·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
上理考研周导师12 小时前
第二章 虚拟仪器及其构成原理
fpga开发
FPGA技术实战14 小时前
《探索Zynq MPSoC》学习笔记(二)
fpga开发·mpsoc
bigbig猩猩1 天前
FPGA(现场可编程门阵列)的时序分析
fpga开发
Terasic友晶科技1 天前
第2篇 使用Intel FPGA Monitor Program创建基于ARM处理器的汇编或C语言工程<二>
fpga开发·汇编语言和c语言
码农阿豪1 天前
基于Zynq FPGA对雷龙SD NAND的测试
fpga开发·sd nand·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
江山如画,佳人北望1 天前
EDA技术简介
fpga开发
淘晶驰AK1 天前
电子设计竞赛准备经历分享
嵌入式硬件·fpga开发
最好有梦想~1 天前
FPGA时序分析和约束学习笔记(4、IO传输模型)
笔记·学习·fpga开发