sensor模块如何识别不同的设备和型号?如何选用相应型号的头文件?

拿imx290举例,它的常用寄存器地址和函数是在imx290_lib.h中定义的。

static sensor_lib_t sensor_lib_ptr =
{
.sensor_slave_info =
{
.sensor_name = SENSOR_MODEL,
.slave_addr = 0x34,
.i2c_freq_mode = SENSOR_I2C_MODE_FAST,
.addr_type = CAMERA_I2C_WORD_ADDR,
.sensor_id_info =
{
.sensor_id_reg_addr = 0x301E, // a fake id register
.sensor_id = 0xB201, // a fixed value
}
...
}
...
}

这是对结构体的乱序赋值,既能初始化时赋值,也可以不考虑顺序。

.exposure_func_table =
{
.sensor_calculate_exposure = sensor_calculate_exposure,
.sensor_fill_exposure_array = sensor_fill_exposure_array,
},

仔细看其中,会发现这个结构体里还有函数方法。而这个函数就是具体不同型号中对寄存器的操作,就比如写入增益寄存器,可能存在有的有一个字节,有的需要两个字节。给一个相同的函数入口,其实就是实现了面向对象编程中的多态。

我们需要修改的是写入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 {
/** Tells the way the gain is computed and how gain and exposure are applied
fill_exp_array_type -> Mentions how exposure and gain api is implemented
calc_exp_array_type -> Mentions how gain is calculated
**/
fill_exp fill_exp_array_type;
calc_exp calc_exp_array_type;
/** Function to calculate exosure based on real gain and
* linecount value, 1st param - real gain, 2nd param -
* linecount, 3rd param - exposure info output, return staus -
* success / failure */
int (*sensor_calculate_exposure) (float, unsigned int,
sensor_exposure_info_t *, float);

/** Function to create register table from exposure settings
* input param1 - register gain value
* input param2 - digital gain value
* input param3 - coarse integration time value
* input param4 - frame length line value
* input param5 - hdr luma
* input param6 - hdr param
* input param7 - register settings
* return value - 0 for success and negative value for
* failure **/
int (*sensor_fill_exposure_array)(unsigned int, unsigned int, unsigned int,
unsigned int, int, unsigned int, struct camera_i2c_reg_setting *,
unsigned int, int, int);
} sensor_exposure_table_t;

static int sensor_calculate_exposure(float real_gain,
unsigned int line_count, sensor_exposure_info_t *exp_info, float s_real_gain);

static int sensor_fill_exposure_array(unsigned int gain,
unsigned int digital_gain, unsigned int line,
unsigned int fl_lines, int luma_avg, unsigned int hdr_param,
struct camera_i2c_reg_setting* reg_setting,
unsigned int s_reg_gain, int s_linecount, int is_hdr_enabled);

结构体中的函数指针和函数名相同,并没直接引用。而是在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;//初始化声明
*(void **)&sensor_open_lib = dlsym(sensor_lib_params->sensor_lib_handle,
open_lib_str);//赋值
sensor_lib_params->sensor_lib_ptr = (sensor_lib_t *)sensor_open_lib();//调用

但是这么多指针什么意思需要考虑一下

由于sensor_fill_exposure_array是对曝光表的直接寄存器操作,因此可以说是最底层了,但是不同的sensor这个函数的内容也不一样,因此我们需要先找到调用它的函数。

我们可以看到sensor模块中的``sensor_apply_exposuresensor_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对曝光和增益进行修改。

让人吐血的是,这里已经有高通的设置了。

#ifdef ENABLE_MANUAL_EXPOSURE_UPDATE
if(updateExtGain(&ext_real_gain) == SENSOR_SUCCESS)
real_gain = ext_real_gain;

if(updateExtLinecount(&ext_linecount) == SENSOR_SUCCESS)
linecount = ext_linecount;
#endif

但是将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文件