Linux设备驱动之Camera驱动

news2024/10/6 14:30:32

Linux设备驱动之Camera驱动

Camera,相机,平常手机使用较多,但是手机的相机怎么进行拍照的,硬件和软件,都是如何配合拍摄到图像的,下面大家一起来了解一下。

基础知识

在介绍具体Camera框架前,我们先了解一下视频设备硬件。日常嵌入式开发,经常会听到说camera,它就是嵌入式开发中的视频设备,有些时候又会听到一些什么sensor、模组等,这些介绍如下:

名词解释
cmos sensor传感器,它将外界的光信息转换为模拟信号,然后再ADC采样转换为数字信号输出
camera模组模组厂将sensor、lens、PCB等组合起来,完成一个摄像头硬件模块
DVP接口sensor数据输出方式的一种,为并口,一般有PCLK、VSYNC、HSYNC、DATA[11:0]等引脚
MIPI接口mipi接口是一个差分串行信号,有clk lane和data lane,可传输很高的数据速率
I2C接口sensor除了有数据输出接口以外,还会有I2C接口,一般会通过该接口进行寄存器配置
分辨率sensor输出图像的大小
帧率sensor每秒可输出多少张转换好的图像数据
YUV颜色编码的一种,Y表示明亮度,也就是灰阶值,U和V表示的则是色度,作用是描述影像色彩及饱和度,用于指定像素的颜色,常用NV21格式
RAW原始图像文件包含图像传感器所处理数据
ISP图像信号处理器,完成RAW到YUV的转换,并进行了若干效果处理

Camera成像原理

首先,我们平常通过眼睛是如何看到世界万物的?光,光线进入眼睛,通过晶状体,落在视网膜上,视网膜上的感光细胞浆光信号转化为神经信号,最终传递到大脑皮层,让我们感受到世界万物。上面的话很粗糙,但是整体的传递通路大差不大。那么,在日常电子产品的Camera,又是如何成像的呢?

成像原理

上图是简单的Camera成像原理,是否与人眼很类似呢。光传递信息,经过凸透镜,映射到sensor的感光区域上,凸透镜不就对应眼睛的晶状体,sensor的感光区域不就是视网膜了么。至于sensor是如何捕捉到光信息的并转换为机器可以使用的数据,我们拿一个sensor的datasheet继续跟进。

sensor block diagram

sensor datasheet中的block diagram所示,Pixel Array就是感光区域,它负责捕捉光信息并转换为电信号,电信号是模拟信号,经过模拟信号处理(信号降噪等),然后再经过ADC采样转换为数字信号,转换为数字信号之后,经过sensor内部的图像信号处理,最后通过传输模块将图像数据传递出来。

上图中的column是列,CDS是指相关双取样电路(Correlated Double Sampling),sensor每个像素的输出波形只在一部分时间内是图像信号,其余时间是复位电平和干扰。为了取出图像信号并消除干扰, 要采用取样保持电路。每个像素信号被取样后, 由一电容把信号保持下来, 直到取样下一个像素信号。

Row Decoder则是行译码器,控制电信号读取,一行行的读取像素数据。

Analog Processing,包含将模拟信号放大,滤波等功能,在设置图像增益时,则是应用到该单元。

ADC,模数转换器,将模拟信号采样转换为数字信号。

Image Signal Processing则是sensor内部的一个图像处理单元,这个单元,一般会将每个像素点的黑电平设置到一个固定值,方便后续芯片进行ISP处理,同时,还会负责输出图像的裁剪、缩放等功能。如果sensor能输出YUV格式的数据,将包含RAW转YUV功能。

V4L2

目前绝大部分的V4L2设备都是由多个组件组成,在/dev下导出多个设备节点。一般我们会基于V4L2框架,通过video_device结构体创建V4L2设备节点,并使用video2_buf出视频缓冲数据。像日常介绍的csi pipeline,里面会包含cmos sensor、mipi、csi、isp等子设备,他们共同完成图像信号的采集、转换及处理。

驱动结构

所有的V4L2驱动都会有如下结构:

  1. 每个设备示例的结构体–包含其设备状态;
  2. 初始化和控制子设备的方法(非必需);
  3. 创建V4L2设备节点(/dev/videoX等);
  4. 特定文件句柄结构体–包含每个文件句柄的数据(可用于配置sensor的一些特性等);
  5. 视频缓冲处理;

框架结构

该框架非常类似驱动结构:它有一个 v4l2_device 结构用于保存设备实例的数据;一个 v4l2_subdev 结构体代表子设备实例;video_device结构体保存 V4L2 设备节点的数据。

v4l2_device 结构体

每个设备实例都通过 v4l2_device (v4l2-device.h)结构体来表示。简单设备可以仅分配这个结构体,但在大多数情况下,都会将这个结构体嵌入到一个更大的结构体中。驱动中,将会通过v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)注册设备示例。

v4l2_subdev结构体

很多camera驱动需要与子设备通信,控制这些子设备完成各种任务,V4L2为了给驱动提供调用子设备的统一接口,创建了v4l2_subdev结构体。每个 v4l2_subdev 都包含子设备驱动需要实现的函数指针(如果对此设备不适用,可为NULL)。由于子设备可完成许多不同的工作,而在一个庞大的函数指针结构体中通常仅有少数有用的函数实现其功能肯定不合适。所以,函数指针根据其实现的功能被分类,每一类都有自己的函数指针结构体。

这些结构体定义如下:

struct v4l2_subdev_core_ops {
        int (*log_status)(struct v4l2_subdev *sd);
        int (*init)(struct v4l2_subdev *sd, u32 val);
        ...
};

struct v4l2_subdev_tuner_ops {
        ...
};

struct v4l2_subdev_audio_ops {
        ...
};

struct v4l2_subdev_video_ops {
        ...
};

struct v4l2_subdev_pad_ops {
        ...
};

struct v4l2_subdev_ops {
        const struct v4l2_subdev_core_ops  *core;
        const struct v4l2_subdev_tuner_ops *tuner;
        const struct v4l2_subdev_audio_ops *audio;
        const struct v4l2_subdev_video_ops *video;
        const struct v4l2_subdev_pad_ops *video;
};

然后驱动可通过v4l2_subdev_init进行初始化,通过v4l2_device_register_subdev向 v4l2_device 注册 v4l2_subdev。在设备注册之后,可以通过以下方式直接调用其操作函数:

	err = sd->ops->core->g_std(sd, &norm);

但使用如下宏会比较容易且合适:

    err = v4l2_subdev_call(sd, core, g_std, &norm);

I2C 子设备驱动

由于sensor大部分都是通过I2C接口传输控制数据,所以V4L2框架也提供了I2C子设备驱动,通过v4l2_i2c_subdev_init(&state->sd, client, subdev_ops)将v4l2_subdev和i2c_client建立联系。

video_device结构体

在/dev目录下的实际设备节点(比如/dev/videoX)根据video_device结构体创建,在这个结构体中,需要设置以下结构成员:

  • v4l2_dev: 设置为 v4l2_device 父设备。

  • name: 设置为唯一的描述性设备名。

  • fops: 设置为已有的 v4l2_file_operations 结构体。

  • ioctl_ops: v4l2_ioctl_ops 结构体.

  • lock: 如果你要在驱动中实现所有的锁操作,则设为 NULL 。

video_device注册

接下来你需要注册视频设备:这会为你创建一个字符设备。

    err = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
    if (err) {
            video_device_release(vdev); /* or kfree(my_vdev); */
            return err;
    }

如果 v4l2_device 父设备的 mdev 域为非 NULL 值,视频设备实体将自动注册为媒体设备。

注册哪种设备是根据类型(type)参数。存在以下类型:

  • VFL_TYPE_VIDEO: 用于视频输入/输出设备的 videoX
  • VFL_TYPE_VBI: 用于垂直消隐数据的 vbiX (例如,隐藏式字幕,图文电视)
  • VFL_TYPE_RADIO: 用于广播调谐器的 radioX

最后一个参数让你确定一个所控制设备的设备节点号数量(例如 videoX 中的 X)。通常你可以传入-1,让 v4l2 框架自己选择第一个空闲的编号。

视频缓冲辅助函数

v4l2 核心 API 提供了一个处理视频缓冲的标准方法(称为“videobuf2”)。这些方法使驱动可以通过统一的方式实现 read()、mmap() 和 overlay()。目前在设备上支持视频缓冲的方法有分散/聚集 DMA(videobuf-dma-sg)、
线性 DMA(videobuf-dma-contig)以及大多用于 USB 设备的用 vmalloc分配的缓冲(videobuf-vmalloc)。

所以V4L2整体框架如下图:

Linux设备驱动模型之V4L2

全志sunxi-vin

suxi-vin是全志平台的camera驱动,主要包含以下目录结构:

  • vin.c 是驱动的主要功能实现,包括注册/注销、参数读取、与 v4l2 上层接口、与各 device 的
    下层接口、中断处理、buffer 申请切换等;
  • modules/sensor 文件夹里面是各个 sensor 的器件层实现,一般包括上下电、初始化,各分
    辨率切换,yuv sensor 包括绝大部分的 v4l2 定义的 ioctrl 命令的实现;而 raw sensor 的话
    大部分 ioctrl 命令在 vfe 层调用 isp 的库实现,少数如曝光/增益调节会透过 vin 层到实际器件
    层;
  • modules/actuator 文件夹内是各种 vcm 的驱动;
  • modules/flash 文件夹内是闪光灯控制接口实现;
  • vin-csi 和 vin-mipi 为对 csi 接口和 mipi 接口的控制文件;
  • vin-isp 文件夹为 isp 的库操作文件,vin-stat则是3A统计值驱动;
  • vin-video 文件夹内主要是 video 设备操作文件;
  • vin-vipp为ISP处理之后的缩放硬件驱动;

前面有介绍到,当前的camera驱动,都是由各个v4l2_subdev子设备组成的,而当前全志sunxi-vin的csi pipeline如下:

cmos sensor ---> mipi ---> csi ---> isp ---> vipp ---> user space

sensor驱动

sensor驱动通过cci_dev_init_helper()向系统注册i2c驱动,驱动与设备匹配之后,再在probe()中通过v4l2_i2c_subdev_init()初始化v4l2_subdev和i2c_client,并初始化media_entity。

初此之外,sensor驱动还会配置I2C总线的位宽、sensor上下电、支持的分辨率、帧率、sensor的输出格式类型、输出接口方式、初始化寄存器配置等信息。

sensor v4l2_subdev支持的操作如下:

static const struct v4l2_subdev_core_ops sensor_core_ops = {
        .g_chip_ident = sensor_g_chip_ident,
        .g_ctrl = sensor_g_ctrl,
        .s_ctrl = sensor_s_ctrl,
        .queryctrl = sensor_queryctrl,
        .reset = sensor_reset,
        .init = sensor_init,
        .s_power = sensor_power,
        .ioctl = sensor_ioctl,
#ifdef CONFIG_COMPAT
        .compat_ioctl32 = sensor_compat_ioctl32,
#endif
};

static const struct v4l2_subdev_video_ops sensor_video_ops = {
        .s_parm = sensor_s_parm,
        .g_parm = sensor_g_parm,
        .s_stream = sensor_s_stream,
        .g_mbus_config = sensor_g_mbus_config,
};

static const struct v4l2_subdev_pad_ops sensor_pad_ops = {
        .enum_mbus_code = sensor_enum_mbus_code,
        .enum_frame_size = sensor_enum_frame_size,
        .get_fmt = sensor_get_fmt,
        .set_fmt = sensor_set_fmt,
};

static const struct v4l2_subdev_ops sensor_ops = {
        .core = &sensor_core_ops,
        .video = &sensor_video_ops,
        .pad = &sensor_pad_ops,
};

mipi、csi、isp、vipp驱动

mipi、csi、isp、vipp驱动也是初始化v4l2_subdev和media_entity,同时提供下面的操作:

/************************** mipi ***********************************/
static const struct v4l2_subdev_core_ops sunxi_mipi_core_ops = {
        .s_power = sunxi_mipi_subdev_s_power,
        .init = sunxi_mipi_subdev_init,
};

static const struct v4l2_subdev_video_ops sunxi_mipi_subdev_video_ops = {
        .s_stream = sunxi_mipi_subdev_s_stream,
        .s_mbus_config = sunxi_mipi_s_mbus_config,
};

static const struct v4l2_subdev_pad_ops sunxi_mipi_subdev_pad_ops = {
        .enum_mbus_code = sunxi_mipi_enum_mbus_code,
        .get_fmt = sunxi_mipi_subdev_get_fmt,
        .set_fmt = sunxi_mipi_subdev_set_fmt,
};

static struct v4l2_subdev_ops sunxi_mipi_subdev_ops = {
        .core = &sunxi_mipi_core_ops,
        .video = &sunxi_mipi_subdev_video_ops,
        .pad = &sunxi_mipi_subdev_pad_ops,
};

/************************** csi ***********************************/
static const struct v4l2_subdev_core_ops sunxi_csi_core_ops = {
        .s_power = sunxi_csi_subdev_s_power,
        .init = sunxi_csi_subdev_init,
        .ioctl = sunxi_csi_subdev_ioctl,
};

static const struct v4l2_subdev_video_ops sunxi_csi_subdev_video_ops = {
        .s_stream = sunxi_csi_subdev_s_stream,
        .s_crop = sunxi_csi_subdev_set_crop,
        .s_mbus_config = sunxi_csi_s_mbus_config,
        .s_parm = sunxi_csi_subdev_s_parm,
};

static const struct v4l2_subdev_pad_ops sunxi_csi_subdev_pad_ops = {
        .enum_mbus_code = sunxi_csi_enum_mbus_code,
        .get_fmt = sunxi_csi_subdev_get_fmt,
        .set_fmt = sunxi_csi_subdev_set_fmt,
};

static struct v4l2_subdev_ops sunxi_csi_subdev_ops = {
        .core = &sunxi_csi_core_ops,
        .video = &sunxi_csi_subdev_video_ops,
        .pad = &sunxi_csi_subdev_pad_ops,
};

/************************** isp ***********************************/
static const struct v4l2_subdev_core_ops sunxi_isp_subdev_core_ops = {
        .g_ctrl = v4l2_subdev_g_ctrl,
        .s_ctrl = v4l2_subdev_s_ctrl,
        .s_power = sunxi_isp_subdev_s_power,
        .init = sunxi_isp_subdev_init,
        .ioctl = sunxi_isp_subdev_ioctl,
#ifdef CONFIG_COMPAT
        .compat_ioctl32 = isp_compat_ioctl32,
#endif
        .subscribe_event = sunxi_isp_subscribe_event,
        .unsubscribe_event = sunxi_isp_unsubscribe_event,
};

static const struct v4l2_subdev_video_ops sunxi_isp_subdev_video_ops = {
        .s_parm = sunxi_isp_s_parm,
        .g_parm = sunxi_isp_g_parm,
        .s_crop = sunxi_isp_subdev_set_crop,
        .s_stream = sunxi_isp_subdev_s_stream,
};

static const struct v4l2_subdev_pad_ops sunxi_isp_subdev_pad_ops = {
        .enum_mbus_code = sunxi_isp_enum_mbus_code,
        .enum_frame_size = sunxi_isp_enum_frame_size,
        .get_fmt = sunxi_isp_subdev_get_fmt,
        .set_fmt = sunxi_isp_subdev_set_fmt,
};

static struct v4l2_subdev_ops sunxi_isp_subdev_ops = {
        .core = &sunxi_isp_subdev_core_ops,
        .video = &sunxi_isp_subdev_video_ops,
        .pad = &sunxi_isp_subdev_pad_ops,
};

/************************** vipp ***********************************/
static const struct v4l2_subdev_core_ops sunxi_scaler_subdev_core_ops = {
        .s_power = sunxi_scaler_subdev_s_power,
        .init = sunxi_scaler_subdev_init,
};
static const struct v4l2_subdev_video_ops sunxi_scaler_subdev_video_ops = {
        .s_stream = sunxi_scaler_subdev_s_stream,
};
/* subdev pad operations */
static const struct v4l2_subdev_pad_ops sunxi_scaler_subdev_pad_ops = {
        .enum_mbus_code = sunxi_scaler_enum_mbus_code,
        .enum_frame_size = sunxi_scaler_enum_frame_size,
        .get_fmt = sunxi_scaler_subdev_get_fmt,
        .set_fmt = sunxi_scaler_subdev_set_fmt,
        .get_selection = sunxi_scaler_subdev_get_selection,
        .set_selection = sunxi_scaler_subdev_set_selection,
};

static struct v4l2_subdev_ops sunxi_scaler_subdev_ops = {
        .core = &sunxi_scaler_subdev_core_ops,
        .video = &sunxi_scaler_subdev_video_ops,
        .pad = &sunxi_scaler_subdev_pad_ops,
};

vin-core驱动

vin-core解析dts得到csi pipeline信息,得知pipeline中sensor、mipi、csi、isp等子设备的索引,并申请vipp中断,最后还是初始化vin_cap的v4l2_subdev和media_entity,它与其他子设备不一样的地方是v4l2_subdev配置了internal_ops。

static const struct v4l2_subdev_internal_ops vin_capture_sd_internal_ops = {
        .registered = vin_capture_subdev_registered,
        .unregistered = vin_capture_subdev_unregistered,
};

	sd->internal_ops = &vin_capture_sd_internal_ops;

驱动加载过程

视频设备节点,在开机的时候驱动与设备配置一致,则将会进行相应的注册,sensor驱动的加载比较简单,就是v4l2_subdev和media_entity设备的初始化。而vin.c,则将会依次向系统注册csi、isp、mipi、flash、vipp、vin等platform驱动,而这些驱动的probe函数中,则是进行v4l2_subdev和media_entity设备的初始化。启动vin的probe进行较多操作,主要如下:

  1. 进行dts信息解析,dts中将配置sensor的硬件信息以及csi pipeline的连续顺序:

    	parse_modules_from_device_tree(vind);
    
  2. 申请gpio硬件资源:

    	vin_gpio_request(vind);
    
  3. 通过v4l2_device_register()注册v4l2_device:

            v4l2_dev = &vind->v4l2_dev;
            v4l2_dev->mdev = &vind->media_dev;
            strlcpy(v4l2_dev->name, "sunxi-vin", sizeof(v4l2_dev->name));
    
            ret = v4l2_device_register(dev, &vind->v4l2_dev);
    
  4. 通过media_device_register()注册media_device:

    	ret = media_device_register(&vind->media_dev);
    
  5. 获取时钟信息:

    	ret = vin_md_get_clocks(vind);
    
  6. 注册各个media entity设备:

    	ret = vin_md_register_entities(vind, dev->of_node);
    
    	/************************ sensor ***************************/
    		for (j = 0; j < sensors->detect_num; j++) {
            	if (sensors->use_sensor_list == 1)
    				__vin_handle_sensor_info(&sensors->inst[j]);
    				__vin_verify_sensor_info(&sensors->inst[j]);
    
    				/* 通过v4l2_device_register_subdev或v4l2_i2c_new_subdev向系统注册v4l2_subdev */
    				if (__vin_register_module(vind, module, j)) {
    					sensors->valid_idx = j;
    					break;
    				}
    		}
    
    	/************************ video device ***************************/
    	/* 通过v4l2_device_register_subdev向系统注册v4l2_subdev */
    	vin_md_register_core_entity(vind, vind->vinc[i]);
    
    	/******************** csi、mipi、isp、vipp *************************/
    	/* 通过v4l2_device_register_subdev向系统注册v4l2_subdev */
    	ret = v4l2_device_register_subdev(&vind->v4l2_dev, vind->csi[i].sd);
    	ret = v4l2_device_register_subdev(&vind->v4l2_dev, vind->mipi[i].sd);
    	ret = v4l2_device_register_subdev(&vind->v4l2_dev, vind->isp[i].sd);
    	ret = v4l2_device_register_subdev(&vind->v4l2_dev, vind->scaler[i].sd);
    
  7. 建立csi pipeline默认连接:

    	ret = vin_setup_default_links(vind);
    	/* 此时csi与isp、isp与vipp建立默认连接,同时根据dts中的配置,初始化好csi pipeline中的subdev */
    
  8. 注册v4l2_subdev节点(/dev/v4l-subdevX):

    int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
    {
    	strlcpy(vdev->name, sd->name, sizeof(vdev->name));
    	vdev->v4l2_dev = v4l2_dev;
    	vdev->fops = &v4l2_subdev_fops;
    	vdev->release = v4l2_device_release_subdev_node;
    	vdev->ctrl_handler = sd->ctrl_handler;
    	err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
                                                  sd->owner);
    }
    

到这里,基本各个设备都已经完成加载注册,硬件与dts都正确的情况,应该存在/dev/videoX节点。但是可能存在疑惑,这个节点到底是什么时候,哪里进行注册的呢,下面介绍。

video节点的注册

在初始化vin-core v4l2_subdev的时候,将v4l2_subdev的internal_ops成员赋值为vin_capture_sd_internal_ops,而在开机加载的第六步处,将会通过v4l2_device_register_subdev()向系统注册v4l2_subdev,该函数中,将会调用到internal_ops->registered()。sunxi-vin中,该函数指针指向vin_capture_subdev_registered()函数,最后的创建配置如下:

int vin_init_video(struct v4l2_device *v4l2_dev, struct vin_vid_cap *cap)
{
        snprintf(cap->vdev.name, sizeof(cap->vdev.name),
                "vin_video%d", cap->vinc->id);
        cap->vdev.fops = &vin_fops;				/* 当操作/dev/videoX时,将调用到这里 */
        cap->vdev.ioctl_ops = &vin_ioctl_ops;	/* ioctl将会来到这里 */
        cap->vdev.release = video_device_release_empty;
        cap->vdev.ctrl_handler = &cap->ctrl_handler;
        cap->vdev.v4l2_dev = v4l2_dev;
        cap->vdev.queue = &cap->vb_vidq;
        cap->vdev.lock = &cap->lock;
        cap->vdev.flags = V4L2_FL_USES_V4L2_FH;
    	/* VFL_TYPE_GRABBER类型,就是/dev/videoX节点 */
        ret = video_register_device(&cap->vdev, VFL_TYPE_GRABBER, cap->vinc->id);

    	/* 创建videobuf2管理队列 */
		/* initialize queue */
        q = &cap->vb_vidq;
        q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
        q->drv_priv = cap;
        q->buf_struct_size = sizeof(struct vin_buffer);
        q->ops = &vin_video_qops;	/* 队列的操作集合 */
        q->mem_ops = &vb2_dma_contig_memops;	/* videobuf2的内存操作集合 */
        q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &cap->lock;

        ret = vb2_queue_init(q);
}

此时,视频设备节点注册完成。

应用操作

全志平台的camera,应用主要进行下面的几个操作:

  1. open /dev/videoX:此时将会调用到sunxi-vin的vin_fops.open,也就是vin_open()函数,这里主要也就是设置一些设备的标志;
  2. ioctl VIDIOC_QUERYCAP:确认底层驱动支持的采集类型,V4L2_CAP_VIDEO_CAPTURE与V4L2_CAP_VIDEO_CAPTURE_MPLANE使用的方式不一样;
  3. ioctl VIDIOC_S_INPUT:此时调用到底层vin_ioctl_ops.vidioc_s_input,sunxi-vin将会根据传递进来的参数,选择相应的sensor,完成csi pipeline的各子设备的连接以及初始化;
  4. ioctl VIDIOC_S_PARM:此时调用到底层vin_ioctl_ops.vidioc_s_parm,驱动将调用各子设备的s_parm函数,实际就是完成帧率的设置;
  5. ioctl VIDIOC_S_FMT:驱动的vin_ioctl_ops.vidioc_s_fmt_vid_cap_mplane,匹配pipeline的各个子设备的输入输出格式与分辨率;
  6. ioctl VIDIOC_REQBUFS、VIDIOC_QUERYBUF、MMAP:完成底层驱动videobuf2的申请以及相应内存的映射;
  7. ioctl VIDIOC_STREAMON:开启流传输,底层vin_ioctl_ops.vidioc_streamon函数的调用,使能各个子设备进行视频流传输;
  8. ioctl VIDIOC_QBUF、VIDIOC_DQBUF:视频缓冲buf的获取;

sensor驱动移植

上面介绍到,sensor实际上也是一个I2C设备,按照全志平台的实际框架,拷贝一份其他的sensor驱动,然后进行相关的配置修改。主要修改点如下:

  1. SENSOR_NAME、I2C_ADDR、MCLK;
  2. sensor的初始化代码,寄存器配置数组;
  3. 曝光、增益设置接口函数;
  4. 上下电时序;
  5. sensor_detect函数;
  6. sensor支持的ioctl操作;
  7. CSI相关的接口:DVP/MIPI,几lane;
  8. 分辨率、帧率等配置;
设备注册异常怎么办?
  1. I2C通信是否正常,如果I2C通信异常,确认sensor的供电、PWDN/RESET、MCLK、I2C addr等是否正确,配合I2C波形测试确认;
  2. I2C正常通信,但是获取不到数据:通过示波器测量sensor是否正常输出数据,如果没有输出,确认配置正确的情况下,找模组厂支持,更新寄存器配置表;如果由数据输出,确认mipi的相应参数是否配置正确,看看内核是否有异常打印;

_QBUF、VIDIOC_DQBUF:视频缓冲buf的获取;

sensor驱动移植

上面介绍到,sensor实际上也是一个I2C设备,按照全志平台的实际框架,拷贝一份其他的sensor驱动,然后进行相关的配置修改。主要修改点如下:

  1. SENSOR_NAME、I2C_ADDR、MCLK;
  2. sensor的初始化代码,寄存器配置数组;
  3. 曝光、增益设置接口函数;
  4. 上下电时序;
  5. sensor_detect函数;
  6. sensor支持的ioctl操作;
  7. CSI相关的接口:DVP/MIPI,几lane;
  8. 分辨率、帧率等配置;
设备注册异常怎么办?
  1. I2C通信是否正常,如果I2C通信异常,确认sensor的供电、PWDN/RESET、MCLK、I2C addr等是否正确,配合I2C波形测试确认;
  2. I2C正常通信,但是获取不到数据:通过示波器测量sensor是否正常输出数据,如果没有输出,确认配置正确的情况下,找模组厂支持,更新寄存器配置表;如果由数据输出,确认mipi的相应参数是否配置正确,看看内核是否有异常打印;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1034655.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图像复原与重建,解决噪声的几种空间域复原方法(数字图像处理概念 P4)

文章目录 图像复原模型噪声模型只存在噪声的空间域复原 图像复原模型 噪声模型 只存在噪声的空间域复原

字节一面:你能手撕节流防抖吗?

前言 最近博主在字节面试中遇到这样一个面试题&#xff0c;这个问题也是前端面试的高频问题&#xff0c;节流防抖是前端性能优化一个很重要的手段&#xff0c;所以作为一个前端工程师必须要深入掌握这个知识点&#xff0c;博主在这给大家细细道来。 &#x1f680; 作者简介&…

01 TextRNN FastText TextCNN-04-训练要点,实验过程

TextRNN & FastText & TextCNN-03-模型总览&#xff0c;后 训练要点 RNN训练 得出来的y&#xff08;m&#xff09;&#xff08;预测标签&#xff09;是每一个分类的概率&#xff0c;比如是一个五分类&#xff0c;化成5个格子&#xff0c;每一个格子是概率&#xff0c…

java生成PDF的Util

java使用itext生成pdf-CSDN博客 接上文 支持表格绘制表格 支持表格中的文本 字体加粗、字体上色、单元格背景上色&#xff0c; 支持拼接文本 支持单行文本 多种背景颜色、字体上色 支持自定义水印 废话不说先上效果图 工具类代码 package com.zxw.文件.PDF.util;import …

建立一张表: 表里面有多个字段,每一个字段对应一种数据类

首先mysql -uroot -p 进入MySQL 选择一个数据库并使用 在该数据库内创建表格 create table homework_tb( id int(11) comment 编号, company_name char(6) comment 公司名称, introduce varchar(100) comment 介绍, content1 tinytext comment 内容1, co…

ad18学习笔记十一:显示和隐藏网络、铺铜

如何显示和隐藏网络&#xff1f; Altium Designer--如何快速查看PCB网络布线_ad原理图查看某一网络的走线_辉_0527的博客-CSDN博客 AD19(Altium Designer)如何显示和隐藏网络 如何显示和隐藏铺铜&#xff1f; Altium Designer 20在PCB中显示或隐藏每层铺铜-百度经验 AD打开与…

React【Context_作用、函数组件订阅Context、Fragments 、错误边界_概念 、错误边界_应用、Refs DOM】(四)

目录 Context_作用 函数组件订阅Context Fragments 错误边界_概念 错误边界_应用 Refs & DOM Context_作用 React组件中数据是通过 props 属性自上而下&#xff08;由父及子&#xff09;进行传递的&#xff0c;但是有的时候中间的一些组件可能并不需要props的值。 //A…

深度学习自学笔记一:神经网络和深度学习

神经网络是一种模拟人脑神经元之间相互连接的计算模型&#xff0c;它由多个节点&#xff08;或称为神经元&#xff09;组成&#xff0c;并通过调整节点之间的连接权重来学习和处理数据。深度学习则是指利用深层次的神经网络进行学习和建模的机器学习方法。 假设有一个数据集&a…

电阻的读数

常见电阻的阻值一般有色环电阻和贴片电阻 &#xff0c;下面介绍两种电阻的阻值读法。 1、色标法&#xff1a; 技巧&#xff1a;四环电阻的的精度一般为银色和金色&#xff0c;如果一眼能可看到这两种颜色可以判断为第4环的精度读数 可见棕色为第1环&#xff0c;黑色第2环&…

Three.js后期处理简明教程

后期处理&#xff08;Post Processing&#xff09;通常是指对 2D 图像应用某种效果或滤镜。 在 THREE.js 中我们有一个包含一堆网格物体的场景。 我们将该场景渲染为 2D 图像。 通常&#xff0c;该图像会直接渲染到画布中并显示在浏览器中&#xff0c;但我们可以将其渲染到渲染…

AI写文章软件-怎么选择不同的AI写文章软件

在如今信息爆炸的时代&#xff0c;无论是学生、职场人士&#xff0c;还是创作者和企业家&#xff0c;写文章都是一项常见而又重要的任务。然而&#xff0c;随着科技的不断进步&#xff0c;AI写文章的软件也逐渐走进了人们的视野。 147GPT批量文章生成工具​www.147seo.com/post…

Java 基于微信小程序的学生选课系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 第一章&#xff1a; 简介第二章 技术栈第三章&#xff1a; 功能分析第四章 系统设计第五章 系统功…

Intel酷睿和AMD锐龙

Intel酷睿系列&#xff0c;主要分i3、i5、i7、i9 如&#xff1a;Intel 酷睿i5 10210U i5&#xff1a;品牌修饰符。 10&#xff1a;代次指示符。 210&#xff1a;sku编号。 常见后缀&#xff1a; G1-G7&#xff1a;集显等级。 U&#xff1a;低功耗。 H&#xff1a;标压版…

通俗易懂了解大语言模型LLM发展历程

1.大语言模型研究路程 NLP的发展阶段大致可以分为以下几个阶段&#xff1a; 词向量词嵌入embedding句向量和全文向量理解上下文超大模型与模型统一 1.1词向量 将自然语言的词使用向量表示&#xff0c;一般构造词语字典&#xff0c;然后使用one-hot表示。   例如2个单词&…

GeekRUN-7芯片跑分表

前两个字母是芯片简写&#xff0c;如麒麟&#xff0c;是QL&#xff0c;骁龙是XL&#xff0c;天玑是TJ&#xff0c;第一串数字是最高值&#xff0c;第二串是最低值&#xff0c;省电模式差不多这个水平。QL9K是麒麟9000&#xff0c;QL9S

aix360-gec

目录 分组条件期望&#xff08;GroupedCE&#xff09;解释程序创建虚拟环境导包加载数据集训练模型计算独立条件期望ICEplot_ice_explanation 计算分组条件期望 (GCE)plot_gce_explanation 记录一下学习过程&#xff0c;官方的代码在https://github.com/Trusted-AI/AIX360/tree…

High-Resolution Side Channels for Untrusted Operating Systems【ATC‘17】

目录 摘要引言贡献• 一个不受信任的操作系统的两个新的高分辨率侧通道来攻击受保护的应用程序&#xff1b;• 对 libjpeg 的显着改进攻击和针对 VC3 的新攻击&#xff1b;• 侧通道攻击对不受信任的操作系统的重要性增加。 系统模型背景Intel SGX页面错误通道时间限制空间限制…

微信小程序案例2-3:婚礼邀请函

文章目录 一、运行效果二、知识储备&#xff08;一&#xff09;导航栏设置1、导航栏的相关配置项2、利用导航栏组件2、在页面配置文件中对导航栏进行配置3、在全局配置文件中对导航栏进行配置 三、实现步骤 一、运行效果 “婚礼邀请函”微信小程序由4个页面组成&#xff0c;分别…

Springboot整合分页插件pagehelper

首先需要有一定的springbootmybatis的基础&#xff0c;才能使用顺畅 项目结构如下 引入依赖&#xff0c;springboot版本选的是2.7.16版本&#xff0c;jdk选的17&#xff0c; <!--分页插件--> <dependency><groupId>com.github.pagehelper</groupId><…

基于微信小程序的房屋租赁系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言运行环境说明用户微信小程序端的主要功能有&#xff1a;户主微信小程序端的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文…