天天看点

BH1750环境光传感器的基于Arduino测试例程及简单滤波运行的硬件环境变量声明和参数设置部分I2C读写函数封装程序初始化部分程序循环执行部分备注

BH1750环境光传感器的基于Arduino测试例程及简单滤波

  • 运行的硬件环境
  • 变量声明和参数设置部分
  • I^2^C读写函数封装
  • 程序初始化部分
  • 程序循环执行部分
    • 简单的求平均数滤波
    • 简单的限幅滤波
    • 2次滤波后的效果
  • 备注

运行的硬件环境

刷过特定BOOTLOADER的[1]STM32F103CBT6, 除了对应链接VCC和GND外,

SCL连接PB6,

SDA连接PB7.

变量声明和参数设置部分

变量声明部分有2个设置是可调的,

第1个是PIN_ADDR_STATUS宏定义,主要是设置IC的I2C地址,如果addr的pin脚拉低就设置为0,拉高就设置为1;

第2个是 resolution_mode变量,主要是把手册的6种工作(分辨率)模式做成选单样式,直接切换;

不怎么会写东西,直接上代码:

#include <Wire.h> //Arduino提供的硬件I2C的库函数

/* --------opecode------------------ */
#define POWER_DOWN      0x00
#define POWER_ON        0x01
#define RESET           0x07

#define CONTINUOUSLY_H_MODE1    0x10  //resolution_mode 0
#define CONTINUOUSLY_H_MODE2    0x11  //resolution_mode 1
#define CONTINUOUSLY_L_MODE1    0x13  //resolution_mode 2

#define ONE_TIME_H_MODE1    0x20  //resolution_mode 3
#define ONE_TIME_H_MODE2    0x21  //resolution_mode 4
#define ONE_TIME_L_MODE1    0x23  //resolution_mode 5
/* -------------------------------- */

#define READ_DATA_LEN    1
#define SIMPLE_AVG_NUM   10

#define PIN_ADDR_STATUS   0 //0-> pull down; 1->pull up

int resolution_mode = 5;  // select running mode

byte i2c_addr = 0; //7-bit addr
float lux_data = 0.0f;

float sum_lux_data = 0.0f;
float avg_lux_data = 0.0f;
int avg_count = 0;
byte first_data = 0;
float compare_lux_data = 0.0f;
           

I2C读写函数封装

这部分主要是依照Arduino提供的硬件I2C库函数,和datasheet上的读写示例,另外结合工作精度模式做切换:

uint8_t i2c_bh1750_write(uint8 reg)
{
  Wire.beginTransmission(i2c_addr);
  Wire.write(reg);
  Wire.endTransmission();
  delayMicroseconds(50);
}

uint8_t i2c_bh1750_read(uint8 *get_data)
{
  uint8 reg = 0;
  int i;
  int len = 2;
  
  //工作精度模式切换部分
  switch (resolution_mode) {
    case 0:
      reg = CONTINUOUSLY_H_MODE1;
      break;
    case 1:
      reg = CONTINUOUSLY_H_MODE2;
      break;
    case 2:
      reg = CONTINUOUSLY_L_MODE1;
      break;
    case 3:
      reg = ONE_TIME_H_MODE1;
      break;
    case 4:
      reg = ONE_TIME_H_MODE2;
      break;
    case 5:
      reg = ONE_TIME_L_MODE1;
      break;
    default:
      reg = ONE_TIME_L_MODE1;
  }

  Wire.beginTransmission(i2c_addr);
  Wire.write(reg);
  Wire.endTransmission();
  
  //工作精度模式切换部分
  if (resolution_mode < 3) {
    delay(180);
  } else {
    delay(24);
  }
  Wire.requestFrom(i2c_addr, len);
  for (i = 0; i < READ_DATA_LEN; i++) {
    get_data[i] = Wire.read();
  }
}
           

程序初始化部分

这部分主要是选择I2C地址, 和复位芯片:

void setup()
{
#if PIN_ADDR_STATUS
  i2c_addr = ~0x23;
#else
  i2c_addr = 0x23;
#endif

  // join I2C bus (I2Cdev library doesn't do this automatically)
  Wire.begin();
  Serial.begin(115200);
  delay(1000);

  //reset
  i2c_bh1750_write(RESET);
}
           

程序循环执行部分

这部分包含2个简单滤波,后面提到,上一个流程图:

void loop()
{
  uint8 data[READ_DATA_LEN] = {0};
  uint16 temp_data = 0;
  float limit_lux = 0.0f;

  //set power on
  i2c_bh1750_write(POWER_ON);

  i2c_bh1750_read(data);
  temp_data = data[0] << 8 | data[1];
  lux_data = (float)temp_data / 1.2f; // Calibration data

  if ( first_data == 0 ) {
    avg_lux_data = lux_data;
    compare_lux_data = avg_lux_data;
  }

  //取一段数据求和后,求平均数,能过滤个别的偏差大的数据
  sum_lux_data += lux_data;
  if (avg_count >= SIMPLE_AVG_NUM) {
    avg_lux_data = (float)sum_lux_data / SIMPLE_AVG_NUM;
    avg_count = 0;
    sum_lux_data = 0;
    first_data = 1;
  }
  avg_count++;
  
  //根据工作精度模式来选限幅滤波的大小
  switch (resolution_mode) {
    case 0:
    case 3:
      limit_lux = 0.5001f;
      break;
    case 1:
    case 4:
      limit_lux = 1.0001f;
      break;
    case 2:
    case 5:
      limit_lux = 4.0001f;
      break;
    default:
      ;
  }

  // limiting by resolution
  if ((compare_lux_data - avg_lux_data > limit_lux) || (compare_lux_data - avg_lux_data < -limit_lux)) {
    compare_lux_data = avg_lux_data;
  }

  Serial.print("lux_data(lux)="); Serial.print("\t");
  Serial.print(lux_data); Serial.print("\t");
  //Serial.print(avg_lux_data); Serial.print("\t");
  Serial.print(compare_lux_data); Serial.print("\t");
  Serial.println();


  //set power off
  if (resolution_mode < 3) {
    i2c_bh1750_write(POWER_ON);
  }


  delay(5000); //ms
}
           

简单的求平均数滤波

通过设置SIMPLE_AVG_NUM样本容量来求出一个稳定数据,过滤个别的跳动大的数据:

sum_lux_data += lux_data;
  if (avg_count >= SIMPLE_AVG_NUM) {
    avg_lux_data = (float)sum_lux_data / SIMPLE_AVG_NUM;
    avg_count = 0;
    sum_lux_data = 0;
    first_data = 1;
  }
  avg_count++;
           

简单的限幅滤波

根据工作精度模式来选限幅滤波的大小,限幅数据依据"Measurement mode explanation"部分的分辨率来写的:

BH1750环境光传感器的基于Arduino测试例程及简单滤波运行的硬件环境变量声明和参数设置部分I2C读写函数封装程序初始化部分程序循环执行部分备注
switch (resolution_mode) {
    case 0:
    case 3:
      limit_lux = 0.5001f;
      break;
    case 1:
    case 4:
      limit_lux = 1.0001f;
      break;
    case 2:
    case 5:
      limit_lux = 4.0001f;
      break;
    default:
      ;
  }

  // limiting by resolution
  if ((compare_lux_data - avg_lux_data > limit_lux) || (compare_lux_data - avg_lux_data < -limit_lux)) {
    compare_lux_data = avg_lux_data;
  }
           

2次滤波后的效果

下图就是2次滤波后的效果,蓝线是原始的实时数据lux_data,红线是求平均和限幅后的数据compare_lux_data,有1个明显的特点就是红线会在整个测量过程相对平稳一些:

BH1750环境光传感器的基于Arduino测试例程及简单滤波运行的硬件环境变量声明和参数设置部分I2C读写函数封装程序初始化部分程序循环执行部分备注

备注

主要是熟悉IC的datasheet,暂时并没有把它封装成库文件;

我也参考一部分其他作者的文章,但不完全是“复读机”,参考文章列表如下:

https://blog.csdn.net/qq_21990661/article/details/81043863

https://blog.csdn.net/qq_41422043/article/details/89184540

本文还有一个功能没有来得及考虑,那就是datasheet的“Adjust measurement result for influence of optical window ”章节,像是扩展的高精度测量功能, 原理是成比例的通过延长默认的采样时间和缩短采样时间来应对弱光和强光进行测量,由于我暂时用不到就放一边了。

继续阅读