1.背景
传感器(Sensor)是物联网(IOT)的重要组成部分,用于感知和采集环境中的各种数据,大部分智能硬件都需要。
为使传感器能正常⼯作,驱动开发者需要开发不同的驱动程序,驱动程序要实现芯片寄存器\IO设置,又要响应使用者的请求,同时应⽤程序也需要适配这些驱动程序来访问传感器。
不同类型的传感器或者不同OS的驱动框架特性差异比较大,其驱动程序也⼤相径庭,应⽤程序就需要对它们的差异处做适配,显然就会增加应⽤程序的开发难度和周期,容易引⼊bug不说,更提高了开发门槛,随着传感器的种类的增多,这⼀问题变的更加严重。为了简化传感器操作⽅式,主流操作系统⼀般都会设计专⻔的sensor软件框架。
本文对主流RTOS的sensor框架进行简单分析。
1.1 框架的基本要求
框架的基本要求,也就是未来这个框架的基本特点。
-
统一的管理sensor的数据、控制、driver model,封装并给出简单API,能对驱动开发、应用开发分别提供标准化的接入流程。
-
能与应用层框架中的api相结合,通过应用层框架的API接口可以对sensor driver进行read、write、ioctl、trigger、batch,enable,flush操作,例如nuttx中的uorb接口。
-
模块要分层,接口清晰易懂且简单。接口定义要简单到,只看头文件和注释就能明确接口的功能。为了降低开发难度,通常sensor框架可分三个层次:
-
芯片驱动。寄存器datasheet有关的代码、称之为device driver代码。设计这个的目的是为了提高驱动的移植效率。
-
框架抽象层(core层)。供device driver和user app调用,对驱动开发、应用开发抽象统一的接口。
-
框架接入层。
-
-
移植性强,OS解耦。
-
所实现的模块,是否能进行独立验证?答案是必须能独立验证。
-
是否需要支持宏定义动态开关编译选项,如果需要,要确保宏打开/关闭,编译和运行都正常,不影响其他模块。
1.2 调研思路
-
梳理需求,每一个模块负责人都要主动梳理需求,同时评审阶段大家一起来看有没有遗漏的需求。sensor框架的需求,包括开发需求,正式产品的业务需求、数据采集需求,保证从协议、数据结构定义方面能兼容所有设备。
-
设计要求要符合1.1中的六个基本要求。
-
结合已有经验,调研主流系统的sensor 框架,主要是学习他们的设计思想,如何封装数据结构、和API。
1.3 RTOS选择
当前RTOS百花齐放,还未有一个系统能一统江湖,我们应该基于哪个RTOS来参考?基于我们现有的基础和国内外的RTOS使用现状来考虑。
1.3.1 贡献者对比
先看一下RTOS使用现状,https://www.embedded.com/will-zephyr-become-the-dominant-rtos/
在过去几年中,RTOS贡献者对比:
1.3.2 posix支持
为什么要看posix支持情况,posix(Portable Operating System Interface)即可移植操作系统接口,是操作系统为应用程序提供的接口标准。POSIX标准同样适用于嵌入式软件,最大的优点就是可移植性强、容易入门、对接口定义能有统一的标准规范,缺点是比裸机占用更多的资源。
这里简单介绍几个常用RTOS的POSIX支持情况:
RTOS | POSIX支持情况 |
VxWorks | User space(RTP):POSIX.13 PSE52 (少数接口存在限制)Kernel space:POSIX.1部分接口和POSIX.1可选功能中的一些实时接口 |
Zephry | PSE54 |
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个,覆盖大部分厂商。
RT-Thread的sensor框架主要特点:
-
将框架中公共的结构体和sensor数据定义都放在同一个头文件即sensor.h中,但其中的结构体都是rt_xxx开头。
-
支持posix接口(可选)。
-
驱动层和应用层接口没有明显的层次关系,可以混淆调用(在不使用posix接口的情况下)。
-
接口定义不是太清晰明确,结构体有一部分荣誉。如rt_sensor_ops中只有fetch_data、ctrol两个接口,不清楚ctrol的具体功能。再如rt_sensor_info中有静态和动态控制信息没有区分开。v1 v2接口不兼容。
-
灵活的加载机制(INIT_XXX_EXPORT)
-
事件儿驱动(中断)支持较差,需要指定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(用于物理耦合的软件处理)
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(用于物理耦合的软件处理)
2.2.2 Sensor V1 && Sensor V2
部分代码:
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操作传感器的流程如下:
有两种方法,一种是使用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 参考点
-
传感器数据结构可以使用同一个头文件(应用层和驱动层接口API最好能分开)。
-
可以选择性的支持posix,兼顾性能和移植性。
-
自动化加载方式(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")
-
缺点:接口定义可以再清晰一些,如一个sensor最基本的几个接口可以从ctrol中进一步提取出来,如 首次使用时active,设置batch 采样率, 设置 triiger阈值,订阅与取消订阅等接口并无体现。
3 Zephyr
3.1 框架概览
zephyr的sensor框架主要特点:
-
将框架中公共的结构体和sensor数据定义都放在同一个头文件即sensor.h中,但是其中的sensor_value定义不是太明确且不包含时间戳,但sensor.h中其他结构体封装思路较清晰。
-
不支持posix接口。
-
驱动层和应用层接口没有明显的层次关系,可以混淆调用(在不使用posix接口的情况下)。
-
接口定义清晰明确。
-
事件儿驱动支持的较好。
-
灵活的加载机制(INIT_XXX_EXPORT)
-
dts设备树机制(非必须实现),使硬件资源与driver code隔离。
-
与rt-thread不同,sensor框架不再基于device驱动框架实现,两者独立。
-
缺少多客户使用仲裁方案。
典型使用场景
3.2 为什么不支持posix
虽然zephyr是linux基金会管控的,但zephyr 驱动/sensor框架不支持posix,主要是设计目标不同,下面的答案来自GPT,尽管zephyr不支持posix,但是它的sensor框架接口清晰明了简洁,仍然具备很强的竞争力。
zephyr
驱动模型不支持POSIX接口可能是由于以下几个原因:
-
设计目标不同:
zephyr
可能被设计为专注于特定的应用场景或硬件平台,而没有考虑与POSIX标准的兼容性。POSIX接口通常用于提供跨平台的兼容性,但并非所有系统或驱动都需要这种兼容性。 -
性能优化:为了达到更高的性能或更低的资源消耗,
zephyr
可能选择了不遵循POSIX标准,而是采用更适合其目标平台的接口和实现方式。 -
开发资源限制:实现POSIX兼容性需要额外的工作量和测试,如果开发团队在资源有限的情况下,可能会优先考虑实现核心功能,而不是POSIX兼容性。
-
特定的硬件需求:某些硬件可能需要特定的驱动接口,这些接口可能与POSIX标准不兼容。为了更好地利用硬件特性,
zephyr
可能选择了非POSIX的接口。 -
历史遗留问题:如果
zephyr
是在没有考虑POSIX兼容性的情况下开发的,后续可能因为各种原因(如维护成本、兼容性问题等)没有进行POSIX兼容性的改造。
zephyr
驱动模型不支持POSIX接口可能是出于对特定应用场景、性能优化、资源限制、硬件需求或历史遗留问题的考虑。
在讨论Linux与Zephyr的驱动模型区别时,我们可以从几个关键方面进行比较:
-
内核架构:
-
Linux:Linux是一个宏内核(monolithic kernel),所有的系统服务和驱动程序都运行在内核空间。这种设计使得内核可以快速高效地处理硬件请求,但也增加了内核崩溃的风险。
-
Zephyr:Zephyr则是一个微内核(microkernel)设计,它将许多服务和驱动程序放在用户空间运行。这种设计提高了系统的稳定性和安全性,因为即使某个驱动程序崩溃,也不会影响到整个系统。
-
-
驱动模型:
-
Linux:Linux的驱动模型是基于设备文件系统的(如/dev目录下的设备文件),驱动程序通过这些设备文件与用户空间的应用程序进行交互。Linux的驱动程序通常需要编译进内核或作为模块加载。
-
Zephyr:Zephyr的驱动模型更为简洁,它直接在内核中管理设备驱动。Zephyr的驱动程序通常是静态链接到内核中,而不是通过模块动态加载。Zephyr的驱动模型更注重于资源受限的嵌入式系统,因此它提供了更高效的资源管理和更简洁的API。
-
-
编程接口:
-
Linux:Linux提供了丰富的API和库函数,支持多种编程语言和复杂的设备管理。Linux的驱动开发相对复杂,需要开发者熟悉内核编程和设备文件系统。
-
Zephyr:Zephyr的API相对简单,更专注于嵌入式系统的基本需求。Zephyr的驱动开发更注重于简洁性和高效性,适合资源受限的设备。
-
-
应用场景:
-
Linux:Linux广泛应用于服务器、桌面、移动设备等多种场景,支持广泛的硬件和软件生态。
-
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 */
/* 其他传感器特定属性 */
};
};
在这个示例中,i2c0
是I2C总线控制器节点,sensor@76
是连接到该总线的传感器设备,地址为0x76。compatible
属性用于匹配正确的驱动程序,reg
属性定义设备地址,interrupts
属性则定义了传感器的中断配置。
通过这种方式,内核在启动时会解析这些设备树信息,并自动加载和配置相应的驱动程序。这不仅简化了驱动开发,也使得系统配置更加直观和易于管理。
3.3.2 设备树的优缺点
设备树(Device Tree)是一种描述硬件信息的数据结构,它使得硬件信息与内核代码分离,从而提高了系统的灵活性和可移植性。以下是设备树的优缺点:
优点:
-
硬件信息与内核代码分离:设备树将硬件描述从内核代码中分离出来,使得内核代码更加通用,不依赖于特定的硬件配置。
-
可移植性:通过修改设备树文件,可以在不同的硬件平台上使用相同的内核镜像,减少了为不同硬件定制内核的工作量。
-
简化内核代码:内核代码不再需要包含大量的硬件特定信息,如寄存器地址、设备ID等,这简化了内核代码的维护和更新。
-
动态配置:设备树可以在系统启动时动态加载,这使得硬件配置可以在不重新编译内核的情况下进行调整。
-
易于调试和维护:设备树文件通常使用人类可读的文本格式(如dts),这使得硬件配置的调试和维护变得更加直观和容易。
缺点:
-
学习曲线:设备树的语法和概念对于新用户来说可能有一定的学习曲线,特别是对于那些习惯于传统方式(如直接在内核代码中硬编码硬件信息)的开发者。
-
复杂性:对于复杂的硬件系统,设备树文件可能会变得非常庞大和复杂,这可能会增加理解和维护的难度。
-
兼容性问题:不同版本的设备树规范可能会有所不同,这可能会导致兼容性问题,特别是在使用第三方设备树文件时。
-
性能影响:虽然设备树本身对性能的影响通常很小,但在某些情况下,设备树的解析和处理可能会引入轻微的性能开销。
-
集成挑战:将设备树集成到现有的内核和硬件开发流程中可能需要一些工作,特别是对于那些已经采用传统方式的项目。
总的来说,设备树是一个强大的工具,它通过将硬件信息从内核代码中分离出来,提高了系统的灵活性和可移植性。然而,它也有一些缺点,如学习曲线和复杂性,需要开发者在使用时进行权衡。
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的驱动:
其中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、不支持一对多实例外,其他设计都很有参考价值。
-
传感器数据结构可以使用同一个头文件(应用层和驱动层接口API最好能分开)。
-
独有的设备树机制,能够使得硬件和driver code隔离。
-
sensor使用的接口定义清晰且全面。
-
驱动中需要有完整的bus接口实现,将总线驱动、设备驱动、用户接口层定义进行隔离。如总线驱动完成spi/iic读写,设备驱动主要完成与硬件寄存器相关的配置,用户接口层只实现sensor框架规定的api。
-
应用和驱动直接交互,缺少中间层抽象,无法支持多客户端仲裁(针对这一点,已经有人提出基于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
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_m | MCU类处理器(例如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源码目录
可以看出当前开放出来的只支持3个os,linux、liteos_a,liteos_m。
看一下openharmony为了适配linux-5.10做的改动:
openharmony把HDF框架移植到了linux-5.10。
4.4 驱动架构
openharmony的sensor框架是基于其驱动框架实现的,需要先了解一下驱动框架。
HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。
HDF驱动架构采用C语言面向对象编程模型构建,通过平台解耦、内核解耦,来达到兼容不同内核,统一平台底座的目的。HDF驱动框架架构如下图所示。
4.4.1 驱动框架图
HDF驱动架构主要组成部分:
-
HDI(Hardware Device Interface,硬件设备统一接口)层:通过规范化的设备接口标准,为系统提供统一、稳定的硬件设备操作接口。
-
HDF驱动框架:提供统一的硬件资源管理、驱动加载管理、设备节点管理、设备电源管理以及驱动服务模型等功能,需要包含设备管理、服务管理、DeviceHost、PnPManager等模块。
-
统一的配置界面:支持硬件资源的抽象描述,屏蔽硬件差异,可以支撑开发者开发出与配置信息不绑定的通用驱动代码,提升开发及迁移效率,并可通过HC-Gen等工具快捷生成配置文件。
-
操作系统抽象层(OSAL,Operating System Abstraction Layer):提供统一封装的内核操作相关接口,屏蔽不同系统操作差异,包含内存、锁、线程、信号量等接口。
-
平台驱动:为外设驱动提供Board硬件(如:I2C/SPI/UART总线等平台资源)操作统一接口,同时对Board硬件操作进行统一的适配接口抽象以便于不同平台迁移。
-
外设驱动模型:面向外设驱动,提供常见的驱动抽象模型,主要达成两个目的,提供标准化的器件驱动,开发者无需独立开发,通过配置即可完成驱动的部署;提供驱动模型抽象,屏蔽驱动与不同系统组件间的交互,使得驱动更具备通用性。
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驱动模型图
4.5.2 基本概念
目前根据sensorId将Sensor分为医学类Sensor、传统类Sensor两种。
-
医学类Sensor:已订阅的sensorId枚举值在128-160范围的为医学类Sensor。
-
传统类Sensor:已订阅的sensorId枚举值不在128-160范围的为传统类Sensor。
4.5.3 运作机制
通过介绍Sensor驱动模型的加载以及运行流程,对模型内部关键组件以及关联组件之间的关系进行了划分,整体加载流程如图2所示:
图 2 Sensor驱动运行图
Sensor驱动模型以标准系统RK3568产品中的加速度传感器驱动为例,介绍整个驱动加载及运行流程:
-
从device_info.hcs配置文件中的Sensor Host读取到Sensor设备管理配置信息。
-
HDF配置框架从HCB数据库中解析Sensor设备管理配置信息,并关联对应设备驱动。
-
加载并初始化Sensor设备管理驱动。
-
Sensor设备管理驱动向HDI发布Sensor基础能力接口。
-
从device_info.hcs配置文件中的Sensor Host读取到加速度传感器驱动配置信息。
-
加载加速度传感器抽象驱动,调用初始化接口,完成Sensor器件的驱动资源分配和数据处理队列的创建。
-
从accel_xxx_config.hcs配置文件中读取到加速度传感器差异化驱动配置和私有化配置信息。
-
加速度传感器芯片差异化驱动,调用通用配置解析接口,完成器件属性信息解析,器件寄存器解析。
-
加速度传感器芯片差异化驱动完成器件的探测,并分配加速度传感器配置资源和加速度传感器差异化接口注册。
-
加速度传感器成功探测到器件之后,加速度传感器芯片差异化驱动通知加速度传感器抽象驱动,注册加速度传感器设备到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/目录。每个传感器的数据类型都有独立的头文件
4.6 Sensor服务概述(sensors_sensor_lite)
sensors_sensor_lite是基于openharmony原有的sensor框架封装了一层上层使用api。
-
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 参考点
-
类似dts的HCS机制,将硬件和驱动 code隔离。
-
提供了OSAL层,能对多个OS能进行适配。
-
类似RT-THread,有独有的自动加载流程。
-
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 框架概览
框架特点:
-
⽀持33种类型的物理sensor,覆盖⼏乎所有智能设备。⽀持⾃定义类型,兼容不规则设备。
-
⽀持抽象、远程sensor创建,覆盖算法、状态、远程等主题数据发布和订阅。
-
统⼀驱动操作集(激活、采样率、batch、selftest、calibrate等),⽀持⾃定义ioctl。
-
⾃动管理所有sensor,⾃动开关控制、降采样和数据发布,⽀持多应⽤读取和控制。
-
统⼀sensor数据结构和规范单位,⽅便传输和处理。可⾃定义类型,⽀持特定事件结构。
-
⽀持中断、轮询、主动读取三种⼯作⽅式,驱动编写者灵活配置。
-
驱动框架内部采⽤环形buffer,弥补应⽤因卡顿延迟未及时读取造成的数据丢失问题。
-
⽀持跨核分布式访问sensor主题数据。
-
Sensor驱动和uORB框架相结合。
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定义
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 参考点
-
能将驱动层、应用层代码解耦的框架。
-
统⼀驱动操作集(激活、采样率、batch、selftest、calibrate等),⽀持⾃定义ioctl。
-
⾃动管理所有sensor,⾃动开关控制、降采样和数据发布,⽀持多应⽤读取和控制。
-
统⼀sensor数据结构和规范单位,⽅便传输和处理。可⾃定义类型,⽀持特定事件结构。
-
⽀持中断、轮询、主动读取三种⼯作⽅式,驱动编写者灵活配置。
-
驱动框架内部采⽤环形buffer,弥补应⽤因卡顿延迟未及时读取造成的数据丢失问题。
-
⽀持跨核分布式访问sensor主题数据。
-
Sensor驱动和uORB框架相结合。
-
为驱动层提供了统一的接口规范,sensor的发布降采样由sensor框架层完成。
-
完美支持posix,结合uorb降低用户层开发难度。
6 其他
6.1 oneOS
https://gitee.com/cmcc-oneos/OneOS
移动的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的传感器框架各具特色,值得我们借鉴并引入到我们的传感器框架中。通过对这些框架的分析,我们可以总结出以下主要特点:
-
POSIX兼容性:NuttX、AliOS-Things和RT-Thread在对POSIX标准的支持上表现最为出色,尤其是RT-Thread可以通过配置来选择是否要支持posix,不支持posix时性能和内存都会得到优化,支持posix时又能兼顾移植性。
-
封装设计:Zephyr的封装设计全面、清晰且简洁,为开发者提供了良好的使用体验,Nuttx对sensor内容数据结构的统一封装更为通俗,容易移植。
-
硬件与驱动隔离:OpenHarmony和Zephyr均采用了类似Linux的设备树(DTS)机制,有效实现了硬件与驱动代码的隔离。
-
驱动与用户层隔离:NuttX和RT-Thread能够将驱动层与用户层完全隔离,增强了系统的稳定性和安全性。
-
开放平台兼容性:OpenHarmony作为开放平台,通过提供操作系统抽象层(OSAL),为兼容多种操作系统提供了参考架构。
-
跨核分布式访问:在跨核分布式访问的支持方面,NuttX表现最佳,而Zephyr虽广泛支持OpenAMP,但在传感器框架中没有直接支持,需要基于rpmsg进行封装。
-
多客户端仲裁:大部分OS的应用程序直接与传感器设备驱动程序交互,以进行所有传感器操作。这种方式缺乏高层次抽象、通用功能管理、多客户端仲裁等功能。
通过学习这些框架的巧妙设计,来打造自研OS的sensor 框架。