Linux设备驱动之Camera驱动

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.mzph.cn/news/86110.shtml

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

相关文章

Linux——进程

目录 一、基本概念 二、描述进程-PCB &#xff08;一&#xff09;task_struct-PCB的一种 &#xff08;二&#xff09;task_ struct内容分类 三、查看进程 &#xff08;一&#xff09;利用ps命令 &#xff08;二&#xff09; 通过 /proc 系统文件夹查看 &#xff08;三…

停车场系统源码

源码下载地址&#xff08;小程序开源地址&#xff09;&#xff1a;停车场系统小程序&#xff0c;新能源电动车充电系统&#xff0c;智慧社区物业人脸门禁小程序: 【涵盖内容】&#xff1a;城市智慧停车系统&#xff0c;汽车新能源充电&#xff0c;两轮电动车充电&#xff0c;物…

zemaxMIF曲线图

调制传递函数&#xff08; Modulation Transfer Function&#xff0c;MTF &#xff09;是用来形容光学系统成像质量的重要指标。 通过对光学系统像空间进行傅里叶变换&#xff0c;可以得到一张分析图表&#xff0c;来描述像面上对比度和空间频率之间的对应关系。 对比度&…

C/C++统计满足条件的4位数个数 2023年5月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C统计满足条件的4位数个数 一、题目要求 1、编程实现 2、输入输出 二、解题思路 1、案例分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 C/C统计满足条件的4位数个数 2019年12月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 给定若干…

numpy 和 tensorflow 中的各种乘法(点乘和矩阵乘)

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python源码、视频教程、插件安装教程、资料我都准备好了&#xff0c;直接在文末名片自取就可 点乘和矩阵乘…

【深度学习实验】前馈神经网络(三):自定义多层感知机(激活函数logistic、线性层算Linear)

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 构建数据集 2. 激活函数logistic 3. 线性层算子 Linear 4. 两层的前馈神经网络MLP 5. 模型训练 一、实验介绍 本实验实现了一个简单的两层前馈神经网络 激活函数…

Linux 链表示例 LIST_INIT LIST_INSERT_HEAD

list(3) — Linux manual page 用Visual Studio 2022创建CMake项目 * CmakeLists.txt # CMakeList.txt : Top-level CMake project file, do global configuration # and include sub-projects here. # cmake_minimum_required (VERSION 3.12)project ("llist")# I…

云原生Kubernetes:K8S存储卷

目录 一、理论 1.存储卷 2.emptyDir 存储卷 3.hostPath卷 4.NFS共享存储 5.PVC 和 PV 6.静态创建PV 7.动态创建PV 二、实验 1.emptyDir 存储卷 2.hostPath卷 3.NFS共享存储 4.静态创建PV 5.动态创建PV 三、问题 1.生成pod一直pending 2.shoumount -e未显示共享…

YOLOv5如何训练自己的数据集

文章目录 前言1、数据标注说明2、定义自己模型文件3、训练模型4、参考文献 前言 本文主要介绍如何利用YOLOv5训练自己的数据集 1、数据标注说明 以生活垃圾数据集为例子 生活垃圾数据集&#xff08;YOLO版&#xff09;点击这里直接下载本文生活垃圾数据集 生活垃圾数据集组成&…

CTF 全讲解:[SWPUCTF 2021 新生赛]jicao

文章目录 参考环境题目index.phphighlight_file()include()多次调用&#xff0c;多次执行单次调用&#xff0c;单次执行 $_POST超全局变量HackBarHackBar 插件的获取 $_POST打开 HackBar 插件通过 HackBar 插件发起 POST 请求 GET 请求查询字符串超全局变量 $_GET JSONJSON 数据…

Android 滑动事件消费监控,Debug 环境下通用思路

Android Debug 环境下滑动事件消费监控通用思路 背景 Android 开发中&#xff0c;经常会遇到滑动事件冲突。在一些简单的场景下&#xff0c;我们如果能够知道是那个 View 拦截了事件&#xff0c;那我们能够很容易得解决。解决方法通常就是内部拦截法或者外部拦截法。ViewPage…

【数据结构】七大排序算法详解

目录 ♫什么是排序 ♪排序的概念 ♪排序的稳定性 ♪排序的分类 ♪常见的排序算法 ♫直接插入排序 ♪基本思想 ♪算法实现 ♪算法稳定性 ♪时间复杂度 ♪空间复杂度 ♫希尔排序 ♪基本思想 ♪算法实现 ♪算法稳定性 ♪时间复杂度 ♪空间复杂度 ♫直接选择排序 ♪基本思想 ♪算法…

【日常业务开发】Java实现异步编程

【日常业务开发】Java实现异步编程 Java实现异步编程什么是异步异步的八种实现方式异步编程线程异步Future异步CompletableFuture实现异步Spring的Async异步Spring ApplicationEvent事件实现异步消息队列ThreadUtil异步工具类Guava异步 CompletableFuture异步编排工具类创建异步…

unity自己对象池的使用

unity出了自己的对象池 这里记录一下用法 命名空间就是这个 一般有两种用法&#xff0c;第一种是在using里面获取&#xff0c;脱离这个域就释放。第二种是在Get和Release配合使用 // This version will only be returned to the pool if we call Release on it.//只有使用Re…

Android进阶之路 - 盈利、亏损金额格式化

在金融类型的app中&#xff0c;关于金额、数字都相对敏感和常见一些&#xff0c;在此仅记录我在金融行业期间学到的皮毛&#xff0c;如后续遇到新的场景也会加入该篇 该篇大多采用 Kotlin 扩展函数的方式进行记录&#xff0c;尽可能熟悉 Kotlin 基础知识 兄弟 Blog StringUti…

MediaPipe+OpenCV 实现实时手势识别(附Python源码)

MediaPipe官网&#xff1a;https://developers.google.com/mediapipe MediaPipe仓库&#xff1a;https://github.com/google/mediapipe 一、MediaPipe介绍 MediaPipe 是一个由 Google 开发的开源跨平台机器学习框架&#xff0c;用于构建视觉和感知应用程序。它提供了一系列预训…

Redis 面霸篇:从高频问题透视核心原理

Redis 为什么这么快&#xff1f; 很多人只知道是 K/V NoSQl 内存数据库&#xff0c;单线程……这都是没有全面理解 Redis 导致无法继续深问下去。 这个问题是基础摸底&#xff0c;我们可以从 Redis 不同数据类型底层的数据结构实现、完全基于内存、IO 多路复用网络模型、线程…

HTML5day02综合案例2

案例展示 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>注册信息</title> </head> &l…

新思路,4.9+氧化应激相关基因构建风险模型

今天给同学们分享一篇氧化应激预后模型的生信文章“Construction of an oxidative stress-related lncRNAs signature to predict prognosis and the immune response in gastric cancer”&#xff0c;这篇文章于2023年5月31日发表在Scientific Reports期刊上&#xff0c;影响因…

分库分表MySQL

目录 Mycat入门 分片配置 分片配置(配置Mycat的用户以及用户的权限) 启动服务 登录Mycat Mycat配置 schema.xml 1.schema标签:配置逻辑库,逻辑表的相关信息 1-1.核心属性 1-2.table标签 2.datanode标签:配置数据节点的相关信息 核心属性 3.datahost标签:配置的是节…