最近做项目需要用到OV7725,于是花了些时间研究。由于OV7725对于工作频率的要求较高,因此使用带FIFO的摄像头模块。代码参考自正点原子官方。! S" ^# H4 X5 F9 W3 d6 L$ V
3 `# P0 ^/ Z q' _, S
' S7 n0 {' h: S& ?OV7725资源# u, [% L1 k* B$ R$ P2 n, a
引脚说明
" |1 @9 }; [1 g1 |以下时关于十八个引脚的说明(来自正点原子的用户手册)
4 m( N- X) L) T) e# x Z# u9 ` h0 _$ `, s. z7 w
0 r! d% P; ?& I. D2 g# h2 h8 k |
* T- d; E" N1 O* [) I% a r其中,OV_SCL和OV_SDA组成SCCB,即串行摄像头控制总线,它的通信机制类似于IIC,有使用过IIC总线的较容易理解。OV_VSYNC为帧同步信号,该引脚产生信号时,意味着一帧数据传输完成后,下一帧数据传输即将开始(这里,从OV7725传输至FIFO)。FIFO_D[7:0] 是FIFO的数据引脚,用于数据传输。当发生帧同步信号时,复位FIFO_WRST写指针信号,使能FIFO_WEN写使能信号,则OV7725开始往FIFO写数据,并置位帧中断标志位(该标志位在读数据完成后被清零),保证FIFO的数据被读取完成之前不会被覆盖,具体见下面的中断服务程序。帧中断标志位有效时,就复位RIFO_RRST读指针信号,然后在每个FIFO_RCLK读时钟信号,使用STM32去读取FIFO_D[7:0] 的数据。FIFO_OE片选信号在初始化之后就拉低,使之有效。
' |( c& j4 h4 @3 d: N5 t3 h; m! j2 n. [# U+ s5 k! t
寄存器
( r% j; n! |3 p- [6 R这是来自datasheet的寄存器描述,看手册,英文很重要:
; {+ r' J5 N5 C, ^2 m6 y/ O! w
9 w7 `" g8 X$ a, n
8 n% I2 ?1 O: `7 T7 a3 |
2 k! ~1 K0 `2 s7 z/ O2 X
这些寄存器在代码里表现为一堆宏定义- /*OV7725寄存器宏定义*/
$ M& t- K7 {$ N/ ]+ a, V! H4 q+ ` - #define GAIN 0x00
8 R) ]/ m- y4 i% t: S - #define BLUE 0x01/ @, m4 c/ [5 |! Z% t# B" @8 x, v
- #define RED 0x02/ E; {- s, b, Q$ s
- #define GREEN 0x03
2 x6 H# L8 I5 j" t8 L - #define BAVG 0x05
: Z3 P6 s) b7 a$ v) |& v - #define GAVG 0x06; K& N3 a4 x4 L2 _1 }, v, e
- …………
- f+ o( Q* D. E) ]/ C- u) i
复制代码
! E& ~8 i( q' X& ]' F" ^模块初始化
6 o. \' S8 k* z" H& {6 d引脚定义
7 o5 E7 C9 ] `+ Z- //D0~D7:PA0~PA7 VSYNC:PA80 T* ~' R6 d8 X2 n' {5 E: D
- //WRST:PB13 RRST:PB14 OE:PB5 RCLK:PB6 WEN:PB7 SDA:PB8 SCL:PB9 5 G4 p4 z; k) H) s0 M2 V7 n
- #define OV7725_VSYNC PAin(8) & \) U+ [+ S: Z3 S( }& Q! t+ D2 I
- #define OV7725_WRST PBout(13)
7 }* |* I1 o/ }" q1 c' [ - #define OV7725_WREN PBout(7) 4 u [* V8 ]% S4 `- f$ ^
- #define OV7725_RCK_H GPIOB->BSRR=1<<61 m+ i& S# ^/ g# y
- #define OV7725_RCK_L GPIOB->BRR=1<<6
: L/ |* z% P0 B4 V9 K' _ - #define OV7725_RRST PBout(14)
2 Q/ v2 p2 w- u7 N - #define OV7725_CS PBout(5)
3 v/ v! D1 [5 w& p
复制代码- #define SCCB_SCL PBout(9) //SCL
' Y" i2 Y9 c% d - #define SCCB_SDA PBout(8) //SDA
P5 p7 M+ r d' ^+ @- I
+ ^, s( U' |4 V, C8 |! ?# x- #define SCCB_READ_SDA PBin(8) //ÊäÈëSDA
1 Z- U5 I+ A5 O( r0 j
复制代码 / e7 |2 K* Q+ p, f
3 r% V0 r/ O1 P: R4 A2 _我使用的是杜邦线连接如,IO口是自己重新定义的(全使用普通IO即可),STM32F1系列的PB3,PB4,PA13,PA14,PA15用于JTAG,如果有使用到其中的引脚,需要进行特殊配置,具体可参考网上更多教程。当时使用PB3,PB4作为SCCB的引脚,出现问题才发现这个原因。4 l- K% z' I, n" Y; @
+ E, M" @/ y8 z) v+ s0 {
初始化+ R, r: y" h) b+ ]
写寄存器的函数,寄存器的配置都是通过这个函数, q3 w; m8 P; K" H
- u8 SCCB_WR_Reg(u8 reg,u8 data)
1 m6 l% i* L, R. Z( |4 Z+ P5 J9 i - {# a8 `8 ^' J; f9 C0 N" d `5 Y, P5 c
- u8 res=0;
/ J3 Q! M' n: x - SCCB_Start(); //启动SCCB传输
6 |0 k- ~* U+ P1 J; u& D: L- E% o2 u - if(SCCB_WR_Byte(SCCB_ID))res=1; //写器件ID,为0x42% D! R( B+ l4 q
- delay_us(100);
- s6 K6 p) Q0 [- J1 L4 O - if(SCCB_WR_Byte(reg))res=1; //写寄存器地址,直接写对应的寄存器地址7 o6 X* \0 D7 W ^8 _& a! x- r
- delay_us(100);. K) U" ]" a1 G! g* [ h4 ~
- if(SCCB_WR_Byte(data))res=1; //写数据,根据寄存器功能写对应数据( j7 k% z* H8 _
- SCCB_Stop();
- U5 b+ n* C" J3 |0 F7 }2 T4 D - return res;
. f1 d* |4 C v; m+ U! c x. H& l - } 5 G% Y' o, a1 t8 F5 g: a
复制代码
- b N+ e) I L8 Y$ L4 l将要配置的寄存器地址和对应的值,存在一个二维数组里边& j; ^; L% M& H2 {
- //初始化寄存器系列及其对应的值/ e, u, s8 f v
- const u8 ov7725_init_reg_tb1[][2]=
8 q& {9 ?* |0 j - { * {: o' \3 P+ y- r9 _+ i
- /*输出窗口设置*/
& M6 [( j+ v: h. h - {CLKRC, 0x00}, //clock config
2 o. m" R0 ]$ a; t, ? {. z- k - {COM7, 0x06}, //VGA RGB565
8 e& \1 a' }: O - {HSTART, 0x3f}, //水平起始位置
' u, o S/ `* p {: L% K - {HSIZE, 0x50}, //水平尺寸6 Q ]3 u3 b; z
- {VSTRT, 0x03}, //垂直其实位置
" ~' ^; |, h8 C- W* d. M; i - ......
: o {. y4 d8 r: `
复制代码
" Y$ Q; s: [# H N+ u$ A1 t初始化时,在循环语句调用写寄存器的函数,依次写入寄存器的地址和对应的值,即可完成寄存器的配置/ @+ N3 ~& \! x6 O
- //初始化OV7725,采用QVGA分辨率(320*240)
6 I4 @; c0 s2 z* P: A, }+ E - for(i=0;i<sizeof(ov7725_init_reg_tb1)/sizeof(ov7725_init_reg_tb1[0]);i++)
# |6 R9 v% P# ?/ H - { & s8 b/ M9 x2 h% E6 I& k2 l' f
- SCCB_WR_Reg(ov7725_init_reg_tb1[i][0],ov7725_init_reg_tb1[i][1]);1 H& U. ]+ g/ z; h% R
- } ' @; \4 e6 [( k5 w" W* `+ r
复制代码
9 f- H0 X" @( \+ U3 m3 k帧中断和数据处理
, m0 M3 Q6 o. i% N$ o; [发生帧中断时
4 Y }! \3 V8 e0 _: r- u8 ov_sta; //帧中断标记. G' C* M& h! h" j% v3 F! F* q
- //中断处理函数
; [( g. ^; f7 }* M# X8 y7 q - void EXTI9_5_IRQHandler(void)4 a% |& { E* W
- { 7 V) t2 ~2 m. {0 F$ D- Y' Z! o$ E
- if(EXTI_GetITStatus(EXTI_Line8)==SET)//是8线的中断
/ G1 V$ h9 ]( ]# b, E+ [1 E - { % p0 P3 \4 Z4 N* Q5 i! l$ J* x
- if(ov_sta==0)/ E: \" h2 {" G% I4 O
- {
4 v/ H( K2 T- h3 ? - OV7725_WRST=0; //复位写指针 E5 p; K# i$ n8 |. x2 D( m
- OV7725_WRST=1; # \7 n& ^ X$ l, S
- OV7725_WREN=1; //允许写入FIFO : w) D( K e! _/ m3 D, W1 S
- ov_sta++; //帧中断加1
3 \* c: e# r. K% q; { - }else OV7725_WREN=0;//禁止写入FIFO' U+ M5 V0 c: `; H' l* ^3 b8 e
- }& p2 d: X( \) N& f1 X7 I! ^
- EXTI_ClearITPendingBit(EXTI_Line8); //清除中断标志位
; ^5 P9 ^, o* S - } ( I. ]1 G4 [% A/ i0 J! B5 o6 \
复制代码 5 A+ g: V3 M$ F7 w( [$ `2 p3 q- a
数据处理& k2 q" ` \0 \" X: e. `: v1 N
- if(ov_sta)//判断是否有帧中断发生
U, w+ }- }* b! L - { G$ X7 i$ n- Z) [* h5 K! x
- OV7725_RRST=0; //复
$ M0 i( ^0 b: ~$ h - OV7725_RCK_L; //位
% Y; g2 k: l$ ~0 b+ W- f: u - OV7725_RCK_H; //读' b9 a' ^6 Y W
- OV7725_RCK_L; //指8 a3 P# ~( L: b* S: s( D+ U
- OV7725_RRST=1; //针
0 D' I5 Z$ D; b - OV7725_RCK_H; 0 v2 b2 H6 E$ D0 j$ Q
- for(i=0;i<OV7725_WINDOW_HEIGHT;i++)//循环语句读取FIFO的数据' m8 d7 s9 w1 M2 ?. V( v1 j3 z
- { v# [+ c: Q% i0 {0 w. F$ T5 A
- for(j=0;j<OV7725_WINDOW_WIDTH;j++) f: t3 Y; g* i' i
- {
9 y+ ~* j: v+ O* E1 F0 `2 T/ z/ d - //将数据填入相应缓冲区5 S6 d: m2 `5 F: e7 y
- ......7 B! d1 P. x. ~; Q7 U# x2 D+ d5 P
- }8 j |+ q# u1 r: o" X
- }
& ~# {. | b9 f1 `, p/ Q% ^ - ov_sta=0; //清零帧中断标志8 v9 q% ^4 F8 I
- }
2 C1 X* a' C; Q5 E
复制代码
' s- s7 T! y& u4 \& P, c w以上是对于OV7725的初步理解,还有很多不懂的地方,后续有进一步研究再补充
+ ~$ ]+ s# \- F2 U* p9 |. A————————————————
. Y8 M1 M0 @8 _ y版权声明:hu_hong_hao
7 u7 X3 v0 S5 n2 K如有侵权请联系删除
# @5 \9 e" N8 R m/ n8 _. J$ d' q8 _* U3 R9 k8 [5 D' c5 \
) Y+ r+ e2 s6 F* G( _
( z; C* H2 O$ ?2 O: S2 y |