调OV7670的一点小经验

2019-08-21 10:10发布

第一次接触STM32和摄像头,之前没有一点经验,也没有学习过STM32的开发,调起来还真是有点困难呢。
也在这里发过两个贴问了一下,最后好歹算是达到了目的。
现在姑且算是有了点小经验(虽然STM32的底层代码还是不咋会写),一点浅见拿出来分享一下,希望有点帮助。也希望各位大神多多指教。

我做的东西需要OV7670输出YUV格式的数据,然后提取U数据(没错是U不是Y)进行处理,期间遇到不少问题,不过好歹算搞定了。
OV7670的官方手册写得实在简略,很多地方没说清楚,所以我查了一些资料作为参考。 http://www.amobbs.com/thread-5490146-1-1.html http://blog.sina.com.cn/s/blog_49c617bb0100qz4m.html http://embeddedprogrammer.blogspot.com/2012/07/hacking-ov7670-camera-module-sccb-cheat.html (第三个链接已经被墙,需要翻墙看)

YUV是个啥东西这里就不用说了吧(★ ̄? ̄★)

从这儿上扒下来的摄像头例程已经写得比较完善了,只需要按照具体的引脚连接和摄像头使用需求来修改底层代码。 摄像头最关键的是配置那百十来个寄存器,我在官方手册OV7670 software application note提供的配置表的基础上稍作了些修改。说说几个重要寄存器的配置。 0x3a TSLB 这个寄存器需要注意的是第4位和第3位。 第4位是U和V的输出模式,0为常规输出,就是摄像头读到啥就是啥;1为固定输出,就是输出固定的预设值,预设值在0x67(MANU)和0x68(MANV)中设置。固定输出可以用于测试。 第3位是和0x3d(COM13)的某一位共同决定YUV422的数据顺序,后面再说。   0x12 COM7 这个寄存器是用来设置分辨率和输出格式的。因为这块开发板配的液晶屏分辨率是320x240,所以摄像头就设置成了QVGA(320x240)的,如果有需求可以设置为VGA的,当然这样需要高分辨率的液晶屏和高性能的CPU。 这个寄存器的第2位和第0位决定了输出格式,都是0时输出YUV。   0x69 GFIX 0x69用于配置 {MOD}彩通道固定增益,从高到低分别为Gr,Gb,R,B的固定增益值。这种CMOS摄像头读 {MOD}彩大致是这个样子:
R Gr R Gr R Gr R Gr R Gr R Gr...
Gr B Gr B Gr B Gr B Gr B Gr B...
为什么搞成这样我也不懂,总之R和B自然是红、蓝,而Gr、Gb都是绿,具体调的时候,Gr和Gb似乎会和R与B通道干涉,我也不太确定。

0x13 COM8
这个寄存器的低3位用来使能AGC(自动增益控制)、AWB(自动白平衡)、AEC(自动曝光调节)。其中AWB建议关掉,设置下面3个寄存器来手动调节白平衡。

0x6a GGain + 0x01 BLUE + 0x02 RED
这仨是红绿蓝的白平衡增益。这三个参数的配置直接影响到颜 {MOD}的读取,需要反复调试。具体每个增益有什么效果要自己感受,我的感受是:某一通道(例如绿)的值越大,其相关颜 {MOD}(如绿、黄)就越偏离U的中心值(0x80)。仅供参考。

0x3d COM13
这里不知道是我理解错了还是手册错了,手册上写的第1位是保留位,而第0位用于调节YUV422的数据顺序,但是具体到说明哪个值对应哪个顺序的时候又给写成了第1位。所以说设置的时候大可将第1位和第0位弄成一样的值。
0x3a TSLB-0x3d COM13
数据顺序
00 YUYV 01 YVYU 10 UYVY 11 VYUY 我的程序中使用的UYVY顺序。
这里的数据顺序,自然就是读取数据的顺序。以YUYV为例,YUYV包含两个像素,一共4字节。首先读到的是Y0,然后是U,然后是Y1,最后读到的是V。其它同理。
这里我之所以用UYVY,是因为由于不明原因,使用YUYV时读到的U极不稳定,老有Y数据溜进来干扰,于是我就换成了UYVY,这样U就是第一个读到的数据,结果数据一下子就稳定下来了。

最后附上我修改的寄存器配置
//输出配置
//{0x3a, 0x14}, //固定UV输出,用于测试
{0x3a, 0x0c}, //常规UV输出*UYVY
//{0x67, 0x11}, //U输出11H
//{0x68, 0x11}, //V输出55H
{0x40, 0xc0}, //数据范围00-FFH,常规RGB格式(for YUV)
{0x12, 0x10}, //QVGA分辨率,YUV输出

//帧同步设置
{0x32, 0x80}, //水平同步设置,不明
{0x17, 0x16}, //同上
{0x18, 0x04}, //同上
{0x19, 0x02}, //垂直同步设置,不明
{0x1a, 0x7b}, //同上
{0x03, 0x06}, //同上

{0x0c, 0x00}, //通用控制,默认00H
//{0x0c, 0x40}, //倒序输出
{0x3e, 0x00}, //分频控制,不分频
//{0x3e, 0x11}, //2分频
{0x70, 0x3a}, //不知道是啥,默认3AH
{0x71, 0x35}, //同上
{0x72, 0x11}, //DCW控制,默认11H
{0x73, 0x00}, //分频控制,因3e设为00H,此处亦取默认00H
//{0x73, 0x01}, //同2分频,则输出PCLK=12M,15 fps
{0xa2, 0x02}, //像素时钟延迟,默认02H
{0x11, 0x00}, //时钟分频控制,不分频

//图像效果控制
{0x7a, 0x20}, //Gamma控制,下同
//{0x7b, 0x1c},
//{0x7c, 0x28},
//{0x7d, 0x3c},
//{0x7e, 0x55},
//{0x7f, 0x68},
//以下为官方手册提供的数据
{0x7b, 0x10},
{0x7c, 0x1e},
{0x7d, 0x35},
{0x7e, 0x5a},
{0x7f, 0x69},

{0x80, 0x76},
{0x81, 0x80},
{0x82, 0x88},
{0x83, 0x8f},
{0x84, 0x96},
{0x85, 0xa3},
{0x86, 0xaf},
{0x87, 0xc4},
{0x88, 0xd7},
{0x89, 0xe8},

//图像优化控制
{0x13, 0xe0}, //这个寄存器将被多次赋值,原因不详

{0x01, 0x50},
{0x02, 0x68},

{0x00, 0x00}, //AGC增益
{0x10, 0x00}, //曝光度控制
{0x0d, 0x40}, //全屏
//{0x14, 0x28}, //自动增益上限8x
{0x14, 0x18}, //4x
//{0xa5, 0x05}, //不知道是啥
{0xa5, 0x07},
//{0xab, 0x07}, //同上
{0xab, 0x08},
//{0x24, 0x75}, //算法区间上限,默认75H
//{0x25, 0x63}, //算法区间下限,默认63H
{0x24, 0x95},
{0x25, 0x33},
//{0x26, 0xa5}, //简化算法上下限
{0x26, 0xe3},
{0x9f, 0x78}, //直方图算法控制
{0xa0, 0x68}, //同上
{0xa1, 0x03}, //这是保留地址,赋值意义不明
//{0xa6, 0xdf}, //直方图算法控制,下同
{0xa6, 0xd8},
//{0xa7, 0xdf},
{0xa7, 0xd8},
{0xa8, 0xf0},
{0xa9, 0x90},

{0xaa, 0x94}, //AEC算法设置
{0x13, 0xe5},
{0x0f, 0x4b}, //不知道是啥
//{0x1e, 0x01}, //镜像设置,不镜像
{0x1e, 0x07}, //black sun enable

//这些是保留地址,赋值意义不明
{0x0e, 0x61},
{0x16, 0x02},
{0x21, 0x02},
{0x22, 0x91},
{0x29, 0x07},
{0x33, 0x0b},
{0x35, 0x0b},
{0x37, 0x1d},
{0x38, 0x71},
{0x39, 0x2a},
{0x8d, 0x4f},
{0x8e, 0x00},
{0x8f, 0x00},
{0x90, 0x00},
{0x91, 0x00},
{0x96, 0x00},
{0x9a, 0x80},
{0xb0, 0x84},
{0xb2, 0x0e},
{0xb8, 0x0a},

{0x3c, 0x78}, //水平同步硬件检测控制
{0x4d, 0x40}, //这个和下一个是保留地址,赋值意义不明
{0x4e, 0x20},
//{0x69, 0x00}, //通道增益控制,全部1x
{0x69, 0x20},
{0x6b, 0x40}, //PLL控制,48MHz
//{0x74, 0x19}, //数字增益
{0x74, 0x10},

//{0x92, 0x00}, //哑线低8位
{0x92, 0x66},
{0xb1, 0x0c}, //ABLC控制
{0xb3, 0x82}, //ABLC

{0x43, 0x14}, //RSVD,下同
{0x44, 0xf0},
{0x45, 0x34},
{0x46, 0x58},
{0x47, 0x28},
{0x48, 0x3a},
{0x59, 0x88},
{0x5a, 0x88},
{0x5b, 0x44},
{0x5c, 0x67},
{0x5d, 0x49},
{0x5e, 0x0e},

//LCC控制
{0x64, 0x04},
//{0x64, 0x20},
{0x65, 0x20},
{0x66, 0x05},
{0x94, 0x04},
{0x95, 0x08},

//AWB控制
{0x6c, 0x0a},
{0x6d, 0x55},
{0x6e, 0x11},
{0x6f, 0x9f}, //9e for advance AWB,用于高像素
{0x55, 0x01},
//{0x6a, 0x40},
//{0x01, 0x40},
//{0x02, 0x40},

{0x6a, 0xfa}, //G channel
{0x01, 0x90}, //B channel
{0x02, 0x60}, //R channel

//{0x13, 0xe7},
{0x13, 0xe5}, //尝试关闭自动白平衡
{0x15, 0x00}, //同步、时钟等通用控制,默认00H

//矩阵系数控制
//saturation 0
/*******
{0x4f, 0x80},
{0x50, 0x80},
{0x51, 0x00},
{0x52, 0x22},
{0x53, 0x5e},
{0x54, 0x80},
{0x58, 0x9e},
*******/
//saturation +2
{0x4f, 0xc0},
{0x50, 0xc0},
{0x51, 0x00},
{0x52, 0x33},
{0x53, 0x8d},
{0x54, 0xc0},
{0x58, 0x9e},

{0x41, 0x08}, //通用控制,默认08H
{0x3f, 0x00}, //EDGE增强,默认00H
//{0x75, 0x05}, //EDGE下限
{0x75, 0x03},
{0x76, 0xe1}, //黑白像素校正及EDGE上限
{0x4c, 0x00}, //降噪控制
//{0x77, 0x01}, //降噪补偿
{0x77, 0x00},

//{0x3d, 0x80}, //输出YUYV
{0x3d, 0xc0}, //输出UYVY
{0x4b, 0x09}, //UV控制
//{0x4b, 0x00}, //尝试采用默认值
{0xc9, 0x60}, //同上
//{0xc9, 0xc0},
{0x41, 0x38}, //通用控制
//{0x56, 0x40}, //对比度控制
{0x56, 0x40},

{0x34, 0x11}, //RSVD
//{0x3b, 0x02}, //通用控制,使能曝光时间调节
{0x3b, 0x0a},
//{0xa4, 0x89}, //哑线调节
{0xa4, 0x88},

//RSVD
{0x96, 0x00},
{0x97, 0x30},
{0x98, 0x20},
{0x99, 0x30},
{0x9a, 0x84},
{0x9b, 0x29},
{0x9c, 0x03},

//{0x9d, 0x4c}, //50Hz滤波
{0x9d, 0x98},
{0x9e, 0x3f}, //60Hz滤波
{0x78, 0x04}, //RSVD

//以下源自官方手册,意义不明
{0x79, 0x01},
{0xc8, 0xf0},
{0x79, 0x0f},
{0xc8, 0x00},
{0x79, 0x10},
{0xc8, 0x7e},
{0x79, 0x0a},
{0xc8, 0x80},
{0x79, 0x0b},
{0xc8, 0x01},
{0x79, 0x0c},
{0xc8, 0x0f},
{0x79, 0x0d},
{0xc8, 0x20},
{0x79, 0x09},
{0xc8, 0x80},
{0x79, 0x02},
{0xc8, 0xc0},
{0x79, 0x03},
{0xc8, 0x40},
{0x79, 0x05},
{0xc8, 0x30},
{0x79, 0x26},

{0x09, 0x03}, //驱动能力最大
};

我这个配置仅供参考,因为我做的东西只需要把黄 {MOD} {MOD}块从一堆粉红 {MOD} {MOD}块中读出来,按照公式算出来的黄 {MOD}的U值比0大一点点,而粉红 {MOD}的U在100左右,理论上很容易分出来啊,但是实际似乎是因为白平衡的原因很难搞,我就把RGB那三个通道的AWB增益调的比较极端,实际图像可能已经失真了……

希望有所帮助啦~
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。