一、问题背景

在 OpenHarmony 设备上,当系统进入休眠(Suspend)后再次唤醒(Resume)时,加速度传感器(Accelerometer)会出现数据异常或完全失效的问题。具体表现为:

  • 设备休眠前 Sensor 工作正常,能正常上报数据;
  • 设备唤醒后,Sensor 不再上报数据,或上报的数据为异常值;
  • 需要重启设备或重新加载驱动才能恢复。

这个问题在依赖 Sensor 数据的应用场景中(如屏幕旋转、计步、重力感应等)影响尤为严重,直接导致用户体验中断。

二、根因分析

2.1 Sensor 硬件特性

加速度传感器芯片(如常见的 LSM6DS3、BMA 系列、ADXL 系列等)通过 I2C/SPI 总线与 SoC 通信。芯片内部有一组寄存器,控制着采样率、量程、工作模式、中断配置等。这些寄存器的状态决定了芯片能否正确采集和输出数据。

当系统进入休眠时,可能发生以下情况之一:

  1. 传感器芯片供电被切断:部分硬件设计中,Sensor 的电源域在系统休眠时会被关断以省电,芯片内部寄存器全部丢失。
  2. 芯片进入低功耗模式:即使供电未断,芯片也可能因总线上无活动而进入待机状态,部分寄存器值被重置。
  3. 总线控制器状态丢失:I2C/SPI 控制器在休眠/唤醒过程中可能需要重新初始化。

无论哪种情况,唤醒后 Sensor 芯片的寄存器状态都不可靠,需要重新配置才能正常工作。

2.2 原驱动代码的缺陷

在原始 drivers\hdf_core\framework\model\sensor\driver\accel\sensor_accel_driver.c 中,驱动注册了 HDF_INIT 入口,完成了传感器初始化、设备注册、工作队列创建等流程。但是,驱动没有注册任何电源管理回调(Power Listener)

这意味着:

系统休眠 → 驱动无感知 → 不做任何处理
系统唤醒 → 驱动无感知 → 不恢复寄存器配置

唤醒后,驱动层的软件状态(如 drvData->enable、定时器、工作队列)可能依然认为 Sensor 处于"已使能"状态,但底层硬件的寄存器配置已经丢失。于是定时器继续触发 AccelDataWorkEntry 读取数据,但芯片实际并未被正确配置,导致读取失败或读到垃圾数据。

三、解决方案

核心思路:利用 HDF 框架的电源管理机制,在设备唤醒时重新配置 Sensor 芯片寄存器。

具体改动分为三部分:

3.1 注册电源管理监听器

引入 hdf_pm.h 头文件,在驱动初始化时注册 IPowerEventListener,使驱动能够感知系统的休眠/唤醒事件。

3.2 实现唤醒回调

在 Resume 回调中,如果 Sensor 在休眠前处于使能状态(drvData->enable == true),则重新下发 SENSOR_INIT_GROUP 和 SENSOR_ENABLE_GROUP 两组寄存器配置,恢复芯片到正常工作状态。

3.3 增强使能流程

在 SetAccelEnable() 中,除了配置 ENABLE 寄存器组外,额外增加 INIT 寄存器组的配置,确保每次使能时芯片都处于已初始化状态。

四、代码改动详解

文件位置:

drivers\hdf_core\framework\model\sensor\driver\accel\sensor_accel_driver.c

改动点一:引入电源管理头文件

#include "hdf_pm.h"

新增 hdf_pm.h 头文件,提供 HdfPmRegisterPowerListenerHdfPmUnregisterPowerListener 等 API 声明。


改动点二:前向声明

static struct AccelDrvData *AccelGetDrvData(void);

由于电源管理回调函数定义在 AccelGetDrvData() 之前,需要提前声明,以便回调函数内部调用。


改动点三:实现电源管理回调函数

新增四个回调函数:

static int32_t AccelDriverResume(struct HdfDeviceObject *deviceObject)
{
    (void)deviceObject;
    struct AccelDrvData *drvData = AccelGetDrvData();

    // 如果驱动未初始化、配置为空、或 Sensor 未使能,直接返回
    if (drvData == NULL || drvData->accelCfg == NULL || !drvData->enable) {
        return HDF_SUCCESS;
    }

    HDF_LOGI("%s: Accel re-init after resume", __func__);

    // 关键:重新下发 INIT 寄存器组,恢复芯片基础配置
    (void)SetSensorRegCfgArray(&drvData->accelCfg->busCfg,
        drvData->accelCfg->regCfgGroup[SENSOR_INIT_GROUP]);

    // 重新下发 ENABLE 寄存器组,使芯片进入工作模式
    (void)SetSensorRegCfgArray(&drvData->accelCfg->busCfg,
        drvData->accelCfg->regCfgGroup[SENSOR_ENABLE_GROUP]);
    return HDF_SUCCESS;
}

这是本次修复的核心代码。 逻辑如下:

  1. 获取驱动数据指针;
  2. 判断 Sensor 是否处于使能状态——如果休眠前没开 Sensor,唤醒后也不需要做任何事;
  3. 重新下发 SENSOR_INIT_GROUP(初始化寄存器组),恢复芯片的基础配置(如采样率、量程、分辨率等);
  4. 重新下发 SENSOR_ENABLE_GROUP(使能寄存器组),让芯片进入测量模式。

其余三个回调:

// 浅度唤醒(Doze Resume),处理方式与普通唤醒一致
static int32_t AccelDriverDozeResume(struct HdfDeviceObject *deviceObject)
{
    return AccelDriverResume(deviceObject);
}

// 休眠回调,仅打印日志
static int32_t AccelDriverSuspend(struct HdfDeviceObject *deviceObject)
{
    (void)deviceObject;
    HDF_LOGI("%s: Accel suspending", __func__);
    return HDF_SUCCESS;
}

// 浅度休眠(Doze Suspend),无需额外处理
static int32_t AccelDriverDozeSuspend(struct HdfDeviceObject *deviceObject)
{
    (void)deviceObject;
    return HDF_SUCCESS;
}

改动点四:定义电源监听器结构体

static const struct IPowerEventListener g_accelPowerListener = {
    .DozeResume  = AccelDriverDozeResume,
    .DozeSuspend = AccelDriverDozeSuspend,
    .Resume      = AccelDriverResume,
    .Suspend     = AccelDriverSuspend,
};

改动点五:SetAccelEnable() 增加 INIT 寄存器组配置

  if (drvData->enable) {
      HDF_LOGE("%s: Accel sensor is enabled", __func__);
      return HDF_SUCCESS;
  }
+
+ (void)SetSensorRegCfgArray(&drvData->accelCfg->busCfg,
+     drvData->accelCfg->regCfgGroup[SENSOR_INIT_GROUP]);
+
  ret = SetSensorRegCfgArray(&drvData->accelCfg->busCfg,
      drvData->accelCfg->regCfgGroup[SENSOR_ENABLE_GROUP]);

在使能 Sensor 时,先下发 SENSOR_INIT_GROUP 初始化寄存器组,再下发 SENSOR_ENABLE_GROUP 使能寄存器组。这样做的好处是:无论芯片当前处于什么状态,先做一次完整初始化再使能,保证状态一致。

改动点六:驱动初始化时注册电源监听器

在 AccelInitDriver() 中新增:

  drvData->accelCfg->regCfgGroup = &g_regCfgGroup[0];
  drvData->cb = NULL;
+
+ if (HdfPmRegisterPowerListener(device, &g_accelPowerListener) != HDF_SUCCESS) {
+     HDF_LOGE("%s: Accel register power listener failed", __func__);
+ }

在驱动 Init 阶段注册电源监听器,之后系统每次休眠/唤醒时,HDF 框架都会调用对应的回调函数。


改动点七:驱动释放时注销电源监听器

在 AccelReleaseDriver() 中新增:

  struct AccelDrvData *drvData = (struct AccelDrvData *)device->service;
  CHECK_NULL_PTR_RETURN(drvData);
+
+ (void)HdfPmUnregisterPowerListener(device, &g_accelPowerListener);

在驱动卸载时注销监听器,避免回调悬空指针。

五、总结

本次修复方案不仅适用于加速度传感器,对于其他类型的传感器(陀螺仪、磁力计、气压计等)如果遇到休眠唤醒后失效的问题,同样可以参考此方案:

  1. 注册 IPowerEventListener
  2. 在 Resume 回调中根据 enable 状态重新下发 INIT 和 ENABLE 寄存器组;
  3. 在 SetXxxEnable() 中增加 INIT 寄存器组的配置。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐