8、硬件抽象层(HAL)移植(上):HAL接口定义规范、Sensor HAL移植实战、Camera HAL移植要点、Audio HAL调试技巧
各位同学,今天咱们聊聊HAL移植。说实话,HAL层是Android系统里最“接地气”的一块。它直接跟硬件打交道,又得对上层的Framework负责。我做了这么多年嵌入式,每次移植HAL都像在走钢丝——既要懂硬件寄存器,又要懂Android的Service管理。
先看一张图,帮你快速建立本章的知识框架:
8.1 HAL接口定义规范
HAL接口定义,说白了就是给上层Framework和底层驱动之间画一条“红线”。这条线画得清不清晰,直接决定了后续移植工作顺不顺利。
我个人习惯,拿到一个新的SoC平台,第一件事不是写代码,而是先看hardware/interfaces/目录下的.hal文件。这些文件定义了所有标准接口。比如Sensor的接口长这样:
// ISensors.hal
package android.hardware.sensors@2.1;
interface ISensors {
getSensorsList() generates (vec<SensorInfo> sensors);
activate(int32_t sensorHandle, bool enabled) generates (Result result);
batch(int32_t sensorHandle, int64_t samplingPeriodNs, int64_t maxReportLatencyNs) generates (Result result);
flush(int32_t sensorHandle) generates (Result result);
// ... 其他方法
};
嗯,这里要注意:接口定义一旦发布,就不能随便改。为什么?因为Framework层已经编译进去了,你改了接口,上层调用就会崩溃。我在项目中遇到过,有人为了图省事,在.hal文件里加了一个自定义参数,结果导致整个系统OTA升级后传感器全部失效——教训深刻啊。
8.2 Sensor HAL移植实战
Sensor HAL移植,是很多入门同学的第一关。其实没那么复杂,核心就三件事:枚举传感器、激活/去激活、上报数据。
先看枚举部分。你需要实现getSensorsList(),返回设备支持的所有传感器。以i.MX8M Plus为例,它通常包含:
| 传感器类型 | 硬件接口 | 典型厂商 | 注意事项 |
|---|---|---|---|
| 加速度计 | I2C | Bosch BMI160 | 注意采样率范围 |
| 陀螺仪 | SPI | InvenSense ICM-20602 | 需要校准偏移 |
| 磁力计 | I2C | AKM AK09918 | 注意磁场干扰 |
| 环境光 | I2C | Vishay VEML6030 | 响应时间要快 |
激活函数activate()的实现,说白了就是控制硬件寄存器的使能位。我曾经在调试一个陀螺仪时,发现激活后数据一直为零。查了两天,最后发现是I2C时钟频率设得太高,传感器没响应。嗯,这种坑踩过一次就记住了。
数据上报是Sensor HAL的核心。Android要求传感器数据通过ISensorsCallback回调上报,频率要稳定。我建议用std::thread开一个独立线程,配合std::chrono做精确计时:
void SensorThread::run() {
while (mRunning) {
auto start = std::chrono::steady_clock::now();
// 读取硬件寄存器
float x, y, z;
readSensorData(&x, &y, &z);
// 构造事件
Event event;
event.sensorHandle = mHandle;
event.timestamp = elapsedRealtimeNano();
event.u.vec3.x = x;
event.u.vec3.y = y;
event.u.vec3.z = z;
// 回调上报
mCallback->onEvent(event);
// 精确延时
auto elapsed = std::chrono::steady_clock::now() - start;
std::this_thread::sleep_for(mPeriod - elapsed);
}
}
activate()里循环读数据,结果把整个SensorService卡死了。数据上报必须异步。
8.3 Camera HAL移植要点
Camera HAL比Sensor复杂得多。它涉及预览、拍照、录像、参数控制等多个维度。我个人的经验是:先让预览跑起来,再谈其他功能。
Camera HAL的核心接口是ICameraDevice和ICameraDeviceSession。你需要实现:
open():打开摄像头设备,初始化硬件configureStreams():配置数据流,包括分辨率、格式、帧率processCaptureRequest():处理拍照请求,返回结果flush():清空所有待处理的请求
这里有个关键点:Stream配置必须匹配硬件能力。比如i.MX8M Plus的ISP支持的最大分辨率是1920x1080,如果你配置了4K,Framework会直接报错。我在项目中遇到过,客户非要支持4K预览,结果发现硬件ISP根本扛不住,最后只能降级处理。
调试Camera HAL时,我常用的三板斧:
- logcat过滤:
adb logcat -s CameraService:V CameraHAL:V,看有没有错误日志 - dumpsys检查:
adb shell dumpsys media.camera,查看CameraService内部状态 - 硬件寄存器验证:直接读ISP寄存器,确认硬件是否正常工作
8.4 Audio HAL调试技巧
Audio HAL的调试,说白了就是跟延迟和噪声作斗争。Android的Audio系统分为输入(录音)和输出(播放)两条路径,每条路径都有各自的HAL接口。
Audio HAL的核心接口是IDevice和IStream。你需要实现:
openOutputStream():打开输出流,配置采样率、通道数、格式openInputStream():打开输入流,配置参数setMasterVolume():设置主音量setMicMute():设置麦克风静音
调试Audio HAL时,我最常用的工具是tinyalsa。它比Android的AudioFlinger更底层,可以直接操作ALSA驱动。比如:
# 播放测试音
tinyplay /system/media/audio/ui/Effect_Tick.ogg -D 0 -d 0
# 录音测试
tinycap /data/test.wav -D 0 -d 0 -c 2 -r 48000 -b 16
# 查看音频设备列表
tinymix
我曾经在调试一个车载项目时,发现播放音乐有“噼啪”的爆音。查了半天,最后用tinymix一看,发现某个混音器的增益设得太高,导致信号削波了。把增益调低后,问题立刻解决。
另一个常见问题是音频路由。比如,蓝牙电话来了,音乐要自动暂停,声音要切换到蓝牙耳机。这个逻辑在AudioPolicyManager里实现,但HAL层必须正确报告设备连接状态。我建议用dumpsys audio查看当前的路由状态:
adb shell dumpsys audio | grep -A 20 "Audio routes"
audio-latency工具测量实际延迟,而不是凭感觉调。
好了,这一章的内容就到这里。HAL移植是个细致活,每个传感器、每个摄像头、每个音频通道都有自己的脾气。多动手、多调试,慢慢就能摸清门道。