diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index d5c9a558..45ec8d5a 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(is31fl3731) add_subdirectory(fatfs) add_subdirectory(sdcard) add_subdirectory(as7262) +add_subdirectory(as7343) add_subdirectory(bh1745) add_subdirectory(bme68x) add_subdirectory(bmp280) diff --git a/drivers/as7343/CMakeLists.txt b/drivers/as7343/CMakeLists.txt new file mode 100644 index 00000000..e231a70a --- /dev/null +++ b/drivers/as7343/CMakeLists.txt @@ -0,0 +1 @@ +include(as7343.cmake) \ No newline at end of file diff --git a/drivers/as7343/as7343.cmake b/drivers/as7343/as7343.cmake new file mode 100644 index 00000000..cd937b36 --- /dev/null +++ b/drivers/as7343/as7343.cmake @@ -0,0 +1,10 @@ +set(DRIVER_NAME as7343) +add_library(${DRIVER_NAME} INTERFACE) + +target_sources(${DRIVER_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp) + +target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +# Pull in pico libraries that we need +target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_i2c pimoroni_i2c) diff --git a/drivers/as7343/as7343.cpp b/drivers/as7343/as7343.cpp new file mode 100644 index 00000000..646711c8 --- /dev/null +++ b/drivers/as7343/as7343.cpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include + +#include "as7343.hpp" + +namespace pimoroni { + + bool AS7343::init() { + if(interrupt != PIN_UNUSED) { + gpio_set_function(interrupt, GPIO_FUNC_SIO); + gpio_set_dir(interrupt, GPIO_IN); + gpio_pull_up(interrupt); + } + + reset(); + + bank_select(0); + + // Enable all 7 channels in output FIFO + i2c->set_bits(address, reg::FIFO_MAP, 0, FIFO_MAP_CH5 | FIFO_MAP_CH4 | FIFO_MAP_CH3 | FIFO_MAP_CH2 | FIFO_MAP_CH1 | FIFO_MAP_CH0 | FIFO_MAP_ASTATUS); + + // Set the PON bit + i2c->reg_write_uint8(address, reg::ENABLE, ENABLE_WEN | ENABLE_SMUXEN | ENABLE_SP_EN | ENABLE_PON); + + return true; + } + + void AS7343::reset() { + i2c->reg_write_uint8(address, reg::CONTROL, CONTROL_SW_RESET); + sleep_ms(1000); + } + + i2c_inst_t* AS7343::get_i2c() const { + return i2c->get_i2c(); + } + + int AS7343::get_sda() const { + return i2c->get_sda(); + } + + int AS7343::get_scl() const { + return i2c->get_scl(); + } + + int AS7343::get_int() const { + return interrupt; + } + + void AS7343::bank_select(uint8_t bank) { + if(bank == 1) { + i2c->set_bits(address, reg::CFG0, 0, CFG0_BANK); + } else { + i2c->clear_bits(address, reg::CFG0, 0, CFG0_BANK); + } + } + + void AS7343::get_version(uint8_t &auxid, uint8_t &revid, uint8_t &hwid) { + bank_select(1); + auxid = i2c->reg_read_uint8(address, reg::AUXID) & 0b00001111; + revid = i2c->reg_read_uint8(address, reg::REVID) & 0b00000111; + hwid = i2c->reg_read_uint8(address, reg::ID); + bank_select(0); + } + + void AS7343::set_gain(float gain) { + if(gain == 0.5f) { + i2c->reg_write_uint8(address, reg::CFG1, 0); + } else { + uint16_t ugain = (uint16_t)gain; + uint16_t bitlength = 0; + while(ugain > 0) { + bitlength++; + ugain >>= 1; + } + i2c->reg_write_uint8(address, reg::CFG1, bitlength & 0x1f); + } + } + + void AS7343::set_channels(channel_count channel_count) { + this->read_cycles = uint8_t(channel_count) / 6; + this->ch_count = (uint8_t)channel_count; + uint8_t temp = i2c->reg_read_uint8(address, reg::CFG20) & ~CFG20_18_CH; + switch(channel_count) { + case channel_count::SIX_CHANNEL: + temp |= CFG20_6_CH; + break; + case channel_count::TWELVE_CHANNEL: + temp |= CFG20_12_CH; + break; + case channel_count::EIGHTEEN_CHANNEL: + temp |= CFG20_18_CH; + break; + } + i2c->reg_write_uint8(address, reg::CFG20, temp); + } + + void AS7343::set_illumination_current(float current) { + current -= 4; + current /= 2.0f; + uint8_t temp = i2c->reg_read_uint8(address, reg::LED) & 0b10000000; + temp |= (uint8_t)current; + i2c->reg_write_uint8(address, reg::LED, temp); + } + + void AS7343::set_illumination_led(bool state) { + uint8_t temp = i2c->reg_read_uint8(address, reg::LED) & 0b01111111; + temp |= state ? 0x80 : 0x00; + i2c->reg_write_uint8(address, reg::LED, temp); + } + + void AS7343::set_measurement_time(float measurement_time_ms) { + constexpr float resolution = 2.78f; + i2c->reg_write_uint8(address, reg::WTIME, (uint8_t)((measurement_time_ms - resolution) / resolution)); + } + + void AS7343::set_integration_time(float integration_time_us, uint8_t repeat) { + constexpr float resolution = 2.78f; + constexpr float max_astep = (65534 + 1) * resolution; + + if (integration_time_us <= max_astep) { + i2c->reg_write_uint8(address, reg::ATIME, repeat - 1); + i2c->reg_write_uint16(address, reg::ASTEP, (uint16_t)((integration_time_us - resolution) / resolution) & 0xfffe); + } else { + // Time out of range... + } + } + + void AS7343::read_fifo(uint16_t *buf) { + uint16_t expected_results = read_cycles * 7; + uint16_t result_slot = 0; + + while (i2c->reg_read_uint8(address, reg::FIFO_LVL) < expected_results) { + sleep_ms(1); + } + + while (i2c->reg_read_uint8(address, reg::FIFO_LVL) > 0 && expected_results > 0) { + buf[result_slot] = i2c->reg_read_uint16(address, reg::FDATA); + expected_results--; + result_slot++; + } + } + + AS7343::reading AS7343::read() { + reading result; + read_fifo((uint16_t *)&result); + return result; + } + + +} \ No newline at end of file diff --git a/drivers/as7343/as7343.hpp b/drivers/as7343/as7343.hpp new file mode 100644 index 00000000..873a1ca5 --- /dev/null +++ b/drivers/as7343/as7343.hpp @@ -0,0 +1,139 @@ +#pragma once + +#include + +#include "hardware/i2c.h" +#include "hardware/gpio.h" +#include "common/pimoroni_common.hpp" +#include "common/pimoroni_i2c.hpp" +#include "as7343_regs.hpp" + +namespace pimoroni { + + class AS7343 { + //-------------------------------------------------- + // Constants + //-------------------------------------------------- + public: + static const uint8_t DEFAULT_I2C_ADDRESS = 0x39; + + //-------------------------------------------------- + // Enums + //-------------------------------------------------- + public: + enum class channel_count : uint8_t { + SIX_CHANNEL = 6, + TWELVE_CHANNEL = 12, + EIGHTEEN_CHANNEL = 18 + }; +/* + enum class COMPENSATION_GAIN : float { + F1 = 1.84f, + F2 = 6.03f, + FZ = 4.88f, + F3 = 13.74f, + F4 = 3.37f, + FY = 2.82f, + F5 = 6.72f, + FXL = 2.22f, + F6 = 3.17f, + F7 = 1.95f, + F8 = 12.25f, + NIR = 1.00f, + }; +*/ + + //-------------------------------------------------- + // Substructures + //-------------------------------------------------- + public: + struct reading { + // Cycle 1 + uint16_t FZ = 0; // 428-480 nm + uint16_t FY = 0; // 534-593 nm + uint16_t FXL = 0; // 593-628 nm + uint16_t NIR = 0; // 849-903 nm + + uint16_t c1_vis_tl = 0; // Visible top-left + uint16_t c1_vis_br = 0; // Visible bottom-right + uint16_t c1_astatus = 0; // (c1_astatus >> 8) & 0b10001111 + // 0b10000000 = saturated + // 0b00001111 = gain lowest nibble + + // Cycle 2 + uint16_t F2 = 0; // 408-448 nm + uint16_t F3 = 0; // 448-500 mn + uint16_t F4 = 0; // 500-534 nm + uint16_t F6 = 0; // 618-665 nm + + uint16_t c2_vis_tl = 0; + uint16_t c2_vis_br = 0; + uint16_t c2_astatus = 0; + + // Cycle 3 + uint16_t F1 = 0; // 396-408 nm + uint16_t F5 = 0; // 531-594 nm + uint16_t F7 = 0; // 685-715 nm + uint16_t F8 = 0; // 715-766 nm + + uint16_t c3_vis_tl = 0; + uint16_t c3_vis_br = 0; + uint16_t c3_astatus = 0; + }; + + + //-------------------------------------------------- + // Variables + //-------------------------------------------------- + private: + I2C *i2c; + + // interface pins with our standard defaults where appropriate + int8_t address = DEFAULT_I2C_ADDRESS; + uint interrupt = PIN_UNUSED; + + uint8_t read_cycles = 1; + uint8_t ch_count = (uint8_t)channel_count::SIX_CHANNEL; + + + //-------------------------------------------------- + // Constructors/Destructor + //-------------------------------------------------- + public: + AS7343(uint interrupt = PIN_UNUSED) : AS7343(new I2C(), interrupt) {}; + + AS7343(I2C *i2c, uint interrupt = PIN_UNUSED) : i2c(i2c), interrupt(interrupt) {} + + + //-------------------------------------------------- + // Methods + //-------------------------------------------------- + public: + bool init(); + void reset(); + + // For print access in micropython + i2c_inst_t* get_i2c() const; + int get_sda() const; + int get_scl() const; + int get_int() const; + + void get_version(uint8_t &auxid, uint8_t &revid, uint8_t &hwid); + + reading read(); + + void read_fifo(uint16_t *buf); + + void set_gain(float gain); + void set_illumination_current(float current); + void set_illumination_led(bool state); + void set_measurement_time(float measurement_time_ms); + void set_integration_time(float integration_time_us, uint8_t repeat=1); + + void set_channels(channel_count channel_count); + +private: + void bank_select(uint8_t bank=0); + }; + +} diff --git a/drivers/as7343/as7343_regs.hpp b/drivers/as7343/as7343_regs.hpp new file mode 100644 index 00000000..0f7e1582 --- /dev/null +++ b/drivers/as7343/as7343_regs.hpp @@ -0,0 +1,155 @@ +#pragma once +#include + +namespace pimoroni { + /***** Device registers and masks here *****/ + + enum reg { + // Bank 1 + AUXID = 0x58, // AUXID = 0b00001111 + REVID = 0x59, // REVID = 0b00000111 + ID = 0x5A, // ID = 0b11111111 + CFG12 = 0x66, // SP_TH_CH = 0b00000111 + + // Bank 0 + ENABLE = 0x80, // FDEN = 0b01000000 + // SMUXEN = 0b00010000 + // WEN = 0b00001000 + // SP_EN = 0b00000010 + // PON = 0b00000001 + ATIME = 0x81, // ATIME = 0b11111111 + // Integration Time Step Size + // 0 = 2.87us + // n 2.87us x (n + 1) + ASTEP = 0xD4, // STEP = 0xFFFF + // Spectral Measurement Wait Time + // 0 = 1 cycle = 2.78ms + // n = 2.78ms x (n + 1) + WTIME = 0x83, // WTIME = 0b11111111 + SP_TH = 0x84, // SP_TH_L = 0xFFFF0000 Spectral Low Threshold + // SP_TH_H = 0x0000FFFF Spectral High Threshold + STATUS = 0x93, // ASAT = 0b10000000 Spectral Saturation (if ASIEN set) + // AINT = 0b00001000 Spectral Channel Interrupt (if SP_IEN set) + // FINT = 0b00000100 FIFO Buffer Interrupt + // SINT = 0b00000001 System Interrupt + ASTATUS = 0x94,// ASAT = 0b10000000 Saturation Status + // AGAIN = 0b00000111 Gain Status + DATA = 0x95, // 36 bytes, fields 0 to 17 (2 bytes eac) + STATUS2 = 0x90,// AVALID = 0b01000000 Spectral Data Valid + // ASAT_DIG = 0b00010000 Digital Saturation + // ASAT_ANA = 0b00001000 Analog Saturation + // FDSAT_ANA= 0b00000010 Flicker Analog Saturation + // FDSAT_DIG= 0b00000001 Flicker Digital Saturation + STATUS3 = 0x91,// INT_SP_H = 0b00100000 Spectral Above High Threshold + // INT_SP_L = 0b00010000 Spectral Below Low Threshold + STATUS5 = 0xBB,// SINT_FD = 0b00001000 Flicker Detect Interrupt (if SIEN_FD set) + // SINT_SMUX= 0b00000100 SMUS Operation Interrupt (SMUX exec finished) + STATUS4 = 0xBC,// FIFO_OV = 0b10000000 FIFO Buffer Overflow + // OVTEMP = 0b00100000 Over Temperature + // FD_TRIG = 0b00010000 Flicker Detect Trigger Error + // SD_TRIG = 0b00000100 Spectral Trigger Error + // SAI_ACT = 0b00000010 Sleep After Interrupt Active + // INT_BUSY = 0b00000001 Initialization busy (1 for ~300us after power on) + CFG0 = 0xBF, // LOW_POWER= 0b00100000 + // REG_BANK = 0b00010000 0 - Register 0x80 and above + // 1 - Register 0x20 to 0x7f + // WLONG = 0b00000100 Increase WTIME by factor of 16 + // Spectral Engines Gain Setting + // 0 = 0.5x, # 1 = 1x, 2 = 2x, 12 = 2048x + // GAINx = 1 << (n - 1) + CFG1 = 0xC6, + CFG3 = 0xC7, // SAI = 0b00100000 Sleep after interrupt + CFG6 = 0xF5, // SMUS_CMD = 0b00011000 0 - ROM_init + // 1 - Read_SMUX + // 2 - Write_SMUX + CFG8 = 0xC9, // FIFO_TH = 0b11000000 0b00, 0b01, 0b10, 0b11 + CFG9 = 0xCA, // SIEN_FD = 0b01000000 System Interrupt Flicker Detection + // SIEN_SMUX= 0b00010000 System Interrupt SMUX Operation + CFG10 = 0x65, // FD_PERS = 0b00000111 Flicker Detect Persistence + // Number of results that must differ before status change + PERS = 0xCF, // APERS = 0b00001111 + GPIO = 0x6B, // GPIO_INV = 0b00001000 Invert GPIO output + // GPIO_IN_EN=0b00000100 Enable GPIO input + // GPIO_OUT = 0b00000010 GPIO Output + // GPIO_IN = 0b00000001 GPIO Input + CFG20 = 0xD6, // FD_FIFO_8b=0b10000000 Enable 8bit FIFO mode for Flicker Detect (FD_TIME < 256) + // auto_SMUX= 0b01100000 Auto channel read-out + LED = 0xCD, // LED_ACT = 0b10000000 External LED (LDR) Control + // LED_DRIVE= 0b01111111 External LED drive strength (N - 4) >> 1 + // Flicker Detection AGC Gain Max + // Max = 2^N (0 = 0.5x) + AGC_GAIN_MAX = 0xD7, // ADC_FD_GAIN_MAX = 0b11110000 + AZ_CONFIG = 0xDE, // AT_NTH_ITERATION = 0b11111111 Auto-zero Frequency + // 0 - Never (Not Recommended) + // n - Every n integration cycles + // 255 - only before first measurement cycle + FD_TIME_1 = 0xE0, // FD_TIME=0b11111111 Flicker Detection Integration Time (Do not change if FDEN = 1 & PON = 1) + FD_TIME_2 = 0xE2, // FD_GAIN=0b11111000 Flicker Detection Gain (0 = 0.5x, 1 = 1x, 2 = 2x, 12 = 2048x) + // FD_TIME=0b00000111 Flicker Detection Time (Do not change if FDEN = 1 & PON = 1) + FD_CFG0 = 0xDF, // FIFO_WRITE_FD = 0b10000000 Write flicker raw data to FIFO (1 byte per sample) + FD_STATUS = 0xE3, // FD_VALID=0b00100000 + // FD_SAT = 0b00010000 + // FD_120HZ_VALID = 0b00001000 + // FD_100HZ_VALID = 0b00000100 + // FD_120HZ = 0b00000010 + // FD_100HZ = 0b00000001 + INTERNAB = 0xF9, // ASIEN = 0b10000000 Saturation Interrupt Enable + // SP_IEN = 0b00001000 Spectral Interrupt Enable + // FIEN = 0b00000100 FIFO Buffer Interrupt Enable + // SIEN = 0b00000001 System Interrupt Enable + CONTROL = 0xFA, // SW_RESET = 0b00001000 Software Reset + // SP_MAN_AZ= 0b00000100 Spectral Manual Auto-zero + // FIFO_CLR = 0b00000010 FIFO Buffer Clear + // CLEAR_SAI_ACT = 0b00000001 Clear sleep-after-interrupt + FIFO_MAP = 0xFC, // CH5 = 0b01000000 + // CH4 = 0b00100000 + // CH3 = 0b00010000 + // CH2 = 0b00001000 + // CH1 = 0b00000100 + // CH0 = 0b00000010 + // ASTATUS = 0b00000001 + FIFO_LVL = 0xFD, + FDATA = 0xFE // 0xFFFF + }; + + constexpr uint8_t CFG0_LOW_POWER = 0b00100000; + constexpr uint8_t CFG0_BANK = 0b00010000; + constexpr uint8_t CFG0_WLONG = 0b00000100; + + constexpr uint8_t CFG20_6_CH = 0b00000000; + constexpr uint8_t CFG20_12_CH = 0b01000000; + constexpr uint8_t CFG20_18_CH = 0b01100000; + + constexpr uint8_t ENABLE_FDEN = 0b01000000; + constexpr uint8_t ENABLE_SMUXEN = 0b00010000; + constexpr uint8_t ENABLE_WEN = 0b00001000; + constexpr uint8_t ENABLE_SP_EN = 0b00000010; + constexpr uint8_t ENABLE_PON = 0b00000001; + + constexpr uint8_t FD_CFG0_FIFO_WRITE = 0b10000000; + + constexpr uint8_t FD_VALID = 0b00100000; + constexpr uint8_t FD_SAT = 0b00010000; + constexpr uint8_t FD_120HZ_VALID = 0b00001000; + constexpr uint8_t FD_100HZ_VALID = 0b00000100; + constexpr uint8_t FD_120HZ = 0b00000010; + constexpr uint8_t FD_100HZ = 0b00000001; + + constexpr uint8_t INTERNAB_ASIEN = 0b10000000; + constexpr uint8_t INTERNAB_SP_IEN = 0b00001000; + constexpr uint8_t INTERNAB_FIEN = 0b00000100; + constexpr uint8_t INTERNAB_SIEN = 0b00000001; + + constexpr uint8_t CONTROL_SW_RESET = 0b00001000; + constexpr uint8_t CONTROL_SP_MAN_AZ = 0b00000100; + constexpr uint8_t CONTROL_FIFO_CLR = 0b00000010; + constexpr uint8_t CONTROL_CLEAR_SAI_ACT = 0b00000001; + + constexpr uint8_t FIFO_MAP_CH5 = 0b01000000; + constexpr uint8_t FIFO_MAP_CH4 = 0b00100000; + constexpr uint8_t FIFO_MAP_CH3 = 0b00010000; + constexpr uint8_t FIFO_MAP_CH2 = 0b00001000; + constexpr uint8_t FIFO_MAP_CH1 = 0b00000100; + constexpr uint8_t FIFO_MAP_CH0 = 0b00000010; + constexpr uint8_t FIFO_MAP_ASTATUS = 0b00000001; +} \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d32cc949..0c4c522c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(breakout_bme688) add_subdirectory(breakout_bmp280) add_subdirectory(breakout_bme280) add_subdirectory(breakout_as7262) +add_subdirectory(as7343) add_subdirectory(breakout_bh1745) add_subdirectory(breakout_icp10125) add_subdirectory(breakout_scd41) diff --git a/examples/as7343/CMakeLists.txt b/examples/as7343/CMakeLists.txt new file mode 100644 index 00000000..0c32ab65 --- /dev/null +++ b/examples/as7343/CMakeLists.txt @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/as7343_demo.cmake") \ No newline at end of file diff --git a/examples/as7343/as7343_demo.cmake b/examples/as7343/as7343_demo.cmake new file mode 100644 index 00000000..edaf80b3 --- /dev/null +++ b/examples/as7343/as7343_demo.cmake @@ -0,0 +1,16 @@ +set(OUTPUT_NAME as7343_demo) + +add_executable( + ${OUTPUT_NAME} + as7343_demo.cpp +) + +# enable usb output, disable uart output +pico_enable_stdio_usb(${OUTPUT_NAME} 1) +pico_enable_stdio_uart(${OUTPUT_NAME} 1) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib as7343) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/as7343/as7343_demo.cpp b/examples/as7343/as7343_demo.cpp new file mode 100644 index 00000000..8a6a205e --- /dev/null +++ b/examples/as7343/as7343_demo.cpp @@ -0,0 +1,69 @@ +#include "pico/stdlib.h" +#include "common/pimoroni_common.hpp" + +#include "as7343.hpp" + +using namespace pimoroni; + +I2C i2c(6, 7); +AS7343 as7343(&i2c); + +int main() { + stdio_init_all(); + + printf("AS7343 Demo\n"); + as7343.init(); + printf("Init done...\n"); + + uint8_t aux_id; + uint8_t revision_id; + uint8_t hardware_id; + + as7343.get_version(aux_id, revision_id, hardware_id); + + printf("Aux: %d, Rev: %d, HW: %d\n", aux_id, revision_id, hardware_id); + + printf("set_channels\n"); + as7343.set_channels(AS7343::channel_count::EIGHTEEN_CHANNEL); + + printf("set_gain\n"); + as7343.set_gain(1024); + + printf("set_measurement_time\n"); + as7343.set_measurement_time(500); + + printf("set_integration_time\n"); + as7343.set_integration_time(27800); + + printf("set_illumination_current\n"); + as7343.set_illumination_current(4); + + printf("set_illumination_led\n"); + as7343.set_illumination_led(true); + + printf("start...\n"); + while(true) { + + AS7343::reading reading = as7343.read(); + printf("FZ: %d FY: %d FXL: %d NIR: %d F2 %d F3: %d F4: %d F5: %d F1: %d F5: %d F7: %d F8: %d \n", + reading.FZ, + reading.FY, + reading.FXL, + reading.NIR, + + reading.F2, + reading.F3, + reading.F4, + reading.F6, + + reading.F1, + reading.F5, + reading.F7, + reading.F8 + ); + + sleep_ms(1000); + } + + return 0; +} \ No newline at end of file