ov2656

android v2.2
linux v2.6.32
board DM3730
sensor ov2656
参考: 《Android 底层开发技术实战详解》
在OMAP平台中,可以使用高级的ISP(图像信号处理)模块通过外接i2c方式连接的Camera Sensor驱动
来获得视频帧的数据
drivers/media/video/
此目录主要由三个部分组成
video for linux 2 设备:实现文件是omap34xxcam.h和omap34xxcam.c
ISP: 实现文件是isp目录中的isp.c isph3a.c isppreview.c ispresizer.c提供通过ISP进行
3A 预览 改变大小等功能
Camera Sensor 驱动:使用v4l2-int-device结果来注册
1.注册i2c主设备和平台数据的设置
arch/arm/mach-omap2/board-xxx
//注册i2c 2 时钟为400kHz
omap_register_i2c_bus(2, 400, xxx_i2c_boardinfo, ARRAY_SIZE(xxx_i2c_boardinfo));
#define OV2656_I2C_ADDR		(0x61 >> 1)
static struct i2c_board_info __initdata xxx_i2c_boardinfo[] = {
	{
		I2C_BOARD_INFO("ov2655", OV2655_I2C_ADDR),
		.platform_data = &xxx_ov2656_platform_data,
	},
};

struct ov2656_platform_data xxx_ov2656_platform_data = {
	.power_set	 = ov2656_sensor_power_set,
	.priv_data_set	 = ov2656_sensor_set_prv_data,
	.set_xclk	 = ov2656_sensor_set_xclk,
};

2.sensor 电源设置
DM3730		GPIO_CAM_RST------GPIO_126
		GPIO_CAM_RDN------GPIO_167
static int ov2656_sensor_power_set(struct v4l2_int_device *s,
				   enum v4l2_power power)
{
	----------------
	switch (power) {
	case V4L2_POWER_ON:
		isp_configure_interface(vdev->cam->isp, &ov2656_if_config);

		if (previous_power == V4L2_POWER_OFF) {
			/* 打开模拟电源 */
			twl_i2c_write_u8(xxx);
			twl_i2c_write_u8(xxx);

			gpio_direction_output(GPIO_CAM_PDN, 0);
			gpio_direction_output(GPIO_CAM_RST, 0);
			mdelay(1);
			gpio_direction_output(GPIO_CAM_RST, 1);
			mdelay(1);
		}
		break;
	case V4L2_POWER_OFF:
		/* 下电时序*/
		twl_i2c_write_u8(xxx);
		break;
	}
3. OMAP3  isp 配置设置
drivers/media/video/isp/isp.c
int isp_configure_interface(struct device *dev,
			    struct isp_interface_config *config)
根据相关的参数配置ISP 控制器 I/F
选择并行或串行硬件preview 
数据通道shifter
像素时钟极性
8到16位的CCDC输入模式
isp_configure_interface(vdev->cam->isp, &ov2656_if_config);
/*
 *@dataline_shift: Data lane shifter.
 *                     0 - No Shift, 1 - CAMEXT[13 to 2]->CAM[11 to 0]
 *                     2 - CAMEXT[13 to 4]->CAM[9 to 0]
 *                     3 - CAMEXT[13 to 6]->CAM[7 to 0]
*/
static struct isp_interface_config ov2656_if_config = {
	.ccdc_par_ser		= ISP_PARLL,
	.dataline_shift 	= 0x1,
	.hsvs_syncdetect 	= ISPCTRL_SYNC_DETECT_VSRISE,
	.strobe 		= 0x0,
	.prestrobe 		= 0x0,
	.shutter 		= 0x0,
    	.wenlog 		= ISPCCDC_CFG_WENLOG_AND,
	.wait_hs_vs             = 2,
	.u.par.par_bridge       = 0x3,
	.u.par.par_clk_pol      = 0x0, //0 - 不反向
};
4. ov2656 driver
/drivers/media/video/ov2656.c

1)注册,删除i2c驱动
static int __init ov2656_init(void)
{
	i2c_add_driver(&ov2656_i2c_driver);
	return 0;
}

static void __exit ov2656_exit(void)
{
	i2c_del_driver(&ov2656_i2c_driver);
}

2)定义sensor i2c驱动
static struct i2_driver ov2656_i2c_driver = {
	.driver = {
		.name = "ov2656",
		.owner = THIS_MODULE,
	},
	.probe = ov2656_probe,
	.remove = ov2656_remove,
	.id_table = ov2656_id,
};

static const struct i2c_device_id ov2656_id[] = {
	{"ov2656", 0},
	{},
};

3)注册sensor为i2c 客户端和V4L2设备
static int ov2656_probe(struct i2c_client *client, 
	const struct i2c_device_id *id)
{
	---------------------------
	i2c_get_clientdata(client);
	//回调平台数据
	sensor->pdata->power_set = pdata->power_set;
	sensor->pdata->set_xclk = pdata->set_xclk;
	sensor->pdata->priv_data_set = pdata->priv_data_set;
	设置sensor的默认配置
		sensor->timeperframe.numerator = 1;
	sensor->timeperframe.denominator = 15;
	sensor->pix.width = 640;
	sensor->pix.height = 480;
	sensor->pix.pixelformat =  V4L2_PIX_FMT_YUYV;   
	sensor->v4l2_int_device = &ov2656_int_device;
	----------------------------
	i2c_set_clientdata(client, sensor);

	v4l2_int_device_register(sensor->v4l2_int_device);

}

4)V4L2 初始化设备

static struct v4l2_int_device = {
	.module = THIS_MODULE,
	.name = "ov2656",
	.type = v4l2_int_type_slave,
	.u = {
		.slave = &ov2656_slave,
	},
};

5)V4L2初始化从设备
static struct v4l2_int_slave ov2656_slave = {
	.ioctls = ov2656_ioctl_desc,
	.num_ioctls = ARRAY_SIZE(ov2656_ioctl_desc),
};
提供给上层的ioctl接口
static struct v4l2_int_ioctl_desc ov2656_ioctl_desc[] = {
};

6)加载sensor驱动的log
------------ioctl_g_priv--------------                                          
------------ioctl_s_power--------------                                         
------------ioctl_dev_init--------------                                        
ov2656 2-0030: Detect success (26,56)                                           
------------ioctl_g_fmt_cap--------------                                       
------------ioctl_s_power-------------- 
//返回sensor的私有数据地址
static int ioctl_g_priv(struct v4l2_int_device *s, void *p)
{
	struct ov2656_sensor *sensor = s->priv;
	return sensor->pdata->priv_data_set(s, p);
	return 0;
}


//电源设置
static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power new_power)
{
	
-----------
switch (new_power) {
	case V4L2_POWER_ON:
		rval = sensor->pdata->set_xclk(s, OV2656_XCLK);//24M camera时钟
		if (rval == -EINVAL)
			break;
		rval = sensor->pdata->power_set(s, V4L2_POWER_ON);
		if (rval)
			break;

		if (sensor->detected)
			ov2656_configure(s);
		else {
			rval = ioctl_dev_init(s);
			printk("-------ioctl_s_power--->dev_init----\n");
			if (rval)
				goto err_on;
		}
		break;
	case V4L2_POWER_OFF:
err_on:
		rval = sensor->pdata->power_set(s, V4L2_POWER_OFF);
		sensor->pdata->set_xclk(s, 0);
		break;
	default:
		return -EINVAL;
}

//初始化设备当从设备和主设备相连的时候

static int ioctl_dev_init(struct v4l2_int_device *s)
{
	----------
	 ov2656_detect(client);
	---------
}

//探测i2c设备,读取i2c sensor的PIDH和PIDL

static int ov2656_detect(struct i2c_client *client)
{
	u32 pidh, pidl;

	if (!client) 
		return -ENODEV;
		
	if (ov2656_read_reg(client, 1, OV2656_PIDH, &pidh)) 
		return -ENODEV;
		
	if (ov2656_read_reg(client, 1, OV2656_PIDL, &pidl))
		return -ENODEV;

	if ((pidh == OV2656_PIDH_MAGIC) && ((pidl == OV2656_PIDL_MAGIC1) )) {
		dev_info(&client->dev, "Detect success (%02X,%02X)\n", pidh, pidl);
		return pidl;
	}

	return -ENODEV;
}

static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
{
	printk("------------%s--------------\n", __func__);
	return 0;
}

5.打开sensor的log
/ # ------------ioctl_s_power--------------                                     
----------------ov2656_configure------------                                    
------------ioctl_s_power--------------                                         
------------ioctl_g_fmt_cap--------------                                       
------------ioctl_enum_fmt_cap--------------                                    
--------ioctl_enum_framesizes--frms->index=0------                              
------------ioctl_enum_frameintervals--------------                             
------------ioctl_enum_frameintervals--------------                             
--------ioctl_enum_framesizes--frms->index=1------                              
------------ioctl_enum_frameintervals--------------                             
------------ioctl_enum_frameintervals--------------                             
--------ioctl_enum_framesizes--frms->index=2------                              
------------ioctl_enum_fmt_cap--------------                                    
------------ioctl_g_fmt_cap--------------                                       
------------ioctl_s_fmt_cap--------------                                       
------------ioctl_s_parm--------------                                          
------------ioctl_s_power--------------                                         
----------------ov2656_configure------------  
1)设置电源
2)ov2656_configure
static int ov2656_configure(struct v4l2_int_device *s)
{
	------------------------------
	//写复位寄存器
	ov2656_write_reg(client, OV2656_SYS, 0x80);
	mdelay(5);
	/* Common registers */
	err = ov2656_write_regs(client, ov2656_common[0]);
	sensor->hsize = pix->width;
	sensor->vsize = pix->height;
	
	/* Store image size */
	sensor->width = pix->width;
	sensor->height = pix->height;

	sensor->crop_rect.left = 0;
	sensor->crop_rect.width = pix->width;
	sensor->crop_rect.top = 0;
	sensor->crop_rect.height = pix->height;
	--------------------------
}
3)ioctl_g_fmt_cap
查询出一种格式,查询驱动所支持的格式
static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
{
	return 0;
}
4)ioctl_enum_framesizes
帧大小
static int ioctl_enum_framesizes(struct v4l2_int_device *s,
				 struct v4l2_frmsizeenum *frms)
{
	if (frms->index >= 2)
		return -EINVAL;

	frms->pixel_format = V4L2_PIX_FMT_YUYV;
	frms->type = V4L2_FRMSIZE_TYPE_DISCRETE;
	frms->discrete.width = 640;
	frms->discrete.height = 480;

}
5)ioctl_enum_frameintervals
每秒15帧
static int ioctl_enum_frameintervals(struct v4l2_int_device *s,
				     struct v4l2_frmivalenum *frmi)
{
	if (frmi->index >= 1) 
			return -EINVAL;
	frmi->type = V4L2_FRMIVAL_TYPE_DISCRETE;
	frmi->discrete.numerator = 1 ;
	frmi->discrete.denominator = 15;		
	return 0;
}
6)ioctl_s_fmt_cap
设置格式
static int ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
{	
	struct ov2656_sensor *sensor = s->priv;
	struct v4l2_pix_format *pix = &f->fmt.pix;
	sensor->pix = *pix;
	return 0;
}
7)ioctl_s_parm
设置参数
static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
{
	printk("------------%s--------------\n", __func__);
	
	return 0;
}

8)寄存器配置列表

const static struct ov2656_reg ov2656_common[2][300] = {
	/* XGA_Default settings */
	{
	     //IO & Clock & Analog Setup
  {0x308c,0x80}, //TMC12: DIS_MIPI_RW
  {0x308d,0x0e}, //TMC13: MIPI disable
  {0x360b,0x00},
  {0x30b0,0xff}, //IO_CTRL0: Cy[7:0]
  {0x30b1,0xff}, //IO_CTRL1: C_VSYNC,C_STROBE,C_PCLK,C_HREF,Cy[9:8]
  {0x30b2,0x2c}, //IO_CTRL2: R_PAD[3:0]
  {0x300f,0xa6},
  {0x3010,0x81},
  {0x3082,0x01},
  {0x30f4,0x01},
  {0x3091,0xc0},
  {0x30ac,0x42},
  {0x30d1,0x08},
  {0x3015,0x02}, //AUTO_3: AGC ceiling = 4x, 5dummy frame
  {0x3093,0x00},
  {0x307e,0xe5}, //TMC8: AGC[7:6]=b'11
  {0x3079,0x00},
  {0x3017,0x40}, //AUTO_5: disable data drop, manual banding counter=0
  {0x30f3,0x82},
  {0x306a,0x0c}, //0x0c->0x0f Joe 0814 : BLC
  {0x306d,0x00},
  {0x336a,0x3c},
  {0x3076,0x6a}, //TMC0: VSYNC drop option: drop
  {0x30d9,0x95},
  {0x3601,0x30},
  {0x304e,0x88},
  {0x30f1,0x82},
  {0x306f,0x14},
  {0x302a,0x02},
  {0x302b,0x6a},
  {0x3012,0x10},  
  {0x3018,0x80}, // jerry, 0624
  {0x3019,0x70}, 
  {0x301a,0xd4},   //Wonder 20090909
  {0x3013,0xf7}, 
  {0x30af,0x10},
  {0x304a,0x00},
  {0x304f,0x00},
  {0x30a3,0x80}, //Wonder From OV, 20090615 0x10
  {0x3013,0xf7}, 
  {0x3014,0xa4}, //R1D bit6 always = 0 , bit[5]=1, bit[0]=1   //Wonder for AE oscillation
  {0x3071,0x00},
  {0x3073,0x00},
  {0x304d,0x42},
  {0x304a,0x00}, //Disable 50/60 auto detection function, due to ov2650 no this function
  {0x304f,0x00}, //Disable 50/60 auto detection function, due to ov2650 no this function
  {0x3095,0x07},
  {0x3096,0x16},
  {0x3097,0x1d},
  {0x3020,0x01}, 
  {0x3021,0x18}, 
  {0x3022,0x00},
  {0x3023,0x06},
  {0x3024,0x06},
  {0x3025,0x58},
  {0x3026,0x02},
  {0x3027,0x5e},
  
  {0x3088,0x02},// ISP_XOUT 640
  {0x3089,0x80},
  {0x308a,0x01},// ISP_YOUT 480
  {0x308b,0xe0},
  
  {0x3316,0x64},
  {0x3317,0x25},
  {0x3318,0x80},
  {0x3319,0x08},
  {0x331a,0x64},
  {0x331b,0x4b},
  {0x331c,0x00},
  {0x331d,0x38},
  {0x3100,0x00},
  {0x3320,0xfa},   //Jerry 0x9a
  {0x3321,0x11},   
  {0x3322,0x92},   
  {0x3323,0x01},   
  {0x3324,0x97},   //Jerry 0x92   
  {0x3325,0x02},   
  {0x3326,0xff},   
  {0x3327,0x10},   //0x0c
  {0x3328,0x11},   //Jerry 0x0f
  {0x3329,0x16},   //Jerry 0x14
  {0x332a,0x59},   //Jerry 0x66
  {0x332b,0x60},   //5f -> 5c  //Jerry 0x5c
  {0x332c,0xbe},   //a5 -> 89    //Jerry 0x89
  {0x332d,0x9b},   //ac -> 96    //Jerry 0x96
  {0x332e,0x34},   //35 -> 3d    //Jerry 0x3d
  {0x332f,0x36},   //Jerry 0x2f
  {0x3330,0x49},   //Jerry 0x57
  {0x3331,0x44},   //Jerry 0x3d
  {0x3332,0xf0},   
  {0x3333,0x00},  //0x10 
  {0x3334,0xf0},   
  {0x3335,0xf0},   
  {0x3336,0xf0},   
  {0x3337,0x40},   
  {0x3338,0x40},   
  {0x3339,0x40},   
  {0x333a,0x00},   
  {0x333b,0x00},   
  {0x3380,0x27}, 
  {0x3381,0x5c}, 
  {0x3382,0x0a}, 
  {0x3383,0x29},  //0x2a
  {0x3384,0xab},  //0xad
  {0x3385,0xd3}, //d5
  {0x3386,0xbf},  
  {0x3387,0xbc}, 
  {0x3388,0x03}, 
  {0x3389,0x98}, 
  {0x338a,0x01}, 
  {0x3340,0x0c},  
  {0x3341,0x18},  
  {0x3342,0x30}, 
  {0x3343,0x3d}, 
  {0x3344,0x4b}, 
  {0x3345,0x59},  //0x60 
  {0x3346,0x67},  //0x6a
  {0x3347,0x71},  
  {0x3348,0x7d},  //0x84
  {0x3349,0x8e},  //0x96
  {0x334a,0x9b},  //0xa2
  {0x334b,0xa6},  //0xac
  {0x334c,0xb9},
  {0x334d,0xc6},
  {0x334e,0xd9},
  {0x334f,0x34},    
  {0x3350,0x35},  //Wonder for lens 20090909
  {0x3351,0x25},
  {0x3352,0x08},
  {0x3353,0x23},
  {0x3354,0x00},
  {0x3355,0x85},
  {0x3356,0x34},
  {0x3357,0x25},
  {0x3358,0x08},
  {0x3359,0x1e},
  {0x335a,0x00},
  {0x335b,0x85}, 
  {0x335c,0x35},
  {0x335d,0x25},
  {0x335e,0x08},
  {0x335f,0x18},
  {0x3360,0x00},
  {0x3361,0x85},
  {0x3363,0x01},
  {0x3364,0x03},
  {0x3365,0x02},
  {0x3366,0x00},
  {0x338b,0x0C}, //auto uv  //add saturation
  {0x338c,0x10},
  {0x338d,0x80},  //40  indoor saturation
  {0x3370,0xd0},
  {0x3371,0x00},
  {0x3372,0x00},
  {0x3374,0x10},
  {0x3375,0x10},
  {0x3376,0x04}, ///0624,jerry, for preview sharp
  {0x3377,0x00},
  {0x3378,0x04},
  {0x3379,0x40},
  {0x3069,0x80}, //Jerry
  {0x3087,0x02},
  {0x3300,0xfc},
  {0x3302,0x11},
  {0x3400,0x00},
  {0x3606,0x20},
  {0x3601,0x30},
  {0x30f3,0x83},
  {0x304e,0x88},
  {0x3086,0x0f}, 
  {0x3086,0x00}, // 
  {0x30a8,0x54}, //0x56   Wonder for Sun black
  {0x30aa,0x52},  //0x42  //Wonder for Sun black
  {0x30af,0x10}, 
  {0x30b2,0x2c}, 
  {0x30d9,0x8c},
  {0x363B,0x01},
  {0x363C,0xF2},
  {0xFFFF,0xFF},
		},
};

6.android HAL
hardware/ti/omap3/camera/V4L2Camera.cpp
CameraHardware.cpp
Converter.cpp
在startPreview()的实现在,保存预览回调函数并建立预览线程,在预览线程的循环中,等待视频数据的到达
视频数据到达后调用预览回调函数,将视频帧送出
status_t CameraHardware::startPreview()
{
 	----------
	mCamera->StartStreaming();

	 mPreviewThread = new PreviewThread(this);
	----------
}
取景器预览的主要步骤
1)在初始化的过程中,建立预览数据的内存队列
2)在startPreview()中建立预览线程
3)在预览线程的循环中,等待视频数据到达
int CameraHardware::previewThread()
{
  ----------VIDEO_FRAME---------
 yuyv422_to_yuv420sp((unsigned char *)rawFramePointer,
                                (unsigned char *)mRecordingHeap->getBase(),
                                PREVIEW_WIDTH, PREVIEW_HEIGHT);
mDataCb(CAMERA_MSG_VIDEO_FRAME, mRecordingBuffer, mCallbackCookie);
            mDataCbTimestamp(systemTime(), CAMERA_MSG_VIDEO_FRAME, mRecordingBuffer, mCallbackCookie);
--------------PREVIEW FRAME------------
yuyv422_to_yuv420sp((unsigned char *)rawFramePointer,
                              (unsigned char *)mHeap->getBase(),
                              PREVIEW_WIDTH, PREVIEW_HEIGHT);
            mDataCb(CAMERA_MSG_PREVIEW_FRAME, mBuffer, mCallbackCookie);
-------------------------------------------------------

4)在预览到达后使用预览回调机制将视频向上传送
int CameraHardware::pictureThread()
{
 -----------------
 if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {
        LOGE("Take Picture COMPRESSED IMAGE");
        mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mCamera->GrabJpegFrame(), mCallbackCookie);
    }
 ------------------
}

录制视频的主要步骤
1)在startRecording()的实现中保存录制视频回调函数
2)录制视频可以使用自己的线程,也可以使用预览线程
status_t CameraHardware::startRecording()
{
 
}
3)通过录制回调函数将视频帧送出
void CameraHardware::releaseRecordingFrame(const sp<IMemory>& mem)
{

}
当调用releaseRecordingFrame()后,表示上层通知Camera 硬件抽象层,这一帧的内存
已经用完,可以进行下一次的处理。

camera 处理流程
1)使用映射内核内存的方式V4L2_MEMORY_MMAP,构建预览的内存MemoryHeapBase需要
从V4L2驱动程序中得到内存指针
status_t CameraHardware::startPreview()
{
	-----------------
    mPreviewFrameSize = PREVIEW_WIDTH * PREVIEW_HEIGHT * 2;

    mHeap = new MemoryHeapBase(mPreviewFrameSize);
	----------------
}
2)在预览线程中,使用VIDIOC_DQBUF调用阻塞等待视频帧的到来,处理完成后使用VIDIOC_QBUF调用
将帧内存再次压入队列,然后等待下一帧的到来
ioctl(fd, VIDIOC_DQBUF, &videoIn->buf);
ioctl(fd, VIDIOC_QBUF, &videoIn->buf);

7. CameraHardware.cpp
打开摄像头
E/CameraHardware(  913): return Preview Heap                                    
E/CameraHardware(  913): beginAutoFocusThread                                   
E/CameraHardware(  913): Picture Thread                                         
E/CameraHardware(  913): Take Picture RAW IMAGE                                 
E/CameraHardware(  913): Take Picture COMPRESSED IMAGE                          
E/CameraHardware(  913): pictureThread: preview started  
调用
sp<IMemoryHeap> CameraHardware::getPreviewHeap() const
{
    LOGE("return Preview Heap");
    return mPreviewHeap;
}
按下拍照按键
int CameraHardware::beginAutoFocusThread(void *cookie)
{
    CameraHardware *c = (CameraHardware *)cookie;
    LOGE("beginAutoFocusThread");
    return c->autoFocusThread();
}

status_t CameraHardware::takePicture()
{
    pictureThread();
    return NO_ERROR;
}

int CameraHardware::pictureThread()
{
	---------------------
	if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {
        LOGE("Take Picture COMPRESSED IMAGE");
        mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mCamera->GrabJpegFrame(), mCallbackCookie);
	LOGE("%s: preview started", __FUNCTION__);
   	mPreviewThread = new PreviewThread(this);
}

调用GrabJpegFrame()在V4L2Camera.cpp中实现


















	


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值