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

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

前言

            最近想做摄像头驱动,看了一些文章,对摄像头驱动的结构还是很晕。于是决定分析内核自带的驱动程序。内核的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 preTargetHsi
OV9650与Mini2440开发板的裸机测试程序 OV9650摄像头传感器是一款广泛应用于嵌入式系统的CMOS图像传感器,它能够提供VGA分辨率的视频流,非常适合于视频监控、实时图像处理等应用。与之配合的处理器和开发板,例如Mini2440和S3C2440,能够提供足够的处理能力以支持摄像头的功能发挥。Mini2440开发板是专为S3C2440处理器设计的一块开发板,用于软件开发和硬件验证。板载资源丰富,包括2MB NOR Flash,64MB SDRAM,以太网接口,音频接口,多种串行接口,以及多个GPIO(通用输入输出)引脚。 阅读详情

相关推荐

S3C2440 camera驱动

自己写的S3C2440 camera裸机驱动,采用类似STM32库函数的架构编写CAMIF接口驱动,适合初学者学习。

s3c2440_driver.rar_2440_2440 asm_2440 驱动_s3c2440-camif_s3c_camif

非操作系统下的所有设备驱动程序,2440usb.h AudioDrv.h camif.h camproset.h dma.h IIC.h lcd.h

S3c 2440 Camera测试程序

基于ARM9的摄像头OV9650的测试程序,将摄像头采集的数据同步在液晶屏上显示出来。

Linux_S3C2440_CAMIF接口驱动程序(二)OV9650驱动完全解析

#ifndef __S3C2440CAMIF_H__ #define __S3C2440CAMIF_H__ //这是为了防止c头文件多次包含造成重复定义错误。 #define OV9650_SCCB_ADDR 0x60//定义设备地址 #define OV9650_MANUFACT_ID 0x7FA2 #define OV9650_PRODUCT_ID 0x9650 stati

KinGil 2438

基于V4L2的视频驱动开发

基于V4L2的视频驱动开发 编写基于V4L2视频驱动主要涉及到以下几个知识点: ●摄像头方面的知识 要了解选用的摄像头的特性,包括访问控制方法、各种参数的配置方法、信号输出类型等。 ●Camera解码器、控制器 如果摄像头是模拟量输出的,要熟悉解码器的配置。最后数字视频

Run for the future! 4494

Linux_S3C2440_CAMIF接口驱动程序(一)驱动和系统相关的学习

实例为OV9650 驱动程序有两种加载方式,一种是直接编译进LINUX内核,直接调用即可。另一种是模 块加载方式。CMOS摄像头驱动以 MODULES 的形式编写,MODULES形式的驱动可动态加载到 Linux 内核,节省了系统开销。程序的设计围绕下面的 file_operations数据结构体展开,主要 编码工作就是具体实现结构体中相应的各个函数的功能。通过这些系统调用,应用程序就可

KinGil 2783

S3C2440-CAMIF接口文档解读

OVERVIEW CAMIF由7部分组成,patternmux,capturing unit,preview scaler,cedec scaler,preview DMA,codec DMA,SFR(特殊功能存储器)。CAMIF支持ITU-RBT.601/656 YCbCr 8-bit standard.最大输入4096*4096 pixels(2048*2048 pixels for sca

KinGil 2392

S3C2440Camera驱动代码模板(RealView MDK)

好记心不如烂笔头,为方便以后查看代码及代码重复利用,这里贴出S3C2440 Camera驱动代码。使用友善MINI2440开发板,开发环境为RealView MDK 4.22。 最初,本人在网上下载了许多mini2440 camera驱动的代码,但结果都不尽人意,因为这些代码看起来都比较晦涩难懂,都是直接对寄存器进行位与位或操作,且有的代码排版布局很乱,对初学者来说难度较大。由于本人接触了STM32开发后,觉得STM32的库函数给初学者提供了很大帮助,仅仅通过函数名及变量名就能知道当前操作的是哪个寄存器,

hexiaolong2009的专栏 1219

S3C2440camera接口特性及WinCE 下的驱动

摘要:分析S3C2440camera接口特性,介绍当摄像设备为CCD摄像头时,在WinCE 5.0/6.O操作系统环境下开发camera驱动的方法。该方法弥补了目前国内在WinCE 5.0/6.0操作系统环境下CCD摄像头无法连接到S3C2440camera接口的缺陷。   S3C2440是应用十分广泛且适用于嵌入式系统的一款嵌入式处理器。winCE 5.0/6.O是微软公司开发的一款专用于嵌入式系统的实时操作系统。其模块化设计使开发人员可以根据需求定制设备。目前,国内大部分OEM商都提供了对S3C2440camera接口在WinCE5.O/6.0下的驱动支持。遗憾的是,目前国内OEM

JZ2440学习笔记一

JZ2440学习笔记

hbutluoxi的博客 1293

s3c2440的摄像接口应用

s3c2440提供了一个摄像接口,使开发人员很容易地实现摄像、照相等功能。摄像接口包括8位来自摄像头的输入数据信号,一个输出主时钟信号,三个来自摄像头的输入同步时钟信号和一个输出复位信号。摄像接口的主时钟信号由USB PLL产生,它的频率为96MHz,再经过分频处理后输出给摄像头摄像头再根据该时钟信号产生三个同步时钟信号(像素时钟、帧同步时钟和行同步时钟),反过来再输入回s3c2440

smart_qiang的专栏 1685

S3C2440裸奔---摄像头源代码

首先需要搭建一个可以编译代码的环境。从一开始我就不想用ADS(安装完ADS后会产生一个CodeWarrior for ARM Developer Suite的工具,可以编译和调试代码),虽然它是个很好的工具,但是它却隐藏了很多的细节,不利于新手去深度学习。所以我还是希望从Makefile入手,一步一步弄懂所有的细节。 第一步:需要先搭建一个Linux环境,我是安装在虚拟机上面的。安装好Linux后,再从网上下载arm-linux-gcc,一般解压后之后就可以直接使用了,但需要先设置好环境参数。 第二步:编写代码,然后在Linux系统上进行编译。代码在后面会给出。 第三步:需要把编译好的bin文件烧写到板子上。这个需要使用J-Link烧录器,还需要安装“Setup_JLinkARM_V408k.exe”软件,特别是一些参数的设置非常重要。

S3C2440平台CAMERA驱动 ov9650

* s3c2440-camif.c (linux-2.6.30.10/S3C2440A) * s3c2440 camera controller driver * Wed Nov 19 21:49:19 CST 2014 * s3c2440-ov9650.c (linux-2.6.30.10/S3C2440A) * ov9650 image sensor driver

OV9650+S3C2440摄像头视频采集

OV9650+S3C2440摄像头视频采集

摄像头驱动学习

\drivers\media\video\s3c2440camif.c \drivers\media\video\s3c2440_ov9650.c \drivers\media\video\sccb.c 硬件连接 I2C总线连接 I2CSCL ——GPE14 I2CSDA——GPE15 Sccb.h #define SIO_C S3C2410_GPE14 #define SIO_D S3C241...

weixin_34176694的博客 290

彻底玩转 S3C2440 Linux 摄像头(包括USB和COMS摄像头

编译系统 Ubuntu10.04 交叉编译器 arm-linux-gcc 4.3.3 硬件设备 LT2440开发板  USB或者CMOS摄像头 测试软件 mjpg_streamer 依赖库 SDL-1.2.14  jpegsrc.v8b.tar.gz 1.

希哈科技 2473

基于S3C2440的USB摄像头应用简单实现之应用层框架(一)

浅解基于S3C2440的USB摄像头视频采集应用之应用层框架解析(一)目的:在嵌入式系统中,视频采集主要采用两种接口:一种是标准摄像头接口,一种是USB接口(USB1.1)。标准的摄像头接口接口复杂,但传输速度快,适合高质量视频采集,而USB接口接口简单,但有性能瓶颈,只能用于低质量的视频采集。我采用的是mini2440开发板,内为S3C2440芯片,S3C2440自带USB1.1主机接口和CM

蜗小牛~~ 2651
上一篇: TQ2440 linux i2c驱动——at24c02(eeprom)
下一篇: ov7670的移植(寄存器的配置)——基于tq2440
izobs
博客等级 码龄14年 43粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值