天天看點

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 ”章節,像是擴充的高精度測量功能, 原理是成比例的通過延長預設的采樣時間和縮短采樣時間來應對弱光和強光進行測量,由于我暫時用不到就放一邊了。

繼續閱讀