s3c2440 camif接口摄像头驱动分析——基于tq2440的ov9650.c

前言

            最近想做摄像头驱动,看了一些文章,对摄像头驱动的结构还是很晕。于是决定分析内核自带的驱动程序。内核的cmos摄像头

有用v4l2的,也有用arm的camif codes通道结构的。本文是针对s3c2440 camif接口而写的驱动的代码导读。写得不好请多多指教。

一、开发环境

1.开发板:tq2440开发板

2.arm cpu:s3c2440

3.摄像头:ov9650

4.linux 内核:linux 2.6.30

5.源码:driver/meida/video/ov9650.c


二、需要了解的概念和准备的东西

1.SCCB协议  点击打开链接

2.ov9650 datasheet 点击打开链接

3.s3c2440 datasheet

4.linux 内存管理、映射(推荐《LDD3》,《linux内核情景分析jkk》)

5.linux misc字符设备

6.自旋锁、原子量、中断等

7.linux 的时钟管理,struct clk。


三、SCCB协议的实现

       sccb是一种与i2c类似的协议,在写的时候跟i2c协议完全相同。但在读的时候与i2c有一点差别。

这里的ov9650驱动没有使用linux的i2c系统,而是用I/O模拟重新写过。

1、需要知道的宏定义

/*ov9650.h*/
#define OV9650_SCCB_ADDR   0x66
#define OV9650_MANUFACT_ID	0x7FA2
#define OV9650_PRODUCT_ID	0x9650
#define SIO_C		S3C2410_GPE14   //i2cscl
#define SIO_D		S3C2410_GPE15   //i2csda

#define State(x)		s3c2410_gpio_getpin(x)

/*#define smp_mb barrrier();设置内存屏障,变量使用最原始的值*/
#define High(x)		do{s3c2410_gpio_setpin(x,1); smp_mb();}while(0)
#define Low(x)		do{s3c2410_gpio_setpin(x,0); smp_mb();}while(0)

#define WAIT_STABLE()	do{udelay(10);}while(0)
#define WAIT_CYCLE()	do{udelay(90);}while(0)

#define CFG_READ(x)	do{s3c2410_gpio_cfgpin(x,S3C2410_GPIO_INPUT);smp_mb();}while(0)
#define CFG_WRITE(x)	do{s3c2410_gpio_cfgpin(x,S3C2410_GPIO_OUTPUT);smp_mb();}while(0)

2、start、write|read、stop

        回到ov9650.c中,先看到第一个与sccb有关函数 sccb_start(void):


static void __inline__ sccb_start(void)
{
	CFG_WRITE(SIO_D); //设置i2csda为输出

	Low(SIO_D);      //将i2csda设置为0
	WAIT_STABLE();   //延时10us
}

       接下是 sccb写一个字节,sccb_write_byte(u8 data):

static void __inline__ sccb_write_byte(u8 data)
{
	int i;

	CFG_WRITE(SIO_D);  //设置i2csda为输出
	WAIT_STABLE();     //延时10us

	/* write 8-bits octet. */
	for (i=0;i<8;i++)
	{
		Low(SIO_C);      //将i2cscl拉低
		WAIT_STABLE();

		if (data & 0x80)  //从高位开始写
		{
			High(SIO_D);
		}
		else
		{
			Low(SIO_D);
		}
		data = data<<1;
		WAIT_CYCLE();   
             /* write byte done, wait the Don't care bit now. */
       {
        Low(SIO_C);
        High(SIO_D);
        CFG_READ(SIO_D);
        WAIT_CYCLE();

        High(SIO_C);
        WAIT_CYCLE();

}
}

下面是读一个字节:

static u8 __inline__ sccb_read_byte(void)
{
	int i;
	u8 data;

	CFG_READ(SIO_D);
	WAIT_STABLE();

	Low(SIO_C);
	WAIT_CYCLE();

	data = 0;
	for (i=0;i<8;i++)
	{
		High(SIO_C);
		WAIT_STABLE();

		data = data<<1;
		data |= State(SIO_D)?1:0;
		WAIT_CYCLE();

		Low(SIO_C);
		WAIT_CYCLE();
	}

	/* read byte down, write the NA bit now.*/
	{
		CFG_WRITE(SIO_D);
		High(SIO_D);
		WAIT_CYCLE();

		High(SIO_C);
		WAIT_CYCLE();
	}

	return data;
}

接着是stop:

static void __inline__ sccb_stop(void)
{
	Low(SIO_C);              
	WAIT_STABLE();

	CFG_WRITE(SIO_D);
	Low(SIO_D);
	WAIT_CYCLE();

	High(SIO_C);
	WAIT_STABLE();

	High(SIO_D);
	WAIT_CYCLE();

	CFG_READ(SIO_D);
}

sccb写,需要三个参数,主线地址idaddr,寄存器地址subaddr,数据data,是单开始信号,

void sccb_write(u8 IdAddr, u8 SubAddr, u8 data)
{
	down(&bus_lock);
	sccb_start();
	sccb_write_byte(IdAddr);
	sccb_write_byte(SubAddr);
	sccb_write_byte(data);
	sccb_stop();
	up (&bus_lock);
}
sccb读,主线地址idaddr,寄存器地址subaddr,双开始信号,

u8 sccb_read(u8 IdAddr, u8 SubAddr)
{
	u8 data;

	down(&bus_lock);
	sccb_start();
	sccb_write_byte(IdAddr);
	sccb_write_byte(SubAddr);
	sccb_stop();

	sccb_start();
	sccb_write_byte(IdAddr|0x01);
	data = sccb_read_byte();
	sccb_stop();
	up(&bus_lock);

	return data;
}

四、__init 和 __exit

        这驱动注册框架是misc字符设备,所以,有__init  __exit,struct file_opreations 结构体,我们先从最简单的

__init和__exit开始好了,分别是static int __init camif_init(void) 和static int __exit camif_cleanup

1.static int __init camif_init(void)

/*
 * camif_init()
 */
static int __init camif_init(void)
{
	int ret;
	struct tq2440_camif_dev * pdev; //声明一个tq2440_camif_dev结构体
	struct clk * camif_upll_clk; //一个时钟结构体

	printk(KERN_ALERT"initializing s3c2440 camera interface......\n");

        /*这个camera在前面有定义
        camera device(s)
        static struct tq2440_camif_dev camera;
        */
	pdev = &camera;

	/* set gpio-j to camera mode. */
        /*把gpio-j设置为camif模式,这个摄像会接在这个接口上*/
	s3c2410_gpio_cfgpin(S3C2440_GPJ0, S3C2440_GPJ0_CAMDATA0);
	s3c2410_gpio_cfgpin(S3C2440_GPJ1, S3C2440_GPJ1_CAMDATA1);
	s3c2410_gpio_cfgpin(S3C2440_GPJ2, S3C2440_GPJ2_CAMDATA2);
	s3c2410_gpio_cfgpin(S3C2440_GPJ3, S3C2440_GPJ3_CAMDATA3);
	s3c2410_gpio_cfgpin(S3C2440_GPJ4, S3C2440_GPJ4_CAMDATA4);
	s3c2410_gpio_cfgpin(S3C2440_GPJ5, S3C2440_GPJ5_CAMDATA5);
	s3c2410_gpio_cfgpin(S3C2440_GPJ6, S3C2440_GPJ6_CAMDATA6);
	s3c2410_gpio_cfgpin(S3C2440_GPJ7, S3C2440_GPJ7_CAMDATA7);
	s3c2410_gpio_cfgpin(S3C2440_GPJ8, S3C2440_GPJ8_CAMPCLK);
	s3c2410_gpio_cfgpin(S3C2440_GPJ9, S3C2440_GPJ9_CAMVSYNC);
	s3c2410_gpio_cfgpin(S3C2440_GPJ10, S3C2440_GPJ10_CAMHREF);
	s3c2410_gpio_cfgpin(S3C2440_GPJ11, S3C2440_GPJ11_CAMCLKOUT);
	s3c2410_gpio_cfgpin(S3C2440_GPJ12, S3C2440_GPJ12_CAMRESET);

	/* init camera's virtual memory. */
        /*初始化摄像头的虚拟内存*/

        /*
          在arch/arm/mach-s3c2410/include/mach/map.h
          #define S3C2440_PA_CAMIF   (0x4F000000)
          #define CARD_NAME  "camera"
          */
	if (!request_mem_region((unsigned long)S3C2440_PA_CAMIF, S3C2440_SZ_CAMIF, CARD_NAME))
	{
		ret = -EBUSY;
		goto error1;
	}

	/* remap the virtual memory. */
        /*重新映射虚拟地址*/
	camif_base_addr = (unsigned long)ioremap_nocache((unsigned long)S3C2440_PA_CAMIF, S3C2440_SZ_CAMIF);
	if (camif_base_addr == (unsigned long)NULL)
	{
		ret = -EBUSY;
		goto error2;
	}

	/* init camera clock. */
        /*初始化camera的时钟*/
	pdev->clk = clk_get(NULL, "camif");
	if (IS_ERR(pdev->clk))
	{
		ret = -ENOENT;
		goto error3;
	}
	clk_enable(pdev->clk);

	camif_upll_clk = clk_get(NULL, "camif-upll");
	clk_set_rate(camif_upll_clk, 24000000);
	mdelay(100);

	/* init reference counter and its mutex. */
	mutex_init(&pdev->rcmutex);
	pdev->rc = 0;

	/* init image input source. */
	pdev->input = 0;

	/* init camif state and its lock. */
	pdev->state = CAMIF_STATE_FREE;

	/* init command code, command lock and the command wait queue. */
	pdev->cmdcode = CAMIF_CMD_NONE;
	init_waitqueue_head(&pdev->cmdqueue);

	/* register to videodev layer. */
	if (misc_register(&misc) < 0)
	{
		ret = -EBUSY;
		goto error4;
	}
	printk(KERN_ALERT"s3c2440 camif init done\n");

//	sccb_init();
	CFG_WRITE(SIO_C);
	CFG_WRITE(SIO_D);

	High(SIO_C);
	High(SIO_D);
	WAIT_STABLE();

	hw_reset_camif();
	has_ov9650 = s3c2440_ov9650_init() >= 0;
	s3c2410_gpio_setpin(S3C2410_GPG4, 1);
	return 0;

error4:
	clk_put(pdev->clk);
error3:
	iounmap((void *)camif_base_addr);
error2:
	release_mem_region((unsigned long)S3C2440_PA_CAMIF, S3C2440_SZ_CAMIF);

error1:
	return ret;
}

看起来好像不是那么简单,我们慢慢来吧。一开始就看到个不常见的结构体struct tq2440_camif_dev 我们跟进去看吧:

/* main s3c2440 camif structure. */
struct tq2440_camif_dev
{
	/* for sub-devices */
        /*定义了一个设备链表*/
	struct list_head devlist;

	/* minor device */
        /*video_device,v4l2的核心结构体*/
	struct video_device * vfd;

	/* hardware clock. */
        /*时钟源,用于时钟管理,详细看这篇博客http://blog.csdn.net/smmei/article/details/8073470*/
	struct clk * clk;

	/* reference count. */
        /*可睡眠的锁——互斥锁*/
	struct mutex rcmutex;
	int rc;

	/* the input images's format select. */
	int input;

	/* source(input) image size. */
        /*源输入图片的大小*/
	int srcHsize;
	int srcVsize;

	/* windowed image size. */
        /*窗口图片的大小*/
	int wndHsize;
	int wndVsize;

	/* codec-path target(output) image size. */
        /*编码管道的图片大小*/
	int coTargetHsize;
	int coTargetVsize;

	/* preview-path target(preview) image size. */
        /*预览管道的图片大小*/
	int preTargetHsize;
	int preTargetVsize;

	/* the camera interface state. */
        /*s3c2440 camera接口的状态:空闲,预览,拍摄*/
	int state;	// CMAIF_STATE_FREE, CAMIF_STATE_PREVIEWING, CAMIF_STATE_CAPTURING.

	/* for executing camif commands. */
        /*camera控制指令*/
	int cmdcode;				// command code, CAMIF_CMD_START, CAMIF_CMD_CFG, etc.
        /*一个等待指令完成的队列*/
	wait_queue_head_t cmdq
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值