1. 传感器系统概述:Android传感器架构、HAL层作用、传感器类型与数据流

各位同学,咱们今天正式开讲《AOSP传感器HAL层开发实战》的第一章。说实话,传感器这块内容,我做了快十年了,踩过的坑比走过的路还多。但别担心,我会把那些血泪史都转化成你们的学习经验。

先问大家一个问题:你手机里的计步器、屏幕自动旋转、微信摇一摇,这些功能背后是谁在干活?答案就是——传感器系统。今天我们就把它扒个底朝天。

1.1 Android传感器架构:从APP到硬件的四层结构

Android的传感器架构,说白了就是一条数据流水线。从最上层的APP到最底层的硬件,一共四层。我习惯把它想象成一个「传话游戏」:APP说「我要加速度数据」,最后硬件芯片就乖乖把数据送上来。

这四层分别是:

  • 应用层(Application Layer):你写的APP,通过SensorManager API来注册监听器。比如微信摇一摇,就是调用了加速度传感器。
  • 框架层(Framework Layer):SystemServer里的SensorService,负责管理所有传感器的注册、数据分发。嗯,这里有个坑,我后面会讲。
  • HAL层(Hardware Abstraction Layer):咱们课程的核心。它把硬件厂商的驱动封装成统一接口,让上层不用关心你是哪家的芯片。
  • 内核层(Kernel Layer):Linux驱动,直接跟I2C/SPI总线打交道,读取寄存器数据。

重点来了:HAL层是承上启下的关键。没有它,Google的AOSP代码就跑不通任何一家厂商的传感器。我当年在MTK平台做移植时,就因为HAL层的一个结构体对齐问题,整整调了三天。

1.2 HAL层到底在干什么?

你可能会问:为什么非要搞个HAL层?直接让Framework调驱动不行吗?

答案很简单:碎片化。Android设备成千上万,每家厂商的传感器芯片都不一样。博世的BMI160、ST的LSM6DS3、ADI的ADXL345……它们的寄存器配置、数据格式、唤醒逻辑全都不一样。

HAL层的作用就是:把「不一样」变成「一样」。它定义了一套标准接口,比如:

// sensors.h 中的核心结构体
struct sensors_module_t {
    struct hw_module_t common;
    int (*get_sensors_list)(struct sensors_module_t* module,
                            struct sensor_t const** list);
};

struct sensor_t {
    const char*     name;       // 传感器名字,比如 "BMI160 Accelerometer"
    const char*     vendor;     // 厂商,比如 "Bosch"
    int             version;    // 版本号
    int             handle;     // 句柄,上层用这个来区分传感器
    int             type;       // 类型,比如 SENSOR_TYPE_ACCELEROMETER
    float           maxRange;   // 量程
    float           resolution; // 分辨率
    float           power;      // 功耗,单位mA
    int32_t         minDelay;   // 最小采样周期,单位微秒
    uint32_t        fifoReservedEventCount; // FIFO保留事件数
    uint32_t        fifoMaxEventCount;      // FIFO最大事件数
    const char*     stringType; // 字符串类型
    const char*     requiredPermission; // 需要的权限
    int32_t         maxDelay;   // 最大采样周期
    uint32_t        flags;      // 标志位
    void*           reserved[2];// 保留字段
};

你看,每个传感器都要实现这个结构体。上层SensorService通过get_sensors_list()拿到所有传感器列表,然后就能统一调度了。

我的经验:写HAL层时,handle字段千万别乱填。我曾经图省事,把所有传感器handle都设成0,结果SensorService只注册了第一个传感器,后面的全丢了。嗯,这个bug查了我一整天。

1.3 传感器类型:你手机里到底藏了多少传感器?

Android官方定义的传感器类型有几十种。但说实话,日常开发中常用的就那么几个。我按使用频率给大家排个序:

类型 常量值 典型应用 备注
加速度计 SENSOR_TYPE_ACCELEROMETER (1) 计步器、屏幕旋转、游戏 最常用,几乎每台设备都有
陀螺仪 SENSOR_TYPE_GYROSCOPE (4) VR、AR、防抖 功耗较高,注意省电
磁力计 SENSOR_TYPE_MAGNETIC_FIELD (2) 电子罗盘、导航 容易受干扰,需要校准
光感 SENSOR_TYPE_LIGHT (5) 自动亮度调节 注意不同光强下的响应速度
距离 SENSOR_TYPE_PROXIMITY (8) 通话熄屏 通常只有近/远两个状态
气压计 SENSOR_TYPE_PRESSURE (6) 海拔测量、室内定位 高端设备才有
温度 SENSOR_TYPE_AMBIENT_TEMPERATURE (13) 环境温度监测 很少见,别跟CPU温度搞混

避坑指南:我曾经在某个项目里,把光感和距离传感器的handle搞反了。结果一打电话,屏幕不是熄屏而是疯狂调节亮度。用户投诉说「手机抽风了」。从那以后,我每次写HAL都会再三核对sensor_t里的type和handle对应关系。

1.4 数据流:一条加速度数据从芯片到APP的完整旅程

咱们来走一遍完整的数据流。假设你摇了一下手机,加速度传感器产生了一个数据。它经历了什么?

  1. 硬件层:BMI160芯片检测到加速度变化,通过I2C总线把原始数据写入寄存器。通常是6个字节:X轴2字节、Y轴2字节、Z轴2字节。
  2. 内核驱动:Linux驱动读取寄存器,把原始数据封装成input_event结构体,上报到input子系统。这里要注意,不同芯片的原始数据格式可能不一样——有的用补码,有的用偏移量。
  3. HAL层:HAL的poll()函数从驱动节点读取数据,转换成Android标准的sensors_event_t结构体。这个结构体包含时间戳、精度、以及各轴的数据。
  4. Framework层:SensorService通过HAL拿到数据,根据注册的监听器,把数据分发给对应的APP。这里有个关键点——数据是批量处理的,不是来一个发一个。
  5. 应用层:你的APP在onSensorChanged()回调里拿到数据,然后做摇一摇的逻辑判断。

你看,一条数据走了五站。每一站都可能出问题。我遇到过最离谱的bug是:HAL层的时间戳用的是系统时间,但内核驱动用的是jiffies,两者差了8个小时。结果APP拿到的数据时间全是乱的。

核心数据结构:sensors_event_t 是HAL层和Framework层之间的「通用货币」。它的定义如下:

typedef struct sensors_event_t {
    int32_t version;          // 版本号,必须设为 sizeof(sensors_event_t)
    int32_t sensor;           // 传感器handle,跟sensor_t里的handle对应
    int32_t type;             // 传感器类型
    int32_t reserved0;        // 保留
    int64_t timestamp;        // 时间戳,单位纳秒
    union {
        float data[16];       // 通用数据,加速度/陀螺仪等都用这个
        sensors_vec_t acceleration; // 加速度专用结构体
        sensors_vec_t magnetic;     // 磁力计专用
        sensors_vec_t orientation;  // 方向
        sensors_vec_t gyro;         // 陀螺仪
        float temperature;          // 温度
        float distance;             // 距离
        float light;                // 光感
        float pressure;             // 气压
        float relative_humidity;    // 湿度
        uncalibrated_event_t uncalibrated_gyro; // 未校准陀螺仪
        uncalibrated_event_t uncalibrated_magnetic; // 未校准磁力计
        meta_data_event_t meta_data; // 元数据
    };
    uint32_t flags;           // 标志位
    uint32_t reserved1[3];    // 保留
} sensors_event_t;

这里有个细节:timestamp字段必须是纳秒级的,而且最好是硬件时间戳,不是软件时间戳。为什么?因为软件时间戳有延迟,会导致多个传感器的数据不同步。你想想看,如果加速度计和陀螺仪的时间戳差了10毫秒,那做姿态融合时算出来的角度就是错的。

1.5 小结:这一章你该记住什么?

好,第一章的内容就到这里。咱们总结几个关键点:

  • Android传感器架构是四层结构:APP → Framework → HAL → Kernel。HAL层是咱们的主战场。
  • HAL层的作用是屏蔽硬件差异,提供统一接口。核心是sensors_module_tsensor_t结构体。
  • 传感器类型很多,但常用的就那七八种。加速度计是老大,几乎每个项目都离不开它。
  • 数据流从芯片到APP要走五站,每一站都可能出问题。时间戳、handle对应关系、数据格式转换,是三个最容易踩坑的地方。

下一章,咱们会深入HAL层的代码结构,手把手教你搭建一个完整的传感器HAL模块。到时候我会拿一个真实的BMI160驱动来讲解,保证干货满满。

对了,如果你在开发中遇到什么奇怪的问题,欢迎来跟我交流。我微信是 deep3321,公众号是「蓝海资料掘金营」。咱们下章见!