Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
DIY综合交流区
智能家居DIY教程连载(1)——如何正确使用 Sensor 框架
发布于 2019-07-17 17:56:33 浏览:4571
订阅该版
[tocm] # 智能家居DIY教程连载(1) > 如何正确使用 Sensor 框架 Hi~各位小伙伴们,距离 DIY 项目的发布已经有一周的时间了,第一周的任务已经完成了吗?会不会遇到了问题无从下手?没关系,这篇文章很好的解决了大家的困惑,一起来看看吧。 ## 1. 第一周任务回顾 我们来回顾一下第一周安排的任务: - 正确读取 ds18b20 温度数据 - 了解 RT-Thread 的 Sensor 框架,并将 ds18b20 对接到 Sensor 框架上 - 了解线程的使用,创建一个线程,在线程中读取温度数据,并通过FinSH控制台实时打印出来 上述任务的重点,其实不在于把温度读取到就好了的,重点在于如何将 ds18b20 对接到 RT-Thread 的 Sensor 框架上去。 Sensor 是物联网重要的一部分,“Sensor 之于物联网”相当于“眼睛之于人类”。人没有眼睛就看不到这大千的花花世界,物联网没有了 Sensor 更是不能感知这变化万千的世界。 现在,为物联网开发的 Sensor 已经很多了,不同的传感器厂商、不同的传感器都需要配套自己独有的驱动才能运转起来,这样在开发应用程序的时候就需要针对不同的传感器做适配,自然加大了开发难度。为了降低应用开发的难度,增加传感器驱动的可复用性,我们设计了 Sensor 驱动框架。 Sensor 驱动框架的作用是:为上层提供统一的操作接口,提高上层代码的可重用性;简化底层驱动开发的难度,只要实现简单的 ops(operations: 操作命令) 就可以将传感器注册到系统上。 本次 DIY 活动,以 ds18b20 温度传感器为例子,教大家如何正确使用 Sensor 框架。 ## 2. Sensor 框架介绍 ![sensor整体框架图.png](/uploads/201907/17/174857gtbpg25rrvbvrtpv.png) Sensor 框架的整体框架图如上。它为上层提供的是标准 device 接口 `open/close/read/write/control`,这些接口与上层用户程序对接,为底层驱动提供的是简单的 ops(operations: 操作命令)接口:`fetch_data/control`,这两个接口对接具体硬件的底层驱动。除此之外,Sensor 框架还支持 module(模块),为底层存在耦合的传感器设备提供服务,如果,ds18b20 的底层不存在耦合,此处不需要用到 module。 Sensor 框架更多的介绍在 RT-Thread 的文档中心已有详细说明,这里不过多赘述,详情请查看[这](https://www.rt-thread.org/document/site/development-guide/sensor/sensor_driver/) ## 3. Sensor 框架的使用 看完文档中心的 Sensor 介绍后,相信大伙儿已经对这个框架有了一定的了解。有的小伙伴是不是早就按耐不住想要跃跃欲试将传感器对接到 Sensor 框架上?这里以 ds18b20 温度传感器为例子。 Sensor 框架的使用分两个步骤:ops 接口对接、传感器设备注册。 ### 3.1 ops 接口对接 我们知道,Sensor 框架的接口分为上层接口和底层接口两种。将 ds18b20 的底层驱动对接到框架上,其实对接就是 Sensor 框架的底层接口,具体的,是底层的 ops 接口。 我们在 RT-Thread 源码中可以找到 Sensor 框架的源码,源码路径为:`rt-thread\components\drivers\sensors`,在 `sensor.h` 文件中,我们可以找到对 ops 接口的定义,有两个函数指针,`fetch_data` 和 `contorl`。 ```c 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 并不支持 `control`,我们只需要实现 `fetch_data` 就好了。 Sensor 框架当前默认支持轮询(RT_DEVICE_FLAG_RDONLY)、中断(RT_DEVICE_FLAG_INT_RX)、FIFO(RT_DEVICE_FLAG_FIFO_RX) 这三种打开方式。需要在这里判断传感器的工作模式,然后再根据不同的模式返回传感器数据。我们以轮询的方式读取 ds18b20 的温度数据,那么 `fetch_data` 的实现如下: ```c 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; } ``` 具体的,`_ds18b20_polling_get_data(sensor, buf)` 的实现如下,其中,`ds18b20_get_temperature` 函数就是 ds18b20 温度传感器底层驱动的获取温度的函数。 ```c 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; } ``` 因为不需要 `control`,我们直接让 `control` 返回 `RT_EOK` 即可 ```c static rt_err_t ds18b20_control(struct rt_sensor_device *sensor, int cmd, void *args) { rt_err_t result = RT_EOK; return result; } ``` 这样,我们的 ops 函数就写好了。然后,需要实现一个设备接口的结构体 ops 存储上面的接口函数: ```c static struct rt_sensor_ops sensor_ops = { ds18b20_fetch_data, ds18b20_control }; ``` 这样一来, ops 接口就对接成功了。 ### 3.2 传感器设备注册 完成 Sensor 的 ops 的对接之后还要注册一个 Sensor 设备,这样上层才能找到这个传感器设备,进而进行控制。 设备的注册一共需要下面几步: - 创建一个 rt_sensor_t 的结构体指针 - 为结构体分配内存 - 完成相关初始化 具体的,放到 ds18b20 上面来,具体实现如下: ```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; } } return RT_EOK; __exit: if (sensor_temp) rt_free(sensor_temp); return -RT_ERROR; } ``` 我们来解读一下。 传感器设备注册的第一步:创建一个 rt_sensor_t 的结构体指针,上述代码中是这么实现的: ```c rt_sensor_t sensor_temp = RT_NULL; ``` 传感器设备注册的第二步:为结构体分配内存,上述代码中是这么实现的: ```c sensor_temp = rt_calloc(1, sizeof(struct rt_sensor_device)); if (sensor_temp == RT_NULL) return -1; ``` 传感器设备注册的第三步:完成相关初始化,上述代码中是这么实现的: ```c 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; ``` 传感器设备注册的三个步骤完成之后,就可以放心大胆地注册传感器设备了,上述代码中是这么实现的: ```c rt_hw_sensor_register(sensor_temp, name, RT_DEVICE_FLAG_RDONLY, RT_NULL); ``` 上述的“ops 接口对接”和“传感器设备注册”两个工作完成后,就可以通过 Sensor 框架中的上层接口 `open/close/read/write/control`,对 ds18b20 进行操作了。 先不着急,我们在 FinSH 中输入 `list_device` 命令查看 ds18b20 温度传感器是否真的已经被注册上去了: ![list_device.jpg](/uploads/201907/17/174857gw7htx6s6au5wty7.jpg) 哇!成功将 ds18b20 注册成传感器设备了,可喜可贺!!! 传感器驱动对接 Sensor 框架的操作中的更多细节,请在 RT-Thread 的文档中心中[查看](https://www.rt-thread.org/document/site/development-guide/sensor/sensor_driver_development/) ## 4. 在线程中读取温度数据 我们通过一个线程,去实时获取 ds18b20 的温度数据。 线程的基本操作有:创建/初始化( rt_thread_create/rt_thread_init)、启动(rt_thread_startup)、运行(rt_thread_delay/rt_thread_control)、删除/脱离(rt_thread_delete/rt_thread_detach)。 之前我们已经将 ds18b20 对接到 ops 接口并成功注册成传感器设备了,接下来就可以利用 Sensor 框架的上层接口 `open/close/read/write/control` 对 ds18b20 进行操作了。 在 `main` 函数中创建一个读取 ds18b20 温度数据的线程并启动它,线程入口函数是 `read_temp_entry`: ```c rt_thread_t ds18b20_thread, led_thread; ds18b20_thread = rt_thread_create("18b20tem", read_temp_entry, "temp_ds18b20", 512, RT_THREAD_PRIORITY_MAX / 2, 20); if (ds18b20_thread != RT_NULL) { rt_thread_startup(ds18b20_thread); } ``` 在线程入口函数 `read_temp_entry` 中,我们通过几个步骤,就可以读取 ds18b20 的温度数据了: - 创建一个 rt_sensor_data 的数据结构体 - 查找传感器设备驱动 - 打开对应的传感器设备 - 读取传感器设备数据 上述步骤具体实现如下: ```c static void read_temp_entry(void *parameter) { rt_device_t dev = RT_NULL; struct rt_sensor_data sensor_data; rt_size_t res; dev = rt_device_find(parameter); if (dev == RT_NULL) { rt_kprintf("Can't find device:%s
", parameter); return; } if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK) { rt_kprintf("open device failed!
"); return; } rt_device_control(dev, RT_SENSOR_CTRL_SET_ODR, (void *)100); while (1) { res = rt_device_read(dev, 0, &sensor_data, 1); if (res != 1) { rt_kprintf("read data failed!size is %d
", res); rt_device_close(dev); return; } else { rt_kprintf("temp:%3d.%dC, timestamp:%5d
", sensor_data.data.temp / 10, sensor_data.data.temp % 10, sensor_data.timestamp); } rt_thread_mdelay(100); } } ``` 通过 FinSH 控制台,查看该线程源源不断输出的数据: ![result.jpg](/uploads/201907/17/174857cnlj0cn0bnsply0v.jpg) 温度数据在线程中被正确读取出来了,到此为止,本周任务就算是成功完成了的,因吹斯听~(^_^) ## 5. 开源代码 为了更进一步便于大家学习,第一周任务的代码已经开源啦~ [请点击这里查看](https://github.com/willianchanlovegithub/DIY_projects_base_on_RT-Thread) ## 6. 注意事项 - 欲使用 Sensor 框架,当然是要在 menuconfig 中将它开启啦: ```c RT-Thread Components ---》 Device Drivers ---》 Using Sensor device drivers ``` - 打开之后,需要使用 `scons --target=mdk5` 更新工程即可。看,Sensor 框架加入到工程当中了: ![sensor in project.jpg](/uploads/201907/17/174857kz995d3wmgjw6zog.jpg)
查看更多
39
个回答
默认排序
按发布时间排序
RTT_猪pi皮皮
2019-09-11
这家伙很懒,什么也没写!
![1568190888(1).jpg](https://club.rt-thread.org/uploads/201909/11/163739mlpmueyh7emcuk1d.jpg) 为什么我直接下载软件包,将代码下到板子里面还是找不到ds18b20
WillianChan
2019-07-17
-
我自己顶自己(沙发
flyboy
2019-07-17
Do my self();
地板
国际哥
2019-07-17
这家伙很懒,什么也没写!
先顶后看,楼主NB!
来一颗糖
2019-07-17
这家伙很懒,什么也没写!
:loveliness:
WillianChan
2019-07-17
-
>地板 你是板凳{:3_49:}
wuhanstudio
2019-07-17
这家伙很懒,什么也没写!
论坛的排版功能好辛苦啊, > 如果能增加源码模式就好了
word天
2019-07-17
这家伙很懒,什么也没写!
不知道从WordPress转自己的博文方便不
WillianChan
2019-07-17
-
>不知道从WordPress转自己的博文方便不 可以试试
WillianChan
2019-07-17
-
>论坛的排版功能好辛苦啊!如果能增加源码模式就好了 全部变成`>`了
撰写答案
登录
注册新账号
关注者
0
被浏览
4.6k
关于作者
WillianChan
-
提问
6
回答
284
被采纳
8
关注TA
发私信
相关问题
1
[项目]搞个开源的硬件项目
2
硬件计划贴,及时更新,欢迎提意见
3
软件计划贴,及时更新,欢迎提意见::WMA,MOUNT,LWIP等问题急需解决.
4
MMS协议
5
定点的wma解压库-libwma
6
QQ群记录 [20090821]
7
STM32网络收音机PCB报名征集
8
第一版调试记录
9
第二版硬件讨论
10
RADIO项目相关模块规格--欢迎大家自己做板时规格与此兼容,减少重复劳动
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
使用百度AI助手辅助编写一个rt-thread下的ONVIF设备发现功能的功能代码
2
RT-Thread 发布 EtherKit开源以太网硬件!
3
rt-thread使用cherryusb实现虚拟串口
4
《C++20 图形界面程序:速度与渲染效率的双重优化秘籍》
5
《原子操作:程序世界里的“最小魔法单位”解析》
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
at_device
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
7
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
3
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部