一、前期準備
超聲波模塊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 驅動框架
Sensor 驅動框架的作用是:爲上層提供統一的操作接口,提高上層代碼的可重用性;簡化底層驅動開發的難度,只要實現簡單的 ops(operations: 操作命令) 就可以將傳感器註冊到系統上。
也就是說將傳感器註冊到系統,成爲設備文件,然後上層應用就可以調用統一的API來控制這個傳感器,諸如獲取傳感器數據之類的操作。
Sensor 驅動框架的整體架構圖如下:
它爲上層提供的是標準 device 接口open/close/read/write/control
。
爲底層驅動提供的是簡單的 ops 接口:fetch_data/control
。
並且框架支持 module(模塊),爲底層存在耦合的傳感器設備提供服務。
2.1、工作原理
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
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支持
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 */
};
裏面包含有耦合的傳感器的設備控制塊指針,通過這個功能就可以在讀取陀螺儀的數據的時候,同時更新加速度計的值,解決了底層耦合的問題。
三、開發指南 --- 超聲波模塊HC-SR04爲例
開發的主要任務就是對接 Sensor 驅動框架的 ops 接口,然後註冊爲 Sensor 設備,進而能夠通過驅動框架控制傳感器的相關行爲。
sensor 框架共給出了兩個接口(fetch_data
/ control
),需要在驅動中實現這兩個接口。
3.1、fetchdata
作用: 獲取傳感器的數據。接口原型:
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;
}
3.2、control
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;
}
3.3、設備接口的結構體 ops
還要實現一個設備接口的結構體 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);