传感器驱动系列之BH1750光照强度传感器

目录

一、BH1750光照强度传感器简介

二、工作原理

2.1 模块寄存器地址

2.2 IIC器件地址说明

2.3 数据采集过程

三、STM32驱动源码

END


一、BH1750光照强度传感器简介

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

图1  BH1750传感器模块实物图

        BH1750传感器模块采用数字输出接口,可以通过I2C总线与微控制器或单片机进行通信。它能够测量0到65535勒克斯(Lux)范围内的光照强度,最小误差变动为±20%,并将结果以数字形式输出。传感器模块内部集成了光敏元件的放大电路和ADC转换电路,能够快速、准确地将光照强度转换为数字信号输出。

        BH1750传感器模块广泛应用于室内和室外光照强度监测、自动光照调节、照明系统控制等领域。它可以帮助实现能源节约和环境保护的目标。

二、工作原理

2.1 模块寄存器地址

        首先我们来看数据手册中的寄存器地址说明,见下图2所示。

图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示。

图3  模块器件地址说明

2.3 数据采集过程

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

图4  模块光照强度测量流程图

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

图5  连续高分辨率模式1下的数据采集时序

        根据上图可以知道,数据的读取分为三步:

  1.         1. 向器件写入读取模式的指令

        首先需要发送IIC开始信号,然后发送设备地址+写命令(0x23<<1 | 0 = 0x46),等待从设备进行响应,然后配置模式为连续高精度读取模式1(即指令0x10),同时等待从设备响应,最后发送IIC停止信号;

  1.         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

  • 27
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
cc2530是一款常见的无线通信芯片,而gy-302和bh1750则是两种常用的光照强度传感器。 针对这个具体的采集程序,在cc2530中集成了对gy-302和bh1750传感器驱动和通信接口。程序首先初始化传感器模块,包括配置传感器的工作模式、设置输出分辨率等等。然后,程序进入一个循环中,不断地读取传感器采集到的光照强度数据。 在读取数据之前,程序需要先向传感器发送读取指令,通过SPI或I2C总线与传感器进行通信。一旦接收到数据,程序将对数据进行解析和处理,得到最终的光照强度数值。 在处理数据时,程序可以进行一些额外的操作,比如将数据转换为人类可读的光照强度单位,或者根据一定的阈值判断光照强度是否达到设定的条件。程序还可以通过无线通信模块将采集到的数据发送到其他设备,实现远程监控和控制。 此外,程序还可以根据实际需求添加一些功能,比如定时采集、数据存储、异常报警等。这些功能可以通过软件编程来实现,利用cc2530的强大处理能力和丰富的接口资源,为用户提供更加灵活和定制化的光照强度采集和处理方案。 总之,cc2530 gy-302 bh1750光照强度采集程序通过在cc2530芯片上集成光照传感器驱动和通信接口,实现了对光照强度数据的采集和处理,为用户提供了简介、高效和可定制化的光照强度监测和控制方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值