以前的传感器是用过中断的方式进行计数的,现在已经有 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, ®_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;
}
注意:到此笔记也结束了,上面程序还有不完善的地方,只能自行更改了。当然上面的程序也是可以直接只用的,但是程序只供学习使用,有什么为题概不负责。最后有写得不好的地方,望各位大佬多多指教。