传感器驱动模型,对传感器的操作操作提供了一个统一的操作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^2SENSOR_CHAN_ACCEL_Y, //y轴加速度,m/s^2SENSOR_CHAN_ACCEL_Z, //z轴加速度,m/s^2SENSOR_CHAN_ACCEL_XYZ, //任意加速度SENSOR_CHAN_ACCEL_ANY = SENSOR_CHAN_ACCEL_XYZ,SENSOR_CHAN_GYRO_X, //绕X轴角速度,radians/sSENSOR_CHAN_GYRO_Y, //绕y轴角速度,radians/sSENSOR_CHAN_GYRO_Z, //绕z轴角速度,radians/sSENSOR_CHAN_GYRO_XYZ, //任意角速度SENSOR_CHAN_GYRO_ANY = SENSOR_CHAN_GYRO_XYZ,SENSOR_CHAN_MAGN_X, //X轴地磁, GaussSENSOR_CHAN_MAGN_Y, //y轴地磁, GaussSENSOR_CHAN_MAGN_Z, //z轴地磁, GaussSENSOR_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, //可见光强, luxSENSOR_CHAN_IR, //红外光强, luxSENSOR_CHAN_RED, //红色光强,luxSENSOR_CHAN_GREEN, //绿色光强,luxSENSOR_CHAN_BLUE, //蓝色光强,luxSENSOR_CHAN_ALTITUDE, //高度传感器, mSENSOR_CHAN_PM_1_0, //PM1.0传感器,ug/m^3SENSOR_CHAN_PM_2_5, //PM2.5传感器,ug/m^3SENSOR_CHAN_PM_10, //PM2.5传感器,ug/m^3SENSOR_CHAN_DISTANCE, //距离传感器,mSENSOR_CHAN_CO2, //CO2传感器, ppmSENSOR_CHAN_VOC, //VOC传感器, ppmSENSOR_CHAN_VOLTAGE, //电压, VSENSOR_CHAN_CURRENT, //电流, ASENSOR_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 + offsetSENSOR_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_handlerif (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可选,但应用层一定要注意使用同一接口时可选的驱动方法可能无效。
