应用与Sensor通信

Solution 框架下的 Sensor 驱动的启动/停止、参数配置等关键操作均由应用层发起,驱动层仅提供标准接口实现。所有跨层、跨核通信均通过 IPC 框架统一封装,确保跨平台、跨核场景下的可移植性与一致性。 alt text

原则:应用层与 Sensor 之间的所有通信必须经由 IPC 框架进行。

1.核心概念

Solution 提供了一套简化的 IPC 通信注册框架(代码位于 ipc_service.c),封装了底层数据交互细节。通过该框架,用户无需关注复杂的跨核通信实现,仅需调用注册接口并实现回调函数,即可快速搭建通信链路

详细参见IPC(核间)通信

IPC 框架初始化

Solution 通过宏区分 HCPU 与 LCPU 角色,分别注册为 IPC 通信的客户端与服务端。

初始化函数

int lcpu_service_init(void)
{
#if defined(BF0_LCPU) || defined(SENSOR_IN_HCPU) || defined(BSP_USING_PC_SIMULATOR)
    lcpu_srv_handle = ipc_server_register("LCPU_APP", NULL, 
                                          lcpu_service_est_cb, 
                                          lcpu_service_sleep_cb, 
                                          lcpu_service_comm_cb);
    RT_ASSERT(lcpu_srv_handle);
#endif
#if defined(BF0_HCPU) || defined(BSP_USING_PC_SIMULATOR)
    ipc_client_register(&lcpu_app_srv_handle, lcpu_srv_handle, "LCPU_APP",
                        app_service_filter_cb, app_service_est_cb, app_service_comm_cb);
    RT_ASSERT(lcpu_app_srv_handle);
#endif
    LOG_I("%s: server %p client %p", __func__, lcpu_srv_handle, lcpu_app_srv_handle);
    return 0;
}
#if defined(BF0_LCPU)
    INIT_COMPONENT_EXPORT(lcpu_service_init);
#else
    INIT_APP_EXPORT(lcpu_service_init); 
#endif

HCPU 端(客户端)

ipc_client_register(&lcpu_app_srv_handle,
                    lcpu_srv_handle,
                    "LCPU_APP", 
                    app_service_filter_cb,
                    app_service_est_cb,
                    app_service_comm_cb);
  • 主动发起服务连接请求

  • 通过回调函数接收 LCPU 上报的数据

LCPU 端(服务端)

lcpu_srv_handle = ipc_server_register("LCPU_APP", NULL, 
                                      lcpu_service_est_cb, 
                                      lcpu_service_sleep_cb, 
                                      lcpu_service_comm_cb);
  • 注册命名服务 "LCPU_APP",等待客户端连接

  • 通过回调函数接收HCPU下发的事件与消息

详细参见solution\components\lcpu\lcpu_service.c

2. 传感器启动流程

2.1 运动传感器启动

运动传感器在系统上电阶段由应用层触发自动启用,其完整工作流程如下:

步骤 1:应用层发起上电通知(HCPU端)

HCPU的电源管理模块 app_pm 通过IPC接口向LCPU发送 LCPU_PWR_ON 事件:

void app_lcpu_pwr_on(void)
{
    ipc_send_event_to_lcpu(LCPU_PWR_ON);
}

步骤 2:IPC框架消息中转(HCPU → LCPU)

lcpu_service 组件的通信回调函数 lcpu_service_comm_cb 接收到该事件后,根据消息类型将其转发至 sensor_service 的对应处理函数:

case LCPU_PWR_ON:
{
    send_msg_to_lcpu_thread(NULL, 0, power_on_cb, 0);
    send_msg_id = msg_id;
    break;
}

#if defined(RT_USING_SENSOR) && (!defined(BSP_USING_PC_SIMULATOR) || defined(SENSOR_ALGO_SIMULATOR))
    if (send_msg_id)
        send_msg_to_lcpu_thread(data, len, sensors_msg_process_in_lcpu_thread_cb, msg_id);
#endif

步骤 3:Sensor Service解析与指令下发(LCPU端)

sensor_servicesensors_msg_process_in_lcpu_thread_cb 函数根据消息ID进行分支处理。 针对开机事件,执行以下操作:

  • 配置运动传感器参数并调用配置接口

  • 创建传感器轮询的软件定时器并启动

case LCPU_PWR_ON:
{
#if defined (SENSOR_USING_6D) &&!defined (BSP_USING_PC_SIMULATOR)
        rt_kprintf("lcpu power on gsensor!\n");
        gsensor_setting_t gs_setting = {SENSOR_ON, 0, 0, 0};
        gsensor_config_req(APP_SENSOR_GSENSOR_SETTING_REQ, (void *)&gs_setting);
#endif
#if defined(SENSOR_USING_GPS) || defined(SENSOR_USING_6D) || defined(SENSOR_USING_HR) || defined(SENSOR_USING_MAG) || defined(SENSOR_USING_ASL)
        sensor_timer_create();
        sensor_timer_start();
#endif
        break;
}

步骤 4:驱动层执行初始化(LCPU端)

驱动程序接收到配置请求后,执行设备初始化并打开传感器:

if (SENSOR_ON == gsensor_state->state && SENSOR_OFF == gsensor_is_opened())
{
    if (RT_EOK == gsensor_init())
    {
        gsensor_enable_set(SENSOR_ON);
    }
}

步骤 5:周期性轮询传感器(LCPU端)

gsensor_algo_scheduler() 由软件定时器周期性调用,实现运动传感器的持续轮询与数据处理:

1. 状态检查 首先验证传感器是否已正常打开,若未打开则立即终止执行:

if (!gsensor_is_opened())
        return;

2. 数据采集与处理 当累计达到指定采样周期时,执行以下操作:

  • 从驱动层读取传感器数据

  • 进行算法处理

  • 保存数据供其他传感器使用

  • 将处理后的数据发送至应用层

#ifndef SENSOR_IN_HCPU
    gsensor_data_fetch(0, (void *)&gsensor_data);
#if defined (USING_GSENSOR_DATA_STORE_LIST)
    gsensor_data_store(&gsensor_data);
#endif
#endif
    RT_ASSERT(gsensor_data.num <= gsensor_fifo_len);
    factory_send_gsensor_data_to_app((int16_t)(gsensor_data.buf[0].acce_data[0] * 100.0f), (int16_t)(gsensor_data.buf[0].acce_data[1] * 100.0f), (int16_t)(gsensor_data.buf[0].acce_data[2] * 100.0f));
    if ((gsensor_data.num > 0) && (sensor_info.gs_algo_ops))
    {
        sensor_info.gs_algo_ops(period, &gsensor_data);
    }

2.2 心率传感器启动

心率传感器的启动依据应用层的具体业务场景动态配置。以下以 Sport Action 应用退出时切换为自动测量模式 为例进行说明:

步骤 1:应用层切换测量模式(HCPU)

当 Sport Action 应用退出并切换至停止状态时,on_stop() 会释放资源,同时将心率传感器由“手动测量”切为“自动测量”。 HCPU 通过IPC框架把配置参数一次性下发到 LCPU:先填充hr_auto_meas_t结构体(使能=1、间隔=5 min),再以APP_SENSOR_SETTING_HR_AUTO_MEAS为消息 ID:

void sport_set_hr_auto_measure(void)
{
#if defined(RT_USING_SENSOR) && defined(BSP_BLE_SIBLES)
    setting_hr_auto_meas_t hr_itrv = {0};
    hr_itrv.enable = 1;
    hr_itrv.interval_time_min = 5;
    hr_itrv.start_hour = 0;
    hr_itrv.start_min = 0;
    hr_itrv.end_hour = 0;
    hr_itrv.end_min = 0;
    ipc_send_msg_to_lcpu(APP_SENSOR_SETTING_HR_AUTO_MEAS, &hr_itrv, sizeof(hr_itrv));
#endif
}

步骤 2:IPC 框架消息中转(HCPU → LCPU)

lcpu_service 组件的通信回调函数 lcpu_service_comm_cb 接收该事件,并依据消息类型将其转发至 sensor_service 的处理函数:

case APP_SENSOR_SETTING_HR_AUTO_MEAS:
{
    send_msg_id = msg_id;
    break;
}

#if defined(RT_USING_SENSOR) && (!defined(BSP_USING_PC_SIMULATOR) || defined(SENSOR_ALGO_SIMULATOR))
    if (send_msg_id)
        send_msg_to_lcpu_thread(data, len, sensors_msg_process_in_lcpu_thread_cb, msg_id);
#endif

步骤 3:Sensor Service 解析与指令下发(LCPU)

sensor_servicesensors_msg_process_in_lcpu_thread_cb 识别为心率传感器相关命令(如 APP_SENSOR_SETTING_HR_AUTO_MEAS),统一调用 hr_config_req 进行配置:

#ifdef SENSOR_USING_HR
    case APP_SENSOR_SETTING_HR_AUTO_MEAS:
    case APP_SENSOR_SETTING_SPO2_AUTO_MEAS:
    case APP_SENSOR_SETTING_HR_REMIND:
    case APP_SENSOR_HR_SETTING_REQ:
    case APP_SENSOR_TODAY_HR_REQ:
    {
        hr_config_req(msg_id, msg->data);
        break;
    }
#endif

步骤 4:驱动层配置生效(LCPU)

驱动层接收配置参数,完成传感器工作模式的配置:

1. 参数分发
hr_config_req() 根据消息 ID 传递至具体设置函数:

void hr_config_req(uint16_t msg_id, uint8_t *data)
{
    switch (msg_id)
    {
    case APP_SENSOR_SETTING_HR_AUTO_MEAS:
        hr_set_auto_meas(data);
        break;
    }
}

2. 参数校验与设置
hr_set_auto_meas() 将配置写入全局变量hr_auto_meas,其中hr_auto_meas.enable被设为 1(启用自动测量),并强制保证最小测量间隔不低于 5 分钟:

static void hr_set_auto_meas(void *meas)
{
#ifndef HR_HOURLY_MEASURE
    hr_auto_meas = *((hr_auto_meas_t *)meas);
    if (hr_auto_meas.interval_time_min < 5)
        hr_auto_meas.interval_time_min = 5;
#endif
}

步骤 5:心率传感器自动测量策略(LCPU)

hr_algo_scheduler() 由软件定时器周期性调用,实现心率测量的自动化调度。整个流程分为状态检查、分钟触发、测量启动、数据采集与停止四个阶段。

1. 状态检查

系统首先检查所有心率/血氧测量模式是否处于关闭状态。若均未激活,则重置相关计时器并跳过算法处理,仅保留每分钟计数功能:

if ((SENSOR_OFF == hr_get_state(HR_MANU_MEAS | HR_AUTO_MEAS))
        && (SENSOR_OFF == spo2_get_state(HR_MANU_MEAS | HR_AUTO_MEAS)))
{
    hr_peroid_time = hr_algo_time = hr_auto_time = spo2_auto_time = 0;
    goto end;  
}

2. 分钟触发

每当累计时间达到 60 秒时,执行 hr_auto_meas_process(false) 以递增测量间隔计数:

end:
if (one_minu >= 60 * 1000)
{
    one_minu = 0;
#ifndef HR_HOURLY_MEASURE
    hr_auto_meas_process(false);
#endif
    hr_send_curday_history_to_app();
}

3. 测量启动

hr_auto_meas_process() 根据预设间隔和使能状态控制传感器上电。当满足以下条件时触发测量:

  • 自动测量功能已启用(hr_auto_meas.enable

  • 到达设定的分钟间隔(或首次启动)

static void hr_auto_meas_process(bool enable)
{
    static uint8_t meas_interval = 0;
    static uint8_t first = 1;

    if (!hr_auto_meas.enable) return;

    if (!enable)  
    {
        meas_interval++;
        if (first || meas_interval >= meas->interval_time_min)
        {
            first = 0;
            meas_interval = 0;
            goto out;
        }
        return;
    }

out:
    if (SENSOR_OFF == hr_get_state(HR_MANU_MEAS) &&
        (start_time_stamp == end_time_stamp || 
         (cur_time_stamp >= start_time_stamp && cur_time_stamp <= end_time_stamp)))
    {
        hr_setting_t hr_state = {SENSOR_ON, HR_AUTO_MEAS, SENSOR_HR, 0};
        send_msg_to_lcpu_thread(&hr_state, sizeof(hr_state), hr_setting_req_cb, 0);
        LOG_I("hr_auto_meas start!"); 
    }
}

4. 数据采集与自动停止

传感器上电后,hr_algo_scheduler()持续执行数据采集与算法运算。测量成功时,数据保存至历史记录后关闭传感器;测量超时则直接关闭传感器。

if ((SENSOR_ON == hr_get_state(HR_AUTO_MEAS)) && 
    (HR_MEAS_SUCC == meas_result || hr_auto_time >= 1000 * 60))
{
    if (HR_MEAS_SUCC == meas_result)
        hr_save_meas_result_to_history(SENSOR_HR);
    
    hr_auto_time = 0;
    hr_setting_t hr_state = {SENSOR_OFF, HR_AUTO_MEAS, SENSOR_HR};
    send_msg_to_lcpu_thread(&hr_state, sizeof(hr_state), hr_setting_req_cb, 0);
    LOG_I("hr_auto_meas finish!");
}