我是靠谱客的博主 迷人彩虹,最近开发中收集的这篇文章主要介绍第4季4:图像sensor的驱动源码解析一、sensor驱动源码的框架二、sensor驱动源码的解析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。 

一、sensor驱动源码的框架

mpp定义了一整套sensor驱动的实现和封装,这里以ar0130型号的sensor为例进行说明。

1、sensor层驱动

(1)sensor层驱动位于mpp/component/isp/sensor/ar0130目录,包括:Makefile、ar0130_cmos.c、ar0130_sensor_ctl.c文件。

(2)ar0130_cmos.c文件定义了一个回调函数sensor_register_callback和一些上层函数,体现了sensor驱动框架中的最上层,或者说功能层。

(3)ar0130_sensor_ctl.c文件定义了一些与底层硬件相关的寄存器值配置函数。这些函数间接调用了ar0130_sensor_ctl.c文件中的sensor_write_register函数,这个函数用来给寄存器配置数值。注意,这个文件并不是sensor驱动框架的最底层,最底层的应该是I2C驱动,因为函数sensor_write_register里面的sensor_i2c_init函数打开I2C设备文件,然后利用ioctl函数来写寄存器的值,也就是说,最后还是需要调用I2C驱动来完成寄存器数值的配置的。一言以蔽之,sensor内部有若干寄存器,可以通过I2C接口来读取。

2、底层I2C驱动

(1)I2C的驱动源码,位于内核源码的driver/i2c目录,提供了I2C层面的物理层操作接口。

(2)这层驱动只与Hi3518e芯片设置有关(主要与I2C控制器有关),海思SDK中已经写好,我们一般不会去改动这层驱动源码。

(3)这层驱动主要体现在I2C设备文件“/dev/i2c-0”。

3、驱动调用关系

(1)我们来分析一下mpp/component/isp/sensor/ar0130/Makefile文件。

# 忽略部分代码

# 寻找mpp/component/isp/sensor/ar0130目录下以.c结尾的文件
SRCS = $(wildcard ./*.c)

# 将这些.c文件换成后缀为.o的文件
OBJS = $(SRCS:%.c=%.o)

# 这个是啥意思?
# 为了不污染源码目录,新建了一个目录./obj/,将来.o文件会放在./obj目录里?
OBJS := $(OBJS:./%=obj/%)

# TARGETLIB=/mpp/component/isp/lib/libsns_ar0130.a
TARGETLIB := $(LIBPATH)/libsns_ar0130.a
# TARGETLIB_SO=/mpp/component/isp/lib/libsns_ar0130.a
TARGETLIB_SO := $(LIBPATH)/libsns_ar0130.so

# 利用这些
# 制作/mpp/component/isp/lib/libsns_ar0130.a静态链接库文件
# 制作/mpp/component/isp/lib/libsns_ar0130.so动态链接库文件
all:$(TARGETLIB)
$(TARGETLIB):$(OBJS)
	@($(AR) $(ARFLAGS) $(TARGETLIB) $(OBJS))
	@($(CC) $(ARFLAGS_SO) $(TARGETLIB_SO) $(OBJS))

(2)分析可知,在该Makefile文件所在目录下执行make后,在mpp/component/isp/目录下生成了lib目录,该目录中有两个文件:libsns_ar0130.a、libsns_ar0130.so文件,它们分别是静态链接库文件、动态链接库文件。

root@ubuntu:/home/xjh/iot/hisi_development/Hi3518E_SDK/Hi3518E_SDK_V1.0.3.0/mpp/component/isp/sensor/ar0130# ls
ar0130_cmos.c  ar0130_sensor_ctl.c  Makefile  obj
root@ubuntu:/home/xjh/iot/hisi_development/Hi3518E_SDK/Hi3518E_SDK_V1.0.3.0/mpp/component/isp/sensor/ar0130# cd ../..
root@ubuntu:/home/xjh/iot/hisi_development/Hi3518E_SDK/Hi3518E_SDK_V1.0.3.0/mpp/component/isp# ls
3a  defog  firmware  include  iniparser  lib  Makefile  sensor
root@ubuntu:/home/xjh/iot/hisi_development/Hi3518E_SDK/Hi3518E_SDK_V1.0.3.0/mpp/component/isp# cd lib/
root@ubuntu:/home/xjh/iot/hisi_development/Hi3518E_SDK/Hi3518E_SDK_V1.0.3.0/mpp/component/isp/lib# ls
libsns_ar0130.a  libsns_ar0130.so
root@ubuntu:/home/xjh/iot/hisi_development/Hi3518E_SDK/Hi3518E_SDK_V1.0.3.0/mpp/component/isp/lib# 

(3)在mpp/component/isp/Makefile中,将上面生成的libsns_ar0130.a、libsns_ar0130.so文件复制到了mpp/lib目录下。

# 省略部分代码
ISP_KO := ./ko
ISP_LIB := ./lib

.PHONY:clean all rel
all:
	@mkdir -p $(REL_KO); cp $(ISP_KO)/*.ko $(REL_KO) -rf
	@mkdir -p $(REL_LIB); cp $(ISP_LIB)/*.a $(REL_LIB) -rf; cp $(ISP_LIB)/*.so $(REL_LIB) -rf

其中的REL_KO、REL_LIB在mpp/Makefile.param文件中定义如下:

export REL_INC := $(REL_DIR)/include
export REL_LIB := $(REL_DIR)/lib
export REL_KO  := $(REL_DIR)/ko

# REL_INC = ……/mpp/include
# REL_LIB = ……/mpp/lib
# REL_KO  = ……/mpp/ko

(4)分析与总结

ar0130的sensor层驱动代码以库的形式存在(对应着mpp/component/isp/lib目录中的libsns_ar0130.a、libsns_ar0130.so文件,最后被复制到mpp/lib目录中),这说明sensor层驱动不属于内核源码,而属于应用层的内容,或者说应用层的驱动(见博客I2C子系统详解1——I2C总线设备的驱动框架的描述)。在对应用程序进行编译链接时,这些库会以静态或者动态的方式链接进去。

在博文第一季7:海思的根文件系统的概览与制作中,我们把mpp/lib/目录中的文件拷贝到了板载系统的/usr/lib目录中。其实拷贝动态链接库就好,因为如果是静态链接库,在虚拟机中编译生成可执行文件时,可执行文件就已经包括静态链接库了,因此没必要把静态链接库也拷贝到板载系统上。

二、sensor驱动源码的解析

1、sample_venc.c调用sensor驱动的流程

利用SI软件建立mpp工程,然后从sample_venc.c入手,分析调用sensor驱动的流程。

SAMPLE_VENC_1080P_CLASSIC
    SAMPLE_COMM_VI_StartVi
        SAMPLE_COMM_VI_StartIspAndVi
            SAMPLE_COMM_ISP_Init
                sensor_register_callback//位于ar0130_cmos.c文件文件

(1)从上面的分析可知,应用层sample_venc.c中调用了sensor_register_callback函数,通过这个函数去操作sensor硬件。这个函数位于ar0130_cmos.c文件中,而ar0130_cmos.c文件将来会被编译成库的形式被调用,而不是驱动的形式。即ar0130_cmos.c文件还是属于应用层的,但因为有驱动的性质,所以称之为“应用层驱动”(见I2C子系统详解1——I2C总线设备的驱动框架)。

(2)但“应用层驱动”最终需要调用内核驱动来操控硬件。比如sensor_register_callback函数流程中的sensor_i2c_init函数,它打开了一个I2C设备文件“/dev/i2c-0”(这个设备文件对应着内核中的I2C驱动相关的内容,它是I2C驱动的表征),然后利用ioctl函数给寄存器写入数值。至于数值怎样写入寄存器的,涉及到I2C驱动的细节内容(比如怎样发送数据),而sensor驱动层是不包含这些细节内容的,它只是操作I2C设备文件“/dev/i2c-0”(从这角度来看,sensor驱动层是应用层)。

sensor_register_callback                //位于ar0130_cmos.c文件文件
    cmos_init_sensor_exp_function       //位于ar0130_cmos.c文件文件
        sensor_init                     //位于ar0130_sensor_ctl.c文件
            sensor_init_720p_30fps      //位于ar0130_sensor_ctl.c文件
                sensor_write_register   //位于ar0130_sensor_ctl.c文件
                    sensor_i2c_init     //位于ar0130_sensor_ctl.c文件
                    ioctl
int sensor_i2c_init(void)
{
    if(g_fd >= 0)
    {
        return 0;
    }    
#ifdef HI_GPIO_I2C
    int ret;

    g_fd = open("/dev/gpioi2c_ex", 0);
    if(g_fd < 0)
    {
        printf("Open gpioi2c_ex error!n");
        return -1;
    }
#else
    int ret;

    g_fd = open("/dev/i2c-0", O_RDWR);
    if(g_fd < 0)
    {
        printf("Open /dev/i2c-0 error!n");
        return -1;
    }

    ret = ioctl(g_fd, I2C_SLAVE_FORCE, sensor_i2c_addr);
    if (ret < 0)
    {
        printf("CMD_SET_DEV error!n");
        return ret;
    }
#endif

    return 0;
}

2、sensor_register_callback函数的分析

(1)该函数内容如下,由代码结构可知有ISP、AE、AWB三部分,我们重点关注ISP部分。

int sensor_register_callback(void)
{
    ISP_DEV IspDev = 0;
    HI_S32 s32Ret;
    ALG_LIB_S stLib;
    ISP_SENSOR_REGISTER_S stIspRegister;
    AE_SENSOR_REGISTER_S  stAeRegister;
    AWB_SENSOR_REGISTER_S stAwbRegister;

    cmos_init_sensor_exp_function(&stIspRegister.stSnsExp);
    s32Ret = HI_MPI_ISP_SensorRegCallBack(IspDev, AR0130_ID, &stIspRegister);
    if (s32Ret)
    {
        printf("sensor register callback function failed!n");
        return s32Ret;
    }
    
    stLib.s32Id = 0;
    strncpy(stLib.acLibName, HI_AE_LIB_NAME, sizeof(HI_AE_LIB_NAME));
    cmos_init_ae_exp_function(&stAeRegister.stSnsExp);
    s32Ret = HI_MPI_AE_SensorRegCallBack(IspDev, &stLib, AR0130_ID, &stAeRegister);
    if (s32Ret)
    {
        printf("sensor register callback function to ae lib failed!n");
        return s32Ret;
    }

    stLib.s32Id = 0;
    strncpy(stLib.acLibName, HI_AWB_LIB_NAME, sizeof(HI_AWB_LIB_NAME));
    cmos_init_awb_exp_function(&stAwbRegister.stSnsExp);
    s32Ret = HI_MPI_AWB_SensorRegCallBack(IspDev, &stLib, AR0130_ID, &stAwbRegister);
    if (s32Ret)
    {
        printf("sensor register callback function to awb lib failed!n");
        return s32Ret;
    }
    
    return 0;
}

(2)函数HI_MPI_ISP_SensorRegCallBack(IspDev, AR0130_ID, &stIspRegister),主要用来绑定sensor和Hi3518e的ISP模块。示意图如下,其中ISP表示Hi3518e中的ISP模块,该模块位于VI模块里。

该函数的参数1,IspDev=0,是因为Hi3518e中只有一个VI模块,所以编号为0。

该函数的参数2,AR0130_ID=130,这是sensor AR0130在mpp中的唯一编号。

重点关注参数3,它表示ISP模块可以对sensor进行哪些操作。该参数是结构体变量,结构体的成员是一些函数指针,如下所示。

typedef struct hiISP_SENSOR_EXP_FUNC_S
{
    HI_VOID(*pfn_cmos_sensor_init)(HI_VOID);
    HI_VOID(*pfn_cmos_sensor_exit)(HI_VOID);
    HI_VOID(*pfn_cmos_sensor_global_init)(HI_VOID);
    HI_S32(*pfn_cmos_set_image_mode)(ISP_CMOS_SENSOR_IMAGE_MODE_S *pstSensorImageMode);
    HI_VOID(*pfn_cmos_set_wdr_mode)(HI_U8 u8Mode);
    
    /* the algs get data which is associated with sensor, except 3a */
    HI_U32(*pfn_cmos_get_isp_default)(ISP_CMOS_DEFAULT_S *pstDef);
    HI_U32(*pfn_cmos_get_isp_black_level)(ISP_CMOS_BLACK_LEVEL_S *pstBlackLevel);
    HI_U32(*pfn_cmos_get_sns_reg_info)(ISP_SNS_REGS_INFO_S *pstSnsRegsInfo);

    /* the function of sensor set pixel detect */
    HI_VOID(*pfn_cmos_set_pixel_detect)(HI_BOOL bEnable);
} ISP_SENSOR_EXP_FUNC_S;

这些函数指针指向哪些函数?或者说什么时候被填充的?

如下所示,在cmos_init_sensor_exp_function(&stIspRegister.stSnsExp)函数中完成了上述函数指针的填充工作。其中sensor_init等实体函数,位于ar0130_sensor_ctl.c文件中,它们是sensor驱动要提供的函数。

HI_S32 cmos_init_sensor_exp_function(ISP_SENSOR_EXP_FUNC_S *pstSensorExpFunc)
{
    memset(pstSensorExpFunc, 0, sizeof(ISP_SENSOR_EXP_FUNC_S));

    pstSensorExpFunc->pfn_cmos_sensor_init = sensor_init;
    pstSensorExpFunc->pfn_cmos_sensor_exit = sensor_exit;
    pstSensorExpFunc->pfn_cmos_sensor_global_init = sensor_global_init;
    pstSensorExpFunc->pfn_cmos_set_image_mode = cmos_set_image_mode;
    pstSensorExpFunc->pfn_cmos_set_wdr_mode = cmos_set_wdr_mode;
    
    pstSensorExpFunc->pfn_cmos_get_isp_default = cmos_get_isp_default;
    pstSensorExpFunc->pfn_cmos_get_isp_black_level = cmos_get_isp_black_level;
    pstSensorExpFunc->pfn_cmos_set_pixel_detect = cmos_set_pixel_detect;
    pstSensorExpFunc->pfn_cmos_get_sns_reg_info = cmos_get_sns_regs_info;

    return 0;
}

(3)上面讨论的是ISP,用来初始化sensor中与ISP(图像采集等)相关的操作;而代码中的AE(自动曝光)用来初始化sensor中与AE相关的操作,AWB(自动白平衡)用来初始化sensor中与AWB相关的操作。其实应该还有自动对焦AF,但AR0130没有这个功能,所以这里没有。因为AE、AWB的内容结构和ISP类似,这里不再赘述。

(4)完成ISP、AE、AWB这三组操作之后,sensor和Hi3518e的对接就基本完成了。代码中其他的内容,基本就是上面的函数指针要指向的实体函数,这也是我们写sensor驱动的人要编写的部分(一般厂商工程师会提供一些较深入的数据与内容)。

(5)关于HI_MPI_ISP_SensorRegCallBack(IspDev, AR0130_ID, &stIspRegister)函数的用法,可以参考海思SDK中Hi3518E V200R001C01SPC0301.softwareboarddocument_cn目录下的文档《ISP_3A开发指南》。

最后

以上就是迷人彩虹为你收集整理的第4季4:图像sensor的驱动源码解析一、sensor驱动源码的框架二、sensor驱动源码的解析的全部内容,希望文章能够帮你解决第4季4:图像sensor的驱动源码解析一、sensor驱动源码的框架二、sensor驱动源码的解析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部