RT-Thread学习---Sensor框架

RT-Thread学习—Sensor框架

1.对接底层驱动

Sensor 驱动框架的作用是:为上层提供统一的操作接口,提高上层代码的可重用性;简化底层驱动开发的难度,只要实现简单的 ops(operations: 操作命令) 就可以将传感器注册到系统上。
在这里插入图片描述
如图所示,sensor为上层应用提供统一的标准接口open/close/read/control,sensor框架底层提供ops接口;featch_data/control,实际上使用rt-thread的sensor框架,只需要对接这个接口就行,然后注册设备,应用层就可以访问了。ops接口的定义在sensor.h中,如下所示:

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);
};

对接设备实际上就是实现fetch_data和control两个函数,以ds18b20做实,ds18b20不支持control,所以只需要实现数据获取函数就行。使用RT-Thread添加ds18b20组件包后,在package工程文件夹下的ds18b20-latest文件夹下的src中的文件中实现了fetch_data。
在这里插入图片描述

static rt_size_t ds18b20_fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
    RT_ASSERT(buf);

    if (sensor->config.mode == RT_SENSOR_MODE_POLLING)
    {
        return _ds18b20_polling_get_data(sensor, buf);
    }
    else
        return 0;
}

其中sensor工作模式类型宏定义在sensor.h中,各个区别见宏定义注释:

/* Sensor work mode types */

#define  RT_SENSOR_MODE_NONE           (0)
#define  RT_SENSOR_MODE_POLLING        (1)  /* One shot only read a data */
#define  RT_SENSOR_MODE_INT            (2)  /* TODO: One shot interrupt only read a data */
#define  RT_SENSOR_MODE_FIFO           (3)  /* TODO: One shot interrupt read all fifo data */

Sensor框架支持三种打开方式轮询、中断、FIFO,注册设备时,需要选择参数,三种方式宏定义在rtdef.h头文件中,如下:

#define RT_DEVICE_FLAG_RDONLY           0x001           /**< read only */
#define RT_DEVICE_FLAG_WRONLY           0x002           /**< write only */
#define RT_DEVICE_FLAG_RDWR             0x003           /**< read and write */

回到刚刚,_ds18b20_polling_get_data()函数实际上就是ds18b20底层驱动,用来获取温度。

static rt_size_t _ds18b20_polling_get_data(rt_sensor_t sensor, struct rt_sensor_data *data)
{
    rt_int32_t temperature_x10;
    if (sensor->info.type == RT_SENSOR_CLASS_TEMP)
    {
        temperature_x10 = ds18b20_get_temperature((rt_base_t)sensor->config.intf.user_data);
        data->data.temp = temperature_x10;
        data->timestamp = rt_sensor_get_ts();
    }    
    return 1;
}

其中传感器的信息类型定义在sensor.h中,我们只用到温度:

/* Sensor types */

#define RT_SENSOR_CLASS_NONE           (0)
#define RT_SENSOR_CLASS_ACCE           (1)  /* Accelerometer     */
#define RT_SENSOR_CLASS_GYRO           (2)  /* Gyroscope         */
#define RT_SENSOR_CLASS_MAG            (3)  /* Magnetometer      */
#define RT_SENSOR_CLASS_TEMP           (4)  /* Temperature       */
#define RT_SENSOR_CLASS_HUMI           (5)  /* Relative Humidity */
#define RT_SENSOR_CLASS_BARO           (6)  /* Barometer         */
#define RT_SENSOR_CLASS_LIGHT          (7)  /* Ambient light     */
#define RT_SENSOR_CLASS_PROXIMITY      (8)  /* Proximity         */
#define RT_SENSOR_CLASS_HR             (9)  /* Heart Rate        */
#define RT_SENSOR_CLASS_TVOC           (10) /* TVOC Level        */
#define RT_SENSOR_CLASS_NOISE          (11) /* Noise Loudness    */
#define RT_SENSOR_CLASS_STEP           (12) /* Step sensor       */
#define RT_SENSOR_CLASS_FORCE          (13) /* Force sensor      */

继续往下探,点开ds18b20_get_temperature()函数,就很清晰了:

int32_t ds18b20_get_temperature(rt_base_t pin)
{
    uint8_t TL, TH;
    int32_t tem;
    
    ds18b20_start(pin);
    ds18b20_init(pin);
    ds18b20_write_byte(pin, 0xcc);
    ds18b20_write_byte(pin, 0xbe);
    TL = ds18b20_read_byte(pin);    /* LSB first */
    TH = ds18b20_read_byte(pin);
    if (TH > 7)
    {
        TH =~ TH;
        TL =~ TL;
        tem = TH;
        tem <<= 8;
        tem += TL;
        tem = (int32_t)(tem * 0.0625 * 10 + 0.5);
        return -tem;
    }
    else
    {
        tem = TH;
        tem <<= 8;
        tem += TL;
        tem = (int32_t)(tem * 0.0625 * 10 + 0.5);
        return tem;
    }
}

这个就跟我们裸机编程ds18b20一样了,就是初始化存在检测、但设备跳过ROM等步骤,参考DS18B20的数据手册,按照时序图操作就行。其中稍有不同的是延时函数,因为DS18B20对延时很敏感,涉及到微妙级别的延时,而rt-thread延时一般只到毫秒级别,RT_TICK_PER_SECOND 1000,那么最小单位为1ms。

#define RT_TICK_PER_SECOND 1000

所以要利用滴答定时器达到微妙延时,然而有操作系统时,滴答定时器提供系统的时基,不能再像裸机那样随意改写,所以采取“时钟摘取法”,不改写滴答定时器,而获得定时,大概方法为:定时开始时获取当时的值,然后实时获取SysTick->value的值,两者做差,差值即为已经延时的滴答数,与目标延时数对比判断即可完成定时。
最后实现一个ops结构体存储以上两个函数(其中ds18b20不需要控制,就没说control函数实现)。

static struct rt_sensor_ops sensor_ops =
{
    ds18b20_fetch_data,
    ds18b20_control
};

2.传感器设备注册

完成Sensor的ops对接后,还需要注册Sensor设备,注册完后上层应用就可以找到设备从而使用上层接口使用设备。
注册3步骤:
a.创建一个 rt_sensor_t 的结构体指针 (赋值后传给注册函数)
b.为结构体分配内存
c.完成相关初始化。示例程序如下:

int rt_hw_ds18b20_init(const char *name, struct rt_sensor_config *cfg)
{
    rt_int8_t result;
    rt_sensor_t sensor_temp = RT_NULL; 
    
    if (!ds18b20_init((rt_base_t)cfg->intf.user_data))
    {
        /* temperature sensor register */
        sensor_temp = rt_calloc(1, sizeof(struct rt_sensor_device));
        if (sensor_temp == RT_NULL)
            return -1;

        sensor_temp->info.type       = RT_SENSOR_CLASS_TEMP;
        sensor_temp->info.vendor     = RT_SENSOR_VENDOR_DALLAS;
        sensor_temp->info.model      = "ds18b20";
        sensor_temp->info.unit       = RT_SENSOR_UNIT_DCELSIUS;
        sensor_temp->info.intf_type  = RT_SENSOR_INTF_ONEWIRE;
        sensor_temp->info.range_max  = SENSOR_TEMP_RANGE_MAX;
        sensor_temp->info.range_min  = SENSOR_TEMP_RANGE_MIN;
        sensor_temp->info.period_min = 5;

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

        result = rt_hw_sensor_register(sensor_temp, name, RT_DEVICE_FLAG_RDONLY, RT_NULL);
        if (result != RT_EOK)
        {
            LOG_E("device register err code: %d", result);
            goto __exit;
        }

    }
    else
    {
        LOG_E("DS18B20 init failed! Please check the connection!");
        goto __exit;
    }
    
    return RT_EOK;
    
__exit:
    if (sensor_temp)
        rt_free(sensor_temp);
    return -RT_ERROR;     
}

注册完毕后,下载到板子,使用list_device,即可看到注册的设备:
在这里插入图片描述
可看到注册的设备temp_ds1,那么注册API的传参name是rt_hw_ds18b20_init()函数传入,在示例中,rt_hw_ds18b20_init()由下main函数调用:

static int rt_hw_ds18b20_port(void)
{
    struct rt_sensor_config cfg;

    cfg.intf.user_data = (void *)DS18B20_DATA_PIN;
    rt_hw_ds18b20_init("ds18b20", &cfg);

    return RT_EOK;
}

此处可见设备名传入的为“ds18b20”,为何控制台显示的却是 “temp_ds1”呢?
原因是因为rt_hw_sensor_register()会为传入的 name 自动添加前缀,比如加速度前缀acce_和此处我们使用的温度传感器,则加入temp_前缀,后面继续跟上传参传入的名字,同时因为系统会截取最大名字长度,超过最大名字长度的部分会被截掉,最大名字定义rtconfig.h中:

#define RT_NAME_MAX 8

我这里设置的为8,所以名字就变成了“temp_ds1”一共8个字符。

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值