最近到手了一个罗姆系列的颜色传感器 ,叫做bh1745nuc,它可以通过运用特有的红外线去除技术和运算方式实现业界最高的红外线去除特性,将红外线的影响降低至以往产品的1/10以下。以往彩色传感器,即便是受到红外线影响无法正确感应的昏暗(透射率低)的光学窗口,也能精确检测出照度、色温。
一、什么是RGB
从它官方的datasheet上了解到,它输出数据为16bit的RGBC,什么是RGBC呢?通俗点说,R代表red也就是红色,同理,G是green,B是blue,C是clear。我们可以通过RGB数据计算得出对应的色温和照度,由于我技术有限,没能找到有关照度计算的资料,所以就用亮度来替代照度,还望有大神看完后可以不吝提供一下方案。
二、色温
色温简称CCT,所谓色温就是我们常说的冷色、暖色等等,色温的单位是K(开尔文)。那怎么通过RGB数据计算出色温呢?通过查找资料了解到,计算色温的步骤是:
1.计算出三个刺激值X,Y,Z;
2.通过三个刺激值计算出色坐标(x,y);
3.通过色坐标与色温的换算公式计算出色温。
首先,计算三个刺激值X,Y,Z。通过查找资料,刺激值与RGB转换公式为:
其次,计算色坐标(x,y),色坐标计算公式为:
x = X / (X + Y + Z)
y = Y / (X + Y + Z)
最后,计算色温,计算公式为:
n = (x - 0.3320) / (0.1858 - y)
CCT = 437 * n * n * n + 3601 * n * n + 6831 * n + 5517,CCT就是色温了。
附上代码:
/*
* @func Calculate_Color_Temperature
* @author taoshi
* @version V1.0.0
* @date 2018/9/18
*/
static double Calculate_Color_Temperature(uint16_t R, uint16_t G, uint16_t B)
{
static double M[3][3] = { {2.789, 1.7517, 1.1302},
{1, 4.5907, 0.0601},
{0, 0.0565, 5.5943} };
double X = 0, Y = 0, Z = 0; //三刺激值
double x = 0, y = 0; //色坐标
double CCT = 0; //色温
double n = 0;
X = M[0][0] * R + M[0][1] * G + M[0][2] * B;
Y = M[1][0] * R + M[1][1] * G + M[1][2] * B;
Z = M[2][0] * R + M[2][1] * G + M[2][2] * B;
x = X / (X + Y + Z);
y = Y / (X + Y + Z);
n = (x - 0.3320) / (0.1858 - y);
CCT = 437 * n * n * n + 3601 * n * n + 6831 * n + 5517;
return CCT;
}
色温的结果出来后,可以通过简单的比较来确定是否属于冷色、中间色、暖色其中之一,后面会有代码。
三、亮度
亮度比较简单,直接附上代码:
/*
* @func Calculate_Brightness
* @author taoshi
* @version V1.0.0
* @date 2018/9/18
*/
static uint16_t Calculate_Brightness(uint16_t R, uint16_t G, uint16_t B)
{
return (uint16_t)((0.299 * R) + (0.587 * G) + (0.114 * B));
}
最后是接口函数,调用上面两个函数:(我的程序里这一部分是属于color_analyses.c,对外提供一个接口)
先是结构体定义
//色温等级结构
typedef enum {
warm = 0, //暖色
middle, //中间色
cold //冷色
} Temperature_Rank;
//环境值结构
typedef struct {
double CCT; //色温
uint16_t brightness; //亮度
Temperature_Rank t_rank; //色温等级
} Enviroment_Typedef;
/*
* @func Color_Analyses
* @author taoshi
* @version V1.0.0
* @date 2018/9/18
*/
void Color_Analyses(Enviroment_Typedef *enviroment, const Color_DataTypedef *color)
{
enviroment->CCT = Calculate_Color_Temperature(color->red_data, color->green_data, color->blue_data);
//大于5000是冷色,3300~5000是中间色,小于3300是暖色
if (enviroment->CCT > 5000) enviroment->t_rank = cold;
else if (enviroment->CCT > 3300) enviroment->t_rank = middle;
else enviroment->t_rank = warm;
enviroment->brightness = Calculate_Brightness(color->red_data, color->green_data, color->blue_data);
}
四、关于bh1745
根据官方给的datasheet,写了它的.h和.c
bh1745.h:
/*
* @file bh1745.h
* @author taoshi
* @version V1.0.0
* @date 2018/9/7
*/
#ifndef __BH1745_H
#define __BH1745_H
#include "stm32f10x.h"
#include "stdbool.h"
//---------------------------------------------------------------------------------------------------
#define BH1745_ADDRESS ((uint8_t)0x38) //ADDR = 'L' ;ADDR = 'H' is 0x71h
#define BH1745_WRITE ((BH1745_ADDRESS << 1) | 0x00)
#define BH1745_READ ((BH1745_ADDRESS << 1) | 0x01)
#define SYSTEM_CONTROL_ID ((uint8_t)0x0b)
//---------------------------------------------------------------------------------------------------
/*
system control register
*/
#define SYSTEM_CONTROL ((uint8_t)0x40)
//---------------------------------------------------------------------------------------------------
/*
function setting registers
*/
#define MODE_CONTROL_BASE ((uint8_t)0x41)
#define MODE_CONTROL_ONE ((uint8_t)(MODE_CONTROL_BASE + 0x00))
#define MODE_CONTROL_TWO ((uint8_t)(MODE_CONTROL_BASE + 0x01))
#define MODE_CONTROL_THREE ((uint8_t)(MODE_CONTROL_BASE + 0x02))
//---------------------------------------------------------------------------------------------------
/*
color data registers
*/
#define COLOR_DATA_BASE ((uint8_t)0x50)
#define RED_DATA_LSBs ((uint8_t)(COLOR_DATA_BASE + 0x00))
#define RED_DATA_MSBs ((uint8_t)(COLOR_DATA_BASE + 0x01))
#define GREEN_DATA_LSBs ((uint8_t)(COLOR_DATA_BASE + 0x02))
#define GREEN_DATA_MSBs ((uint8_t)(COLOR_DATA_BASE + 0x03))
#define BLUE_DATA_LSBs ((uint8_t)(COLOR_DATA_BASE + 0x04))
#define BLUE_DATA_MSBs ((uint8_t)(COLOR_DATA_BASE + 0x05))
#define CLEAR_DATA_LSBs ((uint8_t)(COLOR_DATA_BASE + 0x06))
#define CLEAR_DATA_MSBs ((uint8_t)(COLOR_DATA_BASE + 0x07))
//---------------------------------------------------------------------------------------------------
/*
internal data registers
*/
#define INTERNAL_DATA_BASE ((uint8_t)0x58)
#define DINT_DATA_LSBs ((uint8_t)(INTERNAL_DATA_BASE + 0x00))
#define DINT_DATA_MSBs ((uint8_t)(INTERNAL_DATA_BASE + 0x01))
//---------------------------------------------------------------------------------------------------
/*
Interrupt setting register
*/
#define INTERRUPT ((uint8_t)0x60)
//---------------------------------------------------------------------------------------------------
/*
Persistence setting register
*/
#define PERSISTENCE ((uint8_t)0x61)
//---------------------------------------------------------------------------------------------------
/*
threshold data registers
*/
#define THRESHOLD_DATA_BASE ((uint8_t)0x62)
#define TH_LSBs ((uint8_t)(THRESHOLD_DATA_BASE + 0x00))
#define TH_MSBs ((uint8_t)(THRESHOLD_DATA_BASE + 0x01))
#define TL_LSBs ((uint8_t)(THRESHOLD_DATA_BASE + 0x02))
#define TL_MSBs ((uint8_t)(THRESHOLD_DATA_BASE + 0x03))
//---------------------------------------------------------------------------------------------------
/*
Manufacturer ID register
*/
#define MANUFACTURER_ID ((uint8_t)0x92)
//---------------------------------------------------------------------------------------------------
//颜色数据结构体
typedef struct {
uint16_t red_data;
uint16_t green_data;
uint16_t blue_data;
uint16_t clear_data;
} Color_DataTypedef;
bool BH1745_Init(void);
void Get_Color_Data(Color_DataTypedef *);
#endif
bh1745.c
/*
* @file bh1745.c
* @author taoshi
* @version V1.0.0
* @date 2018/9/18
*/
#include "bh1745.h"
#include "soft_i2c.h"
/*
* @func Get_Red_Data
* @author taoshi
* @version V1.0.0
* @date 2018/9/18
*/
static uint16_t Get_Red_Data(void)
{
uint8_t H = 0, L = 0;
uint16_t data = 0;
I2C_Read_Reg(BH1745_READ, RED_DATA_MSBs, &H);
data |= (uint16_t)H;
I2C_Read_Reg(BH1745_READ, RED_DATA_LSBs, &L);
data = (data << 8) | (uint16_t)L;
return data;
}
/*
* @func Get_Green_Data
* @author taoshi
* @version V1.0.0
* @date 2018/9/18
*/
static uint16_t Get_Green_Data(void)
{
uint8_t H = 0, L = 0;
uint16_t data = 0;
I2C_Read_Reg(BH1745_READ, GREEN_DATA_MSBs, &H);
data |= (uint16_t)H;
I2C_Read_Reg(BH1745_READ, GREEN_DATA_LSBs, &L);
data = (data << 8) | (uint16_t)L;
return data;
}
/*
* @func Get_Blue_Data
* @author taoshi
* @version V1.0.0
* @date 2018/9/18
*/
static uint16_t Get_Blue_Data(void)
{
uint8_t H = 0, L = 0;
uint16_t data = 0;
I2C_Read_Reg(BH1745_READ, BLUE_DATA_MSBs, &H);
data |= (uint16_t)H;
I2C_Read_Reg(BH1745_READ, BLUE_DATA_LSBs, &L);
data = (data << 8) | (uint16_t)L;
return data;
}
/*
* @func Get_Clear_Data
* @author taoshi
* @version V1.0.0
* @date 2018/9/18
*/
static uint16_t Get_Clear_Data(void)
{
uint8_t H = 0, L = 0;
uint16_t data = 0;
I2C_Read_Reg(BH1745_READ, CLEAR_DATA_MSBs, &H);
data |= (uint16_t)H;
I2C_Read_Reg(BH1745_READ, CLEAR_DATA_LSBs, &L);
data = (data << 8) | (uint16_t)L;
return data;
}
/*
* @func Get_Color_Data
* @author taoshi
* @version V1.0.0
* @date 2018/9/18
*/
void Get_Color_Data(Color_DataTypedef *color)
{
color->red_data = Get_Red_Data();
color->green_data = Get_Green_Data();
color->blue_data = Get_Blue_Data();
color->clear_data = Get_Clear_Data();
}
/*
* @func BH1745_Init
* @author taoshi
* @version V1.0.0
* @date 2018/9/18
*/
bool BH1745_Init(void)
{
uint8_t error_times = 0; //错误次数
uint8_t data = 0;
//确认芯片id正确
if (I2C_Read_Reg(BH1745_READ, MANUFACTURER_ID, &data) == false) return false;
while (data != BH1745_ADDRESS << 2) //0xe0,是BH1745_ADDRESS 左移两位
{
error_times++;
if (error_times > 5) return false;
if (I2C_Read_Reg(BH1745_READ, MANUFACTURER_ID, &data) == false) return false;
}
error_times = 0;
//确认系统控制寄存器id正确
if (I2C_Read_Reg(BH1745_READ, SYSTEM_CONTROL, &data) == false) return false;
while ((data & 0x3f) != SYSTEM_CONTROL_ID)
{
error_times++;
if (error_times > 5) return false;
if (I2C_Read_Reg(BH1745_READ, SYSTEM_CONTROL, &data) == false) return false;
}
//开启复位
if (I2C_Write_Reg(BH1745_WRITE, SYSTEM_CONTROL, 0x8b) == false) return false;
//数据更新周期为160ms,这是最快了,还有320ms等
if (I2C_Write_Reg(BH1745_WRITE, MODE_CONTROL_ONE, 0x00) == false) return false;
//设置AD为16位精度,并使能bh1745
if (I2C_Write_Reg(BH1745_WRITE, MODE_CONTROL_TWO, 0x12) == false) return false;
return true;
}
其中要注意的是id寄存器中的值是addr左移两位,再者就是数据更新周期的设定和ad精度的设定不能忘,其它也没什么了,按照官方 datasheet写就行了。其实采集函数还不全,只写了RGBC采集,后序可以逐步添加要采集的数据函数,这些本质是一样的。
五、基于stm32平台上的模拟i2c
模拟i2c就直接附上代码吧。。
soft_i2c.h:
/*
* @file soft_i2c.h
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
#ifndef __SOFT_I2C_H
#define __SOFT_I2C_H
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stdio.h"
#include "stdbool.h"
#include "sys.h"
//---------------------------------------------------------------------------------------------------
//i2c引脚设置,我这里用的是C0和C2
#define I2C_GPIO GPIOC
#define I2C_CLOCK_ON RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE)
#define I2C_SDA_PIN 0
#define I2C_SCL_PIN 2
#define GPIO_I2C_SDA_PIN GPIO_Pin_0
#define GPIO_I2C_SCL_PIN GPIO_Pin_2
#define SDA_PIN_0_7 1 //SDA引脚范围(0~7或8~15)
//---------------------------------------------------------------------------------------------------
#define SOFT_I2C_DELAY_LEVEL 10 //模拟i2c延时等级
//---------------------------------------------------------------------------------------------------
#if SDA_PIN_0_7 //SDA引脚在0~7内,所以用CLR寄存器
#define I2C_SDA_OUT() { I2C_GPIO->CRL &= ~((uint32_t)0xf << 4 * I2C_SDA_PIN);\
I2C_GPIO->CRL |= ((uint32_t)0x3 << 4 * I2C_SDA_PIN); } //推挽输出,速度50MHZ
#define I2C_SDA_IN() { I2C_GPIO->CRL &= ~((uint32_t)0xf << 4 * I2C_SDA_PIN);\
I2C_GPIO->CRL |= ((uint32_t)0x4 << 4 * I2C_SDA_PIN); } //浮空输入
#else //SDA引脚在8~15内,所以CRH寄存器
#define I2C_SDA_OUT() { I2C_GPIO->CRH &= ~((uint32_t)0xf << 4 * (I2C_SDA_PIN - 8));\
I2C_GPIO->CRH |= ((uint32_t)0x3 << 4 * (I2C_SDA_PIN - 8)); } //推挽输出,速度50MHZ
#define I2C_SDA_IN() { I2C_GPIO->CRL &= ~((uint32_t)0xf << 4 * (I2C_SDA_PIN - 8));\
I2C_GPIO->CRL |= ((uint32_t)0x4 << 4 * (I2C_SDA_PIN - 8)); } //浮空输入
#endif
//---------------------------------------------------------------------------------------------------
//更换GPIO时一定不能忘了改这里
#define I2C_SDA_H (PCout(I2C_SDA_PIN) = 1)
#define I2C_SDA_L (PCout(I2C_SDA_PIN) = 0)
#define I2C_SCL_H (PCout(I2C_SCL_PIN) = 1)
#define I2C_SCL_L (PCout(I2C_SCL_PIN) = 0)
#define I2C_SDA_IN_DATA PCin(I2C_SDA_PIN)
//---------------------------------------------------------------------------------------------------
void Soft_I2C_Init(void);
bool I2C_Read_Buff(uint8_t addr_with_read, uint8_t reg_addr, uint8_t *buff, size_t size);
bool I2C_Read_Reg(uint8_t addr_with_read, uint8_t reg_addr, uint8_t *data);
bool I2C_Write_Reg(uint8_t addr_with_write, uint8_t reg_addr, uint8_t data);
#endif
soft_i2c.c
/*
* @file soft_i2c.h
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
#include "soft_i2c.h"
/*
* @func I2C_Delay
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
static void I2C_Delay(uint8_t t)
{
uint8_t i = 0;
while (t--)
{
i = SOFT_I2C_DELAY_LEVEL;
while (i--);
}
}
/*
* @func I2C_Start
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
static void I2C_Start(void)
{
I2C_SDA_OUT();
I2C_SDA_H;
I2C_SCL_H;
I2C_Delay(2);
I2C_SDA_L;
I2C_Delay(2);
I2C_SCL_L;
I2C_Delay(2);
}
/*
* @func I2C_Stop
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
static void I2C_Stop(void)
{
I2C_SDA_OUT();
I2C_SCL_L;
I2C_SDA_L;
I2C_Delay(2);
I2C_SCL_H;
I2C_Delay(2);
I2C_SDA_H;
I2C_Delay(2);
}
/*
* @func I2C_Wait_Ack
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
static bool I2C_Wait_Ack(void)
{
uint8_t err_time = 0;
I2C_SCL_L;
I2C_Delay(1);
I2C_SDA_H;
I2C_Delay(1);
I2C_SDA_IN();
I2C_Delay(1);
I2C_SCL_H;
I2C_Delay(1);
while (I2C_SDA_IN_DATA)
{
err_time++;
if (err_time > 250)
return true;
}
I2C_SCL_L;
I2C_Delay(1);
return false;
}
/*
* @func I2C_Ack
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
static void I2C_Ack(void)
{
I2C_SCL_L;
I2C_Delay(1);
I2C_SDA_OUT();
I2C_SDA_L;
I2C_Delay(1);
I2C_SCL_H;
I2C_Delay(1);
I2C_SCL_L;
I2C_Delay(1);
}
/*
* @func I2C_NAck
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
static void I2C_NAck(void)
{
I2C_SCL_L;
I2C_Delay(1);
I2C_SDA_OUT();
I2C_SDA_H;
I2C_Delay(1);
I2C_SCL_H;
I2C_Delay(1);
I2C_SCL_L;
I2C_Delay(1);
}
/*
* @func I2C_Send_Byte
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
static void I2C_Send_Byte(uint8_t data)
{
uint8_t i;
I2C_SDA_OUT();
I2C_SCL_L;
for (i = 0; i < 8; i++)
{
if ((data & (uint8_t)0x80) >> 7)
I2C_SDA_H;
else
I2C_SDA_L;
data <<= 1;
I2C_Delay(1);
I2C_SCL_H;
I2C_Delay(1);
I2C_SCL_L;
I2C_Delay(1);
}
}
/*
* @func I2C_Read_Byte
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
static uint8_t I2C_Read_Byte(void)
{
uint8_t i, reveive_byte = 0;
I2C_SDA_IN();
for (i = 0; i < 8; i++)
{
I2C_SCL_L;
I2C_Delay(1);
I2C_SCL_H;
I2C_Delay(1);
reveive_byte <<= 1;
if (I2C_SDA_IN_DATA)
reveive_byte |= (uint8_t)0x01;
}
return reveive_byte;
}
/*
* @func I2C_Write_Reg
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
bool I2C_Write_Reg(uint8_t addr_with_write, uint8_t reg_addr, uint8_t data)
{
I2C_Start();
I2C_Send_Byte(addr_with_write);
if (I2C_Wait_Ack()) return false;
I2C_Send_Byte(reg_addr);
if (I2C_Wait_Ack()) return false;
I2C_Send_Byte(data);
if (I2C_Wait_Ack()) return false;
I2C_Stop();
return true;
}
/*
* @func I2C_Read_Reg
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
bool I2C_Read_Reg(uint8_t addr_with_read, uint8_t reg_addr, uint8_t *data)
{
I2C_Start();
I2C_Send_Byte(addr_with_read & (uint8_t)0xfe);
if (I2C_Wait_Ack()) return false;
I2C_Send_Byte(reg_addr);
if (I2C_Wait_Ack()) return false;
I2C_Start();
I2C_Send_Byte(addr_with_read);
if (I2C_Wait_Ack()) return false;
*data = I2C_Read_Byte();
I2C_NAck();
I2C_Stop();
return true;
}
/*
* @func I2C_Read_Buff
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
bool I2C_Read_Buff(uint8_t addr_with_read, uint8_t reg_addr, uint8_t *buff, size_t size)
{
uint8_t i;
I2C_Start();
I2C_Send_Byte(addr_with_read & (uint8_t)0xfe);
if (I2C_Wait_Ack()) return false;
I2C_Send_Byte(reg_addr);
if (I2C_Wait_Ack()) return false;
I2C_Start();
I2C_Send_Byte(addr_with_read);
if (I2C_Wait_Ack()) return false;
for (i = 0; i < size; i++)
{
*buff++ = I2C_Read_Byte();
if (i < size - 1)
I2C_Ack();
}
I2C_NAck();
I2C_Stop();
return true;
}
/*
* @func Soft_I2C_Init
* @author taoshi
* @version V1.0.0
* @date 2018/9/16
*/
void Soft_I2C_Init(void)
{
GPIO_InitTypeDef GPIO_Init_Structure;
I2C_CLOCK_ON;
GPIO_Init_Structure.GPIO_Pin = GPIO_I2C_SDA_PIN | GPIO_I2C_SCL_PIN;
GPIO_Init_Structure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init_Structure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(I2C_GPIO, &GPIO_Init_Structure);
GPIO_SetBits(I2C_GPIO, GPIO_I2C_SDA_PIN | GPIO_I2C_SCL_PIN);
}