ov7670的移植(寄存器的配置)——基于tq2440

发布者:TranquilDreams最新更新时间:2022-10-11 来源: csdn关键字:ov7670  移植  寄存器  配置 手机看文章 扫描二维码
随时随地手机看文章

前言

前几天分析了ov9650的驱动,觉得还看得懂吧。于是开始移植代码,驱动ov7670。其实那个ov9650的驱动程序架构并不好。没有使用v4l2的驱动架构,这样应用就不能用完美使用v4l2的接口了。还有,他只是采集了p-path。并没有对c-path进行任何处理,也没开放什么接口。本文主要想讲讲s3c2440的camera接口和ov7670的寄存器配置。


一、开发环境

1.开发板:tq2440(s3c2440)


2.摄像头: ov7670


二、接口电路

下面是我买来的ov7670的摄像头的pcb图,重点是它的引脚图。为了与tq2440的camera接口更好的做比较,我把他们放在一起吧。



 


我选的是3.3v的那个vdd,这根据摄像头模块要求吧。所以这里VCC-VDD33V,当我把ov7670的管脚全部接到camera接口上时,我发现这接口上有两个口,这个ov7670模块没有。是CAMRST和ENIT19。查一下datasheet,这CAMRST是用来复位cmos摄像头。原来的ov9650应是想通过驱动将该管脚拉高,从而产生硬复位。但很遗憾,这个ov9650的驱动程序并没有这么做。而我们这ov7670模块则直接将这个口连到VCC,通俗易懂。还有一个是ENIT19,这个口在ov9650的驱动程序里看到了他的用法,


staticvoid __inline__ ov9650_poweron(void)


{


s3c2410_gpio_cfgpin(S3C2410_GPG11,S3C2410_GPG11_OUTP);


s3c2410_gpio_setpin(S3C2410_GPG11,0);


mdelay(20);


}



这里的S3C2410_GPG11就是ENIT19了,目的就是用通断cmos摄像头的电。至于是高还是低电平,那要看电路设计。我这个ov7670模块没有放出这个控制口。


温馨提醒:这个东东不支持热插拔,所以请在断电情况下连接!


三、s3c2440camera interface

1.简介

下面的简介是抄自datasheet的,个人认为是学这个接口,需要知道的基础知识吧


s3c2440的摄像头接口支持ITU-RBT.601/656 YCbCr 8-bit 标准,最大的输入分辨率是4096×4096pixels(2048×2048可缩放)和拥有两个转换器。预览转换器专用于将图片缩小,就像PIP(picturein picture),而编码器专注于将图片编码,就像编成YCbCr4:2:0 or4:2:2.两个主DMA通道能对图片取镜像和旋转图片,以适应移动环境。这些特性在折叠式移动手机真的非常有用,并且他的测试模式在标准化的输入信号如CAMHREF,CAMVSYNC也相当有用。


FEATURES


— ITU-RBT. 601/656 8-bit mode external interface support


— DZI(Digital Zoom In) capability


— Programmablepolarity of video sync signals


— Max.4096 x 4096 pixel input support without scaling (2048 x 2048 pixelinput support with scaling)


— Max.4096 x 4096 pixel output support for CODEC path


— Max.640 x 480 pixel output support for PREVIEW path


— Imagemirror and rotation (X-axis mirror, Y-axis mirror, and 180°rotation)


— PIPand codec input image generation (RGB 16/24-bit format and YCbCr4:2:0/4:2:2 format)



2、信号描述

3.框图

4.摄像头接口的操作


(1)两个DMA管道


CAMIF有2个DMA管道。P-path(预览管道)和C-path(编码管道)被分开接AHB总线上。从系统总线的角度看,这两条管道是独立的。P-path储存RGB格式的图片数据到内存中,以便生成预览。C-path保存YCbCr4:2:0或4:2:2图片数据到内存中,以便编码成MPEG-4,H.263等视频格式。

(2)时钟方面(linux下的clk http://blog.csdn.net/bingqingsuimeng/article/category/1228965)


CAMIF有两个时钟。一个是系统总线时钟,HCLK;另一个像素时钟,CAMPCLK。HCLK一定要比CAMPCLK快。

(3)内存储存方法


在c-path采用的二进制储存方法被使用于图片储存。像素数据的储存是从地位到高位的。AHB总线输送字符数据,CAMIF将每个Y-Cb-Cr字符储存成二进制。在p-path存在两种不同的格式。对于RGB24-bit格式一个像素点就是一个字符。或者,对于RGB16-bit两个像素点就是一个字符。如下图:


5.寄存器的设置时序图


第一个寄存器的设置是图片捕捉命令能出现在任何地方。不过,他推荐你先把CAMVSYNC设置成低状态,并且CAMVSYNC的信息能够从SFR状态读出。所有的命令,包括ImCptEn在CAMVSYNC的下降延都是有效的的。注意:除了第一个SFR设置,所有的指令都应该以中断的方式编写(interrupt service routine)。特别地,当与捕捉相关的信息和目标大小发生改变时,捕捉操作应该停止。

6、LastIRQ


IRQexcept LastIRQ is generated before image capturing. Last IRQ whichmeans capture-end can be set by following timing diagram. LastIRQEnis auto-cleared and ,as mentioned, SFR setting in ISR is for nextframe command. So, for adequate last IRQ, you should follow nextsequence between LastIRQEn and ImgCptEn /ImgCptEn_CoSc/ImgCptEnPrSC.It is recommended that ImgCptEn /ImgCptEn_CoSc /ImgCptEnPrSC are setat same time and at last of SFR setting in ISR. FrameCnt which isread in ISR, means next frame count. On following diagram, lastcaptured frame count is “1”. That is, Frame 1 is thelast-captured frame among frame 0~3. FrameCnt is increased by 1 atIRQ rising.

2、寄存器的配置

这个部分有些东东会必需ov7670的寄存器配置一起讲,毕竟接口是双方的,所以不要觉得太跳。


一切开始于camif_open()函数.关系如下


camif_open()——>init_camif_config(fh)——>update_camif_config(fh,CAMIF_CM_STOP)—— > update_camif_regs(fd-dev)——>{


update_source_fmt_regs(pdev);


update_target_wnd_regs(pdev);


update_target_fmt_regs(pdev);


update_target_zoom_regs(pdev);


}


下面一个一个函数讲吧。


(1)update_source_fmt_regs(pdev)



/* update CISRCFMT only. */

static void __inline__ update_source_fmt_regs(struct ov7670_camif_dev * pdev)

{

u32 cisrcfmt;

u32 recisrcfmt;

 

recisrcfmt = ioread32(S3C244X_CISRCFMT);

       #if DEBUG_CAMIF

printk(DEVICE_NAME"--------------before write, S3C244X_CISRCFMT is %xn",recisrcfmt);

#endif

 

cisrcfmt = (1<<31) // ITU-R BT.601 YCbCr 8-bit mode

|(0<<30) // CB,Cr value offset cntrol for YCbCr

|(pdev->srcHsize<<16) // source image width,640

|(0<<14) // input order is CbYCrY,480

|(pdev->srcVsize<<0); // source image height

 

iowrite32(cisrcfmt, S3C244X_CISRCFMT);

mdelay(3);

 

recisrcfmt = ioread32(S3C244X_CISRCFMT);

#if DEBUG_CAMIF

printk(DEVICE_NAME"--------------after wirte,the S3C244X_CISRCFMT is %xn",recisrcfmt);

#endif

}



先看第一个寄存器S3C244X_CISRCFMT

s3c2440的输入源格式必须是YCbCr的,而我选的是ITU-RBT.601 YCbCr 8-bitmode,下面很重要的一个就是YCbCr的排列顺序。我用的是YCbYCr,即00,这必需与ov7670的YUV输出格式一致,在ov7670的datasheet中,也必需选00,下面是0v7670 datasheet的相关描述

(2 )update_target_wnd_regs(pdev)


/* update CIWDOFST only. */

static void __inline__ update_target_wnd_regs(struct ov7670_camif_dev * pdev)

{

u32 ciwdofst;

u32 reciwdofst;

u32 winHorOfst, winVerOfst;

 

winHorOfst = (pdev->srcHsize – pdev->wndHsize)>>1;  //水平偏移量

winVerOfst = (pdev->srcVsize – pdev->wndVsize)>>1;//垂直偏移量

 

winHorOfst &= 0xFFFFFFF8;

winVerOfst &= 0xFFFFFFF8;

if ((winHorOfst == 0)&&(winVerOfst == 0))

{

ciwdofst = 0; // disable windows offset.

}

else

{

ciwdofst = (1<<31) // window offset enable

|(1<<30) // clear the overflow ind flag of input CODEC FIFO Y

|(winHorOfst<<16) // windows horizontal offset

|(1<<15) // clear the overflow ind flag of input CODEC FIFO Cb

|(1<<14) // clear the overflow ind flag of input CODEC FIFO Cr

|(1<<13) // clear the overflow ind flag of input PREVIEW FIFO Cb

|(1<<12) // clear the overflow ind flag of input PREVIEW FIFO Cr

|(winVerOfst<<0); // window vertical offset

}

 

iowrite32(ciwdofst, S3C244X_CIWDOFST);

 

#if DEBUG_CAMIF

mdelay(1);

reciwdofst = ioread32(S3C244X_CIWDOFST);

printk(DEVICE_NAME"----------S3C244X_CIWDOFST is %xn",reciwdofst);

#endif

 

 

#if DEBUG

printk(DEVICE_NAME"----update_target_wnd_regs is donen");

#endif

}



看到datasheet的描述如下:

这个寄存器其实就是对sourimage进行裁剪,截出来的就是Target image。所以target image的大小与window image的大小相同。


3.update_target_fmt_regs(pdev)



static void __inline__ update_target_fmt_regs(struct ov7670_camif_dev * pdev)

{

u32 ciprtrgfmt;

u32 ciprctrl;

u32 ciprscctrl;

 

u32 reciprtrgfmt;

u32 reciprctrl;

 

u32 mainBurstSize, remainedBurstSize;

 

/* CIPRCLRSA1 ~ CIPRCLRSA4. */

        /*RGB frame start address for preview DMA,p-path is RGB*/

iowrite32(img_buff[0].phy_base, S3C244X_CIPRCLRSA1);

iowrite32(img_buff[1].phy_base, S3C244X_CIPRCLRSA2);

iowrite32(img_buff[2].phy_base, S3C244X_CIPRCLRSA3);

iowrite32(img_buff[3].phy_base, S3C244X_CIPRCLRSA4);

 

/* CIPRTRGFMT. */

ciprtrgfmt = (pdev->preTargetHsize<<16) // horizontal pixel number of target image

|(0<<14) // don't mirror or rotation.

|(pdev->preTargetVsize<<0); // vertical pixel number of target image

iowrite32(ciprtrgfmt, S3C244X_CIPRTRGFMT);

 

 

#if DEBUG_CAMIF

mdelay(3);

reciprtrgfmt = ioread32(S3C244X_CIPRTRGFMT);

printk(DEVICE_NAME"--------------the CIPRTRGFMT is %xn",reciprtrgfmt);

#endif

 

 

/* CIPRCTRL. */

/*RGB 16-bit ,2 pixle/word ,mB=16,rB=16*/

calc_burst_size(2, pdev->preTargetHsize, &mainBurstSize, &remainedBurstSize);    //2 pixle a word

ciprctrl = (mainBurstSize<<19)|(remainedBurstSize<<14);

ciprctrl = ciprctrl|(1<<2);

 

iowrite32(ciprctrl, S3C244X_CIPRCTRL);

 

#if DEBUG_CAMIF

mdelay(3);

reciprctrl=ioread32(S3C244X_CIPRCTRL);

printk(DEVICE_NAME"--------------the S3C244X_CIPRCTRL is %xn",reciprctrl);

#endif

 

 

/* CIPRSCCTRL. */

ciprscctrl = ioread32(S3C244X_CIPRSCCTRL);

 

#if DEBUG_CAMIF

printk(DEVICE_NAME"-------------before write,S3C244X_CIPRSCCTRL, is %xn",ciprscctrl);

#endif

 

/* CIPRSCCTRL. */

ciprscctrl &= 1<<15; // clear all other info except 'preview scaler start'.

ciprscctrl |= 0<<30; // 16-bits RGB

iowrite32(ciprscctrl, S3C244X_CIPRSCCTRL); // 16-bit RGB

 

#if DEBUG_CAMIF

mdelay(1);

ciprscctrl = ioread32(S3C244X_CIPRSCCTRL);

printk(DEVICE_NAME"--------------S3C244X_CIPRSCCTRL is %xn",ciprscctrl);

#endif

 

/* CIPRTAREA. */

iowrite32(pdev->preTargetHsize * pdev->preTargetVsize, S3C244X_CIPRTAREA);

 

#if DEBUG

printk(DEVICE_NAME"----update_target_fmt_regs is donen");

#endif

}



这个函数涉及三个寄存器,一个个看:

这四个寄存器放p-path的frame DMA地址,每个存放一个frame,一共四个。

这个是CIPRTRGFMT寄存器,通过配置这个寄存器可实现图片的旋转和镜像。它还存放着target image 的大小。


接下来是CIPRCTRL:

这个burst size和remain burst size的计算函数如下:


/* calculate main burst size and remained burst size. */

static void __inline__ calc_burst_size(u32 pixperword,u32 hSize, u32 *mainBurstSize, u32 *remainedBurstSize)

{

u32 tmp;

 

tmp = (hSize/pixperword)%16;

 

switch(tmp)

{

case 0:

*mainBurstSize = 16;

*remainedBurstSize = 16;

break;

case 4:

*mainBurstSize = 16;

*remainedBurstSize = 4;

break;

case 8:

*mainBurstSize=16;

*remainedBurstSize = 8;

break;

default:

       tmp=(hSize/pixperword)%8;

switch(tmp)

{

case 0:

*mainBurstSize = 8;

*remainedBurstSize = 8;

break;

case 4:

*mainBurstSize = 8;

*remainedBurstSize = 4;

default:

*mainBurstSize = 4;

tmp = (hSize/pixperword)%4;

*remainedBurstSize = (tmp)?tmp:4;

break;

}

break;

}

}


这里特别提一下,这里的LastIRQEN最好是enable,它能保证输出图片的稳定。


4.update_target_zoom_regs(pdev)


static void __inline__ update_target_zoom_regs(struct ov7670_camif_dev * pdev)

{

u32 preHratio, preVratio;

u32 Hshift, Vshift;

u32 shfactor;

u32 preDstWidth, preDstHeight;

u32 Hscale, Vscale;

u32 mainHratio, mainVratio;

 

u32 ciprscpreratio;

u32 ciprscpredst;

u32 ciprscctrl;

 

/* CIPRSCPRERATIO. */

calc_prescaler_ratio_shift(pdev->wndHsize, pdev->preTargetHsize, &preHratio, &Hshift);

calc_prescaler_ratio_shift(pdev->wndVsize, pdev->preTargetVsize, &preVratio, &Vshift);

 

shfactor = 10 - (Hshift + Vshift);

 

ciprscpreratio = (shfactor<<28) // shift factor for preview pre-scaler

|(preHratio<<16) // horizontal ratio of preview pre-scaler

|(preVratio<<0); // vertical ratio of preview pre-scaler

iowrite32(ciprscpreratio, S3C244X_CIPRSCPRERATIO);

 

/* CIPRSCPREDST. */

preDstWidth = pdev->wndHsize / preHratio;

[1] [2]
关键字:ov7670  移植  寄存器  配置 引用地址:ov7670的移植(寄存器的配置)——基于tq2440

上一篇:TQ2440 linux i2c驱动——at24c02(eeprom)
下一篇:基于mini2440的ov9650摄像头裸机测试

小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved