前言
前几天分析了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;
上一篇:TQ2440 linux i2c驱动——at24c02(eeprom)
下一篇:基于mini2440的ov9650摄像头裸机测试
设计资源 培训 开发板 精华推荐
- 使用 Richtek Technology Corporation 的 RT6575B 的参考设计
- DC208A-B,用于具有固定输出的 LTC1559 备用电池控制器的演示板
- Smart_Car
- 使用 ON Semiconductor 的 TN5D51 的参考设计
- 使用 Integrated Silicon Solution Inc 的 IS31AP2031 的参考设计
- REF192 低压差开尔文连接电压基准的典型应用电路
- 使用 Analog Devices 的 LTC3130IMSE-1 的参考设计
- 使用 Microchip Technology 的 MIC2772-T3T3YML 的参考设计
- 用于冰箱的REF_5QR1070AZ_33W1辅助SMPS参考设计
- 冰墩墩nfc门禁钥匙扣
- CGD和Qorvo将共同革新电机控制解决方案
- 是德科技 FieldFox 手持式分析仪配合 VDI 扩频模块,实现毫米波分析功能
- 贸泽开售可精确测量CO2水平的 英飞凌PASCO2V15 XENSIV PAS CO2 5V传感器
- 玩法进阶,浩亭让您的PCB板端连接达到新高度!
- 长城汽车研发新篇章:固态电池技术引领未来
- 纳芯微提供全场景GaN驱动IC解决方案
- 解读华为固态电池新专利,2030 叫板宁德时代?
- 让纯电/插混车抓狂?中企推全球首款-40℃可放电增混电池,不怕冷
- 智驾域控知多少:中低端车型加速上车,行泊一体方案占主体
- Foresight推出六款先进立体传感器套件 彻底改变工业和汽车3D感知