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中实现
ov2656
最新推荐文章于 2019-02-08 22:09:16 发布