# Sensor Driver Development Guide

### [Overview](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development?id=%e6%a6%82%e8%bf%b0) <a href="#gai-shu" id="gai-shu"></a>

#### [Purpose and Overview](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development?id=%e7%9b%ae%e7%9a%84%e4%b8%8e%e6%a6%82%e8%bf%b0) <a href="#mu-di-yu-gai-shu" id="mu-di-yu-gai-shu"></a>

This document is a development guide document for sensor drivers under the RT-Thread Sensor driver framework, providing development standards and specifications for the development team.

#### [Reading object](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development?id=%e9%98%85%e8%af%bb%e5%af%b9%e8%b1%a1) <a href="#yue-du-dui-xiang" id="yue-du-dui-xiang"></a>

* Engineers engaged in sensor-driven development

Note

Note: Before reading this document, please read [the sensor driver framework introduction](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver) first .

### [Development Guide](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development?id=%e5%bc%80%e5%8f%91%e6%8c%87%e5%8d%97) <a href="#kai-fa-zhi-nan" id="kai-fa-zhi-nan"></a>

Developing a sensor driver generally requires the following steps: research and preparation, development, testing, and submission.

During the development process, you can refer to the supported sensors. Click here to view [the list of supported sensors](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/sensor/sensor_list) .

#### [Research and preparation](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development?id=%e8%b0%83%e7%a0%94%e4%b8%8e%e5%87%86%e5%a4%87) <a href="#diao-yan-yu-zhun-bei" id="diao-yan-yu-zhun-bei"></a>

According to the datasheet or other methods, understand the characteristics of the sensor and record them, such as the following:

* Sensor Type
* Communication interface (i2c/spi/...)
* Measuring range
* Shortest measurement cycle
* Support several working modes and power modes

#### [Development](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development?id=%e5%bc%80%e5%8f%91) <a href="#kai-fa" id="kai-fa"></a>

The main task of development is to connect to the ops interface of the Sensor driver framework, and then register it as a Sensor device, so that the relevant behaviors of the sensor can be controlled through the driver framework.

[**ops interface connection**](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development?id=ops-%e6%8e%a5%e5%8f%a3%e5%af%b9%e6%8e%a5)

The sensor framework provides two interfaces ( `fetch_data`/ `control`), which need to be implemented in the driver.

**fetch\_data**

Function: Get sensor data. Interface prototype:

```c
rt_size_t (*fetch_data)(struct rt_sensor_device *sensor, void *buf, rt_size_t len);copymistakeCopy Success
```

The Sensor driver framework currently supports three default opening modes: polling (RT\_DEVICE\_FLAG\_RDONLY), interrupt (RT\_DEVICE\_FLAG\_INT\_RX), and FIFO (RT\_DEVICE\_FLAG\_FIFO\_RX). You need to determine the working mode of the sensor here, and then return the sensor data according to different modes.

As shown below:

```c
static rt_size_t xxx_acc_fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
    if (sensor->parent.open_flag & RT_DEVICE_FLAG_RDONLY)
    {
        return _xxx_acc_polling_get_data(sensor, buf, len);
    }
    else if (sensor->parent.open_flag & RT_DEVICE_FLAG_INT_RX)
    {
        return _xxx_acc_int_get_data(sensor, buf, len);
    }
    else if (sensor->parent.open_flag & RT_DEVICE_FLAG_FIFO_RX)
    {
        return _xxx_acc_fifo_get_data(sensor, buf, len);
    }
    else
        return 0;
}copymistakeCopy Success
```

When returning data, developers should first identify the data type of the stored data, and then fill in the data field and timestamp, as shown below:

```c
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();copymistakeCopy Success
```

Note

Note: - Please use the timestamp acquisition function provided by the Sensor driver framework to obtain the timestamp `rt_sensor_get_ts`. - In FIFO mode, the underlying data may be coupled, and a module needs to be used to update the data of two sensors at the same time. - The data unit must be converted to the data unit specified in the Sensor driver framework.

The data units are as follows:

| Sensor Name        | type                         | unit        | illustrate                                                      |
| ------------------ | ---------------------------- | ----------- | --------------------------------------------------------------- |
| Accelerometer      | RT\_SENSOR\_CLASS\_ACCE      | mg          | 1 g = 1000 mg, 1 g = 9.8 m/s2                                   |
| Gyroscope          | RT\_SENSOR\_CLASS\_GYRO      | mdps        | 1 dps = 1000 mdps, dps (degrees per second)                     |
| Magnetometer       | RT\_SENSOR\_CLASS\_MAG       | mGauss      | 1 G = 1000 mGauss                                               |
| Ambient Light      | RT\_SENSOR\_CLASS\_LIGHT     | lux         | Lumen value                                                     |
| Approaching light  | RT\_SENSOR\_CLASS\_PROXIMITY | centimeters | Represents the distance from the object to the sensor, unit: cm |
| Barometer          | RT\_SENSOR\_CLASS\_BARO      | Pa          | 100 Pa = 1 hPa (hectopascal)                                    |
| thermometer        | RT\_SENSOR\_CLASS\_TEMP      | °c/10       | 0.1 degrees Celsius                                             |
| Hygrometer         | RT\_SENSOR\_CLASS\_HUMI      | ‰           | Relative humidity RH, usually expressed in ‰                    |
| Heart rate monitor | RT\_SENSOR\_CLASS\_HR        | bpm         | Times per minute                                                |
| noise              | RT\_SENSOR\_CLASS\_NOISE     | HZ          | Frequency Unit                                                  |
| Pedometer          | RT\_SENSOR\_CLASS\_STEP      | 1           | Number of steps: dimensionless unit 1                           |
| Force Sensors      | RT\_SENSOR\_UNIT\_MN         | mN          | Pressure: 10^-3 N                                               |

*Note: New sensor data units will be added iteratively later.*

**control**

```c
rt_err_t (*control)(struct rt_sensor_device *sensor, int cmd, void *arg);copymistakeCopy Success
```

The control of the sensor is realized by this interface function. Different operations are performed by judging the different command words passed in. Currently, the following command words are supported:

```c
#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)  /* 自检 */copymistakeCopy Success
```

This function needs to be implemented in the driver as follows:

```c
static rt_err_t xxx_acc_control(struct rt_sensor_device *sensor, int cmd, void *args)
{
    rt_err_t result = RT_EOK;

    switch (cmd)
    {
    case RT_SENSOR_CTRL_GET_ID:
        result = _xxx_acc_get_id(sensor, args);
        break;
    case RT_SENSOR_CTRL_SET_RANGE:
        result = _xxx_acc_set_range(sensor, (rt_int32_t)args);
        break;
    case RT_SENSOR_CTRL_SET_ODR:
        result = _xxx_acc_set_odr(sensor, (rt_uint32_t)args & 0xffff);
        break;
    case RT_SENSOR_CTRL_SET_MODE:
        result = _xxx_acc_set_mode(sensor, (rt_uint32_t)args & 0xff);
        break;
    case RT_SENSOR_CTRL_SET_POWER:
        result = _xxx_acc_set_power(sensor, (rt_uint32_t)args & 0xff);
        break;
    case RT_SENSOR_CTRL_SELF_TEST:
        break;
    default:
        return -RT_ERROR;
    }
    return result;
}copymistakeCopy Success
```

Note

Note: It should be noted that the data type of the parameter passed is specified by the struct rt\_sensor\_config structure. Therefore, `RT_SENSOR_CTRL_SET_RANGE`the parameter passed by this command is `rt_int32_t`of type , which needs to be forced once to get the correct parameter.

Then implement a device interface structure ops to store the above interface functions,

```c
static struct rt_sensor_ops xxx_ops =
{
    xxx_acc_fetch_data,
    xxx_acc_control
};copymistakeCopy Success
```

[**Device Registration**](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development?id=%e8%ae%be%e5%a4%87%e6%b3%a8%e5%86%8c)

After completing the docking of the Sensor's ops, you need to register a sensor device so that the upper layer can find the sensor device and then control it.

Device registration requires the following steps: create a `rt_sensor_t`structure pointer, allocate memory for the structure, and complete related initialization.

```c
int rt_hw_xxx_init(const char *name, struct rt_sensor_config *cfg)
{
    rt_int8_t result;
    rt_sensor_t sensor = RT_NULL;

    sensor = rt_calloc(1, sizeof(struct rt_sensor_device));
    if (sensor == RT_NULL)
        return -1;

    sensor->info.type       = RT_SENSOR_CLASS_ACCE;
    sensor->info.vendor     = RT_SENSOR_VENDOR_STM;
    sensor->info.model      = "xxx_acce";
    sensor->info.unit       = RT_SENSOR_UNIT_MG;
    sensor->info.intf_type  = RT_SENSOR_INTF_I2C;
    sensor->info.range_max  = SENSOR_ACC_RANGE_16G;
    sensor->info.range_min  = SENSOR_ACC_RANGE_2G;
    sensor->info.period_min = 100;

    rt_memcpy(&sensor->config, cfg, sizeof(struct rt_sensor_config));
    sensor->ops = &sensor_ops;

    result = rt_hw_sensor_register(sensor, name, RT_DEVICE_FLAG_RDONLY | RT_DEVICE_FLAG_FIFO_RX, RT_NULL);
    if (result != RT_EOK)
    {
        LOG_E("device register err code: %d", result);
        rt_free(sensor);
        return -RT_ERROR;
    }

    LOG_I("acc sensor init success");
    return 0;
}copymistakeCopy Success
```

Note

Note: - `rt_hw_sensor_register`A prefix will be automatically added to the name passed in, such as `加速度计`the type of sensor will automatically add `acce_`a prefix. Since the system default device name is up to 7 characters, if the name passed in exceeds 3 characters, it will be truncated. - When registering, if the sensor supports FIFO, you need to add the RT\_DEVICE\_FLAG\_FIFO\_RX flag. - If two devices are coupled, you need to use a module to decouple them. Initialize a module, assign the device control blocks of the two sensors to it, and assign the module address to the two sensor devices. The framework will automatically create a module lock.

The parameter passed in `struct rt_sensor_config *cfg`is used to decouple the hardware communication interface. By passing this parameter in when the underlying driver is initialized, the hardware interface configuration is implemented. It contains `struct rt_sensor_intf`this structure,

```c
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 */
};copymistakeCopy Success
```

Among them, type indicates the type of interface, dev\_name indicates the name of the device, such as "i2c1". user\_data is some private data of this interface type. If it is I2C, this is the i2c address corresponding to the sensor, and the input method is `(void*)0x55`.

When initializing the underlying driver, you need to initialize this structure first, and then pass it in as a parameter to complete the decoupling of the communication interface. Similar to this:

```c
#define irq_pin GET_PIN(B, 0)

int lps22hb_port(void)
{
    struct rt_sensor_config cfg;

    cfg.intf.dev_name = "i2c1";
    cfg.intf.user_data = (void *)0x55;
    cfg.irq_pin.pin = irq_pin;
    cfg.irq_pin.mode = PIN_MODE_INPUT_PULLDOWN;
    rt_hw_xxx_init("xxx", &cfg);

    return RT_EOK;
}
INIT_APP_EXPORT(lps22hb_port);copymistakeCopy Success
```

#### [test](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development?id=%e6%b5%8b%e8%af%95) <a href="#ce-shi" id="ce-shi"></a>

1. Use the list\_device command to check whether the corresponding device is successfully registered.
2. Use the exported test function `sensor_polling/int/fifo <sensor_name>` to determine whether the data can be read successfully.
3. Testing other modes and control interfaces

#### [submit](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development?id=%e6%8f%90%e4%ba%a4) <a href="#ti-jiao" id="ti-jiao"></a>

The Sensor driver needs to `软件包`be submitted in the form of . For the specific structure, please refer to the existing Sensor software package. For the specific submission process, please refer to the Document Center: [Software Package Development Guide](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/package/package)

#### [Precautions](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development?id=%e6%b3%a8%e6%84%8f%e4%ba%8b%e9%a1%b9) <a href="#zhu-yi-shi-xiang" id="zhu-yi-shi-xiang"></a>

* Used when dynamically allocating memory `rt_calloc`, this API will initialize the requested memory to 0, and there is no need to manually clear it.
* Please assign initial values ​​to statically defined variables, and initialize unused variables to 0.
* If possible, please support multiple instances. Please pay attention to the following issues:
  * No global variables
  * All configuration information is stored in the sensor structure
  * Configurations not in the sensor structure are stored using the user\_data of rt\_device
