STM32外设系列—BH1750

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 本文详细介绍了BH1750的特点,原理图,IIC通信协议。给出了BH1750程序设计,能够实时获取周围环境光照强度。最后,给出了两种拓展应用,并说明了实现思路。


🎀 文章作者:二土电子
🐸 期待大家一起学习交流!


一、BH1750简介

BH1750是一款数字型光照强度传感器,能够获取周围环境的光照强度。其测量范围在0~65535 lx。lx勒克斯,是光照强度的单位。BH1750可用于调节手机屏幕和键盘的背光功率,或者用于智能灯光控制,比如,随着外界光照强度的变化调节灯光亮度。

bf109be0de135cf55d9d112b497e65aa_20ecce2f72c741ec8e4274f7bf381f10.png

BH1750有以下特点

  • I2C总线接口
  • 接近视觉灵敏度的光谱灵敏度特性
  • 输出对应亮度的数字值
  • 高分辨率(0~65535 lx)
  • 通过降低功率功能,实现低电流化
  • 50Hz / 60Hz光噪声抑制功能
  • 可以选择两种类型的I2C从属地址
  • 最小误差变动在±20%
  • 受红外线影响很小

二、BH1750原理图

bb2a29435a6ff5c89995d82ff2158837_80522080f04146fe86eab32bf5223a05.png

  • 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的数据手册中给出了一些指令

fea5bb1247272b070986c7696e68520e_d2447c50b2e94904961257147cb7db7b.png

其中的H分辨率模式和L分辨率模式等,是BH1750的测量模式,数据手册中也给出了说明

db0184289e4023c2880bf4b932536b65_6911d470a0554d27a27e23e1e0e8cf21.png

我们通常使用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数据手册中给出了一种配置连续高分辨率模式的方法

23e0285c2604ac30bcc7f56d1656cf06_7aefa76889e5425eba1f4efac6d376b4.png

BH1750数据读取格式如下

cb4784eed16148143521cc13c9edd4f2_9556c78c60064ccd8dd46eeae717a7c6.png

光照强度(单位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做颜色是别时,在不同光照强度下,同一种颜色的颜色阈值不同,可以根据不同的光照强度,匹配不同的颜色阈值。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
6月前
|
传感器 芯片 内存技术
STM32F103标准外设库——认识STM32(一)
STM32F103标准外设库——认识STM32(一)
185 0
STM32F103标准外设库——认识STM32(一)
|
数据格式
STM32外设系列—红外遥控
本文详细介绍了红外通信的应用,原理。介绍了一种常用的二进制脉冲码形式。最后,给出了红外遥控的实现思路和程序设计。
382 2
STM32外设系列—红外遥控
|
5月前
|
开发者
【经典案例】使用HAL库配置STM32F407的SPI外设
在嵌入式系统开发中,STM32F407是一款广泛应用的微控制器,而SPI(Serial Peripheral Interface)是一种常用的通信接口。本文将详细介绍如何使用STM32的硬件抽象层(HAL)库配置STM32F407的SPI外设,并提供完整的代码示例。
553 1
|
传感器 芯片
STM32外设系列—HC-SR04(超声波)
本文主要介绍了超声波测距的原理,常用的超声波传感器。并且针对HC-SR04给出了使用思路和程序设计。最后,简单进行了思路拓展。
356 1
STM32外设系列—HC-SR04(超声波)
|
芯片
STM32外设系列—sg90(舵机)
本文介绍了什么是舵机,舵机的控制原理。以sg90为例,介绍了180°舵机的控制方法,给出了详细的程序设计。最后,介绍了360°舵机的控制方法。
1947 1
STM32外设系列—sg90(舵机)
|
6月前
|
传感器 存储 缓存
STM32--MPU6050与I2C外设
STM32--MPU6050与I2C外设
151 1
|
6月前
stm32f4外设学习篇(代码集合)(三)
stm32f4外设学习篇(代码集合)
120 0
|
6月前
stm32f4外设学习篇(代码集合)(二)
stm32f4外设学习篇(代码集合)
|
6月前
|
芯片
stm32f4外设学习篇(代码集合)(一)
stm32f4外设学习篇(代码集合)
144 0
|
传感器 数据格式
STM32外设系列—DHT11
本文详细介绍了什么是DHT11,介绍了DHT11使用的单总线通信,并详细分析了与DHT11进行数据交互时的时序图。此外,给出了DHT11初始化,接收温湿度信息并校验的程序设计。
330 0
STM32外设系列—DHT11