第4季4:图像sensor的驱动源码解析

news2024/11/15 8:43:36

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

一、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 V200R001C01SPC030\01.software\board\document_cn目录下的文档《ISP_3A开发指南》。

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

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

相关文章

Java基础面试题

请介绍全局变量和局部变量的区别 Java中的变量分为成员变量和局部变量&#xff0c;它们的区别如下&#xff1a; 成员变量&#xff1a; 成员变量是在类的范围里定义的变量&#xff1b;成员变量有默认初始值&#xff1b;未被static修饰的成员变量也叫实例变量&#xff0c;它存储于…

知识图谱-KGE-语义匹配-双线性模型-2019:TuckER

【paper】 TuckER: Tensor Factorization for Knowledge Graph Completion【简介】 这篇文章是英国爱丁堡大学的研究者发表于 ICML 2019 上的文章&#xff0c;提出了 TuckER&#xff0c;是一个线性的张量分解模型&#xff0c;对表示三元组事实的二值张量做 Tucker 分解。 背景…

python中nmupy获取本地数据和索引

1. numpy读取数据 可以使用numpy中的loadtxt进行数据读取&#xff0c;所包含的参数如下 参数名解释frame文件&#xff0c;字符串等也可以是.gz或bz2压缩文件dtype数据类型&#xff0c;即CSV中字符串以什么数据类型读入数组中&#xff0c;默认是np.floatdelimiter分隔字符串&a…

CAS:2379387-10-5;TPE-丙烯酰胺;AIE聚集诱导发光

英文名称:2-Propenamide,N-[4-(1,2,2-triphenylethenyl)phenyl]- 英文同义词:2-Propenamide,N-[4-(1,2,2-triphenylethenyl)phenyl]- CAS号:2379387-10-5 分子式:C29H23NO 分子量:401.5 结构式&#xff1a; AIE聚集诱导发光材料的特点&#xff1a; 1.在固态下有强发光特性&…

Typecho-handsome主题如何统计全站字数

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…

phpstudy2020安装及简单使用教程

phpstudyV8.0以上使用教程 简介安装步骤配置搭建网站自己的第一个PHP程序 最新版长这样 简介 按我的理解&#xff0c;phpstudy是一个集成式的PHP运行环境&#xff0c;虽然是个软件&#xff0c;但他实现的功能就是给PHP语言编写的脚本赋予一个可执行的环境&#xff0c;可以…

GPR(高斯过程回归)

写在前面&#xff1a;   本文为科研理论笔记的第三篇&#xff0c;其余笔记目录传送门&#xff1a; 理论笔记专栏目录 介绍结束下面开始进入正题&#xff1a; 1 高斯分布 ​   一元高斯分布的概率密度函数为&#xff1a; p(x)1σ2πexp⁡(−(x−μ)22σ2);简写为&#xff…

Lecture2:损失函数及优化

目录 1.损失函数 1.1 支持向量机SVM 1.1.1 SVM的代价函数及优化目标 1.1.2 如何理解将SVM成为大间距分类器 1.1.3 大间距分类器的数学原理 1.1.4 SVM核函数&#xff08;构建非线性分类器&#xff09; 及控制点的选择 1.1.5 构建自己的SVM 1.2 softmax分类器 1.3 soft…

06-07-08 - 突破512字节的限制

---- 整理自狄泰软件唐佐林老师课程 1. 突破限制的准备工作 辅助函数 字符串打印软盘读取内存比较根目录区查找 1.1 字符串打印 问题&#xff1a; 主引导程序中如何进行字符串的打印&#xff1f; 1.1.1 BIOS中的字符串打印 指定打印参数&#xff08;AX 0x1301&#xff0…

Web大学生网页作业成品:个人博客主页 (纯HTML+CSS代码)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

HUST编译原理实验2

语法分析&#xff0c;建立并显示语法树 以识别 a 10.01; 为例&#xff0c;阐述语法分析的构造过程 1. 建立AST储存结构 由a 10.01得知&#xff0c;语法构成为: SentenceList->Sentence->Exp SEMI->ID ASSIGNOP FLOAT SEMI因此&#xff0c;需要储存结构如下 typ…

Java数据结构与Java算法学习Day10---图的了解和无向图(简略笔记记录)

目录 1.1图的定义及分类 142 1.2无向图 143 1.2.1图的相关术语 143 1.2.2无向图的存储结构 144 1.3图的API设计 145 1.4图的搜索 146 1.4.1深度优先搜索 146 1.4.2深度优先搜索API设计 147 1.5广度优先搜素 149 1.5.1广度优先搜索API设计及代码实现 150 1.5.2广度…

QT 系统学习 day04 事件(鼠标,键盘), UDP 通信, TCP 通信,多个定时器,重写绘画事件

1. 事件 (有 键盘按下&#xff0c; 鼠标按下&#xff0c; 鼠标双击等) 1.重写键盘按下事件&#xff0c; 函数&#xff1a; void keyPressEvent(QKeyEvent *event) /*** 按键按下事件 ***/ 代码&#xff1a; void keyPressEvent(QKeyEvent *event) /*** 按键按下事件 ***/{q…

【吴恩达机器学习笔记】十五、大规模机器学习

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4e3;专栏定位&#xff1a;为学习吴恩达机器学习视频的同学提供的随堂笔记。 &#x1f4da;专栏简介&#xff1a;在这个专栏&#xff0c;我将整理吴恩达机器学习视频的所有内容的笔记&…

低代码开发平台——体验系统的重要性

什么是优秀的体验&#xff1f; 简而言之&#xff0c;优质的体验意味着可以随时随地根据客户的需求提供服务&#xff0c;这在数字化的世界中&#xff0c;意味着真正的多重体验。Gartner早在2018年就定义了MXDP&#xff08;多体验开发平台&#xff09;&#xff0c;以应对我们日常…

游戏开发38课 unity 模板测试

ShaderLab 命令&#xff1a;模板 配置与 GPU 上的模板缓冲区相关的设置。 模板缓冲区为帧缓冲区中的每个像素存储一个 8 位整数值。为给定像素执行片元着色器之前&#xff0c;GPU 可以将模板缓冲区中的当前值与给定参考值进行比较。这称为模板测试。如果模板测试通过&#xff…

liunx如何重启mysql

Linux如何重启MySQL Linux中重启MySQL可以使用service mysql restart命令和脚本启动方式/etc/inint.d/mysql restart。 推荐&#xff1a;MySQL教程 其他命令如下&#xff1a; 一、 启动 1、使用 service 启动&#xff1a;service mysql start 2、使用 mysqld 脚本启动&am…

使用redis做分布式锁

思路步骤 获取锁, 使用 SETNX 命令设置一个key.如果没获取到&#xff0c;从新拿锁&#xff0c;返回步骤1 ; 从新拿锁可以设置等待时间;也可以记录拿锁次数为了做“避免死循环”.如果获取到&#xff0c;使用 EXPIRE 给锁加存活时间&#xff1b;接步骤3&#xff0c;执行业务&…

微机----------------中断控制器8259A(可编程的中断控制器)

目录 功能8259A的引脚8259A的工作方式中断响应顺序8259A的中断优先级管理8259A中断屏蔽管理⭐8259A的中断结束管理功能 ①单片825能管理8级中断,并且可级联管理64级 ②可对任一级中断单独屏蔽或允许 ③中断被响应后,可直接提供中断类型号 ④可通过编程选择其工作方式 IRR中断…

C++入门篇

入门C命名空间命名空间定义命名空间的使用C输入&#xff0c;输出缺省参数缺省参数概念缺省参数分类函数重载函数重载概念C支持函数重载的原因引用引用概念引用特性具体应用const修饰的常量进行引用&#xff08;常引用&#xff09;传值&#xff0c;传引用的效率引用和指针的区别…