sensor模块如何识别不同的设备和型号?如何选用相应型号的头文件?
拿imx290举例,它的常用寄存器地址和函数是在imx290_lib.h中定义的。
static sensor_lib_t sensor_lib_ptr = |
这是对结构体的乱序赋值,既能初始化时赋值,也可以不考虑顺序。
.exposure_func_table = |
仔细看其中,会发现这个结构体里还有函数方法。而这个函数就是具体不同型号中对寄存器的操作,就比如写入增益寄存器,可能存在有的有一个字节,有的需要两个字节。给一个相同的函数入口,其实就是实现了面向对象编程中的多态。
我们需要修改的是写入sensor的增益和快门,因此越接近底层越不容易出现问题,比如避免因为预览和拍照流的参数位置不同造成的影响。
sensor_fill_exposure_array
函数是对曝光类寄存器进行操作,
在头文件中初始化如何引用?不是说变量不要在头文件中初始化么?
虽然在代码中好像使用了相同的变量,但是实际上使用的是不同的变量,在每个源文件中都有单独的变量。所以,在头文件中定义static变量会造成变量多次定义,造成内存空间的浪费,而且也不是真正的全局变量。应该避免使用这种定义方式。
先假定为声明,多次声明是没错的,最后才是定义
问:
一般来说,静态全局变量只应该定义在实现文件中,但有时由于一些特殊的目的,也可能定义在头文件中。比如在有些标准库的实现中,就用这种方法来初始化标准流cin, cout,或者在在tr1库中,也用这种方法来定义占位符。每一个包含该头文件的实现文件中都拥有该变量的一份拷贝,这些变量放在运行体的data段或者bss段。
比如下面这个变量定义在一个头文件中:
static int data[1024*1024];
我把这个文件同时包含在几个cpp文件中,按我的理解,这个程序占用的内存应该显著增大,但是,从实际运行结果来看,并没有变化,生成的exe文件大小也没有变化,这是因为延迟加载呢,还是被编译器优化掉了?有没有明白的达人解释一下。
答:
这不是编译器的问题,而是OS的virtual memeory管理机制导致的。数据在实际使用之前,是不会占用内存的——缺页异常处理程序会为数据分配需要的内存。
要理解static,就必须要先理解另一个与之相对的关键字,很多人可能都还不知道有这个关键字,那就是auto,其实我们通常声明的不用static修饰的变量,都是auto的,因为它是默认的,就象short和long总是默认为int一样;我们通常声明一个变量:
int a;
string s;
其实就是:
auto int a;
auto string s;
而static变量的声明是:
static int a;
static string s;
这样似乎可以更有利于理解auto和static是一对成对的关键字吧,就像private,protected,public一样;
对于static的不理解,其实就是对于auto的不理解,因为它是更一般的;有的东西你天天在用,但未必就代表你真正了解它;auto的含义是由程序自动控制变量的生存周期,通常指的就是变量在进入其作用域的时候被分配,离开其作用域的时候被释放;而static就是不auto,变量在程序初始化时被分配,直到程序退出前才被释放;也就是static是按照程序的生命周期来分配释放变量的,而不是变量自己的生命周期;所以,像这样的例子:
void func()
{
int a;
static int b;
}
每一次调用该函数,变量a都是新的,因为它是在进入函数体的时候被分配,退出函数体的时候被释放,所以多个线程调用该函数,都会拥有各自独立的变量a,因为它总是要被重新分配的;而变量b不管你是否使用该函数,在程序初始化时就被分配的了,或者在第一次执行到它的声明的时候分配(不同的编译器可能不同),所以多个线程调用该函数的时候,总是访问同一个变量b,这也是在多线程编程中必须注意的!
只在cpp内有效的全局变量:
在cpp文件的全局范围内声明:
static int g_ = 0;
这个变量的含义是在该cpp内有效,但是其他的cpp文件不能访问这个变量;如果有两个cpp文件声明了同名的全局静态变量,那么他们实际上是独立的两个变量;
如果不使用static声明全局变量:
int g_ = 0;
那么将无法保证这个变量不被别的cpp共享,也无法保证一定能被别的cpp共享,因为要让多个cpp共享一个全局变量,应将它声明为extern(外部)的;也有可能编译会报告变量被重复定义;总之不建议这样的写法,不明确这个全局变量的用法;
如果在一个头文件中声明:
static int g_vaule = 0;
那么会为每个包含该头文件的cpp都创建一个全局变量,但他们都是独立的;所以也不建议这样的写法,一样不明确需要怎样使用这个变量,因为只是创建了一组同名而不同作用域的变量。
这里顺便说一下如何声明所有cpp可共享的全局变量,在头文件里声明为extern的:
extern int g_; // 注意,不要初始化值!
然后在其中任何一个包含该头文件的cpp中初始化(一次)就好:
int g_ = 0; // 初始化一样不要extern修饰,因为extern也是声明性关键字;
然后所有包含该头文件的cpp文件都可以用g_这个名字访问相同的一个变量;
也就是说在头文件中定义变量,调用一次其实就会创建一个新变量。那么这个的好处是什么呢?为什么不放在C语言中呢?
利用sensor_open_lib
函数传递相应设备的结构体(包含方法和变量),但是他是如何引用?
在sensor.c中的sensor_load_library
函数中用到了sensor_open_lib
;也就是说在这个地方进行了调用。从此函数可以看出来是根据sensor的名字进行调用。
把容易变化的放在动态库中,
注意:sensor_lib.h
typedef struct { |
结构体中的函数指针和函数名相同,并没直接引用。而是在imx290_lib.h文件中进行了引用。
.exposure_func_table =
{
.sensor_calculate_exposure = sensor_calculate_exposure,
.sensor_fill_exposure_array = sensor_fill_exposure_array,
},
其实这两者虽然名字相同,但是并没有进行对应。结构体中的int(*p)(int,int)其实就是定义的一个函数。我觉得虽然加了括号,但是其实本质还是定义了一个指针,交代了指向的区域是什么类型。是否这个括号是必须的?
*p[4] 是一个指针数组,首先它是一个数组,数组中存的是指针,存指针的数组。
(*q)[4] 是数组指针,加括号先与 *结合,它是一个指针,指向一个第一维包含2个元素的2维数组,存数组地址的指针。
也就是说int(*p)(int,int)是一个函数指针,p指向这个函数。
当引用的时候可以直接把函数指针p当做函数名耐用,也可以用*p。
注意区分:
声明 | 意义 |
---|---|
int p(int); | 简单的使用名字调用函数P,但是执行过程可能和我想象的不太一样,函数名p首先转换为一个函数指针,该指针指定函数在内存中的位置。然后函数调用操作符调用该函数,执行开始于这个地址的代码。 |
int *p(); | 声明和普通的表达式在求值所用的规则相同。首先执行的是函数调用操作符(),因为它的优先级高于间接访问操作符*;因此p是一个函数,它的返回类型是一个整型的指针。 |
int (*p)(); | 第二队括号是函数调用操作符,第一对括号迫使间接访问在函数调用之前进行,使f成为一个函数指针,他所指向的函数返回一个整型值。 |
int *(*p)(); | p是一个函数指针,所指向的函数的返回值是一个整型指针。 |
刚刚看到一个有意思的问题:float可以表示的数范围大的多,那么float是否能比int表示更多不同的值呢?
答案是否定的,float和int长度一样,这就代表他们能够表示的不同的值的数量是相同的,但是在绝大多数浮点系统中,这个答案是错误的,0通常有很多种表示形式,而且通过使用不规范的小数形式,其他值也具有多种不同的表示形式。
void *(*sensor_open_lib)(void) = NULL;//初始化声明 |
但是这么多指针什么意思需要考虑一下
由于sensor_fill_exposure_array
是对曝光表的直接寄存器操作,因此可以说是最底层了,但是不同的sensor这个函数的内容也不一样,因此我们需要先找到调用它的函数。
我们可以看到sensor模块中的``sensor_apply_exposure和
sensor_set_exposure`函数不仅仅是在sensor aec的init中进行调用,还有双摄,从摄,手动AE,自动AE的设置,可以说它是所有数据流设置曝光增益流程的必经之处。
在sensor_apply_exposure
中会对aec的最大曝光行最大增益等进行判断,保证不会超过lib.h的限制之后,然后通过sensor_fill_exposure_array
写入。不希望我们写入的值超过了sensor的设置,因此我们最好在apply函数之前,也就是sensor_set_exposure
函数之后进行修改。
在sensor_calculate_exposure
函数中,将增益转换成了可以写入寄存器的整型,因此我们也不要动,在sensor_calculate_exposure
之前进行修改,同时又尽可能的少,就选择在sensor_set_exposure
函数里的sensor_calculate_exposure
对曝光和增益进行修改。
让人吐血的是,这里已经有高通的设置了。
|
但是将ENABLE_MANUAL_EXPOSURE_UPDATE
定义一下,发现并没有出现问题。
但是会有一个问题,打印可能不会显示。
typedef 允许为各种数据类型定义新名字。typedef声明的写法和普通的声明基本相同,只是把typedef这个关键字出现在了声明前面。
如:char *ptr;代表ptr声明为一个指向字符的指针。加上typedef之后:
typedef char *ptr;把标识符ptr作为指向字符类型的指针类型的新名字。
ptr a;就是声明a是一个指向字符的指针。
一直以为typedef和define类似,走进了这个误区。
可以在sensor_load_library看到通过sensor name加载不同的动态库。
又是如何确定sensor name呢?
通过sebsor_probe调用,又被init_xml_probe函数通过xml文件,配置相应的sensor及chromatix。
#define CONFIG_XML_SYSTEM_PATH “/system/etc/camera/“
进入/system/etc/camera/会发现几个文件:camera_config.xml csidtg_camera.xml csidtg_chromatix.xml imx290_chromatix.xml
这就代表了这个用的是290的sensor
可是你会发现在文件夹media-controller\modules\sensors\configs中,xml文件又有很多很多,每个设备都有对应的xml
那么又是如何区分的呢?
media-controller/modules/sensors/configs/Android.mk文件中
LOCAL_SRC_FILES代表着根据不同的设备选用不同的xml文件
看xml文件,里面交代了每个sensor的名字,以及镜头,视场角等信息,
xml里面还有HWID,每个HWID对应了不同的sensor和chromatix文件