第十二篇、CubeMX | 可见光颜色传感器 TCS3472

简介


TCS3472 是 ams OSRAM 的低成本、RGB+Clear 四通道颜色 / 环境光传感器,带红外滤光、16 位 ADC、I2C、3.3V 供电,主打精准颜色识别、色温、亮度(Lux),比 AS7341 便宜、通道少、更适合入门与量产。

引脚说明


引脚号 标识 管脚描述
1 VCC 3.3V/5V电源正
2 GND 电源地
3 SDA I2C数据线
4 SCL I2C时钟线
5 INT 中断输出引脚
6 LED 通用输入/输出

HAL库模拟IIC驱动参考

c 复制代码
#define u32 uint32_t
#define u8  uint8_t
#define u16 uint16_t

/******************************************************************************/
// TCS34725 寄存器地址定义
#define TCS34725_ADDRESS          (0x29)
#define TCS34725_COMMAND_BIT      (0x80)

#define TCS34725_ENABLE           (0x00)
#define TCS34725_ENABLE_AIEN      (0x10)    /* RGBC 中断使能 */
#define TCS34725_ENABLE_WEN       (0x08)    /* 等待定时器使能 */
#define TCS34725_ENABLE_AEN       (0x02)    /* ADC 使能 */
#define TCS34725_ENABLE_PON       (0x01)    /* 电源使能 */
#define TCS34725_ATIME            (0x01)    /* 积分时间寄存器 */
#define TCS34725_WTIME            (0x03)    /* 等待时间寄存器 */
#define TCS34725_AILTL            (0x04)    /* 清除通道中断下限(低8位) */
#define TCS34725_AILTH            (0x05)    /* 清除通道中断下限(高8位) */
#define TCS34725_AIHTL            (0x06)    /* 清除通道中断上限(低8位) */
#define TCS34725_AIHTH            (0x07)    /* 清除通道中断上限(高8位) */
#define TCS34725_PERS             (0x0C)    /* 中断持续寄存器 */
#define TCS34725_CONFIG           (0x0D)    /* 配置寄存器 */
#define TCS34725_CONTROL          (0x0F)    /* 增益控制寄存器 */
#define TCS34725_ID               (0x12)    /* 设备ID寄存器(0x44=34725/34721,0x4D=34727/34723) */
#define TCS34725_STATUS           (0x13)    /* 状态寄存器 */
#define TCS34725_STATUS_AINT      (0x10)    /* RGBC 中断标志 */
#define TCS34725_STATUS_AVALID    (0x01)    /* RGBC 数据有效标志 */
#define TCS34725_CDATAL           (0x14)    /* 清除通道数据(低8位) */
#define TCS34725_CDATAH           (0x15)    /* 清除通道数据(高8位) */
#define TCS34725_RDATAL           (0x16)    /* 红色通道数据(低8位) */
#define TCS34725_RDATAH           (0x17)    /* 红色通道数据(高8位) */
#define TCS34725_GDATAL           (0x18)    /* 绿色通道数据(低8位) */
#define TCS34725_GDATAH           (0x19)    /* 绿色通道数据(高8位) */
#define TCS34725_BDATAL           (0x1A)    /* 蓝色通道数据(低8位) */
#define TCS34725_BDATAH           (0x1B)    /* 蓝色通道数据(高8位) */

// 积分时间配置
#define TCS34725_INTEGRATIONTIME_2_4MS   0xFF   /* 2.4ms - 1周期 - 最大计数1024 */
#define TCS34725_INTEGRATIONTIME_24MS    0xF6   /* 24ms - 10周期 - 最大计数10240 */
#define TCS34725_INTEGRATIONTIME_50MS    0xEB   /* 50ms - 20周期 - 最大计数20480 */
#define TCS34725_INTEGRATIONTIME_101MS   0xD5   /* 101ms - 42周期 - 最大计数43008 */
#define TCS34725_INTEGRATIONTIME_154MS   0xC0   /* 154ms - 64周期 - 最大计数65535 */
#define TCS34725_INTEGRATIONTIME_240MS   0x9C   /* 240ms - 100周期 - 最大计数65535 */
#define TCS34725_INTEGRATIONTIME_700MS   0x00   /* 700ms - 256周期 - 最大计数65535 */

// 增益配置
#define TCS34725_GAIN_1X                 0x00   /* 无增益 */
#define TCS34725_GAIN_4X                 0x01   /* 4倍增益 */
#define TCS34725_GAIN_16X                0x02   /* 16倍增益 */
#define TCS34725_GAIN_60X                0x03   /* 60倍增益 */
/******************************************************************************/
// 延时函数声明(需在其他文件实现,如基于SysTick的延时)
extern void delay_ms(u32 nms);
extern void delay_us(u32 nus);

#define TCS_I2C_SCL_PORT    GPIOA
#define TCS_I2C_SCL_PIN     GPIO_PIN_9
#define TCS_I2C_SDA_PORT    GPIOA
#define TCS_I2C_SDA_PIN     GPIO_PIN_8

#define TCS_SCL_H           HAL_GPIO_WritePin(TCS_I2C_SCL_PORT, TCS_I2C_SCL_PIN, GPIO_PIN_SET)        /* SCL置高 */
#define TCS_SCL_L           HAL_GPIO_WritePin(TCS_I2C_SCL_PORT, TCS_I2C_SCL_PIN, GPIO_PIN_RESET)      /* SCL置低 */
#define TCS_SDA_H           HAL_GPIO_WritePin(TCS_I2C_SDA_PORT, TCS_I2C_SDA_PIN, GPIO_PIN_SET)        /* SDA置高 */
#define TCS_SDA_L           HAL_GPIO_WritePin(TCS_I2C_SDA_PORT, TCS_I2C_SDA_PIN, GPIO_PIN_RESET)      /* SDA置低 */

/******************************************************************************/
// 颜色数据结构体
typedef struct{
    u16 c;      // 清除通道数据 [0-65535]
    u16 r;      // 红色通道数据
    u16 g;      // 绿色通道数据
    u16 b;      // 蓝色通道数据
}COLOR_RGBC;

typedef struct{
    u16 h;       // 色相 [0-360]
    u8  s;       // 饱和度 [0-100]
    u8  l;       // 亮度 [0-100]
}COLOR_HSL;


COLOR_RGBC rgb;
COLOR_HSL  hsl;
/******************************************************************************/
// 辅助函数:三值最大/最小值计算(保持不变)
#define max3v(v1, v2, v3)   ((v1)<(v2)? ((v2)<(v3)?(v3):(v2)):((v1)<(v3)?(v3):(v1)))
#define min3v(v1, v2, v3)   ((v1)>(v2)? ((v2)>(v3)?(v3):(v2)):((v1)>(v3)?(v3):(v1)))
/******************************************************************************/


// 防止编译器优化延时函数
__STATIC_INLINE void delay_us(volatile uint32_t us)
{
    // 72MHz时钟下,每个指令周期约为1/72μs(≈13.89ns)
    // 以下循环体约执行3条指令,因此每个循环耗时约41.67ns
    // 计算循环次数:us * (1μs / 41.67ns) ≈ us * 24
    volatile uint32_t cycles = us * 240;
    
    // 使用汇编指令确保循环不被优化,且精确控制执行时间
   while(cycles--)
   {
   }
}



// I2C初始化(基于STM32 HAL库函数配置GPIO和时钟)
void TCS34725_I2C_Init()
{
    // 1. 使能GPIO时钟(根据实际使用的端口修改,此处以GPIOA为例)
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    // 2. 定义GPIO初始化结构体
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 3. 配置SCL引脚(推挽输出,高速)
    GPIO_InitStruct.Pin = TCS_I2C_SCL_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(TCS_I2C_SCL_PORT, &GPIO_InitStruct);
    
    // 4. 配置SDA引脚(初始设为推挽输出,后续通过SD1_SetInput/OUT切换)
    GPIO_InitStruct.Pin = TCS_I2C_SDA_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(TCS_I2C_SDA_PORT, &GPIO_InitStruct);
    
    // 5. 初始电平:SCL、SDA均置高(I2C空闲状态)
    TCS_SCL_H;
    TCS_SDA_H;
    HAL_Delay(1);  // 稳定电平
}
/*********************************************/
// I2C起始信号(逻辑不变,基于库函数引脚操作)
void TCS34725_I2C_Start()
{
    SD1_SetOutput();  // SDA设为输出
    TCS_SDA_H;
    TCS_SCL_H;
    delay_us(4);    // 延时≥4.7μs(I2C标准时序)
    TCS_SDA_L;
    delay_us(4);    // 延时≥4μs
    TCS_SCL_L;      // 拉低SCL,准备发送数据
}
/*********************************************/
// I2C停止信号(逻辑不变)
void TCS34725_I2C_Stop()
{
    SD1_SetOutput();  // SDA设为输出
    TCS_SCL_L;
    TCS_SDA_L;
    delay_us(4);
    TCS_SCL_H;
    TCS_SDA_H;
    delay_us(4);    // 释放总线
}
/*********************************************/
// I2C等待ACK(逻辑不变)
u8 TCS34725_I2C_Wait_ACK()
{
    u32 timeout = 0;
    SD1_SetInput();   // SDA设为输入(等待从机ACK)
    TCS_SDA_H; 
    delay_us(1);
    TCS_SCL_H; 
    delay_us(1);
    
    // 等待SDA拉低(ACK),超时则返回失败
    while(I2C_SDA_READ())
    {
        timeout++;
        if(timeout > 250)  // 超时阈值可根据实际需求调整
        {
            TCS34725_I2C_Stop();
            return 1;  // ACK失败
        }
    }
    TCS_SCL_L;  // 拉低SCL,结束ACK检测
    return 0;    // ACK成功
}
/*********************************************/
// I2C发送ACK(逻辑不变)
void TCS34725_I2C_ACK()
{
    TCS_SCL_L;
    SD1_SetOutput();
    TCS_SDA_L;
    delay_us(2);
    TCS_SCL_H;
    delay_us(2);
    TCS_SCL_L;
}
/*********************************************/
// I2C发送NACK(逻辑不变)
void TCS34725_I2C_NACK()
{
    TCS_SCL_L;
    SD1_SetOutput();
    TCS_SDA_H;
    delay_us(2);
    TCS_SCL_H;
    delay_us(2);
    TCS_SCL_L;
}
/*********************************************/
// I2C发送1字节(逻辑不变)
void TCS34725_I2C_Send_Byte(u8 byte)
{
    u8 i;
    SD1_SetOutput();  // SDA设为输出
    TCS_SCL_L;      // 拉低时钟,开始数据传输
    
    // 高位先行发送
    for(i = 0; i < 8; i++)
    {
        if(((byte & 0x80) >> 7) == 1) TCS_SDA_H;
        else TCS_SDA_L;
        
        byte <<= 1;
        delay_us(2);
        TCS_SCL_H;  // 高电平期间从机读取数据
        delay_us(2);
        TCS_SCL_L;  // 拉低时钟,准备下一位
        delay_us(2);
    } 
}
/*********************************************/
// I2C读取1字节(逻辑不变)
u8 TCS34725_I2C_Read_Byte(u8 ack)
{
    u8 i, receive = 0;
    SD1_SetInput();   // SDA设为输入
    
    // 高位先行读取
    for(i = 0; i < 8; i++)
    {
        TCS_SCL_L;
        delay_us(2);
        TCS_SCL_H;          // 高电平期间主机读取数据
        receive <<= 1;
        if(I2C_SDA_READ()) receive++;  // 读取当前bit
        delay_us(1);
    }
    
    // 发送ACK/NACK
    if (!ack) TCS34725_I2C_NACK();  // 最后1字节发送NACK
    else TCS34725_I2C_ACK();        // 非最后1字节发送ACK
    
    return receive;
}
/*********************************************/
// I2C写数据到从机(逻辑不变)
void TCS34725_I2C_Write(u8 slaveAddress, u8* dataBuffer, u8 bytesNumber, u8 stopBit)
{
    u8 i = 0;
    TCS34725_I2C_Start();
    // 发送从机地址+写命令(最低位0)
    TCS34725_I2C_Send_Byte((slaveAddress << 1) | 0x00);   
    if(TCS34725_I2C_Wait_ACK() != 0) return;  // 等待ACK,失败则退出
    
    // 发送数据
    for(i = 0; i < bytesNumber; i++)
    {
        TCS34725_I2C_Send_Byte(*(dataBuffer + i));
        if(TCS34725_I2C_Wait_ACK() != 0) return;
    }
    
    if(stopBit == 1) TCS34725_I2C_Stop();  // 按需发送停止信号
}
/*********************************************/
// I2C从从机读数据(逻辑不变)
void TCS34725_I2C_Read(u8 slaveAddress, u8* dataBuffer, u8 bytesNumber, u8 stopBit)
{
    u8 i = 0;
    TCS34725_I2C_Start();
    // 发送从机地址+读命令(最低位1)
    TCS34725_I2C_Send_Byte((slaveAddress << 1) | 0x01);   
    if(TCS34725_I2C_Wait_ACK() != 0) return;  // 等待ACK,失败则退出
    
    // 读取数据
    for(i = 0; i < bytesNumber; i++)
    {
        if(i == bytesNumber - 1)
        {
            // 最后1字节:读后发NACK
            *(dataBuffer + i) = TCS34725_I2C_Read_Byte(0);
        }
        else
        {
            // 非最后1字节:读后发ACK
            *(dataBuffer + i) = TCS34725_I2C_Read_Byte(1);
        }
    }
    
    if(stopBit == 1) TCS34725_I2C_Stop();  // 按需发送停止信号
}
/*********************************************/
/*******************************************************************************
 * @brief Writes data into TCS34725 registers, starting from the selected
 *        register address pointer.
 *
 * @param subAddr - The selected register address pointer.
 * @param dataBuffer - Pointer to a buffer storing the transmission data.
 * @param bytesNumber - Number of bytes that will be sent.
 *
 * @return None.
*******************************************************************************/
void TCS34725_Write(u8 subAddr, u8* dataBuffer, u8 bytesNumber)
{
    u8 sendBuffer[10] = {0, };
    u8 byte = 0;
    
    sendBuffer[0] = subAddr | TCS34725_COMMAND_BIT;
    for(byte = 1; byte <= bytesNumber; byte++)
    {
        sendBuffer[byte] = dataBuffer[byte - 1];
    }
  TCS34725_I2C_Write(TCS34725_ADDRESS, sendBuffer, bytesNumber + 1, 1);
}
/*******************************************************************************
 * @brief Reads data from TCS34725 registers, starting from the selected
 *        register address pointer.
 *
 * @param subAddr - The selected register address pointer.
 * @param dataBuffer - Pointer to a buffer that will store the received data.
 * @param bytesNumber - Number of bytes that will be read.
 *
 * @return None.
*******************************************************************************/
void TCS34725_Read(u8 subAddr, u8* dataBuffer, u8 bytesNumber)
{
  subAddr |= TCS34725_COMMAND_BIT;
  
  TCS34725_I2C_Write(TCS34725_ADDRESS, (u8*)&subAddr, 1, 0);
  TCS34725_I2C_Read(TCS34725_ADDRESS, dataBuffer, bytesNumber, 1);
}
/*******************************************************************************
 * @brief TCS34725设置积分时间
 *
 * @return None
*******************************************************************************/
void TCS34725_SetIntegrationTime(u8 time)
{
  TCS34725_Write(TCS34725_ATIME, &time, 1);
}
/*******************************************************************************
 * @brief TCS34725设置增益
 *
 * @return None
*******************************************************************************/
void TCS34725_SetGain(u8 gain)
{
  TCS34725_Write(TCS34725_CONTROL, &gain, 1);
}
/*******************************************************************************
 * @brief TCS34725使能
 *
 * @return None
*******************************************************************************/
void TCS34725_Enable(void)
{
  u8 cmd = TCS34725_ENABLE_PON;
  
  TCS34725_Write(TCS34725_ENABLE, &cmd, 1);
  cmd = TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN;
  TCS34725_Write(TCS34725_ENABLE, &cmd, 1);
  //delay_s(600000);//delay_ms(3);//延时应该放在设置AEN之后
}
/*******************************************************************************
 * @brief TCS34725失能
 *
 * @return None
*******************************************************************************/
void TCS34725_Disable(void)
{
  u8 cmd = 0;
  
  TCS34725_Read(TCS34725_ENABLE, &cmd, 1);
  cmd = cmd & ~(TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN);
  TCS34725_Write(TCS34725_ENABLE, &cmd, 1);
}
/*******************************************************************************
 * @brief TCS34725初始化
 *
 * @return ID - ID寄存器中的值
*******************************************************************************/
u8 TCS34725_Init(void)
{
  u8 id=0;
  
  TCS34725_I2C_Init(); 
  TCS34725_Read(TCS34725_ID, &id, 1);  //TCS34725 的 ID 是 0x44 可以根据这个来判断是否成功连接,0x4D是TCS34727;
  if(id==0x4D | id==0x44)
    {
      TCS34725_SetIntegrationTime(TCS34725_INTEGRATIONTIME_24MS);
      TCS34725_SetGain(TCS34725_GAIN_4X);
      TCS34725_Enable();
      return 1;
    }
  return 0;
}
/*******************************************************************************
 * @brief TCS34725获取单个通道数据
 *
 * @return data - 该通道的转换值
*******************************************************************************/
u16 TCS34725_GetChannelData(u8 reg)
{
  u8 tmp[2] = {0,0};
  u16 data;
  
  TCS34725_Read(reg, tmp, 2);
  data = (tmp[1] << 8) | tmp[0];
  
  return data;
}
/*******************************************************************************
 * @brief TCS34725获取各个通道数据
 *
 * @return 1 - 转换完成,数据可用
 *        0 - 转换未完成,数据不可用
*******************************************************************************/
#define TCS34725_MAX_RAW_VALUE 65535
#define GAIN 0
u8 TCS34725_GetRawData(COLOR_RGBC *rgbc)
{
  u8 status = TCS34725_STATUS_AVALID;
  u32 total = 0;
  TCS34725_Read(TCS34725_STATUS, &status, 1);
  
  if(status & TCS34725_STATUS_AVALID)
  {
    rgbc->c = TCS34725_GetChannelData(TCS34725_CDATAL)>>GAIN;  
    rgbc->r = TCS34725_GetChannelData(TCS34725_RDATAL)>>GAIN;   
    rgbc->g = TCS34725_GetChannelData(TCS34725_GDATAL)>>GAIN;   
    rgbc->b = TCS34725_GetChannelData(TCS34725_BDATAL)>>GAIN;
    total = rgbc->r + rgbc->g + rgbc->b;
    if(rgbc->c > 765 || total >765 || rgbc->r > 255 || rgbc->g > 255 || rgbc->b > 255)
    {
      rgbc->r = rgbc->r * 255 / total;
      rgbc->g = rgbc->g * 255 / total;
      rgbc->b = rgbc->b * 255 / total;
      rgbc->c = rgbc->c * 255 / total;
    }
    return 1;
  }
  return 0;
}
/******************************************************************************/
//RGB转HSL
void RGBtoHSL(COLOR_RGBC *Rgb, COLOR_HSL *Hsl)
{
  u8 maxVal,minVal,difVal;
  u8 r = Rgb->r*100/Rgb->c;   //[0-100]
  u8 g = Rgb->g*100/Rgb->c;
  u8 b = Rgb->b*100/Rgb->c;
  
  maxVal = max3v(r,g,b);
  minVal = min3v(r,g,b);
  difVal = maxVal-minVal;
  
  //计算亮度
  Hsl->l = (maxVal+minVal)/2;   //[0-100]
  
  if(maxVal == minVal)//若r=g=b,灰度
  {
    Hsl->h = 0; 
    Hsl->s = 0;
  }
  else
  {
    //计算色调
    if(maxVal==r)
    {
      if(g>=b)
        Hsl->h = 60*(g-b)/difVal;
      else
        Hsl->h = 60*(g-b)/difVal+360;
    }
    else
      {
        if(maxVal==g)Hsl->h = 60*(b-r)/difVal+120;
        else
          if(maxVal==b)Hsl->h = 60*(r-g)/difVal+240;
      }
    
    //计算饱和度
    if(Hsl->l<=50)Hsl->s=difVal*100/(maxVal+minVal);  //[0-100]
    else
      Hsl->s=difVal*100/(200-(maxVal+minVal));
  }
}
/******************************************************************************/
相关推荐
Chat_zhanggong3451 小时前
主推22AP10作用有哪些?
嵌入式硬件
Chat_zhanggong34510 小时前
主推RK3567J作用有哪些?
人工智能·嵌入式硬件
Ww.xh10 小时前
STM32与ESP8266AT指令超时重传方案
stm32·单片机·嵌入式硬件
LCG元10 小时前
STM32实战:基于STM32F103的智能共享充电宝管理系统
stm32·单片机·嵌入式硬件
点灯师11 小时前
基于单片机的智能家居智能雨水自动关窗控制系统设计
单片机·嵌入式硬件·毕业设计·智能家居·课程设计·期末大作业
Smart-佀11 小时前
涨薪秘技:智能家居中的BLE协议与实现
网络·arm开发·嵌入式硬件·microsoft
freeinlife'12 小时前
onenet云平台下发数据到单片机并且OLED屏显示
单片机·嵌入式硬件
硅农深芯14 小时前
为什么有的芯片电源pin叫VCC,有的叫VDD?
单片机·嵌入式硬件·vcc·vdd·vee·vss
d111111111d14 小时前
STM32-UART封装问题解析
笔记·stm32·单片机·嵌入式硬件·学习·算法