资讯详情

RT-Thread学习笔记|TCS34725 RGB 颜色识别传感器详解

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去年参加了全连接大赛,获得了优秀奖。参赛项目为无线重力感应遥控车。项目的完整数据已经开源,朋友可以自己拿到。话不多说,凯干!

图片

IIC通信协议简介

I2C(Inter Integrated Circuit)总线是 PHILIPS 公司开发的半双工、双向二线系统同步串行总线。I2C 总线传输数据只需要两条信号线,一条是双向数据线 SDA(serial data),另一个是双向时钟线 SCL(serial clock)。SPI 主从设备之间接收和发送数据的总线有两条线 I2C 数据收发只使用一条线。

I2C 和 SPI 以主从的方式工作是不同的 SPI 一主多从的结构允许同时存在多个主设备。每个连接到总线的设备都有一个唯一的地址。主设备启动数据传输,产生时钟信号,从设备到主设备,同时只允许一个主设备。如下图所示:

如下图所示 I2C 主数据传输格式:

当总线空闲时,SDA 和 SCL 它们都处于高电平状态。当主机想要与从机器通信时,将首先发送启动条件,然后发送从机地址和读写控制位,然后传输数据(主机发送或接收数据)。数据传输结束时,主机将发送停止条件。传输的每个字节为8位,高位在前,低位在后。数据传输过程中的不同术语如下:

  • 开始条件:

SCL 在高电平时,主机会 SDA 降低意味着数据传输即将开始,旧规则,或波形说话,以下波形是对开始条件的最佳描述:

  • 从机地址:

主机发送的第一个字节是从机地址,高 7 地址为,最低位为 R/W 读写控制位,1 表示读操作,0 表示写操作。一般有从机地址 7 地址模式和 10 如果是,有两种位置地址模式 10 位置地址模式,第一字节头 7 位 是 11110XX 最后两个组合(XX)是 10 第二个字节是位置地址的两个最高位 10 位从机地址剩余8位,如下图所示:

  • 应答信号:

每传输完成一个字节的数据,接收方就需要回复一个 ACK(acknowledge)。写数据时从机发送 ACK,读取数据时,由主机发送 ACK。当主机读取最后一个字节数据时,可以发送 NACK(Not acknowledge)然后跟随停止条件。

  • 数据:

从机地址发送后,可以根据从机情况发送一些指令,然后开始传输数据,由主机或从机发送。每个数据都是 8 数据字节数没有限制。

  • 重复开始条件:

在通信过程中,与不同的从机传输数据或切换读写操作时,主机可以发送另一个开始条件。

  • 停止条件:

在 SDA 在低电平时,主机会 SCL 拉高并保持高电平,然后在 SDA 拉高,表示传输结束。

以上就是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设备名称

#defineRGBSensor_I2C_BUS_NAME"i2c3"/*传感器连接I2C总线设备名称*/ staticstructrt_i2c_bus_device*i2c_bus=RT_NULL;/*I2C总线设备句柄*/   staticvoidrgbSensor_init(constchar*name) { /*查找I2C总线设备,获取I2C总线设备句柄*/ i2c_bus=(structrt_i2c_bus_device*)rt_device_find(name);  if(i2c_bus==RT_NULL) { rt_kprintf("can'tfind%sdevicefailed!\n",name); } else { rt_kprintf("find%sdevicesuccess!\n",name); rt_pin_mode(sensor_ledpin,PIN_MODE_OUTPUT); begin(); } } 

数据传输

获取到 I2C 使用总线设备句柄 rt_i2c_transfer() 传输数据。函数原型如下:

rt_size_trt_i2c_transfer(structrt_i2c_bus_device*bus, &bsp;       struct rt_i2c_msg         msgs[],
                          rt_uint32_t               num);
参数 描述
bus I2C 总线设备句柄
msgs[] 待传输的消息数组指针
num 消息数组的元素个数
返回 --
消息数组的元素个数 成功
错误码 失败

和 SPI 总线的自定义传输接口一样,I2C 总线的自定义传输接口传输的数据也是以一个消息为单位。参数 msgs[] 指向待传输的消息数组,用户可以自定义每条消息的内容,实现 I2C 总线所支持的 2 种不同的数据传输模式。如果主设备需要发送重复开始条件,则需要发送 2 个消息。

关于IIC通讯所有到的地址、读写控制、数据长度、数据等参数封装在一个结构体中:

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通讯接口,板子虽小,还挺精致,原理图还是不错的,可以参考参考

红框中的双向电平转换电路是一个不错的低成本转换电路,完全可以借鉴应用到自己的产品中

  • 接口说明

  • 通讯协议

更多的传感器信息,请查看传感器datasheet

代码编写

  • 硬件连接

  • rt-Thread studio配置

新建工程就不说了,小飞哥前面的文章有讲到过,采用模拟IIC,来看看如何配置组件,我选择的是IIC3,随自己心意就好,配置比较简单,配置完保存即可

经过上面章节的介绍,对rtt iic设备的使用有了一些简单的了解,下面来具体使用,先来看初始化过程

  • 1、查找IIC设备是否注册

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

编译完,不出意外的话,应该会出现以下,恭喜你,完成了第一步...

接下来是对传感器的读写操作,先来封装两个读写寄存器函数

/* 写传感器寄存器 */
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;
    }
}
/* 读传感器寄存器数据 */
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有区别,小飞哥被坑了好一会才想起来查手册...所以大家一定要先看手册,看一些关键信息,避免因手册没看浪费时间

/**************************************************************************/
/*!
    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的

获取原始数据,根据下图中的RGBC寄存器地址读取数据即可

/*******************************************************************************
 * @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;
}

如果感觉获取的颜色值跟实际差别很大,可以调节积分时间和增益倍数两个参数

实验结果

用windows画板调的颜色为红色255,0,0,实际识别出来的是200,56,40左右

实际效果会有点偏差,小伙伴们可以自己调调试试,能不能得到比较好的结果

绿色

颜色实际有些偏淡

蓝色

源码获取

欢迎添加小飞哥好友,公众号后台回复“RGB”即可获取源码,欢迎加入群聊,获得更多有趣的设计哦

 

 

标签: 转向传感器组件超低频振动传感器m35工作原理c1623转向传感器rgb传感器0fn传感器rt500传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台