SENSOR Devices

Sensor is an important part of the Internet of Things. "Sensor" is to the Internet of Things as "eyes are to humans". Without eyes, humans cannot see the world, and the same is true for the Internet of Things.

With the development of the Internet of Things, a large number of sensors have been developed for developers to choose from, such as accelerometers, magnetometers, gyroscopes, barometers/pressure, and humidometers. These sensors are produced by major semiconductor manufacturers in the world. Although they increase the market's selectivity, they also increase the difficulty of application development. Because different sensor manufacturers and different sensors need to be equipped with their own unique drivers to operate, when developing applications, they need to adapt to different sensors, which naturally increases the difficulty of development. In order to reduce the difficulty of application development and increase the reusability of sensor drivers, we designed sensor devices.

The function of the sensor device is to provide a unified operation interface for the upper layer and improve the reusability of the upper layer code.

  • Interface: standard device interface (open/close/read/control)

  • Working mode: Support polling, interrupt and FIFO modes

  • Power mode: supports four modes: power off, normal, low power consumption, and high power consumption

Click the sensor list to view currently supported sensors.

The application accesses the sensor device through the I/O device management interface provided by RT-Thread. The relevant interface is as follows:

function

describe

rt_device_find()

Find the device and get the device handle according to the sensor device name

rt_device_open()

Open the sensor device

rt_device_read()

Reading Data

rt_device_control()

Controlling sensor devices

rt_device_set_rx_indicate()

Set the receiving callback function

rt_device_close()

Turn off sensor devices

The application obtains the device handle according to the sensor device name, and then can operate the sensor device. The device search function is as follows:

rt_device_t rt_device_find(const char* name);copymistakeCopy Success

parameter

describe

name

Sensor device name

return

——

Device handle

If the corresponding device is found, the corresponding device handle will be returned.

RT_NULL

No corresponding device object was found

The usage examples are as follows:

#define SENSOR_DEVICE_NAME    "acce_st"    /* 传感器设备名称 */

static rt_device_t sensor_dev;         /* 传感器设备句柄 */
/* 根据设备名称查找传感器设备,获取设备句柄 */
sensor_dev = rt_device_find(SENSOR_DEVICE_NAME);copymistakeCopy Success

Through the device handle, the application can open and close the device. When opening the device, it will detect whether the device has been initialized. If it has not been initialized, the initialization interface will be called by default to initialize the device. Open the device through the following function:

rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);copymistakeCopy Success

parameter

describe

dev

Device handle

oflags

Device mode flags

return

——

RT_EOK

Device opened successfully

- RT_EBUSY

If the device registration parameter includes the RT_DEVICE_FLAG_STANDALONE parameter, the device will not be allowed to be opened repeatedly.

-RT_EINVAL

Unsupported open parameters

Other error codes

Device open failed

The oflags parameter supports the following parameters:

#define RT_DEVICE_FLAG_RDONLY       0x001     /* 标准设备的只读模式,对应传感器的轮询模式 */
#define RT_DEVICE_FLAG_INT_RX       0x100     /* 中断接收模式 */
#define RT_DEVICE_FLAG_FIFO_RX      0x200     /* FIFO 接收模式 */copymistakeCopy Success

There are three modes for receiving and sending sensor data: interrupt mode, polling mode, and FIFO mode. When using, only one of these three modes can be selected . If the sensor's opening parameter oflags does not specify the interrupt mode or FIFO mode, the polling mode is used by default.

FIFO (First Input First Output) means first in, first out. FIFO transmission mode requires sensor hardware support. Data is stored in the hardware FIFO. Multiple data can be read at one time, which saves CPU resources for other operations. It is very useful in low power mode.

If the sensor is to use FIFO receive mode, oflags takes the value RT_DEVICE_FLAG_FIFO_RX.

An example of using a sensor device in polling mode is as follows:

#define SAMPLE_SENSOR_NAME       "acce_st"  /* 传感器设备名称 */
int main(void)
{
    rt_device_t dev;
    struct rt_sensor_data data;

    /* 查找传感器设备 */
    dev = rt_device_find(SAMPLE_SENSOR_NAME);
    /* 以只读及轮询模式打开传感器设备 */
    rt_device_open(dev, RT_DEVICE_FLAG_RDONLY);

    if (rt_device_read(dev, 0, &data, 1) == 1)
    {
        rt_kprintf("acce: x:%5d, y:%5d, z:%5d, timestamp:%5d\n", data.data.acce.x, data.data.acce.y, data.data.acce.z, data.timestamp);
    }
    rt_device_close(dev);

    return RT_EOK;
}copymistakeCopy Success

Through the command control word, the application can configure the sensor device through the following functions:

rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);copymistakeCopy Success

parameter

describe

dev

Device handle

cmd

Command control word, see below for details

arg

Control parameters, detailed description see below

return

——

RT_EOK

Function execution successful

-RT_ENOSYS

Execution failed, dev is empty

Other error codes

Execution failed

The cmd currently supports the following command control characters:

#define  RT_SENSOR_CTRL_GET_ID        /* 读设备ID */
#define  RT_SENSOR_CTRL_GET_INFO      /* 获取设备信息 */
#define  RT_SENSOR_CTRL_SET_RANGE     /* 设置传感器测量范围 */
#define  RT_SENSOR_CTRL_SET_ODR       /* 设置传感器数据输出速率,unit is HZ */
#define  RT_SENSOR_CTRL_SET_POWER     /* 设置电源模式 */
#define  RT_SENSOR_CTRL_SELF_TEST     /* 自检 */copymistakeCopy Success

Get device information

struct rt_sensor_info info;
rt_device_control(dev, RT_SENSOR_CTRL_GET_INFO, &info);
LOG_I("vendor :%d", info.vendor);
LOG_I("model  :%s", info.model);
LOG_I("unit   :%d", info.unit);
LOG_I("intf_type :%d", info.intf_type);
LOG_I("period_min:%d", info.period_min);copymistakeCopy Success

Read device ID

rt_uint8_t reg = 0xFF;
rt_device_control(dev, RT_SENSOR_CTRL_GET_ID, &reg);
LOG_I("device id: 0x%x!", reg);copymistakeCopy Success

Setting the measurement range

The units used when setting the sensor's measurement range are the units provided when the device was registered.

rt_device_control(dev, RT_SENSOR_CTRL_SET_RANGE, (void *)1000);copymistakeCopy Success

Set the data output rate

Set the output rate to 100HZ and call the following interface.

rt_device_control(dev, RT_SENSOR_CTRL_SET_ODR, (void *)100);copymistakeCopy Success

Setting the power mode

/* 设置电源模式为掉电模式 */
rt_device_control(dev, RT_SENSOR_CTRL_SET_POWER, (void *)RT_SEN_POWER_DOWN);
/* 设置电源模式为普通模式 */
rt_device_control(dev, RT_SENSOR_CTRL_SET_POWER, (void *)RT_SEN_POWER_NORMAL);
/* 设置电源模式为低功耗模式 */
rt_device_control(dev, RT_SENSOR_CTRL_SET_POWER, (void *)RT_SEN_POWER_LOW);
/* 设置电源模式为高性能模式 */
rt_device_control(dev, RT_SENSOR_CTRL_SET_POWER, (void *)RT_SEN_POWER_HIGH);copymistakeCopy Success

Equipment self-test

int test_res;
/* 控制设备自检 并把结果返回回来,RT_EOK 自检成功, 其他自检失败 */
rt_device_control(dev, RT_SENSOR_CTRL_SELF_TEST, &test_res);copymistakeCopy Success

The data receiving indication can be set through the following function. When the sensor receives data, it notifies the upper application thread that data has arrived:

rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));copymistakeCopy Success

parameter

describe

dev

Device handle

rx_ind

Callback function pointer

dev

Device handle (callback function parameter)

size

Buffer data size (callback function parameter)

return

——

RT_EOK

Set up for success

The callback function of this function is provided by the caller. If the sensor is turned on in interrupt receiving mode, when the sensor receives data and generates an interrupt, the callback function will be called, and the data size of the buffer at this time will be placed in the size parameter, and the sensor device handle will be placed in the dev parameter for the caller to obtain.

In general, the receiving callback function can send a semaphore or event to notify the sensor data processing thread that data has arrived. The usage example is as follows:

#define SAMPLE_SENSOR_NAME       "acce_st"  /* 传感器设备名称 */
static rt_device_t dev;             /* 传感器设备句柄 */
static struct rt_semaphore rx_sem;    /* 用于接收消息的信号量 */

/* 接收数据回调函数 */
static rt_err_t sensor_input(rt_device_t dev, rt_size_t size)
{
    /* 传感器接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    rt_sem_release(&rx_sem);

    return RT_EOK;
}

static int sensor_sample(int argc, char *argv[])
{
    dev = rt_device_find(SAMPLE_SENSOR_NAME);

    /* 以中断接收及轮询发送模式打开传感器设备 */
    rt_device_open(dev, RT_DEVICE_FLAG_INT_RX);
    /* 初始化信号量 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_PRIO);

    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(dev, sensor_input);
}
copymistakeCopy Success

The following function can be called to read the data received by the sensor:

rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);copymistakeCopy Success

parameter

describe

dev

Device handle

pos

Read data offset. This parameter is not used by the sensor.

buffer

Buffer pointer. The read data will be saved in the buffer.

size

The number of data read

return

——

>0

Returns the number of data read

0

You need to read the errno of the current thread to determine the error status

The following is an example of using the sensor in interrupt reception mode and in conjunction with the reception callback function:

static rt_device_t dev;             /* 传感器设备句柄 */
static struct rt_semaphore rx_sem;    /* 用于接收消息的信号量 */

/* 接收数据的线程 */
static void sensor_irq_rx_entry(void *parameter)
{
    rt_device_t dev = parameter;
    struct rt_sensor_data data;
    rt_size_t res;

    while (1)
    {
        rt_sem_take(rx_sem, RT_WAITING_FOREVER);

        res = rt_device_read(dev, 0, &data, 1);
        if (res == 1)
        {
            sensor_show_data(dev, &data);
        }
    }
}
copymistakeCopy Success

The following is an example of using the sensor in FIFO receiving mode and in conjunction with the receiving callback function:

static rt_sem_t sensor_rx_sem = RT_NULL;
rt_err_t rx_cb(rt_device_t dev, rt_size_t size)
{
    rt_sem_release(sensor_rx_sem);
    return 0;
}
static void sensor_fifo_rx_entry(void *parameter)
{
    rt_device_t dev = parameter;
    struct rt_sensor_data data;
    rt_size_t res, i;

    data = rt_malloc(sizeof(struct rt_sensor_data) * 32);

    while (1)
    {
        rt_sem_take(sensor_rx_sem, RT_WAITING_FOREVER);

        res = rt_device_read(dev, 0, data, 32);
        for (i = 0; i < res; i++)
        {
            sensor_show_data(dev, &data[i]);
        }
    }
}
int main(void)
{
    static rt_thread_t tid1 = RT_NULL;
    rt_device_t dev;
    struct rt_sensor_data data;

    sensor_rx_sem = rt_sem_create("sen_rx_sem", 0, RT_IPC_FLAG_PRIO);
    tid1 = rt_thread_create("sen_rx_thread",
                            sensor_fifo_rx_entry, dev,
                            1024,
                            15, 5);
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1);

    dev = rt_device_find("acce_st");
    rt_device_set_rx_indicate(dev, rx_cb);
    rt_device_open(dev, RT_SEN_FLAG_FIFO);
    return RT_EOK;
}copymistakeCopy Success

When the application completes the sensor operation, it can close the sensor device through the following function:

rt_err_t rt_device_close(rt_device_t dev);copymistakeCopy Success

parameter

describe

dev

Device handle

return

——

RT_EOK

Shut down the device successfully

-RT_ERROR

The device has been completely shut down. You cannot shut down the device again.

Other error codes

Failed to shut down the device

Closing the device interface and opening the device interface must be used in pairs. Each time you open the device, you must close it once. Only in this way can the device be completely closed. Otherwise, the device will still be in an open state.

The specific usage of the sensor device can refer to the following sample code. The main steps of the sample code are as follows:

  1. First look up the sensor settings to get the device handle.

  2. Turn on the sensor in polling mode.

  3. Read the data 5 times continuously and print it out.

  4. Turn off the sensor.

  • This sample code is not limited to a specific BSP. It can be run by entering a different dev_name according to the sensor device registered in the BSP.

/*
 * 程序清单:这是一个 传感器 设备使用例程
 * 例程导出了 sensor_sample 命令到控制终端
 * 命令调用格式:sensor_sample dev_name
 * 命令解释:命令第二个参数是要使用的传感器设备名称
 * 程序功能:打开对应的传感器,然后连续读取 5 次数据并打印出来。
*/

#include "sensor.h"

static void sensor_show_data(rt_size_t num, rt_sensor_t sensor, struct rt_sensor_data *sensor_data)
{
    switch (sensor->info.type)
    {
    case RT_SENSOR_CLASS_ACCE:
        rt_kprintf("num:%3d, x:%5d, y:%5d, z:%5d, timestamp:%5d\n", num, sensor_data->data.acce.x, sensor_data->data.acce.y, sensor_data->data.acce.z, sensor_data->timestamp);
        break;
    case RT_SENSOR_CLASS_GYRO:
        rt_kprintf("num:%3d, x:%8d, y:%8d, z:%8d, timestamp:%5d\n", num, sensor_data->data.gyro.x, sensor_data->data.gyro.y, sensor_data->data.gyro.z, sensor_data->timestamp);
        break;
    case RT_SENSOR_CLASS_MAG:
        rt_kprintf("num:%3d, x:%5d, y:%5d, z:%5d, timestamp:%5d\n", num, sensor_data->data.mag.x, sensor_data->data.mag.y, sensor_data->data.mag.z, sensor_data->timestamp);
        break;
    case RT_SENSOR_CLASS_HUMI:
        rt_kprintf("num:%3d, humi:%3d.%d%%, timestamp:%5d\n", num, sensor_data->data.humi / 10, sensor_data->data.humi % 10, sensor_data->timestamp);
        break;
    case RT_SENSOR_CLASS_TEMP:
        rt_kprintf("num:%3d, temp:%3d.%dC, timestamp:%5d\n", num, sensor_data->data.temp / 10, sensor_data->data.temp % 10, sensor_data->timestamp);
        break;
    case RT_SENSOR_CLASS_BARO:
        rt_kprintf("num:%3d, press:%5d, timestamp:%5d\n", num, sensor_data->data.baro, sensor_data->timestamp);
        break;
    case RT_SENSOR_CLASS_STEP:
        rt_kprintf("num:%3d, step:%5d, timestamp:%5d\n", num, sensor_data->data.step, sensor_data->timestamp);
        break;
    default:
        break;
    }
}

static void sensor_sample(int argc, char **argv)
{
    rt_device_t dev = RT_NULL;
    struct rt_sensor_data data;
    rt_size_t res, i;

    /* 查找系统中的传感器设备 */
    dev = rt_device_find(argv[1]);
    if (dev == RT_NULL)
    {
        rt_kprintf("Can't find device:%s\n", argv[1]);
        return;
    }

    /* 以轮询模式打开传感器设备 */
    if (rt_device_open(dev, RT_DEVICE_FLAG_RDONLY) != RT_EOK)
    {
        rt_kprintf("open device failed!");
        return;
    }

    for (i = 0; i < 5; i++)
    {
        /* 从传感器读取一个数据 */
        res = rt_device_read(dev, 0, &data, 1);
        if (res != 1)
        {
            rt_kprintf("read data failed!size is %d", res);
        }
        else
        {
            sensor_show_data(i, (rt_sensor_t)dev, &data);
        }
        rt_thread_mdelay(100);
    }
    /* 关闭传感器设备 */
    rt_device_close(dev);
}
MSH_CMD_EXPORT(sensor_sample, sensor device sample);copymistakeCopy Success

Last updated

Assoc. Prof. Wiroon Sriborrirux, Founder of Advance Innovation Center (AIC) and Bangsaen Design House (BDH), Electrical Engineering Department, Faculty of Engineering, Burapha University