
传感器驱动系列之BH1750光照强度传感器
目录
一、BH1750光照强度传感器简介
BH1750传感器模块是一种数字光强传感器,用于测量光照强度。它基于BH1750芯片,具有高精度和快速响应的特点。其模块实物图如下图1所示:

BH1750传感器模块采用数字输出接口,可以通过I2C总线与微控制器或单片机进行通信。它能够测量0到65535勒克斯(Lux)范围内的光照强度,最小误差变动为±20%,并将结果以数字形式输出。传感器模块内部集成了光敏元件的放大电路和ADC转换电路,能够快速、准确地将光照强度转换为数字信号输出。
BH1750传感器模块广泛应用于室内和室外光照强度监测、自动光照调节、照明系统控制等领域。它可以帮助实现能源节约和环境保护的目标。
二、工作原理
2.1 模块寄存器地址
首先我们来看数据手册中的寄存器地址说明,见下图2所示。

其中上述寄存器对应编写的源码内容如下:
/* 工作命令 */
typedef enum {
BH1750_CMD_POWER_DOWN = 0x00, /* 关闭模块 */
BH1750_CMD_POWER_ON = 0x01, /* 打开模块等待测量指令 */
BH1750_CMD_RESET = 0x07, /* 重置数据寄存器值在PowerOn模式下有效 */
} ENUM_BH1750_CMD_TYPEDEF;
/* 工作模式选择 */
typedef enum {
/* 连续测量模式 */
BH1750_MODE_HR1 = 0x10, /* 高分辨率模式1 单位 1 lx 测量时间120ms */
BH1750_MODE_HR2 = 0x11, /* 高分辨率模式2 单位 0.5 lx 测量时间120ms */
BH1750_MODE_LR = 0x13, /* 低分辨率 单位 4 lx 测量时间16ms */
/* 单次测量模式 测量后模块自动转到PowerDown模式 */
BH1750_SINGLE_MODE_HR1 = 0x20,/* 一次高分辨率测量 */
BH1750_SINGLE_MODE_HR2 = 0x21,/* 一次高分辨率测量 */
BH1750_SINGLE_MODE_LR = 0x23,/* 低分辨率测量 */
} ENUM_BH1750_MODE_TYPEDEF;
2.2 IIC器件地址说明
数据手册中提到,当模块的ADDR引脚接地或悬空时器件地址为0100011(0x23),接电源正极时器件地址为1011100(0x5C)。见下图3示。

2.3 数据采集过程
由数据手册中数据测量流程图(下图4)可知,光强数据的获取主要分为上电、测量等过程。

接着我们以 ADDR位 置低电平(器件地址0x23)且工作在连续测量模式下的时序图5为例进行说明,整个采集周期的实现。

根据上图可以知道,数据的读取分为三步:
- 1. 向器件写入读取模式的指令
首先需要发送IIC开始信号,然后发送设备地址+写命令(0x23<<1 | 0 = 0x46),等待从设备进行响应,然后配置模式为连续高精度读取模式1(即指令0x10),同时等待从设备响应,最后发送IIC停止信号;
- 2. 设置完成后需延时等待
设置高精度采集模式后第一次测量需要等待最大180ms;
3. 读取器件采集到的光强数据
发送IIC起始信号,然后发送设备地址+读命令(0x23<<1 | 1 = 0x47),等待从设备响应,然后就可以读取光强数据了,这里要提醒一下数据的读取采用的是16 bit的数据(即先读高8位数据,再读低8 位数据)。
在读取数据的过程中,高八位数据读取完成后需要等待从设备响应,而低8 位数据读取完成后不需要从设备进行响应,这里也是需要注意的地方,最后发送IIC停止信号完成一次数据的读取过程。
注意:在获取到16 bit数据后,我们还需要对数据值除以1.2才是正确的光强值;同时当设置的指令模式在连续高分辨率采集过程中需要延时120ms才可以进行下一次数据采集;而在低分辨率模式下需要延时16ms。
三、STM32驱动源码
bh1750.c源码实现如下:
#include "bh1750.h"
#define BH1750_ADDR(x) GPIO_WriteBit(BH1750_ADDR_GPIO_PORT, BH1750_ADDR_GPIO_PIN, (BitAction)x)
#define BH1750_SCL(x) GPIO_WriteBit(BH1750_SCL_GPIO_PORT, BH1750_SCL_GPIO_PIN, (BitAction)x)
#define BH1750_SDA(x) GPIO_WriteBit(BH1750_SDA_GPIO_PORT, BH1750_SDA_GPIO_PIN, (BitAction)x)
#define BH1750_READ_ADDR GPIO_ReadOutputDataBit(BH1750_ADDR_GPIO_PORT, BH1750_ADDR_GPIO_PIN)
#define BH1750_READ_SDA GPIO_ReadInputDataBit(BH1750_SDA_GPIO_PORT, BH1750_SDA_GPIO_PIN)
static STRUCT_BH1750_TYPEDEF bh1750;
/**
* @brief 软件延时函数 us级
* @param 无
* @retval 无
* @note 软件延时不是很精确 不过能用
*/
static void bh1750_iic_soft_delay(void)
{
uint8_t i = 3, j = 0;
while(j --) {
while(i --);
}
}
/**
* @brief 光照强度传感器 器件地址 IO初始化
* @param 无
* @retval 无
* @note: 当ADDR位置0或悬空时 器件地址为 0x46
* ((0100011 << 1) | 0) - 写数据; 0x47 == ((0100011 << 1) | 1) - 读数据
* 当ADDR位置1时 器件地址为 0xB8
* ((1011100 << 1) | 0) - 写数据; 0xB9 == ((1011100 << 1) | 0) - 读数据
*/
void bh1750_addr_config(void)
{
RCC_APB2PeriphClockCmd(BH1750_ADDR_RCC_CLK, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Pin = BH1750_ADDR_GPIO_PIN;
GPIO_Init(BH1750_ADDR_GPIO_PORT, &GPIO_InitStructure);
BH1750_ADDR(0);
}
/**
* @brief 初始化IIC
* @param 无
* @retval 无
*/
static void bh1750_iic_config(void)
{
RCC_APB2PeriphClockCmd(BH1750_SDA_RCC_CLK | BH1750_SCL_RCC_CLK, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = BH1750_SDA_GPIO_PIN;
GPIO_Init(BH1750_SDA_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = BH1750_SCL_GPIO_PIN;
GPIO_Init(BH1750_SCL_GPIO_PORT, &GPIO_InitStructure);
BH1750_SCL(1); BH1750_SDA(1);
}
/**
* @brief 配置SDA线 为输出/输入模式
* @param out - 1:输出模式 0:输入模式
* @retval 无
*/
static void bh1750_sda_inout(uint8_t out)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(BH1750_SDA_RCC_CLK, ENABLE);
if(out == 1) {
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
}
else {
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
}
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = BH1750_SDA_GPIO_PIN;
GPIO_Init(BH1750_SDA_GPIO_PORT, &GPIO_InitStructure);
}
/**
* @brief 产生IIC起始信号
* @param 无
* @retval 无
*/
static void bh1750_iic_start(void)
{
bh1750_sda_inout(1);/* 配置sda线为输出模式 */
BH1750_SDA(1);
BH1750_SCL(1);
bh1750_iic_soft_delay();
BH1750_SDA(0); /* 开始信号 CLK高电平时,SDA从高变低 */
bh1750_iic_soft_delay();
BH1750_SCL(0); /* 钳住I2C总线,准备发送或接收数据 */
}
/**
* @brief 产生IIC停止信号
* @param 无
* @retval 无
*/
static void bh1750_iic_stop(void)
{
bh1750_sda_inout(1);/* 配置sda线为输出模式 */
BH1750_SCL(0);
BH1750_SDA(0); /* 停止信号 CLK高电平时,SDA从低变高 */
bh1750_iic_soft_delay();
BH1750_SCL(1);
BH1750_SDA(1); /* 发送I2C总线结束信号 */
bh1750_iic_soft_delay();
}
/**
* @brief 等待应答信号到来
* @param 无
* @retval 1-接收应答失败;0-接收应答成功
*/
static uint8_t bh1750_iic_waitAck(void)
{
uint8_t ucErrTime = 0;
bh1750_sda_inout(0);
BH1750_SDA(1);
bh1750_iic_soft_delay();
BH1750_SCL(1);
bh1750_iic_soft_delay();
while(BH1750_READ_SDA) {
ucErrTime ++;
if(ucErrTime > 250) {
bh1750_iic_stop();
return 1;
}
}
BH1750_SCL(0);
return 0;
}
/**
* @brief 产生ACK应答
* @param 无
* @retval 无
*/
static void bh1750_iic_ack(void)
{
BH1750_SCL(0);
bh1750_sda_inout(1);
BH1750_SDA(0);
bh1750_iic_soft_delay();
BH1750_SCL(1);
bh1750_iic_soft_delay();
BH1750_SCL(0);
}
/**
* @brief 不产生ACK应答
* @param 无
* @retval 无
*/
static void bh1750_iic_nAck(void)
{
BH1750_SCL(0);
bh1750_sda_inout(1);
BH1750_SDA(1);
bh1750_iic_soft_delay();
BH1750_SCL(1);
bh1750_iic_soft_delay();
BH1750_SCL(0);
}
/**
* @brief 发送一个字节
* @param txd
* @retval 1-有应答; 0-无应答
*/
static void bh1750_iic_sendByte(uint8_t txd)
{
uint8_t tx_flag;
bh1750_sda_inout(1);
BH1750_SCL(0);
for(uint8_t i=0; i<8; i++) {
tx_flag = (txd&0x80) >> 7;
tx_flag ? BH1750_SDA(1) : BH1750_SDA(0);
txd <<= 1;
bh1750_iic_soft_delay();
BH1750_SCL(1);
bh1750_iic_soft_delay();
BH1750_SCL(0);
bh1750_iic_soft_delay();
}
}
/**
* @brief 读1个字节
* @param ack (ack=1 发送ACK; ack=0 发送nACK)
* @retval receive-接收到的数据
*/
static uint8_t bh1750_iic_readByte(uint8_t ack)
{
uint8_t receive = 0;
bh1750_sda_inout(0);
for(uint8_t i=0; i<8; i++ ) {
BH1750_SCL(0);
bh1750_iic_soft_delay();
BH1750_SCL(1);
receive <<= 1;
if(BH1750_READ_SDA) receive ++;
bh1750_iic_soft_delay();
}
if (!ack) bh1750_iic_nAck();
else bh1750_iic_ack();
return receive;
}
/**
* @brief 向模块写指令
* @param cmd-需要发送的指令
* @retval 无
*/
static void bh1750_sendCmd(uint8_t cmd)
{
bh1750_iic_start(); /* IIC起始信号 */
bh1750_iic_sendByte(bh1750.addr<<1 | 0);/* 发送器件地址+写指令 */
bh1750_iic_waitAck(); /* 等待从机应答 */
bh1750_iic_sendByte(cmd); /* 发送指令 */
bh1750_iic_waitAck(); /* 等待从机应答 */
bh1750_iic_stop(); /* IIC停止信号 */
}
/**
* @brief 读光照强度原始数据
* @param 无
* @retval 读出的光照数据
*/
static uint16_t bh1750_readData(void)
{
uint8_t data_h, data_l;
bh1750_iic_start(); /* IIC起始信号 */
bh1750_iic_sendByte(bh1750.addr<<1 | 1);/* 发送器件地址+读指令 */
bh1750_iic_waitAck(); /* 等待从机应答 */
data_h = bh1750_iic_readByte(1); /* 读取并保存高八位数据 */
data_l = bh1750_iic_readByte(0); /* 读取并保存低八位数据 */
bh1750_iic_stop(); /* 发送停止信号 */
return (data_h<<8 | data_l);
}
/**
* @brief 配置结构体初始化
* @param 无
* @retval 无
*/
static void bh1750_struct_config(void)
{
bh1750.mode = BH1750_MODE_HR1;
bh1750.state = BH1750_STATUS_IDLE;
bh1750.wait_time = 180;
bh1750.wait_next = 120;
bh1750.light_data = 0.f;
if(BH1750_READ_ADDR) {
bh1750.addr = 0x5C;
}
else {
bh1750.addr = 0x23;
}
}
/**
* @brief BH1750 初始化
* @param 无
* @retval 无
*/
void bh1750_init(void)
{
bh1750_addr_config(); /* ADDR 地址初始化 */
bh1750_iic_config(); /* IIC 引脚初始化 */
bh1750_struct_config(); /* 输出结构体初始化 */
}
/**
* @brief BH1750设置模块采集模式
* @param mode-采集模式 详见 ENUM_BH1750_MODE_TYPEDEF 说明
* @retval 无
*/
void bh1750_set_mode(ENUM_BH1750_MODE_TYPEDEF mode)
{
bh1750.mode = mode;
bh1750_sendCmd(BH1750_CMD_POWER_ON);/* 发送启动命令 */
bh1750_sendCmd(BH1750_CMD_RESET); /* 清除寄存器内容 */
bh1750_sendCmd(bh1750.mode); /* 设置为连续高精度读取模式1,可自定义 */
}
/**
* @brief BH1750设置模块器件地址
* @param addr-模块地址位 接高电平(1):0x5C; 接低电平(0):0x23
* @retval 无
* @note 默认器件地址为0x23 即默认浮空或接低电平
*/
void bh1750_set_addr(uint8_t addr)
{
bh1750.addr = addr ? 0x5C : 0x23;
BH1750_ADDR(addr ? Bit_SET : Bit_RESET);
}
/**
* @brief bh1750 测量函数
* @param 无
* @retval 无
* @note 采用状态机编写 建议函数定时执行 周期1ms
*/
void bh1750_measure(void)
{
static uint8_t time_cnt = 0; /* 计数缓存 */
static ENUM_BH1750_MODE_TYPEDEF pre_mode = (ENUM_BH1750_MODE_TYPEDEF)0xff; /* 初始化时为异常 */
switch( bh1750.state ) {
case BH1750_STATUS_IDLE: {
/* 判断 mode 是否相等 不同的话需要重新设置一下模式 */
if(pre_mode != bh1750.mode) {
pre_mode = bh1750.mode;
bh1750.state = BH1750_STATUS_SET_MODE;
}
else {
bh1750.state = BH1750_STATUS_GET_DATA;
}
}break;
case BH1750_STATUS_SET_MODE: {
bh1750_set_mode(bh1750.mode);
bh1750.state = BH1750_STATUS_WAIT_TIME;
time_cnt = 0;
if(BH1750_MODE_LR == bh1750.mode || BH1750_SINGLE_MODE_LR == bh1750.mode) {
bh1750.wait_time = 24; /* 低分辨率模式只需要等待24ms */
bh1750.wait_next = 16; /* 低分辨率模式需要延时16ms才能进行下一次采集 */
}
else {
bh1750.wait_time = 180; /* 高分辨率模式需要等待180ms */
bh1750.wait_next = 120; /* 高分辨率模式需要延时120ms才能进行下一次采集 */
}
}break;
case BH1750_STATUS_WAIT_TIME: {
if(++time_cnt > bh1750.wait_time) {
bh1750.state = BH1750_STATUS_GET_DATA;
time_cnt = 0;
}
}break;
case BH1750_STATUS_GET_DATA: {
bh1750.light_data = bh1750_readData();
bh1750.state = BH1750_STATUS_WAIT_NEXT;
time_cnt = 0;
}break;
case BH1750_STATUS_WAIT_NEXT: {
if(++time_cnt > bh1750.wait_next) {
bh1750.state = BH1750_STATUS_IDLE;
time_cnt = 0;
}
}break;
default: {
bh1750.state = BH1750_STATUS_IDLE;
}break;
}
}
/**
* @brief 获取光照强度数据
* @param 无
* @retval 光强数据 单位:lx
*/
float bh1750_get_light_data(void)
{
return (bh1750.light_data / 1.2f);
}
bh1750.h源码如下:
#ifndef _BH1750_H
#define _BH1750_H
#include "stm32f10x.h"
/* -------------------------------- BH1750 ADDR 引脚定义 ------------------------------- */
#define BH1750_ADDR_RCC_CLK RCC_APB2Periph_GPIOB
#define BH1750_ADDR_GPIO_PORT GPIOB
#define BH1750_ADDR_GPIO_PIN GPIO_Pin_1
/* -------------------------------- BH1750 IIC 引脚定义 ------------------------------- */
#define BH1750_SCL_RCC_CLK RCC_APB2Periph_GPIOC
#define BH1750_SCL_GPIO_PORT GPIOC
#define BH1750_SCL_GPIO_PIN GPIO_Pin_5
#define BH1750_SDA_RCC_CLK RCC_APB2Periph_GPIOD
#define BH1750_SDA_GPIO_PORT GPIOD
#define BH1750_SDA_GPIO_PIN GPIO_Pin_2
/* 工作命令 */
typedef enum {
BH1750_CMD_POWER_DOWN = 0x00, /* 关闭模块 */
BH1750_CMD_POWER_ON = 0x01, /* 打开模块等待测量指令 */
BH1750_CMD_RESET = 0x07, /* 重置数据寄存器值在PowerOn模式下有效 */
} ENUM_BH1750_CMD_TYPEDEF;
/* 工作模式选择 */
typedef enum {
/* 连续测量模式 */
BH1750_MODE_HR1 = 0x10, /* 高分辨率模式1 单位 1 lx 测量时间120ms */
BH1750_MODE_HR2 = 0x11, /* 高分辨率模式2 单位 0.5 lx 测量时间120ms */
BH1750_MODE_LR = 0x13, /* 低分辨率 单位 4 lx 测量时间16ms */
/* 单次测量模式 测量后模块自动转到PowerDown模式 */
BH1750_SINGLE_MODE_HR1 = 0x20,/* 一次高分辨率测量 */
BH1750_SINGLE_MODE_HR2 = 0x21,/* 一次高分辨率测量 */
BH1750_SINGLE_MODE_LR = 0x23,/* 低分辨率测量 */
} ENUM_BH1750_MODE_TYPEDEF;
/* bh1750 状态机 */
typedef enum {
BH1750_STATUS_IDLE, /* 空闲态 */
BH1750_STATUS_SET_MODE, /* 设置工作模式 */
BH1750_STATUS_WAIT_TIME,/* 等待模式设置成功 */
BH1750_STATUS_GET_DATA, /* 获取数据 */
BH1750_STATUS_WAIT_NEXT,/* 等待下一次测量 */
} ENUM_BH1750_STATUS_TYPEDEF;
typedef struct {
ENUM_BH1750_MODE_TYPEDEF mode; /* 当前工作模式 */
ENUM_BH1750_STATUS_TYPEDEF state; /* 当前工作状态 */
uint8_t addr; /* 模块器件地址 */
uint8_t wait_time; /* 模式配置需等待的时间 */
uint8_t wait_next; /* 下一次测量需等待的时间 */
float light_data; /* 光照强度数据 */
} STRUCT_BH1750_TYPEDEF;
/* ---------------------- 函数清单 ---------------------- */
void bh1750_init(void); /* BH1750 初始化 */
void bh1750_set_mode(ENUM_BH1750_MODE_TYPEDEF mode); /* BH1750设置模块采集模式 */
void bh1750_set_addr(uint8_t addr); /* BH1750设置模块器件地址 */
void bh1750_measure(void); /* BH1750数据测量 */
float bh1750_get_light_data(void); /* 获取光照强度数据 */
#endif
代码一般驱动和使用流程如下:
1. 在bh1750.h文件中修改传感器IIC的接口引脚宏定义,使之适配自己的端口引脚;
2. 调用bh1750_init()函数用于初始化传感器引脚;
3. 周期性的调用bh1750_measure()函数用于测量传感器数据(注意:这里的测量/采集数据并没有返回值进行数据返回),需要使用接口函数返回实际数据值;
4. 在需要获取光照强度数据的地方调用bh1750_get_light_data()函数获取传感器数据。
END
更多推荐
所有评论(0)