CMOS摄像头驱动分析-i2c驱动

简介: CMOS摄像头驱动分析-i2c驱动

设备树内容

ov2640: camera@0x30 {
       compatible = "ovti,ov2640";
       reg = <0x30>;
       status = "okay";
       pinctrl-names = "default";
       pinctrl-0 = <&pinctrl_csi1
                   &csi_pwn_rst>;
       resetb = <&gpio1 2 GPIO_ACTIVE_LOW>;
       pwdn = <&gpio1 4 GPIO_ACTIVE_HIGH>;
       clocks = <&clks IMX6UL_CLK_CSI>;
       clock-names = "xvclk";
       port {
              camera_ep: endpoint {
                     remote-endpoint = <&csi_ep>;
                     bus-width = <8>;
              };
       };
    };

module_i2c_driver宏分析

// 注册i2c驱动程序

module_i2c_driver(ov2640_i2c_driver);

module_i2c_driver(ov2640_i2c_driver),可以分析出以下信息:

module_i2c_driver是一个宏,它可能是在编程中定义的一个宏,用于简化I2C驱动模块的注册。

ov2640_i2c_driver是一个I2C驱动的结构体或变量名。它可能定义了有关OV2640摄像头的I2C通信设置和功能的信息。

宏module_i2c_driver可能在内部执行一些操作,以便将ov2640_i2c_driver的I2C驱动注册到系统中。这通常涉及使用相关的函数和数据结构将驱动程序添加到I2C驱动程序列表中,并与I2C总线进行关联。

总的来说,module_i2c_driver(ov2640_i2c_driver)宏的作用是将ov2640_i2c_driver所定义的I2C驱动注册到系统中,以便系统能够正确识别和使用与OV2640摄像头相关的I2C通信功能。

module_i2c_driver原型

#define module_i2c_driver(__i2c_driver) \
    module_driver(__i2c_driver, i2c_add_driver, \
            i2c_del_driver)

根据您提供的代码宏定义,module_i2c_driver宏用于简化I2C驱动模块的注册和注销过程,并使用了module_driver宏。

该宏定义的详细分析如下:

module_i2c_driver是宏的名称。

__i2c_driver是一个传入的参数,表示要注册的I2C驱动程序。宏的具体实现包括以下步骤:

使用module_driver宏,传入__i2c_driver作为驱动参数,以及i2c_add_driver和i2c_del_driver作为注册和注销函数。

i2c_add_driver是用于将I2C驱动程序添加到系统中的函数。

i2c_del_driver是用于从系统中注销I2C驱动程序的函数。

通过使用module_i2c_driver宏,可以将注册和注销I2C驱动程序的过程简化为调用module_driver宏,并传入适当的参数。这样可以减少手动编写注册和注销函数的工作量,并提高代码的可读性和可维护性。

总结而言,module_i2c_driver宏的作用是将指定的I2C驱动程序注册到系统中,并在加载模块时调用注册函数,以及在卸载模块时调用注销函数。

module_i2c_driver的原型为module_driver,定义如下

#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
    return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
    __unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

module_driver宏用于简化驱动模块的注册和注销过程,并提供了模块的初始化和退出函数。

该宏定义的详细分析如下:

module_driver是宏的名称。

__driver是一个传入的参数,表示要注册的驱动程序。

__register是一个传入的参数,表示用于注册驱动的函数。

__unregister是一个传入的参数,表示用于注销驱动的函数。

…表示可变参数,用于传递额外的参数给注册和注销函数。

宏的具体实现包括以下步骤:

定义一个静态的初始化函数__driver##_init,该函数在模块初始化时被调用。

在__driver##_init函数中,调用__register函数来注册驱动程序,传递驱动程序结构体和额外的参数。

使用module_init宏将__driver##_init函数指定为模块的初始化函数,确保在加载模块时会调用该函数进行初始化。

定义一个静态的退出函数__driver##_exit,该函数在模块注销时被调用。

在__driver##_exit函数中,调用__unregister函数来注销驱动程序,传递驱动程序结构体和额外的参数。

使用module_exit宏将__driver##_exit函数指定为模块的退出函数,确保在卸载模块时会调用该函数进行注销。

通过使用module_driver宏,可以简化驱动模块的注册和注销过程,减少了手动编写初始化和退出函数的工作量,提高了代码的可读性和可维护性。

ov2640_i2c_driver

ov2640的i2c驱动程序

// 定义ov2640的i2c设备ID
static const struct i2c_device_id ov2640_id[] = {
    { "ov2640", 0 }, // 设备名为ov2640,ID为0
    { } // 结束符
};
MODULE_DEVICE_TABLE(i2c, ov2640_id); // 将ov2640_id注册到i2c设备表中,以便内核能够自动加载驱动程序
// 定义设备树匹配表
static const struct of_device_id ov2640_of_match[] = {
    {.compatible = "ovti,ov2640", }, // 匹配ovti,ov2640
    {}, // 结束符
};
MODULE_DEVICE_TABLE(of, ov2640_of_match); // 将ov2640_of_match注册到设备树匹配表中,以便内核能够自动加载驱动程序
// 定义ov2640的i2c驱动程序
static struct i2c_driver ov2640_i2c_driver = {
    .driver = {
        .name = "ov2640", // 驱动程序名为ov2640
        .of_match_table = of_match_ptr(ov2640_of_match), // 设置设备树匹配表
    },
    .probe    = ov2640_probe, // 设置探测函数
    .remove   = ov2640_remove, // 设置反初始化函数
    .id_table = ov2640_id, // 设置i2c设备ID
};

ov2640_probe

这个函数是用于初始化并探测OV2640摄像头的驱动程序。下面是对该函数的概括总结:

检查所使用的I2C适配器是否支持SMBUS功能,如果不支持则返回错误码。

分配内存并初始化ov2640_priv结构体。

获取摄像头的时钟,如果获取失败则返回延迟探测错误码。

检查是否存在soc_camera_subdev_desc结构体或设备树节点,如果都不存在则返回错误码。

如果不存在soc_camera_subdev_desc结构体,从设备树中获取OV2640的GPIO引脚并进行初始化。

初始化v4l2子设备,使用ov2640_subdev_ops作为操作函数。

初始化v4l2控制器,创建VFLIP和HFLIP控制器。

将控制器绑定到子设备的控制器处理器中。

如果控制器存在错误,则返回错误码。

进行视频探测,初始化摄像头的视频功能。

如果视频探测失败,则释放控制器并释放时钟,并返回错误码。

注册v4l2异步子设备。

如果注册失败,则释放探测的视频,并返回错误码。

打印OV2640已成功探测的消息。

返回0表示探测成功。

如果探测失败,将会在相应的错误标签处释放控制器和时钟,并返回相应的错误码。

static int ov2640_probe(struct i2c_client *client,
            const struct i2c_device_id *did)
{
    struct ov2640_priv    *priv;
    struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); // 获取soc_camera_subdev_desc
    struct i2c_adapter    *adapter = to_i2c_adapter(client->dev.parent); // 获取i2c_adapter
    int            ret;
    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { // 如果i2c_adapter不支持SMBUS
        dev_err(&adapter->dev,
            "OV2640: I2C-Adapter doesn't support SMBUS\n"); // 打印错误信息
        return -EIO; // 返回-EIO
    }
    priv = devm_kzalloc(&client->dev, sizeof(struct ov2640_priv), GFP_KERNEL); // 分配内存
    if (!priv) { // 如果分配失败
        dev_err(&adapter->dev,
            "Failed to allocate memory for private data!\n"); // 打印错误信息
        return -ENOMEM; // 返回-ENOMEM
    }
    priv->clk = v4l2_clk_get(&client->dev, "xvclk"); // 获取时钟
    if (IS_ERR(priv->clk)) // 如果获取失败
        return -EPROBE_DEFER; // 返回-EPROBE_DEFER
    if (!ssdd && !client->dev.of_node) { // 如果soc_camera_subdev_desc不存在且设备树节点不存在
        dev_err(&client->dev, "Missing platform_data for driver\n"); // 打印错误信息
        ret = -EINVAL; // 返回-EINVAL
        goto err_clk; // 跳转到err_clk
    }
    //if (!ssdd) {
        ret = ov2640_probe_dt(client, priv); // 从设备树中获取ov2640的GPIO引脚并进行初始化
        if (ret) // 如果初始化失败
            goto err_clk; // 跳转到err_clk
    //}
    // 初始化v4l2子设备
    v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
    // 初始化v4l2控制器
    v4l2_ctrl_handler_init(&priv->hdl, 2);
    // 添加vflip控制器
    v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
            V4L2_CID_VFLIP, 0, 1, 1, 0);
    // 添加hflip控制器
    v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
            V4L2_CID_HFLIP, 0, 1, 1, 0);
    // 设置子设备的控制器
    priv->subdev.ctrl_handler = &priv->hdl;
    // 如果控制器有错误,则返回错误
    if (priv->hdl.error) {
        ret = priv->hdl.error;
        goto err_clk;
    }
    // 进行视频探测
    ret = ov2640_video_probe(client);
    // 如果探测失败,则跳转到err_videoprobe
    if (ret < 0)
        goto err_videoprobe;
    // 注册v4l2异步子设备
    ret = v4l2_async_register_subdev(&priv->subdev);
    // 如果注册失败,则跳转到err_videoprobe
    if (ret < 0)
        goto err_videoprobe;
    // 打印信息
    dev_info(&adapter->dev, "OV2640 Probed\n");
    // 返回0
    return 0;
// 如果探测失败,则释放控制器并释放时钟
err_videoprobe:
    v4l2_ctrl_handler_free(&priv->hdl);
err_clk:
    v4l2_clk_put(priv->clk);
    // 返回错误码
    return ret;
}

ov2640_remove

这个函数是用于从i2c_client中获取ov2640_priv结构体,并进行反初始化操作。下面是对该函数的概括总结:

从i2c_client中获取ov2640_priv结构体。

取消v4l2异步子设备的注册。

释放时钟资源。

取消v4l2子设备的注册。

释放控制器资源。

返回0表示反初始化成功。

该函数主要用于释放与OV2640摄像头驱动程序相关的资源,包括时钟、控制器和子设备的注册。

// 从i2c_client中获取ov2640_priv,并进行反初始化

static int ov2640_remove(struct i2c_client *client)
{
    struct ov2640_priv       *priv = to_ov2640(client); // 获取ov2640_priv
    v4l2_async_unregister_subdev(&priv->subdev); // 取消v4l2异步子设备的注册
    v4l2_clk_put(priv->clk); // 释放时钟
    v4l2_device_unregister_subdev(&priv->subdev); // 取消v4l2子设备的注册
    v4l2_ctrl_handler_free(&priv->hdl); // 释放控制器
    return 0; // 返回0
}

如果文章对您有帮助,点赞👍支持,感谢🤝


目录
相关文章
|
存储 芯片
SGM58031与单片机驱动实现
SGM58031与单片机驱动实现
385 0
|
4月前
|
异构计算
FPGA强化(8):HDMI显示器驱动
FPGA入门(8):VGA显示器驱动
39 0
|
6月前
|
传感器 芯片
PCF8574芯片介绍及驱动方法
PCF8574芯片介绍及驱动方法
366 0
|
存储
CMOS摄像头驱动分析
CMOS摄像头驱动分析
145 0
CMOS摄像头驱动分析
|
存储 机器人 芯片
嵌入式 STM32 步进电机驱动,干货满满,建议收藏
嵌入式 STM32 步进电机驱动,干货满满,建议收藏
嵌入式 STM32 步进电机驱动,干货满满,建议收藏
|
11月前
|
芯片
一款LED段码显示屏驱动芯片方案
一、基本概述 TM1620是一种LED(发光二极管显示器)驱动控制专用IC,内部集成有MCU数字接口、数据锁存器、LED驱动等电路。本产品质量可靠、稳定性好、抗干扰能力强。 二、基本特性 采用CMOS工艺 显示模式(8段×6位~10段×4位) 辉度调节电路(8 级占空比可调) 串行接口(CLK,STB,DIN) 振荡方式:内置RC振荡 内置上电复位电路 内置数据锁存电路 内置针对LED反偏漏电导致暗亮问题优化电路 抗干扰能力强 封装形式:SOP20 三、主要应用 主要适用于家电设备(智能热水器、微波炉、洗衣机、空调、电磁炉)、机顶盒、电子称、智能电表等数码管或LED
114 0
|
传感器 Linux
linux驱动——dht11温湿度传感器驱动(5.4版本内核)
linux驱动——dht11温湿度传感器驱动(5.4版本内核)
731 0
|
芯片 数据格式
16位ADC芯片SGM58031驱动重点
16位ADC芯片SGM58031驱动重点
801 0
使用 ULN2003 驱动 28BYJ-48 步进电机
使用 ULN2003 驱动 28BYJ-48 步进电机
325 0
使用 ULN2003 驱动 28BYJ-48 步进电机
|
传感器 编解码 芯片
51单片机驱动步进电机——使用ULN2003芯片
51单片机驱动步进电机——使用ULN2003芯片
372 0
51单片机驱动步进电机——使用ULN2003芯片