Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
颜色识别传感器
RT-Thread实战笔记|TCS34725 RGB 颜色识别传感器详解
发布于 2021-08-14 08:33:30 浏览:1051
订阅该版
[tocm] 原文链接: https://mp.weixin.qq.com/s/AVBJwVJz8pd0uXa0ZgiJow # rt-thread是什么? RT-Thread 是一个集实时操作系统(RTOS)内核、中间件组件和开发者社区于一体的技术平台,组件完整丰富、高度可伸缩、简易开发、超低功耗、高安全性的物联网操作系统。 RT-Thread 拥有良好的软件生态,支持市面上所有主流的编译工具如 GCC、Keil、IAR 等,工具链完善、友好,支持各类标准接口,如 POSIX、CMSIS、C++应用环境、Javascript 执行环境等,方便开发者移植各类应用程序。商用支持所有主流MCU架构,如 ARM Cortex-M/R/A, MIPS, X86, Xtensa, C-Sky, RISC-V,几乎支持市场上所有主流的 MCU 和 Wi-Fi 芯片。 小飞哥结缘RT_Thread是去年参加全连接大赛,获得了个优秀奖,参赛项目是无线重力感应遥控小车,项目完整资料已经开源,小伙伴们可以自行获取,话不多说,开淦! ![image.png](https://oss-club.rt-thread.org/uploads/20210814/a91a52461a43ecc13eeb68649233b4a1.png.webp) # IIC通讯协议简介 I2C(Inter Integrated Circuit)总线是 PHILIPS 公司开发的一种半双工、双向二线制同步串行总线。I2C 总线传输数据时只需两根信号线,一根是双向数据线 SDA(serial data),另一根是双向时钟线 SCL(serial clock)。SPI 总线有两根线分别用于主从设备之间接收数据和发送数据,而 I2C 总线只使用一根线进行数据收发。 I2C 和 SPI 一样以主从的方式工作,不同于 SPI 一主多从的结构,它允许同时有多个主设备存在,每个连接到总线上的器件都有唯一的地址,主设备启动数据传输并产生时钟信号,从设备被主设备寻址,同一时刻只允许有一个主设备。如下图所示: ![image.png](https://oss-club.rt-thread.org/uploads/20210814/0d13a34423fda1d9ad1598e5bcc38754.png) 如下图所示为 I2C 总线主要的数据传输格式: ![image.png](https://oss-club.rt-thread.org/uploads/20210814/d992f22f9c2f7785f4111f4446f40b0e.png) 当总线空闲时,SDA 和 SCL 都处于高电平状态,当主机要和某个从机通讯时,会先发送一个开始条件,然后发送从机地址和读写控制位,接下来传输数据(主机发送或者接收数据),数据传输结束时主机会发送停止条件。传输的每个字节为8位,高位在前,低位在后。数据传输过程中的不同名词详解如下所示: - 开始条件: SCL 为高电平时,主机将 SDA 拉低,表示数据传输即将开始,老规矩,还是波形说话,下面的波形就是对开始条件最好的描述: ![image.png](https://oss-club.rt-thread.org/uploads/20210814/7b6204024693f3deb7696ffe1085b915.png.webp) - 从机地址: 主机发送的第一个字节为从机地址,高 7 位为地址,最低位为 R/W 读写控制位,1 表示读操作,0 表示写操作。一般从机地址有 7 位地址模式和 10 位地址模式两种,如果是 10 位地址模式,第一个字节的头 7 位 是 11110XX 的组合,其中最后两位(XX)是 10 位地址的两个最高位,第二个字节为 10 位从机地址的剩下8位,如下图所示: ![image.png](https://oss-club.rt-thread.org/uploads/20210814/914405542f6d20a86dd45535aa95616d.png) - 应答信号: 每传输完成一个字节的数据,接收方就需要回复一个 ACK(acknowledge)。写数据时由从机发送 ACK,读数据时由主机发送 ACK。当主机读到最后一个字节数据时,可发送 NACK(Not acknowledge)然后跟停止条件。 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/768d6133e02527b1cfb25db26f30ce59.png.webp) - 数据: 从机地址发送完后可能会发送一些指令,依从机而定,然后开始传输数据,由主机或者从机发送,每个数据为 8 位,数据的字节数没有限制。 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/768d6133e02527b1cfb25db26f30ce59.png.webp) - 重复开始条件: 在一次通信过程中,主机可能需要和不同的从机传输数据或者需要切换读写操作时,主机可以再发送一个开始条件。 - 停止条件: 在 SDA 为低电平时,主机将 SCL 拉高并保持高电平,然后在将 SDA 拉高,表示传输结束。 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/7b6204024693f3deb7696ffe1085b915.png.webp) 以上就是IIC协议的基本原理,看起来比较简单,实现起来也不难,MCU有硬件IIC的可以使用硬件IIC,没有的话,使用IO模拟即可。 # RT-Thread IIC设备驱动使用 习惯于MCU BSP驱动开发的玩家来说,初识RT_Thread的设备驱动可能有点蒙,原因在于RT_thread代码结构大多是面向对象的,类linux风格,学习过linux开发的小伙伴来看rt-thread一定会倍感亲切的... 接下来就以IIC设备驱动代码来看,如何使用rt-thread的IIC驱动 ## 访问IIC设备 一般情况下 MCU 的 I2C 器件都是作为主机和从机通讯,在 RT-Thread 中将 I2C 主机虚拟为 I2C总线设备,I2C 从机通过 I2C 设备接口和 I2C 总线通讯,相关接口如下所示: | 函数 | 描述 | | :--------- | :--: | | rt_device_find() | 根据 I2C 总线设备名称查找设备获取设备句柄 | | rt_i2c_transfer() | 传输数据 | ## 查找IIC设备 在使用 I2C 总线设备前需要据 I2C 总线设备名称获取设备句柄,进而才可以操作 I2C 总线设备,查找设备函数如下所示: rt_device_t rt_device_find(const char* name); | 参数 | 描述 | | :--------- | :--: | | name | 根据 I2C 总线设备名称查找设备获取设备句柄 | | 返回 | —— | | 设备句柄 | 查找到对应设备将返回相应的设备句柄 | | RT_NULL | 没有找到相应的设备对象 | 如何使用呢,以下面要做的RGB颜色识别传感器为例,参数“name”实际上就是我们要用到的IIC设备名称 ```java #define RGBSensor_I2C_BUS_NAME "i2c3" /* 传感器连接的I2C总线设备名称 */ static struct rt_i2c_bus_device *i2c_bus = RT_NULL; /* I2C总线设备句柄 */ static void rgbSensor_init(const char *name) { /* 查找I2C总线设备,获取I2C总线设备句柄 */ i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name); if (i2c_bus == RT_NULL) { rt_kprintf("can't find %s device failed!\n", name); } else { rt_kprintf("find %s device success!\n", name); rt_pin_mode(sensor_ledpin, PIN_MODE_OUTPUT); begin(); } } ``` ## 数据传输 获取到 I2C 总线设备句柄就可以使用 rt_i2c_transfer() 进行数据传输。函数原型如下所示: ```java rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus, struct rt_i2c_msg msgs[], rt_uint32_t num); ``` | 参数 | 描述 | | :--------- | :--: | | bus |I2C 总线设备句柄 | | msgs[] | 待传输的消息数组指针 | | num | 消息数组的元素个数 | | 返回 | -- | | 消息数组的元素个数 | 成功 | | 错误码 | 失败 | 和 SPI 总线的自定义传输接口一样,I2C 总线的自定义传输接口传输的数据也是以一个消息为单位。参数 msgs[] 指向待传输的消息数组,用户可以自定义每条消息的内容,实现 I2C 总线所支持的 2 种不同的数据传输模式。如果主设备需要发送重复开始条件,则需要发送 2 个消息。 关于IIC通讯所有到的地址、读写控制、数据长度、数据等参数封装在一个结构体中: ```java struct rt_i2c_msg { rt_uint16_t addr; /* 从机地址 */ rt_uint16_t flags; /* 读、写标志等 */ rt_uint16_t len; /* 读写数据字节数 */ rt_uint8_t *buf; /* 读写数据缓冲区指针 */ } ``` - 注意: 此函数会调用 rt_mutex_take(), 不能在中断服务程序里面调用,会导致 assertion 报错。 关于IIC的主要函数原型如上面,底层的东西就不需要关心了,有没有一种很方便(不知所措)的感觉,回想起以前自己编写的,起始信号、停止信号、超时判断,一点一点都要自己动手,简直了...但是初学者学学编写逻辑也挺好的,慢慢转向面向对象也挺好... # RGB颜色识别传感器实战 上面介绍了rtt IIC设备驱动的基本使用,小伙伴们是不是还有点迷糊,下面来实战,如何使用 ## 颜色传感器简介 小飞哥在某宝买的,¥6,IIC通讯接口,板子虽小,还挺精致,原理图还是不错的,可以参考参考 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/73a0ea5185e61dc46491cf8dd5d1c12d.png.webp) 红框中的双向电平转换电路是一个不错的低成本转换电路,完全可以借鉴应用到自己的产品中 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/41a69e1fc5deb7fd97c64e47a2ced8b4.png.webp) - 接口说明 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/4372049483ac95c2a9cb5f9a49cdb905.png.webp) - 通讯协议 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/1b46dd41a500d98b4712276684e2ec72.png.webp) ![image.png](https://oss-club.rt-thread.org/uploads/20210814/323b56e42f546edcc2244c7676f21848.png.webp) 更多的传感器信息,请查看传感器datasheet ## 代码编写 - 硬件连接 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/bb145b68f818577c675ea2ffdb373e49.png.webp) ![image.png](https://oss-club.rt-thread.org/uploads/20210814/e5f7560360de1031dada9d95ded3fc7c.png.webp) - rt-Thread studio配置 新建工程就不说了,小飞哥前面的文章有讲到过,采用模拟IIC,来看看如何配置组件,我选择的是IIC3,随自己心意就好,配置比较简单,配置完保存即可 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/d76c9ebd5d431f8d0a33ece81082f450.png.webp) ![image.png](https://oss-club.rt-thread.org/uploads/20210814/c6b98b077242fdab7ba3d9d000c61752.png.webp) ![image.png](https://oss-club.rt-thread.org/uploads/20210814/b1a962579e46f9b3c0d8bd8eebd1bfbe.png.webp) 经过上面章节的介绍,对rtt iic设备的使用有了一些简单的了解,下面来具体使用,先来看初始化过程 - 1、查找IIC设备是否注册 ```java static void rgbSensor_init(const char *name) { /* 查找I2C总线设备,获取I2C总线设备句柄 */ i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name); if (i2c_bus == RT_NULL) { rt_kprintf("can't find %s device failed!\n", name); } else { rt_kprintf("find %s device success!\n", name); rt_pin_mode(sensor_ledpin, PIN_MODE_OUTPUT); begin(); } } ``` 编译完,不出意外的话,应该会出现以下,恭喜你,完成了第一步... ![](https://oss-club.rt-thread.org/uploads/20220714/dd34eed97b823d16bd4fc14b2b32d5121865167f.png) 接下来是对传感器的读写操作,先来封装两个读写寄存器函数 ```java /* 写传感器寄存器 */ static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint32_t *data) { rt_uint8_t buf[3]; struct rt_i2c_msg msgs; rt_uint32_t buf_size = 1; buf[0] = TCS34725_COMMAND_BIT | reg; //cmd if (data != RT_NULL) { buf[1] = data[0]; buf[2] = data[1]; buf_size = 3; } msgs.addr = TCS34725_ADDRESS; msgs.flags = RT_I2C_WR; msgs.buf = buf; msgs.len = buf_size; /* 调用I2C设备接口传输数据 */ if (rt_i2c_transfer(bus, &msgs, 1) == 1) { return RT_EOK; } else { return -RT_ERROR; } } ``` ```java /* 读传感器寄存器数据 */ static rt_err_t read_regs(struct rt_i2c_bus_device *bus,rt_uint8_t reg,rt_uint8_t len, rt_uint8_t *buf) { struct rt_i2c_msg msgs; write_reg(i2c_bus,reg, RT_NULL); msgs.addr = TCS34725_ADDRESS; msgs.flags = RT_I2C_RD; msgs.buf = buf; msgs.len = len; /* 调用I2C设备接口传输数据 */ if (rt_i2c_transfer(bus, &msgs, 1) == 1) { return RT_EOK; } else { return -RT_ERROR; } } ``` 接下来先来确认模块是不是连接成功,进行读ID操作,这也是确认MCU和模组连接的常用方法先来读ID试试,需要注意下,型号不同,器件ID有区别,小飞哥被坑了好一会才想起来查手册...所以大家一定要先看手册,看一些关键信息,避免因手册没看浪费时间 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/b916e486abaa561d52c4d1170f72c282.png.webp) ```java /**************************************************************************/ /*! Initializes I2C and configures the sensor (call this function before doing anything else) */ /**************************************************************************/ rt_uint8_t begin(void) { rt_uint8_t sensor_id = 0; /* Make sure we're actually connected */ read_regs(i2c_bus,TCS34725_ID,1,&sensor_id); if ((sensor_id != 0x4d) && (sensor_id != 0x10)) { rt_kprintf("find sensor failed!\n"); return 0; } rt_kprintf("sensor ID is:%0x!\n",sensor_id); rt_kprintf("find sensor success!\n"); /* Set default integration time and gain */ RT_setIntegrationTime(TCS34725_INTEGRATIONTIME_50MS); RT_setGain(TCS34725_GAIN_1X); /* Note: by default, the device is in power down mode on bootup */ RT_enable(); return 1; } ``` 顺利的话,是没问题的,设备连接OK的 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/fbe2a342f532ab4cb6a53251fd46bff2.png.webp) 获取原始数据,根据下图中的RGBC寄存器地址读取数据即可 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/fbb4117395958d9d3520d18dcbfb2de8.png.webp) ```java /******************************************************************************* * @brief TCS34725获取各个通道数据 * * @return 1 - 转换完成,数据可用 * 0 - 转换未完成,数据不可用 *******************************************************************************/ rt_uint8_t RT_getRawData (COLOR_RGBC *rgbc) { rt_uint8_t data[2] = {0}; rt_uint8_t status = TCS34725_STATUS_AVALID; read_regs(i2c_bus,TCS34725_STATUS,1,&status); if(status & TCS34725_STATUS_AVALID) { read_regs(i2c_bus,TCS34725_CDATAL,2,data); rgbc->c = (data[1]<<8)|data[0]; read_regs(i2c_bus,TCS34725_RDATAL,2,data); rgbc->r = (data[1]<<8)|data[0]; read_regs(i2c_bus,TCS34725_GDATAL,2,data); rgbc->g = (data[1]<<8)|data[0]; read_regs(i2c_bus,TCS34725_BDATAL,2,data); rgbc->b = (data[1]<<8)|data[0]; return 1; } return 0; } ``` 如果感觉获取的颜色值跟实际差别很大,可有调节积分时间和增益倍数两个参数 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/1bbd848997be6fab8362a8d5eedc950c.png.webp) ![image.png](https://oss-club.rt-thread.org/uploads/20210814/23289d1d403965b58b1344558f0e2dfe.png.webp) # 实验结果 用windows画板调的颜色为红色255,0,0,实际识别出来的是200,56,40左右 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/e4138915273c04fa80f81e817f704a84.png.webp) 实际效果会有点偏差,小伙伴们可以自己调调试试,能不能得到比较好的结果 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/a371a45dfd59b1ab3961e0700b7b8949.png.webp) 绿色 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/617c33bf546055e375796626802ce57a.png.webp) 颜色实际有些偏淡 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/5ad4ff43d930f21a5d8e3970cf5f2eb3.png.webp) 蓝色 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/73a95c7490a0ccd8c59797d7c395676e.png.webp) ![image.png](https://oss-club.rt-thread.org/uploads/20210814/2970ae960ba802466f38ed7058069e58.png.webp) # 源码获取 欢迎添加小飞哥好友,公众号后台回复“RGB”即可获取源码,欢迎加入群聊,获得更多有趣的设计哦 ![image.png](https://oss-club.rt-thread.org/uploads/20210814/f6d70c80b33cdf8c9b4697d8f4857dfc.png)
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
Enthusiasm
公众号:小飞哥玩嵌入式
文章
8
回答
7
被采纳
0
关注TA
发私信
相关文章
推荐文章
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组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
SFUD
msh
ulog
C++_cpp
MicroPython
本月问答贡献
三世执戟
7
个答案
1
次被采纳
RTT_逍遥
4
个答案
1
次被采纳
KunYi
4
个答案
1
次被采纳
xiaorui
1
个答案
1
次被采纳
JonasWen
1
个答案
1
次被采纳
本月文章贡献
出出啊
1
篇文章
3
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部