ov7740摄像头模块
cmos摄像头驱动基于I2C驱动框架实现,在probe函数里基于V4L2框架写摄像头驱动程序。
dev函数
设备地址:
写 – 0x42(01000010)
读 – 0x43(01000011)
8bit的地址 = 7bit设备地址 + 1bit的读/写控制位
设备地址 = 0100001 = 0x21
static struct i2c_board_info cmos_ov7740_info = {
I2C_BOARD_INFO("cmos_ov7740", 0x21), //名字,设备地址
};
static struct i2c_client *cmos_ov7740_client;
static int cmos_ov7740_dev_init(void){
struct i2c_adapter *i2c_adap;
i2c_adap = i2c_get_adapter(0); //获取适配器
cmos_ov7740_client = i2c_new_device(i2c_adap, &cmos_ov7740_info); //在该适配器下创建设备
i2c_put_adapter(i2c_adap);
return 0;
}
static void cmos_ov7740_dev_exit(void){
i2c_unregister_device(cmos_ov7740_client);
}
module_init(cmos_ov7740_dev_init);
module_exit(cmos_ov7740_dev_exit);
MODULE_LICENSE("GPL");
drv函数
static const struct i2c_device_id cmos_ov7740_id_table[] = {
{
"cmos_ov7740", 0 },
{
}
};
/* 1.1. 分配、设置一个i2c_driver */
static struct i2c_driver cmos_ov7740_driver = {
.driver = {
.name = "cmos_ov7740",
.owner = THIS_MODULE,
},
.probe = cmos_ov7740_probe,
.remove = __devexit_p(cmos_ov7740_remove),
.id_table = cmos_ov7740_id_table, //用于匹配
};
static int cmos_ov7740_drv_init(void){
/* 1.2.注册 */
i2c_add_driver(&cmos_ov7740_driver);
return 0;
}
static void cmos_ov7740_drv_exit(void){
i2c_del_driver(&cmos_ov7740_driver);
}
module_init(cmos_ov7740_drv_init);
module_exit(cmos_ov7740_drv_exit);
MODULE_LICENSE("GPL");
与dev函数匹配会调用probe函数,卸载会调用remove函数。
在cmos_ov7740_probe函数里要做硬件相关的工作:映射相应寄存器,设置相应的GPIO引脚用于CAMIF,设置、使能时钟(使能HCLK,使能并设置CAMCLK = 24MHz),复位一下摄像头模块,通过IIC总线初始化摄像头模块,注册中断(每采集一帧的数据,摄像头控制器会触发一次中断)等。
问:为什么需要复位摄像头模块?
答:IIC能够正常操作CMOS摄像头模块内部的寄存器的前提是:
– 提供符合它需求的系统时钟(CAMCLK)
– 需要给它一个复位信号
问:怎样才能复位摄像头模块?
答:通过操作CAMIF控制器中相应的寄存器,让CAMRST发出复位信号,从而复位摄像头模 块,具体操作见驱动源码。
注册两个中断,编码通道和预览通道,每发出一帧数据,触发中断。
static int __devinit cmos_ov7740_probe(struct i2c_client *client,const struct i2c_device_id *id){
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
/* 2.3 硬件相关 */
/* 2.3.1 映射相应的寄存器 */
GPJCON = ioremap(0x560000d0, 4);GPJDAT = ioremap(0x560000d4, 4);GPJUP = ioremap(0x560000d8, 4);
CISRCFMT = ioremap(0x4F000000, 4);CIWDOFST = ioremap(0x4F000004, 4);CIGCTRL = ioremap(0x4F000008, 4);CIPRCLRSA1 = ioremap(0x4F00006C, 4);
CIPRCLRSA2 = ioremap(0x4F000070, 4);CIPRCLRSA3 = ioremap(0x4F000074, 4);CIPRCLRSA4 = ioremap(0x4F000078, 4);CIPRTRGFMT = ioremap(0x4F00007C, 4);
CIPRCTRL = ioremap(0x4F000080, 4);CIPRSCPRERATIO = ioremap(0x4F000084, 4);CIPRSCPREDST = ioremap(0x4F000088, 4);CIPRSCCTRL = ioremap(0x4F00008C, 4);CIPRTAREA = ioremap(0x4F000090, 4);
CIIMGCPT = ioremap(0x4F0000A0, 4);
SRCPND = ioremap(0X4A000000, 4);INTPND = ioremap(0X4A000010, 4);SUBSRCPND = ioremap(0X4A000018, 4);
/* 2.3.2 设置相应的GPIO用于CAMIF */
cmos_ov7740_gpio_cfg();
/* 2.3.3 复位一下CAMIF控制器 */
cmos_ov7740_camif_reset();
/* 2.3.4 设置、使能时钟(使能HCLK、使能并设置CAMCLK = 24MHz) */
cmos_ov7740_clk_cfg();
/* 2.3.5 复位一下摄像头模块 */
cmos_ov7740_reset();
/* 2.3.6 通过IIC总线,初始化摄像头模块 */
cmos_ov7740_client = client;
cmos_ov7740_init();
/* 2.3.7 注册中断 */
if (request_irq(IRQ_S3C2440_CAM_C, cmos_ov7740_camif_irq_c, IRQF_DISABLED , "CAM_C", NULL))
printk("%s:request_irq failed\n", __func__); //编码通道:
if (request_irq(IRQ_S3C2440_CAM_P,cmos_ov7740_camif_irq_p, IRQF_DISABLED , "CAM_P", NULL))
printk("%s:request_irq failed\n", __func__); //预览通道
/* 2.2.注册 */
if(video_register_device(&cmos_ov7740_vdev, VFL_TYPE_GRABBER, -1)) //注册video_device结构体
printk("unable to register video device\n");
return 0;
}
static int __devexit cmos_ov7740_remove(struct i2c_client *client){
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
iounmap(GPJCON);iounmap(GPJDAT);iounmap(GPJUP);
iounmap(CISRCFMT);iounmap(CIWDOFST);iounmap(CIGCTRL);iounmap(CIPRCLRSA1);
iounmap(CIPRCLRSA2);iounmap(CIPRCLRSA3);iounmap(CIPRCLRSA4);iounmap(CIPRTRGFMT);
iounmap(CIPRCTRL);iounmap(CIPRSCPRERATIO);iounmap(CIPRSCPREDST);iounmap(CIPRSCCTRL);
iounmap(CIPRTAREA);iounmap(CIIMGCPT);
iounmap(SRCPND);iounmap(INTPND);iounmap(SUBSRCPND);
free_irq(IRQ_S3C2440_CAM_C, NULL)