运动传感器移植指南

本章节将实现运动传感器的在Solution Sensor框架下的移植,以STK8321为参考。

1. 原生驱动移植

本节实现传感器芯片最基础的硬件通信功能,包括I2C总线操作、中断处理和电源管理。

1.1 配置与宏定义

在 sdk\customer\peripherals\sensor 目录下创建以STK8321命名的目录,添加原生驱动STK8321.c。 修改sdk\customer\peripherals\sensor对应ACC传感器类型的目录下kconfig文件,新建宏来实现驱动的控制

宏名称

类型

说明

STK8321_BUS_NAME

必需

I2C总线设备名称(如"i2c0")

GS_I2C_ADDR

必需

I2C地址

STK8321_MODEL_NAME

必需

传感器设备模型名称(如"stk8321")

STK8321_POW_PIN

可选

电源控制GPIO引脚号

STK8321_INT

可选

中断输入GPIO引脚号

STK8321_USE_INT

可选

启用中断模式

STK8321_USE_DMA

可选

启用DMA传输

PMIC_CONTROL_SERVICE

可选

启用PMIC电源管理

1.2 I2C总线初始化

初始化需要通过宏定义找到对应的总线设备打开并对总线进行对应的参数设置。

static int stk8321_i2c_init(void)
{
    rt_uint16_t flag_open = RT_DEVICE_FLAG_RDWR
    #ifdef STK8321_USE_DMA| RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX
    #endif
                            ;
    /* get i2c bus device */
    gs_content.bus_handle = (struct rt_i2c_bus_device *)rt_device_find(GS_BUS_NAME);
    if (RT_Device_Class_I2CBUS != gs_content.bus_handle->parent.type)
    {
        gs_content.bus_handle = NULL;
    }
    if (gs_content.bus_handle)
    {
        rt_device_open((rt_device_t)gs_content.bus_handle, flag_open);
    }
    else
    {
        rt_kprintf("gs bus not find ! \n");
        return -RT_ERROR;
    }

    {
        struct rt_i2c_configuration configuration =
        {
            .mode = 0,
            .addr = 0,
            .timeout = 200,
            .max_hz = 400000,
        };

        rt_i2c_configure(gs_content.bus_handle, &configuration);
    }
    rt_kprintf("gs i2c bus init ok ! \n");
    return 0;
}

1.3 I2C读写接口

总线读写函数供原生驱动调用,实现寄存器的数据交互。

static int32_t stk8321_i2c_read(GS_CONT_T *ctx, uint8_t reg, uint8_t *data, uint16_t len)
{
    rt_size_t res = 0;
    GS_CONT_T *handle = ctx;

    if (handle && handle->bus_handle && data)
    {
        rt_i2c_mem_read(handle->bus_handle, GS_I2C_ADDR, reg, 8, data, len);
    }

    return res;
}
static int32_t stk8321_i2c_write(GS_CONT_T *ctx, uint8_t reg, uint8_t *data, uint16_t len)
{
    rt_int8_t res = 0;
    uint8_t value[2];
    GS_CONT_T *handle = ctx;
    if (handle && handle->bus_handle && data)
    {
        struct rt_i2c_msg msgs;
        value[0] = reg;
        value[1] = *data;
        msgs.addr  = GS_I2C_ADDR;   /* slave address */
        msgs.flags = RT_I2C_WR;        /* write flag */
        msgs.buf   = &value[0];             /* Send data pointer */
        msgs.len   = sizeof(value);
        if (rt_i2c_transfer(handle->bus_handle, &msgs, 1) == 1)
        {
            res = RT_EOK;
        }
        else
        {
            struct rt_i2c_configuration configuration =
            {
                .mode = 0,
                .addr = 0,
                .timeout = 5000,
                .max_hz = 400000,
            };

            rt_i2c_configure(gs_content.bus_handle, &configuration);

            res = -RT_ERROR;
        }
    }
    return res;
}

1.4 中断处理

当启用STK8321_USE_INT时,需实现中断初始化、处理函数及线程任务。

static void stk8321_int_handle(void *args)
{
    rt_kprintf("stk8321_int_handle\n");
    rt_pin_detach_irq(GS_INT);
    rt_sem_release(&gs_int_sem);
}
static int stk8321_gpio_int_enable(void)
{
    struct rt_device_pin_mode m;
    rt_pin_mode(GS_INT, PIN_MODE_INPUT);
    rt_pin_attach_irq(GS_INT, PIN_IRQ_MODE_RISING, stk8321_int_handle, (void *)(rt_uint32_t)GS_INT);
    rt_pin_irq_enable(GS_INT, 1);
    return 0;
}
static int stk8321_gpio_int_disable(void)
{
    rt_pin_irq_enable(GS_INT, 0);
    rt_pin_detach_irq(GS_INT);
    return 0;

}
static void stk8321_thread_task(void *params)
{
    int32_t ret;
    uint8_t status = 0;

    while (1)
    {
        rt_sem_take(&gs_int_sem, RT_WAITING_FOREVER);
        rt_pin_irq_enable(GS_INT, 0);

        //add fetchdata

        rt_pin_attach_irq(GS_INT, PIN_IRQ_MODE_RISING, stk8321_int_handle, (void *)(rt_uint32_t)GS_INT);
        rt_pin_irq_enable(GS_INT, 1);
    }
}
static int stk8321_thread_init(void)
{
    if (!gs_int_sem.parent.parent.name[0])
        rt_sem_init(&gs_int_sem, "gs_sem", 0, RT_IPC_FLAG_FIFO);
    if (!gs_thread)
        gs_thread = rt_thread_create("gs_thread", stk8321_thread_task, NULL, GS_THRED_STACK_SIZE, 15, 10);
    RT_ASSERT(gs_thread);

    return 0;
}
static void stk8321_thread_deinit(void)
{
    if (gs_thread)
    {
        rt_thread_delete(gs_thread);
        gs_thread = NULL;
    }
    if (gs_int_sem.parent.parent.name[0])
    {
        rt_sem_detach(&gs_int_sem);
        gs_int_sem.parent.parent.name[0] = 0;
    }
    rt_kprintf("gs_thread_deinit\n");
}

1.5 电源管理

支持GPIO电源控制和PMIC管理两种模式。

void stk8321_power_onoff(bool onoff)
{
    if (onoff)
    {
#if defined(PMIC_CONTROL_SERVICE)
        pmic_service_control(PMIC_CONTROL_GSENSOR, 1);

#elif (GS_POW_PIN >= 0)
        rt_pin_mode(GS_POW_PIN, PIN_MODE_OUTPUT);
        rt_pin_write(GS_POW_PIN, PIN_HIGH);
#endif
        rt_kprintf("gs_power_on\n");
    }
    else
    {
#if defined(PMIC_CONTROL_SERVICE)
        pmic_service_control(PMIC_CONTROL_GSENSOR, 0);
#elif (GS_POW_PIN >= 0)
        rt_pin_mode(GS_POW_PIN, PIN_MODE_OUTPUT);
        rt_pin_write(GS_POW_PIN, PIN_LOW);
#endif
        rt_kprintf("gs_power_off\n");
    }
}

1.6 调试方法

在代码添加完成后,可通过命令行接口对传感器驱动进行基础功能验证。具体实现步骤如下:

首先,定义一个静态命令行处理函数 static void STK8321_test(int argc, char *argv[]),然后利用 MSH_CMD_EXPORT 宏将其注册为 Finsh 终端命令。

该命令行函数应至少包含以下核心功能:

  • 传感器初始化:完成通信接口配置及设备地址校验

  • 寄存器读写操作:基于 I2C/SPI 等接口实现对传感器内部寄存器的访问

  • 功能验证:根据传感器类型执行相应测试,例如气压传感器读取气压值、温度传感器获取温度、加速度传感器采集运动数据等:


#ifdef STK8321_DEBUG
static void STK8321_test(int argc, char *argv[])
{
    if (strcmp(argv[1], "init") == 0)
    {
        stk8321_init();
    else if (strcmp(argv[1], "deinit") == 0)
    {
        stk8321_deinit();
    }
    else if (strcmp(argv[1], "read") == 0)
    {
        if (strcmp(argv[2], "fifo") == 0)
        {
            int len = STK8321_Read_fifo_len();
            if (len == 0)
            {
                rt_kprintf("fifo is empty\n");
                return;
            }
            short int x_data[FIFO_FRAME_MAX_CNT], y_data[FIFO_FRAME_MAX_CNT], z_data[FIFO_FRAME_MAX_CNT];
            STK8321_Getfifo_data(x_data, y_data, z_data, len);
            for (int i = 0; i < len; i++)
            {
                rt_kprintf("fifo data[%d]: x = %d, y = %d, z = %d\n", i, x_data[i], y_data[i], z_data[i]);
            }
        }
        else if (strcmp(argv[2], "id") == 0)
        {
            rt_kprintf("chip id = 0x%x\n", stk8321_get_dev_id());
        }
        else if (strcmp(argv[2], "fifo_len") == 0)
        {
            rt_kprintf("fifo len = %d\n", STK8321_Read_fifo_len());
        }
        else if(strcmp(argv[2], "reg") == 0)
        {
            unsigned char reg_addr = atoi(argv[3])& 0xff;
            uint8_t reg_value = 0;
            ReadAccRegister((uint8_t)reg_addr, &reg_value);
            rt_kprintf("Read  0x%02X  value 0x%02X\n", (uint8_t)reg_addr, reg_value);
        }
    }
    else if(strcmp(argv[1], "write") == 0)
    {
        unsigned char reg_addr = atoi(argv[2])& 0xff;
        unsigned char reg_value = atoi(argv[3])& 0xff;
        WriteAccRegister((uint8_t)reg_addr, (uint8_t)reg_value);
        rt_kprintf("Write 0x%02X  value 0x%02X\n", (uint8_t)reg_addr, (uint8_t)reg_value);   
    }
}
MSH_CMD_EXPORT(STK8321_test, STK8321 test commands);
#endif

2. RT-Thread 传感器设备移植

本节将原生驱动封装为RT-Thread标准传感器设备,实现与系统框架的无缝对接。

2.1 配置与宏定义

在目录solution\components\sensor\sensor_algo\acc添加对应的器件目录“stk8321”,在“stk8321"目录下创建stk8321_gsensor_service.c、stk8321_gsensor_service.h文件,用于实现stk8321的注册为Sensor设备;

宏名称

类型

功能说明

取值/注意事项

STK8321_BUFF_SIZE

宏定义

FIFO数据缓冲区大小,限制单次读取的最大帧数

默认10,单位:帧

STK8321_PERIOD_TIMER

宏定义

数据读取周期,决定应用层数据刷新频率

默认200ms,单位:毫秒

ACC_USING_STK8321

条件编译宏

控制整个驱动模块的编译开关

需在menuconfig中定义以启用驱动

STK8321_USING_FIFO

条件编译宏

控制是否使用FIFO模式读取数据

定义后启用stk8321_read_all_fifo_data()批量读取

2.2 关键数据结构

定义设备私有数据结构体和全局变量。

struct stk8321_device {
    rt_device_t bus;
    uint8_t i2c_addr;
    uint8_t id;
    float acce_para;  
};

static struct stk8321_device *stk8321_dev = RT_NULL;  
static struct rt_sensor_device *stk8321_acce = RT_NULL;  

2.3 设备注册接口

将STK8321注册为RT-Thread标准传感器设备。

static int rt_hw_stk8321_register(const char *name)
{
    int ret;
    static char bus_name[] = STK8321_BUS_NAME;

    /* accelerometer sensor register */
    {
        stk8321_acce = rt_calloc(1, sizeof(struct rt_sensor_device));
        if (stk8321_acce == RT_NULL)
            return -1;

        stk8321_acce->info.type       = RT_SENSOR_CLASS_ACCE;
        stk8321_acce->info.vendor     = RT_SENSOR_VENDOR_STM;
        stk8321_acce->info.model      = NULL;
        stk8321_acce->info.unit       = RT_SENSOR_UNIT_MG;
        stk8321_acce->info.intf_type  = RT_SENSOR_INTF_I2C;
        stk8321_acce->info.range_max  = 256;
        stk8321_acce->info.range_min  = 0;
        stk8321_acce->info.period_min = STK8321_PERIOD_TIMER;
        stk8321_acce->info.fifo_max   = STK8321_BUFF_SIZE;
        stk8321_acce->data_buf          = RT_NULL;
        stk8321_acce->data_len          = 0;
        stk8321_acce->config.intf.dev_name = bus_name;
        stk8321_acce->config.irq_pin.pin   = RT_PIN_NONE;
        stk8321_acce->ops = &sensor_ops;

        ret = rt_hw_sensor_register(stk8321_acce, name, RT_DEVICE_FLAG_RDWR, RT_NULL);
        if (ret != RT_EOK)
        {
            rt_kprintf("GS [%s] register err code: %d\n", name, ret);
            goto __exit;
        }
    }

    rt_mutex_init(&stk8321_mutex, "stk8321_mutex", RT_IPC_FLAG_FIFO);
    rt_kprintf("GS [%s] register success!\n", name);
    return RT_EOK;

__exit:
    if (stk8321_acce)
    {
        rt_free(stk8321_acce);
        stk8321_acce = RT_NULL;
    }

    return -RT_ERROR;
}

/**
 * @brief  gsensor register, register name need unique, and longth is less than RT_NAME_MAX
 */
int stk8321_register(void)
{
#ifdef STK8321_MODEL_NAME
    static char  name[] = STK8321_MODEL_NAME;
#else
    static char  name[] = "stk8321";
#endif
    int ret = rt_hw_stk8321_register(name);
    return ret;
}

INIT_COMPONENT_EXPORT(stk8321_register);

INIT_COMPONENT_EXPORT(stk8321_register)是RT-Thread系统组件初始化命令,用于在系统启动时调用stk8321_register()完成设备的注册。

2.4 传感器接口实现

注册为Sensor设备必须完善接口,实现rt_sensor_ops结构体。

static struct rt_sensor_ops sensor_ops =
{
    stk8321_fetch_data,
    stk8321_control
};

2.4.1 数据获取接口

stk8321_fetch_data()用于服务层向驱动层获取数据。

static rt_size_t stk8321_fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
    if (!stk8321_dev || !buf)
    {
        ((gsensors_fifo_t *)buf)->num = 0;
        return 0;
    }

    return stk8321_polling_get_data(sensor, buf);

    return 0;
}

stk8321_polling_get_data()实现具体的数据读取逻辑:

static rt_size_t stk8321_polling_get_data(rt_sensor_t sensor, void *data)
{
    rt_size_t fifo_len = 0;

#if defined (STK8321_USING_FIFO)
#if defined(BSP_USE_PIN_MULTIPLEX)
    pin_multiplex_lock();
    set_pin_i2c5(MULTIPLEX_PIN);
#endif

    curtick = rt_tick_get_millisecond();
    fifo_len = STK8321_Read_fifo_len();

    if (fifo_len > STK8321_BUFF_SIZE)
    {
        fifo_len = STK8321_BUFF_SIZE;
    }
#ifdef STK8321_USE_DMA
    stk8321_i2c_reopen_DMA();
#endif

    stk8321_read_all_fifo_data(data, fifo_len);
    if (RT_ERROR == gsensor_start_stk8321(data))
    {
#if defined(BSP_USE_PIN_MULTIPLEX)
        pin_multiplex_unlock();
#endif
        return 0;
    }
#ifdef STK8321_USE_DMA
    stk8321_i2c_reopen_normal();
#endif
    STK8321_fifo_mode_set(STK_BYPASS_MODE_VAL);
    STK8321_fifo_mode_set(STK_FIFO_MODE_VAL);

#if defined(BSP_USE_PIN_MULTIPLEX)
    pin_multiplex_unlock();
#endif
    pretick = curtick;
#endif
    return fifo_len;
}

2.4.2 设备控制接口

stk8321_control()用于服务层对驱动层的控制。

static rt_err_t stk8321_control(struct rt_sensor_device *sensor, int cmd, void *args)
{
    rt_err_t ret = RT_EOK;

    switch (cmd)
    {
    case RT_SENSOR_CTRL_GET_ID:
    {
        sensor_reg_info_t *info = (sensor_reg_info_t *)args;

        if (!stk8321_dev)
        {
            info->sensor_id = 0;
            return RT_ERROR;
        }
        info->sensor_id = stk8321_dev->id;
        info->dev_period = STK8321_PERIOD_TIMER;
        info->fifo_len = STK8321_BUFF_SIZE;
        info->gs_acce_para = stk8321_dev->acce_para;
        info->gs_algo_ops = RT_NULL;        /*stk8321 driver only supply raw data */
    }
    break;
    case RT_SENSOR_CTRL_SET_RANGE:
        if (!stk8321_dev) return RT_ERROR;
        ret = stk8321_set_range(sensor, (rt_int32_t)args);
        break;
    case RT_SENSOR_CTRL_SET_ODR:
        ret = -RT_EINVAL;
        break;
    case RT_SENSOR_CTRL_SET_MODE:
        ret = stk8321_acc_set_mode(sensor, (rt_uint32_t)args & 0xff);
        break;
    case RT_SENSOR_CTRL_SET_POWER:
        ret = stk8321_set_power(sensor, (rt_uint32_t)args & 0xff);
        break;
    case RT_SENSOR_CTRL_SELF_TEST:
        if (!stk8321_dev) return RT_ERROR;
        ret = stk8321_self_test(sensor, *((rt_uint8_t *)args));
        break;
    default:
        return -RT_ERROR;
    }
    return ret;
}

2.4.3 电源控制实现

stk8321_set_power()实现对传感器电源状态的控制。

static rt_err_t stk8321_set_power(rt_sensor_t sensor, rt_uint8_t power)
{
    rt_err_t ret = RT_EOK;

    switch (power)
    {
    case RT_SENSOR_POWER_DOWN:
        stk8321_gsensor_close();
        stk8321_power_onoff(0);//power disable
        break;
    case RT_SENSOR_POWER_NORMAL:
        stk8321_power_onoff(1);//power enable
#if defined(BSP_USE_PIN_MULTIPLEX)
        pin_multiplex_lock();
        set_pin_i2c5(MULTIPLEX_PIN);//get lock, if I2C pin is multiplex
#endif
        ret = stk8321_gsensor_open();
        if (ret != RT_EOK)
            stk8321_power_onoff(0);//power off
#if defined(BSP_USE_PIN_MULTIPLEX)
        pin_multiplex_unlock();     //unlock
#endif
        break;
    case RT_SENSOR_POWER_LOW:
        // enable fifo mode?
        break;
    case RT_SENSOR_POWER_HIGH:
        break;
    default:
        ;
    }
    return ret;
}

2.5 设备初始化函数

stk8321_gsensor_open()作为STK8321加速度计设备的初始化入口,完成硬件唤醒、资源分配和参数配置,使传感器进入可工作状态。

static rt_err_t stk8321_gsensor_open(void)
{
    rt_err_t ret = RT_EOK;
    int init_result = 0;
    rt_kprintf("gs open start..... ! \n");

    if (!stk8321_dev)
    {
        rt_mutex_take(&stk8321_mutex, RT_WAITING_FOREVER);
        init_result = stk8321_init();
        rt_mutex_release(&stk8321_mutex);
        if (init_result == 0)
        {
            stk8321_dev = rt_calloc(1, sizeof(struct stk8321_device));
            if (stk8321_dev == RT_NULL)
            {
                ret = RT_ENOMEM;
                goto err;
            }
            stk8321_dev->bus = (rt_device_t)stk8321_get_bus_handle();
            stk8321_dev->i2c_addr = stk8321_get_dev_addr();
            stk8321_dev->id = stk8321_get_dev_id();
            stk8321_dev->acce_para = (9.807f) / (float)stk8321_acce->info.range_max;
            curtick = rt_tick_get_millisecond();
            pretick = curtick;
            rt_kprintf("gs(%s) open finish ok..... ! \n", stk8321_acce->parent.parent.name);
            return RT_EOK;
        }

        ret = RT_ERROR;
err:
        rt_kprintf("gsensors_open init err! code: %d\n", ret);
    }

    return ret;

}

acce_para需根据传感器量程与当前重力加速度G计算。 例:量程±4g、12 bit有效数据时,range_max = 2¹² / 8 = 512。


3.RT-Thread 算法设备移植

算法负责原始数据处理(如滤波、特征提取)。 本节实现将算法集成到RT-Thread设备驱动框架上,封装为"算法设备",负责原始数据处理(如滤波、特征提取)。以移植cywee算法为例。

3.1 配置与宏定义

在 sdk\customer\peripherals\sensor目录下创建以cywee_lib命名的目录,添加算法的lib文件,添加驱动算法设备的cwm_motion.c。

说明

默认值

PKG_USING_CYWEE_ALGO

整包开启

n

CYWEE_ALGO_USING_FIFO_MODE

使用 FIFO 批处理

y

CYWEE_ALGO_FIFO_FRAME_MAX

FIFO 最大帧数

10

CYWEE_ALGO_SAMPLE_PERIOD_MS

单次 process 周期

20

CYWEE_ALGO_SYSTICK_PERIOD_MS

1 s 时间片

1000

CYWEE_ALGO_SPORT_INFO_PERIOD_MS

运动信息上报周期

1000

CYWEE_ALGO_HISTORY_1M_PERIOD_MS

历史存储周期

60000

CYWEE_ALGO_USING_GYRO

是否接入陀螺仪

n

CYWEE_ALGO_USING_TEST_LOG

打开算法调试串口

n

3.2 关键数据结构

typedef struct {
    uint8_t  sensorType;   
    float    fData[16];   
} SensorEVT_t, *pSensorEVT_t;

typedef struct {
    float acce_data[3];   
#ifdef USING_GYRO_SENSOR
    float gyro_data[3];    
#endif
} gsensors_data_t;

typedef struct {
    gsensors_data_t *buf;  
    uint16_t         num; 
    uint64_t         time_ns; 
} gsensors_fifo_t;

3.3 设备注册接口

static int rt_hw_cywee_algo_register(const char *name, struct rt_sensor_config *cfg)
{
    /* magnetometer/compass sensor register */
    cywee_algo = rt_calloc(1, sizeof(struct rt_sensor_device));
    RT_ASSERT(cywee_algo)

    cywee_algo->info.type       = RT_SENSOR_CLASS_ACCE;
    cywee_algo->info.vendor     = RT_SENSOR_VENDOR_UNKNOWN;
    cywee_algo->info.model      = "cywee_algo";
    cywee_algo->info.unit       = RT_SENSOR_UNIT_NONE;
    cywee_algo->info.intf_type  = 0;
    cywee_algo->info.range_max  = 220;
    cywee_algo->info.range_min  = 1;
    cywee_algo->info.period_min = 1;
    cywee_algo->info.fifo_max     = 1;
    cywee_algo->data_buf        = RT_NULL;
    cywee_algo->data_len            = 0;        //must be 0, because don't use data buf

    rt_memcpy(&cywee_algo->config, cfg, sizeof(struct rt_sensor_config));
    cywee_algo->ops = &sensor_ops;

    rt_err_t result = rt_hw_sensor_register(cywee_algo, name, RT_DEVICE_FLAG_RDWR, RT_NULL);
    if (result != RT_EOK)
    {
        rt_kprintf("device register err code: %d", result);
        goto __exit;
    }

    rt_kprintf("GS algo[%s] init success!\n", name);

    return RT_EOK;

__exit:
    if (cywee_algo)
    {
        rt_free(cywee_algo);
        cywee_algo = RT_NULL;
    }
    return -RT_ERROR;
}
/**
 * @brief gsensor algo  register, register name need unique, and longth is less than RT_NAME_MAX
*/

static int cyewee_algo_register(void)
{
    //register cywee algo to gsensor algo.
    char name[RT_NAME_MAX] = "a_cywee";
    //all pin will be configed in drv.
    struct rt_sensor_config cfg = {0};
    cfg.irq_pin.pin = RT_PIN_NONE; //disable pin operation of sensor_close
    rt_hw_cywee_algo_register(name, &cfg);
    return 0;
}
INIT_COMPONENT_EXPORT(cyewee_algo_register);

3.4 传感器接口实现

注册为算法设备设备必须完善接口,实现rt_sensor_ops结构体。

static struct rt_sensor_ops sensor_ops =
{
    cywee_algo_fetch_data,
    cywee_algo_control
};

3.4.1 数据获取接口

static rt_size_t cywee_algo_fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
    return 0;
}

:算法设备不通过数据获取接口获取数据,而是通过回调函数直接处理。

3.4.2 设备控制接口

static rt_err_t cywee_algo_control(struct rt_sensor_device *sensor, int cmd, void *args)
{
    rt_err_t result = RT_EOK;

    //VC32S_LOG_I("hr cmd 0x%x\n", cmd);

    switch (cmd)
    {
    case RT_SENSOR_CTRL_GET_ID:
    {
        sensor_reg_info_t *info = args;
        info->sensor_id = 0xff;
        cyewee_algo_init();
        info->gs_para_ops = gsensor_para_process;
        info->gs_algo_ops = gsensor_algo_process;
        rt_kprintf("algo_id	: %d ! \n", info->sensor_id);
        break;
    }
    case RT_SENSOR_CTRL_SET_RANGE:
        result = RT_ERROR;
        break;
    case RT_SENSOR_CTRL_SET_ODR:
        result = RT_ERROR;
        break;
    case RT_SENSOR_CTRL_SET_MODE:
        result = RT_ERROR;
        break;
    case RT_SENSOR_CTRL_SET_POWER:
        result = RT_ERROR;
        break;
    case RT_SENSOR_CTRL_SELF_TEST:
        result = RT_ERROR;
        break;
    default:
        return -RT_ERROR;
    }

    return result;
}

:在这个函数中将gsensor_para_process()gsensor_algo_process()传递给上层的sensor_reg_info_t中的函数指针上,由上层的gsensor_algo进行统一调用。

3.5 算法处理函数

3.5.1 配置入口

用于处理上层下发的配置信息(用户修改设置、时间、运动模式等)

static void gsensor_para_process(uint16_t msg_id, void *data)
{
    switch (msg_id)
    {
    case LCPU_TIME_SET:
    {
        if (g_cwm_inited)
            cwm_set_date_time();
        break;
    }

    case APP_SENSOR_SETTING_USER_INFO:
    {
        sensor_user_info = *((user_info_t *) data);
        if (g_cwm_inited)
            cwm_set_user_info();
        break;
    }
    case APP_SENSOR_SETTING_SPORT_TARGET:
    {
        sensor_sport_target = *((sport_target_t *) data);
        break;
    }
    case APP_SENSOR_SETTING_SEDENTARY:
    {
        sensor_sedentary = *((sedentary_info_t *) data);
        time_data_t *cur_time = service_current_time_get(false);

        if (sensor_sedentary.sedentary_enable)
        {
            if (g_cwm_inited)
            {
                cwm_enable_seden(cur_time);
            }
        }
        else
        {
            cwm_disable_seden();
        }
        break;
    }
    case APP_SENSOR_SETTING_GESTURE_DISPLAY:
    {
        sensor_gesture = *((gesture_info_t *) data);
        if (g_cwm_inited)
            cwm_enable_gesture();
        break;
    }
    case APP_SENSOR_SETTING_SLEEP_TIME:
    {
        sensor_sleep_time = *((sleep_time_t *)data);
        break;
    }
    case APP_SENSOR_SETTING_SPORT_TIME:
    {
        sensor_sport_peroid = *((uint32_t *)data);
#ifdef ALGO_USING_TEST
        rt_kprintf("set sensor_sport_peroid is %d ! \n", sensor_sport_peroid);
#endif
        break;
    }
    case APP_SENSOR_ACTIVE_GET_SLEEP_DATA:
    {
        if (CWM_WAKEUP_NORMAL == g_wakeup_type) // active get sleep data only in normal sleep satuts
            cwm_get_sleep_data_cmd(0);
        break;
    }
    case APP_SENSOR_SPORT_MODE:
    {
        sport_mode_t *sport_mode = (sport_mode_t *)data;
        flag_sporting = sport_mode->flag_sporting;
        if (flag_sporting)
        {
            g_pedo_info.sport_type = sport_mode->sport_type;
            cwm_set_activity_mode(sport_mode->mode);
        }
        else/*default*/
        {
            g_pedo_info.sport_type = 0;
            cwm_set_activity_mode(ACTIVITY_MODE_NORMAL);
        }

        break;
    }
    default:
        ;
    }
}

3.5.2 数据入口

把原始加速度帧塞进算法,产出步数、睡眠、手势等事件

static void gsensor_algo_process(uint32_t peroid, gsensors_fifo_t *fifo)
{
    static uint32_t interval_1s = 0, interval_1m = 0;
    step_info_t  step_info;
    remind_event_t sede_event = REMIND_NULL;
    remind_event_t hand_event = REMIND_NULL;
    rt_memset(&step_info, 0, sizeof(step_info_t));
    //when EXT CYWEE lib was placed in HCPU, buffer it and send the data_sets to hcpu for every interval

    fifo->time_ns = CWM_OS_GetTimeNs();

    //for every gsensor sample peroid, call cywee algo
    cwm_all_fifo_data_input((void *)fifo->buf, fifo->num, fifo->time_ns);
    CWM_process();

    //deal the result of cwm_algo_process
    if (g_gesture_info.gesture > 0)
    {
        if (g_gesture_info.gesture == 1)
        {
            hand_event = REMIND_RAISE_HAND;
        }
        else if (g_gesture_info.gesture == 2)
        {
            hand_event = REMIND_DOWN_HAND;
        }

        g_gesture_info.gesture = 0;
    }

    interval_1s += peroid;
    interval_1m += peroid;
    if (interval_1s >= 1000) // 1s
    {
        interval_1s = 0;
        CWM_systickUpdate();

        if (interval_1m == 60000) //1min
        {
            interval_1m = 0;
            CWM_process();
            cwm_sleeping_seden_algo();

            //sedentary remind
            if (g_seden_info.trigger_type >= 1 || g_seden_info.trigger_type == 3)
            {
                sede_event = REMIND_SEDENTARY;
                g_seden_info.trigger_type = 0;
            }
        }
    }//1s

    //get user information to calculate calories and distance
    gsensor_pedo_main_info_t *main_info = &g_pedo_info.main_info;
    step_info.step = main_info->step;
    step_info.distance = main_info->distance;
    step_info.calories = main_info->cal;
    step_info.active_type = main_info->active_type;
    step_info.step_frequency = main_info->step_frequency;
    step_info.step_length = main_info->step_length;
    step_info.pace = main_info->pace;
    //carry out post-processing according to gsensor algorithm results, save relevant results,
    //and notify HCPU if triggered remind event

    gsensor_meas_result_postprocess(&step_info, sede_event, hand_event, peroid);
}

4.运动传感器调度模块

运动传感器调度模块是 Solution 运动传感器系统的核心,负责将器件采集的原始数据(如 PPG 信号、加速度值)转换为业务可用数据(如心率、计步数)。

fishy

4.1 器件与算法关联

4.1.1 核心接口说明

sensor_reg_open 是 Solution 基于 RT-Thread 扩展的核心接口,负责在系统初始化阶段“连接”已注册的器件设备与算法设备,构建“硬件采集→算法处理”的数据链路,是传感器从“注册态”进入“可用态”的关键步骤。

主注册接口

rt_err_t sensor_reg_open(
    const char *sensor_name,     // 传感器设备名(如"acce_stk8321")
    const char *algo_name,       // 算法设备名(如"acce_a_cywee")
    sensor_reg_info_t *sensor_info, // 输出:传感器+算法信息
    rt_device_t *sensor_device   // 输出:传感器设备句柄
);

功能:完成传感器设备与算法设备的查找、验证与绑定。

数据结构定义

// 算法信息载体(在sensor_reg.h中定义)
typedef struct {
    sensor_type_t type;          // 传感器类型(SENSOR_GSENSOR)
    uint32_t sensor_id;          // 传感器芯片ID
    uint32_t dev_period;         // 采样周期(ms)
    uint8_t fifo_len;            // FIFO深度
    
    // 算法函数指针(由算法设备注入)
    void (*gs_algo_ops)(uint32_t period, void *data);  // 主算法入口
    void (*gs_para_ops)(uint16_t msg_id, uint8_t *data); // 参数配置接口
    float gs_acce_para;          // 加速度量程参数
} sensor_reg_info_t;

// 传感器数据FIFO
typedef struct {
    uint8_t num;                 // 有效数据点数
    sensor_data_t buf[FIFO_LEN]; // 原始数据数组
} gsensors_fifo_t;

4.1.2 应用示例

  1. 修改gsensor_algo.c 中的宏定义,修改GSENSOR_NAME为指定的"stk8321",修改G_ALGO_NAME为指定的"a_cywee"

fishy
  1. 初始化绑定设备,通过sensor_reg_open()分别查找并验证传感器设备(acce_stk8321)与算法设备(acce_a_cywee),将算法模块向sensor_reg_info_t结构体注入其处理函数指针(gs_algo_ops);此后数据流处理时,调度器直接通过该函数指针调用算法。

static rt_err_t gsensor_init(void)
{
    gsensor_device = RT_NULL;
    sensor_info.type = SENSOR_GSENSOR;
    char *gsensor_name = "acce_"G_SENSOR_NAME;
    char *gs_algo_name = "acce_"G_ALGO_NAME;

    if (RT_EOK != sensor_reg_open(gsensor_name, gs_algo_name, &sensor_info, &gsensor_device))
    {
        return RT_ERROR;
    }

#if defined (USING_GSENSOR_DATA_STORE_LIST)
    rt_list_init(&gsensor_data_store_list);
#endif
    gsensor_id = sensor_info.sensor_id;

    if (sensor_info.dev_period)
        gsensor_period  = sensor_info.dev_period;

    if (sensor_info.fifo_len)
        gsensor_fifo_len = sensor_info.fifo_len;

    gsensor_acce_para   = sensor_info.gs_acce_para;
    LOG_I("%s: type = %d, period = %d, fifo_len = %d, gsensor_acce_para = %f;\n", __func__, SENSOR_GSENSOR,  gsensor_period, gsensor_fifo_len, gsensor_acce_para);
    return RT_EOK;
}
  1. 传感器设备与算法设备成功绑定,此时可以通过rt_device_read()直接读取处理经过算法处理后的传感器数据。

static int32_t gsensor_data_fetch(uint32_t data_size, void *data)
{
    if (!gsensor_device || !data) return RT_ERROR;
    rt_size_t size = rt_device_read(gsensor_device, 0, data, 1);
    if (0 == size) LOG_W("%s: err!!!", __func__);
    return RT_EOK;
}

4.2 模块调度

4.2.1 基础概念

Solution 基于 RT-Thread 软件定时器 实现传感器的自动化调度,替代原生轮询机制,可灵活配置采样周期,同时适配低功耗场景(支持动态调整周期)。该调度器是 “器件采集→算法处理→数据上报” 全流程的触发核心。 alt text

4.2.2 示例代码

void gsensor_algo_scheduler(uint32_t period)
{
    static uint16_t gs_period_time = 0;
    if (!gsensor_is_opened())
        return;
    gs_period_time += period;
    if (gs_period_time < gsensor_period)
        return;
    gs_period_time = 0;

    //apply the fifo memory of gsensor, and obtain data from the driver
#ifndef SENSOR_IN_HCPU
    gsensor_data_fetch(0, (void *)&gsensor_data);

    /*save gsensor data for other sensor using*/
#if defined (USING_GSENSOR_DATA_STORE_LIST)
    gsensor_data_store(&gsensor_data);
#endif
#endif

#ifdef GS_ALGO_DEBUG
    rt_kprintf("gsensor_algo_scheduler: data_len: %d, tick %d \n", gsensor_data.num, rt_tick_get_millisecond());
    rt_kprintf("gs:x=%f, y=%f, z=%f;\n", gsensor_data.buf[0].acce_data[0], gsensor_data.buf[0].acce_data[1], gsensor_data.buf[0].acce_data[2]);
#endif
    RT_ASSERT(gsensor_data.num <= gsensor_fifo_len);

    //factory mode, read the data of gsensor and return. Note: this data has been processed by GSENSOR_ACCE_PARA
    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));

    //each gsensor measurement period, calls the gsensor algorithm for response processing with raw data inputing
    if ((gsensor_data.num > 0) && (sensor_info.gs_algo_ops))
    {
        sensor_info.gs_algo_ops(period, &gsensor_data);
    }
}

:该函数由sensor_timer定时器定时调用,实现了数据的采集、处理、上报全流程