From 54169f41503bfd27d85e28d58b30f5271131aa09 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 27 Jan 2021 11:14:25 +0000 Subject: [PATCH] Add BH1745 driver --- drivers/CMakeLists.txt | 1 + drivers/bh1745/CMakeLists.txt | 1 + drivers/bh1745/bh1745.cmake | 9 ++ drivers/bh1745/bh1745.cpp | 172 ++++++++++++++++++++++++ drivers/bh1745/bh1745.hpp | 71 ++++++++++ examples/CMakeLists.txt | 1 + examples/breakout_bh1745/CMakeLists.txt | 10 ++ examples/breakout_bh1745/demo.cpp | 36 +++++ 8 files changed, 301 insertions(+) create mode 100644 drivers/bh1745/CMakeLists.txt create mode 100644 drivers/bh1745/bh1745.cmake create mode 100644 drivers/bh1745/bh1745.cpp create mode 100644 drivers/bh1745/bh1745.hpp create mode 100644 examples/breakout_bh1745/CMakeLists.txt create mode 100644 examples/breakout_bh1745/demo.cpp diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 8ca192f5..617c999f 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -13,5 +13,6 @@ add_subdirectory(is31fl3731) add_subdirectory(fatfs) add_subdirectory(sdcard) add_subdirectory(as7262) +add_subdirectory(bh1745) add_subdirectory(button) add_subdirectory(rgbled) diff --git a/drivers/bh1745/CMakeLists.txt b/drivers/bh1745/CMakeLists.txt new file mode 100644 index 00000000..b6420b9f --- /dev/null +++ b/drivers/bh1745/CMakeLists.txt @@ -0,0 +1 @@ +include(bh1745.cmake) \ No newline at end of file diff --git a/drivers/bh1745/bh1745.cmake b/drivers/bh1745/bh1745.cmake new file mode 100644 index 00000000..6d286cd3 --- /dev/null +++ b/drivers/bh1745/bh1745.cmake @@ -0,0 +1,9 @@ +add_library(bh1745 INTERFACE) + +target_sources(bh1745 INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/bh1745.cpp) + +target_include_directories(bh1745 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +# Pull in pico libraries that we need +target_link_libraries(bh1745 INTERFACE pico_stdlib hardware_i2c) diff --git a/drivers/bh1745/bh1745.cpp b/drivers/bh1745/bh1745.cpp new file mode 100644 index 00000000..f40d4c7c --- /dev/null +++ b/drivers/bh1745/bh1745.cpp @@ -0,0 +1,172 @@ +#include "bh1745.hpp" +#include + +namespace pimoroni { + int BH1745::init() { + i2c_init(i2c, 400000); + + gpio_set_function(sda, GPIO_FUNC_I2C); gpio_pull_up(sda); + gpio_set_function(scl, GPIO_FUNC_I2C); gpio_pull_up(scl); + + reset(); + + if (this->get_chip_id() != CHIP_ID || this->get_manufacturer() != MANUFACTURER) { + return 1; + } + + this->reset(); + this->clear_bits(REG_SYSTEM_CONTROL, 6); // Clear INT reset bit + this->set_measurement_time_ms(640); + this->set_bits(REG_MODE_CONTROL2, 4); // Enable RGBC + this->set_bits(REG_MODE_CONTROL3, 0, 0xff); // Turn on sensor + this->set_threshold_high(0x0000); // Set threshold so int will always fire + this->set_threshold_low(0xFFFF); // this lets us turn on the LEDs with the int pin + this->clear_bits(REG_INTERRUPT, 4); // Enable interrupt latch + + sleep_ms(320); + + return 0; + } + + uint8_t BH1745::get_chip_id() { + uint8_t chip_id; + this->read_bytes(REG_SYSTEM_CONTROL, &chip_id, 1); + return chip_id & 0b00111111; + } + + uint8_t BH1745::get_manufacturer() { + uint8_t manufacturer; + this->read_bytes(REG_MANUFACTURER, &manufacturer, 1); + return manufacturer; + } + + void BH1745::reset() { + this->set_bits(REG_SYSTEM_CONTROL, 7); + + while (this->get_bits(REG_SYSTEM_CONTROL, 7)) { + sleep_ms(100); + } + } + + void BH1745::set_measurement_time_ms(uint16_t value) { + uint8_t reg = 0; + switch(value) { + case 160: + reg = 0b000; + break; + case 320: + reg = 0b001; + break; + case 640: + reg = 0b010; + break; + case 1280: + reg = 0b011; + break; + case 2560: + reg = 0b100; + break; + case 5120: + reg = 0b101; + break; + } + this->write_bytes(REG_MODE_CONTROL1, ®, 1); + } + + void BH1745::set_threshold_high(uint16_t value) { + this->write_bytes(REG_THRESHOLD_HIGH, (uint8_t *)&value, 2); + } + + void BH1745::set_threshold_low(uint16_t value) { + this->write_bytes(REG_THRESHOLD_LOW, (uint8_t *)&value, 2); + } + + void BH1745::set_leds(bool state) { + if(state){ + this->set_bits(REG_INTERRUPT, 0); + } + else + { + this->clear_bits(REG_INTERRUPT, 0); + } + + } + + rgbc_t BH1745::get_rgb_scaled() { + rgbc_t rgbc = this->get_rgbc_raw(); + + if(rgbc.c > 0) { + rgbc.r = (uint16_t)((uint32_t)rgbc.r * 255 / rgbc.c) & 0xff; + rgbc.g = (uint16_t)((uint32_t)rgbc.g * 255 / rgbc.c) & 0xff; + rgbc.b = (uint16_t)((uint32_t)rgbc.b * 255 / rgbc.c) & 0xff; + } else { + rgbc.r = 0; + rgbc.g = 0; + rgbc.b = 0; + } + + return rgbc; + } + + rgbc_t BH1745::get_rgb_clamped() { + rgbc_t rgbc = this->get_rgbc_raw(); + + uint16_t vmax = std::max(rgbc.r, std::max(rgbc.g, rgbc.b)); + + rgbc.r = (uint16_t)((uint32_t)rgbc.r * 255 / vmax); + rgbc.g = (uint16_t)((uint32_t)rgbc.g * 255 / vmax); + rgbc.b = (uint16_t)((uint32_t)rgbc.b * 255 / vmax); + + return rgbc; + } + + rgbc_t BH1745::get_rgbc_raw() { + while(this->get_bits(REG_MODE_CONTROL2, 7) == 0) { + sleep_ms(1); + } + rgbc_t colour_data; + this->read_bytes(REG_COLOUR_DATA, (uint8_t *)&colour_data, 8); + colour_data.r *= this->channel_compensation[0]; + colour_data.g *= this->channel_compensation[1]; + colour_data.b *= this->channel_compensation[2]; + colour_data.c *= this->channel_compensation[3]; + return colour_data; + } + + // i2c functions + + int BH1745::write_bytes(uint8_t reg, uint8_t *buf, int len) { + uint8_t buffer[len + 1]; + buffer[0] = reg; + for(int x = 0; x < len; x++) { + buffer[x + 1] = buf[x]; + } + return i2c_write_blocking(this->i2c, this->address, buffer, len + 1, false); + }; + + int BH1745::read_bytes(uint8_t reg, uint8_t *buf, int len) { + i2c_write_blocking(this->i2c, this->address, ®, 1, true); + i2c_read_blocking(this->i2c, this->address, buf, len, false); + return len; + }; + + uint8_t BH1745::get_bits(uint8_t reg, uint8_t shift, uint8_t mask) { + uint8_t value; + this->read_bytes(reg, &value, 1); + return value & (mask << shift); + } + + void BH1745::set_bits(uint8_t reg, uint8_t shift, uint8_t mask) { + uint8_t value; + this->read_bytes(reg, &value, 1); + value |= mask << shift; + this->write_bytes(reg, &value, 1); + } + + void BH1745::clear_bits(uint8_t reg, uint8_t shift, uint8_t mask) { + uint8_t value; + this->read_bytes(reg, &value, 1); + value &= ~(mask << shift); + this->write_bytes(reg, &value, 1); + } +} \ No newline at end of file diff --git a/drivers/bh1745/bh1745.hpp b/drivers/bh1745/bh1745.hpp new file mode 100644 index 00000000..bf0a4843 --- /dev/null +++ b/drivers/bh1745/bh1745.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include "hardware/i2c.h" +#include "hardware/gpio.h" + +#define REG_SYSTEM_CONTROL 0x40 +#define REG_MODE_CONTROL1 0x41 +#define REG_MODE_CONTROL2 0x42 +#define REG_MODE_CONTROL3 0x44 +#define REG_COLOUR_DATA 0x50 +#define REG_DINT_DATA 0x58 +#define REG_INTERRUPT 0x60 +#define REG_PERSISTENCE 0x61 +#define REG_THRESHOLD_LOW 0x64 +#define REG_THRESHOLD_HIGH 0x62 +#define REG_MANUFACTURER 0x92 + +#define CHIP_ID 0b001011 +#define MANUFACTURER 0xe0 + +#define I2C_ADDR 0x38 +#define I2C_ADDR_ALT 0x39 + +namespace pimoroni { + typedef struct { + uint16_t r; + uint16_t g; + uint16_t b; + uint16_t c; + } rgbc_t; + + class BH1745 { + public: + BH1745() {}; + + BH1745(uint8_t addr) : address(addr) {}; + + BH1745(i2c_inst_t *i2c, uint8_t addr, uint8_t sda, uint8_t scl, uint8_t interrupt) : + i2c(i2c), address(addr), sda(sda), scl(scl), interrupt(interrupt) {}; + + int init(); + uint8_t get_chip_id(); + uint8_t get_manufacturer(); + void set_threshold_high(uint16_t value); + void set_threshold_low(uint16_t value); + void set_measurement_time_ms(uint16_t value); + rgbc_t get_rgbc_raw(); + rgbc_t get_rgb_clamped(); + rgbc_t get_rgb_scaled(); + void reset(); + void set_leds(bool state=true); + + private: + i2c_inst_t *i2c = i2c0; + + // interface pins with our standard defaults where appropriate + int8_t address = 0x38; + int8_t sda = 4; + int8_t scl = 5; + int8_t interrupt = 22; + + float channel_compensation[4] = {2.2f, 1.0f, 1.8f, 10.0f}; + + // From i2cdevice + int write_bytes(uint8_t reg, uint8_t *buf, int len); + int read_bytes(uint8_t reg, uint8_t *buf, int len); + uint8_t get_bits(uint8_t reg, uint8_t shift, uint8_t mask=0b1); + void set_bits(uint8_t reg, uint8_t shift, uint8_t mask=0b1); + void clear_bits(uint8_t reg, uint8_t shift, uint8_t mask=0b1); + }; +} \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 156f45d0..cc18a6aa 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -28,3 +28,4 @@ add_subdirectory(pico_trackball_display) add_subdirectory(pico_audio) add_subdirectory(pico_wireless) add_subdirectory(breakout_as7262) +add_subdirectory(breakout_bh1745) diff --git a/examples/breakout_bh1745/CMakeLists.txt b/examples/breakout_bh1745/CMakeLists.txt new file mode 100644 index 00000000..08b18200 --- /dev/null +++ b/examples/breakout_bh1745/CMakeLists.txt @@ -0,0 +1,10 @@ +add_executable( + breakout_bh1745 + demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(breakout_bh1745 pico_stdlib hardware_spi bh1745) + +# create map/bin/hex file etc. +pico_add_extra_outputs(breakout_bh1745) \ No newline at end of file diff --git a/examples/breakout_bh1745/demo.cpp b/examples/breakout_bh1745/demo.cpp new file mode 100644 index 00000000..244f58b4 --- /dev/null +++ b/examples/breakout_bh1745/demo.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include "pico/stdlib.h" + +#include "bh1745.hpp" + +using namespace pimoroni; + +BH1745 bh1745(0x39); + +int main() { + setup_default_uart(); + + if (bh1745.init() == 1) { + printf("Failed to set up sensor\n"); + return 1; + } + + uint8_t chip_id = bh1745.get_chip_id(); + printf("Found BH1745. Chip ID: 0x%02x\n", chip_id); + + bh1745.set_leds(true); + + while(true) { + rgbc_t colour = bh1745.get_rgbc_raw(); + printf("Colour: %d %d %d %d\n", colour.r, colour.g, colour.b, colour.c); + colour = bh1745.get_rgb_clamped(); + printf("Clamped: %d %d %d %d\n", colour.r, colour.g, colour.b, colour.c); + colour = bh1745.get_rgb_scaled(); + printf("Scaled: %d %d %d %d\n\n", colour.r, colour.g, colour.b, colour.c); + sleep_ms(1000); + } + + return 0; +} \ No newline at end of file