TCS34725 颜色传感器设备驱动程序

2023-02-17,,

一、概述

以前的传感器是用过中断的方式进行计数的,现在已经有 I2C 通行的颜色传感器,不在需要我们像之前那样,通过计数的方式获取数据,直接通过I2C读取即可。当然有通过串口的方式获取采集数据的,串口使用就比较简单了,此笔记只针对 I2C 通信的模块。

我在某宝上随意购买了一个 TCS34725 的颜色采集模块,发现厂家提供的是 Arduino 的案例,还是用 C++ 编写的,我相信有的小伙伴还没接触过 C++ ,这里我将程序改为 C 语言编写,有需要的小伙伴可以收藏一下,

注意: 此笔记中的驱动程序不单纯针对莫一块 MCU,在 ESP32、stm32、arm中都可以使用的,甚至单片机也可稍微修改一下进行使用。

二、TCS34725 使用说明

TCS34725 多数提供的说明文档都是英文版的,英语不要的小伙伴,就有点慌了,不要怕,结合我这里的描述,相信你也能看明白是怎么回事了,下面我会把需要注意的重点进行记录分析。

    传感器接线

    这个就比较简单了,相信能看到这篇笔记的小伙伴应该都没问题的,引脚如下图所示:

    设备地址

    设备寄存器

对于 TCS34725 编程需要注意的就上面三点内容,有这些知识点后,就可以编程了,现在是不是发现,没学过英文也可以完成 TCS34725 传感器的使用。

注意:如果不明白我为啥把说明书中这三个重点拿出的小伙伴,可能是对 I2C 原理还不怎么掌握,建议先去补充一下,然后再回来看就比较清晰了,这里我就不帖链接了,网上有很多。

三、TCS34725 驱动编程

还是老规矩,编程肯定要先配置,然后再使用,这个就不用说了,那么在 TCS34725 传感器中,使用之前主要有五个流程,分别是:读取设备识别号 → 设置集成时间 → 设置增益倍数 → 启动传感器 → 获取采集数据,分析如下所示。

    可操作的寄存器地址

    在编写程序之前,先了解有哪些寄存器可以操作的,如下所示:

    #define TCS34725_address          (0x29)    // 设备地址
    #define TCS34725_COMMAND_BIT (0x80) // 命令字节 /* TCS34725传感器配置寄存器 */
    #define TCS34725_ENABLE (0x00) // 启用传感器
    #define TCS34725_ATIME (0x01) // 集成时间
    #define TCS34725_WTIME (0x03) // R / W 等待时间
    #define TCS34725_AILTL (0x04) // 清除通道下限中断阈值
    #define TCS34725_AILTH (0x05)
    #define TCS34725_AIHTL (0x06) // 清除通道上限中断阈值
    #define TCS34725_AIHTH (0x07) // 配置寄存器
    #define TCS34725_PERS (0x0C) // 中断永久性过滤器
    #define TCS34725_CONFIG (0x0C) // 中断永久性过滤器
    #define TCS34725_CONTROL (0x0F) // 增益倍数
    #define TCS34725_ID (0x12) // 设备识别号 0x44 = TCS34721/TCS34725, 0x4D = TCS34723/TCS34727
    #define TCS34725_STATUS (0x13) // 设备状态
    #define TCS34725_CDATAL (0x14) // 光照强度低字节
    #define TCS34725_CDATAH (0x15) // 光照强度高字节
    #define TCS34725_RDATAL (0x16) // 红色数据低字节
    #define TCS34725_RDATAH (0x17)
    #define TCS34725_GDATAL (0x18) // 绿色数据低字节
    #define TCS34725_GDATAH (0x19)
    #define TCS34725_BDATAL (0x1A) // 蓝色数据低字节
    #define TCS34725_BDATAH (0x1B)

    命令寄存器

    这个命令寄存器的作用图中有详细描述,如下图所示:

    注意:看不懂没关系,只需要记住在进行寄存器操作时,都将寄存器地址或上一个 0x80 即可。

    TCS34725 与 I2C 驱动的接口函数如下

    /**
    * @brief 通过I2C驱动提供的API进行对接,作用是将一个8位数据写入对应的寄存器中
    *
    * @param reg_addr 寄存机地址
    * @param write_data 需要写入的寄存机数据
    * @return uint8_t 无错误时返回 0
    */
    static uint8_t tcs34725_write8(uint8_t reg_addr, uint8_t write_data)
    {
    return esp32_i2c_write8(TCS34725_address, TCS34725_COMMAND_BIT | reg_addr, write_data);
    } /**
    * @brief 通过I2C驱动提供的API进行对接,作用是读取一个8位的数据
    *
    * @param reg_addr 寄存器地址
    * @param read_data 数据存放地址
    * @return uint8_t 无错误时返回 0
    */
    static uint8_t tcs34725_read8(uint8_t reg_addr, uint8_t* read_data)
    {
    return esp32_i2c_read8(TCS34725_address, TCS34725_COMMAND_BIT | reg_addr, read_data);
    } /**
    * @brief 通过I2C驱动提供的API进行对接,作用是读取一个16位的数据
    *
    * @param reg_addr 寄存器地址
    * @param read_data 数据存放地址
    * @return uint8_t 无错误时返回 0
    */
    static uint8_t tcs34725_read16(uint8_t reg_addr, uint16_t* read_data)
    {
    return esp32_i2c_read16(TCS34725_address, TCS34725_COMMAND_BIT | reg_addr, read_data);
    }

    注意:

      从程序中可以看出我在对每个寄存器操作前都或上了一个 0x80
      我使用的 MCU 是 ESP32,所以这里对接的是 ESP32 的 I2C 接口函数,根据自己的需要进行更改即可。
      需要了解 ESP 中 I2C 驱动的可以参考我之间我笔记:ESP32 I2C 总线主模式通信程序

    读取设备识别号

    为什么需要做这步操作了,原因很简单,可以快速验证接线和 I2C 驱动是否正常,这样在完成后面操作是,可以排除这两个麻烦的问题导致的,不然很多小伙伴发现获取不了传感器数据就直接懵逼了。

    读取设备识别号的寄存器是 0x12 ,读取成功后会返回 0x44 或 0x4D,0x44 表示传感器是 TCS34721/TCS34725, 0x4D 表示传感器是 TCS34723/TCS34727,程序如下所示:

    /**
    * @brief 返回设备类型
    *
    * @return uint8_t 设备识别号 0x44 = TCS34721/TCS34725, 0x4D = TCS34723/TCS34727
    */
    uint8_t get_tcs34725_type(void)
    {
    uint8_t data = 0;
    tcs34725_read8(TCS34725_ID, &data);
    return data;
    }

    判断设备是否正确

    /* 1.获取TCS34725型号 */
    uint8_t tcs34725_type = get_tcs34725_type();
    ESP_LOGI(TAG, "device type is : %x ", tcs34725_type);
    if (!((tcs34725_type == 0x44) && (tcs34725_type == 0x4D)))
    {
    ESP_LOGI(TAG, "Wrong device type!!!!!!!!!");
    return -1;
    }

    设置集成时间

    集成时间的寄存器地址是 0x01,不同的集成时间对应的采集范围和采样时间不同,计算方式是:最大RGBC计数 = (256 - cycles) × 1024,集成时间 ≈ (256 - 255) × 2.4ms,其中 cycles 是写入 0x01 寄存器中的值。

    比如向 0x01 寄存器中写入 0xFF ,则最大RGBC计数 = (256 - 255) × 1024 = 1024,且集成时间 ≈ (256 - 255) × 2.4ms = 2.4ms 也就是说此时采集的颜色值的范围是 0 ~ 1024,需要等待的采样时间为2.4ms,下面是常用的几种设置参数,可以根据自己的需要进行添加

    /* 集成时间配置参数
    * 最大RGBC计数 = (256 - cycles) × 1024
    * 集成时间 ≈ (256 - cycles) × 2.4ms */
    typedef enum
    {
    TCS34725_INTEGRATIONTIME_2_4MS = 0xFF, // 2.4ms - 1 cycles - Max Count: 1024
    TCS34725_INTEGRATIONTIME_24MS = 0xF6, // 24ms - 10 cycles - Max Count: 10240
    TCS34725_INTEGRATIONTIME_50MS = 0xEC, // 50ms - 20 cycles - Max Count: 20480
    TCS34725_INTEGRATIONTIME_101MS = 0xD5, // 101ms - 42 cycles - Max Count: 43008
    TCS34725_INTEGRATIONTIME_154MS = 0xC0, // 154ms - 64 cycles - Max Count: 65535
    TCS34725_INTEGRATIONTIME_700MS = 0x00 // 700ms - 256 cycles - Max Count: 65535
    }
    tcs34725_integration_time_t;

    注意:

      不同的集成时间,需要的采样时间不同,如果读取的时间间隔小于采样时间,那获取的颜色值都是0。
      当设置的 cycles 小于 0xC0 后,需要的采样时间和采集范围不在增加。

    设置程序如下所示:

    /**
    * @brief 设置集成时间
    *
    * @param integration_time 集成时间
    * @return uint8_t 无错误时返回 0
    */
    uint8_t set_tcs34725_integration_time(tcs34725_integration_time_t integration_time)
    {
    _tcs34725_config.integration_time = integration_time;
    return tcs34725_write8(TCS34725_ATIME, integration_time);
    }

    增益倍数

    增益倍数的寄存器地址是 0x0F,我不知道 TCS34725 内部的实现原理是什么,不过可以简单的从字面意思,类似将采集的值直接乘以一个倍数,作用是提高采集的敏感度,可以设置的增益倍数是:1、4、16、60。如下所示:

    /* 增益倍数 */
    typedef enum
    {
    TCS34725_GAIN_1X = 0x00, // 1X增益
    TCS34725_GAIN_4X = 0x01, // 4X增益
    TCS34725_GAIN_16X = 0x02, // 16X增益
    TCS34725_GAIN_60X = 0x03 // 60X增益
    }
    tcs34725_gain_t;

    注意:不论设置的增益倍数是多少,采集的数据不会超过集成时间中设置的最大值

    设置程序如下所示:

    /**
    * @brief 设置增益倍数
    *
    * @param gain 增益倍数
    * @return uint8_t 无错误时返回 0
    */
    uint8_t set_tcs34725_gain(tcs34725_gain_t gain)
    {
    _tcs34725_config.gain = gain;
    return tcs34725_write8(TCS34725_CONTROL, gain);
    }

    启动 TCS34725 设备

    启动设置的寄存器地址是 0x00,启动分中断启动和常规启动,具体的设置值如下所示:

    /* 启动传感器 */
    #define TCS34725_ENABLE_AIEN (0x10) // RGBC中断使能
    #define TCS34725_ENABLE_WEN (0x08) // 等待启用:写1激活等待计时器,写0禁用等待计时器
    #define TCS34725_ENABLE_AEN (0x02) // RGBC启用:写1激活RGBC,写0禁用RGBC
    #define TCS34725_ENABLE_PON (0x01) // 通电:写入1激活内部振荡器,0禁用内部振荡器

    程序如下所示:

    /**
    * @brief 启动 tcs34725 设备
    *
    * @param interrupt_start 是否开启中断启动方式
    * @return uint8_t 无错误时返回 0
    */
    uint8_t tcs34725_start(bool interrupt_start)
    {
    int err = 0;
    if (tcs34725_state)
    {
    return -1;
    } err = tcs34725_write8(TCS34725_ENABLE, TCS34725_ENABLE_PON);
    err = tcs34725_write8(TCS34725_ENABLE, TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN); /* 判断是否开启中断启动 */
    if (interrupt_start)
    {
    _tcs34725_config.interrupt_start = interrupt_start;
    err = tcs34725_write8(TCS34725_ENABLE, TCS34725_ENABLE_AIEN);
    } tcs34725_state = true;
    return err;
    }

    采集数据

    采集数据获取的寄存器是:0x14 ~ 0x1B ,这里需要主要的是数据的高低位是反的,所以采集完成后需要交换一下高低位数据,如下所示:

    esp_err_t esp32_i2c_read16(uint8_t dev_addr, uint8_t reg_addr, uint16_t* read_data)
    {
    #if defined(CODE_SIMPLIFY)
    uint8_t buf[2];
    esp_err_t err = 0;
    err = i2c_write(dev_addr, &reg_addr, 1);
    err = i2c_read(dev_addr, buf, 2);
    *read_data = buf[1] << 8 | buf[0];
    return err;
    #else
    return i2c_read16(dev_addr, reg_addr, read_data);
    #endif
    }

    TCS34725 传感器数据的获取程序如下所示:

    /**
    * @brief 获取RGBC的值
    *
    * @param colour_r 数据存放地址
    * @param colour_g 数据存放地址
    * @param colour_b 数据存放地址
    * @param colour_c 数据存放地址
    * @return uint8_t 无错误时返回 0
    */
    uint8_t get_tcs34725_rgbc(uint16_t *colour_r, uint16_t *colour_g, uint16_t *colour_b, uint16_t *colour_c)
    {
    uint8_t err = 0;
    err = tcs34725_read16(TCS34725_RDATAL, colour_r);
    err = tcs34725_read16(TCS34725_GDATAL, colour_g);
    err = tcs34725_read16(TCS34725_BDATAL, colour_b);
    err = tcs34725_read16(TCS34725_CDATAL, colour_c);
    return err;
    }

    停止 TCS34725 设备

    停止是设置的寄存器和启动是一样的,都是 0x00,程序如下所示:

    /**
    * @brief 停止 tcs34725 设备
    *
    * @return uint8_t 无错误时返回 0
    */
    uint8_t tcs34725_stop(void)
    {
    uint8_t err = 0;
    uint8_t data = 0;
    err = tcs34725_read8(TCS34725_ENABLE, &data);
    err = tcs34725_write8(TCS34725_ENABLE, data & ~(TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN)); /* 通过中断启动后时,需要关闭中断设置 */
    if (_tcs34725_config.interrupt_start)
    {
    err = tcs34725_write8(TCS34725_ENABLE, data & ~TCS34725_ENABLE_AIEN);
    } tcs34725_state = false;
    return err;
    }

    范围计算和白平衡

    这里的范围计算和白平衡调节就多种多样了,可以采集完成后根据自己的需要完成。

    这里提供一个简单的思想,因为颜色的范围是 0 ~ 255,所以首先将自己的设置的范围换算到 0 ~ 25,然后再进行一个简单的白平衡校准即可。

四、TCS34725驱动程序

头文件

/**
* @file tcs34725.h
*
*/ #ifndef _TCS34725_H_
#define _TCS34725_H_ #ifdef __cplusplus
extern "C" {
#endif /*********************
* INCLUDES
*********************/
#include <stdlib.h>
#include <stdbool.h>
/*********************
* DEFINES
*********************/ #define TCS34725_address (0x29) // 设备地址
#define TCS34725_COMMAND_BIT (0x80) // 命令字节 /* TCS34725传感器配置寄存器 */
#define TCS34725_ENABLE (0x00) // 启用传感器
#define TCS34725_ATIME (0x01) // 集成时间
#define TCS34725_WTIME (0x03) // R / W 等待时间
#define TCS34725_AILTL (0x04) // 清除通道下限中断阈值
#define TCS34725_AILTH (0x05)
#define TCS34725_AIHTL (0x06) // 清除通道上限中断阈值
#define TCS34725_AIHTH (0x07) // 配置寄存器
#define TCS34725_PERS (0x0C) // 中断永久性过滤器
#define TCS34725_CONFIG (0x0C) // 中断永久性过滤器
#define TCS34725_CONTROL (0x0F) // 增益倍数
#define TCS34725_ID (0x12) // 设备识别号 0x44 = TCS34721/TCS34725, 0x4D = TCS34723/TCS34727
#define TCS34725_STATUS (0x13) // 设备状态
#define TCS34725_CDATAL (0x14) // 光照强度低字节
#define TCS34725_CDATAH (0x15) // 光照强度高字节
#define TCS34725_RDATAL (0x16) // 红色数据低字节
#define TCS34725_RDATAH (0x17)
#define TCS34725_GDATAL (0x18) // 绿色数据低字节
#define TCS34725_GDATAH (0x19)
#define TCS34725_BDATAL (0x1A) // 蓝色数据低字节
#define TCS34725_BDATAH (0x1B) /* 启动传感器 */
#define TCS34725_ENABLE_AIEN (0x10) // RGBC中断使能
#define TCS34725_ENABLE_WEN (0x08) // 等待启用:写1激活等待计时器,写0禁用等待计时器
#define TCS34725_ENABLE_AEN (0x02) // RGBC启用:写1激活RGBC,写0禁用RGBC
#define TCS34725_ENABLE_PON (0x01) // 通电:写入1激活内部振荡器,0禁用内部振荡器 /**********************
* TYPEDEFS
**********************/ /* 集成时间配置参数
* 最大RGBC计数 = (256 - cycles) × 1024
* 集成时间 ≈ (256 - cycles) × 2.4ms */
typedef enum
{
TCS34725_INTEGRATIONTIME_2_4MS = 0xFF, // 2.4ms - 1 cycles - Max Count: 1024
TCS34725_INTEGRATIONTIME_24MS = 0xF6, // 24ms - 10 cycles - Max Count: 10240
TCS34725_INTEGRATIONTIME_50MS = 0xEC, // 50ms - 20 cycles - Max Count: 20480
TCS34725_INTEGRATIONTIME_101MS = 0xD5, // 101ms - 42 cycles - Max Count: 43008
TCS34725_INTEGRATIONTIME_154MS = 0xC0, // 154ms - 64 cycles - Max Count: 65535
TCS34725_INTEGRATIONTIME_700MS = 0x00 // 700ms - 256 cycles - Max Count: 65535
}
tcs34725_integration_time_t; /* 增益倍数 */
typedef enum
{
TCS34725_GAIN_1X = 0x00, // 1X增益
TCS34725_GAIN_4X = 0x01, // 4X增益
TCS34725_GAIN_16X = 0x02, // 16X增益
TCS34725_GAIN_60X = 0x03 // 60X增益
}
tcs34725_gain_t; /**********************
* GLOBAL PROTOTYPES
**********************/
struct tcs34725_config
{
char name[20];
bool interrupt_start;
tcs34725_integration_time_t integration_time;
tcs34725_gain_t gain;
}tcs34725_config_t; uint8_t tcs34725_start(bool interrupt_start);
uint8_t tcs34725_stop(void);
uint8_t get_tcs34725_type(void);
uint8_t set_tcs34725_integration_time(tcs34725_integration_time_t integration_time);
uint8_t set_tcs34725_gain(tcs34725_gain_t gain);
uint8_t get_tcs34725_rgbc(uint16_t *colour_r, uint16_t *colour_g, uint16_t *colour_b, uint16_t *colour_c);
tcs34725_integration_time_t get_tcs34725_integration_time(void);
tcs34725_gain_t get_tcs34725_gain(void); /**********************
* MACROS
**********************/ #ifdef __cplusplus
} /* extern "C" */
#endif #endif /* _TCS34725_H_ */

程序源码

#include "tcs34725.h"
#include "esp32_i2c_drive.h" /***************************************************************
文件名 : tcs34725.c
作者 : jiaozhu
版本 : V1.0
描述 : tcs34725 传感器驱动文件。
其他 : 无
日志 : 初版 V1.0 2022/12/30
***************************************************************/ /* TCS34725设备启动状态 */
static bool tcs34725_state = false; /* 设备默认配置参数 */
struct tcs34725_config _tcs34725_config =
{
.name = "TCS34725",
.interrupt_start = false,
}; /**
* @brief 通过I2C驱动提供的API进行对接,作用是将一个8位数据写入对应的寄存器中
*
* @param reg_addr 寄存机地址
* @param write_data 需要写入的寄存机数据
* @return uint8_t 无错误时返回 0
*/
static uint8_t tcs34725_write8(uint8_t reg_addr, uint8_t write_data)
{
return esp32_i2c_write8(TCS34725_address, TCS34725_COMMAND_BIT | reg_addr, write_data);
} /**
* @brief 通过I2C驱动提供的API进行对接,作用是读取一个8位的数据
*
* @param reg_addr 寄存器地址
* @param read_data 数据存放地址
* @return uint8_t 无错误时返回 0
*/
static uint8_t tcs34725_read8(uint8_t reg_addr, uint8_t* read_data)
{
return esp32_i2c_read8(TCS34725_address, TCS34725_COMMAND_BIT | reg_addr, read_data);
} /**
* @brief 通过I2C驱动提供的API进行对接,作用是读取一个16位的数据
*
* @param reg_addr 寄存器地址
* @param read_data 数据存放地址
* @return uint8_t 无错误时返回 0
*/
static uint8_t tcs34725_read16(uint8_t reg_addr, uint16_t* read_data)
{
return esp32_i2c_read16(TCS34725_address, TCS34725_COMMAND_BIT | reg_addr, read_data);
} /**
* @brief 启动 tcs34725 设备
*
* @param interrupt_start 是否开启中断启动方式
* @return uint8_t 无错误时返回 0
*/
uint8_t tcs34725_start(bool interrupt_start)
{
int err = 0;
if (tcs34725_state)
{
return -1;
} err = tcs34725_write8(TCS34725_ENABLE, TCS34725_ENABLE_PON);
err = tcs34725_write8(TCS34725_ENABLE, TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN); /* 判断是否开启中断启动 */
if (interrupt_start)
{
_tcs34725_config.interrupt_start = interrupt_start;
err = tcs34725_write8(TCS34725_ENABLE, TCS34725_ENABLE_AIEN);
} tcs34725_state = true;
return err;
} /**
* @brief 停止 tcs34725 设备
*
* @return uint8_t 无错误时返回 0
*/
uint8_t tcs34725_stop(void)
{
uint8_t err = 0;
uint8_t data = 0;
err = tcs34725_read8(TCS34725_ENABLE, &data);
err = tcs34725_write8(TCS34725_ENABLE, data & ~(TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN)); /* 通过中断启动后时,需要关闭中断设置 */
if (_tcs34725_config.interrupt_start)
{
err = tcs34725_write8(TCS34725_ENABLE, data & ~TCS34725_ENABLE_AIEN);
} tcs34725_state = false;
return err;
} /**
* @brief 返回设备类型
*
* @return uint8_t 设备识别号 0x44 = TCS34721/TCS34725, 0x4D = TCS34723/TCS34727
*/
uint8_t get_tcs34725_type(void)
{
uint8_t data = 0;
tcs34725_read8(TCS34725_ID, &data);
return data;
} /**
* @brief 设置集成时间
*
* @param integration_time 集成时间
* @return uint8_t 无错误时返回 0
*/
uint8_t set_tcs34725_integration_time(tcs34725_integration_time_t integration_time)
{
_tcs34725_config.integration_time = integration_time;
return tcs34725_write8(TCS34725_ATIME, integration_time);
} /**
* @brief 获取设置的集成时间
*
* @return tcs34725_integration_time_t
*/
tcs34725_integration_time_t get_tcs34725_integration_time(void)
{
return _tcs34725_config.integration_time;
} /**
* @brief 设置增益倍数
*
* @param gain 增益倍数
* @return uint8_t 无错误时返回 0
*/
uint8_t set_tcs34725_gain(tcs34725_gain_t gain)
{
_tcs34725_config.gain = gain;
return tcs34725_write8(TCS34725_CONTROL, gain);
} /**
* @brief 获取设置的增益倍数
*
* @return tcs34725_gain_t
*/
tcs34725_gain_t get_tcs34725_gain(void)
{
return _tcs34725_config.gain;
} /**
* @brief 获取RGBC的值
*
* @param colour_r 数据存放地址
* @param colour_g 数据存放地址
* @param colour_b 数据存放地址
* @param colour_c 数据存放地址
* @return uint8_t 无错误时返回 0
*/
uint8_t get_tcs34725_rgbc(uint16_t *colour_r, uint16_t *colour_g, uint16_t *colour_b, uint16_t *colour_c)
{
uint8_t err = 0;
err = tcs34725_read16(TCS34725_RDATAL, colour_r);
err = tcs34725_read16(TCS34725_GDATAL, colour_g);
err = tcs34725_read16(TCS34725_BDATAL, colour_b);
err = tcs34725_read16(TCS34725_CDATAL, colour_c);
return err;
}

注意:到此笔记也结束了,上面程序还有不完善的地方,只能自行更改了。当然上面的程序也是可以直接只用的,但是程序只供学习使用,有什么为题概不负责。最后有写得不好的地方,望各位大佬多多指教。

TCS34725 颜色传感器设备驱动程序的相关教程结束。

《TCS34725 颜色传感器设备驱动程序.doc》

下载本文的Word格式文档,以方便收藏与打印。