当前位置: 首页 > news >正文

推介网官网上海专业seo公司

推介网官网,上海专业seo公司,有什么做美食的视频网站,北京外包seo公司一、概述 关于 RN6752V1 这个芯片这里就不做介绍了,看到这篇笔记的小伙伴应该都明白,虽然说 RN6752V1 芯片是 AHD 信号的解码芯片,但是也可以把芯片当做是一个 YUV 信号的 MIPI 摄像头,所以驱动的编写和 MIPI 摄像头无太大的区别。…

一、概述

关于 RN6752V1 这个芯片这里就不做介绍了,看到这篇笔记的小伙伴应该都明白,虽然说 RN6752V1 芯片是 AHD 信号的解码芯片,但是也可以把芯片当做是一个 YUV 信号的 MIPI 摄像头,所以驱动的编写和 MIPI 摄像头无太大的区别。这里主要是介绍具体的函数,关于 MIPI 驱动的框架程序看我之前的笔记:Linux MIPI 摄像头驱动框架编写(RN6752解码芯片)

二、RN6752 帧格式

RN6752 支持 DVP 和 MIPI 信号,这里我主要是对 MIPI 信号的使用,当然 DVP 通信的操作也可以做参考。

  1. 寄存地配置通过代理商提供的头文件中可以获取到相关寄存器的配置,如下所示:

static const struct sensor_register rn6752_fhd_1080P25_video[] = {{ 0x19, 0x0A }, // 视频格式检测滞后控制{ 0x81, 0x01 }, // 打开视频解码器{ 0xDF, 0xFE }, // 启用HD格式{ 0xF0, 0xC0 },	// 使能 FIFO 和 144 MHz 解码器输出{ 0xA3, 0x04 }, // 启用 HD 输出{ 0x88, 0x40 }, // 禁用 SCLK1 输出{ 0xF6, 0x40 }, // 禁用 SCLK3A 输出/* 切换到ch0(默认;可选) */{ 0xFF, 0x00 },	// 寄存器集选择{ 0x33, 0x10 }, // 检测中的视频{ 0x4A, 0xA8 }, // 检测中的视频{ 0x00, 0x20 }, // internal use*{ 0x06, 0x08 }, // internal use*{ 0x07, 0x63 }, // 高清格式{ 0x2A, 0x01 }, // 滤波器控制{ 0x3A, 0x24 }, // 在SAV/EAV代码中插入通道ID{ 0x3F, 0x10 }, // 通道ID{ 0x4C, 0x37 }, // 均衡器{ 0x4F, 0x03 }, // 同步控制{ 0x50, 0x03 }, // 1080p分辨率{ 0x56, 0x02 }, // 144M 和 BT656模式{ 0x5F, 0x44 }, // 消隐电平{ 0x63, 0xF8 }, // 滤波器控制{ 0x59, 0x00 }, // 扩展寄存器存取{ 0x5A, 0x48 }, // 扩展寄存器的数据{ 0x58, 0x01 }, // 启用扩展寄存器写入{ 0x59, 0x33 }, // 扩展寄存器存取{ 0x5A, 0x23 }, // 扩展寄存器的数据{ 0x58, 0x01 }, // 启用扩展寄存器写入{ 0x51, 0xF4 }, // 比例因子1{ 0x52, 0x29 }, // 比例因子2{ 0x53, 0x15 }, // 比例因子3{ 0x5B, 0x01 }, // H-标度控制{ 0x5E, 0x08 }, // 启用H缩放控制{ 0x6A, 0x87 }, // H-标度控制{ 0x28, 0x92 }, // 剪裁{ 0x03, 0x80 }, // 饱和{ 0x04, 0x80 }, // 颜色{ 0x05, 0x04 }, // 尖锐{ 0x57, 0x23 }, // 黑色/白色拉伸{ 0x68, 0x00 }, // coring{ 0x37, 0x33 }, // { 0x61, 0x6C }, //
#ifdef USE_BLUE_SCREEN{ 0x3A, 0x24 }, // AHD 断开链接时,屏幕为蓝色
#else{ 0x3A, 0x2C }, // AHD 断开链接时,屏幕为黑色{ 0x3B, 0x00 }, //{ 0x3C, 0x80 }, //{ 0x3D, 0x80 }, //
#endif{ 0x2E, 0x30 }, // 强制不播放视频{ 0x2E, 0x00 }, // 回归平常/* mipi 连接 */{ 0xFF, 0x09 }, // 切换到 mipi tx1{ 0x00, 0x03 }, // enable bias{ 0xFF, 0x08 }, // 切换到 mipi csi1{ 0x04, 0x03 }, // csi1 和 tx1 重置{ 0x6C, 0x11 }, // 禁用 ch 输出,打开 ch0
#ifdef USE_MIPI_4LANES{ 0x06, 0x7C }, // mipi 4 线
#else{ 0x06, 0x4C }, // mipi 2 线
#endif{ 0x21, 0x01 }, // 启用 hs 时钟{ 0x34, 0x06 }, //{ 0x35, 0x0B }, // { 0x78, 0xC0 }, // ch0 的 Y/C 计数{ 0x79, 0x03 }, // ch0 的 Y/C 计数{ 0x6C, 0x01 }, // 启用 ch 输出{ 0x04, 0x00 }, // csi1 和 tx1 重置完成{ 0x20, 0xAA }, // 
#ifdef USE_MIPI_NON_CONTINUOUS_CLOCK{ 0x07, 0x05 }, // 启用非连续时钟
#else{ 0x07, 0x04 }, // 启用连续时钟
#endif{ 0xFF, 0x0A }, // 切换到 mipi csi3{ 0x6C, 0x10 }, // 禁用 ch 输出;关闭 ch0~3{REG_NULL, 0x00},
};

注意: 其他格式的寄存器我这里就不附上了,可以参考代理商提供的头文件

  1. 将配置信息存入帧列表中

static const struct rn6752_framesize rn6752_mipi_framesizes[] = {{.width		= 1280,.height		= 720,.max_fps = {.numerator = 10000,.denominator = 250000,},.regs		= rn6752_fhd_720P25_video,}, {.width		= 1280,.height		= 720,.max_fps = {.numerator = 10000,.denominator = 300000,},.regs		= rn6752_fhd_720P30_video,}, {.width		= 1920,.height		= 1080,.max_fps = {.numerator = 10000,.denominator = 250000,},.regs		= rn6752_fhd_1080P25_video,}, {.width		= 1920,.height		= 1080,.max_fps = {.numerator = 10000,.denominator = 300000,},.regs		= rn6752_fhd_1080P30_video,}, {.width		= 1280,.height		= 960,.max_fps = {.numerator = 10000,.denominator = 250000,},.regs		= rn6752_fhd_960P25_video,}, {.width		= 1280,.height		= 960,.max_fps = {.numerator = 10000,.denominator = 300000,},.regs		= rn6752_fhd_960P30_video,}
};

  1. 配置默认帧在 rn6752_probe 函数中存入默认支持的帧列表,如下所示

static void rn6752_get_default_format(struct rn6752 *rn6752,struct v4l2_mbus_framefmt *format)
{format->width = rn6752->framesize_cfg[2].width; 	/* 设置默认宽度 */format->height = rn6752->framesize_cfg[2].height; 	/* 设置默认高度 */format->colorspace = V4L2_COLORSPACE_SRGB; 			/* 设置默认色彩空间为标准的 sRGB 色彩空间 */format->code = rn6752_formats[0].code; 				/* 设置默认编码格式 */format->field = V4L2_FIELD_NONE; 					/* 设置默认场模式 */
}/* rn6752_mipi_framesizes 是 rn6752 mipi 通信支持的所有帧格式 */
rn6752->framesize_cfg = rn6752_mipi_framesizes;
rn6752->cfg_num = ARRAY_SIZE(rn6752_mipi_framesizes);
/* 获取摄像头传感器支持的图像帧格式 */
rn6752_get_default_format(rn6752, &rn6752->format);
rn6752->frame_size = &rn6752->framesize_cfg[2]; 			/* 设置帧大小 */
rn6752->format.width = rn6752->framesize_cfg[2].width; 		/* 设置宽度 */
rn6752->format.height = rn6752->framesize_cfg[2].height; 	/* 设置高度 */
rn6752->fps = DIV_ROUND_CLOSEST(rn6752->framesize_cfg[2].max_fps.denominator,rn6752->framesize_cfg[2].max_fps.numerator); 			/* 设置最大帧速率 */

注意:

  • 首先将所有支持的帧列表存入了 rn6752->framesize_cfg 中

  • 将支持的列表数量存入 rn6752->cfg_num 中

  • 将默认支持的帧格式和大小存入 rn6752->format 中,这个在用户空间可以查看

  • 将默认支持的帧大小存入 rn6752->frame_size 中

  • 将默认支持的帧率存入 rn6752->fps 中

  • 以上这些默认变量将在后面的函数中经常用到,所以需要特别注意一下,不然很难理解数据从哪里来的

三、Media 设备节点

之前在 Media 子系统中提到过模块之间的关系查看命令media-ctl -p -d /dev/mediaX,通过命令可以得到驱动中的一些信息,如下图所示

  1. Media 帧大小Media 帧大小是在驱动初始化时,通过 rn6752_get_fmt 函数获取的,程序如下

static int rn6752_get_fmt(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_format *fmt)
{struct i2c_client *client = v4l2_get_subdevdata(sd); /* 获取i2c_client指针 */struct rn6752 *rn6752 = to_rn6752(sd);/* 使用dev_dbg打印日志,显示当前函数进入 */// dev_info(&client->dev, "%s enter\n", __func__);/* 条件成立时,表示要获取正在尝试的格式 */if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_APIstruct v4l2_mbus_framefmt *mf;/* 获取正在尝试的格式 */mf = v4l2_subdev_get_try_format(sd, cfg, 0);mutex_lock(&rn6752->lock);fmt->format = *mf;mutex_unlock(&rn6752->lock);return 0;
#elsereturn -ENOTTY;
#endif}/* 条件不成立时,表示要获取当前的格式 */mutex_lock(&rn6752->lock);fmt->format = rn6752->format;mutex_unlock(&rn6752->lock);/* 使用dev_dbg打印日志,显示当前格式的代码值、宽度和高度 */dev_dbg(&client->dev, "%s: %x %dx%d\n", __func__, rn6752->format.code,rn6752->format.width, rn6752->format.height);return 0;
}

  1. 帧格式判断

Media 设备是通过 rn6752_enum_frame_sizes 和 rn6752_enum_frame_interval 函数枚举了帧大小和帧率,这两个函数主要起到判断的作用,确实当前帧率是否是驱动支持的,程序如下

static int rn6752_enum_frame_sizes(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_frame_size_enum *fse)
{struct rn6752 *rn6752 = to_rn6752(sd);struct i2c_client *client = v4l2_get_subdevdata(sd);int i = ARRAY_SIZE(rn6752_formats);printk(KERN_INFO"rn6752_enum_frame_sizes................................................\n");dev_dbg(&client->dev, "%s:\n", __func__);if (fse->index >= rn6752->cfg_num)return -EINVAL;while (--i)if (fse->code == rn6752_formats[i].code)break;fse->code = rn6752_formats[i].code;fse->min_width  = rn6752->framesize_cfg[fse->index].width;fse->max_width  = fse->min_width;fse->max_height = rn6752->framesize_cfg[fse->index].height;fse->min_height = fse->max_height;return 0;
}static int rn6752_enum_frame_interval(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_frame_interval_enum *fie)
{struct rn6752 *rn6752 = to_rn6752(sd);printk(KERN_INFO"rn6752_enum_frame_interval index: %d....................\n", fie->index );/* 检查传入的 fie 结构体中的 index 字段是否超出了 rn6752 所支持的帧间隔配置数量(cfg_num) */if (fie->index >= rn6752->cfg_num)return -EINVAL;/* 检查传入的 fie 结构体中的 code 字段是否与期望的媒体总线格式(MEDIA_BUS_FMT_UYVY8_2X8)匹配 */if (fie->code != MEDIA_BUS_FMT_UYVY8_2X8)return -EINVAL;fie->width = rn6752->framesize_cfg[fie->index].width;           /* 宽 */fie->height = rn6752->framesize_cfg[fie->index].height;         /* 高 */fie->interval = rn6752->framesize_cfg[fie->index].max_fps;      /* 最大帧率 */return 0;
}

  1. 帧大小设置

可以通过 rn6752_set_fmt 函数设置帧的大小,程序如下

tatic int rn6752_set_fmt(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_format *fmt)
{struct i2c_client *client = v4l2_get_subdevdata(sd);int index = ARRAY_SIZE(rn6752_formats);struct v4l2_mbus_framefmt *mf = &fmt->format;const struct rn6752_framesize *size = NULL;struct rn6752 *rn6752 = to_rn6752(sd);printk(KERN_INFO"rn6752_set_fmt................................................\n");dev_info(&client->dev, "%s enter\n", __func__);/* 根据传入的参数调整帧大小和帧速率,并返回适合的帧大小和帧速率 */__rn6752_try_frame_size_fps(rn6752, mf, &size, rn6752->fps);/* 遍历rn6752_formats数组 */while (--index >= 0)if (rn6752_formats[index].code == mf->code)break;if (index < 0)return -EINVAL;/* 色彩空间为sRGB,场为无 */mf->colorspace = V4L2_COLORSPACE_SRGB;mf->code = rn6752_formats[index].code;mf->field = V4L2_FIELD_NONE;mutex_lock(&rn6752->lock);if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_APImf = v4l2_subdev_get_try_format(sd, cfg,fmt->pad); /* 使用v4l2_subdev_get_try_format函数获取正在尝试的格式 */*mf = fmt->format;
#elsereturn -ENOTTY;
#endif} else {if (rn6752->streaming) {mutex_unlock(&rn6752->lock);return -EBUSY;}/* 分别设置为获取到的帧大小和传入的格式 */rn6752->frame_size = size;rn6752->format = fmt->format;}mutex_unlock(&rn6752->lock);return 0;
}

  1. 帧间隔获取

static int rn6752_g_frame_interval(struct v4l2_subdev *sd,struct v4l2_subdev_frame_interval *fi)
{struct rn6752 *rn6752 = to_rn6752(sd);printk(KERN_INFO"rn6752_g_frame_interval................................................\n");mutex_lock(&rn6752->lock);fi->interval = rn6752->frame_size->max_fps;mutex_unlock(&rn6752->lock);return 0;
}

四、总线编码格式

之前有提到过,RN6752 支持 DVP 和 MIPI 总线格式,所以可以在一个驱动中实现两个功能,这里我就是写了 MIPI 的通信方式,我目前对 DVP 也不了解,以后在补上。

刚好驱动中提供了两个函数可以获取驱动总线的格式,如下所示

  1. 获取当前媒体总线配置的函数

static int rn6752_g_mbus_config(struct v4l2_subdev *sd,struct v4l2_mbus_config *config)
{printk(KERN_INFO"rn6752_g_mbus_config................................................\n");/* 总线类型是CSI-2 */config->type = V4L2_MBUS_CSI2;config->flags = V4L2_MBUS_CSI2_4_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |V4L2_MBUS_CSI2_CHANNEL_1 |V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;return 0;
}

  1. 枚举所有支持的媒体总线编码和格式

static int rn6752_enum_mbus_code(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_mbus_code_enum *code)
{struct i2c_client *client = v4l2_get_subdevdata(sd);printk(KERN_INFO"rn6752_enum_mbus_code................................................\n");dev_dbg(&client->dev, "%s:\n", __func__);if (code->index >= ARRAY_SIZE(rn6752_formats))return -EINVAL;code->code = rn6752_formats[code->index].code;return 0;
}

五、电源管理

摄像头每次开启和关闭时,都需要通过电源管理函数配置摄像头电源

static int rn6752_power(struct v4l2_subdev *sd, int on)
{struct rn6752 *rn6752 = to_rn6752(sd);struct i2c_client *client = rn6752->client;int ret = 0;/* 使用dev_info打印日志,显示当前函数和行号,并打印on参数的值 */dev_dbg(&client->dev, "%s(%d) on(%d)\n", __func__, __LINE__, on);mutex_lock(&rn6752->lock);if (rn6752->power_on == !! on)goto unlock_and_return;if (on) {ret = pm_runtime_get_sync(&client->dev);if (ret < 0) {pm_runtime_put_noidle(&client->dev);goto unlock_and_return;}rn6752->power_on = true;} else {pm_runtime_put(&client->dev);rn6752->power_on = false;}unlock_and_return:mutex_unlock(&rn6752->lock);return ret;
}

六、摄像头控制

由于这里我没有实现太多的控制功能,所以只实现了必要的两个控制,最主要的是复位时执行的 RKMODULE_SET_QUICK_STREAM 功能

static long rn6752_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{struct rn6752 *rn6752 = to_rn6752(sd);// struct rkmodule_hdr_cfg *hdr;long ret = 0;u32 stream = 0;// dev_dbg(KERN_INFO "rn6752_ioctl  0x%x..........\n", cmd);switch (cmd) {case RKMODULE_GET_MODULE_INFO:rn6752_get_module_info(rn6752, (struct rkmodule_inf *)arg);break;case RKMODULE_SET_QUICK_STREAM:stream = *((u32 *)arg);rn6752_set_streaming(rn6752, !!stream);break;default:ret = -ENOIOCTLCMD;break;}return ret;
}#ifdef CONFIG_COMPAT
static long rn6752_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd,unsigned long arg)
{void __user *up = compat_ptr(arg);struct rkmodule_inf *inf;struct rkmodule_awb_cfg *cfg;long ret;u32 stream = 0;// dev_dbg(KERN_INFO "rn6752_compat_ioctl32..........\n");switch (cmd) {case RKMODULE_GET_MODULE_INFO:inf = kzalloc(sizeof(*inf), GFP_KERNEL);if (!inf) {ret = -ENOMEM;return ret;}ret = rn6752_ioctl(sd, cmd, inf);if (!ret)ret = copy_to_user(up, inf, sizeof(*inf));kfree(inf);break;case RKMODULE_AWB_CFG:cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);if (!cfg) {ret = -ENOMEM;return ret;}ret = copy_from_user(cfg, up, sizeof(*cfg));if (!ret)ret = rn6752_ioctl(sd, cmd, cfg);kfree(cfg);break;case RKMODULE_SET_QUICK_STREAM:ret = copy_from_user(&stream, up, sizeof(u32));if (!ret)ret = rn6752_ioctl(sd, cmd, &stream);break;default:ret = -ENOIOCTLCMD;break;}return 0;
}
#endif

七、数据流控制

整个驱动最重要的便是流控制函数,通过此函数完成了摄像头的启动和停止

static int rn6752_set_streaming(struct rn6752 *rn6752, int on)
{struct i2c_client *client = rn6752->client;int ret = 0;dev_info(&client->dev, "%s: on: %d\n", __func__, on);if (on){ret = rn6752_write(client, 0x80, 0x31);usleep_range(200, 500);ret |= rn6752_write(client, 0x80, 0x30);if (ret){dev_err(&client->dev, "rn6752 soft reset failed\n");return ret;}ret = rn6752_write_array(client, rn6752->frame_size->regs);if (ret)dev_err(&client->dev, "rn6752 start initialization failed\n");}else{ret = rn6752_write(client, 0x80, 0x00);if (ret)dev_err(&client->dev, "rn6752 soft standby failed\n");}return ret;
}static int rn6752_s_stream(struct v4l2_subdev *sd, int on)
{struct i2c_client *client = v4l2_get_subdevdata(sd);struct rn6752 *rn6752 = to_rn6752(sd);int ret = 0;unsigned int fps;/* 计算帧率和延迟时间 */fps = DIV_ROUND_CLOSEST(rn6752->frame_size->max_fps.denominator,rn6752->frame_size->max_fps.numerator);dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on,rn6752->frame_size->width, rn6752->frame_size->height,DIV_ROUND_CLOSEST(rn6752->frame_size->max_fps.denominator,rn6752->frame_size->max_fps.numerator));mutex_lock(&rn6752->lock);on = !!on;if (rn6752->streaming == on)goto unlock;if (on) {ret = pm_runtime_get_sync(&client->dev);if (ret < 0){pm_runtime_put_noidle(&client->dev);goto unlock;}}rn6752->streaming = on;ret = rn6752_set_streaming(rn6752, on);if (ret)rn6752->streaming = !on;pm_runtime_put(&client->dev);unlock:mutex_unlock(&rn6752->lock);return ret;
}

注意: 摄像头驱动中并没有图像接收之类的关系,而数据流操作函数主要的作用是对芯片进行初始化,使摄像头进入工作模式。从上面的驱动程序可以看出,整个驱动并没有其他特别的功能,就是一个 I2C 控制功能,所以摄像头的驱动其实就是一个 I2C 驱动程序。

由于笔记内容有点多,这里我就不附上完成的驱动程序了,其次是驱动程序也比较简单,看完的小伙伴应该都能明白。主要的难度都在调试摄像头驱动上面,我也折腾了很久,有需要的小伙变可以看我后面的笔记

参考资料

gc2145.c 和 imx335.c 驱动程序

文章转载自:浇筑菜鸟

原文链接:https://www.cnblogs.com/jzcn/p/17825502.html

http://www.hrbkazy.com/news/17212.html

相关文章:

  • 网站建设的主要工作有哪些seo排名优化技巧
  • 吉林大学建设工程学院官方网站网页查询
  • 响应式网站尺寸百度推广平台登陆
  • 济宁哪里做网站百度站长平台电脑版
  • 跨境电商网站制作阳山网站seo
  • 福州有什么做网站的公司山东百度推广总代理
  • 郑州企业网站排名优化公司提供seo服务
  • 网站首页页面设计模板郑州短视频代运营
  • 雄安网站建设机构萧山区seo关键词排名
  • 做公司网站建设价格排名优化公司口碑哪家好
  • 武汉企业建站程序免费注册网站
  • 深圳办公室软装兰州seo新站优化招商
  • 淘宝网站的建设目标网站推广的方式有
  • 豪华跑车网站建设广告招商
  • 宝塔 怎么做网站网站自然排名工具
  • 品牌网站制作报价宁波正规seo快速排名公司
  • 磁县邯郸网站建设手游推广平台
  • 大型门户网站模板qq推广引流怎么做
  • 小说网站静态模板最有效的100个营销方法
  • 光明新区做网站百度地图导航网页版
  • 河南省住房和城乡建设厅投诉网站企业网站推广方案设计
  • 北京做网站的公司拟软文标题例子
  • 天津专门做网站的公司营销推广费用预算表
  • 家居企业网站建设渠道最近国际时事热点事件
  • 衡阳县住房和城乡建设局网站竞价推广的企业
  • asp.net网站和空网站有什么区别宁德市人民医院
  • 成都网站建设赢展最新军事报道
  • 手机模板网站模板下载网站有哪些友情链接互换网站
  • 软件公司网站模板下载十大经典案例
  • 办公用品网站建设市场定位盐城seo排名