Foreword
Ov7670由于都是拆机货,质量太差了,所以找了个替换,GC0328C,不过GC0328C的资料比较少,很多地方没解释清楚,现成代码注释写的很少,大部分都要自己重新对一遍寄存器
GC0328C
参考仓库
https://github.com/RT-Thread-Studio/sdk-bsp-stm32h750-realthread-artpi/blob/master/libraries/drivers/include/drv_gc0328c.h
https://github.com/fukuen/Maixduino_GC0328/tree/master
https://github.com/Hengbote/RT-Thread_GC0308
可以参考的板子比较多,ART-Pi,MaixPy,K210,MT62xx,MT65xx,SC6531,SP7731
多数资料里都有一个类似的图
寄存器
GC0328C的寄存器比较复杂,比较多,所以他用了一个切页的方式来切换寄存器映射
所以可以看到手册里P0:xxx
P1:xxx
就是不同页的寄存器
读取和写入的时候都需要注意当前是哪一页
地址
一般GC0328地址有2种,一个是0x21,一个是0x42,其实都指同一个
21是主地址,42是加了写偏移的,43是读偏移后的地址
时钟
Timing 这里比较复杂,基本都是参考别人的设置
总结一下需要注意P0:05-08和P0:0f-13
输出极性
输出极性,需要和配置的DMA或者DCMI,CSI等匹配上
HSYNC和VSYNC,一般设置的极性都是1有效
{0x46 , 0x03}, // SYNC_mode
在ST中这个极性和DCMI的极性设置是反过来的,需要注意一下
帧率控制
帧率主要是通过曝光时间来控制
const rt_uint8_t gc0328c_10pfs_talbe[][2] =
{
// all AEC_EXP_LEVEL_X set to 0x51e = 1310
{0xFE,0x01}, // page p1
{0x2B,0x05}, // AEC_EXP_LEVEL_0 [11:8]
{0x2C,0x1e}, // AEC_EXP_LEVEL_0 [7:0]
{0x2D,0x05}, // AEC_EXP_LEVEL_1
{0x2E,0x1e},
{0x2F,0x05}, // AEC_EXP_LEVEL_2
{0x30,0x1e},
{0x31,0x05}, // AEC_EXP_LEVEL_3
{0x32,0x1e},
//{0x33,0x25}, // // AEC_EXP_MIN
//{0x34,0x1e},
};
至于为什么1310
就是10fps,暂时我也不知道,但是基于此可以调整为20fps或者5fps
目前实测如果这里填的是512,fps大概是29-30
这里就具体计算和原因没有任何说明,只能用现有代码反推了
输出格式
输出格式主要是0x44控制,一般用的多的就是RGB565和YUV422,YUV其实就是Y Cb Y Cr
YUV444,每个分量都一样,所以没啥可说的
YUV422,实际数据返回是YUYV,两个Y分别代表了2个像素,但是他们共用了一组U和V,这种方式减少了数据量,但是会有一点点失真
YUV420,实际数据返回是YYYY...uvuvuvuv,16个Y分别代表了4个像素,但是他们共用了一组U和V,这种方式减少了数据量,失真比较厉害
还有一些其他变体
UYVY 这种等于YUYV422的变体形式
VYUY 这种等于YUYV422的变体形式
YVYU 这种等于YUYV422的变体形式
在这里YUYV 422,他们实际是用了4字节,每个量1字节,如果隔列获取的话,那么最后拿到的就全是YYYYY,直接把UV都去掉了。
如果全取就是YUYV的字节顺序
如果是RGB565,实际上是指5bitR,6bitG,5bitB,一共还是2字节的数据
同时还会选择average neighbor chroma
,会取平均色度
下面代码则是使用YUYV进行输出,并且取平均色度
{0x44 , 0x22}, // output format YUYV
输出分辨率
分辨率控制,主要是通过三个东西控制,window、crop、subsample,都会影响到最终的分辨率
Window
前面是设置行列数据的开始是什么
后面就是摄像头采样的窗口大小,下面代码中设置的大小就是0x1E8,0x288
转换过来就是488x648
,就是GC0328的像素矩阵大小
这个是由于Timing环节规定的,这个宽度一定是目标宽度+8
Crop
0x50控制的是否对窗口进行剪裁,默认0就是不剪裁,1是剪裁
x1和y1是指像素的剪裁起始点,后面的宽高则是剪裁大小,下面代码中的0x78,0xA0
对应的就是120x160
刚好是qqvga的大小
subsample
下采样模式,0x59控制下采样率,1/x,这个值越大,采样越少,代码中就是0x44
,下采样就是1/4
,默认值是1/1
就是全采,对应的就是分辨率是最大值的几分之几。qqvga就是最大的1/4
,如果是qvga则是1/2
0x5a,这个应该是采样方法,只是不知道具体是指什么
static const uint8_t qqvga_regs[][2] = {
{0xfe , 0x00}, // page 0
// window
//windowing mode
{0x09 , 0x00}, // Row start high
{0x0a , 0x00}, // Row start low
{0x0b , 0x00}, // Col start high
{0x0c , 0x00}, // Col start low
{0x0d , 0x01}, // Window height high
{0x0e , 0xe8}, // Window height low
{0x0f , 0x02}, // Window width high
{0x10 , 0x88}, // Window width low
//crop mode
{0x50 , 0x01}, // crop window mode enable
{0x51 , 0x00}, // Crop _win_y1[9:8]
{0x52 , 0x00}, // Crop _win_y1[7:0]
{0x53 , 0x00}, // Crop _win_x1[10:8]
{0x54 , 0x00}, // Crop _win_x1[7:0]
{0x55 , 0x00}, // Crop _win_height[8]
{0x56 , 0x78}, // Crop _win_height[7:0]
{0x57 , 0x00}, // Crop _win_width[9:8]
{0x58 , 0xA0}, // Crop _win_width[7:0]
//subsample 1/4
{0x59 , 0x44}, // subsample
{0x5a , 0x03}, // Sub mode
{0x5b , 0x00}, // Sub_row_N1
{0x5c , 0x00}, // Sub_row_N2
{0x5d , 0x00}, // Sub_row_N3
{0x5e , 0x00}, // Sub_row_N4
{0x5f , 0x00}, // Sub_col_N1
{0x60 , 0x00}, // Sub_col_N2
{0x61 , 0x00}, // Sub_col_N3
{0x62 , 0x00}, // Sub_col_N4
{0x00 , 0x00}
};
摄像头常用参数
翻转、镜像
画面的调整可以通过这里P0:0x17,既能镜像也能上下翻转
饱和度、对比度
一般来说饱和度通过调整d0、d1、d2
对比度通过d3、d4进行调整,唯一的问题是不知道这里的数值含义是什么
Auto Grey
Auto Grey,自动灰度,不知道具体什么意思,起什么作用
ABS AWB
ABS,不知道
AWB,自动白平衡,简单说就是纠正背景的白光可能偏黄或者偏绿
白平衡的白点设置
如果关闭了白平衡,需要通过下面的设置来控制
AEC,自动曝光控制
可以通过P1的2d-32 修改曝光时间,单位是us
ASDE
ASDE,auto saturation de-noise and edge enhancement,自饱和去噪与边缘增强,
DNDD
不明
Gamma
通过这里设置RGB的Gamma值
// largest gamma curve
{0xfe , 0x00},
{0xBF , 0x14},
{0xc0 , 0x28},
{0xc1 , 0x44},
{0xc2 , 0x5D},
{0xc3 , 0x72},
{0xc4 , 0x86},
{0xc5 , 0x95},
{0xc6 , 0xB1},
{0xc7 , 0xC6},
{0xc8 , 0xD5},
{0xc9 , 0xE1},
{0xcA , 0xEA},
{0xcB , 0xF1},
{0xcC , 0xF5},
{0xcD , 0xFB},
{0xcE , 0xFE},
{0xcF , 0xFF},
这里影响到亮度Y的Gama曲线
{0xfe , 0x00},
{0x63 , 0x00},
{0x64 , 0x10},
{0x65 , 0x1c},
{0x66 , 0x30},
{0x67 , 0x43},
{0x68 , 0x54},
{0x69 , 0x65},
{0x6a , 0x75},
{0x6b , 0x93},
{0x6c , 0xb0},
{0x6d , 0xcb},
{0x6e , 0xe6},
{0x6f , 0xff},
意外bug
K210代码里有这么个结构,然后实际上宏定义不能这样用
enum
{
GC0328_RGB_Gamma_m0 = 0,
GC0328_RGB_Gamma_m1,
GC0328_RGB_Gamma_m2,
GC0328_RGB_Gamma_m3,
GC0328_RGB_Gamma_m4,
GC0328_RGB_Gamma_m5,
GC0328_RGB_Gamma_m6,
GC0328_RGB_Gamma_night,
GC0328_RGB_Gamma_cap,
GC0328_RGB_Gamma_test
};
#define GC0328_RGB_Gamma GC0328_RGB_Gamma_test
这样定义出来的宏,在后续使用的时候无法确定具体宏的数值,所以宏判等等情况都会失败
#if (GC0328_Y_Gamma == GC0328_Y_Gamma_05)
//0.5
{0xfe , 0x00}, //select page0
{0x63 , 0x00}, // Y Gamma 0
{0x64 , 0x49}, // Y Gamma 1
{0x65 , 0x68}, // Y Gamma 2
{0x66 , 0x80}, // ...
#elif (GC0328_Y_Gamma == GC0328_Y_Gamma_06)
//0.5
{0xfe , 0x00}, //select page0
{0x63 , 0x00}, // Y Gamma 0
{0x64 , 0x49}, // Y Gamma 1
{0x65 , 0x68}, // Y Gamma 2
{0x66 , 0x80}, // ...
Summary
移植驱动主要就注意这几点,剩下大量的寄存器配置都是未知的,只能照搬
Quote
https://club.rt-thread.org/ask/question/7e46e3d50a1b6ab5.html
https://github.com/RT-Thread-Studio/sdk-bsp-stm32h750-realthread-artpi/blob/master/libraries/drivers/include/drv_gc0328c.h
https://github.com/fukuen/Maixduino_GC0328/tree/master
https://github.com/Hengbote/RT-Thread_GC0308
https://blog.csdn.net/rjszcb/article/details/118728264