g-sensor bmc156数据传输流程(compass部分)
刚入门的小白第一次写博客,望各路大神指点
一、硬件电路
二、设备树
Msm8909.dtsi Master
i2c_1: i2c@78b5000 { /* BLSP1 QUP1 */compatible = "qcom,i2c-msm-v2";#address-cells = <1>;#size-cells = <0>;reg-names = "qup_phys_addr";reg = <0x78b5000 0x1000>;interrupt-names = "qup_irq";interrupts = <0 95 0>;clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,<&clock_gcc clk_gcc_blsp1_qup1_i2c_apps_clk>;clock-names = "iface_clk", "core_clk";qcom,clk-freq-out = <100000>;qcom,clk-freq-in = <19200000>;pinctrl-names = "i2c_active", "i2c_sleep";pinctrl-0 = <&i2c_1_active>;pinctrl-1 = <&i2c_1_sleep>;qcom,noise-rjct-scl = <0>;qcom,noise-rjct-sda = <0>;dmas = <&dma_blsp1 4 64 0x20000020 0x20>,<&dma_blsp1 5 32 0x20000020 0x20>;dma-names = "tx", "rx";qcom,master-id = <86>;};
Msm8909-pinctrl.dtsi
pmx_i2c_1 { /* CLK, DATA */qcom,pins = <&gp 6>, <&gp 7>;qcom,num-grp-pins = <2>;qcom,pin-func = <3>;label = "pmx_i2c_1";i2c_1_active: i2c_1_active {drive-strength = <2>; /* 2 MA */bias-disable = <0>; /* No PULL */};i2c_1_sleep: i2c_1_sleep {drive-strength = <2>; /* 2 MA */bias-pull-down; /* PULL DOWN */};};
Msm8909-Q9-mtp.dtsi clientbosch@10 { /* Accelerometer sensor */compatible = "bosch,bma2x2";reg = <0x10>;vdd-supply = <&pm8909_l17>;vio-supply = <&pm8909_l6>;};bosch@12 { /* Magnetic field sensor */compatible = "bosch,bmm050";reg = <0x12>;vdd-supply = <&pm8909_l17>;vio-supply = <&pm8909_l6>;};
三、 Makefile 文件
确保所有添加的驱动文件都包含在Makefile中,会被编译进去
obj-$(CONFIG_SENSORS_BMA2X2) += bma2x2_driver.o
ccflags-y += -DBMA2X2_SENSOR_IDENTIFICATION_ENABLE
obj-$(CONFIG_SENSORS_BMM050) += bmm050_driver.o bmm050.o
ccflags-y += -DBMM_USE_BASIC_I2C_FUNC -DCONFIG_BMM_USE_PLATFORM_DATA
四、 Config文件
config SENSORS_BMA2X2tristate "BMA255/BMA250E/BMA222E/BMA280 acceleration sensor support"depends on I2ChelpIf you say yes here you get support for Bosch Sensortec'sacceleration sensors BMA255/BMA250E/BMA222E/BMA280.config SENSORS_BMA2X2_ENABLE_INT1tristate "BMA2X2 acceleration sensor interrupt INT1 support"depends on SENSORS_BMA2X2 && !SENSORS_BMA2X2_ENABLE_INT2helpIf you say yes here you get INT1 support for Bosch Sensortecacceleration sensors BMA255/BMA250E/BMA222E/BMA280.Select it will disable interrupt INT2 supportconfig BOSCH_BMA2X2_ENABLE_INT2tristate "BMA2X2 acceleration sensor interrupt INT2 support"depends on SENSORS_BMA2X2 && !SENSORS_BMA2X2_ENABLE_INT1helpIf you say yes here you get INT2 support for Bosch Sensortecacceleration sensors BMA255/BMA250E/BMA222E/BMA280.Can only open if you do NOT open interrupt INT1 supportconfig SIG_MOTIONtristate "support significant motion sensor function"depends on SENSORS_BMA2X2 && ( SENSORS_BMA2X2_ENABLE_INT1 || BOSCH_BMA2X2_ENABLE_INT2)helpSay Y here if you want to support Bosch significant motion sensor functionconfig DOUBLE_TAPtristate "support double tap sensor function"depends on SENSORS_BMA2X2 && ( SENSORS_BMA2X2_ENABLE_INT1 || BOSCH_BMA2X2_ENABLE_INT2)helpSay Y here if you want to support Bosch double tap sensor functionconfig SENSORS_BMM050tristate "BMM050 Magnetic Sensor Driver"depends on I2ChelpBMM050 Magnetic Sensor Driver implemented by Bosch-Sensortec.
五、 数据流程分析(kernel ->framework)
1、kernel部分
BMC156是acceleration和compass的结合,但是我们还是需要将它看作两个单独的sensor,但是软件原理相差不大,所以我们以compass为例进行叙述。(##此处I2C读出数据(无论对错)代表硬件没有问题且初始化成功)
1) Kernel/driver/bmm050.c bmm050_read_mdataXYZ_s32
这里是最初I2C从寄存器中读取xyz值的地方。XYZ的值存储在两个寄存器中(LSB、MSB),所以在读取寄存器中的值之后会进行位运算得出真正的XYZ值
BMM050_RETURN_FUNCTION_TYPE bmm050_read_mdataXYZ_s32(struct bmm050_mdata_s32 *mdata)
{BMM050_RETURN_FUNCTION_TYPE comres;unsigned char a_data_u8r[8] = "";struct {BMM050_S16 raw_dataX;BMM050_S16 raw_dataY;BMM050_S16 raw_dataZ;BMM050_U16 raw_dataR;} raw_dataXYZ;if (p_bmm050 == BMM050_NULL) {comres = E_BMM050_NULL_PTR;} else {comres = p_bmm050->BMM050_BUS_READ_FUNC(p_bmm050->dev_addr,BMM050_DATAX_LSB, a_data_u8r, 8);/* Reading data for X axis */a_data_u8r[0] = BMM050_GET_BITSLICE(a_data_u8r[0],BMM050_DATAX_LSB_VALUEX);raw_dataXYZ.raw_dataX = (BMM050_S16)((((BMM050_S16)((signed char)a_data_u8r[1])) <a_data_u8r[0]);/* Reading data for Y axis */a_data_u8r[2] = BMM050_GET_BITSLICE(a_data_u8r[2],BMM050_DATAY_LSB_VALUEY);raw_dataXYZ.raw_dataY = (BMM050_S16)((((BMM050_S16)((signed char)a_data_u8r[3])) <a_data_u8r[2]);/* Reading data for Z axis */a_data_u8r[4] = BMM050_GET_BITSLICE(a_data_u8r[4],BMM050_DATAZ_LSB_VALUEZ);raw_dataXYZ.raw_dataZ = (BMM050_S16)((((BMM050_S16)((signed char)a_data_u8r[5])) <a_data_u8r[4]);
PINFO("x = %d , y = %d ,z = %d \n",raw_dataXYZ.raw_dataX,raw_dataXYZ.raw_dataY,raw_dataXYZ.raw_dataZ); // 1、此处是寄存器中的xyz值/* Reading data for Resistance*/if (!comres)mdata->drdy = BMM050_GET_BITSLICE(a_data_u8r[6],BMM050_DATA_RDYSTAT);a_data_u8r[6] = BMM050_GET_BITSLICE(a_data_u8r[6],BMM050_R_LSB_VALUE);raw_dataXYZ.raw_dataR = (BMM050_U16)((((BMM050_U16)a_data_u8r[7]) <a_data_u8r[6]);/* Compensation for X axis */mdata->datax = bmm050_compensate_X_s32(raw_dataXYZ.raw_dataX,raw_dataXYZ.raw_dataR);/* Compensation for Y axis */mdata->datay = bmm050_compensate_Y_s32(raw_dataXYZ.raw_dataY,raw_dataXYZ.raw_dataR);/* Compensation for Z axis */mdata->dataz = bmm050_compensate_Z_s32(raw_dataXYZ.raw_dataZ,raw_dataXYZ.raw_dataR);// 2、在观察到磁力值(x/y/z)正负不对称的时候,可以用excel将下方的log打出的xyz值制成散点图(x-y,x-z),观察图中偏差值,以校正xyz,保证圆心在圆点(文档最后会介绍)/* Output raw resistance value */mdata->resistance = raw_dataXYZ.raw_dataR;PINFO("x = %d , y = %d ,z = %d \n",mdata->datax,mdata->datay,mdata->dataz);// 3、此处读出的是对寄存器中的值进行校正、补偿之后的值}return comres;
}
2) Kernel/driver/bmm050_driver.c bmm_work_func
compass kernel对hardware的接口
static void bmm_work_func(struct work_struct *work)
{struct bmm_client_data *client_data =container_of((struct delayed_work *)work,struct bmm_client_data, work);struct i2c_client *client = client_data->client;unsigned long delay =msecs_to_jiffies(atomic_read(&client_data->delay));mutex_lock(&client_data->mutex_value);//使用FORCED_MODE,因为相较于NORMAL_MODE准确度更高,但一般使用NORMAL_MODE就够了mutex_lock(&client_data->mutex_op_mode);if (BMM_VAL_NAME(NORMAL_MODE) != client_data->op_mode)bmm_set_forced_mode(client);mutex_unlock(&client_data->mutex_op_mode);//这里调用了上文中的函数,读取硬件数据BMM_CALL_API(read_mdataXYZ_s32)(&client_data->value);bmm_remap_sensor_data(&client_data->value, client_data);//input_report_abs将kernel读到的数据都传输到hardware中input_report_abs(client_data->input, ABS_X, client_data->value.datax);input_report_abs(client_data->input, ABS_Y, client_data->value.datay);input_report_abs(client_data->input, ABS_Z, client_data->value.dataz);mutex_unlock(&client_data->mutex_value);/*printk("=============x=%d y=%d z=%d\n",client_data->value.datax,client_data->value.datay, client_data->value.dataz);*/input_sync(client_data->input);schedule_delayed_work(&client_data->work, delay);
}
2、 hardware 部分
1)hardware/sensors/CompassSensor.cpp CompassSensor::readEvents
这个文件是比较重要的,可以根据标志点调整xyz的方向(在kernel完成这部分调整比较好,层次清楚)。readEvents函数的作用是读取kernel上报的数据,并经过计算传递到上层。
打LOG:在打出时会发现data的log值会出现其他Sensor的值,例如Acceleration,这是正常的,因为几个Sensor是个union共用一个存储空间,这个可以看定义。data打出Compass的数据时会发现与result的值相同而可能与mPendingEvent有差异,这就需要看一下,这个差异是不是必要的。
int CompassSensor::readEvents(sensors_event_t* data, int count)
{if (count < 1)return -EINVAL;if (mHasPendingEvent) {mHasPendingEvent = false;mPendingEvent.timestamp = getTimestamp();*data = mPendingEvent;return mEnabled ? 1 : 0;}if (mHasPendingMetadata) {mHasPendingMetadata--;meta_data.timestamp = getTimestamp();*data = meta_data;return mEnabled ? 1 : 0;}ssize_t n = mInputReader.fill(data_fd);if (n < 0)return n;int numEventReceived = 0;input_event const* event;sensors_event_t raw, result;#if FETCH_FULL_EVENT_BEFORE_RETURN
again:
#endifwhile (count && mInputReader.readEvent(&event)) {int type = event->type;//Type == EV_ABS会循环3次,旨在将底层值赋给mPendingEvent.magnetic的x y z,在填充满之后type就会变成EV_SYNif (type == EV_ABS) {float value = event->value;if (event->code == EVENT_TYPE_MAG_X) {mPendingEvent.magnetic.x = value * res;// 此处的res一般为CONVERT_MAG ( 1/16 )} else if (event->code == EVENT_TYPE_MAG_Y) {mPendingEvent.magnetic.y = value * res;} else if (event->code == EVENT_TYPE_MAG_Z) {mPendingEvent.magnetic.z = value * res;}} else if (type == EV_SYN) {switch (event->code) {case SYN_TIME_SEC:mUseAbsTimeStamp = true;report_time = event->value*1000000000LL;break;case SYN_TIME_NSEC:mUseAbsTimeStamp = true;mPendingEvent.timestamp = report_time+event->value;break;
//在SYN_REPORT中raw接了mPendingEvent的值,之后经过计算并赋给了result,最后传递给了datacase SYN_REPORT:if (mUseAbsTimeStamp != true) {mPendingEvent.timestamp = timevalToNano(event->time);}if (mEnabled) {raw = mPendingEvent;if (algo != NULL) {if (algo->methods->convert(&raw, &result, NULL)) {ALOGE("Calibration failed.");result.magnetic.x = CALIBRATE_ERROR_MAGIC;result.magnetic.y = CALIBRATE_ERROR_MAGIC;result.magnetic.z = CALIBRATE_ERROR_MAGIC;result.magnetic.status = 0;}} else {result = raw;}
//这里可添加ALOGE (”Compass x = %f , y = %f , z = %f celine\n ”, result.magnetic.x, result.magnetic.y, result.magnetic.z);*data = result;data->version = sizeof(sensors_event_t);data->sensor = mPendingEvent.sensor;data->type = SENSOR_TYPE_MAGNETIC_FIELD;data->timestamp = mPendingEvent.timestamp;/* The raw data is stored inside sensors_event_t.data after* sensors_event_t.magnetic. Notice that the raw data is* required to composite the virtual sensor uncalibrated* magnetic field sensor.** data[0~2]: calibrated magnetic field data.* data[3]: magnetic field data accuracy.* data[4~6]: uncalibrated magnetic field data.*/data->data[4] = mPendingEvent.data[0];data->data[5] = mPendingEvent.data[1];data->data[6] = mPendingEvent.data[2];data++;numEventReceived++;count--;}break;}} else {ALOGE("CompassSensor: unknown event (type=%d, code=%d)",type, event->code);}ALOGE("Compass x = %f , y = %f , z = %f celine\n",mPendingEvent.magnetic.x,mPendingEvent.magnetic.y,mPendingEvent.magnetic.z);ALOGE (”Compass x = %f , y = %f , z = %f celine\n ”, data.magnetic.x, data.magnetic.y, data.magnetic.z);mInputReader.next();}#if FETCH_FULL_EVENT_BEFORE_RETURN/* if we didn't read a complete event, see if we can fill andtry again instead of returning with nothing and redoing poll. */if (numEventReceived == 0 && mEnabled == 1) {n = mInputReader.fill(data_fd);if (n)goto again;}
#endifreturn numEventReceived;
}
(3) hardware/sensors/NativeSensorManager.cpp NativeSensorManager::readEvents
相当于将所有Sensor的readEvents或injectEvents做了一个汇总(例如CompassSensor::readEvents和AccelSensor::readEvents),在这可以对所有Sensor的数据进行遍历,但一次只能一个Sensor
int NativeSensorManager::readEvents(int handle, sensors_event_t* data, int count)
{const SensorContext *list;int i, j;int number = getSensorCount();int nb;struct listnode *node;struct SensorRefMap *item;list = getInfoByHandle(handle);//根据handle确定Sensor,得到Sensor的listif (list == NULL) {ALOGE("Invalid handle(%d)", handle);return -EINVAL;}do {nb = list->driver->readEvents(data, count); //调用每个Sensor的readevents,获取数据存在data中} while ((nb == -EAGAIN) || (nb == -EINTR));for (j = 0; j < nb; j++) {list_for_each(node, &list->listener) {item = node_to_item(node, struct SensorRefMap, list);if (item->ctx->enable && (item->ctx != list)) {item->ctx->driver->injectEvents(&data[j], 1); //方向传感器之类的Sensor}}}if (list->enable)return nb;/* No need to report the events if the sensor is not enabled */return 0;
}
(4)harware/sensors/sensors.cpp Sensors_poll_context_t::pollEvents
int sensors_poll_context_t::pollEvents(sensors_event_t* data, int count)
{int nbEvents = 0;int n = 0;NativeSensorManager& sm(NativeSensorManager::getInstance());const sensor_t *slist;int number = sm.getSensorList(&slist);do {// 这是在根据Sensor设备的个数进行循环,相当于这个函数是在不停的对所有的Sensor进行轮询 poll()for (int i = 0 ; count && i < number ; i++) {if ((mPollFds[i].revents & POLLIN) || (sm.hasPendingEvents(slist[i].handle))) {Mutex::Autolock _l(mLock);int nb = sm.readEvents(slist[i].handle, data, count);//ALOGE("x : %f , y = %f , z =%f celine compass\n",data->magnetic.x,data->magnetic.y,data->magnetic.z);//ALOGE("azimuth : %f , pitch = %f , roll =%f celine compass\n",data->orientation.azimuth,data->orientation.pitch,data->orientation.roll);//调用到NativeSensorManager的readevents,读取Sensor数据,下面的两个log打出的x y z值会发现是一样的,所以导致每次上报的值只能根据handle来确定,无法像结构体一样读取固定的存储地址。if (nb < 0) {ALOGE("readEvents failed.(%d)", errno);return nb;}if (nb <= count) {// no more data for this sensormPollFds[i].revents = 0;}count -= nb;nbEvents += nb;data += nb;}}if (count) {// we still have some room, so try to see if we can get// some events immediately or just wait if we don't have// anything to returndo {n = poll(mPollFds, number + 1, nbEvents ? 0 : -1);} while (n < 0 && errno == EINTR);if (n<0) {ALOGE("poll() failed (%s)", strerror(errno));return -errno;}if (mPollFds[number].revents & POLLIN) {char msg;int result = read(mPollFds[number].fd, &msg, 1);ALOGE_IF(result<0, "error reading from wake pipe (%s)", strerror(errno));ALOGE_IF(msg != WAKE_MESSAGE, "unknown message on wake queue (0x%02x)", int(msg));mPollFds[number].revents = 0;}}// if we have events and space, go read them} while (n && count);return nbEvents;
}
(5)harware/sensors/sensors.cpp poll_poll
static int poll__poll(struct sensors_poll_device_t *dev,sensors_event_t* data, int count) {sensors_poll_context_t *ctx = (sensors_poll_context_t *)dev;return ctx->pollEvents(data, count); //**
}
(6)harware/sensors/sensors.cpp open_sensors
static int open_sensors(const struct hw_module_t* module, const char*,struct hw_device_t** device)
{int status = -EINVAL;sensors_poll_context_t *dev = new sensors_poll_context_t();NativeSensorManager& sm(NativeSensorManager::getInstance());memset(&dev->device, 0, sizeof(sensors_poll_device_1_ext_t));dev->device.common.tag = HARDWARE_DEVICE_TAG;
#if defined(SENSORS_DEVICE_API_VERSION_1_3)ALOGI("Sensors device API version 1.3 supported\n");dev->device.common.version = SENSORS_DEVICE_API_VERSION_1_3;
#elsedev->device.common.version = SENSORS_DEVICE_API_VERSION_0_1;
#endifdev->device.common.module = const_cast(module);dev->device.common.close = poll__close;dev->device.activate = poll__activate;dev->device.setDelay = poll__setDelay;dev->device.poll = poll__poll; //**dev->device.calibrate = poll_calibrate;
#if defined(SENSORS_DEVICE_API_VERSION_1_3)dev->device.batch = poll__batch;dev->device.flush = poll__flush;
#endif*device = &dev->device.common;status = 0;return status;
}
(7)harware/sensors/sensors.cpp
static struct hw_module_methods_t sensors_module_methods = {open: open_sensors //**
};
这就是hardware对framework的接口HAL_MOUDLE_SYM,framework层根据id来选择模块
struct sensors_module_t HAL_MODULE_INFO_SYM = {common: {tag: HARDWARE_MODULE_TAG,version_major: 1,version_minor: 0,id: SENSORS_HARDWARE_MODULE_ID,name: "Quic Sensor module",author: "Quic",methods: &sensors_module_methods, //**dso: NULL,reserved: {0},},get_sensors_list: sensors__get_sensors_list,
};
3、 framework 部分
(1)framework/native/services/sensorServices/SensorDevices.cpp SensorDevices::SensorDevice()
SensorDevice::SensorDevice(): mSensorDevice(0),mSensorModule(0)
{status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,(hw_module_t const**)&mSensorModule); //获取sensor模块ALOGE_IF(err, "couldn't load %s module (%s)",SENSORS_HARDWARE_MODULE_ID, strerror(-err));if (mSensorModule) {err = sensors_open_1(&mSensorModule->common, &mSensorDevice); //打开sensor设备ALOGE_IF(err, "couldn't open device for module %s (%s)",SENSORS_HARDWARE_MODULE_ID, strerror(-err));if (mSensorDevice) {if (mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_1 ||mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_2) {ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3");}sensor_t const* list;ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list); //获取sensor 列表 mActivationCount.setCapacity(count);Info model;//Sensor<->handle<->info model,都是一一对应的关系,所以SensorDevices去读hal层的数据后根据handle放在对应的info中以便上层使用,在这里的时候每个Sensor才有独立的存储空间存放自己的数据,好处:类似acc、compass这类的传感器会产生大量的数据导致占用大量的存储空间,但可能我们并不需要那么频繁的刷新,那么高的精确度,这样就会造成资源的浪费for (size_t i=0 ; ilist[i].handle, model);mSensorDevice->activate(reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),list[i].handle, 0);}}}
}
1.通过SENSORS_HARDWARE_MODULE_ID获取Sensor模块,&mSensorModule
2.将该模块下的sensor设备公共部分打开,&mSensorDevice
3.获取该模块下的sensor list,&list(所有sensor),并记录下了sensor的个数count
4.for循环中,handle是用来识别sensor的,每一个sensor都会有一个独一无二的handle,这样用handle来标识 Info Model,相当于,每个sensor都有一个独立的Info,hal层传上来的数据就是放在这
5.对sensorDevice,根据handle逐个激活
六、 调试方法
1.Excel画图
上文中提到,在需要校准acceleration或者compass的数值时,我们可以使用excel制作散点图来观察数据是不是在以原点为圆心的圆上均匀分布的。校准x y z三个值的时候需要画两个散点图校准。
x – y
1) 在需要校准的地方(一般为kernel)加上打出 x y z的log,需有特殊词(如:picture)以便挑出log,在数据前后留出较大空格,以便之后导入数据( 如 x = %d , )。
2) 手机以x y 轴组成的面为平面旋转1~3周,确保在每个角度都打出了值,这一步可能需要重复多次(确认数据完整方法,观察数据例如从北顺时针转动手机时 x 值变化顺序是否为 正最大 –> 0 –> 负增大 -> 负减小 0 ->正增大)
3) 将以特殊词选出的log保存为txt格式,以便之后使用
4) 打开excel –> 数据 ->自文本 -> 选择 之前的txt文本 -> 按提示导入文本 ->选中列 x y –> 插入 ->散点图 down
5) 最终结果
2.打log
Kernel :#define DEBUG 打开PINFO
PINFO定义:#define PINFO( fmt , args…)
Printk (KERN_INFO “ \n ” “ [ I ] ” KERN_INFO MODULE_TAG “ < %s > < %d >” fmt “ \n ”,func , LINE ,##args) ;
< %s >:函数名
< %d >:行号
“##”的作用:如果可变参数部分(args …)被忽略或为空 , 那么 “##” 操作会使预处理器去掉它前面的逗号,使宏结束展开(补充完右边的括号)若在调用宏的时候确实提供了一些可变参数,GNU C 也会正常工作,它会把这些可变参数放在逗号后。
adb pull data/logger/kernel.log kernel.log
hardware : ALOGE
adb shell logcat –v time –b system –b event –b main –b radio > logcat_xxx.txt
kernel、hardware….都是单独编译,那么每个模块的log的定义必然都在该模块下,其中有些宏开关不是在代码中出现,而是在Makefile文件中,用来控制大量代码(可以编译出不同的版本),视情况改 (例如ALOGE)
3.调试顺序
1) 初始化是否正确,底层kernel是否可以读到数据,是否向hal层上报数据
2) Hal层是否可以从kernel读到数据(一致性),hal的数据是否上报给了framework
3) Framework是否接收到hal的数据(一致性)
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
