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);free_irq(IRQ_S3C2440_CAM_P, NULL);video_unregister_device(&cmos_ov7740_vdev);return 0;
}

硬件操作相关函数

static void cmos_ov7740_gpio_cfg(void){*GPJCON = 0x2aaaaaa; /* 设置相应的GPIO用于CAMIF */*GPJDAT = 0; *GPJUP = 0;    /* 使能上拉电阻 */
}static void cmos_ov7740_camif_reset(void){ *CISRCFMT |= (1<<31);    /* 传输方式为BT601 */    *CIGCTRL |= (1<<31); mdelay(10); /* 复位CAMIF控制器 */*CIGCTRL &= ~(1<<31);    mdelay(10);
}static void cmos_ov7740_clk_cfg(void){/* 使能CAMIF的时钟源 */struct clk *camif_clk = clk_get(NULL, "camif");if(!camif_clk || IS_ERR(camif_clk))printk(KERN_INFO "failed to get CAMIF clock source\n");clk_enable(camif_clk);/* 使能并设置CAMCLK = 24MHz */struct clk *camif_upll_clk = clk_get(NULL, "camif-upll");clk_set_rate(camif_upll_clk, 24000000);mdelay(100);
}/* 注意:1.S3C2440提供的复位时序(CAMRST)为:0->1->0(0:表示正常工作的电平、1:表示复位电平)但是,实验证明,该复位时序与我们的OV7740需要的复位时序(1->0->1)不符合。2.因此,我们就应该结合OV7740的具体复位时序,来设置相应的寄存器。  */
static void cmos_ov7740_reset(void){*CIGCTRL |= (1<<30); mdelay(30);*CIGCTRL &= ~(1<<30); mdelay(30);*CIGCTRL |= (1<<30);  mdelay(30);
}

通过I2C总线,初始化摄像头模块。用核心层提供的读写函数。

typedef struct cmos_ov7740_i2c_value {unsigned char regaddr; //地址unsigned char value;    //值
}ov7740_t/* init:  640x480,30fps的,YUV422输出格式 */
ov7740_t ov7740_setting_30fps_VGA_640_480[] ={ //初始化数组{0x12, 0x80},    {0x47, 0x02},......{0x4E, 0x14},{0x38, 0x11},{0x84, 0x70}
};static void cmos_ov7740_init(void){unsigned int mid;/* 读 */mid = i2c_smbus_read_byte_data(cmos_ov7740_client, 0x0a)<<8;mid |= i2c_smbus_read_byte_data(cmos_ov7740_client, 0x0b);printk("manufacture ID = 0x%4x\n", mid);/* 写 */for(int i = 0; i < OV7740_INIT_REGS_SIZE; i++){i2c_smbus_write_byte_data(cmos_ov7740_client, ov7740_setting_30fps_VGA_640_480[i].regaddr, ov7740_setting_30fps_VGA_640_480[i].value);mdelay(2);}
}

video_device结构体相关函数

/* 2.1. 分配、设置一个video_device结构体 */
static struct video_device cmos_ov7740_vdev = {.fops           = &cmos_ov7740_fops,.ioctl_ops     = &cmos_ov7740_ioctl_ops,.release      = cmos_ov7740_release,.name            = "cmos_ov7740",
};/*    注意:该函数是必须的,否则在insmod的时候,会出错 */
static void cmos_ov7740_release(struct video_device *vdev){unsigned int order;order = get_order(buf_size);free_pages(img_buff[0].virt_base, order);img_buff[0].phy_base = (unsigned long)NULL;free_pages(img_buff[1].virt_base, order);img_buff[1].phy_base = (unsigned long)NULL;   free_pages(img_buff[2].virt_base, order);img_buff[2].phy_base = (unsigned long)NULL;       free_pages(img_buff[3].virt_base, order);img_buff[3].phy_base = (unsigned long)NULL;
}
static const struct v4l2_file_operations cmos_ov7740_fops = {.owner             = THIS_MODULE,.open            = cmos_ov7740_open,.release            = cmos_ov7740_close,.unlocked_ioctl     = video_ioctl2,.read              = cmos_ov7740_read,
};/* A1 */
static int cmos_ov7740_open(struct file *file){return 0;
}/* A18 关闭 */
static int cmos_ov7740_close(struct file *file){    return 0;
}

以读的方式获取摄像头数据,在读函数中休眠,在预览中断函数中唤醒。

static DECLARE_WAIT_QUEUE_HEAD(cam_wait_queue);
/* 中断标志 */
static volatile int ev_cam = 0;
static irqreturn_t cmos_ov7740_camif_irq_c(int irq, void *dev_id) {  //编码通道return IRQ_HANDLED;
}static irqreturn_t cmos_ov7740_camif_irq_p(int irq, void *dev_id) {     //预览通道 *SRCPND = 1<<6;   /* 清中断 */*INTPND = 1<<6;*SUBSRCPND = 1<<12;ev_cam = 1;  //中断标志位wake_up_interruptible(&cam_wait_queue);  //唤醒return IRQ_HANDLED;
}/* 应用程序通过读的方式读取摄像头的数据 */
static ssize_t cmos_ov7740_read(struct file *filep, char __user *buf, size_t count, loff_t *pos){size_t end= min_t(size_t, buf_size, count);wait_event_interruptible(cam_wait_queue, ev_cam);  //休眠,参数:等待队列,中断标志for(int i=0; i<4; i++){if(copy_to_user(buf, (void *)img_buff[i].virt_base, end))    //将缓存数据传到用户空间return -EFAULT;}ev_cam = 0;return end;
}

v4l2相关操作函数

static const struct v4l2_ioctl_ops cmos_ov7740_ioctl_ops = {// 表示它是一个摄像头设备.vidioc_querycap      = cmos_ov7740_vidioc_querycap,/* 用于列举、获得、测试、设置摄像头的数据的格式 */.vidioc_enum_fmt_vid_cap  = cmos_ov7740_vidioc_enum_fmt_vid_cap,.vidioc_g_fmt_vid_cap     = cmos_ov7740_vidioc_g_fmt_vid_cap,.vidioc_try_fmt_vid_cap   = cmos_ov7740_vidioc_try_fmt_vid_cap,.vidioc_s_fmt_vid_cap     = cmos_ov7740_vidioc_s_fmt_vid_cap,/* 缓冲区操作: 申请/查询/放入队列/取出队列 */.vidioc_reqbufs       = cmos_ov7740_vidioc_reqbufs,
/* 说明: 因为我们是通过读的方式来获得摄像头数据,因此查询/放入队列/取出队列这些操作函数将不在需要 */
#if 0.vidioc_querybuf      = myuvc_vidioc_querybuf,.vidioc_qbuf          = myuvc_vidioc_qbuf,.vidioc_dqbuf         = myuvc_vidioc_dqbuf,
#endif// 启动/停止.vidioc_streamon      = cmos_ov7740_vidioc_streamon,.vidioc_streamoff     = cmos_ov7740_vidioc_streamoff,
};

摄像头设备:cmos_ov7740_vidioc_querycap
列举支持哪种格式:cmos_ov7740_vidioc_enum_fmt_vid_cap
获得当前数据格式:cmos_ov7740_vidioc_g_fmt_vid_cap
测试驱动程序是否支持某种格式:cmos_ov7740_vidioc_try_fmt_vid_cap

struct cmos_ov7740_fmt {char  *name;u32   fourcc;          /* v4l2 format id */int   depth;
};
static struct cmos_ov7740_fmt formats[] = {{.name     = "RGB565",.fourcc   = V4L2_PIX_FMT_RGB565,.depth    = 16,},{.name     = "PACKED_RGB_888",.fourcc   = V4L2_PIX_FMT_RGB24,.depth    = 24,},
};
/* A2 摄像头设备 */
static int cmos_ov7740_vidioc_querycap(struct file *file, void  *priv,struct v4l2_capability *cap){memset(cap, 0, sizeof *cap);strcpy(cap->driver, "cmos_ov7740");strcpy(cap->card, "cmos_ov7740");cap->version = 2;cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;  //读方式/ioctl方式return 0;
}/* A3 列举支持哪种格式 */
static int cmos_ov7740_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,struct v4l2_fmtdesc *f){struct cmos_ov7740_fmt *fmt;if (f->index >= ARRAY_SIZE(formats))return -EINVAL;fmt = &formats[f->index];   //cmos_ov7740_fmt数组strlcpy(f->description, fmt->name, sizeof(f->description));f->pixelformat = fmt->fourcc;return 0;
}/* A4 返回当前所使用的格式 */
static int cmos_ov7740_vidioc_g_fmt_vid_cap(struct file *file, void *priv,struct v4l2_format *f){return 0;
}/* A5 测试驱动程序是否支持某种格式, 强制设置该格式 */
static int cmos_ov7740_vidioc_try_fmt_vid_cap(struct file *file, void *priv,struct v4l2_format *f){if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE){ //是否为摄像头设备return -EINVAL;}if ((f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB565) && (f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB24))return -EINVAL; //是否为RGB格式return 0;
}

设置数据格式:cmos_ov7740_vidioc_s_fmt_vid_cap
CIPRTRGFMT(目标图片格式):目标图片的水平像素大小,是否旋转,表示目标图片的垂直像素大小

/* A6  设置数据格式*/
static int cmos_ov7740_vidioc_s_fmt_vid_cap(struct file *file, void *priv,struct v4l2_format *f){int ret = cmos_ov7740_vidioc_try_fmt_vid_cap(file, NULL, f);  //测试if (ret < 0) return ret;TargetHsize_Pr = f->fmt.pix.width;   //目标图片水平大小(用户传入)TargetVsize_Pr = f->fmt.pix.height; //目标图片垂直大小if(f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565){   //支持RGB565和RGB24*CIPRSCCTRL &= ~(1<<30);f->fmt.pix.bytesperline = (f->fmt.pix.width * 16) >> 3; //行大小f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;  //大小buf_size = f->fmt.pix.sizeimage;    //用于申请缓冲区bytesperline = f->fmt.pix.bytesperline;}else if(f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24){*CIPRSCCTRL |= (1<<30);f->fmt.pix.bytesperline = (f->fmt.pix.width * 32) >> 3;f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;buf_size = f->fmt.pix.sizeimage;bytesperline = f->fmt.pix.bytesperline;}/* CIPRTRGFMT:bit[28:16] -- 表示目标图片的水平像素大小(TargetHsize_Pr)bit[15:14] -- 是否旋转,我们这个驱动就不选择了bit[12:0]   -- 表示目标图片的垂直像素大小(TargetVsize_Pr) */*CIPRTRGFMT = (TargetHsize_Pr<<16)|(0x0<<14)|(TargetVsize_Pr<<0);return 0;
}

申请缓冲区:cmos_ov7740_vidioc_reqbufs
__get_free_pages最大分配4M,kmalloc最大128K。
分配4块缓存

struct camif_buffer{unsigned int order;          //大小unsigned long virt_base;    //虚拟地址unsigned long phy_base;       //物理地址
};
struct camif_buffer img_buff[] ={{.order = 0,.virt_base = (unsigned long)NULL,.phy_base = (unsigned long)NULL       },{.order = 0,.virt_base = (unsigned long)NULL,.phy_base = (unsigned long)NULL       },{.order = 0,.virt_base = (unsigned long)NULL,.phy_base = (unsigned long)NULL       },{.order = 0,.virt_base = (unsigned long)NULL,.phy_base = (unsigned long)NULL       }
};

CIPRCLRSA1,CIPRCLRSA2,CIPRCLRSA3,CIPRCLRSA4:存放帧RGB数据

static int cmos_ov7740_vidioc_reqbufs(struct file *file, void *priv,struct v4l2_requestbuffers *p){unsigned int order;order = get_order(buf_size);  //获得大小(在S_font_vid_cap中设置)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){printk("error0\n");goto error0;}img_buff[0].phy_base = __virt_to_phys(img_buff[0].virt_base);img_buff[1].order = order;img_buff[1].virt_base = __get_free_pages(GFP_KERNEL|__GFP_DMA, img_buff[1].order);if(img_buff[1].virt_base == (unsigned long)NULL){printk("error1\n");goto error1;}img_buff[1].phy_base = __virt_to_phys(img_buff[1].virt_base);img_buff[2].order = order;img_buff[2].virt_base = __get_free_pages(GFP_KERNEL|__GFP_DMA, img_buff[2].order);if(img_buff[2].virt_base == (unsigned long)NULL){printk("error2\n");goto error2;}img_buff[2].phy_base = __virt_to_phys(img_buff[2].virt_base);img_buff[3].order = order;img_buff[3].virt_base = __get_free_pages(GFP_KERNEL|__GFP_DMA, img_buff[3].order);if(img_buff[3].virt_base == (unsigned long)NULL){printk("error3\n");goto error3;}img_buff[3].phy_base = __virt_to_phys(img_buff[3].virt_base);*CIPRCLRSA1 = img_buff[0].phy_base;*CIPRCLRSA2 = img_buff[1].phy_base;*CIPRCLRSA3 = img_buff[2].phy_base;*CIPRCLRSA4 = img_buff[3].phy_base;return 0;
error3:free_pages(img_buff[2].virt_base, order);img_buff[2].phy_base = (unsigned long)NULL;
error2:free_pages(img_buff[1].virt_base, order);img_buff[1].phy_base = (unsigned long)NULL;
error1:free_pages(img_buff[0].virt_base, order);img_buff[0].phy_base = (unsigned long)NULL;
error0: return -ENOMEM;
}

启动传输:cmos_ov7740_vidioc_streamon
停止传输:cmos_ov7740_vidioc_streamoff

CISRCFMT(源图片格式):选择传输方式,设置偏移值,源图片的水平像素值(640),源图片的垂直像素值(480),源图片的颜色顺序(CbYCrY,YCbYCr等)
CIWDOFST(窗口功能):使能窗口功能,水平方向的裁剪的大小,垂直方向的裁剪的大小
CIGCTRL(全局控制):软件复位CAMIF控制器,复位外部摄像头模块,选择信号源(00 = 输入源来自摄像头模块),像素时钟的极性,VSYNC的极性,HREF的极性
CIPRCTRL(DMA控制(突发长度)):主突发长度,剩余突发长度,是否使能LastIRQ功能
预览缩放功能寄存器:
CIPRSCPRERATIO:预览缩放的变化系数,预览缩放的水平比,预览缩放的垂直比
CIPRSCPREDST:预览缩放的目标宽度, 预览缩放的目标高度
CIPRSCCTRL:告诉摄像头控制器(图片是缩小、放大),预览主缩放的水平比,预览主缩放的垂直比,设置图像输出格式是RGB16、RGB24,预览缩放开始

CIPRTAREA(DMA目标区域):预览通道的目标区域(目标水平x目标垂直)
CIIMGCPT(使能):使能摄像头控制器,使能编码通道,使能预览通道

/* A11 启动传输 参考: uvc_video_enable(video, 1):uvc_commit_video,uvc_init_video*/
static int cmos_ov7740_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i{unsigned int Main_burst, Remained_burst;/*CISRCFMT:bit[31]   -- 选择传输方式为BT601或者BT656  bit[30] -- 设置偏移值(0 = +0 (正常情况下) - for YCbCr)bit[29]   -- 保留位,必须设置为0       bit[28:16]  -- 设置源图片的水平像素值(640)bit[15:14]   -- 设置源图片的颜色顺序(0x0c --> 0x2)   bit[12:0] -- 设置源图片的垂直像素值(480)*/*CISRCFMT |= (0<<30)|(0<<29)|(CAM_SRC_HSIZE<<16)|(CAM_ORDER_CbYCrY<<14)|(CAM_SRC_VSIZE<<0);/*CIWDOFST:bit[31] -- 1 = 使能窗口功能、0 = 不使用窗口功能    bit[30、15:12]-- 清除溢出标志位bit[26:16] -- 水平方向的裁剪的大小 bit[10:0]  -- 垂直方向的裁剪的大小*/*CIWDOFST |=(1<<30)|(0xf<<12);*CIWDOFST |= (1<<31)|(WinHorOfst<<16)|(WinVerOfst<<0);SRC_Width = CAM_SRC_HSIZE - 2*WinHorOfst;SRC_Height = CAM_SRC_VSIZE - 2*WinVerOfst;/*CIGCTRL:bit[31] -- 软件复位CAMIF控制器    bit[30] -- 用于复位外部摄像头模块bit[29] -- 保留位,必须设置为1  bit[28:27] -- 用于选择信号源(00 = 输入源来自摄像头模块)bit[26]  -- 设置像素时钟的极性(猜0)    bit[25] -- 设置VSYNC的极性(0)bit[24] -- 设置HREF的极性(0)*/*CIGCTRL |= (1<<29)|(0<<27)|(0<<26)|(0<<25)|(0<<24);/*CIPRCTRL:bit[23:19] -- 主突发长度(Main_burst)   bit[18:14] -- 剩余突发长度(Remained_burst)   bit[2]-- 是否使能LastIRQ功能(不使能)*/CalculateBurstSize(bytesperline, &Main_burst, &Remained_burst);    //计算突发长度*CIPRCTRL = (Main_burst<<19)|(Remained_burst<<14)|(0<<2);/*CIPRSCPRERATIO:bit[31:28]: 预览缩放的变化系数(SHfactor_Pr) bit[22:16]: 预览缩放的水平比(PreHorRatio_Pr)bit[6:0]: 预览缩放的垂直比(PreVerRatio_Pr)CIPRSCPREDST:bit[27:16]: 预览缩放的目标宽度(PreDstWidth_Pr)  bit[11:0]: 预览缩放的目标高度(PreDstHeight_Pr)CIPRSCCTRL:bit[29:28]: 告诉摄像头控制器(图片是缩小、放大)(ScaleUpDown_Pr)    bit[24:16]: 预览主缩放的水平比(MainHorRatio_Pr)bit[8:0]: 预览主缩放的垂直比(MainVerRatio_Pr)  bit[31]: 必须固定设置为1bit[30]: 设置图像输出格式是RGB16、RGB24  bit[15]: 预览缩放开始*/cmos_ov7740_calculate_scaler_info();   //计算缩放信息*CIPRSCPRERATIO = (sc.SHfactor<<28)|(sc.PreHorRatio<<16)|(sc.PreVerRatio<<0);*CIPRSCPREDST = (sc.PreDstWidth<<16)|(sc.PreDstHeight<<0);*CIPRSCCTRL |= (1<<31)|(sc.ScaleUpDown<<28)|(sc.MainHorRatio<<16)|(sc.MainVerRatio<<0);/* CIPRTAREA:表示预览通道的目标区域 */*CIPRTAREA = TargetHsize_Pr * TargetVsize_Pr;//CIIMGCPT:bit[31]: 用来使能摄像头控制器  bit[30]: 使能编码通道  bit[29]: 使能预览通道*CIIMGCPT = (1<<31)|(1<<29);*CIPRSCCTRL |= (1<<15);return 0;
}
/* A17 停止   参考 : uvc_video_enable(video, 0)*/
static int cmos_ov7740_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type t){*CIPRSCCTRL &= ~(1<<15);*CIIMGCPT &= ~((1<<31)|(1<<29));return 0;
}

计算突发长度
突发长度:假如有48字节要DMA传输,但DMA一次传输10字节,就要分批传输。10就是主突发长度,剩余突发长度就是8。
48字节 = 10字节 + 10字节 + 10字节 +10字节+8字节

static void CalculateBurstSize(unsigned int hSize, unsigned int *mainBusrtSize, unsigned int *remainedBustSize){unsigned int tmp = (hSize/4)%16;switch(tmp){case 0:*mainBusrtSize = 16;*remainedBustSize = 16;break;case 4:*mainBusrtSize = 16;*remainedBustSize = 4;break;case 8:*mainBusrtSize = 16;*remainedBustSize = 8;break;default:tmp = (hSize/4)%8;switch(tmp){case 0:*mainBusrtSize = 8;*remainedBustSize = 8;break;case 4:*mainBusrtSize = 8;*remainedBustSize = 4;break;default:*mainBusrtSize = 4;tmp = (hSize/4)%4;*remainedBustSize = (tmp)?tmp:4;break;}break;}
}

计算缩放信息

struct cmos_ov7740_scaler {unsigned int PreHorRatio; //水平比unsigned int PreVerRatio;  //垂直比unsigned int H_Shift;      //水平变比unsigned int V_Shift;     //垂直变比unsigned int PreDstWidth; //目标宽度unsigned int PreDstHeight;    //目标高度unsigned int MainHorRatio;    //主缩放水平比unsigned int MainVerRatio;  //主缩放垂直比unsigned int SHfactor;      //变化unsigned int ScaleUpDown;   //放大/缩小
};static void camif_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift){if(src >= 64*tar) {return;}else if(src >= 32*tar) {*ratio = 32; *shift = 5;}else if(src >= 16*tar) {*ratio = 16; *shift = 4;}else if(src >= 8*tar) {*ratio = 8; *shift = 3;}else if(src >= 4*tar) {*ratio = 4; *shift = 2;}else if(src >= 2*tar) {*ratio = 2; *shift = 1;}else {*ratio = 1; *shift = 0;}
}static void cmos_ov7740_calculate_scaler_info(void){unsigned int sx = SRC_Width;      //源宽度unsigned int sy = SRC_Height;     //源高度unsigned int tx = TargetHsize_Pr; //目标宽度unsigned int ty = TargetVsize_Pr;    //目标水平printk("%s: SRC_in(%d, %d), Target_out(%d, %d)\n", __func__, sx, sy, tx, ty);camif_get_scaler_factor(sx, tx, &sc.PreHorRatio, &sc.H_Shift);camif_get_scaler_factor(sy, ty, &sc.PreVerRatio, &sc.V_Shift);sc.PreDstWidth = sx / sc.PreHorRatio;sc.PreDstHeight = sy / sc.PreVerRatio;  sc.MainHorRatio = (sx << 8) / (tx << sc.H_Shift);sc.MainVerRatio = (sy << 8) / (ty << sc.V_Shift);sc.SHfactor = 10 - (sc.H_Shift + sc.V_Shift);sc.ScaleUpDown = (tx>=sx)?1:0;
}

cmos摄像头-ov7740驱动相关推荐

  1. CMOS摄像头 ov7740驱动

    源码地址:https://github.com/yogach/linux-drive/tree/master/cmos_ov7740 硬件原理 自然景观->摄像头模块->硬件接口-> ...

  2. 从零开始写CMOS摄像头驱动(一)

    先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题,二维码如下: 一 ...

  3. CMOS摄像头简单分析

    CMOS摄像头:ov7740摄像头模块 1.输出分辨率有:VGA(640*480) QVGA(240*320) CIF(352*288)或者更小的任意大小. 2.有效感光阵列:656*488 3.镜头 ...

  4. Linux CMOS摄像头驱动

    1.CMOS摄像头原理 摄像头数据流向:自然景观 > 摄像头模块 > 接口 > S3C2440摄像头控制器 > LCD ov7740(摄像头模块) 输入信号:自然景观等的模拟信 ...

  5. linux cmos摄像头,Linux摄像头驱动4——CMOS摄像头

    Linux摄像头驱动学习第四篇,对CMOS摄像头进行学习,实现在Tiny4412上使用CMOS摄像头采集图像,在LCD上显示图像. 坚持到了Linux摄像头学习的最后一部分--CMOS摄像头. 写完本 ...

  6. 第三阶段应用层——2.7 视频监控—从零写CMOS摄像头驱动

    视频监控-从零写CMOS摄像头驱动 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3),OV7740摄像头 软件平台:运行于VMware Workstation 12 Player下Ubu ...

  7. CMOS摄像头驱动分析

    CMOS摄像头驱动分析 文章目录 CMOS摄像头驱动分析 ov2640_probe_dt从设备树中获取ov2640的GPIO引脚并进行初始化 v4l2_i2c_subdev_init初始化v4l2子设 ...

  8. DM642接CMOS摄像头驱动(OV7725)终于搞定了

    从接触DM642开始,所用的摄像头都是模拟的,并且它们的驱动TI都已经给好了,所以在用它们时,并没有去深究.但是在最近一个项目中,摄像头不能用模拟的,要改用CMOS的,这样就不得不去该底层的驱动了,在 ...

  9. CMOS摄像头驱动分析-i2c驱动

    CMOS摄像头驱动分析-i2c驱动 文章目录 CMOS摄像头驱动分析-i2c驱动 设备树内容 module_i2c_driver宏分析 ov2640_i2c_driver ov2640_probe 设 ...

最新文章

  1. 在rMBP上利用Python的onetimepass库实现Google Authenticator Application的效果
  2. Matlab编程序设计数字滤波器
  3. 如何更好的解决问题 : The puzzle of die
  4. false shell 判断_Shell 流程控制
  5. iOS之深入解析Xcode编译运行的原理与应用
  6. Altium Designer(AD18)常用操作和快捷方式
  7. python pcm 分贝_语音文件 pcm 静默(静音)判断
  8. 抄袭一些别人工作中的小技巧
  9. 精仿腾讯视频php源码开源免安装版,自动采集
  10. 字节跳动和腾讯不正当竞争案将于深圳开庭 抖音:我们也是看新闻才知道本月24日要开庭...
  11. Numpy的使用(3)
  12. Git:git commit后撤销commit 提交
  13. 数学分析 积分表及常用积分公式
  14. matlab进行差分检验,热传导方程几种差分格式的MATLAB数值解法比较
  15. STM32笔记之 PWM(脉宽调制)
  16. 倍福PLC——ADS上位机通讯
  17. python re库,Python中的Re库简要总结
  18. Android ExceptionThrowable 常见异常和解决方法 奔溃日志上报 monkey异常修改
  19. [附源码]Python计算机毕业设计调查问卷及调查数据统计系统Django(程序+LW)
  20. android 亮屏分析,Android亮屏速度分析总结

热门文章

  1. 抽奖动画大转盘抽奖思路与做法
  2. linux i3 桌面,桌面应用
  3. Espressif IDF学习之路(1)项目文件结构的理解
  4. 手机同声传译软件有哪些?推荐四款软件实时翻译软件
  5. 学位与学历的联系与区别
  6. Swift--Result of call to ‘xxx‘ is unused返回值未实现
  7. 使用USB在PC上操作Android手机
  8. ERP应用过程常见问题及解决建议
  9. IDEA 主题,四款牛x炫酷的主题,非常哇塞!
  10. 强大的谷歌搜索技巧_Google到底有多强大? 你能找到什么?