RTOS Sensor Framework对比

news2024/9/22 23:29:54

1.背景

        传感器(Sensor)是物联网(IOT)的重要组成部分,用于感知和采集环境中的各种数据,大部分智能硬件都需要。

     为使传感器能正常⼯作,驱动开发者需要开发不同的驱动程序,驱动程序要实现芯片寄存器\IO设置,又要响应使用者的请求,同时应⽤程序也需要适配这些驱动程序来访问传感器。

        不同类型的传感器或者不同OS的驱动框架特性差异比较大,其驱动程序也⼤相径庭,应⽤程序就需要对它们的差异处做适配,显然就会增加应⽤程序的开发难度和周期,容易引⼊bug不说,更提高了开发门槛,随着传感器的种类的增多,这⼀问题变的更加严重。为了简化传感器操作⽅式,主流操作系统⼀般都会设计专⻔的sensor软件框架。

        本文对主流RTOS的sensor框架进行简单分析。

1.1 框架的基本要求

框架的基本要求,也就是未来这个框架的基本特点。

  1. 统一的管理sensor的数据、控制、driver model,封装并给出简单API,能对驱动开发、应用开发分别提供标准化的接入流程。

  2. 能与应用层框架中的api相结合,通过应用层框架的API接口可以对sensor driver进行read、write、ioctl、trigger、batch,enable,flush操作,例如nuttx中的uorb接口。

  3. 模块要分层,接口清晰易懂且简单。接口定义要简单到,只看头文件和注释就能明确接口的功能。为了降低开发难度,通常sensor框架可分三个层次:

    1. 芯片驱动。寄存器datasheet有关的代码、称之为device driver代码。设计这个的目的是为了提高驱动的移植效率。

    2. 框架抽象层(core层)。供device driver和user app调用,对驱动开发、应用开发抽象统一的接口。

    3. 框架接入层。

  4. 移植性强,OS解耦。

  5. 所实现的模块,是否能进行独立验证?答案是必须能独立验证。

  6. 是否需要支持宏定义动态开关编译选项,如果需要,要确保宏打开/关闭,编译和运行都正常,不影响其他模块。

1.2 调研思路

  1. 梳理需求,每一个模块负责人都要主动梳理需求,同时评审阶段大家一起来看有没有遗漏的需求。sensor框架的需求,包括开发需求,正式产品的业务需求、数据采集需求,保证从协议、数据结构定义方面能兼容所有设备。

  2. 设计要求要符合1.1中的六个基本要求。

  3. 结合已有经验,调研主流系统的sensor 框架,主要是学习他们的设计思想,如何封装数据结构、和API。

 

1.3 RTOS选择

当前RTOS百花齐放,还未有一个系统能一统江湖,我们应该基于哪个RTOS来参考?基于我们现有的基础和国内外的RTOS使用现状来考虑。

  1.3.1 贡献者对比

先看一下RTOS使用现状,https://www.embedded.com/will-zephyr-become-the-dominant-rtos/

在过去几年中,RTOS贡献者对比:4459e53cb9664acda87c633a58270975.png

1.3.2 posix支持

为什么要看posix支持情况,posix(Portable Operating System Interface)即可移植操作系统接口,是操作系统为应用程序提供的接口标准。POSIX标准同样适用于嵌入式软件,最大的优点就是可移植性强、容易入门、对接口定义能有统一的标准规范,缺点是比裸机占用更多的资源。

这里简单介绍几个常用RTOS的POSIX支持情况:

RTOSPOSIX支持情况
VxWorksUser space(RTP):POSIX.13 PSE52 (少数接口存在限制)Kernel space:POSIX.1部分接口和POSIX.1可选功能中的一些实时接口
ZephryPSE54
Nuttx严格符合OpenGroup.org定义的可移植标准操作系统接口
FreeRTOS实验性,仅实现了约 20% 的 POSIX API
RT-Thread未找到明确说明,从源码上看兼容大部分POSIX,部分接口直接返回不支持
oneOS部分支持,支持列表OneOS: OneOS是中国移动针对物联网领域推出的轻量级操作系统,具有可裁剪、跨平台、低功耗、高安全等特点,支持ARM Cortex-M/R/A、MIPS、RISC-V等主流CPU架构,兼容POSIX、CMSIS等标准接口,支持Micropython语言开发,提供图形化开发工具,能够有效提高开发效率并降低开发成本,帮助客户开发稳定可靠、安全易用的物联网应用。 官网地址:https://os.iot.10086.cn/ - Gitee.com
openharmony部分支持,支持列表LiteOS-M小型系统内核——POSIX支持-鸿蒙开发者社区-51CTO.COM

可以看出,对posix支持度最高的是Nuttx、其次是RT-Thread,zephry、freertos,

这里我们选择以下几个操作系统作为参考。

  • RT-Thread。这是我们现在项目中用到的系统,要分析其sensor框架的优缺点。

  • openharmony。国内用的较多的一个系统,恒玄sdk基于openharmony进行portting。

  • zephry。最近几年全球范围内贡献者最多的一个系统。

  • nuttx。虽然国内用的并不多,这个系统对posix的支持、sensor框架都很值得借鉴。

这里先把调研后的感受写在前面。

  • 如果要支持多个OS,可以参考openharmony。

  • 如果要支持posix可以参考Nuttx、

  • 如果要学习接口定义、代码编写规范,openamp的操作等可以参考zephyr。

   我们当前就是在rtthread的基础上,把上述三个系统的优点引进过来。通过对比RT-Thread、zephyr、openharmony、nuttx操作系统的sensor框架,最终打造一款适合平台化使用的框架。其实主要是参考他们的设计思想,头文件定义、驱动/应用 API接口的封装。从数据结构、接口通用性、内聚耦合特点、posix支持度,四个维度来对比这些框架。

2 RT-Thread

2.1 框架概览

RT-Thread 提供了丰富的软件包,其中外设类有178个,覆盖大部分厂商。

3f2de34d93ee4fcd943954031c6daebb.png

RT-Thread的sensor框架主要特点:

  1. 将框架中公共的结构体和sensor数据定义都放在同一个头文件即sensor.h中,但其中的结构体都是rt_xxx开头。

  2. 支持posix接口(可选)。

  3. 驱动层和应用层接口没有明显的层次关系,可以混淆调用(在不使用posix接口的情况下)。

  4. 接口定义不是太清晰明确,结构体有一部分荣誉。如rt_sensor_ops中只有fetch_data、ctrol两个接口,不清楚ctrol的具体功能。再如rt_sensor_info中有静态和动态控制信息没有区分开。v1 v2接口不兼容。

  5. 灵活的加载机制(INIT_XXX_EXPORT)

  6. 事件儿驱动(中断)支持较差,需要指定rt_pin_attach_irq.

struct rt_sensor_info
{
    rt_uint8_t     type;                    /* sensor type */
    rt_uint8_t     vendor;                  /* sensors vendor */
    const char    *name;                    /* name of sensor */
    rt_uint8_t     unit;                    /* unit of measurement */
    rt_uint8_t     intf_type;               /* communication interface type */
    rt_uint16_t    mode;                    /* sensor work mode */
    rt_uint8_t     fifo_max;
    rt_sensor_float_t acquire_min;          /* minimum acquirement period, unit:ms. zero = not a constant rate */
    struct rt_sensor_accuracy accuracy;     /* sensor current measure accuracy */
    struct rt_sensor_scale scale;           /* sensor current scale range */
};

  从rtt-thread源码能看出,为了优化sensor框架,公共接口部分有较大改动,前后用了三个版本,sensor.h(rt-thread3.0) 、sensor.h(_v1,4.0)和 sensor_v2.h(5.0)。

Sensor driver通过rt_hw_sensor_register(内部调用rt_device_register)注册设备,即sensor是Rt-thread内部标准设备(rt_device)的子类设备,增加sensor独有的一些属性和控制命令。

内部有sensor类型,vendor类型,单位类型管理,以及power mode,work mode,控制cmd管理,以及统一的sensor union data structer。

struct rt_sensor_info对应一个sensor实例,包含sensor所有的基本信息

struct rt_sensor_config包含接口和sensor动态配置信息

struct rt_sensor_device 对应sensor设备,内部包含标准设备struct rt_device,以及所有sensor的信息:info,config,ops,data buffer,module(用于物理耦合的软件处理)

3e3354fc90274cb8beda5fca513792f6.png

 

2.2 结构体定义

2.2.1 rt_sensor_device

        struct rt_sensor_info对应一个sensor实例,包含sensor所有的基本信息

        struct rt_sensor_config包含接口和sensor动态配置信息

        struct rt_sensor_device 对应sensor设备,内部包含标准设备struct rt_device,以及所有sensor的信息:info,config,ops,data buffer,module(用于物理耦合的软件处理)

 

573127cf42c64ab1b8150accea34db10.png

2.2.2 Sensor V1 && Sensor V2

fe32bc0b7e1643afad7b400d6f739e7d.png

 

部分代码:

struct rt_sensor_device
2 {
3 struct rt_device parent; /* The standard device */
4
5 struct rt_sensor_info info; /* The sensor info data */
6 struct rt_sensor_config config; /* The sensor config data */
7
8 rt_sensor_data_t data_buf; /* The buf of the data received */
9 rt_size_t data_len; /* The size of the data received */
10
11 const struct rt_sensor_ops *ops; /* The sensor ops */
12
13 struct rt_sensor_module *module; /* The sensor module */
14
15 rt_err_t (*irq_handle)(rt_sensor_t sensor); /* Called when an
interrupt is generated, registered by the driver */
16 };
17
18 struct rt_sensor_ops
19 {
20 rt_ssize_t (*fetch_data)(rt_sensor_t sensor, rt_sensor_data_t buf,
rt_size_t len);
21 rt_err_t (*control)(rt_sensor_t sensor, int cmd, void *arg);
22 };
23
24 struct rt_sensor_config{
26 struct rt_sensor_intf intf; /* sensor interface config */
27 struct rt_device_pin_mode irq_pin; /* Interrupt pin, The purpose of
this pin is to notification read data */
28 };
29
30 /* ISR for sensor interrupt */
31 static void _irq_callback(void *args)
32 {
33 rt_sensor_t sensor = (rt_sensor_t)args;
34 rt_uint8_t i;
35
36 if (sensor->module)
37 {
38 /* Invoke a callback for all sensors in the module */
39 for (i = 0; i < sensor->module->sen_num; i++)
40 {
41 _sensor_cb(sensor->module->sen[i]);
42 }
43 }
44 else
45 {
46 _sensor_cb(sensor);
47 }
48 }
49
50 /*_sensor_control switch (cmd)*/
51 /* Sensor mode: power */
52 #define RT_SENSOR_MODE_POWER_HIGHEST (0)
53 #define RT_SENSOR_MODE_POWER_HIGHEST_STR "Power Highest"
54 #define RT_SENSOR_MODE_POWER_HIGH (1)
55 #define RT_SENSOR_MODE_POWER_HIGH_STR "Power High"
56 #define RT_SENSOR_MODE_POWER_MEDIUM (2)
57 #define RT_SENSOR_MODE_POWER_MEDIUM_STR "Power Medium"
58 #define RT_SENSOR_MODE_POWER_LOW (3)
59 #define RT_SENSOR_MODE_POWER_LOW_STR "Power Low"
60 #define RT_SENSOR_MODE_POWER_LOWEST (4)
61 #define RT_SENSOR_MODE_POWER_LOWEST_STR "Power Lowest"
62 #define RT_SENSOR_MODE_POWER_DOWN (5)
63 #define RT_SENSOR_MODE_POWER_DOWN_STR "Power Down"
64
65 /* Sensor mode: fetch data */
66 #define RT_SENSOR_MODE_FETCH_POLLING (0) /* One shot only read a
data */
67 #define RT_SENSOR_MODE_FETCH_POLLING_STR "Polling Mode"
68 #define RT_SENSOR_MODE_FETCH_INT (1) /* TODO: One shot interrupt
only read a data */
69 #define RT_SENSOR_MODE_FETCH_INT_STR "Interrupt Mode"
70 #define RT_SENSOR_MODE_FETCH_FIFO (2) /* TODO: One shot interrupt
read all fifo data */
71 #define RT_SENSOR_MODE_FETCH_FIFO_STR "FIFO Mode"
72
73 /* Sensor control cmd types */
74 #define RT_SENSOR_CTRL_GET_ID (RT_DEVICE_CTRL_BASE(Sensor) +
0) /* Get device id */
75 #define RT_SENSOR_CTRL_SELF_TEST (RT_DEVICE_CTRL_BASE(Sensor) +
1) /* Take a self test */
76 #define RT_SENSOR_CTRL_SOFT_RESET (RT_DEVICE_CTRL_BASE(Sensor) +
2) /* soft reset sensor */
77 #define RT_SENSOR_CTRL_SET_FETCH_MODE (RT_DEVICE_CTRL_BASE(Sensor) +
3) /* set fetch data mode */
78 #define RT_SENSOR_CTRL_SET_POWER_MODE (RT_DEVICE_CTRL_BASE(Sensor) +
4) /* set power mode */
79 #define RT_SENSOR_CTRL_SET_ACCURACY_MODE (RT_DEVICE_CTRL_BASE(Sensor) +
5) /* set accuracy mode */
80
81 在ctr接⼝中还可以设置fetch模式,但是在fetch接⼝中却不能指定fetch模式。

 

 

2.3 框架接入

为了适配RT-Thread的sensor框架,驱动和应用层需要做哪些?

2.3.1 驱动层API

 #include <rtthread.h>
2 #include <sensor.h>
3
4 // 传感器设备结构体
5 struct my_sensor_device
6 {
7 struct rt_sensor_device sensor;
8 // 其他私有数据
9 };
10
11 // 初始化接⼝
12 static rt_err_t my_sensor_init(struct rt_sensor_device *sensor, rt_uint8_t
mode)
13 {
14 struct my_sensor_device *my_sensor = (struct my_sensor_device *)sensor;
// 初始化传感器硬件和软件配置
16 // ...
17 return RT_EOK;
18 }
19
20 // 去初始化接⼝
21 static rt_err_t my_sensor_deinit(struct rt_sensor_device *sensor)
22 {
23 struct my_sensor_device *my_sensor = (struct my_sensor_device *)sensor;
24 // 关闭传感器设备
25 // ...
26 return RT_EOK;
27 }
28
29 // 读取数据接⼝
30 static rt_size_t my_sensor_read_data(struct rt_sensor_device *sensor, void
*buf, rt_size_t len)
31 {
32 struct my_sensor_device *my_sensor = (struct my_sensor_device *)sensor;
33 // 从传感器读取数据并解析
34 // ...
35 return len;
36 }
37
38 // 控制接⼝
39 static rt_err_t my_sensor_control(struct rt_sensor_device *sensor, int cmd,
void *args)
40 {
41 struct my_sensor_device *my_sensor = (struct my_sensor_device *)sensor;
42 // 根据命令控制传感器
43 // ...
44 return RT_EOK;
45 }
46
47 // 获取信息接⼝
48 static rt_err_t my_sensor_get_info(struct rt_sensor_device *sensor,
rt_sensor_info_t *info)
49 {
50 struct my_sensor_device *my_sensor = (struct my_sensor_device *)sensor;
51 // 获取传感器信息
52 // ...
53 return RT_EOK;
54 }
55
56 // 传感器设备注册
57 int rt_hw_my_sensor_init(void)
58 {
59 struct my_sensor_device *my_sensor;
60 struct rt_sensor_info sensor_info;
61
62 my_sensor = rt_calloc(1, sizeof(struct my_sensor_device));
63 if (my_sensor == RT_NULL)
64 {
65 return -RT_ENOMEM;
66 }
67
68 // 配置传感器信息
69 sensor_info.type = RT_SENSOR_TYPE_ACCELEROMETER;
70 sensor_info.vendor = "MyVendor";
71 sensor_info.model = "MySensorModel";
72 sensor_info.unit = RT_SENSOR_UNIT_METER_PER_SQUARE_SECOND;
73 sensor_info.intf_type = RT_SENSOR_INTF_I2C;
74 sensor_info.range_max = 16.0f;
75 sensor_info.range_min = -16.0f;
76 sensor_info.period_min = 10;
77
78 // 初始化传感器设备
79 rt_hw_sensor_init(&my_sensor->sensor, &sensor_info);
80
81 // 设置操作接⼝
82 my_sensor->sensor.ops->fetch_data= my_sensor_read_data;
83 my_sensor->sensor.ops->control= my_sensor_control;
84
85
86 // 注册传感器设备
87 rt_hw_sensor_register(&my_sensor->sensor, "my_sensor",
RT_DEVICE_FLAG_RDWR, my_sensor);
88
89 return RT_EOK;
90 }
91 INIT_APP_EXPORT(rt_hw_my_sensor_init);

 

2.3.2 应用层API

目前rtthread官方给的demo sensor_cmd.c操作传感器的流程如下:

356151aee2ea44ff814b2730a51e53fc.png

有两种方法,一种是使用RT-Thread device接口,实现在components/drivers/core/device.c中。

另一种办法是直接使用posix接口(当前配置支持devfs,只需在driver中实现filesystem_ops接口即可)

//系统组件儿接口尽可能兼容posix和ansic.

两种方法的代码风格如下:

不使用Posix:

#define SAMPLE_SENSOR_NAME "acce_st" /*

rt_device_t dev;

struct rt_sensor_data data;

/* 查找传感器设备 /

dev = rt_device_find(SAMPLE_SENSOR_NAME);

* /* 以只读及轮询模式打开传感器设备 */

rt_device_open(dev, RT_DEVICE_FLAG_RDONLY);

while (rt_device_read(dev, 0, &data, 1)>0)

{

}

使用Posix:

#define SAMPLE_SENSOR_NAME       "acce_st"  /* 
   int    fd = open(SAMPLE_SENSOR_NAME ,   O_RDONLY | O_NONBLOCK);
    read(fd, &value, 1) ;
    /* 也可以使用poll监测数据 */
    fds[0].fd = fd;
    fds[0].events = POLLIN | POLLRDNORM;
    while (1) {
        ret = poll(fds, 1, 1000);
        if (ret < 0) {
            break;
        } else if (ret == 0) {
            continue;
        } else {
            revents = Events.revents;
            if ((revents == POLLIN) || (revents ==       POLLRDNORM)) {
                ret = read(fd, rd_buf, 4);
        }
    }

两种风格对比,使用posix兼容性会更强,但会增加内存和稍微降低性能。

2.4 RT-Thread 5.0.2改动

Rt-thread 5.0.2内核改进了不少,其中有跟sensor相关的部分

● 新增了AMP支持。

● 新增了rt_hw_interrupt_is_disabled API ,用于检测是否关闭了中断;

● 完善了errno与 POSIX 兼容,提升兼容性。

● 完善了原子操作Atomic支持检测。

● 完善了device_open接口,避免重复调用。

● 移除了互斥锁中的递归,增强了稳定性。

● 修复了用户模式下 MQ 接收阻塞问题。

● 完善fd关闭策略,避免潜在的内存泄漏问题。

● 修复了 tmpfs 自旋锁错误,nfs 64位架构错误。

● 升级设备文件系统(dfs)到v2版本,同时考虑到兼容也保留v1版本供Kconfig配置选择。

● 为 device driver v2.0 做好准备,带来更多的功能和性能提升。

● 添加了设备树子节点搜索宏,提升了设备管理的效率。

● 修复了串口有时重复发送回车符的问题,提升串口通信的稳定性。

● 修复了多线程中的 CPU 定时器问题,dtb 编译警告。

● 优化了 SPI 传输速度并修复了一些问题。

● 修改了 ADC 通道数据类型,并添加了内置通道通用编号识别。

● 修改了将当前传感器框架转回 v1 版本,并使其独立于 v2 。

● 修复了 Sensor-V1 中返回类型不一致的错误,重新定义 Sensor-V2 作为传感器框架。

● 修改了将 tty 的驱动程序更改为设备,并修复了多个 scanfs 导致数据丢失问题。

 

2.5 参考点

  1. 传感器数据结构可以使用同一个头文件(应用层和驱动层接口API最好能分开)。

  2. 可以选择性的支持posix,兼顾性能和移植性。

  3. 自动化加载方式(XXX_INIT_XXX),平台化前期阶段不建议使用,因为有的模块间是有依赖关系和执行顺序要求的,如果使用自动化加载方式,需要了解RT-Thread的init等级执行顺序。当然这需要对自动初始化流程有一点了解

    #define rt_section(x)               __attribute__((section(x)))
    #define INIT_EXPORT(fn, level)                                                       \
                rt_used const init_fn_t __rt_init_##fn rt_section(".rti_fn." level) = fn
    /* pre/device/component/env/app init routines will be called in init_thread */
    /* components pre-initialization (pure software initialization) */
    #define INIT_PREV_EXPORT(fn)            INIT_EXPORT(fn, "2")
    /* device initialization */
    #define INIT_DEVICE_EXPORT(fn)          INIT_EXPORT(fn, "3")
    /* components initialization (dfs, lwip, ...) */
    #define INIT_COMPONENT_EXPORT(fn)       INIT_EXPORT(fn, "4")
    /* environment initialization (mount disk, ...) */
    #define INIT_ENV_EXPORT(fn)             INIT_EXPORT(fn, "5")
    /* application initialization (rtgui application etc ...) */
    #define INIT_APP_EXPORT(fn)             INIT_EXPORT(fn, "6")
    
    /* init after mount fs */
    #define INIT_FS_EXPORT(fn)              INIT_EXPORT(fn, "6.0")
    /* init in secondary_cpu_c_start */
    #define INIT_SECONDARY_CPU_EXPORT(fn)   INIT_EXPORT(fn, "7")

     

  4. 缺点:接口定义可以再清晰一些,如一个sensor最基本的几个接口可以从ctrol中进一步提取出来,如  首次使用时active,设置batch 采样率, 设置 triiger阈值,订阅与取消订阅等接口并无体现。

3 Zephyr

3.1 框架概览

zephyr的sensor框架主要特点:

  1. 将框架中公共的结构体和sensor数据定义都放在同一个头文件即sensor.h中,但是其中的sensor_value定义不是太明确且不包含时间戳,但sensor.h中其他结构体封装思路较清晰。

  2. 不支持posix接口。

  3. 驱动层和应用层接口没有明显的层次关系,可以混淆调用(在不使用posix接口的情况下)。

  4. 接口定义清晰明确。

  5. 事件儿驱动支持的较好。

  6. 灵活的加载机制(INIT_XXX_EXPORT)

  7. dts设备树机制(非必须实现),使硬件资源与driver code隔离。

  8. 与rt-thread不同,sensor框架不再基于device驱动框架实现,两者独立。

  9. 缺少多客户使用仲裁方案。

典型使用场景

a47bf23603784841af48356b769b1d1a.png

3.2 为什么不支持posix

虽然zephyr是linux基金会管控的,但zephyr 驱动/sensor框架不支持posix,主要是设计目标不同,下面的答案来自GPT,尽管zephyr不支持posix,但是它的sensor框架接口清晰明了简洁,仍然具备很强的竞争力。

zephyr驱动模型不支持POSIX接口可能是由于以下几个原因:

  1. 设计目标不同zephyr可能被设计为专注于特定的应用场景或硬件平台,而没有考虑与POSIX标准的兼容性。POSIX接口通常用于提供跨平台的兼容性,但并非所有系统或驱动都需要这种兼容性。

  2. 性能优化:为了达到更高的性能或更低的资源消耗,zephyr可能选择了不遵循POSIX标准,而是采用更适合其目标平台的接口和实现方式。

  3. 开发资源限制:实现POSIX兼容性需要额外的工作量和测试,如果开发团队在资源有限的情况下,可能会优先考虑实现核心功能,而不是POSIX兼容性。

  4. 特定的硬件需求:某些硬件可能需要特定的驱动接口,这些接口可能与POSIX标准不兼容。为了更好地利用硬件特性,zephyr可能选择了非POSIX的接口。

  5. 历史遗留问题:如果zephyr是在没有考虑POSIX兼容性的情况下开发的,后续可能因为各种原因(如维护成本、兼容性问题等)没有进行POSIX兼容性的改造。

       zephyr驱动模型不支持POSIX接口可能是出于对特定应用场景、性能优化、资源限制、硬件需求或历史遗留问题的考虑。

在讨论Linux与Zephyr的驱动模型区别时,我们可以从几个关键方面进行比较:

  1. 内核架构

    1. Linux:Linux是一个宏内核(monolithic kernel),所有的系统服务和驱动程序都运行在内核空间。这种设计使得内核可以快速高效地处理硬件请求,但也增加了内核崩溃的风险。

    2. Zephyr:Zephyr则是一个微内核(microkernel)设计,它将许多服务和驱动程序放在用户空间运行。这种设计提高了系统的稳定性和安全性,因为即使某个驱动程序崩溃,也不会影响到整个系统。

  2. 驱动模型

    1. Linux:Linux的驱动模型是基于设备文件系统的(如/dev目录下的设备文件),驱动程序通过这些设备文件与用户空间的应用程序进行交互。Linux的驱动程序通常需要编译进内核或作为模块加载。

    2. Zephyr:Zephyr的驱动模型更为简洁,它直接在内核中管理设备驱动。Zephyr的驱动程序通常是静态链接到内核中,而不是通过模块动态加载。Zephyr的驱动模型更注重于资源受限的嵌入式系统,因此它提供了更高效的资源管理和更简洁的API。

  3. 编程接口

    1. Linux:Linux提供了丰富的API和库函数,支持多种编程语言和复杂的设备管理。Linux的驱动开发相对复杂,需要开发者熟悉内核编程和设备文件系统。

    2. Zephyr:Zephyr的API相对简单,更专注于嵌入式系统的基本需求。Zephyr的驱动开发更注重于简洁性和高效性,适合资源受限的设备。

  4. 应用场景

    1. Linux:Linux广泛应用于服务器、桌面、移动设备等多种场景,支持广泛的硬件和软件生态。

    2. Zephyr:Zephyr主要针对物联网(IoT)设备、可穿戴设备和其他资源受限的嵌入式系统。

        总的来说,Linux和Zephyr的驱动模型各有特点,Linux更适合复杂和高性能的系统,而Zephyr则更适合简单和资源受限的嵌入式系统。选择哪种驱动模型取决于具体的应用需求和硬件环境。

 

3.3 dts文件

3.3.1 简介

     在设计Zypher的sensor驱动时沿用了Linux的Device Tree(设备树)机制,通过dts(设备树源文件)来描述硬件信息,dts其实相当于"描述硬件原理图的一份配置文件",例如当前主板的cpu信息、外设驱动用到的总线类型、总线io 管脚的定义等。使用这个机制的优点是硬件信息和driver代码能够解耦,简化驱动代码,缺点是需要掌握它的语法和规则,明确语法与硬件的对应关系,还需要编译器支持,系统启动过程中要解析,驱动层要做适配。

      设备树的主要优势在于它能够简化硬件配置和驱动程序的编写,使得硬件描述和软件实现分离,提高了系统的灵活性和可维护性。

     具体到sensor驱动的设计,使用dts文件可以清晰地定义传感器硬件的各个属性,如I2C地址、中断号、寄存器配置等。这样,驱动程序开发者可以专注于实现传感器的数据读取和处理逻辑,而不必硬编码硬件细节,从而加快开发速度并减少错误。

以下是一个简单的dts示例,展示了如何描述一个I2C传感器节点:

&i2c0 {
    sensor@76 {
        compatible = "my_sensor";//probe driver
        reg = <0x76>; //器件地址
        interrupt-parent = <&gpio>;
        interrupts = <17 2>; /* GPIO 17, falling edge */
        /* 其他传感器特定属性 */
    };
};

 

c8bcfa7dbc3842ffb18415b3b4a3b8e0.png

 

     在这个示例中,i2c0是I2C总线控制器节点,sensor@76是连接到该总线的传感器设备,地址为0x76。compatible属性用于匹配正确的驱动程序,reg属性定义设备地址,interrupts属性则定义了传感器的中断配置。

       通过这种方式,内核在启动时会解析这些设备树信息,并自动加载和配置相应的驱动程序。这不仅简化了驱动开发,也使得系统配置更加直观和易于管理。

3.3.2 设备树的优缺点

设备树(Device Tree)是一种描述硬件信息的数据结构,它使得硬件信息与内核代码分离,从而提高了系统的灵活性和可移植性。以下是设备树的优缺点:

优点:

  1. 硬件信息与内核代码分离:设备树将硬件描述从内核代码中分离出来,使得内核代码更加通用,不依赖于特定的硬件配置。

  2. 可移植性:通过修改设备树文件,可以在不同的硬件平台上使用相同的内核镜像,减少了为不同硬件定制内核的工作量。

  3. 简化内核代码:内核代码不再需要包含大量的硬件特定信息,如寄存器地址、设备ID等,这简化了内核代码的维护和更新。

  4. 动态配置:设备树可以在系统启动时动态加载,这使得硬件配置可以在不重新编译内核的情况下进行调整。

  5. 易于调试和维护:设备树文件通常使用人类可读的文本格式(如dts),这使得硬件配置的调试和维护变得更加直观和容易。

缺点:

  1. 学习曲线:设备树的语法和概念对于新用户来说可能有一定的学习曲线,特别是对于那些习惯于传统方式(如直接在内核代码中硬编码硬件信息)的开发者。

  2. 复杂性:对于复杂的硬件系统,设备树文件可能会变得非常庞大和复杂,这可能会增加理解和维护的难度。

  3. 兼容性问题:不同版本的设备树规范可能会有所不同,这可能会导致兼容性问题,特别是在使用第三方设备树文件时。

  4. 性能影响:虽然设备树本身对性能的影响通常很小,但在某些情况下,设备树的解析和处理可能会引入轻微的性能开销。

  5. 集成挑战:将设备树集成到现有的内核和硬件开发流程中可能需要一些工作,特别是对于那些已经采用传统方式的项目。

总的来说,设备树是一个强大的工具,它通过将硬件信息从内核代码中分离出来,提高了系统的灵活性和可移植性。然而,它也有一些缺点,如学习曲线和复杂性,需要开发者在使用时进行权衡。

 

3.4 结构体定义

zephyr\include\zephyr\drivers\sensor.h中定义

按结构体功能划分,大致可分为数据通道、数据格式、triger type、属性设置等。

3.4.1 数据通道

   zephyr把根据传感器表达的物理量不同,分割成不同的通道,在enum sensor_channel定义了几乎所有可测量的物理量


/**
 * @brief Sensor channels.
 */
enum sensor_channel {
        /** Acceleration on the X axis, in m/s^2. */
        SENSOR_CHAN_ACCEL_X,
        /** Acceleration on the Y axis, in m/s^2. */
        SENSOR_CHAN_ACCEL_Y,
        /** Acceleration on the Z axis, in m/s^2. */
        SENSOR_CHAN_ACCEL_Z,
        /** Acceleration on the X, Y and Z axes. */
        SENSOR_CHAN_ACCEL_XYZ,
        /** Angular velocity around the X axis, in radians/s. */
        SENSOR_CHAN_GYRO_X,
        /** Angular velocity around the Y axis, in radians/s. */
        SENSOR_CHAN_GYRO_Y,
        /** Angular velocity around the Z axis, in radians/s. */
        SENSOR_CHAN_GYRO_Z,
        /** Angular velocity around the X, Y and Z axes. */
        SENSOR_CHAN_GYRO_XYZ,
        /** Magnetic field on the X axis, in Gauss. */
        SENSOR_CHAN_MAGN_X,
        /** Magnetic field on the Y axis, in Gauss. */
        SENSOR_CHAN_MAGN_Y,
        /** Magnetic field on the Z axis, in Gauss. */
        SENSOR_CHAN_MAGN_Z,
        /** Magnetic field on the X, Y and Z axes. */
        SENSOR_CHAN_MAGN_XYZ,
        /** Device die temperature in degrees Celsius. */
        SENSOR_CHAN_DIE_TEMP,
        /** Ambient temperature in degrees Celsius. */
        SENSOR_CHAN_AMBIENT_TEMP,
        /** Pressure in kilopascal. */
        SENSOR_CHAN_PRESS,
        /**
         * Proximity.  Adimensional.  A value of 1 indicates that an
         * object is close.
         */
        SENSOR_CHAN_PROX,
        /** Humidity, in percent. */
        SENSOR_CHAN_HUMIDITY,
        /** Illuminance in visible spectrum, in lux. */
        SENSOR_CHAN_LIGHT,
        /** Illuminance in infra-red spectrum, in lux. */
        SENSOR_CHAN_IR,
        /** Illuminance in red spectrum, in lux. */
        SENSOR_CHAN_RED,
        /** Illuminance in green spectrum, in lux. */
        SENSOR_CHAN_GREEN,
        /** Illuminance in blue spectrum, in lux. */
        SENSOR_CHAN_BLUE,
        /** Altitude, in meters */
        SENSOR_CHAN_ALTITUDE,

        /** 1.0 micro-meters Particulate Matter, in ug/m^3 */
        SENSOR_CHAN_PM_1_0,
        /** 2.5 micro-meters Particulate Matter, in ug/m^3 */
        SENSOR_CHAN_PM_2_5,
        /** 10 micro-meters Particulate Matter, in ug/m^3 */
        SENSOR_CHAN_PM_10,
        /** Distance. From sensor to target, in meters */
        SENSOR_CHAN_DISTANCE,

        /** CO2 level, in parts per million (ppm) **/
        SENSOR_CHAN_CO2,
        /** O2 level, in parts per million (ppm) **/
        SENSOR_CHAN_O2,
        /** VOC level, in parts per billion (ppb) **/
        SENSOR_CHAN_VOC,
        /** Gas sensor resistance in ohms. */
        SENSOR_CHAN_GAS_RES,

        /** Voltage, in volts **/
        SENSOR_CHAN_VOLTAGE,

        /** Current Shunt Voltage in milli-volts **/
        SENSOR_CHAN_VSHUNT,

        /** Current, in amps **/
        SENSOR_CHAN_CURRENT,
        /** Power in watts **/
        SENSOR_CHAN_POWER,

        /** Resistance , in Ohm **/
        SENSOR_CHAN_RESISTANCE,

        /** Angular rotation, in degrees */
        SENSOR_CHAN_ROTATION,

        /** Position change on the X axis, in points. */
        SENSOR_CHAN_POS_DX,
        /** Position change on the Y axis, in points. */
        SENSOR_CHAN_POS_DY,
        /** Position change on the Z axis, in points. */
        SENSOR_CHAN_POS_DZ,
        /** Position change on the X, Y and Z axis, in points. */
        SENSOR_CHAN_POS_DXYZ,

        /** Revolutions per minute, in RPM. */
        SENSOR_CHAN_RPM,

        /** Voltage, in volts **/
        SENSOR_CHAN_GAUGE_VOLTAGE,
        /** Average current, in amps **/
        SENSOR_CHAN_GAUGE_AVG_CURRENT,
        /** Standby current, in amps **/
        SENSOR_CHAN_GAUGE_STDBY_CURRENT,
        /** Max load current, in amps **/
        SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT,
        /** Gauge temperature  **/
        SENSOR_CHAN_GAUGE_TEMP,
        /** State of charge measurement in % **/
        SENSOR_CHAN_GAUGE_STATE_OF_CHARGE,
        /** Full Charge Capacity in mAh **/
        SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY,
        /** Remaining Charge Capacity in mAh **/
        SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY,
        /** Nominal Available Capacity in mAh **/
        SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY,
        /** Full Available Capacity in mAh **/
        SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY,
        /** Average power in mW **/
        SENSOR_CHAN_GAUGE_AVG_POWER,
        /** State of health measurement in % **/
        SENSOR_CHAN_GAUGE_STATE_OF_HEALTH,
        /** Time to empty in minutes **/
        SENSOR_CHAN_GAUGE_TIME_TO_EMPTY,
        /** Time to full in minutes **/
        SENSOR_CHAN_GAUGE_TIME_TO_FULL,
        /** Cycle count (total number of charge/discharge cycles) **/
        SENSOR_CHAN_GAUGE_CYCLE_COUNT,
        /** Design voltage of cell in V (max voltage)*/
        SENSOR_CHAN_GAUGE_DESIGN_VOLTAGE,
        /** Desired voltage of cell in V (nominal voltage) */
        SENSOR_CHAN_GAUGE_DESIRED_VOLTAGE,
        /** Desired charging current in mA */
        SENSOR_CHAN_GAUGE_DESIRED_CHARGING_CURRENT,

        /** All channels. */
        SENSOR_CHAN_ALL,

        /**
         * Number of all common sensor channels.
         */
        SENSOR_CHAN_COMMON_COUNT,

        /**
         * This and higher values are sensor specific.
         * Refer to the sensor header file.
         */
        SENSOR_CHAN_PRIV_START = SENSOR_CHAN_COMMON_COUNT,

        /**
         * Maximum value describing a sensor channel type.
         */
        SENSOR_CHAN_MAX = INT16_MAX,
};

3.4.2 传感器数据格式

        zephyr的传感器数据使用了统一的一个结构体,这与rt-thread、nuttx为每个传感器都封装一个结构体的方式不同。在rt-thread中要获取acc的三个字段即 x y z;只要拿到sensor_accel结构体就行,但在zephyr中则需要分两步操作,获取通道设置为SENSOR_CHAN_ACCEL_XYZ,然后从SENSOR_CHAN_ACCEL_XYZ通道中将驱动设置的三个sensor_value提取出来,然后将sensor_value[0]、sensor_value[1]、sensor_value[2]转换成对应的xyz。可以看出zephyr的传感器数据格式虽然统一,但是却不能给传感器的具体数据格式提供统一的规范约束。这里我们会采用rt-thread的数据封装。

struct sensor_value {
        /** Integer part of the value. */
        int32_t val1;
        /** Fractional part of the value (in one-millionth parts). */
        int32_t val2;
};

 

同时在sensor.h中还定义了从sensor_value转化为其他类型的转换函数

static inline int32_t sensor_rad_to_10udegrees(const struct sensor_value *rad)
{
    int64_t micro_rad_s = rad->val1 * 1000000LL + rad->val2;

    return (micro_rad_s * 180LL * 100000LL) / SENSOR_PI;
}

/**
 * @brief Helper function for converting 10 micro degrees to radians.
 *
 * @param d The value (in 10 micro degrees) to be converted.
 * @param rad A pointer to a sensor_value struct, where the result is stored.
 */
static inline void sensor_10udegrees_to_rad(int32_t d, struct sensor_value *rad)
{
    rad->val1 = ((int64_t)d * SENSOR_PI / 180LL / 100000LL) / 1000000LL;
    rad->val2 = ((int64_t)d * SENSOR_PI / 180LL / 100000LL) % 1000000LL;
}

/**
 * @brief Helper function for converting struct sensor_value to double.
 *
 * @param val A pointer to a sensor_value struct.
 * @return The converted value.
 */
static inline double sensor_value_to_double(const struct sensor_value *val)
{
    return (double)val->val1 + (double)val->val2 / 1000000;
}

/**
 * @brief Helper function for converting struct sensor_value to float.
 *
 * @param val A pointer to a sensor_value struct.
 * @return The converted value.
 */
static inline float sensor_value_to_float(const struct sensor_value *val)
{
    return (float)val->val1 + (float)val->val2 / 1000000;
}

在从驱动中拿到sensor_value后做二次转换,例如

   printf("LSM6DSV16X: Accel (m.s-2): x: %.3f, y: %.3f, z: %.3f\n",
            sensor_value_to_double(&lsm6dsv16x_accel[0]),
            sensor_value_to_double(&lsm6dsv16x_accel[1]),
            sensor_value_to_double(&lsm6dsv16x_accel[2]));

 

3.4.3 Trigger type

当传感器满足trigger触发时,会调用用户层的trigger回调。

trigger触发类型定义在sensor_trigger_type 中,列举的比较全面,其中常见的触发方式有timer触发、data_ready触发、delta触发、阈值触发、count触发、fifo触发、motion触发。


struct sensor_trigger {
        /** Trigger type. */
        enum sensor_trigger_type type;
        /** Channel the trigger is set on. */
        enum sensor_channel chan;
};
/**
 * @brief Sensor trigger types.
 */
enum sensor_trigger_type {
        /**
         * Timer-based trigger, useful when the sensor does not have an
         * interrupt line.
         */
        SENSOR_TRIG_TIMER,
        /** Trigger fires whenever new data is ready. */
        SENSOR_TRIG_DATA_READY,
        /**
         * Trigger fires when the selected channel varies significantly.
         * This includes any-motion detection when the channel is
         * acceleration or gyro. If detection is based on slope between
         * successive channel readings, the slope threshold is configured
         * via the @ref SENSOR_ATTR_SLOPE_TH and @ref SENSOR_ATTR_SLOPE_DUR
         * attributes.
         */
        SENSOR_TRIG_DELTA,
        /** Trigger fires when a near/far event is detected. */
        SENSOR_TRIG_NEAR_FAR,
        /**
         * Trigger fires when channel reading transitions configured
         * thresholds.  The thresholds are configured via the @ref
         * SENSOR_ATTR_LOWER_THRESH, @ref SENSOR_ATTR_UPPER_THRESH, and
         * @ref SENSOR_ATTR_HYSTERESIS attributes.
         */
        SENSOR_TRIG_THRESHOLD,

        /** Trigger fires when a single tap is detected. */
        SENSOR_TRIG_TAP,

        /** Trigger fires when a double tap is detected. */
        SENSOR_TRIG_DOUBLE_TAP,

        /** Trigger fires when a free fall is detected. */
        SENSOR_TRIG_FREEFALL,

        /** Trigger fires when motion is detected. */
        SENSOR_TRIG_MOTION,

        /** Trigger fires when no motion has been detected for a while. */
        SENSOR_TRIG_STATIONARY,

        /** Trigger fires when the FIFO watermark has been reached. */
        SENSOR_TRIG_FIFO_WATERMARK,

        /** Trigger fires when the FIFO becomes full. */
        SENSOR_TRIG_FIFO_FULL,
        /**
         * Number of all common sensor triggers.
         */
        SENSOR_TRIG_COMMON_COUNT,

        /**
         * This and higher values are sensor specific.
         * Refer to the sensor header file.
         */
        SENSOR_TRIG_PRIV_START = SENSOR_TRIG_COMMON_COUNT,

        /**
         * Maximum value describing a sensor trigger type.
         */
        SENSOR_TRIG_MAX = INT16_MAX,
};

 

3.4.4 传感器属性

     可进行传感器的采样率、batch、trigger触发阈值、量程设置、校准参数设置。


/**
 * @brief Sensor attribute types.
 */
enum sensor_attribute {
        /**
         * Sensor sampling frequency, i.e. how many times a second the
         * sensor takes a measurement.
         */
        SENSOR_ATTR_SAMPLING_FREQUENCY,
        /** Lower threshold for trigger. */
        SENSOR_ATTR_LOWER_THRESH,
        /** Upper threshold for trigger. */
        SENSOR_ATTR_UPPER_THRESH,
        /** Threshold for any-motion (slope) trigger. */
        SENSOR_ATTR_SLOPE_TH,
        /**
         * Duration for which the slope values needs to be
         * outside the threshold for the trigger to fire.
         */
        SENSOR_ATTR_SLOPE_DUR,
        /* Hysteresis for trigger thresholds. */
        SENSOR_ATTR_HYSTERESIS,
        /** Oversampling factor */
        SENSOR_ATTR_OVERSAMPLING,
        /** Sensor range, in SI units. */
        SENSOR_ATTR_FULL_SCALE,
        /**
         * The sensor value returned will be altered by the amount indicated by
         * offset: final_value = sensor_value + offset.
         */
        SENSOR_ATTR_OFFSET,
        /**
         * Calibration target. This will be used by the internal chip's
         * algorithms to calibrate itself on a certain axis, or all of them.
         */
        SENSOR_ATTR_CALIB_TARGET,
        /** Configure the operating modes of a sensor. */
        SENSOR_ATTR_CONFIGURATION,
        /** Set a calibration value needed by a sensor. */
        SENSOR_ATTR_CALIBRATION,
        /** Enable/disable sensor features */
        SENSOR_ATTR_FEATURE_MASK,
        /** Alert threshold or alert enable/disable */
        SENSOR_ATTR_ALERT,
        /** Free-fall duration represented in milliseconds.
         *  If the sampling frequency is changed during runtime,
         *  this attribute should be set to adjust freefall duration
         *  to the new sampling frequency.
         */
        SENSOR_ATTR_FF_DUR,

        /** Hardware batch duration in ticks */
        SENSOR_ATTR_BATCH_DURATION,

        /**
         * Number of all common sensor attributes.
         */
        SENSOR_ATTR_COMMON_COUNT,

        /**
         * This and higher values are sensor specific.
         * Refer to the sensor header file.
         */
        SENSOR_ATTR_PRIV_START = SENSOR_ATTR_COMMON_COUNT,

        /**
         * Maximum value describing a sensor attribute type.
         */
        SENSOR_ATTR_MAX = INT16_MAX,
};

3.5 框架接入

3.5.1 驱动层API && 模板

3.5.1.1 驱动层API

__subsystem struct sensor_driver_api {
    sensor_attr_set_t attr_set;
    sensor_attr_get_t attr_get;
    sensor_trigger_set_t trigger_set;
    sensor_sample_fetch_t sample_fetch;
    sensor_channel_get_t channel_get;
    sensor_get_decoder_t get_decoder;
    sensor_submit_t submit;
};

 

3.5.1.2 驱动代码模板

#define BMI270_CONFIG_SPI(inst)             \
    .bus.spi = SPI_DT_SPEC_INST_GET(        \
        inst, BMI270_SPI_OPERATION, 0),     \
    .bus_io = &bmi270_bus_io_spi,    //其中spi驱动接口实现在spi.h中,与外设驱动隔离

#define BMI270_CREATE_INST(inst)                    \
                                    \
    static struct bmi270_data bmi270_drv_##inst;            \
                                    \
    static const struct bmi270_config bmi270_config_##inst = {  \
        COND_CODE_1(DT_INST_ON_BUS(inst, spi),          \
                (BMI270_CONFIG_SPI(inst)),          \
                (BMI270_CONFIG_I2C(inst)))          \
        .feature = BMI270_FEATURE(inst),            \
        BMI270_CONFIG_INT(inst)                 \
    };                              \
                                    \
    SENSOR_DEVICE_DT_INST_DEFINE(inst,              \
                  bmi270_init,              \
                  NULL,                 \
                  &bmi270_drv_##inst,           \
                  &bmi270_config_##inst,            \
                  POST_KERNEL,              \
                  CONFIG_SENSOR_INIT_PRIORITY,      \
                  &bmi270_driver_api);

DT_INST_FOREACH_STATUS_OKAY(BMI270_CREATE_INST);


static const struct sensor_driver_api bmi270_driver_api = {
    .sample_fetch = bmi270_sample_fetch,
    .channel_get = bmi270_channel_get,
    .attr_set = bmi270_attr_set,
#if defined(CONFIG_BMI270_TRIGGER)
    .trigger_set = bmi270_trigger_set,
#endif
};

static const struct bmi270_feature_config bmi270_feature_max_fifo = {
    .name = "max_fifo",
    .config_file = bmi270_config_file_max_fifo,
    .config_file_len = sizeof(bmi270_config_file_max_fifo),
};

static const struct bmi270_feature_config bmi270_feature_base = {
    .name = "base",
    .config_file = bmi270_config_file_base,
    .config_file_len = sizeof(bmi270_config_file_base),
    .anymo_1 = &(struct bmi270_feature_reg){ .page = 1, .addr = 0x3C },
    .anymo_2 = &(struct bmi270_feature_reg){ .page = 1, .addr = 0x3E },
};

#define BMI270_FEATURE(inst) (                      \
    DT_INST_NODE_HAS_COMPAT(inst, bosch_bmi270_base) ?          \
        &bmi270_feature_base :                  \
        &bmi270_feature_max_fifo)

#if CONFIG_BMI270_TRIGGER
#define BMI270_CONFIG_INT(inst) \
    .int1 = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, irq_gpios, 0, {}),\
    .int2 = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, irq_gpios, 1, {}),
#else
#define BMI270_CONFIG_INT(inst)
#endif

  此外zephyr作为开源项目,要求驱动开发者必须实现完整的bus接口,如果某个传感器的interface接口能支持spi和i2c两种接口,那么驱动开发者就必须同时提供这两种方式的访问接口,且总线IO的c文件与驱动源代码要隔离,例如bmi270的驱动:

3e59e27044bc4a9ba8e5692146496229.png

其中bmi270_i2c.会通过i2c core的接口来操作bmi270的寄存器。

这种设计思想类似regmap,Linux 下⼤部分设备的驱动开发都是操作其内部寄存器,⽐如 I2C/SPI 设备的本质都是⼀样的,通过 I2C/SPI 接⼝读写芯⽚内部寄存器。芯⽚内部寄存器也是同样的道理,⽐如 I.MX6ULL的 PWM、定 时器等外设初始化,最终都是要落到寄存器的设置上。

3.5.2 应用层

  3.5.2.1 API

与rt-thread类似,zephyr的sensor驱动和应用层api代码也可以混在一起,zephyr使用__syscall 来标注哪些是用户层该调用的。

Zephyr在sensor.h中定义了统一的传感器驱动接口,由sensor_sample_fetch从指定传感器读取数据到内存,由sensor_channel_get来获取指fetch的数据。对于传感器阈值通知的情况,Zephyr也提供了sensor_trigger_set,注册callback函数,在传感器打到阈值时被执行。 由于不同的传感器测量物理特性值不一样,同一个传感器也会有不同物理特性测量值,zephyr 将不同的物理量抽象为channel,一个channel对应一个物理测量量。

#zephyr\include\zephyr\drivers>cat sensor.h | grep  __syscall
__syscall int sensor_attr_set(const struct device *dev,
__syscall int sensor_attr_get(const struct device *dev,
__syscall int sensor_sample_fetch(const struct device *dev);
__syscall int sensor_sample_fetch_chan(const struct device *dev,
__syscall int sensor_channel_get(const struct device *dev,
__syscall int sensor_get_decoder(const struct device *dev,
__syscall int sensor_reconfigure_read_iodev(struct rtio_iodev *iodev, const struct device *sensor,

//从传感器获取所有数据放到内存
__syscall int sensor_sample_fetch(struct device *dev);                
//从传感器获取指定channel的数据到内存
__syscall int sensor_sample_fetch_chan(struct device *dev,
                                       enum sensor_channel type);                                       
//从内存获取指定channel的数据                                           
__syscall int sensor_channel_get(struct device *dev,
                                 enum sensor_channel chan,
                                 struct sensor_value *val);
//设定传感器指定channel的参数,例如采样频率,触发阈值等                                 
__syscall int sensor_attr_set(struct device *dev,
                              enum sensor_channel chan,
                              enum sensor_attribute attr,
                              const struct sensor_value *val);
//设置传感器触发
static inline int sensor_trigger_set(struct device *dev,
                                     struct sensor_trigger *trig,
                                     sensor_trigger_handler_t handler)

__syscall声明API为系统调用API

__syscall int sensor_sample_fetch(const struct device *dev);

实现API名一定是固定模式名z_impl_<syscall API name>

 

static inline int z_impl_sensor_sample_fetch(const struct device *dev)
{
        const struct sensor_driver_api *api = (const struct sensor_driver_api *)dev->api;
        return api->sample_fetch(dev, SENSOR_CHAN_ALL);
}

验证API一定是固定模式名z_vrfy_<syscall API name>, 验证API包装实现API,与实现API具有相同的返回类型和参数类型。验证API主要是附加完成对系统调用API的参数检查和在用户模式与内核模式之间转换参数。

sensor_handlers.c中定义

#include <zephyr/syscalls/sensor_attr_get_mrsh.c>

static inline int z_vrfy_sensor_sample_fetch(const struct device *dev)
{
        K_OOPS(K_SYSCALL_DRIVER_SENSOR(dev, sample_fetch));
        return z_impl_sensor_sample_fetch((const struct device *)dev);
}
Makefile
zephyr_library_sources_ifdef(CONFIG_USERSPACE sensor_handlers.c)

sensor系统调用文件列表

syscalls/sensor.h
syscalls/sensor_sample_fetch_mrsh.c
syscalls/sensor_sample_fetch_chan_mrsh.c
syscalls/sensor_channel_get_mrsh.c
syscalls/sensor_attr_set_mrsh.c

3.5.2.2 Demo

zephyr在进行传感器操作之前,同样需要拿到device句柄。

主动获取传感器数据

void main(void)
{
        struct device *dev = DEVICE_DT_GET_ANY("DHT11");  //根据Device name获取驱动通过节点标识符获取设备指针,
        //该设备是由DEVICE_DT_DEFINE(node_id, ...)注册的
        printf("dev %p name %s\n", dev, dev->config->name);
        while (1) {
                struct sensor_value temp, humidity;
                sensor_sample_fetch(dev);                                                                        //从dh11读取数据
                sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);        //读取温度
                sensor_channel_get(dev, SENSOR_CHAN_HUMIDITY, &humidity);        //读取湿度

                printf("temp: %d.%06d; humidity: %d.%06d\n",
                      temp.val1, temp.val2, humidity.val1, humidity.val2);

                k_sleep(1000);
        }
}

 

Trigger获取传感器数据

static void trigger_handler(struct device *dev, struct sensor_trigger *trig)
{
        struct sensor_value temp;

        sensor_sample_fetch(dev);
        sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);

        printf("trigger fired, temp %d.%06d\n", temp.val1, temp.val2);
}

void main(void)
{
        struct device *dev = DEVICE_DT_GET_ANY("MCP9808");

        if (dev == NULL) {
                printf("device not found.  aborting test.\n");
                return;
        }

        struct sensor_value val;
        struct sensor_trigger trig;

        val.val1 = 26;
        val.val2 = 0;
        
        //设置26度为高阈值
        sensor_attr_set(dev, SENSOR_CHAN_AMBIENT_TEMP,
                        SENSOR_ATTR_UPPER_THRESH, &val);                

        trig.type = SENSOR_TRIG_THRESHOLD;
        trig.chan = SENSOR_CHAN_AMBIENT_TEMP;

        //当温度超过26度时触发,呼叫trigger_handler
        if (sensor_trigger_set(dev, &trig, trigger_handler)) {
                printf("Could not set trigger.  aborting test.\n");
                return;
        }


        while (1) {
                k_sleep(2000);
        }
}

3.6 参考点

zephyr的sensor框架除了不支持posix、不支持一对多实例外,其他设计都很有参考价值。

  1. 传感器数据结构可以使用同一个头文件(应用层和驱动层接口API最好能分开)。

  2. 独有的设备树机制,能够使得硬件和driver code隔离。

  3. sensor使用的接口定义清晰且全面。

  4. 驱动中需要有完整的bus接口实现,将总线驱动、设备驱动、用户接口层定义进行隔离。如总线驱动完成spi/iic读写,设备驱动主要完成与硬件寄存器相关的配置,用户接口层只实现sensor框架规定的api。

  5. 应用和驱动直接交互,缺少中间层抽象,无法支持多客户端仲裁(针对这一点,已经有人提出基于zephyr现有的sensor框架进行改进 https://esnl.hnu.edu.cn/info/1005/2643.htm)。

    Zephyr sensor框架的缺点主要体现在其sensing子系统的设计上,即多个应用调用sensing子系统时,直接使用driver的sensorAPI无法解决1对多问题。这意味着,尽管sensing子系统旨在解决多个应用同时访问传感器的问题,但在实际操作中,由于设计上的不足,无法有效地处理多个应用同时调用传感器的情况,这可能会影响到系统的性能和稳定性。

 

4 openharmony

openharmony的驱动框架HDF(Hardware Driver Foundation)引入了HCS(硬件驱动配置),HCS是HDF框架的一部分,类似linux/zephyr中的dts配置描述文件,实现了配置代码与驱动代码解耦,便于开发者进行配置管理。但同样使用起来需要了解它的语法和使用规范,这个并不是系统必须要实现的。

4.1 openharmony、HarmonyOS、LiteOS的关系

Openharmony与harmonyOS、harmony OS next

3ba1b27d832d41d8a6b593b3e3199968.png

OpenHarmony与HarmonyOS的区别?

OpenHarmony是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目(开源基金会有哪些:https://baijiahao.baidu.com/s?id=1711027808899578520&wfr=spider&for=pc),它是鸿蒙操作系统的基础版本,提供了一个全面的、基于微内核的分布式操作系统框架。OpenHarmony 主要包含操作系统的最基础部分,任何个人或组织都可以基于 OpenHarmony 进行二次开发和创新,类似于 Android。

HarmonyOS,则是华为基于 OpenHarmony 开源项目进一步开发的面向不同设备的商业化操作系统。它包含了 OpenHarmony 的所有基础能力,并加入了华为自研的HMS服务、UI 框架、系统应用以及一系列优化和增强功能,为用户提供完整的、经过严格测试和优化的商业级操作系统体验。HarmonyOS 主要应用于华为的各种智能设备,包括手机、平板、智能穿戴设备、智慧屏等,旨在实现不同设备之间的无缝协同,同时也向第三方合作伙伴的设备开放。

LiteOS与OpenHarmony的关系?

LiteOS 是华为开发的一款轻量级实时操作系统,特别适合物联网(IoT)设备,强调低功耗、快速启动和小体积。在 OpenHarmony 中,LiteOS 被用作其中一个可选的内核之一,尤其是在资源受限的设备上,如微控制器单元(MCU)等。这意味着 OpenHarmony 在设计时考虑到了不同应用场景的需求,可以根据设备的硬件能力选择使用 LiteOS 或 Linux 作为中央内核,以达到最佳的系统性能和资源利用效率。

因此,LiteOS 与 OpenHarmony 是一种组件与平台的关系,LiteOS 作为组件之一集成在 OpenHarmony 中,为特定类型的物联网设备提供运行时支持。而 OpenHarmony 则是一个更大的生态系统,它不仅包含了 LiteOS,还包含了更多系统服务和框架层内容,以及支持多种设备和场景的应用开发能力。

OpenHarmony里的LiteOS是否为完整版LiteOS?

OpenHarmony中的LiteOS实际上并不是“完整版”的LiteOS,而是根据OpenHarmony的需求和架构进行定制和集成的LiteOS内核版本。OpenHarmony会根据系统类型(如标准系统、小型系统等)和目标设备的需求,对LiteOS进行相应的功能裁剪和优化,使其更适合于特定的硬件平台和应用场景。因此,OpenHarmony中的LiteOS更像是LiteOS的一个定制版或增强版,这种定制化使得LiteOS在OpenHarmony中能够更好地服务于物联网设备,同时保持系统的轻量化和高效性。

liteOS: https://gitee.com/LiteOS/LiteOS

openharmony liteos_m https://gitee.com/openharmony/kernel_liteos_m

4.2 liteos的三个版本

类型处理器最小内存能力
轻量系统(mini system)litsos_mMCU类处理器(例如Arm Cortex-M、RISC-V 32位的设备)128KiB提供多种轻量级网络协议,轻量级的图形框架,以及丰富的IOT总线读写部件等。可支撑的产品如智能家居领域的连接类模组、传感器设备、穿戴类设备等。
小型系统(small system)应用处理器(例如Arm Cortex-A的设备)1MiB提供更高的安全能力、标准的图形框架、视频编解码的多媒体能力。可支撑的产品如智能家居领域的IP Camera、电子猫眼、路由器以及智慧出行域的行车记录仪等。
标准系统(standard system)应用处理器(例如Arm Cortex-A的设备)128MiB提供增强的交互能力、3D GPU以及硬件合成能力、更多控件以及动效更丰富的图形能力、完整的应用框架。可支撑的产品如高端的冰箱显示屏。

4.3 openharmony源码目录

a7f3fc86a9ad4ec8b159754c115037d3.png

可以看出当前开放出来的只支持3个os,linux、liteos_a,liteos_m。

看一下openharmony为了适配linux-5.10做的改动:

d32539c4865e45f5ac43375d40fb1f9f.png

openharmony把HDF框架移植到了linux-5.10。

4.4 驱动架构

openharmony的sensor框架是基于其驱动框架实现的,需要先了解一下驱动框架。

HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。

HDF驱动架构采用C语言面向对象编程模型构建,通过平台解耦、内核解耦,来达到兼容不同内核,统一平台底座的目的。HDF驱动框架架构如下图所示。

4.4.1 驱动框架图

613e875fd22a45808dcb242653946963.png

 

751af42c44644b7ea8fbc188062fd3e9.png

HDF驱动架构主要组成部分:

  • HDI(Hardware Device Interface,硬件设备统一接口)层:通过规范化的设备接口标准,为系统提供统一、稳定的硬件设备操作接口。

  • HDF驱动框架:提供统一的硬件资源管理、驱动加载管理、设备节点管理、设备电源管理以及驱动服务模型等功能,需要包含设备管理、服务管理、DeviceHost、PnPManager等模块。

  • 统一的配置界面:支持硬件资源的抽象描述,屏蔽硬件差异,可以支撑开发者开发出与配置信息不绑定的通用驱动代码,提升开发及迁移效率,并可通过HC-Gen等工具快捷生成配置文件。

  • 操作系统抽象层(OSAL,Operating System Abstraction Layer):提供统一封装的内核操作相关接口,屏蔽不同系统操作差异,包含内存、锁、线程、信号量等接口。

  • 平台驱动:为外设驱动提供Board硬件(如:I2C/SPI/UART总线等平台资源)操作统一接口,同时对Board硬件操作进行统一的适配接口抽象以便于不同平台迁移。

  • 外设驱动模型:面向外设驱动,提供常见的驱动抽象模型,主要达成两个目的,提供标准化的器件驱动,开发者无需独立开发,通过配置即可完成驱动的部署;提供驱动模型抽象,屏蔽驱动与不同系统组件间的交互,使得驱动更具备通用性。

 

b28cfa880a254255a7bfbaaabce32629.png

385823ce8ee0426c98e4c57105fdcab5.png

a91ba478ead94e37b3cd550408a028d0.png

4.4.2 驱动代码目录

OpenHarmony驱动子系统核心源码信息在drivers/hdf_core/framework目录,其中包括驱动框架、配置管理、配置解析、驱动通用框架模型、硬件通用平台能力接口等。

/drivers/hdf_core/framework
├── core           #实现驱动框架的核心代码
│   ├── adapter    #实现对内核操作接口适配,提供抽象化的接口供开发者使用
│   ├── common     #驱动框架公共基础代码
│   ├── host       #驱动宿主环境模块
│   ├── manager    #驱动框架管理模块
│   └── shared     #host和manager共享模块代码
├── include        #驱动框架对外提供能力的头文件
│   ├── audio      #AUDIO对外提供能力的头文件
│   ├── bluetooth  #蓝牙对外提供能力的头文件
│   ├── core       #驱动框架对外提供的头文件
│   ├── ethernnet  #以太网操作相关的头文件
│   ├── net        #网络数据操作相关的头文件
│   ├── osal       #系统适配相关接口的头文件
│   ├── platform   #平台设备相关接口的头文件
│   ├── utils      #驱动框架公共能力的头文件
│   └── wifi       #WLAN对外提供能力的头文件
├── model          #提供驱动通用框架模型
│   ├── audio      #AUDIO框架模型
│   ├── display    #显示框架模型
│   ├── input      #输入框架模型
│   ├── misc       #杂项设备框架模型,包括dsoftbus、light、vibrator
│   ├── network    #WLAN框架模型
│   └── sensor     #Sensor驱动模型
│   └── storage    #存储驱动模型
│   └── usb        #USB驱动模型
├── sample         #HCS配置描述示例及HDF驱动示例
├── support        #提系统的基础能力 
│   └── platform   #平台设备驱动框架及访问接口,范围包括GPIO、I2C、SPI等
│   └── posix      #posix框架及访问接口,范围包括Mem、Mutex、Sem、Spinlock、Thread、Time等
├── test           #测试用例├── tools          #hdf框架工具相关的源码
│   └── hc-gen     #配置管理工具源码
│   └── hcs-view   #
│   └── hdf-dbg    #
│   └── hdf-dev_eco_tool #
│   └── hdf-gen    #
│   └── idl-gen    #
│   └── leagecy    #
└── utils          #提供基础数据结构和算法等

 

sensor驱动模型在model/sensor目录。

4.5 sensor框架

openharmony的sensor驱动是外设驱动的一种,源码目录OpenHarmony/drivers/peripheral/sensor/

4.5.1 简介

Sensor驱动模型屏蔽硬件器件差异,为上层Sensor服务系统提供稳定的Sensor基础能力接口,包括Sensor列表查询、Sensor启停、Sensor订阅及取消订阅,Sensor参数配置等功能。Sensor设备驱动的开发是基于HDF驱动框架基础上,结合操作系统适配层(OSAL)和平台驱动接口(比如I2C/SPI/UART总线等平台资源)能力,屏蔽不同操作系统和平台总线资源差异,实现Sensor驱动“一次开发,多系统部署”的目标。Sensor驱动模型如图1所示。

图 1 Sensor驱动模型图

cec1a39cc4de4c46a9b4de70e962d41a.png

4.5.2 基本概念

目前根据sensorId将Sensor分为医学类Sensor、传统类Sensor两种。

  • 医学类Sensor:已订阅的sensorId枚举值在128-160范围的为医学类Sensor。

  • 传统类Sensor:已订阅的sensorId枚举值不在128-160范围的为传统类Sensor。

4.5.3 运作机制

通过介绍Sensor驱动模型的加载以及运行流程,对模型内部关键组件以及关联组件之间的关系进行了划分,整体加载流程如图2所示:

图 2 Sensor驱动运行图

5f8a08acd531404dbb1e1af935968f25.png

Sensor驱动模型以标准系统RK3568产品中的加速度传感器驱动为例,介绍整个驱动加载及运行流程:

  1. 从device_info.hcs配置文件中的Sensor Host读取到Sensor设备管理配置信息。

  2. HDF配置框架从HCB数据库中解析Sensor设备管理配置信息,并关联对应设备驱动。

  3. 加载并初始化Sensor设备管理驱动。

  4. Sensor设备管理驱动向HDI发布Sensor基础能力接口。

  5. 从device_info.hcs配置文件中的Sensor Host读取到加速度传感器驱动配置信息。

  6. 加载加速度传感器抽象驱动,调用初始化接口,完成Sensor器件的驱动资源分配和数据处理队列的创建。

  7. 从accel_xxx_config.hcs配置文件中读取到加速度传感器差异化驱动配置和私有化配置信息。

  8. 加速度传感器芯片差异化驱动,调用通用配置解析接口,完成器件属性信息解析,器件寄存器解析。

  9. 加速度传感器芯片差异化驱动完成器件的探测,并分配加速度传感器配置资源和加速度传感器差异化接口注册。

  10. 加速度传感器成功探测到器件之后,加速度传感器芯片差异化驱动通知加速度传感器抽象驱动,注册加速度传感器设备到Sensor设备管理中。

4.5.4 接口说明

Sensor驱动模型对外开放的API接口能力如下:

  • 提供Sensor HDI(Hardware Device Interface)能力接口,简化服务开发。

  • 提供Sensor驱动模型能力接口:

    • 依赖HDF驱动框架实现Sensor器件驱动的加载、器件探测、注册和去注册等能力。

    • 提供同一类型Sensor器件驱动归一接口、寄存器配置解析操作接口、总线访问抽象接口和平台抽象接口。

  • 提供开发者实现的能力接口:依赖HDF驱动框架的HCS(HDF Configuration Source)配置管理,根据同类型Sensor差异化配置,实现Sensor器件参数序列化配置和器件部分操作接口,简化Sensor器件驱动开发。

Sensor驱动模型对外开放的API接口能力的具体实现请参考:

表 1 Sensor驱动模型对外API接口功能介绍

注:以下接口列举的为C接口,接口声明见文件/drivers/peripheral/sensor/interfaces/include。

公共接口

/OpenHarmony/drivers/peripheral/sensor/interfaces/include ((OpenHarmony-v4.1-Release))
$ cat *.h | grep ");"
 * Licensed under the Apache License, Version 2.0 (the "License");
    int32_t (*GetAllSensors)(struct SensorInformation **sensorInfo, int32_t *count);
    int32_t (*Enable)(int32_t sensorId);
    int32_t (*Disable)(int32_t sensorId);
    int32_t (*SetBatch)(int32_t sensorId, int64_t samplingInterval, int64_t reportInterval);
    int32_t (*SetMode)(int32_t sensorId, int32_t mode);
    int32_t (*SetOption)(int32_t sensorId, uint32_t option);
    int32_t (*Register)(int32_t groupId, RecordDataCallback cb);
    int32_t (*Unregister)(int32_t groupId, RecordDataCallback cb);
    int32_t (*ReadData)(int32_t sensorId, struct SensorEvents *event);
    int32_t (*GetSdcSensorInfo)(struct SdcSensorInfo sdcSensorInfo[]);
const struct SensorInterface *NewSensorInterfaceInstance(void);
int32_t FreeSensorInterfaceInstance(void);
 * Licensed under the Apache License, Version 2.0 (the "License");
typedef int32_t (*RecordDataCallback)(const struct SensorEvents*);

 

对应的实现代码:

class SensorImpl : public ISensorInterfaceVdi, public RefBase {
public:
    SensorImpl(): sensorInterface(NULL) {}

    virtual ~SensorImpl();
    int32_t Init() override;

    int32_t GetAllSensorInfo(std::vector<HdfSensorInformationVdi>& info) override;
    int32_t Enable(int32_t sensorId) override;
    int32_t Disable(int32_t sensorId) override;
    int32_t SetBatch(int32_t sensorId, int64_t samplingInterval, int64_t reportInterval) override;
    int32_t SetMode(int32_t sensorId, int32_t mode) override;
    int32_t SetOption(int32_t sensorId, uint32_t option) override;
    int32_t Register(int32_t groupId, const sptr<ISensorCallbackVdi>& callbackObj) override;
    int32_t Unregister(int32_t groupId, const sptr<ISensorCallbackVdi>& callbackObj) override;
    int32_t GetSdcSensorInfo(std::vector<SdcSensorInfoVdi>& sdcSensorInfoVdi) override;
private:
    const SensorInterface *sensorInterface;
    int32_t UnregisterImpl(int32_t groupId, IRemoteObject *callbackObj);
};

 

驱动层接口


#ifndef SENSOR_DEVICE_IF_H
#define SENSOR_DEVICE_IF_H

#include "sensor_device_type.h"

struct SensorOps {
    int32_t (*Enable)(void);
    int32_t (*Disable)(void);
    int32_t (*SetBatch)(int64_t samplingInterval, int64_t reportInterval);
    int32_t (*SetMode)(int32_t mode);
    int32_t (*SetOption)(uint32_t option);
};

struct SensorDeviceInfo {
    struct SensorBasicInfo sensorInfo;
    struct SensorOps ops;
};

用户层代码

用户层代码主要是看在openharmony系统中订阅一个sensor的执行流程。

void SensorSample(void)
{
    int ret;
    struct SensorInformation *sensorInfo = NULL;
    int32_t count = 0;
    int32_t sensorInterval = 200000000; /* 数据采样率设置200毫秒,单位纳秒 */

    /* 1.创建传感器接口实例 */
    struct SensorInterface *sensorDev = NewSensorInterfaceInstance();
    if (sensorDev == NULL) {
        return;
    }
    /* 2.订阅者注册传感器数据回调处理函数 */
    ret = sensorDev->Register(0, SensorDataCallback);
    if (ret != 0) {
        return;
    }
    /* 3.获取设备支持的Sensor列表 */
    ret = GetAllSensors(&sensorInfo, &count);
    if (ret != 0) {
        return;
    }
    /* 4.设置传感器采样率 */
    ret = SetBatch(SENSOR_TYPE_ACCELEROMETER, sensorInterval, 0);
    if (ret != 0) {
        return;
    }
    /* 5.使能传感器 */
    ret = Enable(SENSOR_TYPE_ACCELEROMETER);
    if (ret != 0) {
        return;
    }
    /* 6.去使能传感器 */
    ret = Disable(SENSOR_TYPE_ACCELEROMETER);
    if (ret != 0) {
        return;
    }
    /* 7.取消传感器数据订阅函数 */
    ret = Unregister(0);
    if (ret != 0) {
        return;
    }
    /* 8.释放传感器接口实例 */
    ret = FreeSensorInterfaceInstance();
    if (ret != 0) {
        return;
    }
}

 

sensor_mamager.c

struct SensorDevManager *GetSensorDevManager(void)
{
    static struct SensorDevManager devManager = {
        .initState = false,
        .hasSensorListener = false,
        .sensorSum = 0,
        .sensorInfoEntry = NULL,
    };

    return &devManager;
}
const struct SensorInterface *NewSensorInterfaceInstance(void)
{
    static struct SensorInterface sensorDevInstance;
    struct SensorDevManager *manager = GetSensorDevManager();

    if (manager->initState) {
        return &sensorDevInstance;
    }

    // Construct device interface instance
    GetSensorDeviceMethods(&sensorDevInstance);//sensor_ctrol.c GetAllSensors 对应于 GetSensorInfo

    if (InitSensorManager() != SENSOR_SUCCESS) {
        HDF_LOGE("%{public}s: Sensor init manager failed", __func__);
        return NULL;
    }
    manager->initState = true;
    HDF_LOGD("%{public}s: Get sensor device instance success", __func__);
    return &sensorDevInstance;
}

//sesnor_ctrol.c 实现GetSensorDeviceMethods
void GetSensorDeviceMethods(struct SensorInterface *device)
{
    CHECK_NULL_PTR_RETURN(device);
    device->GetAllSensors = GetSensorInfo;
    device->Enable = EnableSensor;
    device->Disable = DisableSensor;
    device->SetBatch = SetSensorBatch;
    device->SetMode = SetSensorMode;
    device->SetOption = SetSensorOption;
    device->Register = Register;
    device->Unregister = Unregister;
}

static int32_t EnableSensor(int32_t sensorId)
{
    struct HdfSBuf *msg = HdfSbufObtainDefaultSize();
    if (msg == NULL) {
        HDF_LOGE("%{public}s: Failed to obtain sBuf size", __func__);
        return SENSOR_FAILURE;
    }

    if (!HdfSbufWriteInt32(msg, sensorId)) {
        HDF_LOGE("%{public}s: Sensor write id failed", __func__);
        HdfSbufRecycle(msg);
        return SENSOR_FAILURE;
    }

    if (!HdfSbufWriteInt32(msg, SENSOR_OPS_IO_CMD_ENABLE)) {
        HDF_LOGE("%{public}s: Sensor write enable failed", __func__);
        HdfSbufRecycle(msg);
        return SENSOR_FAILURE;
    }

    int32_t ret = SendSensorMsg(sensorId, msg, NULL);
    if (ret != SENSOR_SUCCESS) {
        HDF_LOGE("%{public}s: Sensor enable failed, ret[%{public}d]", __func__, ret);
    }
    HdfSbufRecycle(msg);

    return ret;
}

4.5.5 数据结构

openharmony的传感器数据结构定义较为混乱。

公用数据结构

drivers\peripheral\sensor\interfaces\include\sensor_type.h

定义了传感器的类型、传感器基础信息和传感器事件儿。

/**
 * @brief Enumerates sensor types.
 *
 * @since 2.2
 */
enum SensorTypeTag {
    SENSOR_TYPE_NONE                = 0,   /**< None, for testing only */
    SENSOR_TYPE_ACCELEROMETER       = 1,   /**< Acceleration sensor */
    SENSOR_TYPE_GYROSCOPE           = 2,   /**< Gyroscope sensor */
    SENSOR_TYPE_PHOTOPLETHYSMOGRAPH = 3,   /**< Photoplethysmography sensor */
    SENSOR_TYPE_ELECTROCARDIOGRAPH  = 4,   /**< Electrocardiogram (ECG) sensor */
    SENSOR_TYPE_AMBIENT_LIGHT       = 5,   /**< Ambient light sensor */
    SENSOR_TYPE_MAGNETIC_FIELD      = 6,   /**< Magnetic field sensor */
    SENSOR_TYPE_CAPACITIVE          = 7,   /**< Capacitive sensor */
    SENSOR_TYPE_BAROMETER           = 8,   /**< Barometric pressure sensor */
    SENSOR_TYPE_TEMPERATURE         = 9,   /**< Temperature sensor */
    SENSOR_TYPE_HALL                = 10,  /**< Hall effect sensor */
    SENSOR_TYPE_GESTURE             = 11,  /**< Gesture sensor */
    SENSOR_TYPE_PROXIMITY           = 12,  /**< Proximity sensor */
    SENSOR_TYPE_HUMIDITY            = 13,  /**< Humidity sensor */
    SENSOR_TYPE_COLOR               = 14,  /**< Color sensor */
    SENSOR_TYPE_SAR                 = 15,  /**< SAR sensor */
    SENSOR_TYPE_AMBIENT_LIGHT1      = 16,  /**< Secondary ambient light sensor*/
    SENSOR_TYPE_HALL1               = 17,  /**< Secondary hall effect sensor */
    SENSOR_TYPE_MEDICAL_BEGIN       = 128, /**< The begin of medical sensorId enumeration value range */
    SENSOR_TYPE_MEDICAL_END         = 160, /**< The end of medical sensorId enumeration value range */
    SENSOR_TYPE_PHYSICAL_MAX        = 255, /**< Maximum type of a physical sensor */
    SENSOR_TYPE_ORIENTATION         = 256, /**< Orientation sensor */
    SENSOR_TYPE_GRAVITY             = 257, /**< Gravity sensor */
    SENSOR_TYPE_LINEAR_ACCELERATION = 258, /**< Linear acceleration sensor */
    SENSOR_TYPE_ROTATION_VECTOR     = 259, /**< Rotation vector sensor */
    SENSOR_TYPE_AMBIENT_TEMPERATURE = 260, /**< Ambient temperature sensor */
    SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED = 261,   /**< Uncalibrated magnetic field sensor */
    SENSOR_TYPE_GAME_ROTATION_VECTOR        = 262,   /**< Game rotation vector sensor */
    SENSOR_TYPE_GYROSCOPE_UNCALIBRATED      = 263,   /**< Uncalibrated gyroscope sensor */
    SENSOR_TYPE_SIGNIFICANT_MOTION          = 264,   /**< Significant motion sensor */
    SENSOR_TYPE_PEDOMETER_DETECTION         = 265,   /**< Pedometer detection sensor */
    SENSOR_TYPE_PEDOMETER                   = 266,   /**< Pedometer sensor */
    SENSOR_TYPE_POSTURE                     = 267,   /**< Posture sensor */
    SENSOR_TYPE_HEADPOSTURE                 = 268,   /**< Headposture sensor */
    SENSOR_TYPE_DROP_DETECT                 = 269,   /**< Drop detection sensor */
    SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR = 277,   /**< Geomagnetic rotation vector sensor */
    SENSOR_TYPE_HEART_RATE                  = 278,   /**< Heart rate sensor */
    SENSOR_TYPE_DEVICE_ORIENTATION          = 279,   /**< Device orientation sensor */
    SENSOR_TYPE_WEAR_DETECTION              = 280,   /**< Wear detection sensor */
    SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED  = 281,   /**< Uncalibrated acceleration sensor */
    SENSOR_TYPE_MAX,                                 /**< Maximum number of sensor types */
};

/**
 * @brief Defines basic sensor information.
 *
 * Information about a sensor includes the sensor name, vendor, firmware version, hardware version, sensor type ID,
 * sensor ID, maximum measurement range, accuracy, and power.
 *
 * @since 2.2
 */
struct SensorInformation {
    char sensorName[SENSOR_NAME_MAX_LEN]; /**< Sensor name */
    char vendorName[SENSOR_NAME_MAX_LEN]; /**< Sensor vendor */
    char firmwareVersion[SENSOR_VERSION_MAX_LEN]; /**< Sensor firmware version */
    char hardwareVersion[SENSOR_VERSION_MAX_LEN]; /**< Sensor hardware version */
    int32_t sensorTypeId; /**< Sensor type ID (described in {@link SensorTypeTag}) */
    int32_t sensorId;     /**< Sensor ID, defined by the sensor driver developer */
    float maxRange;       /**< Maximum measurement range of the sensor */
    float accuracy;       /**< Sensor accuracy */
    float power;          /**< Sensor power */
};

/**
 * @brief Defines the data reported by the sensor.
 *
 * The reported sensor data includes the sensor ID, sensor algorithm version, data generation time,
 * data options (such as the measurement range and accuracy), data reporting mode, data address, and data length.
 *
 * @since 2.2
 */
struct SensorEvents {
    int32_t sensorId;  /**< Sensor ID */
    int32_t version;   /**< Sensor algorithm version */
    int64_t timestamp; /**< Time when sensor data was generated */
    uint32_t option;   /**< Sensor data options, including the measurement range and accuracy */
    int32_t mode;      /**< Sensor data reporting mode */
    uint8_t *data;     /**< Sensor data address */
    uint32_t dataLen;  /**< Sensor data length */
};

传感器内容数据结构

openharmony的sensor数据结构比较分散,其中传感器数据内容的结构体定义在drivers/hdf_core/framework/model/sensor/目录。每个传感器的数据类型都有独立的头文件

87afeb338afb4939859a018c8f21c369.png

 

4.6 Sensor服务概述(sensors_sensor_lite)

sensors_sensor_lite是基于openharmony原有的sensor框架封装了一层上层使用api。

3db1824155624d7a98091a160259117f.png

  • Sensor API:提供传感器的基础API,主要包含查询传感器的列表、订阅/取消传感器数据、执行控制命令等,简化应用开发。

  • Sensor Framework:主要实现传感器的订阅管理、数据通道的创建、销毁等,实现与传感器服务层的通信。

  • Sensor Service:主要实现HDF层数据接收、解析、分发,对设备传感器的管理,数据上报管理以及传感器权限管控等。

typedef struct SensorFeatureApi {
    INHERIT_SERVER_IPROXY;
    int32_t (*GetAllSensors)(SensorInfo **sensorInfo, int32_t *count);
    int32_t (*ActivateSensor)(int32_t sensorId, const SensorUser *user);
    int32_t (*DeactivateSensor)(int32_t sensorId, const SensorUser *user);
    int32_t (*SetBatch)(int32_t sensorId, const SensorUser *user, int64_t samplingInterval, int64_t reportInterval);
    int32_t (*SubscribeSensor)(int32_t sensorId, const SensorUser *user);
    int32_t (*UnsubscribeSensor)(int32_t sensorId, const SensorUser *user);
    int32_t (*SetMode)(int32_t sensorId, const SensorUser *user, int32_t mode);
    int32_t (*SetOption)(int32_t sensorId, const SensorUser *user, int32_t option);
} SensorFeatureApi;

 

表1 Sensor服务框架API接口功能介绍

接口名作用接口说明参数要求
int32_t GetAllSensors(
SensorInfo **sensorIn
fo, int32_t *count)
获取系统中注册的所有传感器信息,一组完整传感器信息包括传感器名字、设备厂商、固件版本号、硬件版本号、传感器类型编号、传感器标识、最大量程、精度、功耗。
 
作用:获取系统中所有传感器的信息。
返回值:0表示成功,其他返回值表示失败。
sensorInfo(NOT NULL):输出系统中所有传感器的信息;
count(NOT NULL):输出系统中所有传感器的数量。
int32_t SubscribeSensor
(int32_t sensorTypeId,
SensorUser *user)
数据订阅者,才能获取订阅的传感器数据。
 
作用:订阅传感器数据,系统会将获取到的传感器数据上报给订阅者。
返回值: 0为成功,其他返回值表示失败。
sensorTypeId:唯一标识一个传感器类型;
user(NOT NULL):传感器的用户,用于从传感器获取数据,一般一个用户只属于一个传感器。
int32_t UnsubscribeSensor
(int32_t sensorTypeId, SensorUser *user)
取消订阅
 
作用:去订阅传感器数据,系统将取消传感器数据上报给订阅者。
返回值:0为成功,其他返回值表示失败。
sensorTypeId:唯一标识一个传感器类型;
user(NOT NULL):传感器的用户,用于从传感器获取数据,一般一个用户只属于一个传感器。
int32_t SetBatch(int32_t sensorTypeId, SensorUser
*user, int64_t samplingInterval, int64_t reportInterval)
设置指定传感器的数据采样间隔和数据上报间隔。
 
作用:设置传感器的数据采样间隔和数据上报间隔
返回值:0为成功,其他返回值表示失败。
sensorTypeId:唯一标识一个传感器类型;
user(NOT NULL):传感器的用户,用于从传感器获取数据,一般一个用户只属于一个传感器;
samplingInterval:传感器数据采样间隔,单位纳秒;
reportInterval:传感器数据上报间隔,单位纳秒。
int32_t ActivateSensor(int32_t sensorTypeId,
SensorUser *user)
传感器使能,一般首次被订阅会执行,内部会调用driver ops的enable函数。作用:使能一个传感器订阅用户。
返回值:0为成功,其他返回值表示失败。
sensorTypeId:唯一标识一个传感器类型;
user(NOT NULL):传感器的用户,用于从传感器获取数据,一般一个用户只属于一个传感器。
int32_t DeactivateSensor(int32_t sensor
TypeId, SensorUser *user)
传感器关闭,一般末次被取消订阅会执行,内部会调用driver ops的disable函数。作用:去使能一个传感器订阅用户
返回值:0为成功,其他返回值表示失败。
sensorTypeId:唯一标识一个传感器类型;
user(NOT NULL):传感器的用户,用于从传感器获取数据,一般一个用户只属于一个传感器。
int32_t SetMode(int32_t sensorTypeId,
SensorUser *user, int32_t mode)
设置指定传感器的工作模式,不同的工作模式,上报数据方式不同。
 
作用:设置传感器的工作模式
返回值:0为成功,其他返回值表示失败。
sensorTypeId:唯一标识一个传感器类型;
user(NOT NULL):传感器的用户,用于从传感器获取数据,一般一个用户只属于一个传感器;
mode:传感器的数据上报模式。

4.7 参考点

  1. 类似dts的HCS机制,将硬件和驱动 code隔离。

  2. 提供了OSAL层,能对多个OS能进行适配。

  3. 类似RT-THread,有独有的自动加载流程。

  4. sensor的数据结构定义比较混乱,没啥可参考的,但是API可以参考,如enable、disable、setbatch、setopt、Register、unRegister等接口是比较符合使用场景的。不过其Register是通过callback的全局数组来实现的,不支持重复占用使用,模块间还是会有耦合,不如zephyr的set_trigger设计好。


/**
 * @brief Enumerates hardware service group for sensors
 *
 * @since 2.2
 */
enum SensorGroupType {
    TRADITIONAL_SENSOR_TYPE = 0, /**< traditional sensor type, the sensorId enumeration value range is 128-160 */
    MEDICAL_SENSOR_TYPE = 1,  /**< medical sensor type, the sensorId enumeration value range is not within 128-160 */
    SENSOR_GROUP_TYPE_MAX,          /**< Maximum sensor type*/
};

int32_t Register(int32_t groupId, RecordDataCallback cb)
{
    if (groupId < TRADITIONAL_SENSOR_TYPE || groupId > MEDICAL_SENSOR_TYPE) {
        HDF_LOGE("%{public}s: groupId [%{public}d] out of range", __func__, groupId);
        return SENSOR_INVALID_PARAM;
    }
    struct SensorDevManager *manager = NULL;
    CHECK_NULL_PTR_RETURN_VALUE(cb, SENSOR_NULL_PTR);
    manager = GetSensorDevManager();
    (void)OsalMutexLock(&manager->eventMutex);
    if (manager->recordDataCb[groupId] != NULL) {//如果已经被占用则返回失败
        HDF_LOGE("%{public}s: groupId [%{public}d] callback already exists", __func__, groupId);
        (void)OsalMutexUnlock(&manager->eventMutex);
        return SENSOR_FAILURE;
    }

    if (manager->serviceGroup != NULL) {
        manager->recordDataCb[groupId] = cb;//占用通道callback
        (void)OsalMutexUnlock(&manager->eventMutex);
        return SENSOR_SUCCESS;
    }
    int32_t ret = AddSensorDevServiceGroup();
    if (ret == SENSOR_SUCCESS) {
        manager->recordDataCb[groupId] = cb;
    }
    (void)OsalMutexUnlock(&manager->eventMutex);
    return ret;
}

 

5 Nuttx

5.1 框架概览

框架特点:

  1. ⽀持33种类型的物理sensor,覆盖⼏乎所有智能设备。⽀持⾃定义类型,兼容不规则设备。

  2. ⽀持抽象、远程sensor创建,覆盖算法、状态、远程等主题数据发布和订阅。

  3. 统⼀驱动操作集(激活、采样率、batch、selftest、calibrate等),⽀持⾃定义ioctl。

  4. ⾃动管理所有sensor,⾃动开关控制、降采样和数据发布,⽀持多应⽤读取和控制。

  5. 统⼀sensor数据结构和规范单位,⽅便传输和处理。可⾃定义类型,⽀持特定事件结构。

  6. ⽀持中断、轮询、主动读取三种⼯作⽅式,驱动编写者灵活配置。

  7. 驱动框架内部采⽤环形buffer,弥补应⽤因卡顿延迟未及时读取造成的数据丢失问题。

  8. ⽀持跨核分布式访问sensor主题数据。

  9. Sensor驱动和uORB框架相结合。

8b33eceef413422f9f27b4ec6b745a8e.png

5.2 结构体定义

5.2.1 sensor.h

  sensor.h所定义的接口、数据结构并非最优解,主要是参考其封装思想。

  sensor.h定义了sensor type, 传感器内容的数据结构、传感器状态信息。

#define SENSOR_TYPE_CUSTOM                          0

/* Accelerometer
 * All values are in SI units (m/s^2), and measure the acceleration of the
 * device minus the acceleration dut to gravity.
 */

#define SENSOR_TYPE_ACCELEROMETER                   1
......
#define SENSOR_TYPE_GAS                             33

struct sensor_accel         /* Type: Accerometer */
{
  uint64_t timestamp;       /* Units is microseconds */
  float x;                  /* Axis X in m/s^2 */
  float y;                  /* Axis Y in m/s^2 */
  float z;                  /* Axis Z in m/s^2 */
  float temperature;        /* Temperature in degrees celsius */
};

...
struct sensor_rgb           /* Type: RGB */
{
  uint64_t timestamp;       /* Units is microseconds */
  float r;                  /* Units is percent */
  float g;                  /* Units is percent */
  float b;                  /* Units is percent */
};
struct sensor_gps           /* Type: Gps */
{
  uint64_t timestamp;       /* Time since system start, Units is microseconds */

  /* This is the timestamp which comes from the gps module. It might be
   * unavailable right after cold start, indicated by a value of 0,
   * Units is microseconds
   */

  uint64_t time_utc;

  float latitude;           /* Unit is degrees */
  float longitude;          /* Unit is degrees */
  float altitude;           /* Altitude above MSL(mean seal level), Unit is SI m */
  float altitude_ellipsoid; /* Altitude bove Ellipsoid, Unit is SI m */

  float eph;                /* GPS horizontal position accuracy (metres) */
  float epv;                /* GPS vertical position accuracy (metres) */

  float hdop;               /* Horizontal dilution of precision */
  float pdop;               /* Position dilution of precision */
  float vdop;               /* Vertical dilution of precision */

  float ground_speed;       /* GPS ground speed, Unit is m/s */

  /* Course over ground (NOT heading, but direction of movement),
   * Unit is Si degrees
   */

  float course;

  uint32_t satellites_used; /* Number of satellites used */
};

struct sensor_ppgq          /* Type: PPDQ */
{
  uint64_t timestamp;       /* Unit is microseconds */
  uint32_t ppg[4];          /* PPG from 4 channels. Units are ADC counts. */
  uint32_t current;         /* LED current. Unit is uA. */
  uint16_t gain[4];         /* ADC gains of channels. Units are V/V or V/A. */
};

struct sensor_ecg           /* Type: ECG */
{
  uint64_t timestamp;       /* Unit is microseconds */
  float ecg;                /* Unit is μV */
  uint32_t status;          /* Status info */
};

struct sensor_ops_s
{
  CODE int (*open)(FAR struct sensor_lowerhalf_s *lower,
                   FAR struct file *filep);
  CODE int (*close)(FAR struct sensor_lowerhalf_s *lower,
                    FAR struct file *filep);
  CODE int (*activate)(FAR struct sensor_lowerhalf_s *lower,
                       FAR struct file *filep, bool enable);
  CODE int (*set_interval)(FAR struct sensor_lowerhalf_s *lower,
                           FAR struct file *filep,
                           FAR unsigned long *period_us);
  CODE int (*batch)(FAR struct sensor_lowerhalf_s *lower,
                    FAR struct file *filep,
                    FAR unsigned long *latency_us);
  CODE int (*fetch)(FAR struct sensor_lowerhalf_s *lower,
                    FAR struct file *filep,
                    FAR char *buffer, size_t buflen);
  CODE int (*set_calibvalue)(FAR struct sensor_lowerhalf_s *lower,
                             FAR struct file *filep,
                             unsigned long arg);
  CODE int (*control)(FAR struct sensor_lowerhalf_s *lower,
                      FAR struct file *filep,
                      int cmd, unsigned long arg);                              
}

typedef CODE ssize_t (*sensor_push_event_t)(FAR void *priv,
                                            FAR const void *data,
                                                   size_t bytes);
//sensor 驱动状态信息                                                   
struct sensor_state_s
{
  unsigned long esize;         /* The element size of circular buffer */
  unsigned long nbuffer;       /* The number of events that the circular buffer can hold */
  unsigned long min_latency;   /* The minimum batch latency for sensor, in us */
  unsigned long min_interval;  /* The minimum subscription interval for sensor, in us */
  unsigned long nsubscribers;  /* The number of subcribers */
  unsigned long nadvertisers;  /* The number of advertisers */
  unsigned long generation;    /* The recent generation of circular buffer */
  FAR void     *priv;          /* The pointer to private data of userspace user */
};

//sensor 用户使用信息
struct sensor_ustate_s
{
  unsigned long esize;         /* The element size of circular buffer */
  unsigned long latency;       /* The batch latency for user, in us */
  unsigned long interval;      /* The subscription interval for user, in us */
  unsigned long generation;    /* The recent generation of circular buffer */
};

int sensor_register(FAR struct sensor_lowerhalf_s *dev, int devno);
int sensor_custom_register(FAR struct sensor_lowerhalf_s *dev,
                           FAR const char *path, unsigned long esize);
void sensor_unregister(FAR struct sensor_lowerhalf_s *dev, int devno);
void sensor_custom_unregister(FAR struct sensor_lowerhalf_s *dev,
                              FAR const char *path);
                        

 

  5.2.2 xxx_uorb.h

  nuttx中为每个sensor都有对应的uorb定义

2e60e467cd9e477e853a16da94b4f6c6.png

 

5.3 框架接入

  在Linux设备驱动中,所有sensor driver是基于IIO⼦系统提供的接⼝实现的。IIO⼦系统提供设备节点注册,buffer管理的等功能,这样driver只关注sensor本⾝。Vela亦采⽤这种思想,为统⼀管理所有sensor,复⽤公共代码,减少空间占⽤,将所有sensor driver的common部分提取为upper half层, 提供通⽤功能,真正与sensor寄存器交互的为lower half层。 Vela sensor driver更多的关注物理 sensor,对于融合⽣成的虚拟 sensor,通过应⽤的⼴告或订阅⾃动⽣成。对于像IMU器件,即多种sensor集成为⼀体,需要在driver中实例化多个lowerhalf,通过 upper half提供的API(sensor_register)分别注册设备节点。

  5.3.1 驱动层API

    Upper half(用户转接层)

    Upper层不与真正的物理设备交互,为sensor驱动模型提供通⽤功能,如:注册设备节点(/dev/sensor/accel0)、绑定syscall回调集合(struct file_operations)、多⽤⼾访问(引⽤计

    数)、提供circular buff管理、batch mode管理、控制sensor等

static const struct file_operations g_bmi270fops =
{
  bmi270_open,     /* open */
  bmi270_close,    /* close */
  bmi270_read,     /* read */
  NULL,            /* write */
  NULL,            /* seek */
  bmi270_ioctl,    /* ioctl */
};
ret = register_driver(devpath, &g_bmi270fops, 0666, priv);

struct sensor_upperhalf_s
{
  FAR struct sensor_lowerhalf_s *lower;  /* The handle of lower half driver */
  struct sensor_state_s          state;  /* The state of sensor device */
  struct circbuf_s   timing;             /* The circular buffer of generation */
  struct circbuf_s   buffer;             /* The circular buffer of data */
  rmutex_t           lock;               /* Manages exclusive access to file operations */
  struct list_node   userlist;           /* List of users */
};

 

    lower half(硬件转接层)

    Lowerhalf Driver 分为rpmsg half和通⽤lower half,rpmsg half负责与远程CPU进⾏跨核订阅和发布,通⽤lower half负责与传感器硬件交互,主要的⾏为表现为 sensor操作集:activate、set_interval、batch、selftest、set_calibvalue、calibrate、control等, 并在中断、轮询机制下将产⽣的sensor event送⼊upper层环形缓冲区。

struct sensor_lowerhalf_s 
{ 
    int type; 
    unsigned long nbuffer; 
    bool uncalibrated; 
    FAR const struct sensor_ops_s *ops; 
    bool persist; 
    union 
    { 
    sensor_push_event_t push_event; 
    sensor_notify_event_t notify_event; 
    }; 
    CODE void (*sensor_lock)(FAR void * priv); 
    CODE void (*sensor_unlock)(FAR void * priv); 
    FAR void *priv; 
}; 

struct sensor_ops_s
{
  CODE int (*open)(FAR struct sensor_lowerhalf_s *lower,
                   FAR struct file *filep);
  CODE int (*close)(FAR struct sensor_lowerhalf_s *lower,
                    FAR struct file *filep);
  CODE int (*activate)(FAR struct sensor_lowerhalf_s *lower,
                       FAR struct file *filep, bool enable);
  CODE int (*set_interval)(FAR struct sensor_lowerhalf_s *lower,
                           FAR struct file *filep,
                           FAR unsigned long *period_us);
  CODE int (*batch)(FAR struct sensor_lowerhalf_s *lower,
                    FAR struct file *filep,
                    FAR unsigned long *latency_us);
  CODE int (*fetch)(FAR struct sensor_lowerhalf_s *lower,
                    FAR struct file *filep,
                    FAR char *buffer, size_t buflen);
  CODE int (*set_calibvalue)(FAR struct sensor_lowerhalf_s *lower,
                             FAR struct file *filep,
                             unsigned long arg);
  CODE int (*control)(FAR struct sensor_lowerhalf_s *lower,
                      FAR struct file *filep,
                      int cmd, unsigned long arg);                              
}

  5.3.2 应用层API

    posix

    nuttx严格遵守posix设计规范,传感器驱动作为字符设备使用,同样支持posix接口,用户层可以通过open/read/write/ioctl/poll/close来访问传感器。

    uorb

    posix接口虽然容易入门,但是想在项目中使用往往需要进行一下二次封装,nuttx为了降低应用层开发难度,nuttx为sensor访问封装了uorb接口。Uorb的使用可以参考PX4-Devguide/en/middleware/uorb.md at master · PX4/PX4-Devguide · GitHub

    fd = orb_subscribe_multi(p_dev->difp.obj.meta, p_dev->difp.obj.instance);
    if (fd < 0) {
        ret = fd;
        goto exit;
    }
   epoll_ctl(p_dev->difp.epfd, EPOLL_CTL_ADD, fd, &p_dev->difp.epev);
    if (ret < 0) {
        goto exit;
    }
    ret = orb_set_batch_interval(fd, PIEZOPUMP_DIFP_BATCH_US);
    if (ret < 0) {
        goto exit;
    }
    ret = orb_set_frequency(fd, PIEZOPUMP_DIFP_ODR_HZ);
    if (ret < 0) {
        goto exit;
    }

 

 

5.4 参考点

  1. 能将驱动层、应用层代码解耦的框架。

  2. 统⼀驱动操作集(激活、采样率、batch、selftest、calibrate等),⽀持⾃定义ioctl。

  3. ⾃动管理所有sensor,⾃动开关控制、降采样和数据发布,⽀持多应⽤读取和控制。

  4. 统⼀sensor数据结构和规范单位,⽅便传输和处理。可⾃定义类型,⽀持特定事件结构。

  5. ⽀持中断、轮询、主动读取三种⼯作⽅式,驱动编写者灵活配置。

  6. 驱动框架内部采⽤环形buffer,弥补应⽤因卡顿延迟未及时读取造成的数据丢失问题。

  7. ⽀持跨核分布式访问sensor主题数据。

  8. Sensor驱动和uORB框架相结合。

  9. 为驱动层提供了统一的接口规范,sensor的发布降采样由sensor框架层完成。

  10. 完美支持posix,结合uorb降低用户层开发难度。

6 其他

6.1 oneOS

https://gitee.com/cmcc-oneos/OneOS

30eac3b269714fcfb046b7aa55d49743.png

移动的oneOS sensor框架在数据结构、API定义以及使用方式上与RT-Thread有很多的相似之处:

 

ONEOS sensor.h

/**
 ***********************************************************************************************************************
 * Copyright (c) 2020, China Mobile Communications Group Co.,Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * @file        sensor.h
 *
 * @brief       This file provides struct/enum definition and sensor functions declaration.
 *
 * @revision
 * Date         Author          Notes
 * 2020-02-20   OneOS Team      First Version
 ***********************************************************************************************************************
 */

#ifndef __SENSOR_H__
#define __SENSOR_H__

#include <os_task.h>
#include <os_mutex.h>
#include <os_clock.h>
#include <drv_cfg.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifdef OS_USING_RTC
#define os_sensor_get_ts() time(OS_NULL) /* API for the sensor to get the timestamp */
#else
#define os_sensor_get_ts() os_tick_get_value() /* API for the sensor to get the timestamp */
#endif

#define OS_PIN_NONE            0xFFFF /* PIN NONE */
#define OS_DEVICE_FLAG_FIFO_RX 0x200  /* Flag to use when the sensor is open by fifo mode */

#define OS_SENSOR_MODULE_MAX (3) /* The maximum number of members of a sensor module */

/* Sensor types */
#define OS_SENSOR_CLASS_NONE      (0)
#define OS_SENSOR_CLASS_ACCE      (1)  /* Accelerometer     */
#define OS_SENSOR_CLASS_GYRO      (2)  /* Gyroscope         */
#define OS_SENSOR_CLASS_MAG       (3)  /* Magnetometer      */
#define OS_SENSOR_CLASS_TEMP      (4)  /* Temperature       */
#define OS_SENSOR_CLASS_HUMI      (5)  /* Relative Humidity */
#define OS_SENSOR_CLASS_BARO      (6)  /* Barometer         */
#define OS_SENSOR_CLASS_LIGHT     (7)  /* Ambient light     */
#define OS_SENSOR_CLASS_PROXIMITY (8)  /* Proximity         */
#define OS_SENSOR_CLASS_HR        (9)  /* Heart Rate        */
#define OS_SENSOR_CLASS_TVOC      (10) /* TVOC Level        */
#define OS_SENSOR_CLASS_NOISE     (11) /* Noise Loudness    */
#define OS_SENSOR_CLASS_STEP      (12) /* Step sensor       */
#define OS_SENSOR_CLASS_FORCE     (13) /* Force sensor      */
#define OS_SENSOR_CLASS_ALTI      (14) /* Altitude sensor   */
#define OS_SENSOR_CLASS_IR        (15) /* IR intensity sensor   */

/* Sensor vendor types */
#define OS_SENSOR_VENDOR_UNKNOWN    (0)
#define OS_SENSOR_VENDOR_STM        (1)  /* STMicroelectronics */
#define OS_SENSOR_VENDOR_BOSCH      (2)  /* Bosch */
#define OS_SENSOR_VENDOR_INVENSENSE (3)  /* Invensense */
#define OS_SENSOR_VENDOR_SEMTECH    (4)  /* Semtech */
#define OS_SENSOR_VENDOR_GOERTEK    (5)  /* Goertek */
#define OS_SENSOR_VENDOR_MIRAMEMS   (6)  /* MiraMEMS */
#define OS_SENSOR_VENDOR_DALLAS     (7)  /* Dallas */
#define OS_SENSOR_VENDOR_ADI        (8)  /* ADI */
#define OS_SENSOR_VENDOR_SENSIRION  (9)  /* Sensirion */
#define OS_SENSOR_VENDOR_QST        (10) /* QST */

/* Sensor unit types */
#define OS_SENSOR_UNIT_NONE        (0)
#define OS_SENSOR_UNIT_MG          (1)  /* Accelerometer           unit: mG          */
#define OS_SENSOR_UNIT_MDPS        (2)  /* Gyroscope               unit: mdps        */
#define OS_SENSOR_UNIT_MGAUSS      (3)  /* Magnetometer            unit: mGauss      */
#define OS_SENSOR_UNIT_MLUX        (4)  /* Ambient light           unit: mlux        */
#define OS_SENSOR_UNIT_LUX         (5)  /* Ambient light           unit: lux         */
#define OS_SENSOR_UNIT_M           (6)  /* Distance                unit: m           */
#define OS_SENSOR_UNIT_CM          (7)  /* Distance                unit: cm          */
#define OS_SENSOR_UNIT_MM          (8)  /* Distance                unit: mm          */
#define OS_SENSOR_UNIT_PA          (9)  /* Barometer               unit: pa          */
#define OS_SENSOR_UNIT_MPERMILLAGE (10) /* Relative Humidity       unit: mpermillage */
#define OS_SENSOR_UNIT_PERMILLAGE  (11) /* Relative Humidity       unit: permillage  */
#define OS_SENSOR_UNIT_MDCELSIUS   (12) /* Temperature             unit: mdCelsius   */
#define OS_SENSOR_UNIT_DCELSIUS    (13) /* Temperature             unit: dCelsius    */
#define OS_SENSOR_UNIT_HZ          (14) /* Frequency               unit: HZ          */
#define OS_SENSOR_UNIT_ONE         (15) /* Dimensionless quantity  unit: 1           */
#define OS_SENSOR_UNIT_BPM         (16) /* Heart rate              unit: bpm         */
#define OS_SENSOR_UNIT_MN          (17) /* Force                   unit: mN          */
#define OS_SENSOR_UNIT_RAW         (18) /* Raw Data                unit: count       */

/* Sensor communication interface types */
#define OS_SENSOR_INTF_I2C     (1 << 0)
#define OS_SENSOR_INTF_SPI     (1 << 1)
#define OS_SENSOR_INTF_UART    (1 << 2)
#define OS_SENSOR_INTF_ONEWIRE (1 << 3)
#define OS_SENSOR_INTF_ADC     (1 << 4)

/* Sensor power mode types */
#define OS_SENSOR_POWER_NONE   (0)
#define OS_SENSOR_POWER_DOWN   (1) /* power down mode   */
#define OS_SENSOR_POWER_NORMAL (2) /* normal-power mode */
#define OS_SENSOR_POWER_LOW    (3) /* low-power mode    */
#define OS_SENSOR_POWER_HIGH   (4) /* high-power mode   */

/* Sensor work mode types */
#define OS_SENSOR_MODE_NONE    (0)
#define OS_SENSOR_MODE_POLLING (1) /* One shot only read a data */
#define OS_SENSOR_MODE_INT     (2) /* TODO: One shot interrupt only read a data */
#define OS_SENSOR_MODE_FIFO    (3) /* TODO: One shot interrupt read all fifo data */

/* Sensor control cmd types */
#define OS_SENSOR_CTRL_GET_ID    (0) /* Get device id */
#define OS_SENSOR_CTRL_GET_INFO  (1) /* Get sensor info */
#define OS_SENSOR_CTRL_SET_RANGE (2) /* Set the measure range of sensor. unit is info of sensor */
#define OS_SENSOR_CTRL_SET_ODR   (3) /* Set output date rate. unit is HZ */
#define OS_SENSOR_CTRL_SET_MODE  (4) /* Set sensor's work mode. ex. OS_SENSOR_MODE_POLLING,OS_SENSOR_MODE_INT */
#define OS_SENSOR_CTRL_SET_POWER (5) /* Set power mode. ex. OS_SENSOR_POWER_DOWN,OS_SENSOR_POWER_NORMAL */
#define OS_SENSOR_CTRL_SELF_TEST (6) /* Take a self test */

struct os_sensor_info
{
    uint8_t  type;       /* The sensor type */
    uint8_t  vendor;     /* Vendor of sensors */
    const char *model;      /* model name of sensor */
    uint8_t  unit;       /* unit of measurement */
    uint8_t  intf_type;  /* Communication interface type */
    int32_t  range_max;  /* maximum range of this sensor's value. unit is 'unit' */
    int32_t  range_min;  /* minimum range of this sensor's value. unit is 'unit' */
    uint32_t period_min; /* Minimum measurement period,unit:ms. zero = not a constant rate */
    uint8_t  fifo_max;
};

struct os_sensor_intf
{
    char      *dev_name;  /* The name of the communication device */
    uint8_t type;      /* Communication interface type */
    void      *user_data; /* Private data for the sensor. ex. i2c addr,spi cs,control I/O */
};

struct os_sensor_config
{
    struct os_sensor_intf     intf;    /* sensor interface config */
    struct os_device_pin_mode irq_pin; /* Interrupt pin, The purpose of this pin is to notification read data */
    uint8_t                mode;    /* sensor work mode */
    uint8_t                power;   /* sensor power mode */
    uint16_t               odr;     /* sensor out data rate */
    int32_t                range;   /* sensor range of measurement */
};

typedef struct os_sensor_device *os_sensor_t;

struct os_sensor_device
{
    struct os_device parent; /* The standard device */

    struct os_sensor_info   info;   /* The sensor info data */
    struct os_sensor_config config; /* The sensor config data */

    void     *data_buf; /* The buf of the data received */
    os_size_t data_len; /* The size of the data received */

    const struct os_sensor_ops *ops; /* The sensor ops */

    os_err_t (*irq_handle)(os_sensor_t sensor); /* Called when an interrupt is generated, registered by the driver */
};

struct sensor_3_axis
{
    int32_t x;
    int32_t y;
    int32_t z;
};

struct os_sensor_data
{
    uint32_t timestamp; /* The timestamp when the data was received */
    uint8_t  type;      /* The sensor type of the data */
    union
    {
        struct sensor_3_axis acce;      /* Accelerometer.       unit: mG          */
        struct sensor_3_axis gyro;      /* Gyroscope.           unit: mdps        */
        struct sensor_3_axis mag;       /* Magnetometer.        unit: mGauss      */
        int32_t           temp;      /* Temperature.         unit: dCelsius    */
        int32_t           humi;      /* Relative humidity.   unit: permillage  */
        int32_t           baro;      /* Pressure.            unit: pascal (Pa) */
        int32_t           light;     /* Light.               unit: lux         */
        int32_t           proximity; /* Distance.            unit: centimeters */
        int32_t           hr;        /* Heart rate.          unit: bpm         */
        int32_t           tvoc;      /* TVOC.                unit: permillage  */
        int32_t           noise;     /* Noise Loudness.      unit: HZ          */
        uint32_t          step;      /* Step sensor.         unit: 1           */
        int32_t           force;     /* Force sensor.        unit: mN          */
        int32_t           alti;      /* Altitude sensor.     unit: m           */
        uint32_t          raw;       /* Raw Data.            unit: 1           */
    } data;
};

struct os_sensor_ops
{
    os_size_t (*fetch_data)(struct os_sensor_device *sensor, void *buf, os_size_t len);
    os_err_t (*control)(struct os_sensor_device *sensor, int cmd, void *arg);
};

int os_hw_sensor_register(os_sensor_t sensor, const char *name, void *data);

#ifdef __cplusplus
}
#endif

#endif /* __SENSOR_H__ */

 

rt-thread sensor.h

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-01-31     flybreak     first version
 */

#ifndef __SENSOR_H__
#define __SENSOR_H__

#include <rtthread.h>
#include "pin.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifdef RT_USING_RTC
#define  rt_sensor_get_ts()  time(RT_NULL)   /* API for the sensor to get the timestamp */
#else
#define  rt_sensor_get_ts()  rt_tick_get()   /* API for the sensor to get the timestamp */
#endif

#define  RT_PIN_NONE                   0xFFFF    /* RT PIN NONE */
#define  RT_DEVICE_FLAG_FIFO_RX        0x200     /* Flag to use when the sensor is open by fifo mode */

#define  RT_SENSOR_MODULE_MAX          (3)       /* The maximum number of members of a sensor module */

/* Sensor types */

#define RT_SENSOR_CLASS_NONE           (0)
#define RT_SENSOR_CLASS_ACCE           (1)  /* Accelerometer     */
#define RT_SENSOR_CLASS_GYRO           (2)  /* Gyroscope         */
#define RT_SENSOR_CLASS_MAG            (3)  /* Magnetometer      */
#define RT_SENSOR_CLASS_TEMP           (4)  /* Temperature       */
#define RT_SENSOR_CLASS_HUMI           (5)  /* Relative Humidity */
#define RT_SENSOR_CLASS_BARO           (6)  /* Barometer         */
#define RT_SENSOR_CLASS_LIGHT          (7)  /* Ambient light     */
#define RT_SENSOR_CLASS_PROXIMITY      (8)  /* Proximity         */
#define RT_SENSOR_CLASS_HR             (9)  /* Heart Rate        */
#define RT_SENSOR_CLASS_TVOC           (10) /* TVOC Level        */
#define RT_SENSOR_CLASS_NOISE          (11) /* Noise Loudness    */
#define RT_SENSOR_CLASS_STEP           (12) /* Step sensor       */
#define RT_SENSOR_CLASS_FORCE          (13) /* Force sensor      */
#define RT_SENSOR_CLASS_DUST           (14) /* Dust sensor       */
#define RT_SENSOR_CLASS_ECO2           (15) /* eCO2 sensor       */
#define RT_SENSOR_CLASS_GNSS           (16) /* GPS/GNSS sensor   */
#define RT_SENSOR_CLASS_TOF            (17) /* TOF sensor        */
#define RT_SENSOR_CLASS_SPO2           (18) /* SpO2 sensor       */
#define RT_SENSOR_CLASS_IAQ            (19) /* IAQ sensor.       */
#define RT_SENSOR_CLASS_ETOH           (20) /* EtOH sensor.      */
#define RT_SENSOR_CLASS_BP             (21) /* Blood Pressure    */

/* Sensor vendor types */

#define RT_SENSOR_VENDOR_UNKNOWN       (0)
#define RT_SENSOR_VENDOR_STM           (1)  /* STMicroelectronics */
#define RT_SENSOR_VENDOR_BOSCH         (2)  /* Bosch */
#define RT_SENSOR_VENDOR_INVENSENSE    (3)  /* Invensense */
#define RT_SENSOR_VENDOR_SEMTECH       (4)  /* Semtech */
#define RT_SENSOR_VENDOR_GOERTEK       (5)  /* Goertek */
#define RT_SENSOR_VENDOR_MIRAMEMS      (6)  /* MiraMEMS */
#define RT_SENSOR_VENDOR_DALLAS        (7)  /* Dallas */
#define RT_SENSOR_VENDOR_ASAIR         (8)  /* Aosong */
#define RT_SENSOR_VENDOR_SHARP         (9)  /* Sharp */
#define RT_SENSOR_VENDOR_SENSIRION     (10) /* Sensirion */
#define RT_SENSOR_VENDOR_TI            (11) /* Texas Instruments */
#define RT_SENSOR_VENDOR_PLANTOWER     (12) /* Plantower */
#define RT_SENSOR_VENDOR_AMS           (13) /* ams AG */
#define RT_SENSOR_VENDOR_MAXIM         (14) /* Maxim Integrated */
#define RT_SENSOR_VENDOR_MELEXIS       (15) /* Melexis */

/* Sensor unit types */

#define  RT_SENSOR_UNIT_NONE           (0)
#define  RT_SENSOR_UNIT_MG             (1)  /* Accelerometer           unit: mG         */
#define  RT_SENSOR_UNIT_MDPS           (2)  /* Gyroscope               unit: mdps       */
#define  RT_SENSOR_UNIT_MGAUSS         (3)  /* Magnetometer            unit: mGauss     */
#define  RT_SENSOR_UNIT_LUX            (4)  /* Ambient light           unit: lux        */
#define  RT_SENSOR_UNIT_CM             (5)  /* Distance                unit: cm         */
#define  RT_SENSOR_UNIT_PA             (6)  /* Barometer               unit: pa         */
#define  RT_SENSOR_UNIT_PERMILLAGE     (7)  /* Relative Humidity       unit: permillage */
#define  RT_SENSOR_UNIT_DCELSIUS       (8)  /* Temperature             unit: dCelsius   */
#define  RT_SENSOR_UNIT_HZ             (9)  /* Frequency               unit: HZ         */
#define  RT_SENSOR_UNIT_ONE            (10) /* Dimensionless quantity  unit: 1          */
#define  RT_SENSOR_UNIT_BPM            (11) /* Heart rate              unit: bpm        */
#define  RT_SENSOR_UNIT_MM             (12) /* Distance                unit: mm         */
#define  RT_SENSOR_UNIT_MN             (13) /* Force                   unit: mN         */
#define  RT_SENSOR_UNIT_PPM            (14) /* Concentration           unit: ppm        */
#define  RT_SENSOR_UNIT_PPB            (15) /* Concentration           unit: ppb        */
#define  RT_SENSOR_UNIT_DMS            (16) /* Coordinates             unit: DMS        */
#define  RT_SENSOR_UNIT_DD             (17) /* Coordinates             unit: DD         */
#define  RT_SENSOR_UNIT_MGM3           (18) /* Concentration           unit: mg/m3      */
#define  RT_SENSOR_UNIT_MMHG           (19) /* Blood Pressure          unit: mmHg       */
/* Sensor communication interface types */

#define  RT_SENSOR_INTF_I2C            (1 << 0)
#define  RT_SENSOR_INTF_SPI            (1 << 1)
#define  RT_SENSOR_INTF_UART           (1 << 2)
#define  RT_SENSOR_INTF_ONEWIRE        (1 << 3)

/* Sensor power mode types */

#define  RT_SENSOR_POWER_NONE          (0)
#define  RT_SENSOR_POWER_DOWN          (1)  /* power down mode   */
#define  RT_SENSOR_POWER_NORMAL        (2)  /* normal-power mode */
#define  RT_SENSOR_POWER_LOW           (3)  /* low-power mode    */
#define  RT_SENSOR_POWER_HIGH          (4)  /* high-power mode   */

/* Sensor work mode types */

#define  RT_SENSOR_MODE_NONE           (0)
#define  RT_SENSOR_MODE_POLLING        (1)  /* One shot only read a data */
#define  RT_SENSOR_MODE_INT            (2)  /* TODO: One shot interrupt only read a data */
#define  RT_SENSOR_MODE_FIFO           (3)  /* TODO: One shot interrupt read all fifo data */

/* Sensor control cmd types */

#define  RT_SENSOR_CTRL_GET_ID         (RT_DEVICE_CTRL_BASE(Sensor) + 0)  /* Get device id */
#define  RT_SENSOR_CTRL_GET_INFO       (RT_DEVICE_CTRL_BASE(Sensor) + 1)  /* Get sensor info */
#define  RT_SENSOR_CTRL_SET_RANGE      (RT_DEVICE_CTRL_BASE(Sensor) + 2)  /* Set the measure range of sensor. unit is info of sensor */
#define  RT_SENSOR_CTRL_SET_ODR        (RT_DEVICE_CTRL_BASE(Sensor) + 3)  /* Set output date rate. unit is HZ */
#define  RT_SENSOR_CTRL_SET_MODE       (RT_DEVICE_CTRL_BASE(Sensor) + 4)  /* Set sensor's work mode. ex. RT_SENSOR_MODE_POLLING,RT_SENSOR_MODE_INT */
#define  RT_SENSOR_CTRL_SET_POWER      (RT_DEVICE_CTRL_BASE(Sensor) + 5)  /* Set power mode. args type of sensor power mode. ex. RT_SENSOR_POWER_DOWN,RT_SENSOR_POWER_NORMAL */
#define  RT_SENSOR_CTRL_SELF_TEST      (RT_DEVICE_CTRL_BASE(Sensor) + 6)  /* Take a self test */

#define  RT_SENSOR_CTRL_USER_CMD_START 0x100  /* User commands should be greater than 0x100 */

struct rt_sensor_info
{
    rt_uint8_t     type;                    /* The sensor type */
    rt_uint8_t     vendor;                  /* Vendor of sensors */
    const char    *model;                   /* model name of sensor */
    rt_uint8_t     unit;                    /* unit of measurement */
    rt_uint8_t     intf_type;               /* Communication interface type */
    rt_int32_t     range_max;               /* maximum range of this sensor's value. unit is 'unit' */
    rt_int32_t     range_min;               /* minimum range of this sensor's value. unit is 'unit' */
    rt_uint32_t    period_min;              /* Minimum measurement period,unit:ms. zero = not a constant rate */
    rt_uint8_t     fifo_max;
};

struct rt_sensor_intf
{
    char                       *dev_name;   /* The name of the communication device */
    rt_uint8_t                  type;       /* Communication interface type */
    void                       *user_data;  /* Private data for the sensor. ex. i2c addr,spi cs,control I/O */
};

struct rt_sensor_config
{
    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 */
};

typedef struct rt_sensor_device *rt_sensor_t;

struct rt_sensor_device
{
    struct rt_device             parent;    /* The standard device */

    struct rt_sensor_info        info;      /* The sensor info data */
    struct rt_sensor_config      config;    /* The sensor config data */

    void                        *data_buf;  /* The buf of the data received */
    rt_size_t                    data_len;  /* The size of the data received */

    const struct rt_sensor_ops  *ops;       /* The sensor ops */

    struct rt_sensor_module     *module;    /* The sensor module */

    rt_err_t (*irq_handle)(rt_sensor_t sensor);             /* Called when an interrupt is generated, registered by the driver */
};

struct rt_sensor_module
{
    rt_mutex_t                   lock;                      /* The module lock */

    rt_sensor_t                  sen[RT_SENSOR_MODULE_MAX]; /* The module contains a list of sensors */
    rt_uint8_t                   sen_num;                   /* Number of sensors contained in the module */
};

/* 3-axis Data Type */
struct sensor_3_axis
{
    rt_int32_t x;
    rt_int32_t y;
    rt_int32_t z;
};

/* Blood Pressure Data Type */
struct sensor_bp
{
    rt_int32_t sbp; /* SBP : systolic pressure */
    rt_int32_t dbp; /* DBP : diastolic pressure */
};

struct coordinates
{
    double longitude;
    double latitude;
};

struct rt_sensor_data
{
    rt_uint32_t         timestamp;          /* The timestamp when the data was received */
    rt_uint8_t          type;               /* The sensor type of the data */
    union
    {
        struct sensor_3_axis acce;          /* Accelerometer.       unit: mG          */
        struct sensor_3_axis gyro;          /* Gyroscope.           unit: mdps        */
        struct sensor_3_axis mag;           /* Magnetometer.        unit: mGauss      */
        struct coordinates   coord;         /* Coordinates          unit: degrees     */
        rt_int32_t           temp;          /* Temperature.         unit: dCelsius    */
        rt_int32_t           humi;          /* Relative humidity.   unit: permillage  */
        rt_int32_t           baro;          /* Pressure.            unit: pascal (Pa) */
        rt_int32_t           light;         /* Light.               unit: lux         */
        rt_int32_t           proximity;     /* Distance.            unit: centimeters */
        rt_int32_t           hr;            /* Heart rate.          unit: bpm         */
        rt_int32_t           tvoc;          /* TVOC.                unit: permillage  */
        rt_int32_t           noise;         /* Noise Loudness.      unit: HZ          */
        rt_uint32_t          step;          /* Step sensor.         unit: 1           */
        rt_int32_t           force;         /* Force sensor.        unit: mN          */
        rt_uint32_t          dust;          /* Dust sensor.         unit: ug/m3       */
        rt_uint32_t          eco2;          /* eCO2 sensor.         unit: ppm         */
        rt_uint32_t          spo2;          /* SpO2 sensor.         unit: permillage  */
        rt_uint32_t          iaq;           /* IAQ sensor.          unit: 1 */
        rt_uint32_t          etoh;          /* EtOH sensor.         unit: ppm */
        struct sensor_bp     bp;            /* BloodPressure.       unit: mmHg        */
    } data;
};

struct rt_sensor_ops
{
    rt_ssize_t (*fetch_data)(struct rt_sensor_device *sensor, void *buf, rt_size_t len);
    rt_err_t (*control)(struct rt_sensor_device *sensor, int cmd, void *arg);
};

int rt_hw_sensor_register(rt_sensor_t sensor,
                          const char              *name,
                          rt_uint32_t              flag,
                          void                    *data);

#ifdef __cplusplus
}
#endif

#endif /* __SENSOR_H__ */

6.2 AliOS Things

https://github.com/swaitech/AliOS-Things

AliOS Things 适配了分层架构和组件架构。包括以下部分:

  • BSP: 板级支持包

  • HAL: 硬件适配层,包括WiFi,蓝牙,I2C,SPI,UART,Flash 等

  • Kernel: 包括Rhino RTOS 内核,VFS,KV Storage,CLI,C++ 等

  • Network: 包括LwIP 轻量级TCP/IP协议栈,BLE 低功耗蓝牙协议栈,LoRaWAN 协议栈等

  • Security: 包括TLS(mbedTLS and cutomized iTLS),ID2,SST(Trusted Storage),Crypto,TEE(Trusted Execution Environment) 等

  • AOS API: AliOS Things 提供给应用软件和组件的API

  • VFS驱动框架:设备驱动提供给组件和应用的服务接口

  • Component: 阿里巴巴增值和常用的物联网组件,包括LinkSDK,OTA(安全差分升级),ulog(日志服务),uData(传感器框架),uDisplay(图形接口),WiFi配网 等

  • Application: 丰富的示例代码

 

Alios Things的sensor框架支持posix,sensor_ops接口只实现了open、read、close、ioctl、write,不支持poll,也没有看到对trigger回调的支持,虽然sensor_obj_t定义了irq,但并没有看到实际调用。

#define dev_acc_path           "/dev/acc"
#define dev_mag_path           "/dev/mag"
#define dev_gyro_path          "/dev/gyro"
#define dev_baro_path          "/dev/baro"

/* ioctl cmd list for sensor */
typedef enum {
    SENSOR_IOCTL_ODR_SET = 1,
    SENSOR_IOCTL_RANGE_SET,
    SENSOR_IOCTL_GET_INFO,
    SENSOR_IOCTL_BIST_PROCESS,
    SENSOR_IOCTL_WHO_AM_I,
    SENSOR_IOCTL_SET_POWER,
    SENSOR_IOCTL_GET_SENSOR_LIST,
    SENSOR_IOCTL_DTC_CYCLE_SET,
    SENSOR_IOCTL_GET_SENSOR_MODE,
    SENSOR_IOCTL_SET_SENSOR_IRQ_CB,
    SENSOR_IOCTL_SELF_TEST,
    SENSOR_IOCTL_MAX
} sensor_cmd_type;

typedef struct _sensor_obj_t {
    char                  *path;
    sensor_tag_e           tag;
    uint8_t                instance;
    dev_io_port_e          io_port;
    work_mode_e            mode;
    void                  *data_buf;
    uint32_t               data_len;
    dev_power_mode_e       power;
    gpio_dev_t             gpio;
    dev_sensor_full_info_t info;
    uint8_t                ref;
    uint8_t                drv_index;

    int  (*open)(void);
    int  (*close)(void);
    int  (*read)(void *, size_t);
    int  (*write)(const void *buf, size_t len);
    int  (*ioctl)(int cmd, unsigned long arg);
    void (*irq_handle)(void);
} sensor_obj_t;

static int drv_acc_bosch_bmi160_ioctl(int cmd, unsigned long arg)
{
    int ret = 0;

    switch(cmd) {
        case SENSOR_IOCTL_ODR_SET: {
            ret = drv_acc_bosch_bmi160_set_odr(&bmi160_ctx, arg);
            if(unlikely(ret) != 0) {
                return -1;
            }
        }
        break;
        case SENSOR_IOCTL_RANGE_SET: {
            ret = drv_acc_bosch_bmi160_set_range(&bmi160_ctx, arg);
            if(unlikely(ret) != 0) {
                return -1;
            }
        }
        break;
        case SENSOR_IOCTL_SET_POWER: {
            ret = drv_acc_bosch_bmi160_set_power_mode(&bmi160_ctx, arg);
            if(unlikely(ret) != 0) {
                return -1;
            }
        }
        break;
        case SENSOR_IOCTL_GET_INFO: {
            /* fill the dev info here */
            dev_sensor_info_t *info = (dev_sensor_info_t*)arg;
            info->model = "BMI160";
            info->range_max = 16;
            info->range_min = 2;
            info->unit = mg;
        }
        break;

        default:
            break;
    }

    return 0;
}

static sensor_obj_t *      g_sensor_obj[SENSOR_MAX_NUM];
static int sensor_obj_register(int index )
{
    int ret = 0;

    if ((g_sensor_obj[index]->mode == DEV_INT) ||
        (g_sensor_obj[index]->mode == DEV_DATA_READY) ||
        (g_sensor_obj[index]->mode == DEV_FIFO)) {
        ret = sensor_register_irq(index);
        if (unlikely(ret)) {
            return -1;
        }
    }

    ret = aos_register_driver(g_sensor_path[index], &sensor_fops, NULL);
    if (unlikely(ret)) {
        return -1;
    }
    return 0;
}

 

7 总结

RT-Thread、Zephyr、OpenHarmony、NuttX、AliOS-Things的传感器框架各具特色,值得我们借鉴并引入到我们的传感器框架中。通过对这些框架的分析,我们可以总结出以下主要特点:

  1. POSIX兼容性:NuttX、AliOS-Things和RT-Thread在对POSIX标准的支持上表现最为出色,尤其是RT-Thread可以通过配置来选择是否要支持posix,不支持posix时性能和内存都会得到优化,支持posix时又能兼顾移植性。

  2. 封装设计:Zephyr的封装设计全面、清晰且简洁,为开发者提供了良好的使用体验,Nuttx对sensor内容数据结构的统一封装更为通俗,容易移植。

  3. 硬件与驱动隔离:OpenHarmony和Zephyr均采用了类似Linux的设备树(DTS)机制,有效实现了硬件与驱动代码的隔离。

  4. 驱动与用户层隔离:NuttX和RT-Thread能够将驱动层与用户层完全隔离,增强了系统的稳定性和安全性。

  5. 开放平台兼容性:OpenHarmony作为开放平台,通过提供操作系统抽象层(OSAL),为兼容多种操作系统提供了参考架构。

  6. 跨核分布式访问:在跨核分布式访问的支持方面,NuttX表现最佳,而Zephyr虽广泛支持OpenAMP,但在传感器框架中没有直接支持,需要基于rpmsg进行封装。

  7. 多客户端仲裁:大部分OS的应用程序直接与传感器设备驱动程序交互,以进行所有传感器操作。这种方式缺乏高层次抽象、通用功能管理、多客户端仲裁等功能。

通过学习这些框架的巧妙设计,来打造自研OS的sensor 框架。

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2111119.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

搭建 canal 监控mysql数据到 elasticsearch 中(本机到远端sql)

搭建 canal 监控mysql数据到 elasticsearch 中&#xff08;本机到远端sql&#xff09; 需求: 要将 MySQL 数据库 info 中的 notice 和 result 表的增、删、改操作同步到 Elasticsearch 的 notice 和 result 索引&#xff0c;您需要正确配置 MySQL、Canal 、Canal Adapter 、 …

3--Web前端开发-前端工程化,vue项目

目录 端口配置 element 快速入门 table表格组件 分页组件 Dialog对话框组件 表单组件 端口配置 在vue.config.js中更改 源代码为 const { defineConfig } require(vue/cli-service) module.exports defineConfig({transpileDependencies: true })更改为 const { def…

Linux——redis主从复制、哨兵模式

一、redis 的安全加固&#xff1a; 对redis数据库访问的角度 auth // 验证登录redis 数据库的用户acl // 设置redis用户的权限将配置完成的ACL策略写入配置文件 config rewrite //目前redis生效的配置全部写入到默认配置文件的尾部写入到acl文件中&#xff0c;在加载配置文件时…

《论软件设计模式及其应用》通关范文,软考高级系统架构设计师

论文真题 设计模式(Design Pattern)是一套被反复使用的代码设计经验总结,代表了软件开发人员在软件开发过程中面临的一般问题的解决方案和最佳实践。使用设计模式的目的是提高代码的可重用性,让代码更容易被他人理解,并保证代码可靠性。现有的设计模式已经在前人的系统中…

每日一练:和为K的子数组

一、题目要求 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,1], k 2 输出&#xff1a;2示例 2&#xff1a; 输入&#xff1a;n…

python深度学习:从注意力机制到生成模型,全面解析现代AI技术

近年来&#xff0c;伴随着以卷积神经网络&#xff08;CNN&#xff09;为代表的深度学习的快速发展&#xff0c;人工智能迈入了第三次发展浪潮&#xff0c;AI技术在各个领域中的应用越来越广泛。注意力机制、Transformer模型&#xff08;BERT、GPT-1/2/3/3.5/4、DETR、ViT、Swin…

OpenCV结构分析与形状描述符(10)检测并提取轮廓函数findContours()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在二值图像中查找轮廓。 该函数使用算法 253从二值图像中检索轮廓。轮廓是有用的工具&#xff0c;可用于形状分析和对象检测与识别。参见 OpenC…

SDN架构详解

目录 1&#xff09;经典的IP网络-分布式网络 2&#xff09;经典网络面临的问题 3&#xff09;SDN起源 4&#xff09;OpenFlow基本概念 5&#xff09;Flow Table简介 6&#xff09;SDN的网络架构 7&#xff09;华为SDN网络架构 8&#xff09;传统网络 vs SDN 9&#xf…

网络安全与恶意攻击:如何应对?

引言 随着技术的发展&#xff0c;我们的生活越来越依赖于网络。但是&#xff0c;这也暴露了我们的系统对各种网络威胁的脆弱性。无论是个人还是企业&#xff0c;网络安全都成为了我们不能忽视的话题。 网络威胁的类型 网络威胁主要有以下几种&#xff1a; 网络钓鱼攻击&#…

linux学习--第四天

--linux文件操作 文件IO操作包括&#xff1a; &#xff08;注&#xff1a;I&#xff1a;input&#xff08;输入&#xff09;O&#xff1a;output&#xff08;输出&#xff09;&#xff09; open&#xff1a;打开 close&#xff1a;关闭 read&#xff1a;读取 write&#xff1a;…

c++一个数因子和(快速求解)

void 一个数因子和(int 整数) {//缘由https://ask.csdn.net/questions/1054457#answer_1251715int he 0, j 0; string a "";while (j < 整数)if (!(整数%j))he j, a to_string(j) "";cout << a << "的因子和&#xff1a;" …

如何在 Java 应用程序中定位高 CPU 使用率问题

文章目录 ♨ 前言♨ 提前准备♨ 线上定位♨ 结语 ♨ 前言 在运行 Java 应用程序的服务器上&#xff0c;高 CPU 使用率可能会影响应用程序的性能和稳定性。本文将介绍如何通过一系列步骤和工具来准确诊断和解决高 CPU 使用率问题&#xff0c;特别是针对 Java 环境下的应用程序。…

OpenCV影像数据处理入门-学习篇

目录 简介如何安装图像数据处理简单操作视频数据处理简单操作 一、简介 在计算机视觉项目的开发中&#xff0c;OpenCV作为最大众的开源库&#xff0c;拥有了丰富的常用图像处理函数库&#xff0c;可用于开发实时的图像处理、计算机视觉以及模式识别程序。采用C/C语言编写&am…

用于辅助视障人士检测人行道障碍物的 TinyML 模型

这篇论文的标题为《A TinyML model for sidewalk obstacle detection: aiding the blind and visually impaired people》&#xff0c;发表在《Multimedia Tools and Applications》上。以下是论文的主要内容概述&#xff1a; 摘要&#xff1a; 论文介绍了在资源受限的物联网…

C语言程序设计 笔记代码梳理 重制版

前言 第1章 C语言的流程 1.C程序经历的六个阶段 编辑(Edit)预处理(Preprocess)编译(Compile)汇编(Assemble)链接(Link)执行(Execute) 2.C语言编写代码到运行 都是先编译&#xff0c;后链接&#xff0c;最后运行。&#xff08;.c ---> .obj --->.exe&#xff09;这个过…

热老化的行业应用

热老化应用行业&#xff1a;深度解析与图像呈现 热老化&#xff0c;作为一种重要的材料测试方法&#xff0c;在众多行业中扮演着关键角色。它通过模拟产品在高温环境下的长期使用&#xff0c;提前发现潜在的材料缺陷、性能衰退等问题&#xff0c;从而提高产品的可靠性&#xf…

打造个性化时装购物平台:Spring Boot框架的实践

第1章 绪论 1.1背景及意义 随着社会的快速发展&#xff0c;计算机的影响是全面且深入的。人们生活水平的不断提高&#xff0c;日常生活中人们对时装购物系统方面的要求也在不断提高&#xff0c;喜欢购物的人数更是不断增加&#xff0c;使得时装购物系统的开发成为必需而且紧迫的…

顶刊精析|METI:整合细胞形态与空间转录组学的肿瘤微环境分析框架·24-09-06

小罗碎碎念 本期精读文献&#xff1a;《METI: Deep profiling of tumor ecosystems by integrating cell morphology and spatial transcriptomics》 今天分享的这篇文献于2023年8月25日发表在Nat Commun&#xff0c;目前IF14.7。 作者类型作者姓名单位名称&#xff08;中文&am…

【免费分享】25秋招提前批25秋招信息表

秋招&#xff0c;即秋季校园招聘&#xff0c;通常是指每年秋季&#xff08;大约从9月到11月&#xff09;企业在各大高校举办的招聘活动。这是许多公司为了吸引优秀应届毕业生而进行的招聘活动&#xff0c;也是许多学生毕业后进入职场的重要途径。以下是秋招的一些关键点&#x…

手机TF卡格式化后数据恢复:方法、挑战与预防措施

在现代生活中&#xff0c;‌手机已经成为我们不可或缺的一部分&#xff0c;‌而TF卡&#xff08;‌即MicroSD卡&#xff09;‌作为手机存储的扩展&#xff0c;‌更是承载了我们大量的重要数据。‌然而&#xff0c;‌不慎的格式化操作往往导致数据丢失&#xff0c;‌给用户带来不…