最近做项目需要用到OV7725,于是花了些时间研究。由于OV7725对于工作频率的要求较高,因此使用带FIFO的摄像头模块。代码参考自正点原子官方。
- T6 m) s- J, o& m4 x1 J, K* ^: d# K* E3 B5 |+ L, S" C
% o( ]( F$ `1 u' j) S) U1 E ?1 H
OV7725资源0 C; v' }3 [& [- s
引脚说明
- f/ s) B% E: H* J8 G以下时关于十八个引脚的说明(来自正点原子的用户手册)9 R b% q$ l0 `4 v( x1 b* G/ d9 X
) n% \' s. h+ t4 X
2 j p s+ I4 Y4 L: w
' j* K3 S9 Y* w" b1 q, \其中,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片选信号在初始化之后就拉低,使之有效。
$ A( k$ `; p w6 X4 U5 T. l" f: y
- r/ R% J3 v1 b3 B1 o寄存器
# W9 F% m, m, B W' D这是来自datasheet的寄存器描述,看手册,英文很重要:) N. v+ d5 y* f+ ]8 J
- t. q. w+ k8 n9 Z8 t
6 x v+ S. M0 N5 J5 O5 n0 w' s
1 W9 `, B% k, S2 ?6 U" A4 N这些寄存器在代码里表现为一堆宏定义- /*OV7725寄存器宏定义*/
7 Z1 o8 e7 u/ p) M' P - #define GAIN 0x00
4 u/ ?4 ~( ]# D; W- I% u - #define BLUE 0x018 O- `! w( b! x. R* g; s; L
- #define RED 0x02
) [3 J4 W d. \* f; z - #define GREEN 0x03( J! W1 j. e* f; }9 d; J/ e
- #define BAVG 0x058 R1 F/ C/ h/ Q: E0 X- U; i
- #define GAVG 0x06
( N* j4 i6 S1 i# g/ n# p; P - …………
) V2 D8 y& r" k- G
复制代码
/ F: P0 s! m7 u& Q8 L模块初始化
( u. o* \/ z6 B% {引脚定义2 s* W& c8 e& J6 Q8 {7 x. e
- //D0~D7:PA0~PA7 VSYNC:PA84 n5 O; p( c. r3 L/ _* X
- //WRST:PB13 RRST:PB14 OE:PB5 RCLK:PB6 WEN:PB7 SDA:PB8 SCL:PB9
" ~" a- V9 [4 u3 `7 p4 @0 t. l - #define OV7725_VSYNC PAin(8) $ e+ }( \$ f) n
- #define OV7725_WRST PBout(13)
# Q5 G/ K: U% Z/ q" r - #define OV7725_WREN PBout(7)
2 ^. B& M+ H X6 O' P* n8 j, T* b - #define OV7725_RCK_H GPIOB->BSRR=1<<6
+ g$ p6 w$ h2 b9 C/ P$ k( } - #define OV7725_RCK_L GPIOB->BRR=1<<61 T4 s7 Q' D# d0 d3 o: V# k' L5 `
- #define OV7725_RRST PBout(14)
3 ?( m: N k8 r0 S! G1 L - #define OV7725_CS PBout(5)
- ?; k; ^0 Z, T) X% O* Q4 J
复制代码- #define SCCB_SCL PBout(9) //SCL
9 ?4 g! Q8 B" X; Y+ _4 ?: A: j - #define SCCB_SDA PBout(8) //SDA [, b# H! H& f' y& I X! u! H# [
- # }. g: q* |/ B. S G( w2 m
- #define SCCB_READ_SDA PBin(8) //ÊäÈëSDA 4 U+ i! p7 [5 S- G9 e0 i, Q
复制代码 ( }/ I; ~" {! n) d" V5 a6 y, [
, n) G: `; l/ s* |# P$ L b/ e我使用的是杜邦线连接如,IO口是自己重新定义的(全使用普通IO即可),STM32F1系列的PB3,PB4,PA13,PA14,PA15用于JTAG,如果有使用到其中的引脚,需要进行特殊配置,具体可参考网上更多教程。当时使用PB3,PB4作为SCCB的引脚,出现问题才发现这个原因。
. v- L' p7 ?* D( ^! [' c% G5 b. j" \1 e# X
初始化" N0 X* V& z/ d: @. m5 i) O4 B4 W1 n
写寄存器的函数,寄存器的配置都是通过这个函数$ r* U- \0 N9 ^
- u8 SCCB_WR_Reg(u8 reg,u8 data)
. d h! t0 r: F; M8 S7 p - {9 P1 ?) M- u, E3 G( b* V3 b
- u8 res=0;
, n& I( [+ f5 d5 C6 H f4 { - SCCB_Start(); //启动SCCB传输
: C. D3 V% s+ i; a& e+ @ - if(SCCB_WR_Byte(SCCB_ID))res=1; //写器件ID,为0x42
- D1 Q1 {: Q' t1 I6 i6 U - delay_us(100);
) h4 l& e+ e( C - if(SCCB_WR_Byte(reg))res=1; //写寄存器地址,直接写对应的寄存器地址" @+ J% Y* ?& t' d Z2 d B
- delay_us(100);% |1 M/ d. N. u& s9 k6 u/ ?+ p! Z
- if(SCCB_WR_Byte(data))res=1; //写数据,根据寄存器功能写对应数据: }+ l6 L$ r- t' o/ U# X
- SCCB_Stop(); 8 W( N$ a ]) k8 H( ?) [7 j! A
- return res;& |9 Z& n& w! V! Z' g& \; d
- } & }) H# J! B, I+ L/ B# B& p; Z
复制代码
7 H; C/ ~/ b! ?, G. H将要配置的寄存器地址和对应的值,存在一个二维数组里边/ B" l U! H1 h8 `7 h! H0 G
- //初始化寄存器系列及其对应的值
2 X; \7 A3 `( P4 ?8 P! J - const u8 ov7725_init_reg_tb1[][2]=
% ~/ G, P" Q2 h) D1 v/ o* V: e - {
+ n3 `: F1 Z* x& o6 ?; ~5 ^- T/ a - /*输出窗口设置*/
2 G6 Z7 A% ^5 c1 x$ U - {CLKRC, 0x00}, //clock config0 Q0 p$ H8 i* d) c* ^; q# _/ p
- {COM7, 0x06}, //VGA RGB565
& o+ @3 |' b2 O3 o) D - {HSTART, 0x3f}, //水平起始位置0 x7 ]. ^' U3 h. C1 \
- {HSIZE, 0x50}, //水平尺寸4 t- a- t! T$ X3 }6 _& X7 P* U
- {VSTRT, 0x03}, //垂直其实位置
. Q- p* R& W8 X, a5 H - ......; ?9 U" g& J" M7 N
复制代码 2 `7 F7 D {/ O- x
初始化时,在循环语句调用写寄存器的函数,依次写入寄存器的地址和对应的值,即可完成寄存器的配置
. R3 ]' n1 P0 \/ X& H2 r3 ]% |- u- //初始化OV7725,采用QVGA分辨率(320*240)
_6 [- S" a: l5 l) t C3 Z [; i - for(i=0;i<sizeof(ov7725_init_reg_tb1)/sizeof(ov7725_init_reg_tb1[0]);i++)" q* y1 e9 R( s6 K8 S" l
- {
, S @) X- i/ D; f0 E; d: `2 @$ i - SCCB_WR_Reg(ov7725_init_reg_tb1[i][0],ov7725_init_reg_tb1[i][1]);
6 X' R1 e' ?$ g* y - }
8 u0 Q% U# F8 S( F7 V2 i; A
复制代码
4 N, i( N& O9 a! x, k2 P) v; c帧中断和数据处理* F, i% W% k3 P/ y
发生帧中断时
- J# R# N& h" C8 i, I- u8 ov_sta; //帧中断标记2 }7 v" C4 k5 h" v5 I1 {
- //中断处理函数. q$ }5 u! t6 h5 I
- void EXTI9_5_IRQHandler(void)
3 J& k- ^; `+ ^ - { * V1 f" U" [% _/ X5 d3 S
- if(EXTI_GetITStatus(EXTI_Line8)==SET)//是8线的中断
/ [: A9 D# S& L7 w* Q6 @ - { * i1 u' ~7 ^. A
- if(ov_sta==0)9 ^0 {* a: p# w5 ]/ h4 f) k
- {
! J( k( _. N% h \5 I5 {8 q - OV7725_WRST=0; //复位写指针 - d Z3 r5 l& K% }% U! P' H: I/ {
- OV7725_WRST=1;
% }8 U' \( r- V# O! | - OV7725_WREN=1; //允许写入FIFO
( X9 v9 p6 ]/ _9 q' ~9 ^ - ov_sta++; //帧中断加1# q1 _; _! |+ o5 w
- }else OV7725_WREN=0;//禁止写入FIFO
$ J) Q# Q2 [# B - }' o6 I; L8 M( L9 d9 l7 q
- EXTI_ClearITPendingBit(EXTI_Line8); //清除中断标志位
1 U0 l3 i+ ]/ d) n2 Q - } # v# k) c4 F5 R: w2 p
复制代码 4 p+ X/ B% c) \/ ~
数据处理9 ~) l r, [3 H( V. A, t
- if(ov_sta)//判断是否有帧中断发生$ g4 K0 `4 O: ?! v
- {! o1 K1 m% r- H
- OV7725_RRST=0; //复. i& W! I- c* ~& S Z; A
- OV7725_RCK_L; //位* g W. j/ e7 Y3 Z) M
- OV7725_RCK_H; //读
' @. b- M+ k: T - OV7725_RCK_L; //指( }5 V; a: `: B8 Q
- OV7725_RRST=1; //针
+ @+ e9 c7 u) Z: ?1 h8 p4 {8 ^1 a - OV7725_RCK_H;
( X8 K6 y2 D9 s- N1 @, z, u - for(i=0;i<OV7725_WINDOW_HEIGHT;i++)//循环语句读取FIFO的数据( t5 x" }5 p. K" t, J
- {3 I$ G& C3 S# U0 w1 u
- for(j=0;j<OV7725_WINDOW_WIDTH;j++)
. _8 E) R; W* I3 V* O - {
& ~ ` c: C C/ [* x - //将数据填入相应缓冲区
0 i& K. S& s+ {1 J/ | - ......
# b. a7 O3 _3 W- T3 j - }. S/ v) p& Q1 L _1 V3 L D
- }+ Q( y6 m$ Y5 f+ a# P" q
- ov_sta=0; //清零帧中断标志
+ S1 R- }: A4 `$ ~) ^+ h) Z' X - }& D% N u2 W% W3 K6 g/ |' h
复制代码
& h4 Q. l! L0 A, H, r) X7 t3 P以上是对于OV7725的初步理解,还有很多不懂的地方,后续有进一步研究再补充. d- W8 L! ~! y* }$ U1 P
————————————————
, p' k. j/ E# h8 V7 }; D4 K版权声明:hu_hong_hao
7 {9 L9 {# p# @( {如有侵权请联系删除
( [7 M" l0 ?+ U( o# d! \# J2 R# h& g T5 Y! \/ Z9 L$ N
; l6 v( [8 v" w! ^5 h0 A
* @; B+ H/ B) u2 {* H1 ~ |