Zephyr传感器驱动模型

Creative Commons
本作品采用知识共享署名

本文介绍zephyr传感器驱动接口定义,使用和实现方法。

概述

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

统一接口

统一API列表

传感器统一接口一共5个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//从传感器获取所有数据放到内存
__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)

统一参数

物理量channel

zephyr在enum sensor_channel定义了几乎所有可测量的物理量

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
45
46
47
48
49
50
51
52
enum sensor_channel {

SENSOR_CHAN_ACCEL_X, //X轴加速度,m/s^2
SENSOR_CHAN_ACCEL_Y, //y轴加速度,m/s^2
SENSOR_CHAN_ACCEL_Z, //z轴加速度,m/s^2
SENSOR_CHAN_ACCEL_XYZ, //任意加速度
SENSOR_CHAN_ACCEL_ANY = SENSOR_CHAN_ACCEL_XYZ,

SENSOR_CHAN_GYRO_X, //绕X轴角速度,radians/s
SENSOR_CHAN_GYRO_Y, //绕y轴角速度,radians/s
SENSOR_CHAN_GYRO_Z, //绕z轴角速度,radians/s
SENSOR_CHAN_GYRO_XYZ, //任意角速度
SENSOR_CHAN_GYRO_ANY = SENSOR_CHAN_GYRO_XYZ,

SENSOR_CHAN_MAGN_X, //X轴地磁, Gauss
SENSOR_CHAN_MAGN_Y, //y轴地磁, Gauss
SENSOR_CHAN_MAGN_Z, //z轴地磁, Gauss
SENSOR_CHAN_MAGN_XYZ, //任意轴地磁
SENSOR_CHAN_MAGN_ANY = SENSOR_CHAN_MAGN_XYZ,

SENSOR_CHAN_TEMP, //温度,摄氏度
SENSOR_CHAN_DIE_TEMP, //器件温度,摄氏度
SENSOR_CHAN_AMBIENT_TEMP, //环境温度,摄氏度

SENSOR_CHAN_PRESS, //大气压, 千帕

SENSOR_CHAN_PROX, //距离(靠近)传感器,1表示接近

SENSOR_CHAN_HUMIDITY, //湿度, 百分比

SENSOR_CHAN_LIGHT, //可见光强, lux
SENSOR_CHAN_IR, //红外光强, lux
SENSOR_CHAN_RED, //红色光强,lux
SENSOR_CHAN_GREEN, //绿色光强,lux
SENSOR_CHAN_BLUE, //蓝色光强,lux

SENSOR_CHAN_ALTITUDE, //高度传感器, m

SENSOR_CHAN_PM_1_0, //PM1.0传感器,ug/m^3
SENSOR_CHAN_PM_2_5, //PM2.5传感器,ug/m^3
SENSOR_CHAN_PM_10, //PM2.5传感器,ug/m^3

SENSOR_CHAN_DISTANCE, //距离传感器,m

SENSOR_CHAN_CO2, //CO2传感器, ppm
SENSOR_CHAN_VOC, //VOC传感器, ppm

SENSOR_CHAN_VOLTAGE, //电压, V
SENSOR_CHAN_CURRENT, //电流, A

SENSOR_CHAN_ALL,
};

传感器数据格式

为了避免传递浮点数,zephyr使用结构体来表示一个传感器的浮点测量值

1
2
3
4
struct sensor_value {
s32_t val1; //整数
s32_t val2; //小数
};

val = val1 + val2 * 10^(-6) 例如

1
2
3
4
*      0.5: val1 =  0, val2 =  500000
* -0.5: val1 = 0, val2 = -500000
* -1.0: val1 = -1, val2 = 0
* -1.5: val1 = -1, val2 = -500000

传感器参数

一般情况下zephy在编译的时候就已经确定传感器的工作参数,但也存在运行时改变传感器工作参数的情况,sensor_attribute用于指示设置传感器的那种参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum sensor_attribute {

SENSOR_ATTR_SAMPLING_FREQUENCY, //传感器采样频率(具体含义由实际驱动实现决定,例如:一秒测量多少次)

SENSOR_ATTR_LOWER_THRESH, //低触发阈值
SENSOR_ATTR_UPPER_THRESH, //高触发阈值

SENSOR_ATTR_SLOPE_TH, //斜率(任意运动)触发
SENSOR_ATTR_SLOPE_DUR, //斜率维持超过一定时间触发

SENSOR_ATTR_OVERSAMPLING, //过采样参数

SENSOR_ATTR_FULL_SCALE, //传感器量程

SENSOR_ATTR_OFFSET, //传感器值校正,sensor_channel_get返回的传感器值将被改值偏置final_value = sensor_value + offset

SENSOR_ATTR_CALIB_TARGET, //传感器自身校正,用芯片内部的算法校正传感器的某个或者所有轴(传感器内部校正功能)
};

传感器触发方式

对于有中断输出的传感器,通常有不同的触发方式或者在实现驱动中有thread驱动的传感器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enum sensor_trigger_type {

SENSOR_TRIG_TIMER, //定时触发

SENSOR_TRIG_DATA_READY, //传感器数据准备好触发

SENSOR_TRIG_DELTA, //通道量有连续变化时触发(需设置SENSOR_ATTR_SLOPE_TH和SENSOR_ATTR_SLOPE_DUR)

SENSOR_TRIG_NEAR_FAR, //接近或者远离触发

SENSOR_TRIG_THRESHOLD, //超过配置阈值触发(需设置SENSOR_ATTR_LOWER_THRESH和SENSOR_ATTR_UPPER_THRESH)

SENSOR_TRIG_TAP, //单击触发

SENSOR_TRIG_DOUBLE_TAP, //双击触发
};

统一接口使用方法

主动获取传感器数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void main(void)
{
struct device *dev = device_get_binding("DHT11"); //根据Device name获取驱动

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获取传感器数据

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
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_get_binding("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);
}
}

统一接口下驱动实现方式

驱动接口模型

在sensor.h中定义了统一接口驱动模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typedef int (*sensor_attr_set_t)(struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val);

typedef int (*sensor_trigger_set_t)(struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);

typedef int (*sensor_sample_fetch_t)(struct device *dev,
enum sensor_channel chan);

typedef int (*sensor_channel_get_t)(struct device *dev,
enum sensor_channel chan,
struct sensor_value *val);

struct sensor_driver_api {
sensor_attr_set_t attr_set;
sensor_trigger_set_t trigger_set;
sensor_sample_fetch_t sample_fetch;
sensor_channel_get_t channel_get;
};

驱动模型实现

对上由系统调用实现呼叫驱动实现,例如(其它三个API也类似):

1
2
3
4
5
6
7
8
static inline int _impl_sensor_channel_get(struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
const struct sensor_driver_api *api = dev->driver_api;

return api->channel_get(dev, chan, val);
}

驱动实现

对下,在实现驱动时按照sensor_driver_api成员的接口和功能定义进行实现,例如dht.c(其它三个API也类似):

1
2
3
4
5
6
static int dht_channel_get(struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
...
}

在驱动初始化的时候将对应的sensor_driver_api注册,上层驱动模型就可以通过device name来寻找到驱动实现

1
2
3
4
5
6
static const struct sensor_driver_api dht_api = {
.sample_fetch = &dht_sample_fetch,
.channel_get = &dht_channel_get,
};
DEVICE_AND_API_INIT(dht_dev, DHT11, &dht_init, &dht_data,
NULL, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &dht_api);

驱动实现范围

struct sensor_driver_api中sample_fetch和channel_get必须实现,而attr_set和trigger_set可选,但应用层一定要注意使用同一接口时可选的驱动方法可能无效

参考

http://docs.zephyrproject.org/subsystems/sensor.html