Zephyr传感器驱动模型

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

概述 链接到标题

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

统一接口 链接到标题

统一API列表 链接到标题

传感器统一接口一共5个

//从传感器获取所有数据放到内存
__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定义了几乎所有可测量的物理量

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使用结构体来表示一个传感器的浮点测量值

struct sensor_value {
	s32_t val1;		//整数
	s32_t val2;		//小数
};

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

 *      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用于指示设置传感器的那种参数

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驱动的传感器

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,	//双击触发
};

统一接口使用方法 链接到标题

主动获取传感器数据 链接到标题

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获取传感器数据 链接到标题

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中定义了统一接口驱动模型

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也类似):

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也类似):

static int dht_channel_get(struct device *dev,
			   enum sensor_channel chan,
			   struct sensor_value *val)
{
	...
}

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

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