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的完整旅程
咱们来走一遍完整的数据流。假设你摇了一下手机,加速度传感器产生了一个数据。它经历了什么?
- 硬件层:BMI160芯片检测到加速度变化,通过I2C总线把原始数据写入寄存器。通常是6个字节:X轴2字节、Y轴2字节、Z轴2字节。
- 内核驱动:Linux驱动读取寄存器,把原始数据封装成
input_event结构体,上报到input子系统。这里要注意,不同芯片的原始数据格式可能不一样——有的用补码,有的用偏移量。 - HAL层:HAL的
poll()函数从驱动节点读取数据,转换成Android标准的sensors_event_t结构体。这个结构体包含时间戳、精度、以及各轴的数据。 - Framework层:SensorService通过HAL拿到数据,根据注册的监听器,把数据分发给对应的APP。这里有个关键点——数据是批量处理的,不是来一个发一个。
- 应用层:你的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_t和sensor_t结构体。 - 传感器类型很多,但常用的就那七八种。加速度计是老大,几乎每个项目都离不开它。
- 数据流从芯片到APP要走五站,每一站都可能出问题。时间戳、handle对应关系、数据格式转换,是三个最容易踩坑的地方。
下一章,咱们会深入HAL层的代码结构,手把手教你搭建一个完整的传感器HAL模块。到时候我会拿一个真实的BMI160驱动来讲解,保证干货满满。
对了,如果你在开发中遇到什么奇怪的问题,欢迎来跟我交流。我微信是 deep3321,公众号是「蓝海资料掘金营」。咱们下章见!