Sensor应用指南

1. 介绍

Solution 的传感器管理系统完全基于 RT-Thread Sensor 架构 构建,复用其标准化的设备抽象与接口设计,降低跨硬件适配成本。开发者可参考 RT-Thread 官方文档深入学习核心框架(RT-Thread Sensor 架构官网介绍),本章节先对关键概念进行说明。

1.1 核心概念说明

概念

定义与作用

Sensor 设备抽象

RT-Thread 将所有传感器统一抽象为「设备对象」,通过 struct rt_sensor_device 结构体描述,包含设备类型、操作接口、私有数据等核心信息,实现“硬件无关”的上层调用。

操作接口集

struct rt_sensor_ops,定义传感器的核心操作(如初始化、数据读取、参数控制),是驱动层与上层交互的桥梁,所有传感器驱动需实现统一接口。

设备注册机制

通过 rt_hw_sensor_register() 函数将传感器设备注册到 RT-Thread 内核,注册后可通过设备名(如“gsensor”)被上层发现和调用。

设备注册机制

RT-Thread 定义 enum rt_sensor_class_type 标识传感器类型(如加速度计、心率传感器),Solution 直接复用该枚举实现类型管理。

1.2 代码位置

  1. sensor原厂驱动:\sdk\customer\peripherals\sensor

  2. 传感器周期性调度:solution\components\sensor\sensor_algo\timer

  3. 传感器打开(sensor_reg_open):solution\components\sensor\sensor_algo\reg

  4. 算法调度框架以及驱动适配

    • accelerometer: solution\components\sensor\sensor_algo\acc

    • 地磁: solution\components\sensor\sensor_algo\mag

    • 光感:solution\components\sensor\sensor_algo\asl

    • 心率:solution\components\sensor\sensor_algo\hr

    • gps:solution\components\sensor\sensor_algo\gps

  5. IPC service:solution\components\sensor\sensor_service

2. Solution 适配的传感器型号

Solution 已完成以下几类主流传感器的驱动适配,开发者可直接调用或基于现有驱动扩展:

传感器类别

适配型号列表

核心应用场景

Gsensor(运动传感器)

STK8321、STK8328C、LSM6DSL_STdC

计步、姿态识别、运动状态检测

HR(心率传感器)

VC32S、VC9202、HRS3300

实时心率监测、健康数据采集

MAG(地磁传感器)

MMC56X3、QMC6310

方向定位、电子罗盘

光感传感器

LTR303

环境光强检测、屏幕亮度自适应

3. Solution 传感器管理思路

基于 RT-Thread 基础架构,Solution 设计了“注册-打开-调度-算法处理-数据上报”的全流程管理逻辑。

3.1 传感器注册(rt_hw_sensor_register)—— 器件与算法分离注册

为实现硬件与算法的解耦,Solution 采用“器件驱动”与“算法模块”分别注册的机制,二者通过设备名关联调用。

  • 接口:rt_hw_sensor_register(struct rt_sensor_device *sensor, const char *name, rt_uint32_t flag, void *data)

    接口定义在sdk\rtos\rtthread\components\drivers\sensors\sensor.c

参数说明

关键参数

说明

sensor

传感器设备结构体(需填充类型、操作接口、私有数据等)

name

器件设备名(如“stk8321”,需唯一,用于设备查找与识别)

flag

设备标志(如 RT_DEVICE_FLAG_RDWR 表示支持读写操作,控制设备访问权限)

3.1.1 器件驱动注册

负责硬件控制(如 I2C 通讯、中断处理),注册为“器件设备”:

  • 示例

     // STK8321 加速度计器件注册
     static struct rt_sensor_ops sensor_ops =
     {
         stk8321_fetch_data,
         stk8321_control
     };
    
     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_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)
     {
         static char  name[] = "stk8321";
         int ret = rt_hw_stk8321_register(name);
         return ret;
     }
    
     INIT_COMPONENT_EXPORT(stk8321_register);
    

    具体参见:solution\components\sensor\sensor_algo\acc\stk8321\stk8321_gsensor_service.c

3.1.2 算法模块注册

负责原始数据处理(如滤波、特征提取),注册为“算法设备”:

  • 示例逻辑:

     // Gsensor 算法模块注册
     /**
      * @brief gsensor ops for register;
    
      */
     static struct rt_sensor_ops sensor_ops =
     {
         cywee_algo_fetch_data,
         cywee_algo_control
     };
     /**
      * @brief gsensor algo  register;
      * @param const char *name  algo name;
      * @param struct rt_sensor_config *cfg  sensor config include
          struct rt_sensor_intf        intf;      // sensor interface config
         struct rt_device_pin_mode    irq_pin;   // Interrupt pin, The purpose of this pin is to notification read data
         rt_uint8_t                   mode;      //sensor work mode
         rt_uint8_t                   power;     // sensor power mode
         rt_uint16_t                  odr;       /// sensor out data rate
         rt_int32_t                   range;     // sensor range of measurement
     */
    
     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.2 传感器打开(sensor_reg_open)—— 建立器件与算法关联

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

3.2.1 核心功能与设计逻辑

该接口通过传感器类型作为匹配依据,自动完成器件与算法的绑定,无需开发者手动关联,核心逻辑分为4步:

  1. 按传感器类型(如加速度计、心率传感器)查找已注册的器件设备

  2. 按“类型+固定后缀”的规则拼接算法设备名,查找已注册的算法设备

  3. 将器件设备句柄存入算法设备的私有数据区,实现二者关联;

  4. 调用 rt_device_open 打开器件与算法设备,使其进入可操作状态。

3.2.2 接口sensor_reg_open

rt_err_t sensor_reg_open(
    const char *sensor_name,          // 传感器器件名称
    const char *algo_name,            // 算法模块名称
    sensor_reg_info_t *sensor_info,   // 传感器注册信息结构体
    rt_device_t *sensor_device        // 输出:传感器设备句柄
);

参数说明

参数名

类型

描述

sensor_name

const char *

传感器器件名称(如 “stk8321”、“vc32s”),为 NULL 时不执行器件打开操作

algo_name

const char *

算法模块名称(如 “gsensor_algo”、“hr_algo”),为 NULL 时不执行算法关联操作

sensor_info

sensor_reg_info_t*

传感器注册信息结构体,用于存储传感器类型、算法操作接口等关键数据,属于输入输出参数

sensor_device

rt_device_t *

输出参数,函数执行成功后返回打开的传感器设备句柄,后续可通过该句柄调用器件操作接口

返回值说明

返回值

含义说明

RT_EOK

传感器器件打开、算法关联等所有操作均成功完成

RT_ERROR

传感器器件打开失败或算法关联失败

接口定义在:solution\components\sensor\sensor_algo\reg\sensor_reg.c

3.2.3 调用示例

在系统初始化函数中,按传感器类型批量调用 sensor_reg_open 即可激活设备,建立器件与算法的关联链路:

   /**
   * @brief gsensor init, including: open device ,algo, updata info ,
   */
   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;
       return RT_EOK;
   }
   

3.3 传感器周期性调度(sensor_timer)

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

具体参见:solution\components\sensor\sensor_algo\timer\sensor_timer.c

3.3.1 流程

  • 创建定时器:初始化周期性软定时器,绑定回调 sensor_timer_handle,加载所有sensor采集的最小周期。

  • 周期触发回调:定时器按间隔触发回调,发送消息给 LCPU 线程。

  • 执行算法调度:LCPU 线程接收消息,触发 sensor_timeout_cb,调用各传感器算法(如计步、心率计算)。

    • 按传感器类型(加速度计、心率、地磁、光感等)的周期,调用相应的算法;

    • 算法的输出结果分为event和data,通过 IPC 将处理后的数据上报至应用层。

3.3.2 52x 平台专属优化

为避免高优先级的任务干扰数据读取,额外增加:

  • 初始化采集线程:创建与高优先级的 sensor_data_thread 及事件对象。

  • 事件触发采集:定时器回调同时发送事件,线程收到信号后立即读取传感器原始数据,确保数据准确。

3.4 算法模块(sensor_algo)—— 按传感器类型实现数据处理

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

各类型传感器算法实现:

传感器类别

算法模块文件

适配器件型号

加速度计(Gsensor)

gsensor_algo.c

Solution适配cywee算法STK8321、STK8328C、LSM6DSL_STdC

心率(HR)

hr_algo.c

VC32S、VC9202、HRS3300

地磁(MAG)

mag_algo.c

MMC56X3、QMC6310

光感

asl_algo.c

LTR303

注意:Solution 适配了 cywee 算法,但集成的版本为 cywee 算法测试版,存在“数小时后算法自动重置”的限制。若需正式使用 cywee 算法,请联系 cywee 官方获取授权版本。

4. 如何新增一个传感器

  1. sdk\customer\peripherals\sensor 目录下创建以传感器名命名的目录,添加原生驱动。

  2. 修改sdk\customer\peripherals\sensor对应传感器类型的目录下kconfig文件,新建宏来实现驱动的控制。

  3. solution\components\sensor\sensor_algo 目录下,根据传感器类型选择对应类型目录,新建该传感器名的子目录,参照已有目录完成驱动适配。

  4. 若涉及新的 gsensor 算法,参照 cywee_lib\cwm_motion.c 进行适配(通过 rt_hw_sensor_register 注册算法)。

  5. 根据传感器算法调度周期,确认是否修改 sensor_timer.c 中的 SENSOR_TIMER_PERIOD 及计时,并在 sensor_timeout_cbxxx_algo_scheduler 中完成进一步适配。

4.1 添加心率驱动

以下以vc32s心率为例,实现快速添加驱动如下:

  1. 新建vcs32s目录,存放原厂的驱动,修改kconfig以及SConscript文件

  2. 驱动文件适配电源控制,参考原理图配置menuconfig的电源pin,并实现对应功能;

void vc32s_hw_power_onoff(bool  onoff)
{

    if (onoff)
    {
#if defined(PMIC_CONTROL_SERVICE)
        pmic_service_control(PMIC_CONTROL_HR, 1);
#endif
#if(VC32S_POW_PIN >= 0)
        rt_pin_mode(VC32S_POW_PIN, PIN_MODE_OUTPUT);
        rt_pin_write(VC32S_POW_PIN, PIN_HIGH);
#endif
#if(VC32S_VDDIO_POW_PIN>=0)
        rt_pin_mode(VC32S_VDDIO_POW_PIN, PIN_MODE_OUTPUT);
        rt_pin_write(VC32S_VDDIO_POW_PIN, PIN_HIGH);
#endif

        LOG_I("hr_power_on\n");
    }
    else
    {
#if defined(PMIC_CONTROL_SERVICE)
        pmic_service_control(PMIC_CONTROL_HR, 0);
#endif
#if(VC32S_POW_PIN >= 0)
        rt_pin_mode(VC32S_POW_PIN, PIN_MODE_OUTPUT);
        rt_pin_write(VC32S_POW_PIN, PIN_LOW);
#endif
#if(VC32S_VDDIO_POW_PIN>=0)
        rt_pin_mode(VC32S_VDDIO_POW_PIN, PIN_MODE_OUTPUT);
        rt_pin_write(VC32S_VDDIO_POW_PIN, PIN_LOW);
#endif


        LOG_I("hr_power_off\n");
    }
}
  1. VC32S要求使用中断,需要配置中断pin,以及线程。

static void vc32s_irq_init(void)
{
    rt_pin_mode(VC32S_INT_BIT, PIN_MODE_INPUT);
    rt_pin_irq_enable(VC32S_INT_BIT, 0);



#ifdef BSP_USING_PM // add v32s INT WAKE
    GPIO_TypeDef *gpio = GET_GPIO_INSTANCE(VC32S_INT_BIT);
    uint16_t gpio_pin = GET_GPIOx_PIN(VC32S_INT_BIT);

    int8_t wakeup_pin = HAL_LPAON_QueryWakeupPin(gpio, gpio_pin);
    RT_ASSERT(wakeup_pin >= 0);
    pm_enable_pin_wakeup(wakeup_pin, AON_PIN_MODE_POS_EDGE);
#endif /* BSP_USING_PM */
}

static void vc32s_irq_deinit(void)
{
#ifdef BSP_USING_PM
    GPIO_TypeDef *gpio = GET_GPIO_INSTANCE(VC32S_INT_BIT);
    int8_t gpio_pin = GET_GPIOx_PIN(VC32S_INT_BIT);

    int8_t wakeup_pin = HAL_LPAON_QueryWakeupPin(gpio, gpio_pin);
    RT_ASSERT(wakeup_pin >= 0);
    pm_disable_pin_wakeup(wakeup_pin);

#endif /* BSP_USING_PM */
    rt_pin_irq_enable(VC32S_INT_BIT, 0);
    rt_pin_detach_irq(VC32S_INT_BIT);
}

中断pin除了配置为中断外,还需要配置为唤醒源,

#ifdef BSP_USING_PM // add v32s INT WAKE
    GPIO_TypeDef *gpio = GET_GPIO_INSTANCE(VC32S_INT_BIT);
    uint16_t gpio_pin = GET_GPIOx_PIN(VC32S_INT_BIT);

    int8_t wakeup_pin = HAL_LPAON_QueryWakeupPin(gpio, gpio_pin);
    RT_ASSERT(wakeup_pin >= 0);
    pm_enable_pin_wakeup(wakeup_pin, AON_PIN_MODE_POS_EDGE);
#endif /* BSP_USING_PM */
  1. 通讯接口配置,vc32s使用的I2C2,需要配置menuconfig使能I2C2 ,并且配置I2C 初始化,id检查, i2c_write, i2c_read等。

  • I2C初始化参考

rt_err_t vc32s_hw_init(void)
{
    rt_uint16_t flag_open = RT_DEVICE_FLAG_RDWR
#ifdef VC32S_USE_DMA
                            | RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX
#endif
                            ;

    vc32s_i2cbus = (struct rt_i2c_bus_device *)rt_device_find(VC32S_I2C_BUS);
    if (RT_Device_Class_I2CBUS != vc32s_i2cbus->parent.type)
    {
        vc32s_i2cbus = NULL;
    }

    if (vc32s_i2cbus)
    {
        rt_device_open((rt_device_t)vc32s_i2cbus, flag_open);
#ifdef VC32S_USE_DMA
        LOG_I("HR vc32s i2c use DMA ! \n");
#endif
        LOG_I("find bus %s;\n", VC32S_I2C_BUS);
    }
    else
    {
        LOG_I("bus not find\n");
        return RT_ERROR;
    }

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

        rt_i2c_configure(vc32s_i2cbus, &configuration);
    }

    if (RT_EOK != vc32s_init())
    {
        vc32s_hw_deinit();
        return RT_ERROR;
    }

    return RT_EOK;
}
  • id检查

static rt_err_t vc32s_init(void)
{
    uint8_t chip_id = 0;

    vc32sReadRegisters_ex(0x00, &chip_id, 1);
    LOG_I("hr_vc32s_init chip_id =0x%x(should be 0x21)\n", chip_id);

    if (chip_id == VC32S_ID)
    {
        vc32s_id = chip_id;
        return RT_EOK;
    }

    return RT_ERROR;
}
  • i2c write

static rt_err_t vc32_i2c_base_write(rt_uint8_t *buf, rt_uint16_t len)
{
    rt_int8_t res = 0;
    struct rt_i2c_msg msgs;

    msgs.addr  = VC32S_SLAVE_ADDRESS;    /* slave address */
    msgs.flags = RT_I2C_WR;              /* write flag */
    msgs.buf   = buf;                    /* Send data pointer */
    msgs.len   = len;

    if (rt_i2c_transfer(vc32s_i2cbus, &msgs, 1) == 1)
    {
        res = RT_EOK;
    }
    else
    {
        res = -RT_ERROR;
    }
    return res;
}
  • i2c read

rt_err_t vc32s_i2c_read(unsigned char regAddr,
                        unsigned char len,
                        unsigned char *data)
{
    rt_int8_t res = 0;
    rt_uint8_t buf[2];
    buf[0] = regAddr;  //cmd
    struct rt_i2c_msg msgs[2];
    msgs[0].addr  = VC32S_SLAVE_ADDRESS;    /* Slave address */
    msgs[0].flags = RT_I2C_WR;              /* Read flag */
    msgs[0].buf   = buf;                    /* Read data pointer */
    msgs[0].len   = 1;                      /* Number of bytes read */

    msgs[1].addr  = VC32S_SLAVE_ADDRESS;    /* Slave address */
    msgs[1].flags = RT_I2C_RD;              /* Read flag */
    msgs[1].buf   = data;                   /* Read data pointer */
    msgs[1].len   = len;                    /* Number of bytes read */

    //LOG_I("vc32s_i2c_read \n");

    if (rt_i2c_transfer(vc32s_i2cbus, msgs, 2) == 2)
    {
        res = RT_EOK;
    }
    else
    {
        res = -RT_ERROR;
    }
    return res;
}
  1. 校准tick 配置, vc32s 的时钟需要通过系统时钟进行校准;

static uint32_t vcHr02GetRtcCountFromMCU(void)
{
    return HAL_GTIMER_READ();  //get  tick
}
static void vcHr02Init(vcHr02_t *pVcHr02)
{
    mcuOscData = HAL_LPTIM_GetFreq();   //lptime freq
}
  1. 在目录solution\components\sensor\sensor_algo\hr添加对应的器件目录“vc32s”,在“vc32s"目录下创建vc32s_hr_service.c/vc32s_hr_service.h文件,用于实现vc32s的注册为rtos设备;

static int rt_hw_vc32s_register(const char *name, struct rt_sensor_config *cfg)
{
    /* magnetometer/compass sensor register */
    {
        vc32s_sensor = rt_calloc(1, sizeof(struct rt_sensor_device));
        if (vc32s_sensor == RT_NULL)
            goto __exit;

        vc32s_sensor->info.type       = RT_SENSOR_CLASS_HR;
        vc32s_sensor->info.vendor     = RT_SENSOR_VENDOR_UNKNOWN;
        vc32s_sensor->info.model      = name;
        vc32s_sensor->info.unit       = RT_SENSOR_UNIT_BPM;
        vc32s_sensor->info.intf_type  = RT_SENSOR_INTF_I2C;
        vc32s_sensor->info.range_max  = 220;
        vc32s_sensor->info.range_min  = 30;
        vc32s_sensor->info.period_min = VC32S_PERIOD_TIMER;
        vc32s_sensor->info.fifo_max   = VC32S_BUFF_SIZE;
        vc32s_sensor->data_buf          = RT_NULL;
        vc32s_sensor->data_len          = 0;        //must be 0, because don't use data buf

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

        rt_err_t result = rt_hw_sensor_register(vc32s_sensor, name, RT_DEVICE_FLAG_RDWR, RT_NULL);
        if (result != RT_EOK)
        {
            VC32S_LOG_I("HR [%s] register err code: %d", name, result);
            goto __exit;
        }
    }
    VC32S_LOG_I("HR [%s] init success!\n", name);

    return RT_EOK;

__exit:
    if (vc32s_sensor != RT_NULL)
    {
        rt_free(vc32s_sensor);
        vc32s_sensor = RT_NULL;
    }
    return -RT_ERROR;

}

int vc32s_register(void)
{
    //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
    int ret = rt_hw_vc32s_register("vc32s", &cfg);
    rt_err_t err = rt_mutex_init(&vc32s_service_mutex, "vc32s_service_mutex", RT_IPC_FLAG_FIFO);

    return ret;
}

INIT_COMPONENT_EXPORT(vc32s_register);

注册设备名长度不建议超过系统要求。可以通过宏RT_NAME_MAX来查看当前系统支持的最长名字;注册为RTOS设备,需要完善RTOS设备的接口,必须要实现的内容如下:

static struct rt_sensor_ops sensor_ops =
{
    vc32s_fetch_data,
    vc32s_control
};

vc32s_fetch_data 用于服务层从驱动获取数据;
vc32s_control 用于驱动各种控制功能,必须要实现的部分如下:

fishy fishy
  1. 服务层调度文件hr_algo.c文件,修改使用的心率器件为“vc32s”,这个名字和注册的设备名要对应;

fishy

至此,心率器件的驱动能够正常运行了。

4.2 添加gsensor驱动

以下以stk8321 加速度计为例,实现快速添加加速度计驱动如下:

  1. 新建stk8321目录,存放原厂的驱动,修改kconfig以及SConscript文件

  2. 加速度计驱动需要适配内容包括:通讯总线,延时,中断及中断处理线程等。下面先适配通讯总线;

  • 总线初始化

static int stk8321_i2c_init(void)
{
    /* 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, RT_DEVICE_FLAG_RDWR);
    }
    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;
}
  • 总线读

static int32_t gs_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)
    {
        struct rt_i2c_msg msgs[2];

        msgs[0].addr  = GS_I2C_ADDR;    /* slave address */
        msgs[0].flags = RT_I2C_WR;        /* write flag */
        msgs[0].buf   = ®              /* Send data pointer */
        msgs[0].len   = 1;

        msgs[1].addr  = GS_I2C_ADDR;    /* slave address */
        msgs[1].flags = RT_I2C_RD;        /* write flag */
        msgs[1].buf   = data;              /* Send data pointer */
        msgs[1].len   = len;

        if (rt_i2c_transfer(handle->bus_handle, msgs, 2) == 2)
        {
            res = RT_EOK;
        }
        else
        {
            res = -RT_ERROR;
        }
    }

    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;
}
  • 延时

void delay_ms(uint16_t ms)
{
    rt_thread_mdelay(ms);
}
  • 中断 线程

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)
{
    rt_sem_init(&gs_int_sem, "gs_sem", 0, RT_IPC_FLAG_FIFO);

    gs_thread = rt_thread_create("gs_thread_init", stk8321_thread_task, NULL, GS_THRED_STACK_SIZE, 15, 10);   //HR_THRED_STACK_SIZE
    RT_ASSERT(gs_thread);

    return 0;
}

static void stk8321_thread_deinit(void)
{
    if (gs_thread)
    {
        rt_thread_delete(gs_thread);
        gs_thread = NULL;
    }
    rt_sem_detach(&gs_int_sem);
    rt_kprintf("gs_thread_deinit\n");
}
  1. 在目录“siflisolution\solution\components\sensor\sensor_algo\acc”添加对应的器件目录“stk8321”,在“stk8321"目录下创建stk8321_gsensor_service.c/stk8321_gsensor_service.h文件,用于实现stk8321的注册为rtos设备;

static int rt_hw_stk8321_register(const char *name, struct rt_sensor_config *cfg)
{
    int ret;

    /* 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      = name;
        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 = 200;
        stk8321_acce->info.fifo_max   = 10;
        stk8321_acce->data_buf          = RT_NULL;
        stk8321_acce->data_len          = 0;

        rt_memcpy(&stk8321_acce->config, cfg, sizeof(struct rt_sensor_config));
        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_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)
{
    struct rt_sensor_config cfg = {0};
    cfg.intf.dev_name = STK8321_BUS_NAME;
    cfg.irq_pin.pin = STK8321_INT;    //it must be config
    int ret = rt_hw_stk8321_register("stk8321", &cfg);
    return ret;
}

INIT_COMPONENT_EXPORT(stk8321_register);

注册设备名长度不建议超过系统要求。可以通过宏RT_NAME_MAX来查看当前系统支持的最长名字;
注册为RTOS设备,需要完善RTOS设备的接口,必须要实现的内容如下:

   static struct rt_sensor_ops sensor_ops =
{
    stk8321_fetch_data,
    stk8321_control
};

stk8321_fetch_data 用于服务层向驱动层获取数据; stk8321_control 用于服务层对驱动层的控制; 必须实现的内容如下:

fishy
  1. 服务层调度文件gensor_algo.c文件,修改使用的gsensor器件为“stk8321”,这个名字和注册的设备名要对应;

fishy

至此,加速度计的驱动能够正常运行了。