移植ov5640摄像头到imx6ull开发板(二)

目录

概要

重要参数结构体

enum ov5640_mode

static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1]

struct reg_value

像素格式结构体

设置参数的函数

输出分辨率设置

ov5640_change_mode_direct

像素点格式设置

ov5640_s_fmt

ov5640_g_fmt

总结

概要

在上一篇文章中,分析了驱动代码中probe函数,probe函数主要工作是初始化摄像头。但想要移植ov5640摄像头驱动,还需要梳理一下驱动代码中几个重要函数和结构体之间的关系,以及函数是如何设置参数条件的。

重要参数结构体

enum ov5640_mode

如下枚举类型,枚举了驱动代码中摄像头所支持的模式,可以看到并不支持1024*600分辨率,因此后续的移植需要添加1024*600分辨率编号

enum ov5640_mode {
	ov5640_mode_MIN = 0,
	ov5640_mode_VGA_640_480 = 0,
	ov5640_mode_QVGA_320_240 = 1,
	ov5640_mode_NTSC_720_480 = 2,
	ov5640_mode_PAL_720_576 = 3,
	ov5640_mode_720P_1280_720 = 4,
	ov5640_mode_1080P_1920_1080 = 5,
	ov5640_mode_QSXGA_2592_1944 = 6,
	ov5640_mode_QCIF_176_144 = 7,
	ov5640_mode_XGA_1024_768 = 8,
	ov5640_mode_MAX = 8
};

static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1]

这个结构体比较庞大,ov5640_mode_info_data[2]将总设备分为两类,一类是15fps,一类是30fps,之后他们各自又涵盖了所有支持设备的LCD分辨率,输出宽度,输出高度等信息(不包括像素点格式)

static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1] = {
	{
		{ov5640_mode_VGA_640_480,      640,  480,
		ov5640_setting_15fps_VGA_640_480,
		ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
		{ov5640_mode_QVGA_320_240,     320,  240,
		ov5640_setting_15fps_QVGA_320_240,
		ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
		{ov5640_mode_NTSC_720_480,     720,  480,
		ov5640_setting_15fps_NTSC_720_480,
		ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
		{ov5640_mode_PAL_720_576,      720,  576,
		ov5640_setting_15fps_PAL_720_576,
		ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
		{ov5640_mode_720P_1280_720,   1280,  720,
		ov5640_setting_15fps_720P_1280_720,
		ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
		{ov5640_mode_1080P_1920_1080, 1920, 1080,
		ov5640_setting_15fps_1080P_1920_1080,
		ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
		{ov5640_mode_QSXGA_2592_1944, 2592, 1944,
		ov5640_setting_15fps_QSXGA_2592_1944,
		ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
		{ov5640_mode_QCIF_176_144,     176,  144,
		ov5640_setting_15fps_QCIF_176_144,
		ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
		{ov5640_mode_XGA_1024_768,    1024,  768,
		ov5640_setting_15fps_XGA_1024_768,
		ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
	},
	{
		{ov5640_mode_VGA_640_480,      640,  480,
		ov5640_setting_30fps_VGA_640_480,
		ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
		{ov5640_mode_QVGA_320_240,     320,  240,
		ov5640_setting_30fps_QVGA_320_240,
		ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
		{ov5640_mode_NTSC_720_480,     720,  480,
		ov5640_setting_30fps_NTSC_720_480,
		ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
		{ov5640_mode_PAL_720_576,      720,  576,
		ov5640_setting_30fps_PAL_720_576,
		ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
		{ov5640_mode_720P_1280_720,   1280,  720,
		ov5640_setting_30fps_720P_1280_720,
		ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
		{ov5640_mode_1080P_1920_1080, 0, 0, NULL, 0},
		{ov5640_mode_QSXGA_2592_1944, 0, 0, NULL, 0},
		{ov5640_mode_QCIF_176_144,     176,  144,
		ov5640_setting_30fps_QCIF_176_144,
		ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
		{ov5640_mode_XGA_1024_768,    1024,  768,
		ov5640_setting_30fps_XGA_1024_768,
		ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
	},
};
struct ov5640_mode_info {
	enum ov5640_mode mode;
	u32 width;
	u32 height;
	struct reg_value *init_data_ptr;
	u32 init_data_size;
};

struct reg_value

将要发送的数据打包成一个结构体,由写入寄存器的地址,写入的值等组成。嵌入在struct ov5640_mode_info结构体中

struct reg_value {
	u16 u16RegAddr;
	u8 u8Val;
	u8 u8Mask;
	u32 u32Delay_ms;
};

综上:struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1]描述了除像素格式之外所有设备参数的信息,这个结构体中又包含了struct reg_valueenum ov5640_mode等数据类型,说白了就是在套娃。因此如果需要添加自己的设备信息,则需要在enum ov5640_modestruct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1]添加适配的参数即可。

像素格式结构体

struct ov5640_datafmt结构体描述了输出像素格式,结构体中的code与enum v4l2_colorspace枚举的像素格式相对应。原驱动代码只列出了YUYV和JPEG等格式,后续移植摄像头需在结构体中添加rgb模式

struct ov5640_datafmt {
	u32	code;
	enum v4l2_colorspace		colorspace;
};

static const struct ov5640_datafmt ov5640_colour_fmts[] = {
	{MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
};

设置参数的函数

输出分辨率设置

ov5640_change_mode_direct

原驱动代码中对函数有如下注释,/* change to or back to subsampling mode set the mode directly  * image size below 1280 * 960 is subsampling mode */,设置图像分辨率低于1280*960的采样方式。当然驱动代码中也存在大于1280 * 960的设置函数:ov5640_change_mode_exposure_calc

/* change to or back to subsampling mode set the mode directly
 * image size below 1280 * 960 is subsampling mode */
static int ov5640_change_mode_direct(enum ov5640_frame_rate frame_rate,
			    enum ov5640_mode mode)
{
	struct reg_value *pModeSetting = NULL;
	s32 ArySize = 0;
	int retval = 0;

	if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) {
		pr_err("Wrong ov5640 mode detected!\n");
		return -1;
	}

	pModeSetting = ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
	ArySize =
		ov5640_mode_info_data[frame_rate][mode].init_data_size;

	ov5640_data.pix.width = ov5640_mode_info_data[frame_rate][mode].width;
	ov5640_data.pix.height = ov5640_mode_info_data[frame_rate][mode].height;

	if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||
	    pModeSetting == NULL || ArySize == 0)
		return -EINVAL;

	/* set ov5640 to subsampling mode */
	retval = ov5640_download_firmware(pModeSetting, ArySize);

	/* turn on AE AG for subsampling mode, in case the firmware didn't */
	ov5640_turn_on_AE_AG(1);

	/* calculate banding filter */
	ov5640_set_bandingfilter();

	/* set AE target */
	ov5640_set_AE_target(AE_Target);

	/* update night mode setting */
	ov5640_set_night_mode(night_mode);

	/* skip 9 vysnc: start capture at 10th vsync */
	if (mode == ov5640_mode_XGA_1024_768 && frame_rate == ov5640_30_fps) {
		pr_warning("ov5640: actual frame rate of XGA is 22.5fps\n");
		/* 1/22.5 * 9*/
		msleep(400);
		return retval;
	}

	if (frame_rate == ov5640_15_fps) {
		/* 1/15 * 9*/
		msleep(600);
	} else if (frame_rate == ov5640_30_fps) {
		/* 1/30 * 9*/
		msleep(300);
	}

	return retval;
}

此函数后续移植过程中不用改动,但还是看一下它是如何设置参数的。

if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) 首先判断LCD分辨率是否支持的范围内。之后ov5640_mode_info_data就排上用场了

pModeSetting = ov5640_mode_info_data[frame_rate][mode].init_data_ptr,frame_rate表示15fps或30fps即设置输出帧率,mode:在enum ov5640_mode结构体中选择输出LCD分辨率,init_data_ptr写入ov5640传感器的值即配置寄存器。后续移植的一个重要工作就是配置支持1024*600分辨率的寄存器组,类似于如下代码:

static struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
	{0x3c07, 0x08, 0, 0}, {0x3820, 0x47, 0, 0}, {0x3821, 0x07, 0, 0},
	{0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
	{0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
	{0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
	{0x3807, 0x9b, 0, 0}, {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0},
	{0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, {0x380c, 0x07, 0, 0},
	{0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
	{0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
	{0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
	{0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
	{0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
	{0x460c, 0x20, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x01, 0, 0},
	{0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
	{0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0},
};

当然这是1024*768分辨率的寄存器组,并不符合我们的要求,后续需要根据硬件手册配置寄存器

ov5640_download_firmware,ov5640_turn_on_AE_AG,ov5640_set_bandingfilter等函数是就写入寄存器的操作,配置相应的模式,这里不多解释。

重点要理清结构体如何包含摄像头参数信息,以及函数是如何传入参数。

无论是在enum ov5640_mode还是在struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1]添加自己所设置的一些参数信息,最终都是定位到寄存器组的设置。也就是向ov5640写入对应的寄存器和对应的值

像素点格式设置

ov5640_s_fmt
static int ov5640_s_fmt(struct v4l2_subdev *sd,
			struct v4l2_mbus_framefmt *mf)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	struct ov5640 *sensor = to_ov5640(client);

	/* MIPI CSI could have changed the format, double-check */
	if (!ov5640_find_datafmt(mf->code))
		return -EINVAL;

	ov5640_try_fmt(sd, mf);
	sensor->fmt = ov5640_find_datafmt(mf->code);

	return 0;
}

从传入的形参struct v4l2_mbus_framefmt *mf可以看出此函数用于设置像素点格式

v4l2_mbus_framefmt结构体如下

struct v4l2_mbus_framefmt {
	__u32			width;   //输出宽度
	__u32			height;   //输出高度
	__u32			code;    //ov5640_datafmt结构体中的code
	__u32			field;
	__u32			colorspace;//具体像素格式
	__u16			ycbcr_enc;
	__u16			quantization;
	__u32			reserved[6];
};

看一下ov5640_s_fmt遇到的第一个函数ov5640_find_datafmt,原型如下:函数功能就是设置ov5640_colour_fmts结构体中的像素点格式,返回所选像素点格式的地址

/* Find a data format by a pixel code in an array */
static const struct ov5640_datafmt
			*ov5640_find_datafmt(u32 code)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(ov5640_colour_fmts); i++)
		if (ov5640_colour_fmts[i].code == code)
			return ov5640_colour_fmts + i;

	return NULL;
}

ov5640_try_fmt:在函数上方有这么一段注释/* MIPI CSI could have changed the format, double-check */,因此这个函数的功能依旧是设置像素点格式

static int ov5640_try_fmt(struct v4l2_subdev *sd,
			  struct v4l2_mbus_framefmt *mf)
{
	const struct ov5640_datafmt *fmt = ov5640_find_datafmt(mf->code);

	if (!fmt) {
		mf->code	= ov5640_colour_fmts[0].code;
		mf->colorspace	= ov5640_colour_fmts[0].colorspace;
	}

	mf->field	= V4L2_FIELD_NONE;

	return 0;
}
ov5640_g_fmt
static int ov5640_g_fmt(struct v4l2_subdev *sd,
			struct v4l2_mbus_framefmt *mf)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	struct ov5640 *sensor = to_ov5640(client);

	const struct ov5640_datafmt *fmt = sensor->fmt;

	mf->code	= fmt->code;
	mf->colorspace	= fmt->colorspace;
	mf->field	= V4L2_FIELD_NONE;

	return 0;
}

ov5640_g_fmt函数是获取视频数据源提供的当前像素格式。ov5640_s_fmt则是设置像素格式。因此在后续移植过程中,我们主要修改ov5640_s_fmt函数。

总结

梳理了一下包含摄像头参数的结构体以及设置参数的相关函数,我们需要修改的地方主要有:

1.enum ov5640_mode枚举类型,添加自己LCD的编号

2.static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1]结构体中添加设配自己LCD分辨率的参数

3.static const struct ov5640_datafmt ov5640_colour_fmts添加像素点格式

4.添加适配LCD分辨率的寄存器组

5.在ov5640_s_fmt函数中添加适配的设置

可以看到主要工作就是添加所使用LCD的分辨率的相关参数信息,后续开始移植ov5640摄像头驱动。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值