RT-thread 傳感器驅動開發 ---- 以超聲波模塊HC-SR04爲例

一、前期準備

超聲波模塊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);

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章