M-Design设计竞赛 - 基于树莓派PICO 2的电子测光笔设计
该项目使用了树莓派PICO 2,实现了简易电子测光笔的设计,它的主要功能为:根据光照传感器BH1750计算出曝光值EV,然后根据光圈F和感光值ISO计算出快门时间T。所有的结果都动态的展示在OLED屏幕上。
标签
树莓派PICO
OLED
2025 M-Design
电子测光笔
liyy
更新2025-04-01
23

项目介绍:

项目选题:方向一:边缘智能、智能设备。细分方向是影音,然后是测光表方向。

该项目使用了树莓派PICO 2,实现了简易电子测光笔的设计,它的主要功能为:根据光照传感器BH1750计算出曝光值EV,然后根据光圈F和感光值ISO计算出快门时间T。所有的结果都动态的展示在OLED屏幕上。


项目框图:

项目的简易系统框图如下所示。


项目软件流程图

image.png



项目细节:

这部分内容主要涉及屏幕OLED的驱动,光传感器BH1750的驱动,EV曝光值和快门速度计算等诸多细节。

OLED屏幕驱动:

项目上使用的是之前硬禾学堂的树莓派PICO扩展板。资料链接: https://www.eetree.cn/platform/584

image.png

从硬件原理图看到是spi接口:

image.png

在代码中,定义相关的引脚:

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels


// Declaration for SSD1306 display connected using software SPI (default case):
#define OLED_MOSI   11
#define OLED_CLK   10
#define OLED_DC    9
#define OLED_CS    16
#define OLED_RESET 8
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,
  OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

之后在setup函数中进行屏幕的初始化工作:

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

实物效果:

image.png

BH1750光亮度传感器驱动

光传感器是实现电子测光表的重要元件,本项目上使用的是M5STACK公司的Unit DLight模块(资料链接: https://docs.m5stack.com/zh_CN/unit/DLight%20Unit

image.png

Unit DLight 是一款 数字环境光检测传感器,硬件采用 BH1750FVI 照度传感器 IC ( I2C 接口 ),内置 16bit AD 转换支持 ( 1 ~ 65535 lx ) 照度值检测。具有体积小,功耗低等特点。适用于各种照度检测,光控调节场景。

产品特性:

  • I2C通信接口 (addr: 0x23)
  • 照度数字转换
  • 光源依赖性小 (检测光源: 白炽灯,荧光灯,卤素灯,白光LED,太阳光均可)
  • 检测范围 (1 ~ 65535 lx)

模块的电路原理图如下所示:

image.png

扩展板的I2C接口使用的是GPIO 14, GPIO 15:

image.png

image.png

连接图:

安装驱动DLight:

image.png


编译遇到错误:

image.png

后续也尝试使用PICO2的硬件I2C1驱动BH1750始终还是有问题,后来改为使用软件I2C:

#include <SoftI2C.h>
SoftI2C SoftWire = SoftI2C(14, 15);  //sda, scl

之后修改M5Stack的BH1750驱动:

extern SoftI2C SoftWire;

/*! @brief Write data to the register address. */
void M5_DLight::writeByte(byte cmd) {
    SoftWire.beginTransmission(_addr);
    SoftWire.write(cmd);
    SoftWire.endTransmission();
}


/*! @brief Write the specified length of data to the register.*/
void M5_DLight::writeBytes(uint8_t *buffer, size_t size) {
    SoftWire.beginTransmission(_addr);
    SoftWire.write(buffer, size);
    SoftWire.endTransmission();
}


/*! @brief Read a certain length of data to the specified register address.*/
void M5_DLight::readBytes(uint8_t *buffer, size_t size) {
    SoftWire.requestFrom(_addr, size);
    for (uint16_t i = 0; i < size; i++) {
        buffer[i] = SoftWire.read();
    }
}

主要是使用SoftWire来代替原来的Wire或者Wire1。烧录,验证。串口输出了获取的LUX值。

image.png


计算曝光值等

关于曝光值(EV)、光圈(AV)、快门(TV)、感光度(SV)、照度(Lux)等变量的关系,可以参考如下文章:【超详细公式】

EV=AV+TVSV

image.png

项目上在ISO 100情况下,SV = 0。并且根据如下公式可以根据LUX亮度值计算出曝光值:

image.png


主要思路是根据LUX计算EV曝光值,然后光圈AV是可以通过按键进行调整,T是根据公式进行计算出来的。感光值默认是ISO 100等级。

image.png

代码部分:

  if (digitalRead(12) == LOW) {
    F = 1.4;
    boolBtnPressed = true;
  } else if (digitalRead(13) == LOW) {
    F = 2;
    boolBtnPressed = true;
  }


  lux = sensor.getLUX();
  EV = 2 + log2(sensor.getLUX() / 10);
  T = pow(2, log2(F * F) - EV);

快门值除了使用上述的公式计算,还可以使用查表法快速的找到对应的快门值。本项目使用第二种方法。

const char *TV[] = { "1  ", "1/2 ", "1/4 ", "1/8 ", "1/15", "1/30","1/60", ".125", ".250", ".500", "1000"}; 
lux = sensor.getLUX();
  EV = 2 + log2(sensor.getLUX() / 10);
  //T = pow(2, log2(F * F) - EV);
  Tq = EV - AV;

综合显示

项目最终把结果显示在OLED屏幕上:

  lux = sensor.getLUX();
  EV = 2 + log2(sensor.getLUX() / 10);
  T = pow(2, log2(F * F) - EV);


  sprintf(info, "LUX: %d", lux);
  Serial.println(info);
  sprintf(info1, "EV:%d ISO%d", EV, ISO);
  Serial.print("EV Value: ");
  Serial.println(2 + log2(sensor.getLUX() / 10));
  Serial.println(info1);


  sprintf(info2, "F:%.1f T%.2f", F, T);
  Serial.println(info2);


  display.clearDisplay();
  display.setTextSize(1);  // Normal 1:1 pixel scale
  display.setFont(&FreeMono9pt7b);
  display.setTextColor(SSD1306_WHITE);  // Draw white text
  display.setCursor(24, 12);            // Start at top-left corner
  display.println(F("M Design"));


  display.setCursor(0, 28);  // Start at top-left corner
  display.println(info);
  display.setCursor(0, 44);  // Start at top-left corner
  display.println(info1);
  display.setCursor(0, 60);  // Start at top-left corner
  display.println(info2);


此外,为了增加项目的趣味性,还增加了WS2812灯带的效果,使用的是Adafruit NeoPixel库。结合FreeRTOS,首先定义一个Task专门用于WS2812。

    TaskHandle_t blinkTask;
    xTaskCreate(blink, "BLINK", 256, nullptr, 1, &blinkTask);
void blink(void *param) {
  (void) param;
  delay(500);
  pinMode(LED_BUILTIN, OUTPUT);
  while (true) {
    digitalWrite(LED_BUILTIN, LOW);
    delay(750);
    digitalWrite(LED_BUILTIN, HIGH);
    delay(250);


    MyNeoPixel();
    vTaskDelay(10*60*1000/portTICK_PERIOD_MS);
  }
}

使用vTaskDelay函数可以在不影响FreeRTOS调度的情况下,灯带和测光表可以分别正常工作,互补影响。

实物展示:

image.png

通过按键可以调节光圈小,进行计算出来的快门速度也会实时变化。



WS2812灯带展示:


总结

本次项目使用到了树莓派MCU系列最新的PICO2,结合Arduino开发环境和BH1750光传感器,快速实现了一个光度计的设计。不仅实时计算快门速度还可以通过按键模拟调节光圈大小。


附件下载
代码.zip
团队介绍
go go go
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号