摄像头驱动学习

发布时间:2024-07-10  

driversmediavideos3c2440camif.c

driversmediavideos3c2440_ov9650.c

driversmediavideosccb.c

硬件连接

I2C总线连接

clip_image002clip_image004

I2CSCL ——GPE14

I2CSDA——GPE15

Sccb.h

#define SIO_C S3C2410_GPE14

#define SIO_D S3C2410_GPE15

#define State(x) s3c2410_gpio_getpin(x)

#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)

void sccb_write(u8 IdAddr, u8 SubAddr, u8 data);

u8 sccb_read(u8 IdAddr, u8 SubAddr);

CFG_WRITE(x)把GPIO设置为输出

CFG_READ(x)把GPIO设置为输入

High(x) 把GPIO设置为高电平

Low(x) 把GPIO设置为低电平

Sccb.c

clip_image006

把SCL和Data都拉高,即为默认初始化状态电平

int sccb_init(void)

{

CFG_WRITE(SIO_C);

CFG_WRITE(SIO_D);

High(SIO_C);

High(SIO_D);

WAIT_STABLE();

return 0;

}

由上面的图可以看出,CLK高电平时,DATA拉低,即为START

static void __inline__ sccb_start(void)

{

CFG_WRITE(SIO_D);

Low(SIO_D);

WAIT_STABLE();

}

使用到一个信号量

static DECLARE_MUTEX(bus_lock);

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);

}

首先把信号量数值降低,表示自己使用,如果此时信号量不大于0,表示bus正在使用,驱动会在此等待,知道信号量大于0,然后,将其减1,此时bus对外界不可用。最后再把信号量加1,表示bus可用。

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

摄像头接口初始化:

/*

* camif_init()

*/

static int __init camif_init(void)

{

配置CAMIF的GPIO接口功能

/* set gpio-j to camera mode. */

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);

申请CAMIF的寄存器区域,由于OS的系统统一管理,所以,需要确定,该寄存器区域没有被别的进程获取,所以要申请。

/* init camera's virtual memory. */

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;

}

获取CAMIF的时钟,使用24M

/* init camera clock. */

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);

初始化CAMIF的状态

/* init camif state and its lock. */

pdev->state = CAMIF_STATE_FREE;

CAMIF DEV的状态:

/* for s3c2440camif_dev->state field. */

enum

{

CAMIF_STATE_FREE = 0, // not openned

CAMIF_STATE_READY = 1, // openned, but standby

CAMIF_STATE_PREVIEWING = 2, // in previewing

CAMIF_STATE_CODECING = 3 // in capturing

};

注册杂项设备:

/* register to videodev layer. */

if (misc_register(&misc) < 0)

{

ret = -EBUSY;

goto error4;

}

printk(KERN_ALERT"s3c2440 camif init donen");

初始化SCCB接口,Serial Camera Control Bus,其实是I2C接口

sccb_init();

硬件复位CAMIF

hw_reset_camif();

其实是软件复位,对CIGCTRL寄存器配置软件复位

/* software reset camera interface. */

static void __inline__ hw_reset_camif(void)

{

u32 cigctrl;

cigctrl = (1<<30)|(1<<29);

iowrite32(cigctrl, S3C244X_CIGCTRL);

mdelay(10);

cigctrl = (1<<29);

iowrite32(cigctrl, S3C244X_CIGCTRL);

mdelay(10);

}

clip_image008

检测是否存在OV9650,GPG4连的是LCD_PWR,不知道为什么?

has_ov9650 = s3c2440_ov9650_init() >= 0;

s3c2410_gpio_setpin(S3C2410_GPG4, 1);

clip_image010

OV9650的初始化:

int s3c2440_ov9650_init(void)

{

printk(KERN_ALERT"Loading OV9650 driver.........n");

/* power on. */

ov9650_poweron();

mdelay(100);

/* check device. */

if (ov9650_check() == 0 && ov9650_check() == 0)

{

printk(KERN_ERR"No OV9650 found!!!n");

return -ENODEV;

}

show_ov9650_product_id();

ov9650_init_regs();

printk("ov9650 init done!n");

return 0;

}

OV9650上电,这里对GPG12设置为输出,这里使用的虽然是中断引脚,但似乎没有用中断,这是一个电源控制引脚,PWDN,0:power on,1:power down

static void __inline__ ov9650_poweron(void)

{

s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_OUTP);

s3c2410_gpio_setpin(S3C2410_GPG12, 0);

mdelay(20);

}

clip_image012

clip_image014

OV9650检测,读取OV9650的Manu ID

static int __inline__ ov9650_check(void)

{

u32 mid;

mid = sccb_read(OV9650_SCCB_ADDR, 0x1c)<<8;

mid |= sccb_read(OV9650_SCCB_ADDR, 0x1d);

printk("SCCB address 0x%02X, manufacture ID 0x%04X, expect 0x%04Xn", OV9650_SCCB_ADDR, mid, OV9650_MANUFACT_ID);

return (mid==OV9650_MANUFACT_ID)?1:0;

}

#define OV9650_SCCB_ADDR 0x60

#define OV9650_MANUFACT_ID 0x7FA2

#define OV9650_PRODUCT_ID 0x9650

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

摄像头接口打开:

/*

* camif_open()

*/

static int camif_open(struct inode *inode, struct file *file)

{

首先判断是否存在OV9650 camera是文件开始的一个static变量。

struct s3c2440camif_dev *pdev;

struct s3c2440camif_fh *fh;

int ret;

if (!has_ov9650) {

return -ENODEV;

}

pdev = &camera;

为file handle分配内存

fh = kzalloc(sizeof(*fh),GFP_KERNEL); // alloc memory for filehandle

设置设备状态为open and standby,初始化CAMIF的配置。

pdev->state = CAMIF_STATE_READY;

init_camif_config(fh);

对CAMIF进行配置,最后更新一下配置

/* config camif when master-open camera.*/

static void init_camif_config(struct s3c2440camif_fh * fh)

{

struct s3c2440camif_dev * pdev;

pdev = fh->dev;

pdev->input = 0; // FIXME, the default input image format, see inputs[] for detail.

/* the source image size (input from external camera). */

pdev->srcHsize = 1280; // FIXME, the OV9650's horizontal output pixels.

pdev->srcVsize = 1024; // FIXME, the OV9650's verical output pixels.

/* the windowed image size. */

pdev->wndHsize = 1280;

pdev->wndVsize = 1024;

/* codec-path target(output) image size. */

pdev->coTargetHsize = pdev->wndHsize;

pdev->coTargetVsize = pdev->wndVsize;

/* preview-path target(preview) image size. */

pdev->preTargetHsize = 640;

pdev->preTargetVsize = 512;

update_camif_config(fh, CAMIF_CMD_STOP);

}

由于设备状态是CAMIF_STATE_READY所以,直接更新寄存器。

/* update camera interface with the new config. */

static void update_camif_config (struct s3c2440camif_fh * fh, u32 cmdcode)

{

struct s3c2440camif_dev * pdev;

pdev = fh->dev;

switch (pdev->state)

{

case CAMIF_STATE_READY:

update_camif_regs(fh->dev); // config the regs directly.

break;

case CAMIF_STATE_PREVIEWING:

/* camif is previewing image. */

disable_irq(IRQ_S3C2440_CAM_P); // disable cam-preview irq.

/* source image format. */

if (cmdcode & CAMIF_CMD_SFMT)

{

// ignore it, nothing to do now.

}

/* target image format. */

if (cmdcode & CAMIF_CMD_TFMT)

{

/* change target image format only. */

pdev->cmdcode |= CAMIF_CMD_TFMT;

}

不知为什么要等待VSYNC为L?然后对寄存器进行配置。

/* update camif registers, called only when camif ready, or ISR. */

static void __inline__ update_camif_regs(struct s3c2440camif_dev * pdev)

{

if (!in_irq())

{

while(1) // wait until VSYNC is 'L'

{

barrier();

if ((ioread32(S3C244X_CICOSTATUS)&(1<<28)) == 0)

break;

}

}

/* WARNING: don't change the statement sort below!!! */

update_source_fmt_regs(pdev);

update_target_wnd_regs(pdev);

update_target_fmt_regs(pdev);

update_target_zoom_regs(pdev);

}

初始化CAMIF的DMA内存。

ret = init_image_buffer(); // init image buffer.

这里为DMA分配内存,按页分配,内存管理的知识需要学习?

/* init image buffer (only when the camif is first open). */

static int __inline__ init_image_buffer(void)

{

int size1, size2;

unsigned long size;

unsigned int order;

/* size1 is the max image size of codec path. */

size1 = MAX_C_WIDTH * MAX_C_HEIGHT * 16 / 8;

/* size2 is the max image size of preview path. */

size2 = MAX_P_WIDTH * MAX_P_HEIGHT * 16 / 8;

size = (size1 > size2)?size1:size2;

order = get_order(size); //获取需要分配字节的2的阶数,内存按页分配

img_buff[0].order = order;

img_buff[0].virt_base = __get_free_pages(GFP_KERNEL|GFP_DMA, img_buff[0].order);

if (img_buff[0].virt_base == (unsigned long)NULL)

{

goto error0;

}

img_buff[0].phy_base = img_buff[0].virt_base - PAGE_OFFSET + PHYS_OFFSET; // the DMA address.

申请中断,分别为Codec的中断和Preview的中断

request_irq(IRQ_S3C2440_CAM_C, on_camif_irq_c, IRQF_DISABLED, "CAM_C", pdev);

request_irq(IRQ_S3C2440_CAM_P, on_camif_irq_p, IRQF_DISABLED, "CAM_P", pdev);

使能时钟,软件复位,更新CAMIF的配置。

clk_enable(pdev->clk); // and enable camif clock.

soft_reset_camif();

file->private_data = fh;

fh->dev = pdev;

update_camif_config(fh, 0);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

摄像头接口读取函数:

static ssize_t camif_read(struct file *file, char __user *data, size_t count, loff_t *ppos)

{

int i;

struct s3c2440camif_fh * fh;

struct s3c2440camif_dev * pdev;

fh = file->private_data;

pdev = fh->dev;

if (start_capture(pdev, 0) != 0) //此处是捕获一张图片,所以会阻塞在此,直至中断发生。

{

return -ERESTARTSYS;

}

//中断已经发生,数据已经更新。

disable_irq(IRQ_S3C2440_CAM_C);

disable_irq(IRQ_S3C2440_CAM_P);

for (i = 0; i < 4; i++)

{

if (img_buff[i].state != CAMIF_BUFF_INVALID) //如果数据已经更新,移动数据

{

copy_to_user(data, (void *)img_buff[i].virt_base, count);

img_buff[i].state = CAMIF_BUFF_INVALID; //设置数据无效

}

}

enable_irq(IRQ_S3C2440_CAM_P); //重新使能中断

enable_irq(IRQ_S3C2440_CAM_C);

return count;

}

开始捕获函数,设置window offset

/* start image capture.

*

* param 'stream' means capture pictures streamly or capture only one picture.

*/

static int start_capture(struct s3c2440camif_dev * pdev, int stream)

{

int ret;

u32 ciwdofst;

u32 ciprscctrl;

u32 ciimgcpt;

ciwdofst = ioread32(S3C244X_CIWDOFST); //window offset

ciwdofst |= (1<<30) // Clear the overflow indication flag of input CODEC FIFO Y

|(1<<15) // Clear the overflow indication flag of input CODEC FIFO Cb

|(1<<14) // Clear the overflow indication flag of input CODEC FIFO Cr

|(1<<13) // Clear the overflow indication flag of input PREVIEW FIFO Cb

|(1<<12); // Clear the overflow indication flag of input PREVIEW FIFO Cr

iowrite32(ciwdofst, S3C244X_CIWDOFST);

ciprscctrl = ioread32(S3C244X_CIPRSCCTRL); //Preview main scale control开始prev

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>