传感器驱动

传感器子系统暴露了一个统一访问传感器设备的 API。该 API 的通用操作是:读取数据,并在指定的条件满足时执行代码。

基本操作

通道

从根本上讲,通道是一个传感器设备可以测量的量。

传感器可以有多个通道,既可以表示不同物理属性(例如加速器)的不同轴;又可以同时测量不同的属性(温度、压力、湿度等)。复杂的传感器包括所述的所有情况,因此一个设备可以可以占用三个加速通道和一个温度通道。

让支持所给通道的所有传感器使用同一个测试单元是非常有必要的。下面列举了所支持的所有通道以及它们的描述和测试单元。

警告

doxygenenum: Cannot find file: /home/docs/checkouts/readthedocs.org/user_builds/zephyr-doc/checkouts/latest/doc/doxygen/xml/index.xml

传感器设备返回值的类型是 struct sensor_value 。这种形式能避免在不支持或不希望进行浮点操作的时候进行了浮点操作。

大多数传感器有类型为 SENSOR_TYPE_INT_PLUS_MICRO 的值;其它可能的形式在下面列举了出来。应用程序负责解释返回值的 type 字段。

警告

doxygenenum: Cannot find file: /home/docs/checkouts/readthedocs.org/user_builds/zephyr-doc/checkouts/latest/doc/doxygen/xml/index.xml

获取值

从传感器获取值需要两个操作。首先,应用程序指示驱动程序对它的每个通道进行采样;然后,个体通道被读取。如果通道有多个轴,可以通过一个相应类型为 _ANY 的通道和一个具有 3 个 struct sensor_value 对象的缓冲一次性读取所有的轴。这种方法能确保在读和通信效率之间保持一致性。

下面是使用 BME280 传感器测量环境温度和大气压力的一个例程。注意,函数 sensor_sample_fetch() 只被调用了依次,因为它对所有的通道都进行了读取和补偿。

1
2
	}
}

该例程假设返回值的类型是 struct sensor_value 。一个真实的支持多传感器的驱动程序应当检查 temppresstype 字段,以及结构体中其它的相应字段。

配置和属性

设置通信总线和地址是传感器设备的最基本配置。这个设置是在编译时通过配置菜单完成的。如果传感器支持中断,中段号和触发器参数也是在编译时配置的。

除了这些通信参数外,传感器芯片通常还暴露了多个控制精确度和测量频率的参数。为了兼容 Zephyr 的设计目标,这些值大多数都是在编译时静态配置的。

不过,某些参数也需要在运行时配置,例如中断的阈值。这些值通过属性进行配置。下一节中的例程使用了一个带中断的传感器,该中断会在温度超过阈值时被触发器。该阈值是在编译时通过属性配置的。

触发器

Zephyr 中的 触发器(Triggers) 指的是传感器芯片的中断号。许多传感器芯片支持一到多个触发器。触发器的一些例子包括:有新数据可读、通道值超过某个阈值、设备感知到运动等。

配置触发器时,应用程序需要提供一个结构体 struct sensor_trigger 以及一个处理函数。上面这个结构体中包含了触发器的类型以及必须被配置的触发通道。

由于大多数传感器使用 SPI 或者 I2C 总线进行连接,因此它们可能在中断执行上下文中无法通信。触发器处理函数的执行会被推迟到线程中,取数据操作才能完成。驱动程序可以创建它自己的线程去取数据以确保最小的延迟。多个传感器驱动程序也可以共享一个全路(system-wide)线程。共享线程的方法会增加处理中断的延迟,但是能减小内存占用。您可以为每个驱动程序配置相应的方法。大多数驱动程序可以完成禁止触发器,以达到减小内存的效果。

下面的例程包含了一个触发器,它会在温度跨越 26 摄氏度时被触发。此外,这个例程还会每秒采集一次温度。在真实的应用程序中,如果需要省电,则可以禁止周期性采集。由于应用程序直接访问内核的配置符号,当驱动程序的配置禁止了触发功能时不会注册触发器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
	}

#ifdef DEBUG
	printf("dev %p\n", dev);
	printf("dev %p name %s\n", dev, dev->config->name);
#endif

#ifdef CONFIG_MCP9808_TRIGGER
	struct sensor_value val;
	struct sensor_trigger trig;

	val.val1 = 26;
	val.val2 = 0;

	sensor_attr_set(dev, SENSOR_CHAN_TEMP,
			SENSOR_ATTR_UPPER_THRESH, &val);

	trig.type = SENSOR_TRIG_THRESHOLD;
	trig.chan = SENSOR_CHAN_TEMP;

	sensor_trigger_set(dev, &trig, trigger_handler);
#endif

	while (1) {
		struct sensor_value temp;
		int rc;

		rc = sensor_sample_fetch(dev);
		if (rc != 0) {
			printf("sensor_sample_fetch error: %d\n", rc);
			break;
		}

		rc = sensor_channel_get(dev, SENSOR_CHAN_TEMP, &temp);
		if (rc != 0) {
			printf("sensor_channel_get error: %d\n", rc);
			break;
		}

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

		k_sleep(2000);
	}
}