Tmd27711 三合一传感器流程

云科世纪   戴楊一如

参考http://blog.sina.com.cn/s/blog_89f592f5010132qy.html

零、編譯與燒寫

编内核与android

./buildT20WGRETAIL

烧system.2knand.bin

编内核

./buildT20WGRETAIL kernel

烧boot.2knand.bin

编单个android模块

trunk下

source build/envsetup.sh

choosecombo 1(release),9(QHD4500),3(eng)

进模块目录

如:device/cct/common/libsku7sensors

执行mm

烧  out/.../system/lib/hw/sensor...(see Makefile)

壹、Kernel層:

源文件:kernel/drivers/input/misc/  Tmd27711.c

一、名词:

als:ambient light sensor

ps:proximity sensor

ailt: als interrupt lower threshold

aiht: als interrupt higher threshold

pilt: ps interrupt lower threshold

piht:ps interrupt higher threshold

二、数据采集方式:

Tmd27711 默认通过中断方式采集数据。驱动的主要工作是为传感器设置上下阀值,当传感器侦测到光线/距离高于上阀值或低于下阀值时,就产生一个硬件中断,然后驱动扑捉这个中断,并将此时的报值处理后通过input子系统报给上层。

三、sysfs接口

Tmd27711通过sysfs接口向上层提供获取/设置sensor enable、获取/设置sensor delay的方法。

774行:static DEVICE_ATTR(proximity_poll_delay, S_IRUGO | S_IWUGO,

proximity_poll_delay_show, proximity_poll_delay_store);

static DEVICE_ATTR(p_data, S_IRUGO | S_IWUGO,

p_data_show, NULL);

754行:static DEVICE_ATTR(light_poll_delay, S_IRUGO | S_IWUGO,

light_poll_delay_show, light_poll_delay_store);

这个宏原型是:#define DEVICE_ATTR(_name, _mode, _show, _store) \

struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

主要实现了proximity_poll_delay, proximity_poll_delay_show, proximity_poll_delay_store, light_poll_delay,light_poll_delay_show, light_poll_delay_store这几个回调函数。

四、流程

从模块初始化开始tmd27711_init()->  i2c_add_driver()->  i2c_register_driver() ->  driver_register()  ->  bus_add_driver()  ->  driver_attach()  ->  __driver_attach()  ->  driver_probe_device()  -> really_probe()  其中有这样一句:

Dd.c 270行:ret = drv->probe(dev);

在这里调用了驱动的探针函数bma020_probe()

接下来看探针函数。

测试适配器是否支持I2C_FUNC_SMBUS_BYTE:

925行:i2c_check_functionality(client->adapter, I2C_FUNC_I2C)

给client添加name,给sharp_dev的g_client成员赋值:

932行:if (!strcmp(client->name, "tmd27711"))

{

sharp_dev.g_client_ps = client;

sharp_dev.g_client_als = client;

}

取出平台数据,并用它初始化中断队列:

937行:p_data = client->dev.platform_data;

if( NULL != p_data->init_irq ){

p_data->init_irq();

}

客户端初始化:

943:i2c_set_clientdata(client, &sharp_dev);

该句最终将调用Core.c下的函数device_private_init(),其中有这句:

dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);

它将给主数据结构体分配空间。

下面开始初始化设备状态(并非硬件实际状态,而是记录的状态):

947:mutex_init(&sharp_dev.update_lock);  //加持自旋锁

949:sharp_dev.enable = 0;   /* default mode is standard */

sharp_dev.h_threshold = 0;

sharp_dev.l_threshold = 0;

sharp_dev.ps_detection = 0;  /* default to no detection */

sharp_dev.power_state = 0;

下面初始化外围设备(对应软件概念client

959:err = tmd27711_init_client(client);

这个函数主要是调用下面这两句,并在后面做一些验证。

837:struct tmd27711_data *data = i2c_get_clientdata(client);//得到client的data结构。

841:err=tmd27711_init_register(client);//初始化寄存器,即把设备中一些可写的寄存器写入初始值。

回到probe中,接下来两行分别注册距离和光线传感器

962:ret = ps_register_device(sharp_dev.g_client_ps);

965:ret = als_register_device(sharp_dev.g_client_als);

这两个函数的内容是一致的,注意全局变量sharp_dev,这里实现的是sharp_dev.g_ps / sharp_dev.g_als. 我猜g_的意思是get_

其中注意这句:

sharp_dev.g_ps = input_allocate_device(); / sharp_dev.g_als = input_allocate_device();

它们完成I/O空间注册,为输入子系统申请 input_dev 结构。

再回到probe,接下来几句是重点,因为这东西本项目的acceleration sensor里面没有,这是区别:

969:wake_lock_init(&sharp_dev.tmd_wake_lock, WAKE_LOCK_SUSPEND, "tmd-wake-lock");

使用Wakelock机制,开始持有一个wakelock注意第二个实参WAKE_LOCK_SUSPEND,初始化休眠锁

然后初始化工作队列:

       970行: INIT_DELAYED_WORK(&sharp_dev.dwork, tmd27711_work_handler);

       上面这句往宏里传入回调函数tmd27711_work_handler,这个回调函数将是设备硬件中断的处理函数。来具体看这个函数:

它先读取tmd的状态寄存器0x13,目的是对中断来源做判断,而后分别对两种中断(Proximity/light)作不同的处理:

status = i2c_smbus_read_byte_data(g_client, CMD_BYTE|TMD27711_STATUS_REG);

……

if ((status & data->enable & 0x20) == 0x20)

{

。。。

}

else if ((status & data->enable & 0x10) == 0x10)

。。。

下面分别来看处理方法。

首先,如果状态寄存器显示proximity sensor产生了中断,则先读取光感数据,目的是做一次判断,把它与atime(als time)乘以系数后的值相比,若光感值更大,则认为此次psensor中断是由light sensor产生的噪声,从而将它过滤;若光感值小于乘积,则暂时认可这个中断,调用tmd27711_change_ps_threshold。

cdata = i2c_smbus_read_word_data(g_client, CMD_WORD|TMD27711_CDATAL_REG);

if (cdata < (75*(1024*(256-data->atime)))/100)

tmd27711_change_ps_threshold(g_client);

else {

pr_debug("Triggered by background ambient noise\n");

}

函数tmd27711_change_ps_threshold定义在本源文件,是距离传感器中断处理的核心函数。在函数中,首先读取数据传感:186:Proximity Data Register (0x18 − 0x19h),将之与上下阀值作比较,若此次中断是第一次中断,阀值就是初始化时设置的:

190:if ( (data->ps_data > data->pilt) && (data->ps_data >= data->piht) ) {

若所报值高于上阀值与下阀值(说明该中断是由于硬件检测到反射红外线强度高于设置的“上阀值”而产生的),那么认为有物体接近了psensor,将侦测状态置零,表示far-to-near detected:

192:data->ps_detection = 0;

然后改写阀值寄存器,低阀值写为初始值,高阀值写为1023;这样设置之后,只有侦测到1023这个值才会产生中断。

194:i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_PILTL_REG, data->l_threshold);

i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_PIHTL_REG, 1023);//1023

然后改固件中记录的阀值(并非硬件改动,硬件改动为上面2行):

197: data->pilt = data->l_threshold;

data->piht = 1023;

上面是ps报值高于上下阀值导致的中断这种情况,下面是报值低于上下阀值的情况,判断条件如下:

202:else if ( (data->ps_data <= data->pilt) && (data->ps_data < data->piht) ) {

步骤与上面一一对应,因为检测到低强度红外反射,故认为有物体远离传感器,将ps_detection置1,表near-to-far,改写外设的阀值寄存器,高值写为初始,低值写为0,这样只有检测到0这个值时才会产生中断。代码如下:

/* near-to-far detected */

data->ps_detection = 1;

i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_PILTL_REG, 0);//0

i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_PIHTL_REG, data->h_threshold);

data->pilt = 0;

data->piht = data->h_threshold;

//set_irq_wake(client->irq,0);

pr_debug("near-to-far detected data->ps_detection=%d\n",data->ps_detection);

然后将ps_detection通过input子系统上报给上层,注意上报的只是0或1,即near-to-far/far-to-near

214: input_report_abs(sharp_dev.g_ps, ABS_DISTANCE, data->ps_detection);

input_sync(sharp_dev.g_ps);

上面第二句是每次上报结束时,再上报一个结束标志,就像对讲机的“over”

              以上是tmd27711_change_ps_threshold处理的全过程。

       下面回到tmd27711_work_handler,第一种中断情况(即:硬件状态显示产生了ps中断,且不是als的噪声)处理还没有结束,还有下面两句:

              365  i2c_smbus_write_byte(g_client, CMD_CLR_PS_INT);

                     i2c_smbus_write_byte(g_client, CMD_CLR_PS_ALS_INT);

写入0xe5和0xe7,清除中断信号。

接下来是第二种情况。这里与上面的分支有些许不同。当检测到状态寄存器的0x10这个值的时候,直接认为als引起了中断,而不再去排除噪声的情况:

tmd27711_change_als_threshold(g_client);

i2c_smbus_write_byte(g_client, CMD_CLR_ALS_INT);

操作是与上面对应的,先调用als中断处理的核心函数,后清除中断信号。下面来看这个核心函数tmd27711_change_als_threshold。

先读取当前光感值(0x14,0x15):

365:data->als_data = i2c_smbus_read_word_data(client, CMD_WORD|TMD27711_CDATAL_REG);

再分别取这个值的0.8倍与1.2倍作为上下阀值,其中这个系数是由TMD27711_ALS_THRESHOLD_HSYTERESIS这个宏设定的:

308:data->als_threshold_l = (data->als_data * (100-TMD27711_ALS_THRESHOLD_HSYTERESIS) ) /100;

data->als_threshold_h = (data->als_data * (100+TMD27711_ALS_THRESHOLD_HSYTERESIS) ) /100;

将这两个值写入als阀值寄存器:

313:i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_AILTL_REG, data->als_threshold_l);

i2c_smbus_write_word_data(client, CMD_WORD|TMD27711_AIHTL_REG, data->als_threshold_h);

刚刚读的光强度来自于0x14与0x15,这两个寄存器的报值反映的是可见光与红外光的强度,还有0x16与0x17这两个寄存器,它们只响应红外线。这在硬件的datasheet中表述为:

Each device combines one photodiode (CH0), which is responsive to both visible and infrared light, and a second photodiode (CH1), which is responsive primarily to infrared light.

下面要报给上层的值,就引入了ch1,也就是0x16与0x17的值,将它们与ch0做一番处理:

317:if ((lux_val = taos_get_lux()) < 0)

printk(KERN_ERR "TAOS: call to taos_get_lux() returned error %d in ioctl als_data\n", lux_val);

下面来看taos_get_lux()这个处理光线强度值的函数。在一番声明之后,通过一个for循环获取四个寄存器(ch0,ch1)的值:

for (i = 0; i < 4; i++) {

if ((ret = (i2c_smbus_write_byte(sharp_dev.g_client_als, (CMD_BYTE | (TMD27711_CDATAL_REG + i))))) < 0) {

printk(KERN_ERR "TAOS: i2c_smbus_write_byte() to chan0/1/lo/hi reg failed in taos_get_lux()\n");

return (ret);

}

chdata[i] = i2c_smbus_read_byte(sharp_dev.g_client_als);

}

用raw_clear表示ch0,raw_ir表示ch1:

raw_clear = chdata[1];

raw_clear <<= 8;

raw_clear |= chdata[0];

raw_ir    = chdata[3];

raw_ir    <<= 8;

raw_ir    |= chdata[2];

接下来把这两个值自乘一些系数,然后做一些判断,然后我们迎来了这两句,它们的目的其实是要确定两个系数:

ratio = (raw_ir<<15)/raw_clear;

for (p = lux_tablep; p->ratio && p->ratio < ratio; p++);

if(!p->ratio)

return 0;

上面是将raw_ir(红外光)乘以2的15次方除以raw_clear(红外+可见),把商赋予ratio。然后用一个for循环去检索lux_tablep,目的是从lux_tablep中找到一组数据,这组数据里的ratio从左侧最接近(小于且差最小)局部变量ratio。lux_tablep结构如下:

struct lux_data {

u16  ratio;

u16  clear;

u16  ir;

};

struct lux_data TritonFN_lux_data[] = {

{ 9830,  8320,  15360 },

{ 12452, 10554, 22797 },

{ 14746, 6234,  11430 },

{ 17695, 3968,  6400  },

{ 0,     0,     0     }

};

很明显,这个结构中,ratio是用来确定“我需要的是哪一组”,clear和ir是真正要用作下一句的系数的。

现在,来看本函数中最重要的一句:

lux = ((raw_clear*(p->clear)) - (raw_ir*(p->ir)));

现在知道了,原来这个函数的主要目的是把红外线的读数(ch1)从红外线+可见光(ch0)中剥离。

后面几句只是对lux这个值做一些增益处理和最大值修正。然后返回lux。

以上是处理光感数值的函数taos_get_lux的全部过程,现在回到调用者tmd27711_change_als_threshold从上个函数中拿到剥离了红外线的光感值后,将它除以1000:

lux_val=(int)(lux_val/1000);

然后通过input子系统上报这个值,同样的,报完后报一个“over”:

input_report_abs(sharp_dev.g_als, ABS_MISC, lux_val);

input_sync(sharp_dev.g_als);

至此,光感中断的核心处理函数tmd27711_change_als_threshold也全部走完了,回到调用者tmd27711_work_handler上面说过,调用完处理函数后, 将清理中断信号。最后唤醒休眠锁:

wake_unlock(&sharp_dev.tmd_wake_lock);

总的中断处理函数tmd27711_work_handler也走完,回到probe。刚刚进入处理函数是因为在probe中将它注册为工作队列:

INIT_DELAYED_WORK(&sharp_dev.dwork, tmd27711_work_handler);

最后注册中断处理函数:

if (request_irq(client->irq, tmd27711_interrupt, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,

client->name, &sharp_dev)) {

dev_info(&client->dev, "tmd27711 : Unable to acquire irq!\n");

goto exit_kfree;

}

关注回调函数tmd27711_interrupt()

384:tmd27711_reschedule_work(data, 0);

在跟下去,执行

__cancel_delayed_work(&data->dwork);

schedule_delayed_work(&data->dwork, delay);

延迟工作队列。

以上是probe全部流程。

贰、HAL層

源文件:device/qcom/common/libsku7sensors/  ProximitySensor.cpp , LightSensor.cpp

一、构造函数

ProximitySensor::ProximitySensor()

首先给事件变量mPendingEvent的成员赋值,分别给这些成员赋值version、sensor、type、acceleration.status。注意到距离传感器的type是SENSOR_TYPE_PROXIMITY。

然后将input_sysfs_path写为/sys/class/input/$input_name/device,

并调用setEnable(0, 1);

立刻来看setEnable。

函数的原型是virtual int setEnable(int32_t handle, int enabled);

但定义时却是int ProximitySensor::setEnable(int32_t, int en) {

所以忽略了第一个参数,不论传什么进来,都没有影响。

这个函数用来开/关Psensor,开还是关,由其第二个参数控制。主要做的事就是根据第二个参数,将0或者1写入/sys/class/input/$input_name/device/enable:

78: strcpy(&input_sysfs_path[input_sysfs_path_len], "enable");

fd = open(input_sysfs_path, O_RDWR);

88: if(write(fd, buf, sizeof(buf)) < 0)

这与驱动程序中的proximity_enable_store()函数以下几行对应:

Tmd27711.c 692行 if (sysfs_streq(buf, "1"))

new_value = true;

else if (sysfs_streq(buf, "0"))

new_value = false;

于是实现了enable设备。

然后关闭sysfs文件,改变本类中的状态变量:

close(fd);

mEnabled = flags;

接下来一句调用本源文件中的函数:

92: setInitialState();

看这个函数:

65: if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_PROXIMITY), &absinfo)) {

// make sure to report an event immediately

mHasPendingEvent = true;

mPendingEvent.distance = indexToValue(2);

}

return 0;

使用 EVIOCGABS ioctl提供绝对坐标轴设备的状态信息,若得到0,说明设备响应正常,则将mHasPendingEvent置为真,并设置距离为indexToValue(2);

函数indexToValue()在本源文件定义,只是简单地将值乘以一个系数,这个系数是宏PROXIMITY_THRESHOLD_GP2A:5.0f。

然后setInitialState函数跑完,返回0。

setEnable函数也跑完,返回0.

构造函数到此为止。

二、轮询函数

reandEvents()

这个函数是上层轮询设备的桥梁。上层通过策略,在某段时间里主动读取input缓冲区,查看驱动报上来的值。

首先检查mHasPendingEvent的值,若为真,则表示还有已读取但未处理的事件,则,将mHasPendingEvent值假,然后给事件重新盖时间戳,再把传进来的数据指针指向mPendingEvent,最后返回传感器时能状态:

if (mHasPendingEvent) {

mHasPendingEvent = false;

mPendingEvent.timestamp = getTimestamp();

*data = mPendingEvent;

return mEnabled ? 1 : 0;

}

下一句调用mInputReader.fill,传入的文件描述符是data_fd,fill会用系统调用read读取这个文件,将内容读入input_event:

116:ssize_t n = mInputReader.fill(data_fd);

下面在一个while循环中,使用mInputReader.readEvent从input_event队列读值,先判断事件类型,

while (count && mInputReader.readEvent(&event)) {

int type = event->type;

125:if (type == EV_ABS) {

if (event->code == EVENT_TYPE_PROXIMITY) {

if (event->value != -1) {

// FIXME: not sure why we're getting -1 sometimes

mPendingEvent.distance = indexToValue(event->value);

}

}

若是坐标绝对值(type==EV_ABS),则通过刚刚提到的indexToValue将值保存在mPendingEvent.distance中;

132:} else if (type == EV_SYN) {

mPendingEvent.timestamp = timevalToNano(event->time);// 给mPendingEvent盖时间戳

if (mEnabled) {

*data++ = mPendingEvent;// 将上层传来取值的指针赋上数据

count--;

numEventReceived++;

}

}

若是报值结束标志(EV_SYN),则认为这次读值完成,给mPendingEvent盖时间戳,将上层传来取值的指针赋上数据,计数器count自减,numEventReceived自加。

143:mInputReader.next();

在while循环的结尾,input_reader指向下一条事件。

146:return numEventReceived;

函数最后返回已读取的事件数目。

三、轮询函数

光线传感器lightSensor比Psensor在HAL层多了一个函数setDelay,它用来向文件/sys/class/input/$input_name/device/poll_delay写入64位的超长整型ns

叁、JNI層(本章从服务端角度出发,与Framework层的界限模糊)

附图一张:

源文件:frameworks/base/services/sensorservice/SensorService.cpp

frameworks/native/libs/gui/           SensorEventQueue.cpp, BitTube.cpp等

//frameworks/base/libs/gui/Sensor.cpp

//frameworks/base/services/sensorservice/SensorDevice.cpp

//frameworks/base/core/jni/android_hardware_SensorManager.cpp 为了叙述连贯性,将这个源文件的内容放在下面一起讲

下面用使能函数举例。这个情景是客户端响应顶层应用的enable lightSensor动作,并且已经调用到了服务端的enableDisnable,现在服务端要把开启/关闭的动作传递给HAL层:

看SensorService.cpp的enableDisable()

简单地调用类SensorService的enable和disable

if (enabled) {

err = mService->enable(this, handle);

} else {

err = mService->disable(this, handle);

}

return err;

先看enable()这个函数

它最终调用的是

432:connection->sendEvents(&event, 1);

这个函数也定义在本源文件,它将调用:

600:ssize_t size = SensorEventQueue::write(mChannel,

reinterpret_cast<ASensorEvent const*>(scratch), count);

这个函数定义在frameworks/native/libs/gui/SensorEventQueue.cpp

ssize_t SensorEventQueue::write(const sp<BitTube>& tube,

ASensorEvent const* events, size_t numEvents) {

return BitTube::sendObjects(tube, events, numEvents);

}

于是BitTube.cpp BitTube::sendObjects()  –>  BitTube::write()

到此,服务端就完成了往管道的写端写入数据。

肆、Framework層(这章从客户端角度出发,与JNI层的界限模糊)

附图一张:

源文件:frameworks/base/core/java/android/hardware/     SensorManager.java(声明native函数,在android_hardware_SensorManager.cpp中实现,实现java与c++交互),SystemSensorManager.cpp等

frameworks/base/core/jni/android_hardware_SensorManager.cpp

frameworks/native/libs/gui/           SensorEventQueue.cpp, BitTube.cpp等

ISensorEventConnection.cpp

下面的情景是应用层发出请求后,客户端怎样将请求传递给服务端。

看SensorManager.java 中的registerListener()函數,注意到源文件里有四個registerListener(),仔細分辨函數原型,應用層調用的是boolean registerListener(SensorEventListener listener, Sensor sensor, int rate)

找到這個函數,只有一句:return registerListener(listener, sensor, rate, null);

繼續trace,

628:        int delay = -1;

switch (rate) {

case SENSOR_DELAY_FASTEST:

delay = 0;

break;

case SENSOR_DELAY_GAME:

delay = 20000;

break;

case SENSOR_DELAY_UI:

delay = 66667;

break;

case SENSOR_DELAY_NORMAL:

delay = 200000;

break;

default:

delay = rate;

break;

}

原來第三個參數是延遲。

646: return registerListenerImpl(listener, sensor, delay, handler);

再trace進,這個函數的定義在同文件夾下的SystemSensorManager.cpp里找到。

該函數首先搜索sListeners這個列表,若列表中有這個listener,再查看這個listener中是否有sensor,沒有的話就加入sensor,然後調用enableSensorLocked對這個sensor使能、設置採樣時間:

349:} else if (!l.hasSensor(sensor)) {

l.addSensor(sensor);

if (!enableSensorLocked(sensor, delay)) {

// oops. there was an error

l.removeSensor(sensor);

result = false;

如果監聽器列表sListeners中沒有這個listener,則新建一個ListenerDelegate,并把它加入sListeners。此時再判斷一次sListeners是否為空,不為空,則調用sSensorThread.startLocked()開啟主線程,調用enableSensorLocked對這個sensor使能、設置採樣時間:

333:if (!sListeners.isEmpty()) {

if (sSensorThread.startLocked()) {

if (!enableSensorLocked(sensor, delay)) {

// oops. there was an error

sListeners.remove(l);

result = false;

}

開啟主線程的方法sSensorThread.startLocked()定義在本源文件,在裏面會去構造一個SensorThreadRunnable并調用thread.start()使之開始運行:

76:SensorThreadRunnable runnable = new SensorThreadRunnable();

Thread thread = new Thread(runnable, SensorThread.class.getName());

thread.start();

再看類SensorThreadRunnable,也定義在本源文件。根據直覺,我覺得當上面運行到thread.start()的時候,會調用SensorThreadRunnable:run()。看run方法,裏面有一個無限循環:

120行:               while (true) {

// wait for an event

final int sensor = sensors_data_poll(sQueue, values, status, timestamp);

。。。。。。

listener.onSensorChangedLocked(sensorObject, values, timestamp, accuracy);

以上死循環第一句,sensors_data_poll函数等待硬件传感器的事件;

这个poll函数在本源文件最后一行有声明static native int sensors_data_poll(int queue, float[] values, int[] status, long[] timestamp);,但没有定义,native关键字说明是本地库中的函数,事实上,这几个native函数全部定义在frameworks/base/core/jni/android_hardware_SensorManager.cpp里

sensors_data_poll函数128行:   res = queue->read(&event, 1)  ->  BitTube::recvObjects(mSensorChannel, events, numEvents)  ->  recvObjects   ->  BitTube::read  ->  recv

到此客户端便完成了向管道读取端读数据

回到类SensorThreadRunnable,死循环的最後一句,調用到監聽者函數listener.onSensorChangedLocked,即應用層的監聽者接口。

現在回到監聽器註冊函數registerListenerImpl,上面提到它在開啟主線程(startLocked)之後會調用enableSensorLocked。

追蹤enableSensorLocked函數,它仍定義在本源文件,它會去遍歷每一個listener,并判斷該監聽器是否含有形參sensor,若有,則調用sensors_enable_sensor:

290行:for (ListenerDelegate i : sListeners) {

if (i.hasSensor(sensor)) {

String name = sensor.getName();

int handle = sensor.getHandle();

result = sensors_enable_sensor(sQueue, name, handle, delay);

break;

}

static native boolean sensors_enable_sensor(int queue, String name, int sensor, int enable)方法与poll方法一样,定位到frameworks/base/core/jni/android_hardware_SensorManager.cpp的sensors_enable_sensor函数

static jboolean sensors_enable_sensor  –>  enableSensor()  ->  SensorEventConnection.enableDisable() 这个函数定义在ISensorEventConnection.cpp (frameworks\native\libs\gui)  ->  Binder: transact()

上面红字部分,我一直找不到类ISensorEventConnection下的enableDisable方法,直到看到这句:

class BpSensorEventConnection : public BpInterface<ISensorEventConnection>

搜了下cpp中这种冒号的用法:类名后的冒号是用来定义类的继承,即:class 派生类名 : 继承方式基类名

原来BpSensorEventConnection是派生类名,它的基类正是SensorEventConnection

最终通过IBindertransact把使能sensor这个请求发送给服务端。

从log中得到验证,上述猜测是错误的,SensorEventQueue.cpp中的enableSensor()调用的ISensorEventConnection->enableDisable,它并不会调用ISensorEventConnection.cpp中的enableDisable方法,而是直接调用sensorService.cpp中的 SensorService::SensorEventConnection::enableDisable 方法。Log如下:

E/Sensors (  452): dyyr-To enable sensor in client, what's the next?

E/SensorService(  452): dyyr-it's here! sensorservice_enableDisable

E/Sensors (  452): activate enable=1 handle=3

即,并不通过binder,直接通过函数调用将这个使能设备的请求传递到服务端。

伍、應用層

phone应用太复杂,所以先分析硬件测试的应用

源文件: Vendor\Cct\Cct_factory_kit\Src\Com\Cct\Factory\PSensor\PSensor.java

一、实现方式

整个源文件实现一个PSensor类,它继承于Activity,重写父类的onCreate、onDestroy、finish等方法。在这个类中定义了一个内部类PSensorListener,它是距离传感器的专属listener,继承于SensorEventListener,并重写父类的onSensorChanged()方法,在这个方法里实现对硬件中断的应用层响应。当距离传感器的硬件中断产生时,SensorService就会自动调用这个子类的onSensorChanged方法。

二、函數

1、onCreate()

在某个时机里sensorService会调用这个onCreate方法。在这里面,会去建立显示psensor报值的窗口,最主要的,会去调用本源文件的getService()方法。然后会进行第一次输出。

2、getService()

这个函数的最终目的是注册那个内部类PSensorListener,在这之前做了很多准备工作:取得SensorManager实例,取得PSensor实例,取得PSensorListener实例。

69: mPSensor = mSensorManager.getDefaultSensor(SENSOR_TYPE);

注意取得proximity sensor是在这里,最顶层,所以在后面的sensorservice中看不到具体的sensor名称(proximity),而不同的sensor在HAL层也提供统一的接口,所以service层可以完全忽略具体的sensor名。

74:mPSensorListener = new PSensorListener(this);

构造一个PSensorListener,这个PSensorListener主要重写父类SensorEventListener的onSensorChanged()方法,即当有报值时,做独特的处理,在这个应用里,是将距离传感器所上报的值通过updateView()写到一个textbox里面。

75 if (!mSensorManager.registerListener(mPSensorListener, mPSensor,

SensorManager.SENSOR_DELAY_FASTEST)) {

关键步骤,创建sensorListener,把刚才得到的sensor注册到sensorManager上。

3、onSensorChanged()

这个是内部类PSensorListener的方法,实现扑捉到中断后的处理方法。

141:updateView(value);

调用updateView()把值显示出来。

143:if (value != INIT_VALUE && pre_value != INIT_VALUE && value < pre_value) {

pass();

}

pre_value = value;

Pass即测试通过。由于距离传感器只报接近和远离两种状态,根据上面的逻辑,若两次报值为1,0,则pass;或三次报值为010,也pass。

转载于:https://www.cnblogs.com/yiru/archive/2013/02/01/2889503.html

Tmd27711 三合一传感器流程相关推荐

  1. 使用libmodbus读传感器流程

    [1.项目描述] 手上有一个温湿度传感器,基于modbus RTU协议,采用RS485串口和Tiny6410通信,把采集到的温湿度显示在Tiny6410的界面程序上.这里简要给出使用libmodbus ...

  2. 隆云通空气温湿,光照三合一传感器

    空气温湿度+光照度传感器外壳采用室外防辐射罩外形设计,结构精致.外型美观,是一款应用范围广泛.性价比高的测量产品. 空气温湿度+光照度传感器特点 (1)高,宽电压设计. (2)数字线性化修正,高精度. ...

  3. RT-Thread 传感器软件包归类

    简介 介绍了目前已经适配了rtthread的sensor框架的软件包,注意:有些传感器是即支持IIC也支持SPI,但是目前适配sensor框架只用了一种通讯接口. 传感器软件包列表 传感器型号 类型 ...

  4. 物联网Lora模块从入门到精通(五)光照与温湿度传感器

    一.前言 在程序开发中,光照与温湿度的获取是十分常见与重要的,本文我们主要是使用M21温湿度光照三合一传感器,其中温湿度数据通过协议获取,而光照通过ADC获取. 二.代码实现 本文内容较为简单,且后续 ...

  5. 在找传感器设计方案或者应用吗?这里这里,全都是!

    传感器(英文名称:换能器/传感器)是一种检测装置,能感受到被测量的信息,并能将感受到的信息,按一定规律变换成为电信号或其他所需形式的信息输出,以满足信息的传输,处理,存储,显示,记录和控制等要求. 今 ...

  6. 4G 模块DTU网关+传感器接入OneNET平台 (MQTT新版)智慧农业解决方案

    4G 模块DTU网关+传感器上传到 OneNET平台 (MQTT新版)智慧农业解决方案 在上一篇<4G模块 DTU网关 ZL-LTE系列 OneNET平台连接教程(MQTT新版)>的基础上 ...

  7. 中国第二大传感器企业宝座易主!14家公司新上市!国产传感器风起云涌!(附最新市值排名榜单)

    ​据工信部中国电子信息产业发展研究院下属的赛迪顾问统计数据显示,2021年全球传感器市场规模达到1710.3亿美元(约合11819亿人民币),过去三年(2019-2022)平均增长率为7.1%,其中, ...

  8. 万物共享的物联网架构

    前言 作为物联网领域最贴近用户的一个分支,智能家居行业在这两年持续火热.但是,除了智能家居外,物联网领域还有很多重要的组成部分:车联物流.智慧医疗.智慧社区.公共基础服务.智慧农业等.由于物联网的第一 ...

  9. 基于机智云的智能花盆2.0

    目录 一.版本更新内容 二.总体设计 2.1整体模型 2.2硬件结构 2.3软件结构 三.硬件设计 3.1硬件资源布局 3.2主控芯片 3.2.1主控选型 3.2.2引脚配置 3.3数据采集部分 3. ...

最新文章

  1. python 模拟浏览器下载文件-python爬虫:使用Selenium模拟浏览器行为
  2. 用eclipse创建WebService Step by Step
  3. 使用bat文件快速解决adb找不到设备的问题
  4. 【.Net Micro Framework PortingKit(补) – 1】USB驱动开发
  5. modelsim的库仿真流程--1
  6. IDEA出现import org.junit.Test飘红解决方案
  7. 迷失在小镇上的日记(16)
  8. 不好好学C++还想做好算法?
  9. php中finally不能用,php-什么时候以及为什么`finally`有用?
  10. php使用redis持久化,redis如何持久化
  11. concurrent模块的使用
  12. php 遍历文件夹并压成zip_php ZipArchive实现多文件打包下载实例
  13. dvm与art的区别_Android运行时– DVM与ART,AOT与JIT
  14. 安川控制器MP3300与C# 上位机通讯
  15. 网络盘的计算机密码是什么,如何让win7映射网络驱动器记住密码
  16. 游戏框架(框架入门篇)
  17. ABAQUS计算不收敛问题,排查方法和解决方案都在这儿了
  18. 微软.Net离线语音识别
  19. 树莓派+aria2+yaaw搭建下载机
  20. 一流的企业做标准,二流的企业做品牌,三流的企业做产品怎么理解

热门文章

  1. 装逼篇 | Python制作抖音超火的九宫格视频
  2. ae master速卖通_全球速卖通核心项目AE PLUS在俄罗斯上线
  3. 零基础10分钟运行DQN图文教程 Playing Flappy Bird Using Deep Reinforcement Learning (Based on Deep Q Learning DQN
  4. Centos 7 生产环境优化 Mysql 5.7
  5. python读取文件指定行的三种方法
  6. Windows 11升级不了?来,我教你!
  7. 【自动化测试】Web自动化测试02
  8. 做素的女人还是香的女人?
  9. 某商务咨询公司IT系统优化实践|案例
  10. 数据结构——树的一些基本概念