最近做项目需要用到OV7725,于是花了些时间研究。由于OV7725对于工作频率的要求较高,因此使用带FIFO的摄像头模块。代码参考自正点原子官方。- V0 L3 p( z7 f7 |9 [0 W( R
n4 J' u) l: m" T% x+ ~$ e+ N2 p& M
OV7725资源. D: I! `# G: {9 f1 O4 h' s
引脚说明
B1 q+ M8 E( z3 t$ ~以下时关于十八个引脚的说明(来自正点原子的用户手册)
" g3 t. Q; O: j9 S$ J" k# [: A/ s7 p& a8 w+ j( z$ V
# S/ H* B3 l' o4 E8 ?* v
6 w/ m, ?# P' ^, M3 }
其中,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片选信号在初始化之后就拉低,使之有效。
/ \; |) i" Q8 M: X$ `
' B$ ?- F) ?; o+ i& H寄存器
! V. D4 C9 f1 k n, ]/ t8 {) J这是来自datasheet的寄存器描述,看手册,英文很重要:- \9 Q+ @: O; F6 Q+ [/ Z: @
) K* s4 l' Z9 W! ~
6 U e2 w0 B1 O8 H, I
. z% N+ Q1 y+ S- |5 W这些寄存器在代码里表现为一堆宏定义- /*OV7725寄存器宏定义*/
; O9 V7 P2 q6 R3 F - #define GAIN 0x006 x9 f% J' _5 ~ x; g
- #define BLUE 0x01
6 s+ |6 ~4 m' _8 C! K - #define RED 0x02
* Q1 s3 _: ^- C Y+ g - #define GREEN 0x03
* a4 r! I: s* M) v- | - #define BAVG 0x059 S0 `& {0 b' r7 q) G9 z2 h6 @/ f
- #define GAVG 0x06& o( l8 T0 }8 a$ E, A0 j' |
- …………
+ Y, }4 ]! P; n \0 [, S
复制代码
" Q" V" _6 K2 K& T- E3 i6 M模块初始化
* K" z" |$ q# V A引脚定义
0 V1 A$ a3 l# u9 a/ J; W- //D0~D7:PA0~PA7 VSYNC:PA8! E( X' ^& B" ~9 W* ]* @- y; ]
- //WRST:PB13 RRST:PB14 OE:PB5 RCLK:PB6 WEN:PB7 SDA:PB8 SCL:PB9
, Y E: _- m( e4 X- u- D - #define OV7725_VSYNC PAin(8)
3 O: O. p8 i1 f0 y& _ - #define OV7725_WRST PBout(13)
. C) V# h3 z! _+ ` - #define OV7725_WREN PBout(7) / E$ R/ X; D) ~
- #define OV7725_RCK_H GPIOB->BSRR=1<<66 _( m4 m7 P. `7 Y) t% m! g0 f* g
- #define OV7725_RCK_L GPIOB->BRR=1<<6
2 ~9 k7 N: |* W/ f* u2 r% m - #define OV7725_RRST PBout(14)
/ N+ }. H% Z [. g* ]* `* v - #define OV7725_CS PBout(5) . J7 q' m# b u- ]: c* _9 [0 ^. {
复制代码- #define SCCB_SCL PBout(9) //SCL
+ B' N u- V6 L! ^4 d$ E& Q" Q - #define SCCB_SDA PBout(8) //SDA 4 i; O% L1 A* b! i
8 t( ~& m, A4 I& }% W2 @7 X- #define SCCB_READ_SDA PBin(8) //ÊäÈëSDA % T7 e3 T9 N8 R0 }/ X. X) e- I
复制代码
0 L* Q' A6 F5 Y6 t! |: d6 y+ R1 z: y6 e$ |2 @
我使用的是杜邦线连接如,IO口是自己重新定义的(全使用普通IO即可),STM32F1系列的PB3,PB4,PA13,PA14,PA15用于JTAG,如果有使用到其中的引脚,需要进行特殊配置,具体可参考网上更多教程。当时使用PB3,PB4作为SCCB的引脚,出现问题才发现这个原因。
# b7 B5 k' u, x
6 q) U: }9 r$ n# Y+ }/ \初始化
0 t' i1 X' x* P- W( c写寄存器的函数,寄存器的配置都是通过这个函数3 A& G6 K& ~6 `
- u8 SCCB_WR_Reg(u8 reg,u8 data)1 W- n3 o! P' y
- {
, N, m, ?1 I9 \- s$ _ - u8 res=0;* L8 ?' c% u+ E8 q& f7 N5 _
- SCCB_Start(); //启动SCCB传输
5 W. a5 T& [" v# N - if(SCCB_WR_Byte(SCCB_ID))res=1; //写器件ID,为0x42) h; J! _3 l8 r2 ?5 T$ V2 ^
- delay_us(100);
) M! f. h# z4 {# @ - if(SCCB_WR_Byte(reg))res=1; //写寄存器地址,直接写对应的寄存器地址4 i- j4 G& O8 a B& [
- delay_us(100);- D$ F( r8 r' m5 l3 u" t* L* C% }. o
- if(SCCB_WR_Byte(data))res=1; //写数据,根据寄存器功能写对应数据& j+ @) M% |8 T
- SCCB_Stop(); 5 ^. E4 A( e. N1 h: [7 I
- return res;, Y! I* @9 h$ ~* k Y& I) V0 @
- } 0 X9 m% N6 D. g. G* A q0 s
复制代码
. f% m& ?" v4 x将要配置的寄存器地址和对应的值,存在一个二维数组里边 N6 f7 v6 u4 z" l$ A) H
- //初始化寄存器系列及其对应的值
4 l! X, ?8 G! `5 ?! E - const u8 ov7725_init_reg_tb1[][2]=
; n1 o+ y. [5 W- w/ x - {
8 o9 X; M/ U# a- S - /*输出窗口设置*/
! j+ Z0 ^( o( N% ] - {CLKRC, 0x00}, //clock config
+ U) d) I- j- z% U( e - {COM7, 0x06}, //VGA RGB565$ H3 A; A0 T- \* T
- {HSTART, 0x3f}, //水平起始位置
3 U2 x i1 M3 x3 Z+ e4 } - {HSIZE, 0x50}, //水平尺寸
8 v7 B0 h4 c2 C" q. q& g: o - {VSTRT, 0x03}, //垂直其实位置8 M6 }8 I; e/ z% N4 Q) ~ J2 n; K5 n
- ......: G1 \6 \. @% R6 S8 D
复制代码
, s+ Z- j. k' x; f; m; z% y初始化时,在循环语句调用写寄存器的函数,依次写入寄存器的地址和对应的值,即可完成寄存器的配置
. s y' N8 N8 e- a- //初始化OV7725,采用QVGA分辨率(320*240)
; h2 g, R- H3 a2 ]. b - for(i=0;i<sizeof(ov7725_init_reg_tb1)/sizeof(ov7725_init_reg_tb1[0]);i++)
' p( p; @0 z2 n& q - {
0 C: ]7 e' v* s - SCCB_WR_Reg(ov7725_init_reg_tb1[i][0],ov7725_init_reg_tb1[i][1]);% s" X1 c2 C5 D! t- o. q: d
- }
! k {! B0 O: m& {6 ~! H
复制代码 ( G% B/ r2 H" i; e4 S3 E" |
帧中断和数据处理
; J; ^( D" D0 v, R2 D, d7 ?发生帧中断时0 Z( J' ~4 D# Q+ g1 ]& w
- u8 ov_sta; //帧中断标记
' J3 J( [, I; r; [; _8 f4 ]9 N" c - //中断处理函数
% m/ S: b$ H1 c1 c4 i - void EXTI9_5_IRQHandler(void)
) Z8 M, Q( c8 Q& T7 r- f - { , r& e$ d6 {! [; d% ]2 v
- if(EXTI_GetITStatus(EXTI_Line8)==SET)//是8线的中断
( Y A9 T6 Z# Z2 a6 B( t - { F- T) Y* K& n
- if(ov_sta==0). ?7 u# x/ ?1 p; ?0 z. D
- {( e; Y) ?9 u- V
- OV7725_WRST=0; //复位写指针 5 ^3 z }5 H' m; W& L9 M
- OV7725_WRST=1; # h" \* Q2 z" ~4 N1 K% F, Q6 t
- OV7725_WREN=1; //允许写入FIFO / P6 _, R0 G% ~% c
- ov_sta++; //帧中断加1
( Q) n0 X7 g% I; J, S - }else OV7725_WREN=0;//禁止写入FIFO2 k' {7 n: L. \6 T0 k3 {! }
- }
$ [+ x8 n6 \* m9 A- S) d: o% t - EXTI_ClearITPendingBit(EXTI_Line8); //清除中断标志位
7 A1 P' S1 u" u* i/ T - }
" A$ T. c* g9 O
复制代码 - o( o1 B' E, R3 s9 e, m
数据处理! T& D/ c9 \# O! Z3 o; V
- if(ov_sta)//判断是否有帧中断发生7 a' F# l1 m9 q: e6 x) M" V
- {
" K0 |9 {* i0 B' U - OV7725_RRST=0; //复9 x2 ~, t1 J& g; I. R& }
- OV7725_RCK_L; //位
! J1 l- n/ F4 Y! S8 F5 } - OV7725_RCK_H; //读
; e$ z$ \. [* y' O- c$ B" | - OV7725_RCK_L; //指
/ ?! X: A, z$ k5 G2 _ - OV7725_RRST=1; //针
" e/ e/ O9 y B2 C( I/ w1 g. U z - OV7725_RCK_H;
& Q+ q- [1 F: J4 I - for(i=0;i<OV7725_WINDOW_HEIGHT;i++)//循环语句读取FIFO的数据9 ^: i0 }0 |' F% J
- {
8 c' b$ X* ~! t3 h, h7 d, ~% Q4 F7 F - for(j=0;j<OV7725_WINDOW_WIDTH;j++)8 D) ]) L9 Z: o! r# `9 b. M- f4 z
- {! o) P9 x- q! l, l* A
- //将数据填入相应缓冲区
6 d/ N0 E& }/ l2 U - ...... i, @6 `' g8 {' }6 v3 ?7 ]* K: n
- }
9 @5 F6 ]( f0 ]- Z$ K - }' K/ z$ C7 M4 n- B7 I0 z% }/ y
- ov_sta=0; //清零帧中断标志7 ]$ K+ @' e9 q2 m5 u
- }% K- \3 b! m5 m' x1 \
复制代码
4 Y+ X# Y/ q0 q3 {以上是对于OV7725的初步理解,还有很多不懂的地方,后续有进一步研究再补充
2 B. K# Z8 m# {/ X' `+ V( l————————————————
! C% n4 R7 |7 B* i# `版权声明:hu_hong_hao
1 R* B1 c* B3 t. j如有侵权请联系删除
, m/ ?/ T+ U! f0 q& _
0 O# k% B7 I1 O# J2 z' Q
: a/ |( O Y$ L, w3 V# A0 I5 K2 G; H4 J
|