app注册传感器监听

Android Sensor Framework 的整体架构如下图所示:

前几篇sensor相关的文章介绍了sensor的hal的知识,以press_sensor实时显示气压坐标来分析,app层数据获取的过程,其实实现数据监控非常简单,主要分为下面三个步骤:

  • 获取Sensor服务:getSystemService;
  • 获取具体Sensor对象:getDefaultSensor;
  • 注册数据监听器:registerListener;

SensorService启动

开机后,system server启动时,就会初始化sensor service,也就是说,开机后她一直都在后台运行着,客户端部分,直接connect就行了。至于怎么connect,这一切都被封装到SensorManager里了。

SensorService服务启动后,在随后的第一次被强引用时,其onFirstRef会被调用,紧接着,它会获取我们的SensorDevice实例:

  1. void SensorService::onFirstRef() {
  2. ALOGD("nuSensorService starting...");
  3. SensorDevice& dev(SensorDevice::getInstance());
  4. sHmacGlobalKeyIsValid = initializeHmacKey();
  5. if (dev.initCheck() == NO_ERROR) {
  6. sensor_t const* list;
  7. ssize_t count = dev.getSensorList(&list);
  8. if (count > 0) {

附上这部分的流程

SensorDevice作为Sensor架构中native的最后一个文件,与Hal层进行通信,故而在SensorDevice的构造方法中,我们就可以看到著名的hw_get_module和sensors_open_1方法了:

  1. SensorDevice::SensorDevice()
  2. : mSensorDevice(0),
  3. mSensorModule(0) {
  4. status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
  5. (hw_module_t const**)&mSensorModule);
  6. ALOGE_IF(err, "couldn't load %s module (%s)",
  7. SENSORS_HARDWARE_MODULE_ID, strerror(-err));
  8. if (mSensorModule) {
  9. err = sensors_open_1(&mSensorModule->common, &mSensorDevice);
  10. ALOGE_IF(err, "couldn't open device for module %s (%s)",
  11. SENSORS_HARDWARE_MODULE_ID, strerror(-err));
  12. if (mSensorDevice) {
  13. if (mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_1 ||
  14. mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_2) {
  15. ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3");
  16. }
  17. sensor_t const* list;
  18. ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);
  19. mActivationCount.setCapacity(count);
  20. Info model;
  21. for (size_t i=0 ; i<size_t(count) ; i++) {
  22. mActivationCount.add(list[i].handle, model);
  23. mSensorDevice->activate(
  24. reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
  25. list[i].handle, 0);
  26. }
  27. }
  28. }
  29. }

其中SENSORS_HARDWARE_MODULE_ID是在hardware/sensors.h中定义的module名字:

  1. /**
  2. * The id of this module
  3. */
  4. #define SENSORS_HARDWARE_MODULE_ID "sensors"

而mSensorModule就是我们的sensors_module_t结构体,这些都是在hal层sensors.h中定义的:

  1. /**
  2. * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
  3. * and the fields of this data structure must begin with hw_module_t
  4. * followed by module specific information.
  5. */
  6. struct sensors_module_t {
  7. struct hw_module_t common;
  8. /**
  9. * Enumerate all available sensors. The list is returned in "list".
  10. * @return number of sensors in the list
  11. */
  12. int (*get_sensors_list)(struct sensors_module_t* module,
  13. struct sensor_t const** list);
  14. /**
  15. * Place the module in a specific mode. The following modes are defined
  16. *
  17. * 0 - Normal operation. Default state of the module.
  18. * 1 - Loopback mode. Data is injected for the supported
  19. * sensors by the sensor service in this mode.
  20. * @return 0 on success
  21. * -EINVAL if requested mode is not supported
  22. * -EPERM if operation is not allowed
  23. */
  24. int (*set_operation_mode)(unsigned int mode);
  25. };

可以看到sensors_module_t结构体扩展了hw_module_t,它里面额外提供了get_sensor_list方法来获取系统支持的sensor列表以及一个模式设置方法。

接下来,我们跟进hw_get_module方法,看看它到底做了什么?

hw_get_module

该函数具体实现在hardware/libhardware/hardware.c中

  1. int hw_get_module(const char *id, const struct hw_module_t **module)
  2. {
  3. return hw_get_module_by_class(id, NULL, module);
  4. }
  1. int hw_get_module_by_class(const char *class_id, const char *inst,
  2. const struct hw_module_t **module)
  3. {
  4. int i = 0;
  5. char prop[PATH_MAX] = {0};
  6. char path[PATH_MAX] = {0};
  7. char name[PATH_MAX] = {0};
  8. char prop_name[PATH_MAX] = {0};
  9. if (inst)
  10. snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
  11. else
  12. strlcpy(name, class_id, PATH_MAX);
  13. /*
  14. * Here we rely on the fact that calling dlopen multiple times on
  15. * the same .so will simply increment a refcount (and not load
  16. * a new copy of the library).
  17. * We also assume that dlopen() is thread-safe.
  18. */
  19. /* First try a property specific to the class and possibly instance */
  20. snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
  21. if (property_get(prop_name, prop, NULL) > 0) {
  22. if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
  23. goto found;
  24. }
  25. }
  26. /* Loop through the configuration variants looking for a module */
  27. for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
  28. if (property_get(variant_keys[i], prop, NULL) == 0) {
  29. continue;
  30. }
  31. if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
  32. goto found;
  33. }
  34. }
  35. /* Nothing found, try the default */
  36. if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
  37. goto found;
  38. }
  39. return -ENOENT;
  40. found:
  41. /* load the module, if this fails, we're doomed, and we should not try
  42. * to load a different variant. */
  43. return load(class_id, path, module);
  44. }

我们主要看hw_get_module_by_class,这里传入的参数分别是“sensors”,null,以及我们的mSensorModule结构体。

首先将字符串拷贝给name:

  1. strlcpy(name, class_id, PATH_MAX);

接着拼接prop_name为ro.hardware.name,即prop_name=ro.hardware.sensors

通过property_get方法并没有得到这个值的定义(因为在系统中并没有对其定义),所以接下来会进入下面的循环:

  1. for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
  2. if (property_get(variant_keys[i], prop, NULL) == 0) {
  3. continue;
  4. }
  5. if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
  6. goto found;
  7. }
  8. }
  1. /**
  2. * There are a set of variant filename for modules. The form of the filename
  3. * is "<MODULE_ID>.variant.so" so for the led module the Dream variants
  4. * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
  5. *
  6. * led.trout.so
  7. * led.msm7k.so
  8. * led.ARMV6.so
  9. * led.default.so
  10. */
  11. static const char *variant_keys[] = {
  12. "ro.hardware", /* This goes first so that it can pick up a different
  13. file on the emulator. */
  14. "ro.product.board",
  15. "ro.board.platform",
  16. "ro.arch"
  17. };

根据上面的解析我门也可以看到,将会分别查找sensors.variant.sosensors.product.sosensors.platform.so,以及sensors.default.so,最终我们会在/system/lib/hw/路径下找到sensors.msm8909.so,然后将其通过load方法加载进内存中运行。由此也可知,我分析的是高通8909平台。

小细节:当我们实现了自己的HAL层module,并且写了一个应用程序测试module是否正常工作,那么在编译的时候,下面的参数应该要这样写:

  1. LOCAL_MODULE := moduleName.default

或者

  1. LOCAL_MODULE := moduleName.$(TARGET_BOARD_PLATFORM)

由于上面源码的原因,如果module名字对应不到,你的这个模块将不会被正常的load进去,因而也就无法正常工作了。

接着我们分析load的实现。

  1. static int load(const char *id,
  2. const char *path,
  3. const struct hw_module_t **pHmi)
  4. {
  5. int status = -EINVAL;
  6. void *handle = NULL;
  7. struct hw_module_t *hmi = NULL;
  8. /*
  9. * load the symbols resolving undefined symbols before
  10. * dlopen returns. Since RTLD_GLOBAL is not or'd in with
  11. * RTLD_NOW the external symbols will not be global
  12. */
  13. handle = dlopen(path, RTLD_NOW);
  14. if (handle == NULL) {
  15. char const *err_str = dlerror();
  16. ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
  17. status = -EINVAL;
  18. goto done;
  19. }
  20. /* Get the address of the struct hal_module_info. */
  21. const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
  22. hmi = (struct hw_module_t *)dlsym(handle, sym);
  23. if (hmi == NULL) {
  24. ALOGE("load: couldn't find symbol %s", sym);
  25. status = -EINVAL;
  26. goto done;
  27. }
  28. /* Check that the id matches */
  29. if (strcmp(id, hmi->id) != 0) {
  30. ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
  31. status = -EINVAL;
  32. goto done;
  33. }
  34. hmi->dso = handle;
  35. /* success */
  36. status = 0;
  37. done:
  38. if (status != 0) {
  39. hmi = NULL;
  40. if (handle != NULL) {
  41. dlclose(handle);
  42. handle = NULL;
  43. }
  44. } else {
  45. ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
  46. id, path, *pHmi, handle);
  47. }
  48. *pHmi = hmi;
  49. return status;
  50. }
  1. 首先通过dlopen打开sensors.xxx.so模块,获得其句柄handle
  2. 调用dlsym去获取结构体hw_module_t结构体的地址,注意这里传入的字符串为HAL_MODULE_INFO_SYM_AS_STR,定义在hardware.h头文件中
  1. /**
  2. * Name of the hal_module_info
  3. */
  4. #define HAL_MODULE_INFO_SYM HMI
  5. /**
  6. * Name of the hal_module_info as a string
  7. */
  8. #define HAL_MODULE_INFO_SYM_AS_STR "HMI"

这里为什么要去取名字为HMI的地址,我猜想它应该是HAL模块的入口了。

ELF文件格式:

ELF = Executable and Linkable Format,可执行连接格式,是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的,扩展名为elf。一个ELF头在文件的开始,保存了路线图(road map),描述了该文件的组织情况。sections保存着object 文件的信息,从连接角度看:包括指令,数据,符号表,重定位信息等等。通过file命令我们可知sensors.xx.so是一个ELF文件格式

  1. tiny.hui@build-server:~$ file sensors.msm8909.so
  2. sensors.msm8909.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), BuildID[md5/uuid]=0x25812b01ab4700281b41f61327075611, not stripped

因此,通过linux的readelf命令我们可以查看该文件的内部布局及符号表等信息。

  1. tiny.hui@build-server:~$ readelf -s sensors.msm8909.so
  2. Symbol table '.dynsym' contains 157 entries:
  3. Num: Value Size Type Bind Vis Ndx Name
  4. 0: 00000000 0 NOTYPE LOCAL DEFAULT UND
  5. 1: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize@LIBC (2)
  6. 2: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@LIBC (2)
  7. 3: 00000000 0 FUNC GLOBAL DEFAULT UND __register_atfork@LIBC (2)
  8. 4: 00000000 0 FUNC GLOBAL DEFAULT UND pthread_mutex_lock@LIBC (2)
  9.         …………………………// 省略无关信息
  10. 179: 0000c179 120 FUNC GLOBAL DEFAULT 13 _ZN19NativeSensorManager1
  11. 180: 0000bd21 392 FUNC GLOBAL DEFAULT 13 _ZN19NativeSensorManager2
  12. 181: 0000a45b 114 FUNC GLOBAL DEFAULT 13 _ZN24InputEventCircularRe
  13. 182: 000064d9 148 FUNC GLOBAL DEFAULT 13 _ZN22sensors_poll_context
  14. 183: 0000d889 6 FUNC GLOBAL DEFAULT 13 _ZN11sensors_XMLC1Ev
  15. 184: 0000663d 156 FUNC GLOBAL DEFAULT 13 _ZN10SensorBaseC2EPKcS1_P
  16. 185: 000086d5 248 FUNC GLOBAL DEFAULT 13 _ZN11AccelSensorC1Ev
  17. 186: 000088dd 248 FUNC GLOBAL DEFAULT 13 _ZN11AccelSensorC2EP13Sen
  18. 187: 00014220 4 OBJECT GLOBAL DEFAULT 23 _ZN7android9SingletonI11s
  19. 188: 0000a53b 46 FUNC GLOBAL DEFAULT 13 _ZN18CalibrationManager10
  20. 189: 00007775 56 FUNC GLOBAL DEFAULT 13 _ZN15ProximitySensorD1Ev
  21. 190: 00014008 136 OBJECT GLOBAL DEFAULT 22 HMI
  22. 191: 0000721d 26 FUNC GLOBAL DEFAULT 13 _ZNK11AccelSensor16hasPen
  23. 192: 0000d475 16 FUNC WEAK DEFAULT 13 _ZNK7android12SortedVecto
  24. 193: 00006dd9 264 FUNC GLOBAL DEFAULT 13 _ZN11LightSensorC2EPc
  25. 194: 00006181 48 FUNC GLOBAL DEFAULT 13 _ZN22sensors_poll_context
  26. 195: 0000d4fd 48 FUNC GLOBAL DEFAULT 13 _ZN13VirtualSensorD1Ev
  27. 196: 0000aa15 80 FUNC GLOBAL DEFAULT 13 _ZN18CalibrationManagerD2
  28. 197: 000087cd 272 FUNC GLOBAL DEFAULT 13 _ZN11AccelSensorC1EPc

由符号表可知,HMI的地址为00014008,拿到函数地址,当然就可以执行对应的代码了。

QualComm Sensor HAL

因此我们接着看sensor_hal层,高通的Sensor实现了自己的HAL,其源码在hardware\qcom\sensors路径下,通过Android.mk我们也可以确定他确实是我们前面load方法打开的动态链接库,其编译后会生成sensor.msm8909.so

  1. ifneq ($(filter msm8960 msm8610 msm8916 msm8909,$(TARGET_BOARD_PLATFORM)),)
  2. # Exclude SSC targets
  3. ifneq ($(TARGET_USES_SSC),true)
  4. # Disable temporarily for compilling error
  5. ifneq ($(BUILD_TINY_ANDROID),true)
  6. LOCAL_PATH := $(call my-dir)
  7. # HAL module implemenation stored in
  8. include $(CLEAR_VARS)
  9. ifeq ($(USE_SENSOR_MULTI_HAL),true)
  10. LOCAL_MODULE := sensors.native
  11. else
  12. ifneq ($(filter msm8610,$(TARGET_BOARD_PLATFORM)),)
  13. LOCAL_MODULE := sensors.$(TARGET_BOARD_PLATFORM)
  14. LOCAL_CFLAGS := -DTARGET_8610
  15. else
  16. ifneq ($(filter msm8916 msm8909,$(TARGET_BOARD_PLATFORM)),)
  17. LOCAL_MODULE := sensors.$(TARGET_BOARD_PLATFORM)
  18. else
  19. LOCAL_MODULE := sensors.msm8960
  20. endif
  21. endif
  22. ifdef TARGET_2ND_ARCH
  23. LOCAL_MODULE_RELATIVE_PATH := hw
  24. else
  25. LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
  26. endif
  27. endif
  28. LOCAL_MODULE_TAGS := optional
  29. LOCAL_CFLAGS += -DLOG_TAG=\"Sensors\"
  30. ifeq ($(call is-board-platform,msm8960),true)
  31. LOCAL_CFLAGS += -DTARGET_8930
  32. endif
  33. LOCAL_C_INCLUDES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
  34. LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
  35. # Export calibration library needed dependency headers
  36. LOCAL_COPY_HEADERS_TO := sensors/inc
  37. LOCAL_COPY_HEADERS := \
  38. CalibrationModule.h \
  39. sensors_extension.h \
  40. sensors.h
  41. LOCAL_SRC_FILES := \
  42. sensors.cpp \
  43. SensorBase.cpp \
  44. LightSensor.cpp \
  45. ProximitySensor.cpp \
  46. CompassSensor.cpp \
  47. Accelerometer.cpp \
  48. Gyroscope.cpp \
  49. Bmp180.cpp \
  50. InputEventReader.cpp \
  51. CalibrationManager.cpp \
  52. NativeSensorManager.cpp \
  53. VirtualSensor.cpp \
  54. sensors_XML.cpp \
  55. SignificantMotion.cpp
  56. LOCAL_C_INCLUDES += external/libxml2/include \
  57. ifeq ($(call is-platform-sdk-version-at-least,20),true)
  58. LOCAL_C_INCLUDES += external/icu/icu4c/source/common
  59. else
  60. LOCAL_C_INCLUDES += external/icu4c/common
  61. endif
  62. LOCAL_SHARED_LIBRARIES := liblog libcutils libdl libxml2 libutils
  63. include $(BUILD_SHARED_LIBRARY)
  64. include $(CLEAR_VARS)
  65. LOCAL_MODULE := libcalmodule_common
  66. LOCAL_SRC_FILES := \
  67. algo/common/common_wrapper.c \
  68. algo/common/compass/AKFS_AOC.c \
  69. algo/common/compass/AKFS_Device.c \
  70. algo/common/compass/AKFS_Direction.c \
  71. algo/common/compass/AKFS_VNorm.c
  72. LOCAL_SHARED_LIBRARIES := liblog libcutils
  73. LOCAL_MODULE_TAGS := optional
  74. ifdef TARGET_2ND_ARCH
  75. LOCAL_MODULE_PATH_32 := $(TARGET_OUT_VENDOR)/lib
  76. LOCAL_MODULE_PATH_64 := $(TARGET_OUT_VENDOR)/lib64
  77. else
  78. LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)
  79. endif
  80. include $(BUILD_SHARED_LIBRARY)
  81. include $(CLEAR_VARS)
  82. LOCAL_MODULE := calmodule.cfg
  83. LOCAL_MODULE_TAGS := optional
  84. LOCAL_MODULE_CLASS := ETC
  85. LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)
  86. LOCAL_SRC_FILES := calmodule.cfg
  87. include $(BUILD_PREBUILT)
  88. endif #BUILD_TINY_ANDROID
  89. endif #TARGET_USES_SSC
  90. endif #TARGET_BOARD_PLATFORM

那么HMI的入口到底定义在这里的那个文件中呢?

功夫不负有心人,在sensors.cpp中,我们终于找到了HMI的入口,即下面的结构体:

  1. static struct hw_module_methods_t sensors_module_methods = {
  2. .open = sensors_open
  3. };
  4. struct sensors_module_t HAL_MODULE_INFO_SYM = {
  5. .common = {
  6. .tag = HARDWARE_MODULE_TAG,
  7. .module_api_version = (uint16_t)SENSORS_DEVICE_API_VERSION_1_3,
  8. .hal_api_version = HARDWARE_HAL_API_VERSION,
  9. .id = SENSORS_HARDWARE_MODULE_ID,
  10. .name = "QTI Sensors Module",
  11. .author = "Qualcomm Technologies, Inc.",
  12. .methods = &sensors_module_methods,
  13. .dso = NULL,
  14. .reserved = {0},
  15. },
  16. .get_sensors_list = sensors_get_sensors_list,
  17. .set_operation_mode = sensors_set_operation_mode
  18. };

HAL_MODULE_INFO_SYM即上文提到的HMI变量,恭喜各位,这里我们就开启了QualComm Sensor HAL的大门。

最后这个hw_module_t的结构体句柄会返回给我们的SensorDevice的构造函数里:

  1. SensorDevice::SensorDevice()
  2. : mSensorDevice(0),
  3. mSensorModule(0)
  4. {
  5. status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
  6. (hw_module_t const**)&mSensorModule);
  7. ALOGE_IF(err, "couldn't load %s module (%s)",
  8. SENSORS_HARDWARE_MODULE_ID, strerror(-err));
  9. if (mSensorModule) {
  10. err = sensors_open_1(&mSensorModule->common, &mSensorDevice);

接着,通过sensors_open_1方法将module->common传入,打开我们的sensor驱动。

  1. // hardware/libhardware/include/hardware/sensors.h
  2. static inline int sensors_open_1(const struct hw_module_t* module,
  3. sensors_poll_device_1_t** device) {
  4. return module->methods->open(module,
  5. SENSORS_HARDWARE_POLL, (struct hw_device_t**)device);
  6. }
  7. static inline int sensors_close_1(sensors_poll_device_1_t* device) {
  8. return device->common.close(&device->common);
  9. }

回过头去看看HMI的结构体定义,其中module->common->open被赋值为sensors_module_methods,其只有一个open方法,因此,module->methods->open最终会调用sensors_open方法来打开驱动程序。

到这里native到hal层的逻辑其实已经基本上分析完了。

高通 sensor 从native到HAL的更多相关文章

  1. linux驱动由浅入深系列:高通sensor架构实例分析之二(驱动代码结构)【转】

    本文转载自:https://blog.csdn.net/radianceblau/article/details/73498303 本系列导航: linux驱动由浅入深系列:高通sensor架构实例分 ...

  2. linux驱动由浅入深系列:高通sensor架构实例分析之三(adsp上报数据详解、校准流程详解)【转】

    本文转载自:https://blog.csdn.net/radianceblau/article/details/76180915 本系列导航: linux驱动由浅入深系列:高通sensor架构实例分 ...

  3. 高通sensor理解

    .1.高通为什么引入adsp? 2.adsp sensor 是如何工作起来的? 3.adsp 和ap 是如何通信的? 4.adsp 架构组成 解答: 1.高通在msm8960之前sensor 是挂在p ...

  4. 高通非adsp 架构下的sensor的bug调试

    高通 sensor 从native到HAL 高通HAL层之Sensor HAL 高通HAL层之bmp18x.cpp 问题现象: 当休眠后,再次打开preesure sensor的时候,会出现隔一段时候 ...

  5. 高通adsp架构下sensor

    一.高通sensor架构: linux驱动由浅入深系列:高通sensor架构实例分析之一(整体概览+AP侧代码分析) linux驱动由浅入深系列:高通sensor架构实例分析之二(adsp驱动代码结构 ...

  6. 高通HAL层之Sensor HAL

    高通的HAL层其实分为两种,一种是直接从kernel这边报数据上来的,由sensor HAL层来监听,另一种是走ADSP的模式,HAL层是通过qmi的形式进行监听的: 走ADSP架构的可以看下面的博客 ...

  7. android 6.0 高通平台sensor 工作机制及流程(原创)

    最近工作上有碰到sensor的相关问题,正好分析下其流程作个笔记. 这个笔记分三个部分: sensor硬件和驱动的工作机制 sensor 上层app如何使用 从驱动到上层app这中间的流程是如何 Se ...

  8. 高通qxdm抓取sensor的log【学习笔记】

    高通qxdm抓取sensor的log 打开qxdm,打开设置界面,去掉其他无关的log,打开Log packets .Message packets的SNS的log 之后需要把端口打开,把端口打开之后 ...

  9. 高通 8x26 andorid light sensor(TSL258x) 开发【转】

    本文转载自:http://www.voidcn.com/blog/u012296694/article/p-1669831.html 前言 8926平台的sensor架构与之前的平台完全不同,实际上已 ...

随机推荐

  1. EasyNetQ中使用自定义的ISerializer

    最近在使用EasyNetQ时,遇到一个问题:c++项目组发送的消息数据不是Json数据,而是自定义的数据格式(各字段+‘|’连接成一个字符串),EasyNetQ中消费消息接收的都是强类型,没办法直接消 ...

  2. 全网最详细的实用的搜索工具Listary和Everything对比的区别【堪称比Everything要好】(图文详解)

    不多说,直接上干货! 引言 无论是工作还是科研,我们都希望工作既快又好,然而大多数时候却迷失在繁杂的重复劳动中,久久无法摆脱繁杂的事情.   你是不是曾有这样一种想法:如果我有哆啦A梦的口袋,只要拿出 ...

  3. 喜大普奔,SITE4J网站上线啦

    喜大普奔,SITE4J网站上线啦: 你懂的:https://peterchenhdu.club/

  4. Zuul过滤器

    1.Zuul过滤器生命周期Zuul大部分功能都是通过过滤器来实现的,Zuul定义了4种标准的过滤器类型,这些过滤器类型对应于请求的典型生命周期.a.pre: 这种过滤器在请求被路由之前调用.可利用这种 ...

  5. Golang GC原理

    一.内存泄漏 内存泄露,是从操作系统的角度上来阐述的,形象的比喻就是“操作系统可提供给所有进程的存储空间(虚拟内存空间)正在被某个进程榨干”,导致的原因就是程序在运行的时候,会不断地动态开辟的存储空间 ...

  6. docker学习系列(四):数据持久化

    需要搞清楚一个概念的是,docker的容器设计理念是可以即开即用,用完可以随意删除,而新建容器是根据镜像进行渲染,容器的修改是不会影响到镜像,但是有时候容器里面运行的产生的数据(如mysql)或者配置 ...

  7. 120分钟React快速扫盲教程

    在教程开端先说些题外话,我喜欢在学习一门新技术或读过一本书后,写一篇教程或总结,既能帮助消化,也能加深印象和发现自己未注意的细节,写的过程其实仍然是一个学习的过程.有个记录的话,在未来需要用到相关知识 ...

  8. 自己动手实现java数据结构(四)双端队列

    1.双端队列介绍 在介绍双端队列之前,我们需要先介绍队列的概念.和栈相对应,在许多算法设计中,需要一种"先进先出(First Input First Output)"的数据结构,因 ...

  9. sip (db33)信令交互-视频点播与回播

    请求视频流: INVITE sip:@ SIP/2.0 Via: SIP/;rport;branch=z9hG4bK178329191 From: <sip:@>;tag= To: < ...

  10. c++中虚析构函数

    当指向基类的指针指向新建立的派生类对象而且基类和派生类都调用new向堆申请空间时,必须将基类的析构函数声明为虚函数,从而派生类的析构函数也为虚函数,这样才能在程序结束时自动调用它,从而将派生类对象申请 ...