传感器驱动模型,对传感器的操作操作提供了一个统一的操作api
,这些统一的api
可以让我们做如下两个事情:
- 从传感器读取值
- 根据设置的传感器阈值,执行某些事情
简介
Zephyr
在include/drivers/sensor.h
中定义了统一的传感器驱动接口,由sensor_sample_fetch
从指定传感器读取数据到内存,由sensor_channel_get
来获取指fetch
的数据。对于传感器阈值通知的情况,Zephyr也提供了sensor_trigger_set
,注册callback函数,在传感器打到阈值时被执行。
由于不同的传感器测量物理特性值不一样,同一个传感器也会有不同物理特性测量值,zephyr 将不同的物理量抽象为channel
,一个channel
对应一个物理测量量。
接口API
设置属性值:
//从传感器获取所有数据放到内存
__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);
//获取传感器指定channel的参数,例如采样频率,触发阈值等
__syscall int sensor_attr_get(const 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结构
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,
};
传感器数据格式
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
传感器参数
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);
}
}
传感器驱动实现
驱动API的模型
在include/drivers/sensor.h
中定义传感器的API模型:
/**
* @typedef sensor_trigger_handler_t
* @brief Callback API upon firing of a trigger
*
* @param dev Pointer to the sensor device
* @param trigger The trigger
*/
typedef void (*sensor_trigger_handler_t)(const struct device *dev,
struct sensor_trigger *trigger);
/**
* @typedef sensor_attr_set_t
* @brief Callback API upon setting a sensor's attributes
*
* See sensor_attr_set() for argument description
*/
typedef int (*sensor_attr_set_t)(const struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val);
/**
* @typedef sensor_attr_get_t
* @brief Callback API upon getting a sensor's attributes
*
* See sensor_attr_get() for argument description
*/
typedef int (*sensor_attr_get_t)(const struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
struct sensor_value *val);
/**
* @typedef sensor_trigger_set_t
* @brief Callback API for setting a sensor's trigger and handler
*
* See sensor_trigger_set() for argument description
*/
typedef int (*sensor_trigger_set_t)(const struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
/**
* @typedef sensor_sample_fetch_t
* @brief Callback API for fetching data from a sensor
*
* See sensor_sample_fetch() for argument description
*/
typedef int (*sensor_sample_fetch_t)(const struct device *dev,
enum sensor_channel chan);
/**
* @typedef sensor_channel_get_t
* @brief Callback API for getting a reading from a sensor
*
* See sensor_channel_get() for argument description
*/
typedef int (*sensor_channel_get_t)(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val);
__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;
};
驱动的系统调用
在include/drivers/sensor.h
中定义传感器的系统调用接口。例如:sensor_attr_set
的系统调用:
/**
* @brief Set an attribute for a sensor
*
* @param dev Pointer to the sensor device
* @param chan The channel the attribute belongs to, if any. Some
* attributes may only be set for all channels of a device, depending on
* device capabilities.
* @param attr The attribute to set
* @param val The value to set the attribute to
*
* @return 0 if successful, negative errno code if failure.
*/
__syscall int sensor_attr_set(const struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val);
static inline int z_impl_sensor_attr_set(const struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
const struct sensor_driver_api *api =
(const struct sensor_driver_api *)dev->api;
if (api->attr_set == NULL) {
return -ENOSYS;
}
return api->attr_set(dev, chan, attr, val);
}
从上可以看出我们只需要在驱动中实现API
模型中函数就可以了。
传感器驱动实现
在实现驱动时按照sensor_driver_api
成员的接口和功能定义进行实现,例如dht.c
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);
DEVICE_AND_API_INIT
宏是定义在include/device.h
中的:
/**
* @def DEVICE_AND_API_INIT
*
* @brief Invoke DEVICE_DEFINE() with no power management support (@p
* pm_control_fn).
*/
#define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, \
data_ptr, cfg_ptr, level, prio, api_ptr) \
__DEPRECATED_MACRO \
DEVICE_DEFINE(dev_name, drv_name, init_fn, \
NULL, \
data_ptr, cfg_ptr, level, prio, api_ptr)
dev_name
:设备名称drv_name
:驱动名称,通过device_get_binding
来查找init_fn
: 初始化函数data_ptr
: 驱动中data数据指针cfg_ptr
: 驱动中的config数据指针level
:注册级别prio
:注册级别中优先级api_ptr
:驱动中的api数据指针
驱动实现范围
struct sensor_driver_api
中sample_fetch
和channel_get
必须实现,而attr_set
、attr_get
和trigger_set
可选,但应用层一定要注意使用同一接口时可选的驱动方法可能无效。