🎀 文章作者:二土电子
🐸 期待大家一起学习交流!
一、BH1750简介
BH1750是一款数字型光照强度传感器,能够获取周围环境的光照强度。其测量范围在0~65535 lx。lx勒克斯,是光照强度的单位。
BH1750可用于调节手机屏幕和键盘的背光功率,或者用于智能灯光控制,比如,随着外界光照强度的变化调节灯光亮度。
BH1750有以下特点
- I2C总线接口
- 接近视觉灵敏度的光谱灵敏度特性
- 输出对应亮度的数字值
- 高分辨率(0~65535 lx)
- 通过降低功率功能,实现低电流化
- 50Hz / 60Hz光噪声抑制功能
- 可以选择两种类型的I2C从属地址
- 最小误差变动在±20%
- 受红外线影响很小
二、BH1750原理图
- PD —— 接近人眼反应的光敏二极管
- AMP —— 集成运算放大器(将 PD 电流转换为 PD 电压)
- ADC —— 模数转换获取 16 位数字数据
- Logic + IC Interface(逻辑+ IC 界面)
- OSC —— 内部振荡器(该时钟为内部逻辑时钟,时钟频率典型值:320kHz)
PD二极管通过光伏效应将输入光信号转换成电信号,经运放电路放大后,电压经ADC采集,再经逻辑电路转换成16位二进制数,存储在内部的寄存器中(进入光窗的光越强,光电流越大,电压就越大,所以光强可以通过电压的大小判断,但是应该注意的是,虽然电压和光强一一对应,但它们不是成正比关系,所以该芯片内部是对数据进行了线性处理,这就是为什么直接使用集成IC而不是光电二极管的原因)。BH1750引出了时钟线和数据线,单片机可以通过I2C协议与BH1750进行通讯,可选择BH1750的工作模式,提取BH1750寄存器中的照度数据。
三、BH1750数据手册
3.1 指令集
BH1750的数据手册中给出了一些指令
其中的H分辨率模式和L分辨率模式等,是BH1750的测量模式,数据手册中也给出了说明
我们通常使用H分辨率模式,H 分辨率模式下足够长的测量时间(积分时间)能够抑制一些噪声(包括 50Hz/60Hz)。同时,H 分辨率模式的分辨率在 1lx 下,适用于黑暗场合下(少于 10 lx)。
3.2 IIC通信读/写
上面介绍,BH1750有两种从属地址,由 ADDR 端口的电平决定。
- ADDR=“H”( ADDR ≧ 0.7VCC ) →“1011100”
- ADDR=“L”( ADDR ≦ 0.3VCC ) →“0100011”
关于IIC通信的详细内容,这里就不再介绍了,可以去本系列的OLED篇查看。BH1750数据手册中给出了一种配置连续高分辨率模式的方法
BH1750数据读取格式如下
光照强度(单位lx)=(High Byte + Low Byte)/ 1.2 * 测量精度
四、BH1750程序设计
4.1 IIC程序
相比于之前的OLED的IIC程序,增加了主设备发送应答信号(Ack),非应答信号(NAck),读取一个字节数据程序。修改了等待应答信号程序,增加了返回值。
/*
*==============================================================================
*函数名称:IIC_Delay
*函数功能:IIC延时
*输入参数:无
*返回值:无
*备 注:数据手册提供
*==============================================================================
*/
void IIC_Delay (void)
{
u8 t = 10;
while (t--);
}
/*
*==============================================================================
*函数名称:I2C_Start
*函数功能:IIC起始信号
*输入参数:无
*返回值:无
*备 注:数据手册提供
*==============================================================================
*/
void I2C_Start (void)
{
BH1750_SDA_Set();
BH1750_SCL_Set();
IIC_Delay();
BH1750_SDA_Clr();
IIC_Delay();
BH1750_SCL_Clr();
IIC_Delay();
}
/*
*==============================================================================
*函数名称:I2C_Stop
*函数功能:IIC终止信号
*输入参数:无
*返回值:无
*备 注:数据手册提供
*==============================================================================
*/
void I2C_Stop (void)
{
BH1750_SDA_Clr();
BH1750_SCL_Set();
IIC_Delay();
BH1750_SDA_Set();
}
/*
*==============================================================================
*函数名称:IIC_Ack
*函数功能:CPU产生一个ACK信号
*输入参数:无
*返回值:无
*备 注:无
*==============================================================================
*/
void IIC_Ack (void)
{
BH1750_SDA_Clr();
IIC_Delay();
BH1750_SCL_Set();
IIC_Delay();
BH1750_SCL_Clr();
IIC_Delay();
BH1750_SDA_Set();
}
/*
*==============================================================================
*函数名称:IIC_NAck
*函数功能:CPU产生一个NACK信号
*输入参数:无
*返回值:无
*备 注:无
*==============================================================================
*/
void IIC_NAck (void)
{
BH1750_SDA_Set();
IIC_Delay();
BH1750_SCL_Set();
IIC_Delay();
BH1750_SCL_Clr();
IIC_Delay();
}
/*
*==============================================================================
*函数名称:I2C_WaitAck
*函数功能:IIC等待应答
*输入参数:无
*返回值:0:未收到应答信号;1:收到应答信号
*备 注:无
*==============================================================================
*/
u8 I2C_WaitAck (void)
{
u8 re;
BH1750_SDA_Set();
IIC_Delay();
BH1750_SCL_Set();
IIC_Delay();
if (BH1750_SDA_DATA())
{
re = 1;
}
else
{
re = 0;
}
BH1750_SCL_Clr();
IIC_Delay();
return re;
}
/*
*==============================================================================
*函数名称:Send_Byte
*函数功能:写入一个字节
*输入参数:dat:需要写入的数据
*返回值:无
*备 注:数据手册提供
*==============================================================================
*/
void Send_Byte (u8 dat)
{
u8 i;
for (i = 0;i < 8;i ++)
{
// 发送数据时,从高位依次写入
if (dat & 0x80)
{
BH1750_SDA_Set();
}
else
{
BH1750_SDA_Clr();
}
IIC_Delay();
BH1750_SCL_Set();
IIC_Delay();
BH1750_SCL_Clr();
// dat左移1位
dat <<= 1;
}
}
/*
*==============================================================================
*函数名称:IIC_Read_Byte
*函数功能:IIC读取一个字节数据
*输入参数:无
*返回值:无
*备 注:读取到的一个字节数据
*==============================================================================
*/
u8 IIC_Read_Byte (void)
{
u8 i;
u8 value;
// 高位在前
value = 0;
// 循环读取8bit数据
for (i = 0; i < 8; i ++)
{
value <<= 1; // 循环左移一位
BH1750_SCL_Set();
IIC_Delay();
// 如果是“1”
if (BH1750_SDA_DATA())
{
value ++;
}
BH1750_SCL_Clr();
IIC_Delay();
}
return value;
}
//BH1750写一个字节
//返回值 成功:0 失败:非0
/*
*==============================================================================
*函数名称:BH1750_Byte_Write
*函数功能:BH1750写一个字节
*输入参数:data:要写入的数据
*返回值:0:写入成功;1/2:写入失败
*备 注:无
*==============================================================================
*/
u8 BH1750_Byte_Write (u8 data)
{
I2C_Start();
// 发送从设备地址,0:写
Send_Byte (BH1750_Addr | 0);
// 收到应答信号
if(I2C_WaitAck() == 1)
{
return 1;
}
//发送控制命令
Send_Byte(data);
// 收到应答信号
if(I2C_WaitAck() == 1)
{
return 2;
}
I2C_Stop();
return 0;
}
4.2 BH1750初始化程序
初始化包括两部分,一部分是初始化IIC引脚,另一部分是初始化BH1750。也就是给BH1750上电,并复位。
/*
*==============================================================================
*函数名称:Drv_Bh1750_Init
*函数功能:初始化BH1750
*输入参数:无
*返回值:无
*备 注:无
*==============================================================================
*/
void Drv_Bh1750_Init (void)
{
// 结构体定义
GPIO_InitTypeDef GPIO_InitStructure;
// 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 初始化GPIO结构体
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽式输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 全部拉高,IIC处于空闲状态
GPIO_SetBits(GPIOB,GPIO_Pin_6 | GPIO_Pin_7);
// BH1750上电
BH1750_Byte_Write(POWER_ON);
// 复位BH1750
BH1750_Byte_Write(MODULE_RESET);
}
4.3 读取BH1750测量结果
BH1750返回两字节的测量结果数据
/*
*==============================================================================
*函数名称:Drv_Bh1750_Read_Measure
*函数功能:读取BH1750测量数据
*输入参数:无
*返回值:0:读取失败;其他:光照强度
*备 注:无
*==============================================================================
*/
u16 Drv_Bh1750_Read_Measure (void)
{
u16 receData = 0;
I2C_Start();
// 发送从设备地址,1:读
Send_Byte(BH1750_Addr | 1);
if(I2C_WaitAck() == 1)
{
return 0;
}
// 读取高八位
receData = IIC_Read_Byte();
IIC_Ack();
// 读取低八位
receData = (receData << 8) + IIC_Read_Byte();
IIC_NAck();
I2C_Stop();
return receData; // 返回读取到的数据
}
4.4 获取光照强度
/*
*==============================================================================
*函数名称:Med_Bh1750_GetLightIntensity
*函数功能:获取光照强度
*输入参数:无
*返回值:光照强度
*备 注:分辨率 光照强度(单位lx)=(High Byte + Low Byte)/ 1.2 * 测量精度
*==============================================================================
*/
float Med_Bh1750_GetLightIntensity (void)
{
return (float)(Drv_Bh1750_Read_Measure() / 1.1f * Resolurtion); //返回测量光照强度
}
4.5 相关宏定义
// BH1750的地址(ADDR=“H”)
#define BH1750_Addr 0x46
// BH1750指令
#define POWER_OFF 0x00
#define POWER_ON 0x01
#define MODULE_RESET 0x07
#define CONTINUE_H_MODE 0x10
#define CONTINUE_H_MODE2 0x11
#define CONTINUE_L_MODE 0x13
#define ONE_TIME_H_MODE 0x20
#define ONE_TIME_H_MODE2 0x21
#define ONE_TIME_L_MODE 0x23
//测量模式
#define Measure_Mode CONTINUE_H_MODE
//分辨率 光照强度(单位lx)=(High Byte + Low Byte)/ 1.2 * 测量精度
#if ((Measure_Mode == CONTINUE_H_MODE2)|(Measure_Mode == ONE_TIME_H_MODE2))
#define Resolurtion 0.5
#elif ((Measure_Mode == CONTINUE_H_MODE)|(Measure_Mode == ONE_TIME_H_MODE))
#define Resolurtion 1
#elif ((Measure_Mode == CONTINUE_L_MODE)|(Measure_Mode == ONE_TIME_L_MODE))
#define Resolurtion 4
#endif
// SCL
#define BH1750_SCL_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_6)
#define BH1750_SCL_Set() GPIO_SetBits(GPIOB,GPIO_Pin_6)
// SDA
#define BH1750_SDA_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_7)
#define BH1750_SDA_Set() GPIO_SetBits(GPIOB,GPIO_Pin_7)
// 读取SDA电平
#define BH1750_SDA_DATA() GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)
五、应用实例
使用串口打印光照强度,main函数如下
float gLingtIntensity = 0;
int main(void)
{
Med_Mcu_Iint(); // 系统初始化
while(1)
{
gLingtIntensity = Med_Bh1750_GetLightIntensity(); // 获取光照强度
printf ("Light:%.1f lx",gLingtIntensity); // 串口打印光照强度
delay_ms(500); //延时500ms = 0.5s
}
}
六、拓展应用
利用BH1750获取到的周围环境光强可用于许多方面,这里举几个例子,比如设计一个教室灯光控制系统,根据实际环境光强来调节灯光亮度,使室内环境光强保持在一个稳定的值。另外,比如做颜色识别时,周围环境的光照强度不同,识别的效果也不同。可以利用BH1750实时监测周围环境光照强度变化,不同的光照强度下,切换不同的颜色阈值,可以改善颜色识别的效果。这里简单介绍一下实现思路。
6.1 实时调节LED亮度
可以用PWM来控制LED的亮度。根据周围环境的光照强度的变化,实时调节PWM的占空比,达到LED亮度根据周围环境光照强度变化而变化的效果。但是需要注意光照强度与占空比的换算关系。
6.2 实时调整颜色阈值
比如使用Open MV做颜色是别时,在不同光照强度下,同一种颜色的颜色阈值不同,可以根据不同的光照强度,匹配不同的颜色阈值。