超声波模块HC-SR04介绍:https://blog.csdn.net/super828/article/details/84112824。
传感器驱动开发指南:https://www.rt-thread.org/document/site/development-guide/sensor/sensor_driver_development/#。
传感器驱动框架介绍:https://www.rt-thread.org/document/site/development-guide/sensor/sensor_driver/。
Sensor 驱动框架的作用是:为上层提供统一的操作接口,提高上层代码的可重用性;简化底层驱动开发的难度,只要实现简单的 ops(operations: 操作命令) 就可以将传感器注册到系统上。
也就是说将传感器注册到系统,成为设备文件,然后上层应用就可以调用统一的API来控制这个传感器,诸如获取传感器数据之类的操作。
Sensor 驱动框架的整体架构图如下:
它为上层提供的是标准 device 接口open/close/read/write/control
。
为底层驱动提供的是简单的 ops 接口:fetch_data/control
。
并且框架支持 module(模块),为底层存在耦合的传感器设备提供服务。
Sensor 设备其实是对标准设备 rt_device
的一个丰富,是在原有标准设备的基础上增加了 Sensor 自己独有的一部分 属性
和 控制命令
,如下图所示:
整个 Sensor 设备包括两个部分:
1、继承自标准设备的一些特性,包括:标准的控制接口 、回调函数
、device_id
等。
2、Sensor 设备独有的部分,包括:Sensor 的类型
、相关的信息
、特有的控制命令
、ops
、以及一些 数据的结构
。
.Sensor 设备的结构体如下所示:
struct rt_sensor_device
{
struct rt_device parent; /* The standard device */
struct rt_sensor_info info; /* The sensor info data */
struct rt_sensor_config config; /* The sensor config data */
void *data_buf; /* The buf of the data received */
rt_size_t data_len; /* The size of the data received */
const struct rt_sensor_ops *ops; /* The sensor ops 操作命令*/
struct rt_sensor_module *module; /* The sensor module */
};
typedef struct rt_sensor_device *rt_sensor_t;
其中,struct rt_sensor_info info 里存储的是一些与 Sensor 自身相关的信息,在 Sensor 设备注册的时候提供,在使用的过程中不应修改其内容。具体成员如下所示。
struct rt_sensor_info
{
rt_uint8_t type; /* The sensor type */
rt_uint8_t vendor; /* Vendor of sensors 供应商*/
const char *model; /* model name of sensor 比如MPU6050*/
rt_uint8_t unit; /* unit of measurement */
rt_uint8_t intf_type; /* Communication interface type ,such as IIc */
rt_int32_t range_max; /* maximum range of this sensor's value. unit is 'unit'*/
rt_int32_t range_min; /* minimum range of this sensor's value. unit is 'unit' */
rt_uint32_t period_min; /* Minimum measurement period,unit:ms. zero = not a constant rate */
rt_uint8_t fifo_max; /* Maximum depth of fifo */
};
Sensor 驱动框架抽象出了一些公共的配置选项,这些可配置的选项置于 struct rt_sensor_config
里, 成员如下:
struct rt_sensor_config
{
struct rt_sensor_intf intf; /* sensor interface config */
struct rt_device_pin_mode irq_pin; /* Interrupt pin, The purpose of this pin is to notification read data */
rt_uint8_t mode; /* sensor work mode */
rt_uint8_t power; /* sensor power mode */
rt_uint16_t odr; /* sensor out data rate */
rt_int32_t range; /* sensor range of measurement */
};
struct rt_sensor_intf
{
char *dev_name; /* The name of the communication device */
rt_uint8_t type; /* Communication interface type */
void *user_data; /* Private data for the sensor. ex. i2c addr,spi cs,control I/O */
};
其余的一些配置项是用 Sensor 特有控制命令控制的,结合 ops 中的 control 接口使用,就可以完成传感器的配置了。如下所示:
#define RT_SENSOR_CTRL_GET_ID (0) /* 读设备ID */
#define RT_SENSOR_CTRL_GET_INFO (1) /* 获取设备信息 */
#define RT_SENSOR_CTRL_SET_RANGE (2) /* 设置传感器测量范围 */
#define RT_SENSOR_CTRL_SET_ODR (3) /* 设置传感器数据输出速率,unit is HZ */
#define RT_SENSOR_CTRL_SET_MODE (4) /* 设置工作模式 */
#define RT_SENSOR_CTRL_SET_POWER (5) /* 设置电源模式 */
#define RT_SENSOR_CTRL_SELF_TEST (6) /* 自检 */
对于Sensor 数据的存储,为了方便数据的解析,规定每一个类型的 Sensor 都有自己独有的数据结构,这些成员之间使用共用体
以减少代码量。
/* 3-axis Data Type */
struct sensor_3_axis
{
rt_int32_t x;
rt_int32_t y;
rt_int32_t z;
};
struct rt_sensor_data
{
rt_uint32_t timestamp; /* The timestamp when the data was received */
rt_uint8_t type; /* The sensor type of the data */
union
{
struct sensor_3_axis acce; /* Accelerometer. unit: mG */
struct sensor_3_axis gyro; /* Gyroscope. unit: mdps */
struct sensor_3_axis mag; /* Magnetometer. unit: mGauss */
rt_int32_t temp; /* Temperature. unit: dCelsius */
rt_int32_t humi; /* Relative humidity. unit: permillage */
rt_int32_t baro; /* Pressure. unit: pascal (Pa) */
rt_int32_t light; /* Light. unit: lux */
rt_int32_t proximity; /* Distance. unit: centimeters */
rt_int32_t hr; /* Heat rate. unit: HZ */
rt_int32_t tvoc; /* TVOC. unit: permillage */
rt_int32_t noise; /* Noise Loudness. unit: HZ */
rt_uint32_t step; /* Step sensor. unit: 1 */
} data;
};
ops(操作函数)包含两个函数指针, 一个的作用是获取传感器数据(fetch_data),另一个的作用是通过控制命令控制传感器(control)。
struct rt_sensor_ops
{
rt_size_t (*fetch_data)(struct rt_sensor_device *sensor, void *buf, rt_size_t len);
rt_err_t (*control)(struct rt_sensor_device *sensor, int cmd, void *arg);
};
传感器驱动框架提供了一个 Sensor 注册函数,通过传入 Sensor 的控制块,名称,标志位和私有数据,就可以完成传感器设备的注册。
int rt_hw_sensor_register(rt_sensor_t sensor,
const char *name,
rt_uint32_t flag,
void *data);
这样看来 Sensor 驱动框架依托于标准的设备框架,只要将传感器驱动对接到 Sensor 的 ops 上,并通过调用 rt_hw_sensor_register
函数注册为 Sensor 设备就可以通过标准的设备接口控制传感器了。
module 的定义是解决底层有耦合的两个传感器而出现的,有些传感器既有加速度计的功能又有陀螺仪的功能,并且他们的FIFO是共用的,在 FIFO 模式下,只能将两个类型的传感器的数据同时读出,这就说明他们的数据是耦合的。为了解决这个问题,我们定义了 module 的类型
struct rt_sensor_module
{
rt_mutex_t lock; /* The module lock */
rt_sensor_t sen[RT_SENSOR_MODULE_MAX]; /* The module contains a list of sensors */
rt_uint8_t sen_num; /* Number of sensors contained in the module */
};
里面包含有耦合的传感器的设备控制块指针,通过这个功能就可以在读取陀螺仪的数据的时候,同时更新加速度计的值,解决了底层耦合的问题。
开发的主要任务就是对接 Sensor 驱动框架的 ops 接口,然后注册为 Sensor 设备,进而能够通过驱动框架控制传感器的相关行为。
sensor 框架共给出了两个接口(fetch_data
/ control
),需要在驱动中实现这两个接口。
作用: 获取传感器的数据。接口原型:
rt_size_t (*fetch_data)(struct rt_sensor_device *sensor, void *buf, rt_size_t len);
Sensor 驱动框架当前默认支持 轮询(POLLING)、中断(INT)、FIFO 这三种工作模式。如果要开发的传感器支持中断
和FIFO
的工作模式,需要在这里判断传感器的工作模式,然后再根据不同的模式返回传感器数据。如下所示:
static rt_size_t xxx_acc_fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
if (sensor->config.mode == RT_SENSOR_MODE_POLLING)
{
return _xxx_acc_polling_get_data(sensor, buf, len);
}
else if (sensor->config.mode == RT_SENSOR_MODE_INT)
{
return _xxx_acc_int_get_data(sensor, buf, len);
}
else if (sensor->config.mode == RT_SENSOR_MODE_FIFO)
{
return _xxx_acc_fifo_get_data(sensor, buf, len);
}
else
return 0;
}
开发人员在返回数据时应先标识存储数据的数据类型,然后再填充数据域与时间戳,如下所示:
sensor_data->type = RT_SENSOR_CLASS_ACCE;
sensor_data->data.acce.x = acceleration.x;
sensor_data->data.acce.y = acceleration.y;
sensor_data->data.acce.z = acceleration.z;
sensor_data->timestamp = rt_sensor_get_ts();
HC-SR04为例:
static rt_size_t _fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
struct rt_sensor_data *data = buf;
data->type = RT_SENSOR_CLASS_PROXIMITY;
data->data.proximity = distance;
data->timestamp = rt_sensor_get_ts();
return 1;
}
rt_err_t (*control)(struct rt_sensor_device *sensor, int cmd, void *arg);
传感器的控制就是依靠这个接口函数实现的,通过判断传入的命令字的不同执行不同的操作。
HC-SR04为例:
static rt_err_t _control(struct rt_sensor_device *sensor, int cmd, void *args)
{
rt_err_t result = RT_EOK;
switch (cmd)
{
case RT_SENSOR_CTRL_SET_POWER:
result = _set_power(sensor, (rt_uint32_t)args & 0xff);
break;
default:
return RT_EOK;
}
return result;
}
还要实现一个设备接口的结构体 ops 存储上面的接口函数:
static struct rt_sensor_ops xxx_ops =
{
xxx_acc_fetch_data,
xxx_acc_control
};
rt_int8_t result;
rt_sensor_t sensor_pr = RT_NULL;
sensor_pr = rt_calloc(1, sizeof(struct rt_sensor_device));
if (sensor_pr == RT_NULL)
return RT_NULL;
sensor_pr->info.type = RT_SENSOR_CLASS_PROXIMITY;
sensor_pr->info.model = "hr_sr04";
sensor_pr->info.unit = RT_SENSOR_UNIT_MM;
sensor_pr->info.intf_type = RT_SENSOR_INTF_ONEWIRE;
sensor_pr->info.range_max = RANG_MAX;
sensor_pr->info.range_min = RANG_MIN;
sensor_pr->info.period_min = 100;
rt_memcpy(&sensor_pr->config, cfg, sizeof(struct rt_sensor_config));
sensor_pr->ops = &sensor_ops;
sensor_pr->irq_handle = sr04_irq_handle;
result = rt_hw_sensor_register(sensor_pr, name, RT_DEVICE_FLAG_RDWR, RT_NULL);