RK Camera 驱动调试(ov426)

一、摄像头类型

Mipi: 当前主要使用的摄像头接口,速度快,抗干扰强。
Lvds:个别 摄像头  mipi 接口接收不了,所以使用 lvds 接口
Dvp: 相对 mipi 接口,传输的速率有限,一般 5M 或以下摄像头。

二、摄像头dts配置

ov426: ov426@36 {
                status = "okay";
                compatible = "ovti,ov426";
                reg = <0x36>;
                clocks = <&cru CLK_CIF_OUT>;
                clock-names = "xvclk";
                avdd-supply = <&vcc_avdd>;
                dovdd-supply = <&vcc_dovdd>;
                dvdd-supply = <&vcc_dvdd>;
                power-domains = <&power RV1126_PD_VI>;
                pwdn-gpios = <&gpio3 RK_PA5 GPIO_ACTIVE_LOW>;
                //pwdn-gpios = <&gpio2 RK_PA6 GPIO_ACTIVE_HIGH>;
                reset-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>;
                //rockchip,grf = <&grf>;
                pinctrl-names = "default";
                pinctrl-0 = <&cifm0_dvp_ctl>;
                rockchip,camera-module-index = <0>;
                rockchip,camera-module-facing = "back";
                rockchip,camera-module-name = "default";
                rockchip,camera-module-lens-name = "default";

                port {
                        cam_para_out1: endpoint {
                                remote-endpoint = <&cif_para_in>;
                                bus-width = <10>;
                                hsync-active = <1>;
                                vsync-active = <0>;
                        };
                };

        };

         调单个摄像头的时候,建议就按照这个来改,这是标准 i2c 设备,定义在 i2c 的节点下面,前面的一大串如果原理图没怎么变的话,都一样就行,当然 i2c 的地址要注意。如果摄像头 lane 数有变,在后面的 port 里面有 data-lane 这个参数,都配置成一 样的。

1.rockchip,camera-module-index:  这个定义多个摄像头时候做个区分
2.rockchip,camera-module-facing : 摄像头前后置标识
3.rockchip,camera-module-name :摄像头模块名
4.rockchip,camera-module-lens-name :摄像头镜头名
主要功能是通过关键字找对应的效果文件。比如当前 dts 找的 xml 文件为default_default.xml ,对于 yuv 摄像头不需要 xml ,可以不管这个配置。
5.Ir-cut 是红外滤光片,我这里没配置,就删掉了
6.Port 是根据摄像头在内部的级联关系 默认的关系是
sensor0->csi_dphy0->csi2->vicap->isp0->ispp0
sensor0->csi_dphy0->csi2->vicap->isp1->ispp1
sensor1->csi_dphy1->isp0->ispp0

  如果是DVP摄像头时

 对应的数据链路

三、驱动

1.摄像头寄存器

static const struct ov426_mode supported_modes[] = {
        {
                /* linear mode0 */
                .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
                .width = 400,
                .height = 400,
                .max_fps = {
                        .numerator = 10000,
                        .denominator = 300000,
                },
                .exp_def = 0x01a0,
                .hts_def = 0x0240,
                .vts_def = 0x01ce,
                .reg_list = ov426_linear10bit_400x400_regs,
                .hdr_mode = NO_HDR,
        },

        {
                /* linear mode1 */
                .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
                .width = 400,
                .height = 400,
                .max_fps = {
                        .numerator = 10000,
                        .denominator = 300000,
                },
                .exp_def = 0x01a0,
                .hts_def = 0x0240,
                .vts_def = 0x01ce,
                .reg_list = ov426_color_400x400_regs,
                .hdr_mode = NO_HDR,
        }

};
        摄像头可以有支持不同的格式,不同的分辨率以及帧率等,可以写在 supported_modes 下面, 400x400  分辨率,帧率是 30 帧,后面有对应的 曝光等描述。
        reg_list 里面是摄像头该 supported_modes 对应的寄存器。注意确定跟上面的参数匹配

2.摄像头 id

#define OV426_CHIP_ID                   0x694F
#define OV426_REG_CHIP_ID               0x300A
寄存器和 id ,如果读到的值不匹配,节点都注册不上。如果匹配成功,打印正常的ID值

2.probe函数

static int probe(struct i2c_client *client)
{
        struct device *dev = &client->dev;
        struct device_node *node = dev->of_node;
        struct ov426 *ov426;
        struct v4l2_subdev *sd;
        char facing[2];
        int ret;
        dev_info(dev, " driver version: %02x.%02x.%02x",
                 DRIVER_VERSION >> 16,
                 (DRIVER_VERSION & 0xff00) >> 8,
                 DRIVER_VERSION & 0x00ff);

        ov426 = devm_kzalloc(dev, sizeof(*ov426), GFP_KERNEL);
        printk("devm_kzalloc ok");
        if (!ov426)
                return -ENOMEM;

        ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
                                   &ov426->module_index);
        

        ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
                                       &ov426->module_facing);
        

        ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
                                       &ov426->module_name);
       

        ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
                                       &ov426->len_name);
        
        ov426->cfg_num = ARRAY_SIZE(supported_modes);

    
        ov426->cur_mode = &supported_modes[0];
        printk("Current mode: %d\n",mode);
        ov426->client = client;

        ov426->xvclk = devm_clk_get(dev, "xvclk");
        
        ov426->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
        
        ov426->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_HIGH);
        
         ret = ov426_configure_regulators(ov426);
        

        mutex_init(&ov426->mutex);

        sd = &ov426->subdev;
        v4l2_i2c_subdev_init(sd, client, &ov426_subdev_ops);
        ret = ov426_initialize_controls(ov426);
        
        ret = __ov426_power_on(ov426);

        
        ret = ov426_check_sensor_id(ov426, client);
       
        printk("check id ok");

#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
        sd->internal_ops = &ov426_internal_ops;
        sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
                     V4L2_SUBDEV_FL_HAS_EVENTS;
#endif
#if defined(CONFIG_MEDIA_CONTROLLER)
        ov426->pad.flags = MEDIA_PAD_FL_SOURCE;
        sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
        ret = media_entity_pads_init(&sd->entity, 1, &ov426->pad);
        if (ret < 0)
                goto err_power_off;
#endif
        memset(facing, 0, sizeof(facing));
        if (strcmp(ov426->module_facing, "back") == 0)
                facing[0] = 'b';
        else
                facing[0] = 'f';

        snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
                 ov426->module_index, facing,
OV426_NAME, dev_name(sd->dev));
        printk("sd->name: %s\n", sd->name);

        ret = v4l2_async_register_subdev_sensor_common(sd);
        if (ret) {
                dev_err(dev, "v4l2 async register subdev failed\n");
                goto err_clean_entity;
        }
        pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
        pm_runtime_idle(dev);

 	return ret;

}

主要是进行设备的初始化设置和配置。

  1. 从设备节点读取属性信息,包括模块索引、模块朝向、模块名称和镜头名称等。
  2. 设置当前的工作模式。
  3. 获取设备的时钟和GPIO引脚,并进行错误检查和处理。
  4. 初始化互斥锁和v4l2子设备,并注册相关的控制项。
  5. 打开OV426传感器,并检查传感器的ID。
  6. 设置v4l2子设备的属性。
  7. 注册v4l2子设备,并启用设备的运行时电源管理功能。

四、调式

驱动编译好之后,烧入就可以尝试抓图了。

media-ctl 工具查看节点链路:media-ctl -p -d /dev/mediaX

1.抓原图(RAW)

通过如下命令可以实现紧凑与非紧凑格式的切换,0表示非紧凑型,1表示紧凑型;

echo 0 > /sys/devices/platform/rkcif_dvp/compact_test
echo 0 > /sys/devices/platform/rkcif_dvp/align_test

抓图命令:

v4l2-ctl -d /dev/video0 --set-fmt-video=width=400,height=400,pixelformat=BG10 --stream-mmap=4 --stream-count=1 --stream-to=/data/BG10.raw --stream-skip=2

具体含义如下:

-d /dev/video0:指定要配置和采集的视频设备为/dev/video0。
--set-fmt-video=width=400,height=400,pixelformat=BG10:设置视频格式为宽度为400,高度为400,像素格式为BG10。
--stream-mmap=4:使用mmap内存映射方式进行视频采集,缓冲区数量为4。
--stream-count=1:采集一帧视频数据。
--stream-to=/data/BG10.raw:将采集到的视频数据保存为文件/data/BG10.raw。
--stream-skip=2:跳过前两个采集缓冲区。

 2.抓YUV图

经过 isp ispp 处理后,我们可以抓 yuv 图,找到 rkispp_m_bypass 对应的节点。

抓图命令

v4l2-ctl -d /dev/video18 --set-fmt-video=width=400,height=400,pixelformat=NV12 --stream-mmap=4 --stream-count=1 --stream-to=/data/NV12.out --stream-skip=2

具体含义如下:

-d /dev/video18:指定要配置和采集的视频设备为/dev/video18。
--set-fmt-video=width=400,height=400,pixelformat=NV12:设置视频格式为宽度为400,高度为400,像素格式为NV12。
--stream-mmap=4:使用mmap内存映射方式进行视频采集,缓冲区数量为4。
--stream-count=1:采集一帧视频数据。
--stream-to=/data/NV12.out:将采集到的视频数据保存为文件/data/NV12.out。
--stream-skip=2:跳过前两个采集缓冲区。
  • 31
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
### 回答1: 在调试 camera 驱动 OTP 之前,您需要了解 OTP 是什么以及它在相机模块中的作用。OTP 指 One-Time Programming,可以将一些固定的参数写入到相机模块中,例如模块的 ID、校准信息等。这些参数在生产过程中被写入到 OTP 中,然后被相机模块读取。 下面是一些调试 OTP 的步骤: 1. 首先,您需要确定 OTP 中存储的参数以及其在相机模块中的作用。这通常需要查看相机模块的数据手册或者与厂商联系。 2. 然后,您需要使用相应的工具将 OTP 中的参数读取出来。这通常需要一些专有的工具,例如生产线测试工具或者 OTP 编程器。 3. 一旦您成功地从 OTP 中读取了参数,您需要将其与相机模块中实际读取到的参数进行比较。如果它们不匹配,您需要确定是哪个环节出了问题,例如是否写入 OTP 时出错,或者读取 OTP 时出错。 4. 如果您确定了问题的来源,您需要采取相应的措施来解决它。例如,如果写入 OTP 时出错,您可以尝试重新写入这些参数;如果读取 OTP 时出错,您可以检查 OTP 的连接或者更换 OTP 编程器。 需要注意的是,OTP 的调试通常需要专门的硬件和软件支持,因此您需要仔细阅读相机模块的数据手册,或者与厂商联系以获取更多信息。 ### 回答2: 相机驱动OTP(One-Time Programmable)调试是指对相机模块中OTP存储器内容的调试工作。OTP存储器是一种只能被编程一次的存储器,用于存储相机模块的相关参数和设置。通过调试OTP存储器,可以优化相机模块的性能和功能。 首先,需要准备OTP调试工具和相关设备。OTP调试工具可以通过OTP编程设备进行操作,将特定参数写入OTP存储器中。同时,需要连接好相机模块和OTP编程设备,确保其正常通信。 接下来,进行OTP调试的具体步骤如下: 1. 确认OTP存储器的布局和编程方式。了解存储器的结构和编程规则,以便正确操作。 2. 读取OTP存储器的内容。使用OTP调试工具中的读取功能,读取存储器中已编程的参数值。记录下这些参数值,以便后续对比和调试。 3. 根据需要修改参数值。根据实际需求,对特定参数进行调整和优化。比如,可以调整分辨率、曝光时间、白平衡等参数,以达到更好的图像质量。 4. 将修改后的参数值写入OTP存储器。使用OTP调试工具中的编程功能,将修改后的参数值写入OTP存储器中。确保编程过程正确无误。 5. 重新读取OTP存储器的内容。再次使用OTP调试工具中的读取功能,读取编程后的参数值。与之前记录的参数值进行对比,确保参数值已成功写入存储器。 6. 进行功能验证和性能测试。通过调试后的OTP存储器,启动相机模块进行功能验证和性能测试。确保相机模块正常工作,并且参数值能够实现预期效果。 总结起来,相机驱动OTP调试是一项重要的工作,通过对OTP存储器内容的调整和优化,可以改善相机模块的性能和功能。准备好OTP调试工具和相关设备,按照一定步骤进行调试,最终验证相机模块的正常工作和参数值的实际效果。 ### 回答3: Camera驱动OTP调试是指对摄像头驱动程序中的一种特殊功能进行调试。 OTP全称为One-Time Programmable,即一次性可编程。在摄像头模块中,OTP用于存储一些初始参数或配置信息,例如光学模块的校准数据、镜头的校正参数等。这些数据一般是在生产过程中由供应商提前写入到OTP存储器中的,用户无法修改。 在Camera驱动OTP调试过程中,主要包括以下步骤: 1. 读取OTP数据:首先需要从OTP存储器中读取出相关的配置数据。这可以通过驱动程序中的OTP读取接口来实现。 2. 解析OTP数据:将读取到的OTP数据进行解析,提取出其中的参数信息。这些参数可能包括图像传感器的配置信息、模组的校准数据等。 3. 应用OTP数据:将解析出的OTP参数应用到相应的设置中,例如更新驱动程序中的配置寄存器或传递给后续的图像处理算法。 4. 验证调试结果:通过测试不同的场景或使用不同的测试样本,来验证应用了OTP配置后相机模块的性能是否得到了优化或改善。 5. 修改OTP数据:如果验证结果不满意,可以尝试修改OTP数据中的某些参数,然后重新进行调试和验证。 需要注意的是,Camera驱动OTP调试需要具备一定的硬件和软件技术支持,包括理解相关的摄像头芯片和供应商提供的OTP文档、熟悉驱动程序的实现和调试方式、以及具备相关的图像处理和校准知识。 总之,Camera驱动OTP调试是对摄像头驱动程序中的OTP数据进行读取、解析、应用和验证的过程,旨在优化相机模块的性能和图像质量。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春风从不入睡、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值