From 283b625ae13c3c65f89eff145ebaa5abb43d4e23 Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:15:43 -0700 Subject: [PATCH 01/39] Delete --- src/SparkFun_AS7331.h | 1325 ------------------------------------ src/sfe_as7331_registers.h | 274 -------- src/sfe_bus.h | 71 -- src/sfe_i2c.h | 60 -- src/sfe_i2c_arduino.cpp | 302 -------- src/sfe_i2c_arduino.h | 108 --- 6 files changed, 2140 deletions(-) delete mode 100644 src/SparkFun_AS7331.h delete mode 100644 src/sfe_as7331_registers.h delete mode 100644 src/sfe_bus.h delete mode 100644 src/sfe_i2c.h delete mode 100644 src/sfe_i2c_arduino.cpp delete mode 100644 src/sfe_i2c_arduino.h diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h deleted file mode 100644 index 869cc82..0000000 --- a/src/SparkFun_AS7331.h +++ /dev/null @@ -1,1325 +0,0 @@ -/* - SparkFun Spectral UV Sensor - AS7331 - - Qwiic 1x1 - https://www.sparkfun.com/products/ - Qwiic Mini - https://www.sparkfun.com/products/ - - Repository - https://github.com/sparkfun/SparkFun_AS7331_Arduino_Library - - SPDX-License-Identifier: MIT - - Copyright (c) 2023 SparkFun Electronics - - Name: SparkFun_AS7331.h - - Description: - The SfeAS7331ArdI2C is a templated class that defines Arduino specific - behavior for initializing devices. It inherits from the base C++ class - SfeAS7331Base which defines the functions for interacting with the AS7331 - Spectral UV Sensor. - -*/ - -#pragma once - -#include "sfe_as7331_registers.h" - -#include -#include "sfe_i2c_arduino.h" - -/// @brief Measures structure to hold return values. -typedef struct { - float uva; - float uvb; - float uvc; - float temperature; - uint32_t outputConversionTime; -} sfeAS7331_measures_t; - -/// @brief Configuration struct to hold local sensor configuration. -typedef struct { - as7331_gain_t sensorGain; - as7331_conv_clk_freq_t cclk; - as7331_conv_time_t conversionTime; - as7331_ready_pin_mode_t readyPinMode; - as7331_divider_enable_t dividerEnabled; - as7331_divider_val_t dividerRange; - as7331_ext_syn_temp_meas_t enableTempConv; - as7331_simple_reg_read_mode_t indexMode; - uint8_t breakTime; - uint8_t numEdges; -} sfeAS7331_config_t; - -/// @brief State struct to hold local sensor state. -typedef struct { - as7331_power_state_t pd; - as7331_device_op_state_t opMode; - as7331_meas_mode_t mmode; - as7331_standby_mode_t sb; - as7331_startstate_t ss; -} sfeAS7331_state_t; - -/// @brief Default measures struct for initialization and reset. -const sfeAS7331_measures_t measuresDefault = { - .uva = (uint16_t)(-1), - .uvb = (uint16_t)(-1), - .uvc = (uint16_t)(-1), - .temperature = (uint16_t)(-1), - .outputConversionTime = (uint32_t)(-1) - }; - -/// @brief Default configuration struct for initialization and reset. -const sfeAS7331_config_t configDefault = { - .sensorGain = GAIN_2, - .cclk = CCLK_1_024_MHZ, - .conversionTime = TIME_64MS, - .readyPinMode = READYPIN_PUSHPULL, - .dividerEnabled = DIVIDER_DISABLED, - .dividerRange = DIV_2, - .enableTempConv = SYN_TEMP_ENABLED, - .indexMode = INDEX_REPEAT_START, - .breakTime = 25, // 25 * 8us = 200us - .numEdges = 1 - }; - -/// @brief Default state struct for initialization and reset. -const sfeAS7331_state_t stateDefault = { - .pd = POWER_DOWN_ENABLE, - .opMode = DEVICE_MODE_CFG, - .mmode = MEAS_MODE_CMD, - .sb = STANDBY_DISABLED, - .ss = START_STATE_DISABLED - }; - -/// @class SfeAS7331Base -/// @brief Template device class for the AS7331 Spectral UV Sensor. -template -class SfeAS7331Base { - public: - SfeAS7331Base(){ - _state = stateDefault; - - _config = configDefault; - - measures = measuresDefault; - }; - - /// @brief This method is called to initialize the AS7331 device through the specified bus. - /// @param theBus Pointer to the bus object. - /// @param devSettings Pointer to the device settings object. - /// @param deviceAddress I2C address for the device. - /// @return True if successful, false if it fails. - bool begin(sfeBusDevice *theBus, sfeBusDeviceSettings *devSettings, const uint8_t deviceAddress = AS7331_ADDR_DEFAULT) - { - setCommunicationBus(theBus, devSettings); - return begin(deviceAddress); - } - - /// @brief This method is called to initialize the AS7331 device through the specified bus. - /// @param theBus Pointer to the bus object. - /// @param deviceAddress I2C address for the device. - /// @return True if successful, false if it fails. - bool begin(sfeBusDevice *theBus, uint8_t deviceAddress = AS7331_ADDR_DEFAULT) - { - setCommunicationBus(theBus); - return begin(deviceAddress); - } - - /// @brief This method is called to initialize the AS7331 device at the specified bus address. - /// @param deviceAddress I2C address for the device. - /// @return True if successful, false if it fails. - bool begin(uint8_t deviceAddress = AS7331_ADDR_DEFAULT) - { - if(!_sfeBus) - setCommunicationBus(); - - setDeviceAddress(deviceAddress); - - _sfeBus->begin(); - - return init(); - } - - /// @brief Finds the device and initializes the default state. - /// @param runSetup Flag to run the default setup if set. - /// @return True if successful, false if it fails. - bool init(bool runSetup = true) - { - if(SFE_BUS_OK != _sfeBus->ping(_devSettings)) - return false; - - reset(); - - if(!isConnected()) - return false; - - if(runSetup) - return runDefaultSetup(); - - return true; - } - - /// @brief Writes to the data to the given register. - /// @param offset The register to write to. - /// @param data The data to write to the register. - /// @param length The number of writes - /// @return The SFE_BUS status code on the write to the given register. - int8_t writeRegister(const uint8_t offset, const uint8_t *data, const uint16_t length = 1) - { - return _sfeBus->writeRegisterBytes((SFEBusDevSettings*)_devSettings, offset, data, length); - } - - /// @brief Reads data from the specified register. - /// @param offset The register to read from. - /// @param data The pointer to the value to store the value. - /// @param length The number of reads - /// @return The SFE_BUS status code on the read to the given register. - int8_t readRegister(const uint8_t offset, uint8_t *data, const uint16_t length = 1) - { - return _sfeBus->readRegisterBytes((SFEBusDevSettings*)_devSettings, offset, data, length); - } - - /// @brief Checks to see if the AS7331 is connected. - /// @return True if successful, false otherwise. - bool isConnected(void) - { - return (AS7331_DEFAULT_DEV_ID == getDeviceID()); - } - - /// @brief Requests the device ID from the sensor. - /// @return The device ID of the sensor.` - uint8_t getDeviceID(void) - { - uint8_t devID; - - if(SFE_BUS_OK != readRegister(SFE_AS7331_REGISTER_CFG_AGEN, &devID)) - return 0; - - return devID; - } - - /// @brief Helper class that sets up the sensor and state in the POR configuration. - /// @param runSoftReset Flag that runs the soft reset function to reset the device. - /// @return True if successful, false otherwise. - bool runDefaultSetup(bool runSoftReset = false) - { - if(runSoftReset) - return reset(); - else - { - _state = stateDefault; - _config = configDefault; - measures = measuresDefault; - } - - sfe_as7331_reg_cfg_osr_t osr; - if(SFE_BUS_OK != getOSR(&osr)) - return false; - - uint8_t regs[6]; - if(SFE_BUS_OK != readRegister(SFE_AS7331_REGISTER_CFG_CREG1, regs, 6U)) - return false; - - sfe_as7331_reg_cfg_creg1_t creg1 = {.byte = regs[0]}; - sfe_as7331_reg_cfg_creg2_t creg2 = {.byte = regs[1]}; - sfe_as7331_reg_cfg_creg3_t creg3 = {.byte = regs[2]}; - sfe_as7331_reg_cfg_break_t breakreg = regs[3]; - sfe_as7331_reg_cfg_edges_t edgesreg = regs[4]; - sfe_as7331_reg_cfg_optreg_t optreg = {.byte = regs[5]}; - - osr.ss = _state.ss; - osr.pd = _state.pd; - osr.dos = _state.opMode; - - creg1.gain = _config.sensorGain; - creg1.time = _config.conversionTime; - - creg2.en_tm = _config.enableTempConv; - creg2.en_div = _config.dividerEnabled; - creg2.div = _config.dividerRange; - - creg3.mmode = _state.mmode; - creg3.sb = _state.sb; - creg3.rdyod = _config.readyPinMode; - creg3.cclk = _config.cclk; - - breakreg = _config.breakTime; - - edgesreg = _config.numEdges; - - optreg.init_idx = _config.indexMode; - - if(SFE_BUS_OK != setOSR(&osr)) - return false; - - regs[0] = creg1.byte; - regs[1] = creg2.byte; - regs[2] = creg3.byte; - regs[3] = breakreg; - regs[4] = edgesreg; - regs[5] = optreg.byte; - - if(SFE_BUS_OK != writeRegister(SFE_AS7331_REGISTER_CFG_CREG1, regs, 6U)) - return false; - - calculateConversionFactors(); - - return true; - } - - /// @brief Puts the sensor in the specified measurement mode. - /// @param measMode Measurement mode to enter. - /// @param startMeasure Flag to start measuring immediately if set. - /// @return True if successful, false otherwise. - bool startMeasurement(const as7331_meas_mode_t measMode = MEAS_MODE_CONT, bool startMeasure = false) - { - if(_state.pd == POWER_DOWN_ENABLE) - if(SFE_BUS_OK != setPowerState(POWER_DOWN_DISABLE)) return false; - - if(_state.mmode != measMode) - { - if(SFE_BUS_OK != setStandbyMode(STANDBY_DISABLED)) - return false; - if(SFE_BUS_OK != setMeasurementMode(measMode)) - return false; - } - - if(_state.opMode != DEVICE_MODE_MEAS) - if(SFE_BUS_OK != setOperationMode(DEVICE_MODE_MEAS)) return false; - - if(startMeasure) - if(SFE_BUS_OK != setStartStateMode(START_STATE_ENABLED)) return false; - - return true; - } - - /// @brief Sets the communication bus to the specified bus and device settings. - /// @param theBus Bus to set as the communication device. - /// @param deviceSettings Device settings to use when setting the bus for this sensor. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCommunicationBus(sfeBusDevice *theBus, sfeBusDeviceSettings *deviceSettings) - { - int8_t result = setCommunicationBus(theBus); - if(SFE_BUS_OK != result) - return result; - - return setCommunicationDevSettings(deviceSettings); - } - - /// @brief Sets teh communication bus to the specified bus. Creates new instance if none specified. - /// @param theBus Bus to set as the communication devie. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCommunicationBus(sfeBusDevice *theBus = nullptr) - { - if(theBus == nullptr) - theBus = new sfeBusDevice(); - - if(!theBus) - return SFE_BUS_E_NULL_PTR; - - _sfeBus = theBus; - - return SFE_BUS_OK; - } - - /// @brief Sets settings of this sensor's device. - /// @param deviceSettings Settings structure for this device. Creates new instance if none specified. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCommunicationDevSettings(sfeBusDeviceSettings *deviceSettings = nullptr) - { - if(deviceSettings == nullptr) - deviceSettings = new sfeBusDeviceSettings(); - - if(!deviceSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - _devSettings = deviceSettings; - - return SFE_BUS_OK; - } - - /// @brief Sets the address that the bus uses to communicate with the sensor. - /// @param deviceAddress Device address to use. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setDeviceAddress(const uint8_t deviceAddress) - { - int8_t result = SFE_BUS_OK; - - if(!_devSettings) - result = setCommunicationDevSettings(); - - if(SFE_BUS_OK != result) - return result; - - _devSettings->devAddr = deviceAddress; - - return SFE_BUS_OK; - } - - /// @brief Performs a soft reset of the sensor. - /// @return True if successful, false otherwise. - bool reset(void) - { - sfe_as7331_reg_cfg_osr_t osr; - - - if(SFE_BUS_OK != getOSR(&osr)) - return false; - - osr.sw_res = 1; - - if(SFE_BUS_OK != setOSR(&osr)) - return false; - - _state = stateDefault; - _config = configDefault; - measures = measuresDefault; - - return true; - } - - /// @brief Reads the sensor's temperature, converts it to a usable form, and saves it to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readTemp(void) - { - uint8_t tempRaw[2]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_TEMP, tempRaw, 2U); - - if(SFE_BUS_OK != result) - return result; - - measures.temperature = convertRawTempToTempC((uint16_t)(((uint16_t)tempRaw[1] << 8 | tempRaw[0]))); - - return SFE_BUS_OK; - } - - /// @brief Reads the sensor's UVA register, converts it to a usable form, and saves it to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readUVA(void) - { - uint8_t uvaRaw[2]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES1, uvaRaw, 2U); - - if(SFE_BUS_OK != result) - return result; - - if(_state.mmode == MEAS_MODE_SYND) { - result = readOutConv(); - - if(SFE_BUS_OK != result) - return result; - - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); - measures.uva = (float)((uint16_t)(((uint16_t)uvaRaw[1]) << 8 | uvaRaw[0])-1.0f)*fsrA*divFactor/convFactor; - } - else { - measures.uva = (float)((uint16_t)(((uint16_t)uvaRaw[1]) << 8 | uvaRaw[0])-1.0f)*_conversionA; - } - - return SFE_BUS_OK; - } - - /// @brief Reads the sensor's UVB register, converts it to a usable form, and saves it to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readUVB(void) - { - uint8_t uvbRaw[2]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES2, uvbRaw, 2U); - - if(SFE_BUS_OK != result) - return result; - - if(_state.mmode == MEAS_MODE_SYND) { - result = readOutConv(); - - if(SFE_BUS_OK != result) - return result; - - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); - measures.uvb = (float)((uint16_t)(((uint16_t)uvbRaw[1]) << 8 | uvbRaw[0])-1.0f)*fsrB*divFactor/convFactor; - } - else { - measures.uvb = (float)((uint16_t)(((uint16_t)uvbRaw[1]) << 8 | uvbRaw[0])-1.0f)*_conversionB; - } - - return SFE_BUS_OK; - } - - /// @brief Reads the sensor's UVC register, converts it to a usable form, and saves it to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readUVC(void) - { - uint8_t uvcRaw[2]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES3, uvcRaw, 2U); - - if(SFE_BUS_OK != result) - return result; - - if(_state.mmode == MEAS_MODE_SYND) { - result = readOutConv(); - - if(SFE_BUS_OK != result) - return result; - - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); - measures.uvc = (float)((uint16_t)(((uint16_t)uvcRaw[1]) << 8 | uvcRaw[0])-1.0f)*fsrC*divFactor/convFactor; - } - else { - measures.uvc = (float)((uint16_t)(((uint16_t)uvcRaw[1]) << 8 | uvcRaw[0])-1.0f)*_conversionC; - } - - return SFE_BUS_OK; - } - - /// @brief Read's all three UV registers, converts them to a usable form, then saves them to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readAllUV(void) - { - uint8_t dataRaw[6]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES1, dataRaw, 6U); - - if(SFE_BUS_OK != result) - return result; - - if(_state.mmode == MEAS_MODE_SYND) { - result = readOutConv(); - - if(SFE_BUS_OK != result) - return result; - - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); - measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[1]) << 8 | dataRaw[0])-1.0f)*fsrA*divFactor/convFactor; - measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*fsrB*divFactor/convFactor; - measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*fsrC*divFactor/convFactor; - } - else { - measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[1]) << 8 | dataRaw[0])-1.0f)*_conversionA; - measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*_conversionB; - measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*_conversionC; - } - - return SFE_BUS_OK; - } - - /// @brief Read the sensor's temperature, UV, and external time conversion clock counts, converts them, and then saves them to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readAll(void) - { - uint8_t dataRaw[8]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_TEMP, dataRaw, 8U); - - if(SFE_BUS_OK != result) - return result; - - result = readOutConv(); - if(SFE_BUS_OK != result) - return result; - - if(_state.mmode == MEAS_MODE_SYND) { - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); - measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*fsrA*divFactor/convFactor; - measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*fsrB*divFactor/convFactor; - measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[7]) << 8 | dataRaw[6])-1.0f)*fsrC*divFactor/convFactor; - } - else { - measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*_conversionA; - measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*_conversionB; - measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[7]) << 8 | dataRaw[6])-1.0f)*_conversionC; - } - - measures.temperature = convertRawTempToTempC((uint16_t)(((uint16_t)dataRaw[1] << 8 | dataRaw[0]))); - - return SFE_BUS_OK; - } - - /// @brief Read the conversion clock counts register and saves it to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readOutConv(void) - { - uint8_t tconvRaw[4]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_OUTCONV_L, tconvRaw, 4U); - - if(SFE_BUS_OK != result) - return result; - - measures.outputConversionTime = (uint32_t)(((uint32_t)tconvRaw[3] << 24) | ((uint32_t)tconvRaw[2] << 16) | ((uint32_t)tconvRaw[1] << 8) | tconvRaw[0]); - - return SFE_BUS_OK; - } - - /// @brief Getter function to get the current internal state. - /// @return internal state struct. - sfeAS7331_state_t getState(void) - { - return _state; - } - - /// @brief Getter function to get the current internal configuration. - /// @return internal configuration struct. - sfeAS7331_config_t getConfig(void) - { - return _config; - } - - /// @brief Sets the UV sensor's gain. - /// @param gain The gain to set the sensor to. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setGain(const as7331_gain_t gain) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg1_t creg1; - - result = getCReg1(&creg1); - if(SFE_BUS_OK != result) - return result; - - creg1.gain = gain; - - result = setCReg1(&creg1); - if(SFE_BUS_OK != result) - return result; - - _config.sensorGain = gain; - - calculateConversionFactors(); - - return SFE_BUS_OK; - } - - /// @brief Sets the conversion time that the sensor will run to. - /// @param convTime Conversion time to set the sensor to. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setConversionTime(const as7331_conv_time_t convTime) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg1_t creg1; - - result = getCReg1(&creg1); - if(SFE_BUS_OK != result) - return result; - - creg1.time = convTime; - - result = setCReg1(&creg1); - if(SFE_BUS_OK != result) - return result; - - _config.conversionTime = convTime; - - calculateConversionFactors(); - - return SFE_BUS_OK; - } - - /// @brief Set the sensor's internal clock speed. - /// @param cclk Clock speed to set on the sensor. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCClk(const as7331_conv_clk_freq_t cclk) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg3_t creg3; - - result = getCReg1(&creg3); - if(SFE_BUS_OK != result) - return result; - - creg3.cclk = cclk; - - result = setCReg1(&creg3); - if(SFE_BUS_OK != result) - return result; - - _config.cclk = cclk; - - calculateConversionFactors(); - - return SFE_BUS_OK; - } - - /// @brief Enables or disables the internal UV result divider. - /// @param isEnabled Enable or disable the divder. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setEnableDivider(const as7331_divider_enable_t isEnabled) - { - if(_config.dividerEnabled == isEnabled) - return SFE_BUS_OK; - - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg2_t creg2; - - result = getCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - creg2.en_div = isEnabled; - - result = setCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - _config.dividerEnabled = isEnabled; - - if(isEnabled == DIVIDER_ENABLED) - calculateConversionFactors(); - - return SFE_BUS_OK; - } - - /// @brief Sets the value of the internal UV result divider. - /// @param divider Divider value to set. - /// @param setEnableDiv Option to turn on the divider if desired. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setDigitalDivider(const as7331_divider_val_t divider, const bool setEnableDiv = false) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg2_t creg2; - - result = getCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - creg2.div = divider; - - result = setCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - _config.dividerRange = divider; - - if(setEnableDiv) - result = setEnableDivider(DIVIDER_ENABLED); - else - calculateConversionFactors(); - - return SFE_BUS_OK; - } - - /// @brief Sets the ready pin type to push-pull or open-drain. - /// @param pinMode Mode to set the ready pin to. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setReadyPinMode(const as7331_ready_pin_mode_t pinMode) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg3_t creg3; - - result = getCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - creg3.rdyod = pinMode; - - result = setCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - _config.readyPinMode = pinMode; - - return SFE_BUS_OK; - } - - /// @brief Enables or disables the temperature conversion when in the SYND mode. - /// @param isEnabled Enable or disable the feature. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setEnableTemperatureConversion(const as7331_ext_syn_temp_meas_t isEnabled) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg2_t creg2; - - result = getCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - creg2.en_tm = isEnabled; - - result = setCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - _config.enableTempConv = isEnabled; - - return SFE_BUS_OK; - } - - /// @brief Set the index mode for compatibility with I2C controllers that don't support repeated start. - /// @param indexMode Simple or standard I2C addressing mode. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setIndexMode(const as7331_simple_reg_read_mode_t indexMode) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_optreg_t optreg; - - result = getOptIndex(&optreg); - if(SFE_BUS_OK != result) - return result; - - optreg.init_idx = indexMode; - - result = setOptIndex(&optreg); - if(SFE_BUS_OK != result) - return result; - - _config.indexMode = indexMode; - - return SFE_BUS_OK; - } - - /// @brief Set the minimum break time between measurements in CONT, SYNS, and SYND modes. - /// @param breakTime Time between measurements, 8us step time, max 2048us. A 0 value is a minimum of 3 fclk cycles. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setBreakTime(const uint8_t breakTime) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_break_t breakreg; - - result = getBreak(&breakreg); - if(SFE_BUS_OK != result) - return result; - - breakreg = breakTime; - - result = setBreak(&breakreg); - if(SFE_BUS_OK != result) - return result; - - _config.breakTime = breakTime; - - return SFE_BUS_OK; - } - - /// @brief Set the minimum number of falling edges required at the SYN input until the conversion is terminated. Only operational in SYND mode. - /// @param numEdges Number of edges prior to terminating conversion in SYND mode. 0 is not allowed, 1 is the minimum. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setNumEdges(const uint8_t numEdges) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_edges_t edgesreg; - - result = getEdges(&edgesreg); - if(SFE_BUS_OK != result) - return result; - - edgesreg = numEdges; - - result = setEdges(&edgesreg); - if(SFE_BUS_OK != result) - return result; - - _config.numEdges = numEdges; - - return SFE_BUS_OK; - } - - /// @brief Getter for the currently configured gain. - /// @return Sensor's gain expressed as (1 << (11 - gain)). - as7331_gain_t getConfigGain(void) - { - return _config.sensorGain; - } - - /// @brief Getter for the currently configured conversion clock. - /// @return Sensor's conversion clock expressed as 1024*(1 << cclk). - as7331_conv_clk_freq_t getConfigCClk(void) - { - return _config.cclk; - } - - /// @brief Getter for the currently configured conversion time. - /// @return Sensor's conversion time expressed as (1 << time). - as7331_conv_time_t getConfigConversionTime(void) - { - return _config.conversionTime; - } - - /// @brief Getter for the currently configured pin mode. - /// @return Sensor's ready pin configuration. - as7331_ready_pin_mode_t getConfigReadyPinMode(void) - { - return _config.readyPinMode; - } - - /// @brief Getter for the currently configured divider status. - /// @return Whether the sensor's divider is enabled or disabled. - as7331_divider_enable_t getConfigDividerEnabled(void) - { - return _config.dividerEnabled; - } - - /// @brief Getter for the currently configured divider range. - /// @return Sensor's internal UV predivider range. - as7331_divider_val_t getConfigDividerRange(void) - { - return _config.dividerRange; - } - - /// @brief Getter for the currently configured SYND temperature conversion status. - /// @return Whether the sensor will output temperature in SYND mode. - as7331_ext_syn_temp_meas_t getConfigExternalSyncTempConversion(void) - { - return _config.enableTempConv; - } - - /// @brief Getter for the currently configured I2C compatibility mode. - /// @return Sensor's configuration for interacting with simpler I2C controllers. - as7331_simple_reg_read_mode_t getConfigIndexMode(void) - { - return _config.indexMode; - } - - /// @brief Getter for the currently configured minimum break time in CONT, CMD, SYNS modes. - /// @return Sensor's breaktime in 8us steps. - uint8_t getConfigBreakTime(void) - { - return _config.breakTime; - } - - /// @brief Getter for the currently configured minimum number of edges to end conversion when in SYND mode. - /// @return Sensor's minimum number of edges, minimum 1 edge. - uint8_t getConfigNumEdges(void) - { - return _config.numEdges; - } - - /// @brief Gets the current power state from the sensor. - /// @param pd Pointer to an enum to store the current power state. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getPowerState(as7331_power_state_t *pd) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_osr_t osr; - - result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.pd = (as7331_power_state_t)osr.pd; - *pd = _state.pd; - - return SFE_BUS_OK; - } - - /// @brief Sets the power state of the sensor. - /// @param pd Power state to set. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setPowerState(const as7331_power_state_t pd = POWER_DOWN_DISABLE) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_osr_t osr; - - result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - osr.pd = pd; - - result = setOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.pd = pd; - - return SFE_BUS_OK; - } - - /// @brief Gets the current operation mode from the sensor. - /// @param opMode Pointer to an enum to store the current operating mode. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getOperationMode(as7331_device_op_state_t *opMode) - { - sfe_as7331_reg_cfg_osr_t osr; - int8_t result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.opMode = (as7331_device_op_state_t)osr.dos; - *opMode = _state.opMode; - - return SFE_BUS_OK; - } - - /// @brief Set the sensor's operating mode. - /// @param opMode Operating mode to set. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setOperationMode(const as7331_device_op_state_t opMode) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_osr_t osr; - - result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - osr.dos = opMode; - - result = setOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.opMode = opMode; - - return SFE_BUS_OK; - } - - /// @brief Gets the sensor's current measurement mode. - /// @param measMode Pointer to an enum to store the current measurment mode. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getMeasurementMode(as7331_meas_mode_t *measMode) - { - sfe_as7331_reg_cfg_creg3_t creg3; - int8_t result = getCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - _state.mmode = (as7331_meas_mode_t)creg3.mmode; - *measMode = _state.mmode; - - return SFE_BUS_OK; - } - - /// @brief Sets the sensor's measurement mode. - /// @param measMode Measurement mode to set. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setMeasurementMode(const as7331_meas_mode_t measMode) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg3_t creg3; - - result = getCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - creg3.mmode = measMode; - - result = setCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - _state.mmode = measMode; - - return SFE_BUS_OK; - } - - /// @brief Gets the sensor's current standby mode state. - /// @param standby Pointer to an enum to store the current standby state. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getStandbyMode(as7331_standby_mode_t *standby) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg3_t creg3; - - result = getCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - _state.sb = (as7331_standby_mode_t)creg3.sb; - *standby = _state.sb; - - return SFE_BUS_OK; - } - - /// @brief Sets the sensor's standby mode. - /// @param standby State to set. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setStandbyMode(const as7331_standby_mode_t standby) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg3_t creg3; - - result = getCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - creg3.sb = standby; - - result = setCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - _state.sb = standby; - - return SFE_BUS_OK; - } - - /// @brief Gets the sensor's current start state. - /// @param startState Pointer to an enum to store the current start state. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getStartStateMode(as7331_startstate_t *startState) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_osr_t osr; - - result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.ss = (as7331_startstate_t)osr.ss; - *startState = _state.ss; - - return SFE_BUS_OK; - } - - /// @brief Sets the sensor's start state. This begins measurement. - /// @param startState Start state to set. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setStartStateMode(const as7331_startstate_t startState) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_osr_t osr; - - result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - osr.ss = startState; - - result = setOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.ss = startState; - - return SFE_BUS_OK; - } - - /// @brief Getter for the current power state. - /// @return Sensor's power state. - as7331_power_state_t getStatePower(void) - { - return _state.pd; - } - - /// @brief Getter for the current operational state. - /// @return Sensor's operational state. - as7331_device_op_state_t getStateOperational(void) - { - return _state.opMode; - } - - /// @brief Getter for the current measurement state. - /// @return Sensor's measurement state. - as7331_meas_mode_t getStateMeasurement(void) - { - return _state.mmode; - } - - /// @brief Getter for the current standby state. - /// @return Sensor's standby state. - as7331_standby_mode_t getStateStandby(void) - { - return _state.sb; - } - - /// @brief Getter for the current start state. - /// @return Sensor's start state. - as7331_startstate_t getStateStart(void) - { - return _state.ss; - } - - /// @brief Gets the sensor's status when in measurement operation mode. - /// @param statusReg Pointer to a register struct to store the sensor's current status. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getStatus(sfe_as7331_reg_meas_osr_status_t *statusReg) - { - int8_t result = SFE_BUS_OK; - uint8_t statusRaw[2]; - - result = readRegister(SFE_AS7331_REGISTER_MEAS_OSR_STATUS, statusRaw, 2U); - - if(SFE_BUS_OK != result) - return result; - - statusReg->word = ((uint16_t)statusRaw[1] << 8) | statusRaw[0]; - - return SFE_BUS_OK; - } - - /// @brief Gets the operational state register when in configuration operation mode. - /// @param osrReg Pointer to a register struct to store the sensor's current OSR register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getOSR(sfe_as7331_reg_cfg_osr_t *osrReg) - { - return readRegister(SFE_AS7331_REGISTER_CFG_OSR, &osrReg->byte); - } - - /// @brief Sets the operational state register when in configuration operation mode. - /// @param osrReg Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setOSR(const sfe_as7331_reg_cfg_osr_t *osrReg) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_OSR, &osrReg->byte); - } - - /// @brief Gets the configuration register #1 when in configuration operation mode. - /// @param creg1 Pointer to a register struct to store the sensor's current creg1 register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getCReg1(sfe_as7331_reg_cfg_creg1_t *creg1) - { - return readRegister(SFE_AS7331_REGISTER_CFG_CREG1, &creg1->byte); - } - - /// @brief Sets the configuration register #1 when in configuration operation mode. - /// @param creg1 Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCReg1(const sfe_as7331_reg_cfg_creg1_t *creg1) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_CREG1, &creg1->byte); - } - - /// @brief Gets the configuration register #2 when in configuration operation mode. - /// @param creg2 Pointer to a register struct to store the sensor's current creg2 register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getCReg2(sfe_as7331_reg_cfg_creg2_t *creg2) - { - return readRegister(SFE_AS7331_REGISTER_CFG_CREG2, &creg2->byte); - } - - /// @brief Sets the configuration register #2 when in configuration operation mode. - /// @param creg2 Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCReg2(const sfe_as7331_reg_cfg_creg2_t *creg2) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_CREG2, &creg2->byte); - } - - /// @brief Gets the configuration register #3 when in configuration operation mode. - /// @param creg3 Pointer to a register struct to store the sensor's current creg3 register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getCReg3(sfe_as7331_reg_cfg_creg3_t *creg3) - { - return readRegister(SFE_AS7331_REGISTER_CFG_CREG3, &creg3->byte); - } - - /// @brief Sets the configuration register #3 when in configuration operation mode. - /// @param creg3 Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCReg3(const sfe_as7331_reg_cfg_creg3_t *creg3) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_CREG3, &creg3->byte); - } - - /// @brief Gets the break register when in configuration operation mode. - /// @param breakReg Pointer to a register struct to store the sensor's current break register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getBreak(sfe_as7331_reg_cfg_break_t *breakReg) - { - return readRegister(SFE_AS7331_REGISTER_CFG_BREAK, breakReg); - } - - /// @brief Sets the break register when in configuration operation mode. - /// @param breakReg Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setBreak(const sfe_as7331_reg_cfg_break_t *breakReg) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_BREAK, breakReg); - } - - /// @brief Gets the edges register when in configuration operation mode. - /// @param edgesReg Pointer to a register struct to store the sensor's current edges register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getEdges(sfe_as7331_reg_cfg_edges_t *edgesReg) - { - return readRegister(SFE_AS7331_REGISTER_CFG_EDGES, edgesReg); - } - - - /// @brief Sets the edges register when in configuration operation mode. - /// @param edgesReg Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setEdges(const sfe_as7331_reg_cfg_edges_t *edgesReg) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_EDGES, edgesReg); - } - - /// @brief Gets the option register when in configuration operation mode. - /// @param optReg Pointer to a register struct to store the sensor's current option register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getOptIndex(sfe_as7331_reg_cfg_optreg_t *optReg) - { - return readRegister(SFE_AS7331_REGISTER_CFG_OPTREG, &optReg->byte); - } - - /// @brief Sets the option register when in configuration operation mode. - /// @param optReg Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setOptIndex(const sfe_as7331_reg_cfg_optreg_t *optReg) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_OPTREG, &optReg->byte); - } - - sfeAS7331_measures_t measures; - - private: - /// @brief Converts the raw temperature value to a human readable form. - /// @param inputVal Raw temperature value to convert. - /// @return The converted device temperature in degree C. - float convertRawTempToTempC(const uint16_t inputVal) - { - // T_chip = TEMP*0.05 - 66.9 - // EX: TEMP=0x922 aka TEMP=0d2338, returns 50.0 - return ((float)(inputVal) * 0.05f) - 66.9f; - } - - /// @brief Called when changing values that affect the conversion, calculates a new conversion factor to reduce the conversion overhead. - void calculateConversionFactors(void) - { - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - _conversionA = fsrA/((float)(1 << (11 - _config.sensorGain)))/((float)(1 << _config.conversionTime))/((float)(1024.0 * (1 << _config.cclk)))*divFactor; - _conversionB = fsrB/((float)(1 << (11 - _config.sensorGain)))/((float)(1 << _config.conversionTime))/((float)(1024.0 * (1 << _config.cclk)))*divFactor; - _conversionC = fsrC/((float)(1 << (11 - _config.sensorGain)))/((float)(1 << _config.conversionTime))/((float)(1024.0 * (1 << _config.cclk)))*divFactor; - } - - sfeBusDevice *_sfeBus; // Pointer to bus device. - sfeBusDeviceSettings *_devSettings; // Pointer to the device settings for this sensor. - - sfeAS7331_config_t _config; // Internal sensor configuration. - sfeAS7331_state_t _state; // Internal sensor state. - - float _conversionA; // Internal conversion factor for the UVA sensor. - float _conversionB; // Internal conversion factor for the UVB sensor. - float _conversionC; // Internal conversion factor for the UVC sensor. - - static constexpr float fsrA = 348160.0; // Full Scale Resolution for the UVA sensor. - static constexpr float fsrB = 387072.0; // Full Scale Resolution for the UVB sensor. - static constexpr float fsrC = 169984.0; // Full Scale Resolution for the UVC sensor. - -}; - -/// @class SfeAS7331ArdI2C -/// @brief Arduino I2C implementation of the SfeAS7331Base class for the AS7331 Spectral UV Sensor. -class SfeAS7331ArdI2C : public SfeAS7331Base -{ - /* Nothing to see here, see above. */ -}; diff --git a/src/sfe_as7331_registers.h b/src/sfe_as7331_registers.h deleted file mode 100644 index 797c948..0000000 --- a/src/sfe_as7331_registers.h +++ /dev/null @@ -1,274 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Copyright (c) 2023 SparkFun Electronics - -*/ - -#pragma once -#include - -/// I2C addresses -// 7-bit address defined as [1, 1, 1, 0, 1, A1, A0] where A1/A0 are the address pins tied high or low -#define AS7331_ADDR_DEFAULT 0x74 -#define AS7331_ADDR_SEC 0x75 -#define AS7331_ADDR_TER 0x76 -#define AS7331_ADDR_QUA 0x77 - -#define AS7331_DEFAULT_DEV_ID 0x21 -#define AS7331_DEV_ID_HIGH 0x2 - - -/// Enum Settings - -typedef enum { - // 0b00X invalid - DEVICE_MODE_CFG = 0x2, - DEVICE_MODE_MEAS = 0x3 - // 0b1XX invalid -} as7331_device_op_state_t; - -typedef enum { - POWER_DOWN_DISABLE = 0x0, - POWER_DOWN_ENABLE -} as7331_power_state_t; - -typedef enum { - START_STATE_DISABLED = 0x0, - START_STATE_ENABLED -} as7331_startstate_t; - -typedef enum { - GAIN_2048 = 0x0000, - GAIN_1024, - GAIN_512, - GAIN_256, - GAIN_128, - GAIN_64, - GAIN_32, - GAIN_16, - GAIN_8, - GAIN_4, - GAIN_2, - GAIN_1 -} as7331_gain_t; - -typedef enum { - SYN_TEMP_DISABLED = 0x0, - SYN_TEMP_ENABLED -} as7331_ext_syn_temp_meas_t; - -typedef enum { - CCLK_1_024_MHZ = 0x00, - CCLK_2_048_MHZ, - CCLK_4_096_MHZ, - CCLK_8_192_MHZ -} as7331_conv_clk_freq_t; - -typedef enum { - READYPIN_PUSHPULL = 0x0, - READYPIN_OPENDRAIN -} as7331_ready_pin_mode_t; - -typedef enum { - STANDBY_DISABLED = 0x0, - STANDBY_ENABLED -} as7331_standby_mode_t; - -typedef enum { - MEAS_MODE_CONT = 0x0, - MEAS_MODE_CMD, - MEAS_MODE_SYNS, - MEAS_MODE_SYND -} as7331_meas_mode_t; - -typedef enum { - INDEX_NO_REPEAT_START = 0x0, - INDEX_REPEAT_START -} as7331_simple_reg_read_mode_t; - -typedef enum { - TIME_1MS = 0x0, - TIME_2MS, - TIME_4MS, - TIME_8MS, - TIME_16MS, - TIME_32MS, - TIME_64MS, - TIME_128MS, - TIME_256MS, - TIME_512MS, - TIME_1024MS, - TIME_2048MS, - TIME_4096MS, - TIME_8192MS, - TIME_16384MS -} as7331_conv_time_t; - -typedef enum { - DIVIDER_DISABLED = 0x0, - DIVIDER_ENABLED = 0x1 -} as7331_divider_enable_t; - -typedef enum { - DIV_2 = 0x0, - DIV_4, - DIV_8, - DIV_16, - DIV_32, - DIV_64, - DIV_128, - DIV_256 -} as7331_divider_val_t; - -/// Configuration Registers -// These registers can only be accessed in the configuration state, and they -// are all 8 bits long. - -#define SFE_AS7331_REGISTER_CFG_OSR 0x00 -typedef union { - struct { - uint8_t dos : 3; - uint8_t sw_res : 1; - uint8_t reserved : 2; - uint8_t pd : 1; - uint8_t ss : 1; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_osr_t; - -#define SFE_AS7331_REGISTER_CFG_AGEN 0x02 -typedef union { - struct { - uint8_t mut : 4; - uint8_t devid : 4; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_agen_t; - -#define SFE_AS7331_REGISTER_CFG_CREG1 0x06 -typedef union { - struct { - uint8_t time : 4; - uint8_t gain : 4; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_creg1_t; - -#define SFE_AS7331_REGISTER_CFG_CREG2 0x07 -typedef union { - struct { - uint8_t div : 3; - uint8_t en_div : 1; - uint8_t reserved : 2; - uint8_t en_tm : 1; - uint8_t reserved1 : 1; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_creg2_t; - -#define SFE_AS7331_REGISTER_CFG_CREG3 0x08 -typedef union { - struct { - uint8_t cclk : 2; - uint8_t reserved : 1; - uint8_t rdyod : 1; - uint8_t sb : 1; - uint8_t reserved1 : 1; - uint8_t mmode : 2; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_creg3_t; - -#define SFE_AS7331_REGISTER_CFG_BREAK 0x09 -typedef uint8_t sfe_as7331_reg_cfg_break_t; - -#define SFE_AS7331_REGISTER_CFG_EDGES 0x0A -typedef uint8_t sfe_as7331_reg_cfg_edges_t; - -#define SFE_AS7331_REGISTER_CFG_OPTREG 0x0B -typedef union { - struct { - uint8_t init_idx : 1; - uint8_t reserved : 7; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_optreg_t; - - -/// Measurement Registers -// These registers can only be accessed in the measurement state. They are -// read-only and 16 bits long, except OUTCONV, which is 24 bits long. - -#define SFE_AS7331_REGISTER_MEAS_OSR_STATUS 0x00 -typedef union { - struct { - sfe_as7331_reg_cfg_osr_t osr; - uint8_t powerstate : 1; - uint8_t standbystate : 1; - uint8_t notready : 1; - uint8_t ndata : 1; - uint8_t ldata : 1; - uint8_t adcof : 1; - uint8_t mresof : 1; - uint8_t outconvof : 1; - }; - uint16_t word; -} sfe_as7331_reg_meas_osr_status_t; - -#define SFE_AS7331_REGISTER_MEAS_TEMP 0x01 -typedef union { - struct { - uint16_t temp : 12; - uint8_t zeros : 4; - }; - uint16_t word; -} sfe_as7331_reg_meas_temp_t; - -#define SFE_AS7331_REGISTER_MEAS_MRES1 0x02 -typedef union { - struct { - uint8_t res_l; - uint8_t res_h; - }; - uint16_t word; -} sfe_as7331_reg_meas_mres1_t; - -#define SFE_AS7331_REGISTER_MEAS_MRES2 0x03 -typedef union { - struct { - uint8_t res_l; - uint8_t res_h; - }; - uint16_t word; -} sfe_as7331_reg_meas_mres2_t; - - -#define SFE_AS7331_REGISTER_MEAS_MRES3 0x04 -typedef union { - struct { - uint8_t res_l; - uint8_t res_h; - }; - uint16_t word; -} sfe_as7331_reg_meas_mres3_t; - - -#define SFE_AS7331_REGISTER_MEAS_OUTCONV_L 0x05 -typedef union { - struct { - uint8_t outconv_l; - uint8_t outconv_m; - }; - uint16_t word; -} sfe_as7331_reg_meas_outconv_l_t; - - -#define SFE_AS7331_REGISTER_MEAS_OUTCONV_H 0x06 -typedef union { - struct { - uint8_t outconv_h; - uint8_t zeros; - }; - uint16_t word; -} sfe_as7331_reg_meas_outconv_h_t; diff --git a/src/sfe_bus.h b/src/sfe_bus.h deleted file mode 100644 index 0bff2a7..0000000 --- a/src/sfe_bus.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Copyright (c) 2023 SparkFun Electronics - - A pure virtual base class for implementing a common communication interface - in SparkFun products. -*/ - -#pragma once - -#include - -// Error and warning codes -#define SFE_BUS_OK 0 -#define SFE_BUS_E_UNKNOWN -1 -#define SFE_BUS_E_NULL_PTR -2 -#define SFE_BUS_E_TIMEOUT -3 -#define SFE_BUS_E_NO_RESPONSE -4 -#define SFE_BUS_E_DATA_TOO_LONG -5 -#define SFE_BUS_E_NULL_DEV_SETTINGS -6 -#define SFE_BUS_E_NULL_DATA_BUFFER -7 -#define SFE_BUS_W_UNKNOWN 1 -#define SFE_BUS_W_UNDER_READ 2 -#define SFE_BUS_W_NOT_ENABLED 3 - -/// @brief An abstract Bus address class for enabling multiple types of addresses. -class SFEBusDevSettings{}; // Nothing to see here... - -/// @brief An abstract interface for a communication bus -class SFEBus -{ - public: - /// @brief Begin bus. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t begin(void) = 0; - - /// @brief End bus. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t end(void) = 0; - - /// @brief Writes a number of bytes starting at the given register address. - /// @param devSettings Settings of the device. - /// @param regAddr The first register address to write to. - /// @param data Data buffer to write to registers. - /// @param numBytes Number of bytes to write. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, const uint8_t *data, const uint32_t numBytes) = 0; - - /// @brief Reads a number of bytes starting at the given register address. - /// @param devSettings Settings of the device. - /// @param regAddr The first register address to read from. - /// @param data Data buffer to read from registers. - /// @param numBytes Number of bytes to read. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, const uint32_t numBytes) = 0; - - /// @brief Writes a number of bytes to a device that doesn't use registers for communications. - /// @param devSettings Settings of the device. - /// @param data Data buffer to write from registers. - /// @param numBytes Number of bytes to write. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, const uint32_t numBytes) = 0; - - /// @brief Reads a number of bytes to a device that doesn't use registers for communications. - /// @param devSettings Settings of the device. - /// @param data Data buffer to read from registers. - /// @param numBytes Number of bytes to read. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, const uint32_t numBytes) = 0; -}; diff --git a/src/sfe_i2c.h b/src/sfe_i2c.h deleted file mode 100644 index d30b5b0..0000000 --- a/src/sfe_i2c.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Copyright (c) 2023 SparkFun Electronics -*/ - -#pragma once - -#include "sfe_bus.h" - -#define SFE_BUS_I2C_DEFAULT_ADDRESS 0x00 -#define SFE_BUS_I2C_DEFAULT_I2C_SPEED 100000 -#define SFE_BUS_I2C_BUFFER_SIZE 32 - -// @brief A simple bus address implementation for a generic I2C. -class SFEBusDevSettingsI2C : public SFEBusDevSettings -{ - public: - uint8_t devAddr = SFE_BUS_I2C_DEFAULT_ADDRESS; // Default I2C Address - uint32_t maxDataRate = SFE_BUS_I2C_DEFAULT_I2C_SPEED; // Default I2C Speed -}; - -/// @brief An abstract interface for an I2C communication bus -class SFEBusI2C : public SFEBus -{ - public: - /// @brief Pings I2C device and looks for an ACK response. - /// @param devAddr Address to ping. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t ping(const uint8_t devAddr) = 0; - - /// @brief Pings I2C device and looks for an ACK response. - /// @param devSettings Settings of device to ping. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t ping(const SFEBusDevSettings *devSettings) = 0; - - /// @brief Changes the I2C buffer size. - /// @param bufferSize New buffer size. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t setBufferSize(const uint32_t bufferSize) = 0; - - /// @brief Returns the I2C buffer size. - /// @param bufferSize Buffer to return the size of the transmit buffer. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t getBufferSize(uint32_t *bufferSize) = 0; - - /// @brief Changes the Bus transmit frequency. - /// @param frequency New bus frequency. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t setBusFrequency(const uint32_t frequency) = 0; - - /// @brief Returns the Bus transmit frequency. - /// @param frequency Buffer to return the frequency. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t getBusFrequency(uint32_t *frequency) = 0; - - protected: - uint32_t _i2cBufferSize = SFE_BUS_I2C_BUFFER_SIZE; // Default Buffer Size - uint32_t _busFrequency = SFE_BUS_I2C_DEFAULT_I2C_SPEED; // Default Speed is 100kHz -}; diff --git a/src/sfe_i2c_arduino.cpp b/src/sfe_i2c_arduino.cpp deleted file mode 100644 index cada1ee..0000000 --- a/src/sfe_i2c_arduino.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Copyright (c) 2023 SparkFun Electronics -*/ - -#include "sfe_i2c_arduino.h" - -int8_t SFEBusArduinoI2C::begin(void) -{ - return begin(_i2cBus ? _i2cBus : &Wire ); -} - -int8_t SFEBusArduinoI2C::begin(TwoWire &wirePort) -{ - return begin(&wirePort); -} - -int8_t SFEBusArduinoI2C::begin(TwoWire *i2cBus) -{ - if (!i2cBus) - return SFE_BUS_E_NULL_PTR; - - _i2cBus = i2cBus; - - _i2cBus->begin(); - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::end(void) -{ - // Null pointer check. - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - _i2cBus->end(); - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::ping(const uint8_t devAddr) -{ - // Null pointer check. - if (_i2cBus == nullptr) - return SFE_BUS_E_NULL_PTR; - if (!devAddr) - return SFE_BUS_E_NULL_PTR; - // Begin and end transmission to check for ACK response - _i2cBus->beginTransmission(devAddr); - - return _mapError(_i2cBus->endTransmission()); -} - -int8_t SFEBusArduinoI2C::ping(const SFEBusDevSettings *devSettings) -{ - // Null pointer check. - if (!devSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; - - return ping(pAddr->devAddr); -} - -int8_t SFEBusArduinoI2C::writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, - const uint8_t *data, const uint32_t numBytes) -{ - // Null pointer check. - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - // Null pointer check. - if (!devSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; - - uint32_t writeOffset = 0; - uint32_t bytesToSend = numBytes; - int8_t result = 0; - - // Start transmission and send register address. - _i2cBus->beginTransmission(pAddr->devAddr); - _i2cBus->write(regAddr); - - while (bytesToSend > 0) - { - // Limit sendLength to the size of the I2C buffer to send in chunks. - uint8_t sendLength = (bytesToSend > _i2cBufferSize) ? _i2cBufferSize : bytesToSend; - - // Do the write thing. - for (uint8_t i = 0; i < sendLength; i++) - _i2cBus->write(data[writeOffset + i]); - - // If there's still more to send, send a repeat start. - if (bytesToSend > _i2cBufferSize) - { - result = _mapError(_i2cBus->endTransmission(false)); - if (SFE_BUS_OK != result) - return result; - } - - writeOffset += sendLength; - bytesToSend -= sendLength; - } - - return _mapError(_i2cBus->endTransmission()); -} - -int8_t SFEBusArduinoI2C::readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, - const uint32_t numBytes) -{ - // Null pointer check. - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - // Null pointer check. - if (!devSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - // Null pointer check. - if (!data) - return SFE_BUS_E_NULL_DATA_BUFFER; - - SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; - - // Start transmission and send register address. - _i2cBus->beginTransmission(pAddr->devAddr); - _i2cBus->write(regAddr); - - // Repeat start condition, return if there's an error. - int8_t result = _mapError(_i2cBus->endTransmission(false)); - if (SFE_BUS_OK != result) - return result; - - uint32_t bytesLeftToRead = numBytes; - uint32_t readOffset = 0; - - while (bytesLeftToRead > 0) - { - // Limit readLength to the size of the I2C buffer to read in chunks. - uint8_t readLength = (bytesLeftToRead > _i2cBufferSize) ? _i2cBufferSize : bytesLeftToRead; - - // Request bytes, then read them into the data buffer. - uint32_t numRead = _i2cBus->requestFrom(pAddr->devAddr, readLength); - - if (numRead < readLength) - return SFE_BUS_W_UNDER_READ; - - if (_i2cBus->available()) - { - for (uint8_t i = 0; i < readLength; i++) - data[readOffset + i] = _i2cBus->read(); - } - else - return SFE_BUS_E_NO_RESPONSE; - - readOffset += readLength; - bytesLeftToRead -= readLength; - } - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, uint32_t numBytes) -{ - // Null pointer check. - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - // Null pointer check. - if (!devSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; - - uint32_t writeOffset = 0; - uint32_t bytesToSend = numBytes; - int8_t result = 0; - - // Start transmission. - _i2cBus->beginTransmission(pAddr->devAddr); - - while (bytesToSend > 0) - { - // Limit sendLength to the size of the I2C buffer to send in chunks. - uint8_t sendLength = (bytesToSend > _i2cBufferSize) ? _i2cBufferSize : bytesToSend; - - // Do the write thing. - for (uint8_t i = 0; i < sendLength; i++) - _i2cBus->write(data[writeOffset + i]); - - // If there's still more to send, send a repeat start. - if (bytesToSend > _i2cBufferSize) - { - result = _mapError(_i2cBus->endTransmission(false)); - if (SFE_BUS_OK != result) - return result; - } - - writeOffset += sendLength; - bytesToSend -= sendLength; - } - - return _mapError(_i2cBus->endTransmission()); -} - -int8_t SFEBusArduinoI2C::readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, uint32_t numBytes) -{ - // Null pointer check. - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - // Null pointer check. - if (!devSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; - - // Start transmission. - _i2cBus->beginTransmission(pAddr->devAddr); - - // Repeat start condition, return if there's an error. - int8_t result = _mapError(_i2cBus->endTransmission(false)); - if (SFE_BUS_OK != result) - return result; - - uint32_t bytesLeftToRead = numBytes; - uint32_t readOffset = 0; - - while (bytesLeftToRead > 0) - { - // Limit readLength to the size of the I2C buffer to read in chunks. - uint8_t readLength = (bytesLeftToRead > _i2cBufferSize) ? _i2cBufferSize : bytesLeftToRead; - - // Request bytes, then read them into the data buffer. - uint32_t numRead = _i2cBus->requestFrom(pAddr->devAddr, readLength); - - if (numRead < readLength) - return SFE_BUS_W_UNDER_READ; - - if (_i2cBus->available()) - { - for (uint8_t i = 0; i < readLength; i++) - data[readOffset + i] = _i2cBus->read(); - } - else - return SFE_BUS_E_NO_RESPONSE; - - readOffset += readLength; - bytesLeftToRead -= readLength; - } - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::setBufferSize(const uint32_t bufferSize) -{ - _i2cBufferSize = bufferSize; - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::getBufferSize(uint32_t *bufferSize) -{ - *bufferSize = _i2cBufferSize; - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::setBusFrequency(const uint32_t frequency) -{ - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - _i2cBus->setClock(frequency); - _busFrequency = frequency; - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::getBusFrequency(uint32_t *frequency) -{ - *frequency = _busFrequency; - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::_mapError(const uint8_t error) -{ - if (!error) - return SFE_BUS_OK; - else if (error == 1) - return SFE_BUS_E_DATA_TOO_LONG; - else if ((error == 2) || (error == 3)) - return SFE_BUS_E_NO_RESPONSE; - else if (error == 5) - return SFE_BUS_E_TIMEOUT; - else - return SFE_BUS_E_UNKNOWN; -} diff --git a/src/sfe_i2c_arduino.h b/src/sfe_i2c_arduino.h deleted file mode 100644 index cad7824..0000000 --- a/src/sfe_i2c_arduino.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Copyright (c) 2023 SparkFun Electronics -*/ - -#pragma once - -#include -#include "sfe_i2c.h" -#include - -/// @brief An I2C communication bus implementation for Arduino -class SFEBusArduinoI2C : public SFEBusI2C -{ - public: - /// @brief Empty Constructor. - SFEBusArduinoI2C(void) : _i2cBus{nullptr}{}; - - /// @brief Passing in a TwoWire Object. - SFEBusArduinoI2C(TwoWire &wirePort) : _i2cBus{&wirePort}{}; - - /// @brief Begin the I2C object with the default Wire object. - /// @return 0 for success, negative for failure, positive for warning. - int8_t begin(void); - - /// @brief Begin the I2C object with the inputted Wire object. - /// @param wirePort I2C object to use for this bus. - /// @return 0 for success, negative for failure, positive for warning. - int8_t begin(TwoWire &wirePort); - - /// @brief Begin the I2C object with the inputted Wire object pointer. - /// @param i2cBus I2C bus pointer. - /// @return 0 for success, negative for failure, positive for warning. - int8_t begin(TwoWire *i2cBus); - - /// @brief End the I2C object. - /// @return 0 for success, negative for failure, positive for warning. - int8_t end(void); - - /// @brief Pings I2C device and looks for an ACK response. - /// @param devAddr Address to ping. - /// @return 0 for success, negative for failure, positive for warning. - int8_t ping(const uint8_t devAddr); - - /// @brief Pings I2C device and looks for an ACK response. - /// @param devSettings Settings object containing the address to ping. - /// @return 0 for success, negative for failure, positive for warning. - int8_t ping(const SFEBusDevSettings *devSettings); - - /// @brief Writes a number of bytes starting at the given register address. - /// @param devSettings I2C Settings object containing the address of the device. - /// @param regAddr The register address to write to. - /// @param data Data buffer to write to registers. - /// @param numBytes Number of bytes to write. - /// @return 0 for success, negative for failure, positive for warning. - int8_t writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, const uint8_t *data, const uint32_t numBytes); - - /// @brief Reads a number of bytes starting at the given register address. - /// @param devSettings I2C Settings object containing the address of the device. - /// @param regAddr The first register address to read from. - /// @param data Data buffer to read from registers. - /// @param numBytes Number of bytes to read. - /// @return 0 for success, negative for failure, positive for warning. - int8_t readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, const uint32_t numBytes); - - /// @brief Writes a number of bytes to a device that doesn't use registers for communications. - /// @param devSettings I2C Settings object containing the address of the device. - /// @param data Data buffer to write to registers. - /// @param numBytes Number of bytes to write. - /// @return 0 for success, negative for failure, positive for warning. - int8_t writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, const uint32_t numBytes); - - /// @brief Reads a number of bytes to a device that doesn't use registers for communications. - /// @param devSettings I2C Settings object containing the address of the device. - /// @param data Data buffer to read from registers. - /// @param numBytes Number of bytes to read. - /// @return 0 for success, negative for failure, positive for warning. - int8_t readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, const uint32_t numBytes); - - /// @brief Changes the I2C buffer size. - /// @param bufferSize New buffer size. - /// @return 0 for success, negative for failure, positive for warning. - int8_t setBufferSize(const uint32_t bufferSize); - - /// @brief Returns the I2C buffer size. - /// @param bufferSize Buffer to return the buffer... yep. - /// @return 0 for success, negative for failure, positive for warning. - int8_t getBufferSize(uint32_t *bufferSize); - - /// @brief Changes the Bus transmit frequency. - /// @param frequency New bus frequency. - /// @return 0 for success, negative for failure, positive for warning. - int8_t setBusFrequency(const uint32_t frequency); - - /// @brief Returns the Bus transmit frequency. - /// @param frequency Buffer to return the frequency. - /// @return 0 for success, negative for failure, positive for warning. - int8_t getBusFrequency(uint32_t *frequency); - - private: - TwoWire* _i2cBus; - - /// @brief Maps the TwoWire interface error scheme to the common bus error scheme. - /// @param error TwoWire error code. - /// @return 0 for success, negative for failure, positive for warning. - int8_t _mapError(const uint8_t error); -}; From 46784d48fcaca75280aaa0daef7f9c264a5c754d Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:08:34 -0700 Subject: [PATCH 02/39] add files in. --- src/SparkFun_AS7331.h | 1325 ++++++++++++++++++++++++++++++++++++ src/sfe_as7331_registers.h | 274 ++++++++ src/sfe_bus.h | 71 ++ src/sfe_i2c.h | 60 ++ src/sfe_i2c_arduino.cpp | 302 ++++++++ src/sfe_i2c_arduino.h | 108 +++ 6 files changed, 2140 insertions(+) create mode 100644 src/SparkFun_AS7331.h create mode 100644 src/sfe_as7331_registers.h create mode 100644 src/sfe_bus.h create mode 100644 src/sfe_i2c.h create mode 100644 src/sfe_i2c_arduino.cpp create mode 100644 src/sfe_i2c_arduino.h diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h new file mode 100644 index 0000000..869cc82 --- /dev/null +++ b/src/SparkFun_AS7331.h @@ -0,0 +1,1325 @@ +/* + SparkFun Spectral UV Sensor - AS7331 + + Qwiic 1x1 + https://www.sparkfun.com/products/ + Qwiic Mini + https://www.sparkfun.com/products/ + + Repository + https://github.com/sparkfun/SparkFun_AS7331_Arduino_Library + + SPDX-License-Identifier: MIT + + Copyright (c) 2023 SparkFun Electronics + + Name: SparkFun_AS7331.h + + Description: + The SfeAS7331ArdI2C is a templated class that defines Arduino specific + behavior for initializing devices. It inherits from the base C++ class + SfeAS7331Base which defines the functions for interacting with the AS7331 + Spectral UV Sensor. + +*/ + +#pragma once + +#include "sfe_as7331_registers.h" + +#include +#include "sfe_i2c_arduino.h" + +/// @brief Measures structure to hold return values. +typedef struct { + float uva; + float uvb; + float uvc; + float temperature; + uint32_t outputConversionTime; +} sfeAS7331_measures_t; + +/// @brief Configuration struct to hold local sensor configuration. +typedef struct { + as7331_gain_t sensorGain; + as7331_conv_clk_freq_t cclk; + as7331_conv_time_t conversionTime; + as7331_ready_pin_mode_t readyPinMode; + as7331_divider_enable_t dividerEnabled; + as7331_divider_val_t dividerRange; + as7331_ext_syn_temp_meas_t enableTempConv; + as7331_simple_reg_read_mode_t indexMode; + uint8_t breakTime; + uint8_t numEdges; +} sfeAS7331_config_t; + +/// @brief State struct to hold local sensor state. +typedef struct { + as7331_power_state_t pd; + as7331_device_op_state_t opMode; + as7331_meas_mode_t mmode; + as7331_standby_mode_t sb; + as7331_startstate_t ss; +} sfeAS7331_state_t; + +/// @brief Default measures struct for initialization and reset. +const sfeAS7331_measures_t measuresDefault = { + .uva = (uint16_t)(-1), + .uvb = (uint16_t)(-1), + .uvc = (uint16_t)(-1), + .temperature = (uint16_t)(-1), + .outputConversionTime = (uint32_t)(-1) + }; + +/// @brief Default configuration struct for initialization and reset. +const sfeAS7331_config_t configDefault = { + .sensorGain = GAIN_2, + .cclk = CCLK_1_024_MHZ, + .conversionTime = TIME_64MS, + .readyPinMode = READYPIN_PUSHPULL, + .dividerEnabled = DIVIDER_DISABLED, + .dividerRange = DIV_2, + .enableTempConv = SYN_TEMP_ENABLED, + .indexMode = INDEX_REPEAT_START, + .breakTime = 25, // 25 * 8us = 200us + .numEdges = 1 + }; + +/// @brief Default state struct for initialization and reset. +const sfeAS7331_state_t stateDefault = { + .pd = POWER_DOWN_ENABLE, + .opMode = DEVICE_MODE_CFG, + .mmode = MEAS_MODE_CMD, + .sb = STANDBY_DISABLED, + .ss = START_STATE_DISABLED + }; + +/// @class SfeAS7331Base +/// @brief Template device class for the AS7331 Spectral UV Sensor. +template +class SfeAS7331Base { + public: + SfeAS7331Base(){ + _state = stateDefault; + + _config = configDefault; + + measures = measuresDefault; + }; + + /// @brief This method is called to initialize the AS7331 device through the specified bus. + /// @param theBus Pointer to the bus object. + /// @param devSettings Pointer to the device settings object. + /// @param deviceAddress I2C address for the device. + /// @return True if successful, false if it fails. + bool begin(sfeBusDevice *theBus, sfeBusDeviceSettings *devSettings, const uint8_t deviceAddress = AS7331_ADDR_DEFAULT) + { + setCommunicationBus(theBus, devSettings); + return begin(deviceAddress); + } + + /// @brief This method is called to initialize the AS7331 device through the specified bus. + /// @param theBus Pointer to the bus object. + /// @param deviceAddress I2C address for the device. + /// @return True if successful, false if it fails. + bool begin(sfeBusDevice *theBus, uint8_t deviceAddress = AS7331_ADDR_DEFAULT) + { + setCommunicationBus(theBus); + return begin(deviceAddress); + } + + /// @brief This method is called to initialize the AS7331 device at the specified bus address. + /// @param deviceAddress I2C address for the device. + /// @return True if successful, false if it fails. + bool begin(uint8_t deviceAddress = AS7331_ADDR_DEFAULT) + { + if(!_sfeBus) + setCommunicationBus(); + + setDeviceAddress(deviceAddress); + + _sfeBus->begin(); + + return init(); + } + + /// @brief Finds the device and initializes the default state. + /// @param runSetup Flag to run the default setup if set. + /// @return True if successful, false if it fails. + bool init(bool runSetup = true) + { + if(SFE_BUS_OK != _sfeBus->ping(_devSettings)) + return false; + + reset(); + + if(!isConnected()) + return false; + + if(runSetup) + return runDefaultSetup(); + + return true; + } + + /// @brief Writes to the data to the given register. + /// @param offset The register to write to. + /// @param data The data to write to the register. + /// @param length The number of writes + /// @return The SFE_BUS status code on the write to the given register. + int8_t writeRegister(const uint8_t offset, const uint8_t *data, const uint16_t length = 1) + { + return _sfeBus->writeRegisterBytes((SFEBusDevSettings*)_devSettings, offset, data, length); + } + + /// @brief Reads data from the specified register. + /// @param offset The register to read from. + /// @param data The pointer to the value to store the value. + /// @param length The number of reads + /// @return The SFE_BUS status code on the read to the given register. + int8_t readRegister(const uint8_t offset, uint8_t *data, const uint16_t length = 1) + { + return _sfeBus->readRegisterBytes((SFEBusDevSettings*)_devSettings, offset, data, length); + } + + /// @brief Checks to see if the AS7331 is connected. + /// @return True if successful, false otherwise. + bool isConnected(void) + { + return (AS7331_DEFAULT_DEV_ID == getDeviceID()); + } + + /// @brief Requests the device ID from the sensor. + /// @return The device ID of the sensor.` + uint8_t getDeviceID(void) + { + uint8_t devID; + + if(SFE_BUS_OK != readRegister(SFE_AS7331_REGISTER_CFG_AGEN, &devID)) + return 0; + + return devID; + } + + /// @brief Helper class that sets up the sensor and state in the POR configuration. + /// @param runSoftReset Flag that runs the soft reset function to reset the device. + /// @return True if successful, false otherwise. + bool runDefaultSetup(bool runSoftReset = false) + { + if(runSoftReset) + return reset(); + else + { + _state = stateDefault; + _config = configDefault; + measures = measuresDefault; + } + + sfe_as7331_reg_cfg_osr_t osr; + if(SFE_BUS_OK != getOSR(&osr)) + return false; + + uint8_t regs[6]; + if(SFE_BUS_OK != readRegister(SFE_AS7331_REGISTER_CFG_CREG1, regs, 6U)) + return false; + + sfe_as7331_reg_cfg_creg1_t creg1 = {.byte = regs[0]}; + sfe_as7331_reg_cfg_creg2_t creg2 = {.byte = regs[1]}; + sfe_as7331_reg_cfg_creg3_t creg3 = {.byte = regs[2]}; + sfe_as7331_reg_cfg_break_t breakreg = regs[3]; + sfe_as7331_reg_cfg_edges_t edgesreg = regs[4]; + sfe_as7331_reg_cfg_optreg_t optreg = {.byte = regs[5]}; + + osr.ss = _state.ss; + osr.pd = _state.pd; + osr.dos = _state.opMode; + + creg1.gain = _config.sensorGain; + creg1.time = _config.conversionTime; + + creg2.en_tm = _config.enableTempConv; + creg2.en_div = _config.dividerEnabled; + creg2.div = _config.dividerRange; + + creg3.mmode = _state.mmode; + creg3.sb = _state.sb; + creg3.rdyod = _config.readyPinMode; + creg3.cclk = _config.cclk; + + breakreg = _config.breakTime; + + edgesreg = _config.numEdges; + + optreg.init_idx = _config.indexMode; + + if(SFE_BUS_OK != setOSR(&osr)) + return false; + + regs[0] = creg1.byte; + regs[1] = creg2.byte; + regs[2] = creg3.byte; + regs[3] = breakreg; + regs[4] = edgesreg; + regs[5] = optreg.byte; + + if(SFE_BUS_OK != writeRegister(SFE_AS7331_REGISTER_CFG_CREG1, regs, 6U)) + return false; + + calculateConversionFactors(); + + return true; + } + + /// @brief Puts the sensor in the specified measurement mode. + /// @param measMode Measurement mode to enter. + /// @param startMeasure Flag to start measuring immediately if set. + /// @return True if successful, false otherwise. + bool startMeasurement(const as7331_meas_mode_t measMode = MEAS_MODE_CONT, bool startMeasure = false) + { + if(_state.pd == POWER_DOWN_ENABLE) + if(SFE_BUS_OK != setPowerState(POWER_DOWN_DISABLE)) return false; + + if(_state.mmode != measMode) + { + if(SFE_BUS_OK != setStandbyMode(STANDBY_DISABLED)) + return false; + if(SFE_BUS_OK != setMeasurementMode(measMode)) + return false; + } + + if(_state.opMode != DEVICE_MODE_MEAS) + if(SFE_BUS_OK != setOperationMode(DEVICE_MODE_MEAS)) return false; + + if(startMeasure) + if(SFE_BUS_OK != setStartStateMode(START_STATE_ENABLED)) return false; + + return true; + } + + /// @brief Sets the communication bus to the specified bus and device settings. + /// @param theBus Bus to set as the communication device. + /// @param deviceSettings Device settings to use when setting the bus for this sensor. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCommunicationBus(sfeBusDevice *theBus, sfeBusDeviceSettings *deviceSettings) + { + int8_t result = setCommunicationBus(theBus); + if(SFE_BUS_OK != result) + return result; + + return setCommunicationDevSettings(deviceSettings); + } + + /// @brief Sets teh communication bus to the specified bus. Creates new instance if none specified. + /// @param theBus Bus to set as the communication devie. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCommunicationBus(sfeBusDevice *theBus = nullptr) + { + if(theBus == nullptr) + theBus = new sfeBusDevice(); + + if(!theBus) + return SFE_BUS_E_NULL_PTR; + + _sfeBus = theBus; + + return SFE_BUS_OK; + } + + /// @brief Sets settings of this sensor's device. + /// @param deviceSettings Settings structure for this device. Creates new instance if none specified. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCommunicationDevSettings(sfeBusDeviceSettings *deviceSettings = nullptr) + { + if(deviceSettings == nullptr) + deviceSettings = new sfeBusDeviceSettings(); + + if(!deviceSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + _devSettings = deviceSettings; + + return SFE_BUS_OK; + } + + /// @brief Sets the address that the bus uses to communicate with the sensor. + /// @param deviceAddress Device address to use. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setDeviceAddress(const uint8_t deviceAddress) + { + int8_t result = SFE_BUS_OK; + + if(!_devSettings) + result = setCommunicationDevSettings(); + + if(SFE_BUS_OK != result) + return result; + + _devSettings->devAddr = deviceAddress; + + return SFE_BUS_OK; + } + + /// @brief Performs a soft reset of the sensor. + /// @return True if successful, false otherwise. + bool reset(void) + { + sfe_as7331_reg_cfg_osr_t osr; + + + if(SFE_BUS_OK != getOSR(&osr)) + return false; + + osr.sw_res = 1; + + if(SFE_BUS_OK != setOSR(&osr)) + return false; + + _state = stateDefault; + _config = configDefault; + measures = measuresDefault; + + return true; + } + + /// @brief Reads the sensor's temperature, converts it to a usable form, and saves it to the measures struct. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readTemp(void) + { + uint8_t tempRaw[2]; + + int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_TEMP, tempRaw, 2U); + + if(SFE_BUS_OK != result) + return result; + + measures.temperature = convertRawTempToTempC((uint16_t)(((uint16_t)tempRaw[1] << 8 | tempRaw[0]))); + + return SFE_BUS_OK; + } + + /// @brief Reads the sensor's UVA register, converts it to a usable form, and saves it to the measures struct. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readUVA(void) + { + uint8_t uvaRaw[2]; + + int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES1, uvaRaw, 2U); + + if(SFE_BUS_OK != result) + return result; + + if(_state.mmode == MEAS_MODE_SYND) { + result = readOutConv(); + + if(SFE_BUS_OK != result) + return result; + + float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; + float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); + measures.uva = (float)((uint16_t)(((uint16_t)uvaRaw[1]) << 8 | uvaRaw[0])-1.0f)*fsrA*divFactor/convFactor; + } + else { + measures.uva = (float)((uint16_t)(((uint16_t)uvaRaw[1]) << 8 | uvaRaw[0])-1.0f)*_conversionA; + } + + return SFE_BUS_OK; + } + + /// @brief Reads the sensor's UVB register, converts it to a usable form, and saves it to the measures struct. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readUVB(void) + { + uint8_t uvbRaw[2]; + + int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES2, uvbRaw, 2U); + + if(SFE_BUS_OK != result) + return result; + + if(_state.mmode == MEAS_MODE_SYND) { + result = readOutConv(); + + if(SFE_BUS_OK != result) + return result; + + float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; + float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); + measures.uvb = (float)((uint16_t)(((uint16_t)uvbRaw[1]) << 8 | uvbRaw[0])-1.0f)*fsrB*divFactor/convFactor; + } + else { + measures.uvb = (float)((uint16_t)(((uint16_t)uvbRaw[1]) << 8 | uvbRaw[0])-1.0f)*_conversionB; + } + + return SFE_BUS_OK; + } + + /// @brief Reads the sensor's UVC register, converts it to a usable form, and saves it to the measures struct. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readUVC(void) + { + uint8_t uvcRaw[2]; + + int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES3, uvcRaw, 2U); + + if(SFE_BUS_OK != result) + return result; + + if(_state.mmode == MEAS_MODE_SYND) { + result = readOutConv(); + + if(SFE_BUS_OK != result) + return result; + + float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; + float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); + measures.uvc = (float)((uint16_t)(((uint16_t)uvcRaw[1]) << 8 | uvcRaw[0])-1.0f)*fsrC*divFactor/convFactor; + } + else { + measures.uvc = (float)((uint16_t)(((uint16_t)uvcRaw[1]) << 8 | uvcRaw[0])-1.0f)*_conversionC; + } + + return SFE_BUS_OK; + } + + /// @brief Read's all three UV registers, converts them to a usable form, then saves them to the measures struct. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readAllUV(void) + { + uint8_t dataRaw[6]; + + int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES1, dataRaw, 6U); + + if(SFE_BUS_OK != result) + return result; + + if(_state.mmode == MEAS_MODE_SYND) { + result = readOutConv(); + + if(SFE_BUS_OK != result) + return result; + + float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; + float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); + measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[1]) << 8 | dataRaw[0])-1.0f)*fsrA*divFactor/convFactor; + measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*fsrB*divFactor/convFactor; + measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*fsrC*divFactor/convFactor; + } + else { + measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[1]) << 8 | dataRaw[0])-1.0f)*_conversionA; + measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*_conversionB; + measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*_conversionC; + } + + return SFE_BUS_OK; + } + + /// @brief Read the sensor's temperature, UV, and external time conversion clock counts, converts them, and then saves them to the measures struct. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readAll(void) + { + uint8_t dataRaw[8]; + + int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_TEMP, dataRaw, 8U); + + if(SFE_BUS_OK != result) + return result; + + result = readOutConv(); + if(SFE_BUS_OK != result) + return result; + + if(_state.mmode == MEAS_MODE_SYND) { + float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; + float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); + measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*fsrA*divFactor/convFactor; + measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*fsrB*divFactor/convFactor; + measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[7]) << 8 | dataRaw[6])-1.0f)*fsrC*divFactor/convFactor; + } + else { + measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*_conversionA; + measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*_conversionB; + measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[7]) << 8 | dataRaw[6])-1.0f)*_conversionC; + } + + measures.temperature = convertRawTempToTempC((uint16_t)(((uint16_t)dataRaw[1] << 8 | dataRaw[0]))); + + return SFE_BUS_OK; + } + + /// @brief Read the conversion clock counts register and saves it to the measures struct. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readOutConv(void) + { + uint8_t tconvRaw[4]; + + int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_OUTCONV_L, tconvRaw, 4U); + + if(SFE_BUS_OK != result) + return result; + + measures.outputConversionTime = (uint32_t)(((uint32_t)tconvRaw[3] << 24) | ((uint32_t)tconvRaw[2] << 16) | ((uint32_t)tconvRaw[1] << 8) | tconvRaw[0]); + + return SFE_BUS_OK; + } + + /// @brief Getter function to get the current internal state. + /// @return internal state struct. + sfeAS7331_state_t getState(void) + { + return _state; + } + + /// @brief Getter function to get the current internal configuration. + /// @return internal configuration struct. + sfeAS7331_config_t getConfig(void) + { + return _config; + } + + /// @brief Sets the UV sensor's gain. + /// @param gain The gain to set the sensor to. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setGain(const as7331_gain_t gain) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg1_t creg1; + + result = getCReg1(&creg1); + if(SFE_BUS_OK != result) + return result; + + creg1.gain = gain; + + result = setCReg1(&creg1); + if(SFE_BUS_OK != result) + return result; + + _config.sensorGain = gain; + + calculateConversionFactors(); + + return SFE_BUS_OK; + } + + /// @brief Sets the conversion time that the sensor will run to. + /// @param convTime Conversion time to set the sensor to. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setConversionTime(const as7331_conv_time_t convTime) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg1_t creg1; + + result = getCReg1(&creg1); + if(SFE_BUS_OK != result) + return result; + + creg1.time = convTime; + + result = setCReg1(&creg1); + if(SFE_BUS_OK != result) + return result; + + _config.conversionTime = convTime; + + calculateConversionFactors(); + + return SFE_BUS_OK; + } + + /// @brief Set the sensor's internal clock speed. + /// @param cclk Clock speed to set on the sensor. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCClk(const as7331_conv_clk_freq_t cclk) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg3_t creg3; + + result = getCReg1(&creg3); + if(SFE_BUS_OK != result) + return result; + + creg3.cclk = cclk; + + result = setCReg1(&creg3); + if(SFE_BUS_OK != result) + return result; + + _config.cclk = cclk; + + calculateConversionFactors(); + + return SFE_BUS_OK; + } + + /// @brief Enables or disables the internal UV result divider. + /// @param isEnabled Enable or disable the divder. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setEnableDivider(const as7331_divider_enable_t isEnabled) + { + if(_config.dividerEnabled == isEnabled) + return SFE_BUS_OK; + + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg2_t creg2; + + result = getCReg2(&creg2); + if(SFE_BUS_OK != result) + return result; + + creg2.en_div = isEnabled; + + result = setCReg2(&creg2); + if(SFE_BUS_OK != result) + return result; + + _config.dividerEnabled = isEnabled; + + if(isEnabled == DIVIDER_ENABLED) + calculateConversionFactors(); + + return SFE_BUS_OK; + } + + /// @brief Sets the value of the internal UV result divider. + /// @param divider Divider value to set. + /// @param setEnableDiv Option to turn on the divider if desired. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setDigitalDivider(const as7331_divider_val_t divider, const bool setEnableDiv = false) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg2_t creg2; + + result = getCReg2(&creg2); + if(SFE_BUS_OK != result) + return result; + + creg2.div = divider; + + result = setCReg2(&creg2); + if(SFE_BUS_OK != result) + return result; + + _config.dividerRange = divider; + + if(setEnableDiv) + result = setEnableDivider(DIVIDER_ENABLED); + else + calculateConversionFactors(); + + return SFE_BUS_OK; + } + + /// @brief Sets the ready pin type to push-pull or open-drain. + /// @param pinMode Mode to set the ready pin to. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setReadyPinMode(const as7331_ready_pin_mode_t pinMode) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg3_t creg3; + + result = getCReg3(&creg3); + if(SFE_BUS_OK != result) + return result; + + creg3.rdyod = pinMode; + + result = setCReg3(&creg3); + if(SFE_BUS_OK != result) + return result; + + _config.readyPinMode = pinMode; + + return SFE_BUS_OK; + } + + /// @brief Enables or disables the temperature conversion when in the SYND mode. + /// @param isEnabled Enable or disable the feature. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setEnableTemperatureConversion(const as7331_ext_syn_temp_meas_t isEnabled) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg2_t creg2; + + result = getCReg2(&creg2); + if(SFE_BUS_OK != result) + return result; + + creg2.en_tm = isEnabled; + + result = setCReg2(&creg2); + if(SFE_BUS_OK != result) + return result; + + _config.enableTempConv = isEnabled; + + return SFE_BUS_OK; + } + + /// @brief Set the index mode for compatibility with I2C controllers that don't support repeated start. + /// @param indexMode Simple or standard I2C addressing mode. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setIndexMode(const as7331_simple_reg_read_mode_t indexMode) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_optreg_t optreg; + + result = getOptIndex(&optreg); + if(SFE_BUS_OK != result) + return result; + + optreg.init_idx = indexMode; + + result = setOptIndex(&optreg); + if(SFE_BUS_OK != result) + return result; + + _config.indexMode = indexMode; + + return SFE_BUS_OK; + } + + /// @brief Set the minimum break time between measurements in CONT, SYNS, and SYND modes. + /// @param breakTime Time between measurements, 8us step time, max 2048us. A 0 value is a minimum of 3 fclk cycles. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setBreakTime(const uint8_t breakTime) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_break_t breakreg; + + result = getBreak(&breakreg); + if(SFE_BUS_OK != result) + return result; + + breakreg = breakTime; + + result = setBreak(&breakreg); + if(SFE_BUS_OK != result) + return result; + + _config.breakTime = breakTime; + + return SFE_BUS_OK; + } + + /// @brief Set the minimum number of falling edges required at the SYN input until the conversion is terminated. Only operational in SYND mode. + /// @param numEdges Number of edges prior to terminating conversion in SYND mode. 0 is not allowed, 1 is the minimum. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setNumEdges(const uint8_t numEdges) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_edges_t edgesreg; + + result = getEdges(&edgesreg); + if(SFE_BUS_OK != result) + return result; + + edgesreg = numEdges; + + result = setEdges(&edgesreg); + if(SFE_BUS_OK != result) + return result; + + _config.numEdges = numEdges; + + return SFE_BUS_OK; + } + + /// @brief Getter for the currently configured gain. + /// @return Sensor's gain expressed as (1 << (11 - gain)). + as7331_gain_t getConfigGain(void) + { + return _config.sensorGain; + } + + /// @brief Getter for the currently configured conversion clock. + /// @return Sensor's conversion clock expressed as 1024*(1 << cclk). + as7331_conv_clk_freq_t getConfigCClk(void) + { + return _config.cclk; + } + + /// @brief Getter for the currently configured conversion time. + /// @return Sensor's conversion time expressed as (1 << time). + as7331_conv_time_t getConfigConversionTime(void) + { + return _config.conversionTime; + } + + /// @brief Getter for the currently configured pin mode. + /// @return Sensor's ready pin configuration. + as7331_ready_pin_mode_t getConfigReadyPinMode(void) + { + return _config.readyPinMode; + } + + /// @brief Getter for the currently configured divider status. + /// @return Whether the sensor's divider is enabled or disabled. + as7331_divider_enable_t getConfigDividerEnabled(void) + { + return _config.dividerEnabled; + } + + /// @brief Getter for the currently configured divider range. + /// @return Sensor's internal UV predivider range. + as7331_divider_val_t getConfigDividerRange(void) + { + return _config.dividerRange; + } + + /// @brief Getter for the currently configured SYND temperature conversion status. + /// @return Whether the sensor will output temperature in SYND mode. + as7331_ext_syn_temp_meas_t getConfigExternalSyncTempConversion(void) + { + return _config.enableTempConv; + } + + /// @brief Getter for the currently configured I2C compatibility mode. + /// @return Sensor's configuration for interacting with simpler I2C controllers. + as7331_simple_reg_read_mode_t getConfigIndexMode(void) + { + return _config.indexMode; + } + + /// @brief Getter for the currently configured minimum break time in CONT, CMD, SYNS modes. + /// @return Sensor's breaktime in 8us steps. + uint8_t getConfigBreakTime(void) + { + return _config.breakTime; + } + + /// @brief Getter for the currently configured minimum number of edges to end conversion when in SYND mode. + /// @return Sensor's minimum number of edges, minimum 1 edge. + uint8_t getConfigNumEdges(void) + { + return _config.numEdges; + } + + /// @brief Gets the current power state from the sensor. + /// @param pd Pointer to an enum to store the current power state. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getPowerState(as7331_power_state_t *pd) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_osr_t osr; + + result = getOSR(&osr); + if(SFE_BUS_OK != result) + return result; + + _state.pd = (as7331_power_state_t)osr.pd; + *pd = _state.pd; + + return SFE_BUS_OK; + } + + /// @brief Sets the power state of the sensor. + /// @param pd Power state to set. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setPowerState(const as7331_power_state_t pd = POWER_DOWN_DISABLE) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_osr_t osr; + + result = getOSR(&osr); + if(SFE_BUS_OK != result) + return result; + + osr.pd = pd; + + result = setOSR(&osr); + if(SFE_BUS_OK != result) + return result; + + _state.pd = pd; + + return SFE_BUS_OK; + } + + /// @brief Gets the current operation mode from the sensor. + /// @param opMode Pointer to an enum to store the current operating mode. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getOperationMode(as7331_device_op_state_t *opMode) + { + sfe_as7331_reg_cfg_osr_t osr; + int8_t result = getOSR(&osr); + if(SFE_BUS_OK != result) + return result; + + _state.opMode = (as7331_device_op_state_t)osr.dos; + *opMode = _state.opMode; + + return SFE_BUS_OK; + } + + /// @brief Set the sensor's operating mode. + /// @param opMode Operating mode to set. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setOperationMode(const as7331_device_op_state_t opMode) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_osr_t osr; + + result = getOSR(&osr); + if(SFE_BUS_OK != result) + return result; + + osr.dos = opMode; + + result = setOSR(&osr); + if(SFE_BUS_OK != result) + return result; + + _state.opMode = opMode; + + return SFE_BUS_OK; + } + + /// @brief Gets the sensor's current measurement mode. + /// @param measMode Pointer to an enum to store the current measurment mode. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getMeasurementMode(as7331_meas_mode_t *measMode) + { + sfe_as7331_reg_cfg_creg3_t creg3; + int8_t result = getCReg3(&creg3); + if(SFE_BUS_OK != result) + return result; + + _state.mmode = (as7331_meas_mode_t)creg3.mmode; + *measMode = _state.mmode; + + return SFE_BUS_OK; + } + + /// @brief Sets the sensor's measurement mode. + /// @param measMode Measurement mode to set. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setMeasurementMode(const as7331_meas_mode_t measMode) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg3_t creg3; + + result = getCReg3(&creg3); + if(SFE_BUS_OK != result) + return result; + + creg3.mmode = measMode; + + result = setCReg3(&creg3); + if(SFE_BUS_OK != result) + return result; + + _state.mmode = measMode; + + return SFE_BUS_OK; + } + + /// @brief Gets the sensor's current standby mode state. + /// @param standby Pointer to an enum to store the current standby state. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getStandbyMode(as7331_standby_mode_t *standby) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg3_t creg3; + + result = getCReg3(&creg3); + if(SFE_BUS_OK != result) + return result; + + _state.sb = (as7331_standby_mode_t)creg3.sb; + *standby = _state.sb; + + return SFE_BUS_OK; + } + + /// @brief Sets the sensor's standby mode. + /// @param standby State to set. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setStandbyMode(const as7331_standby_mode_t standby) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg3_t creg3; + + result = getCReg3(&creg3); + if(SFE_BUS_OK != result) + return result; + + creg3.sb = standby; + + result = setCReg3(&creg3); + if(SFE_BUS_OK != result) + return result; + + _state.sb = standby; + + return SFE_BUS_OK; + } + + /// @brief Gets the sensor's current start state. + /// @param startState Pointer to an enum to store the current start state. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getStartStateMode(as7331_startstate_t *startState) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_osr_t osr; + + result = getOSR(&osr); + if(SFE_BUS_OK != result) + return result; + + _state.ss = (as7331_startstate_t)osr.ss; + *startState = _state.ss; + + return SFE_BUS_OK; + } + + /// @brief Sets the sensor's start state. This begins measurement. + /// @param startState Start state to set. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setStartStateMode(const as7331_startstate_t startState) + { + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_osr_t osr; + + result = getOSR(&osr); + if(SFE_BUS_OK != result) + return result; + + osr.ss = startState; + + result = setOSR(&osr); + if(SFE_BUS_OK != result) + return result; + + _state.ss = startState; + + return SFE_BUS_OK; + } + + /// @brief Getter for the current power state. + /// @return Sensor's power state. + as7331_power_state_t getStatePower(void) + { + return _state.pd; + } + + /// @brief Getter for the current operational state. + /// @return Sensor's operational state. + as7331_device_op_state_t getStateOperational(void) + { + return _state.opMode; + } + + /// @brief Getter for the current measurement state. + /// @return Sensor's measurement state. + as7331_meas_mode_t getStateMeasurement(void) + { + return _state.mmode; + } + + /// @brief Getter for the current standby state. + /// @return Sensor's standby state. + as7331_standby_mode_t getStateStandby(void) + { + return _state.sb; + } + + /// @brief Getter for the current start state. + /// @return Sensor's start state. + as7331_startstate_t getStateStart(void) + { + return _state.ss; + } + + /// @brief Gets the sensor's status when in measurement operation mode. + /// @param statusReg Pointer to a register struct to store the sensor's current status. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getStatus(sfe_as7331_reg_meas_osr_status_t *statusReg) + { + int8_t result = SFE_BUS_OK; + uint8_t statusRaw[2]; + + result = readRegister(SFE_AS7331_REGISTER_MEAS_OSR_STATUS, statusRaw, 2U); + + if(SFE_BUS_OK != result) + return result; + + statusReg->word = ((uint16_t)statusRaw[1] << 8) | statusRaw[0]; + + return SFE_BUS_OK; + } + + /// @brief Gets the operational state register when in configuration operation mode. + /// @param osrReg Pointer to a register struct to store the sensor's current OSR register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getOSR(sfe_as7331_reg_cfg_osr_t *osrReg) + { + return readRegister(SFE_AS7331_REGISTER_CFG_OSR, &osrReg->byte); + } + + /// @brief Sets the operational state register when in configuration operation mode. + /// @param osrReg Pointer to a register struct that has the new register configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setOSR(const sfe_as7331_reg_cfg_osr_t *osrReg) + { + return writeRegister(SFE_AS7331_REGISTER_CFG_OSR, &osrReg->byte); + } + + /// @brief Gets the configuration register #1 when in configuration operation mode. + /// @param creg1 Pointer to a register struct to store the sensor's current creg1 register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getCReg1(sfe_as7331_reg_cfg_creg1_t *creg1) + { + return readRegister(SFE_AS7331_REGISTER_CFG_CREG1, &creg1->byte); + } + + /// @brief Sets the configuration register #1 when in configuration operation mode. + /// @param creg1 Pointer to a register struct that has the new register configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCReg1(const sfe_as7331_reg_cfg_creg1_t *creg1) + { + return writeRegister(SFE_AS7331_REGISTER_CFG_CREG1, &creg1->byte); + } + + /// @brief Gets the configuration register #2 when in configuration operation mode. + /// @param creg2 Pointer to a register struct to store the sensor's current creg2 register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getCReg2(sfe_as7331_reg_cfg_creg2_t *creg2) + { + return readRegister(SFE_AS7331_REGISTER_CFG_CREG2, &creg2->byte); + } + + /// @brief Sets the configuration register #2 when in configuration operation mode. + /// @param creg2 Pointer to a register struct that has the new register configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCReg2(const sfe_as7331_reg_cfg_creg2_t *creg2) + { + return writeRegister(SFE_AS7331_REGISTER_CFG_CREG2, &creg2->byte); + } + + /// @brief Gets the configuration register #3 when in configuration operation mode. + /// @param creg3 Pointer to a register struct to store the sensor's current creg3 register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getCReg3(sfe_as7331_reg_cfg_creg3_t *creg3) + { + return readRegister(SFE_AS7331_REGISTER_CFG_CREG3, &creg3->byte); + } + + /// @brief Sets the configuration register #3 when in configuration operation mode. + /// @param creg3 Pointer to a register struct that has the new register configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCReg3(const sfe_as7331_reg_cfg_creg3_t *creg3) + { + return writeRegister(SFE_AS7331_REGISTER_CFG_CREG3, &creg3->byte); + } + + /// @brief Gets the break register when in configuration operation mode. + /// @param breakReg Pointer to a register struct to store the sensor's current break register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getBreak(sfe_as7331_reg_cfg_break_t *breakReg) + { + return readRegister(SFE_AS7331_REGISTER_CFG_BREAK, breakReg); + } + + /// @brief Sets the break register when in configuration operation mode. + /// @param breakReg Pointer to a register struct that has the new register configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setBreak(const sfe_as7331_reg_cfg_break_t *breakReg) + { + return writeRegister(SFE_AS7331_REGISTER_CFG_BREAK, breakReg); + } + + /// @brief Gets the edges register when in configuration operation mode. + /// @param edgesReg Pointer to a register struct to store the sensor's current edges register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getEdges(sfe_as7331_reg_cfg_edges_t *edgesReg) + { + return readRegister(SFE_AS7331_REGISTER_CFG_EDGES, edgesReg); + } + + + /// @brief Sets the edges register when in configuration operation mode. + /// @param edgesReg Pointer to a register struct that has the new register configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setEdges(const sfe_as7331_reg_cfg_edges_t *edgesReg) + { + return writeRegister(SFE_AS7331_REGISTER_CFG_EDGES, edgesReg); + } + + /// @brief Gets the option register when in configuration operation mode. + /// @param optReg Pointer to a register struct to store the sensor's current option register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getOptIndex(sfe_as7331_reg_cfg_optreg_t *optReg) + { + return readRegister(SFE_AS7331_REGISTER_CFG_OPTREG, &optReg->byte); + } + + /// @brief Sets the option register when in configuration operation mode. + /// @param optReg Pointer to a register struct that has the new register configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setOptIndex(const sfe_as7331_reg_cfg_optreg_t *optReg) + { + return writeRegister(SFE_AS7331_REGISTER_CFG_OPTREG, &optReg->byte); + } + + sfeAS7331_measures_t measures; + + private: + /// @brief Converts the raw temperature value to a human readable form. + /// @param inputVal Raw temperature value to convert. + /// @return The converted device temperature in degree C. + float convertRawTempToTempC(const uint16_t inputVal) + { + // T_chip = TEMP*0.05 - 66.9 + // EX: TEMP=0x922 aka TEMP=0d2338, returns 50.0 + return ((float)(inputVal) * 0.05f) - 66.9f; + } + + /// @brief Called when changing values that affect the conversion, calculates a new conversion factor to reduce the conversion overhead. + void calculateConversionFactors(void) + { + float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; + _conversionA = fsrA/((float)(1 << (11 - _config.sensorGain)))/((float)(1 << _config.conversionTime))/((float)(1024.0 * (1 << _config.cclk)))*divFactor; + _conversionB = fsrB/((float)(1 << (11 - _config.sensorGain)))/((float)(1 << _config.conversionTime))/((float)(1024.0 * (1 << _config.cclk)))*divFactor; + _conversionC = fsrC/((float)(1 << (11 - _config.sensorGain)))/((float)(1 << _config.conversionTime))/((float)(1024.0 * (1 << _config.cclk)))*divFactor; + } + + sfeBusDevice *_sfeBus; // Pointer to bus device. + sfeBusDeviceSettings *_devSettings; // Pointer to the device settings for this sensor. + + sfeAS7331_config_t _config; // Internal sensor configuration. + sfeAS7331_state_t _state; // Internal sensor state. + + float _conversionA; // Internal conversion factor for the UVA sensor. + float _conversionB; // Internal conversion factor for the UVB sensor. + float _conversionC; // Internal conversion factor for the UVC sensor. + + static constexpr float fsrA = 348160.0; // Full Scale Resolution for the UVA sensor. + static constexpr float fsrB = 387072.0; // Full Scale Resolution for the UVB sensor. + static constexpr float fsrC = 169984.0; // Full Scale Resolution for the UVC sensor. + +}; + +/// @class SfeAS7331ArdI2C +/// @brief Arduino I2C implementation of the SfeAS7331Base class for the AS7331 Spectral UV Sensor. +class SfeAS7331ArdI2C : public SfeAS7331Base +{ + /* Nothing to see here, see above. */ +}; diff --git a/src/sfe_as7331_registers.h b/src/sfe_as7331_registers.h new file mode 100644 index 0000000..797c948 --- /dev/null +++ b/src/sfe_as7331_registers.h @@ -0,0 +1,274 @@ +/* + SPDX-License-Identifier: MIT + + Copyright (c) 2023 SparkFun Electronics + +*/ + +#pragma once +#include + +/// I2C addresses +// 7-bit address defined as [1, 1, 1, 0, 1, A1, A0] where A1/A0 are the address pins tied high or low +#define AS7331_ADDR_DEFAULT 0x74 +#define AS7331_ADDR_SEC 0x75 +#define AS7331_ADDR_TER 0x76 +#define AS7331_ADDR_QUA 0x77 + +#define AS7331_DEFAULT_DEV_ID 0x21 +#define AS7331_DEV_ID_HIGH 0x2 + + +/// Enum Settings + +typedef enum { + // 0b00X invalid + DEVICE_MODE_CFG = 0x2, + DEVICE_MODE_MEAS = 0x3 + // 0b1XX invalid +} as7331_device_op_state_t; + +typedef enum { + POWER_DOWN_DISABLE = 0x0, + POWER_DOWN_ENABLE +} as7331_power_state_t; + +typedef enum { + START_STATE_DISABLED = 0x0, + START_STATE_ENABLED +} as7331_startstate_t; + +typedef enum { + GAIN_2048 = 0x0000, + GAIN_1024, + GAIN_512, + GAIN_256, + GAIN_128, + GAIN_64, + GAIN_32, + GAIN_16, + GAIN_8, + GAIN_4, + GAIN_2, + GAIN_1 +} as7331_gain_t; + +typedef enum { + SYN_TEMP_DISABLED = 0x0, + SYN_TEMP_ENABLED +} as7331_ext_syn_temp_meas_t; + +typedef enum { + CCLK_1_024_MHZ = 0x00, + CCLK_2_048_MHZ, + CCLK_4_096_MHZ, + CCLK_8_192_MHZ +} as7331_conv_clk_freq_t; + +typedef enum { + READYPIN_PUSHPULL = 0x0, + READYPIN_OPENDRAIN +} as7331_ready_pin_mode_t; + +typedef enum { + STANDBY_DISABLED = 0x0, + STANDBY_ENABLED +} as7331_standby_mode_t; + +typedef enum { + MEAS_MODE_CONT = 0x0, + MEAS_MODE_CMD, + MEAS_MODE_SYNS, + MEAS_MODE_SYND +} as7331_meas_mode_t; + +typedef enum { + INDEX_NO_REPEAT_START = 0x0, + INDEX_REPEAT_START +} as7331_simple_reg_read_mode_t; + +typedef enum { + TIME_1MS = 0x0, + TIME_2MS, + TIME_4MS, + TIME_8MS, + TIME_16MS, + TIME_32MS, + TIME_64MS, + TIME_128MS, + TIME_256MS, + TIME_512MS, + TIME_1024MS, + TIME_2048MS, + TIME_4096MS, + TIME_8192MS, + TIME_16384MS +} as7331_conv_time_t; + +typedef enum { + DIVIDER_DISABLED = 0x0, + DIVIDER_ENABLED = 0x1 +} as7331_divider_enable_t; + +typedef enum { + DIV_2 = 0x0, + DIV_4, + DIV_8, + DIV_16, + DIV_32, + DIV_64, + DIV_128, + DIV_256 +} as7331_divider_val_t; + +/// Configuration Registers +// These registers can only be accessed in the configuration state, and they +// are all 8 bits long. + +#define SFE_AS7331_REGISTER_CFG_OSR 0x00 +typedef union { + struct { + uint8_t dos : 3; + uint8_t sw_res : 1; + uint8_t reserved : 2; + uint8_t pd : 1; + uint8_t ss : 1; + }; + uint8_t byte; +} sfe_as7331_reg_cfg_osr_t; + +#define SFE_AS7331_REGISTER_CFG_AGEN 0x02 +typedef union { + struct { + uint8_t mut : 4; + uint8_t devid : 4; + }; + uint8_t byte; +} sfe_as7331_reg_cfg_agen_t; + +#define SFE_AS7331_REGISTER_CFG_CREG1 0x06 +typedef union { + struct { + uint8_t time : 4; + uint8_t gain : 4; + }; + uint8_t byte; +} sfe_as7331_reg_cfg_creg1_t; + +#define SFE_AS7331_REGISTER_CFG_CREG2 0x07 +typedef union { + struct { + uint8_t div : 3; + uint8_t en_div : 1; + uint8_t reserved : 2; + uint8_t en_tm : 1; + uint8_t reserved1 : 1; + }; + uint8_t byte; +} sfe_as7331_reg_cfg_creg2_t; + +#define SFE_AS7331_REGISTER_CFG_CREG3 0x08 +typedef union { + struct { + uint8_t cclk : 2; + uint8_t reserved : 1; + uint8_t rdyod : 1; + uint8_t sb : 1; + uint8_t reserved1 : 1; + uint8_t mmode : 2; + }; + uint8_t byte; +} sfe_as7331_reg_cfg_creg3_t; + +#define SFE_AS7331_REGISTER_CFG_BREAK 0x09 +typedef uint8_t sfe_as7331_reg_cfg_break_t; + +#define SFE_AS7331_REGISTER_CFG_EDGES 0x0A +typedef uint8_t sfe_as7331_reg_cfg_edges_t; + +#define SFE_AS7331_REGISTER_CFG_OPTREG 0x0B +typedef union { + struct { + uint8_t init_idx : 1; + uint8_t reserved : 7; + }; + uint8_t byte; +} sfe_as7331_reg_cfg_optreg_t; + + +/// Measurement Registers +// These registers can only be accessed in the measurement state. They are +// read-only and 16 bits long, except OUTCONV, which is 24 bits long. + +#define SFE_AS7331_REGISTER_MEAS_OSR_STATUS 0x00 +typedef union { + struct { + sfe_as7331_reg_cfg_osr_t osr; + uint8_t powerstate : 1; + uint8_t standbystate : 1; + uint8_t notready : 1; + uint8_t ndata : 1; + uint8_t ldata : 1; + uint8_t adcof : 1; + uint8_t mresof : 1; + uint8_t outconvof : 1; + }; + uint16_t word; +} sfe_as7331_reg_meas_osr_status_t; + +#define SFE_AS7331_REGISTER_MEAS_TEMP 0x01 +typedef union { + struct { + uint16_t temp : 12; + uint8_t zeros : 4; + }; + uint16_t word; +} sfe_as7331_reg_meas_temp_t; + +#define SFE_AS7331_REGISTER_MEAS_MRES1 0x02 +typedef union { + struct { + uint8_t res_l; + uint8_t res_h; + }; + uint16_t word; +} sfe_as7331_reg_meas_mres1_t; + +#define SFE_AS7331_REGISTER_MEAS_MRES2 0x03 +typedef union { + struct { + uint8_t res_l; + uint8_t res_h; + }; + uint16_t word; +} sfe_as7331_reg_meas_mres2_t; + + +#define SFE_AS7331_REGISTER_MEAS_MRES3 0x04 +typedef union { + struct { + uint8_t res_l; + uint8_t res_h; + }; + uint16_t word; +} sfe_as7331_reg_meas_mres3_t; + + +#define SFE_AS7331_REGISTER_MEAS_OUTCONV_L 0x05 +typedef union { + struct { + uint8_t outconv_l; + uint8_t outconv_m; + }; + uint16_t word; +} sfe_as7331_reg_meas_outconv_l_t; + + +#define SFE_AS7331_REGISTER_MEAS_OUTCONV_H 0x06 +typedef union { + struct { + uint8_t outconv_h; + uint8_t zeros; + }; + uint16_t word; +} sfe_as7331_reg_meas_outconv_h_t; diff --git a/src/sfe_bus.h b/src/sfe_bus.h new file mode 100644 index 0000000..0bff2a7 --- /dev/null +++ b/src/sfe_bus.h @@ -0,0 +1,71 @@ +/* + SPDX-License-Identifier: MIT + + Copyright (c) 2023 SparkFun Electronics + + A pure virtual base class for implementing a common communication interface + in SparkFun products. +*/ + +#pragma once + +#include + +// Error and warning codes +#define SFE_BUS_OK 0 +#define SFE_BUS_E_UNKNOWN -1 +#define SFE_BUS_E_NULL_PTR -2 +#define SFE_BUS_E_TIMEOUT -3 +#define SFE_BUS_E_NO_RESPONSE -4 +#define SFE_BUS_E_DATA_TOO_LONG -5 +#define SFE_BUS_E_NULL_DEV_SETTINGS -6 +#define SFE_BUS_E_NULL_DATA_BUFFER -7 +#define SFE_BUS_W_UNKNOWN 1 +#define SFE_BUS_W_UNDER_READ 2 +#define SFE_BUS_W_NOT_ENABLED 3 + +/// @brief An abstract Bus address class for enabling multiple types of addresses. +class SFEBusDevSettings{}; // Nothing to see here... + +/// @brief An abstract interface for a communication bus +class SFEBus +{ + public: + /// @brief Begin bus. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t begin(void) = 0; + + /// @brief End bus. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t end(void) = 0; + + /// @brief Writes a number of bytes starting at the given register address. + /// @param devSettings Settings of the device. + /// @param regAddr The first register address to write to. + /// @param data Data buffer to write to registers. + /// @param numBytes Number of bytes to write. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, const uint8_t *data, const uint32_t numBytes) = 0; + + /// @brief Reads a number of bytes starting at the given register address. + /// @param devSettings Settings of the device. + /// @param regAddr The first register address to read from. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, const uint32_t numBytes) = 0; + + /// @brief Writes a number of bytes to a device that doesn't use registers for communications. + /// @param devSettings Settings of the device. + /// @param data Data buffer to write from registers. + /// @param numBytes Number of bytes to write. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, const uint32_t numBytes) = 0; + + /// @brief Reads a number of bytes to a device that doesn't use registers for communications. + /// @param devSettings Settings of the device. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, const uint32_t numBytes) = 0; +}; diff --git a/src/sfe_i2c.h b/src/sfe_i2c.h new file mode 100644 index 0000000..d30b5b0 --- /dev/null +++ b/src/sfe_i2c.h @@ -0,0 +1,60 @@ +/* + SPDX-License-Identifier: MIT + + Copyright (c) 2023 SparkFun Electronics +*/ + +#pragma once + +#include "sfe_bus.h" + +#define SFE_BUS_I2C_DEFAULT_ADDRESS 0x00 +#define SFE_BUS_I2C_DEFAULT_I2C_SPEED 100000 +#define SFE_BUS_I2C_BUFFER_SIZE 32 + +// @brief A simple bus address implementation for a generic I2C. +class SFEBusDevSettingsI2C : public SFEBusDevSettings +{ + public: + uint8_t devAddr = SFE_BUS_I2C_DEFAULT_ADDRESS; // Default I2C Address + uint32_t maxDataRate = SFE_BUS_I2C_DEFAULT_I2C_SPEED; // Default I2C Speed +}; + +/// @brief An abstract interface for an I2C communication bus +class SFEBusI2C : public SFEBus +{ + public: + /// @brief Pings I2C device and looks for an ACK response. + /// @param devAddr Address to ping. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t ping(const uint8_t devAddr) = 0; + + /// @brief Pings I2C device and looks for an ACK response. + /// @param devSettings Settings of device to ping. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t ping(const SFEBusDevSettings *devSettings) = 0; + + /// @brief Changes the I2C buffer size. + /// @param bufferSize New buffer size. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t setBufferSize(const uint32_t bufferSize) = 0; + + /// @brief Returns the I2C buffer size. + /// @param bufferSize Buffer to return the size of the transmit buffer. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t getBufferSize(uint32_t *bufferSize) = 0; + + /// @brief Changes the Bus transmit frequency. + /// @param frequency New bus frequency. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t setBusFrequency(const uint32_t frequency) = 0; + + /// @brief Returns the Bus transmit frequency. + /// @param frequency Buffer to return the frequency. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t getBusFrequency(uint32_t *frequency) = 0; + + protected: + uint32_t _i2cBufferSize = SFE_BUS_I2C_BUFFER_SIZE; // Default Buffer Size + uint32_t _busFrequency = SFE_BUS_I2C_DEFAULT_I2C_SPEED; // Default Speed is 100kHz +}; diff --git a/src/sfe_i2c_arduino.cpp b/src/sfe_i2c_arduino.cpp new file mode 100644 index 0000000..cada1ee --- /dev/null +++ b/src/sfe_i2c_arduino.cpp @@ -0,0 +1,302 @@ +/* + SPDX-License-Identifier: MIT + + Copyright (c) 2023 SparkFun Electronics +*/ + +#include "sfe_i2c_arduino.h" + +int8_t SFEBusArduinoI2C::begin(void) +{ + return begin(_i2cBus ? _i2cBus : &Wire ); +} + +int8_t SFEBusArduinoI2C::begin(TwoWire &wirePort) +{ + return begin(&wirePort); +} + +int8_t SFEBusArduinoI2C::begin(TwoWire *i2cBus) +{ + if (!i2cBus) + return SFE_BUS_E_NULL_PTR; + + _i2cBus = i2cBus; + + _i2cBus->begin(); + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::end(void) +{ + // Null pointer check. + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + _i2cBus->end(); + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::ping(const uint8_t devAddr) +{ + // Null pointer check. + if (_i2cBus == nullptr) + return SFE_BUS_E_NULL_PTR; + if (!devAddr) + return SFE_BUS_E_NULL_PTR; + // Begin and end transmission to check for ACK response + _i2cBus->beginTransmission(devAddr); + + return _mapError(_i2cBus->endTransmission()); +} + +int8_t SFEBusArduinoI2C::ping(const SFEBusDevSettings *devSettings) +{ + // Null pointer check. + if (!devSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; + + return ping(pAddr->devAddr); +} + +int8_t SFEBusArduinoI2C::writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, + const uint8_t *data, const uint32_t numBytes) +{ + // Null pointer check. + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + // Null pointer check. + if (!devSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; + + uint32_t writeOffset = 0; + uint32_t bytesToSend = numBytes; + int8_t result = 0; + + // Start transmission and send register address. + _i2cBus->beginTransmission(pAddr->devAddr); + _i2cBus->write(regAddr); + + while (bytesToSend > 0) + { + // Limit sendLength to the size of the I2C buffer to send in chunks. + uint8_t sendLength = (bytesToSend > _i2cBufferSize) ? _i2cBufferSize : bytesToSend; + + // Do the write thing. + for (uint8_t i = 0; i < sendLength; i++) + _i2cBus->write(data[writeOffset + i]); + + // If there's still more to send, send a repeat start. + if (bytesToSend > _i2cBufferSize) + { + result = _mapError(_i2cBus->endTransmission(false)); + if (SFE_BUS_OK != result) + return result; + } + + writeOffset += sendLength; + bytesToSend -= sendLength; + } + + return _mapError(_i2cBus->endTransmission()); +} + +int8_t SFEBusArduinoI2C::readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, + const uint32_t numBytes) +{ + // Null pointer check. + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + // Null pointer check. + if (!devSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + // Null pointer check. + if (!data) + return SFE_BUS_E_NULL_DATA_BUFFER; + + SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; + + // Start transmission and send register address. + _i2cBus->beginTransmission(pAddr->devAddr); + _i2cBus->write(regAddr); + + // Repeat start condition, return if there's an error. + int8_t result = _mapError(_i2cBus->endTransmission(false)); + if (SFE_BUS_OK != result) + return result; + + uint32_t bytesLeftToRead = numBytes; + uint32_t readOffset = 0; + + while (bytesLeftToRead > 0) + { + // Limit readLength to the size of the I2C buffer to read in chunks. + uint8_t readLength = (bytesLeftToRead > _i2cBufferSize) ? _i2cBufferSize : bytesLeftToRead; + + // Request bytes, then read them into the data buffer. + uint32_t numRead = _i2cBus->requestFrom(pAddr->devAddr, readLength); + + if (numRead < readLength) + return SFE_BUS_W_UNDER_READ; + + if (_i2cBus->available()) + { + for (uint8_t i = 0; i < readLength; i++) + data[readOffset + i] = _i2cBus->read(); + } + else + return SFE_BUS_E_NO_RESPONSE; + + readOffset += readLength; + bytesLeftToRead -= readLength; + } + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, uint32_t numBytes) +{ + // Null pointer check. + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + // Null pointer check. + if (!devSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; + + uint32_t writeOffset = 0; + uint32_t bytesToSend = numBytes; + int8_t result = 0; + + // Start transmission. + _i2cBus->beginTransmission(pAddr->devAddr); + + while (bytesToSend > 0) + { + // Limit sendLength to the size of the I2C buffer to send in chunks. + uint8_t sendLength = (bytesToSend > _i2cBufferSize) ? _i2cBufferSize : bytesToSend; + + // Do the write thing. + for (uint8_t i = 0; i < sendLength; i++) + _i2cBus->write(data[writeOffset + i]); + + // If there's still more to send, send a repeat start. + if (bytesToSend > _i2cBufferSize) + { + result = _mapError(_i2cBus->endTransmission(false)); + if (SFE_BUS_OK != result) + return result; + } + + writeOffset += sendLength; + bytesToSend -= sendLength; + } + + return _mapError(_i2cBus->endTransmission()); +} + +int8_t SFEBusArduinoI2C::readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, uint32_t numBytes) +{ + // Null pointer check. + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + // Null pointer check. + if (!devSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; + + // Start transmission. + _i2cBus->beginTransmission(pAddr->devAddr); + + // Repeat start condition, return if there's an error. + int8_t result = _mapError(_i2cBus->endTransmission(false)); + if (SFE_BUS_OK != result) + return result; + + uint32_t bytesLeftToRead = numBytes; + uint32_t readOffset = 0; + + while (bytesLeftToRead > 0) + { + // Limit readLength to the size of the I2C buffer to read in chunks. + uint8_t readLength = (bytesLeftToRead > _i2cBufferSize) ? _i2cBufferSize : bytesLeftToRead; + + // Request bytes, then read them into the data buffer. + uint32_t numRead = _i2cBus->requestFrom(pAddr->devAddr, readLength); + + if (numRead < readLength) + return SFE_BUS_W_UNDER_READ; + + if (_i2cBus->available()) + { + for (uint8_t i = 0; i < readLength; i++) + data[readOffset + i] = _i2cBus->read(); + } + else + return SFE_BUS_E_NO_RESPONSE; + + readOffset += readLength; + bytesLeftToRead -= readLength; + } + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::setBufferSize(const uint32_t bufferSize) +{ + _i2cBufferSize = bufferSize; + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::getBufferSize(uint32_t *bufferSize) +{ + *bufferSize = _i2cBufferSize; + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::setBusFrequency(const uint32_t frequency) +{ + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + _i2cBus->setClock(frequency); + _busFrequency = frequency; + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::getBusFrequency(uint32_t *frequency) +{ + *frequency = _busFrequency; + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::_mapError(const uint8_t error) +{ + if (!error) + return SFE_BUS_OK; + else if (error == 1) + return SFE_BUS_E_DATA_TOO_LONG; + else if ((error == 2) || (error == 3)) + return SFE_BUS_E_NO_RESPONSE; + else if (error == 5) + return SFE_BUS_E_TIMEOUT; + else + return SFE_BUS_E_UNKNOWN; +} diff --git a/src/sfe_i2c_arduino.h b/src/sfe_i2c_arduino.h new file mode 100644 index 0000000..cad7824 --- /dev/null +++ b/src/sfe_i2c_arduino.h @@ -0,0 +1,108 @@ +/* + SPDX-License-Identifier: MIT + + Copyright (c) 2023 SparkFun Electronics +*/ + +#pragma once + +#include +#include "sfe_i2c.h" +#include + +/// @brief An I2C communication bus implementation for Arduino +class SFEBusArduinoI2C : public SFEBusI2C +{ + public: + /// @brief Empty Constructor. + SFEBusArduinoI2C(void) : _i2cBus{nullptr}{}; + + /// @brief Passing in a TwoWire Object. + SFEBusArduinoI2C(TwoWire &wirePort) : _i2cBus{&wirePort}{}; + + /// @brief Begin the I2C object with the default Wire object. + /// @return 0 for success, negative for failure, positive for warning. + int8_t begin(void); + + /// @brief Begin the I2C object with the inputted Wire object. + /// @param wirePort I2C object to use for this bus. + /// @return 0 for success, negative for failure, positive for warning. + int8_t begin(TwoWire &wirePort); + + /// @brief Begin the I2C object with the inputted Wire object pointer. + /// @param i2cBus I2C bus pointer. + /// @return 0 for success, negative for failure, positive for warning. + int8_t begin(TwoWire *i2cBus); + + /// @brief End the I2C object. + /// @return 0 for success, negative for failure, positive for warning. + int8_t end(void); + + /// @brief Pings I2C device and looks for an ACK response. + /// @param devAddr Address to ping. + /// @return 0 for success, negative for failure, positive for warning. + int8_t ping(const uint8_t devAddr); + + /// @brief Pings I2C device and looks for an ACK response. + /// @param devSettings Settings object containing the address to ping. + /// @return 0 for success, negative for failure, positive for warning. + int8_t ping(const SFEBusDevSettings *devSettings); + + /// @brief Writes a number of bytes starting at the given register address. + /// @param devSettings I2C Settings object containing the address of the device. + /// @param regAddr The register address to write to. + /// @param data Data buffer to write to registers. + /// @param numBytes Number of bytes to write. + /// @return 0 for success, negative for failure, positive for warning. + int8_t writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, const uint8_t *data, const uint32_t numBytes); + + /// @brief Reads a number of bytes starting at the given register address. + /// @param devSettings I2C Settings object containing the address of the device. + /// @param regAddr The first register address to read from. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + int8_t readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, const uint32_t numBytes); + + /// @brief Writes a number of bytes to a device that doesn't use registers for communications. + /// @param devSettings I2C Settings object containing the address of the device. + /// @param data Data buffer to write to registers. + /// @param numBytes Number of bytes to write. + /// @return 0 for success, negative for failure, positive for warning. + int8_t writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, const uint32_t numBytes); + + /// @brief Reads a number of bytes to a device that doesn't use registers for communications. + /// @param devSettings I2C Settings object containing the address of the device. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + int8_t readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, const uint32_t numBytes); + + /// @brief Changes the I2C buffer size. + /// @param bufferSize New buffer size. + /// @return 0 for success, negative for failure, positive for warning. + int8_t setBufferSize(const uint32_t bufferSize); + + /// @brief Returns the I2C buffer size. + /// @param bufferSize Buffer to return the buffer... yep. + /// @return 0 for success, negative for failure, positive for warning. + int8_t getBufferSize(uint32_t *bufferSize); + + /// @brief Changes the Bus transmit frequency. + /// @param frequency New bus frequency. + /// @return 0 for success, negative for failure, positive for warning. + int8_t setBusFrequency(const uint32_t frequency); + + /// @brief Returns the Bus transmit frequency. + /// @param frequency Buffer to return the frequency. + /// @return 0 for success, negative for failure, positive for warning. + int8_t getBusFrequency(uint32_t *frequency); + + private: + TwoWire* _i2cBus; + + /// @brief Maps the TwoWire interface error scheme to the common bus error scheme. + /// @param error TwoWire error code. + /// @return 0 for success, negative for failure, positive for warning. + int8_t _mapError(const uint8_t error); +}; From 1502e1b0d14283a579610e8989bab6448e2ffb1d Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:48:25 -0700 Subject: [PATCH 03/39] WIP update. Delete toolkit files. Migrate registers file to header. Refactor of library in progress. --- src/SparkFun_AS7331.cpp | 967 +++++++++++++++++ src/SparkFun_AS7331.h | 1997 +++++++++++++----------------------- src/sfe_as7331_registers.h | 274 ----- src/sfe_bus.h | 71 -- src/sfe_i2c.h | 60 -- src/sfe_i2c_arduino.cpp | 302 ------ src/sfe_i2c_arduino.h | 108 -- 7 files changed, 1667 insertions(+), 2112 deletions(-) create mode 100644 src/SparkFun_AS7331.cpp delete mode 100644 src/sfe_as7331_registers.h delete mode 100644 src/sfe_bus.h delete mode 100644 src/sfe_i2c.h delete mode 100644 src/sfe_i2c_arduino.cpp delete mode 100644 src/sfe_i2c_arduino.h diff --git a/src/SparkFun_AS7331.cpp b/src/SparkFun_AS7331.cpp new file mode 100644 index 0000000..c794859 --- /dev/null +++ b/src/SparkFun_AS7331.cpp @@ -0,0 +1,967 @@ +#include "SparkFun_AS7331.h" + +// TODO: FIX +bool SfeAS7331Driver::begin(sfeBusDevice *theBus, sfeBusDeviceSettings *devSettings, const uint8_t deviceAddress) +{ + setCommunicationBus(theBus, devSettings); + return begin(deviceAddress); +} + +// TODO: FIX +bool SfeAS7331Driver::begin(sfeBusDevice *theBus, uint8_t deviceAddress) +{ + setCommunicationBus(theBus); + return begin(deviceAddress); +} + +// TODO: FIX +bool SfeAS7331Driver::begin(uint8_t deviceAddress) +{ + if (!_sfeBus) + setCommunicationBus(); + + setDeviceAddress(deviceAddress); + + _sfeBus->begin(); + + return init(); +} + +// TODO: FIX +bool SfeAS7331Driver::init(bool runSetup) +{ + if (SFE_BUS_OK != _sfeBus->ping(_devSettings)) + return false; + + reset(); + + if (!isConnected()) + return false; + + if (runSetup) + return runDefaultSetup(); + + return true; +} + +// bool SfeAS7331Driver::isConnected(void) +// { +// return (AS7331_DEFAULT_DEV_ID == getDeviceID()); +// } + +uint8_t SfeAS7331Driver::getDeviceID(void) +{ + uint8_t devID; + + if (SFE_BUS_OK != _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_AGEN, &devID, 1U)) + return 0; + + return devID; +} + +// TODO: FIX +bool SfeAS7331Driver::runDefaultSetup(bool runSoftReset = false) +{ + if (runSoftReset) + return reset(); + else + { + _state = stateDefault; + _config = configDefault; + measures = measuresDefault; + } + + sfe_as7331_reg_cfg_osr_t osr; + if (SFE_BUS_OK != getOSR(&osr)) + return false; + + uint8_t regs[6]; + if (SFE_BUS_OK != readRegister(SFE_AS7331_REGISTER_CFG_CREG1, regs, 6U)) + return false; + + sfe_as7331_reg_cfg_creg1_t creg1 = {.byte = regs[0]}; + sfe_as7331_reg_cfg_creg2_t creg2 = {.byte = regs[1]}; + sfe_as7331_reg_cfg_creg3_t creg3 = {.byte = regs[2]}; + sfe_as7331_reg_cfg_break_t breakreg = regs[3]; + sfe_as7331_reg_cfg_edges_t edgesreg = regs[4]; + sfe_as7331_reg_cfg_optreg_t optreg = {.byte = regs[5]}; + + osr.ss = _state.ss; + osr.pd = _state.pd; + osr.dos = _state.opMode; + + creg1.gain = _config.sensorGain; + creg1.time = _config.conversionTime; + + creg2.en_tm = _config.enableTempConv; + creg2.en_div = _config.dividerEnabled; + creg2.div = _config.dividerRange; + + creg3.mmode = _state.mmode; + creg3.sb = _state.sb; + creg3.rdyod = _config.readyPinMode; + creg3.cclk = _config.cclk; + + breakreg = _config.breakTime; + + edgesreg = _config.numEdges; + + optreg.init_idx = _config.indexMode; + + if (SFE_BUS_OK != setOSR(&osr)) + return false; + + regs[0] = creg1.byte; + regs[1] = creg2.byte; + regs[2] = creg3.byte; + regs[3] = breakreg; + regs[4] = edgesreg; + regs[5] = optreg.byte; + + if (SFE_BUS_OK != writeRegister(SFE_AS7331_REGISTER_CFG_CREG1, regs, 6U)) + return false; + + calculateConversionFactors(); + + return true; +} + +// TODO: FIX +bool SfeAS7331Driver::prepareMeasurement(const as7331_meas_mode_t measMode = MEAS_MODE_CONT, bool startMeasure = false) +{ + if (_state.pd == POWER_DOWN_ENABLE) + if (SFE_BUS_OK != setPowerState(POWER_DOWN_DISABLE)) + return false; + + if (_state.mmode != measMode) + { + if (SFE_BUS_OK != setStandbyMode(STANDBY_DISABLED)) + return false; + if (SFE_BUS_OK != setMeasurementMode(measMode)) + return false; + } + + if (_state.opMode != DEVICE_MODE_MEAS) + if (SFE_BUS_OK != setOperationMode(DEVICE_MODE_MEAS)) + return false; + + if (startMeasure) + if (SFE_BUS_OK != setStartStateMode(START_STATE_ENABLED)) + return false; + + return true; +} + +// TODO: FIX +int8_t SfeAS7331Driver::setCommunicationBus(sfeBusDevice *theBus, sfeBusDeviceSettings *deviceSettings) +{ + int8_t result = setCommunicationBus(theBus); + if (SFE_BUS_OK != result) + return result; + + return setCommunicationDevSettings(deviceSettings); +} + +// TODO: FIX +int8_t SfeAS7331Driver::setCommunicationBus(sfeBusDevice *theBus = nullptr) +{ + if (theBus == nullptr) + theBus = new sfeBusDevice(); + + if (!theBus) + return SFE_BUS_E_NULL_PTR; + + _sfeBus = theBus; + + return SFE_BUS_OK; +} + +// TODO: FIX +int8_t SfeAS7331Driver::setCommunicationDevSettings(sfeBusDeviceSettings *deviceSettings = nullptr) +{ + if (deviceSettings == nullptr) + deviceSettings = new sfeBusDeviceSettings(); + + if (!deviceSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + _devSettings = deviceSettings; + + return SFE_BUS_OK; +} + +// TODO: FIX +int8_t SfeAS7331Driver::setDeviceAddress(const uint8_t deviceAddress) +{ + int8_t result = SFE_BUS_OK; + + if (!_devSettings) + result = setCommunicationDevSettings(); + + if (SFE_BUS_OK != result) + return result; + + _devSettings->devAddr = deviceAddress; + + return SFE_BUS_OK; +} + +bool SfeAS7331Driver::reset(void) +{ + sfe_as7331_reg_cfg_osr_t osr; + + if (SFE_BUS_OK != getOSR(osr)) + return false; + + osr.sw_res = 1; + + if (SFE_BUS_OK != setOSR(osr)) + return false; + + setDefaultSettings(); + + return true; +} + +int8_t SfeAS7331Driver::readTemp(void) +{ + uint8_t tempRaw[2]; + + int8_t result = _theBus->readRegisterRegion(SFE_AS7331_REGISTER_MEAS_TEMP, tempRaw, 2U); + + if (SFE_BUS_OK != result) + return result; + + _temperature = convertRawTempToTempC((uint16_t)tempRaw[1] << 8 | tempRaw[0]); + + return SFE_BUS_OK; +} + +int8_t SfeAS7331Driver::readUVA(void) +{ + return readRawUV(AS7331_UVA); +} + +int8_t SfeAS7331Driver::readUVB(void) +{ + return readRawUV(AS7331_UVB); +} + +int8_t SfeAS7331Driver::readUVC(void) +{ + return readRawUV(AS7331_UVC); +} + +int8_t SfeAS7331Driver::readAllUV(void) +{ + uint8_t dataRaw[6]; + + int8_t result = _theBus->readRegisterRegion(SFE_AS7331_REGISTER_MEAS_MRES1, dataRaw, 6U); + + if (SFE_BUS_OK != result) + return result; + + if (_mmode == MEAS_MODE_SYND) + { + result = readOutConv(); + + if (SFE_BUS_OK != result) + return result; + + float convFactor = 1.0f / (((float)_outputConversionTime) * ((float)(1 << (11 - _sensorGain)))); + + _uva = convertRawUVVal(((uint16_t)dataRaw[1] << 8 | dataRaw[0]) * _fsrA, convFactor); + _uvb = convertRawUVVal(((uint16_t)dataRaw[3] << 8 | dataRaw[2]) * _fsrB, convFactor); + _uvc = convertRawUVVal(((uint16_t)dataRaw[5] << 8 | dataRaw[4]) * _fsrC, convFactor); + } + else + { + _uva = convertRawUVVal((uint16_t)dataRaw[1] << 8 | dataRaw[0], _conversionA); + _uvb = convertRawUVVal((uint16_t)dataRaw[3] << 8 | dataRaw[2], _conversionB); + _uvc = convertRawUVVal((uint16_t)dataRaw[5] << 8 | dataRaw[4], _conversionC); + } + + return SFE_BUS_OK; +} + +int8_t SfeAS7331Driver::readAll(void) +{ + uint8_t dataRaw[8]; + + int8_t result = _theBus->readRegisterRegion(SFE_AS7331_REGISTER_MEAS_TEMP, dataRaw, 8U); + + if (SFE_BUS_OK != result) + return result; + + result = readOutConv(); + + if (SFE_BUS_OK != result) + return result; + + if (_mmode == MEAS_MODE_SYND) + { + float convFactor = 1.0f / (((float)_outputConversionTime) * ((float)(1 << (11 - _sensorGain)))); + + _uva = convertRawUVVal(((uint16_t)dataRaw[3] << 8 | dataRaw[2]) * _fsrA, convFactor); + _uvb = convertRawUVVal(((uint16_t)dataRaw[5] << 8 | dataRaw[4]) * _fsrB, convFactor); + _uvc = convertRawUVVal(((uint16_t)dataRaw[7] << 8 | dataRaw[6]) * _fsrC, convFactor); + } + else + { + _uva = convertRawUVVal((uint16_t)dataRaw[3] << 8 | dataRaw[2], _conversionA); + _uvb = convertRawUVVal((uint16_t)dataRaw[5] << 8 | dataRaw[4], _conversionB); + _uvc = convertRawUVVal((uint16_t)dataRaw[7] << 8 | dataRaw[6], _conversionC); + } + + _temperature = convertRawTempToTempC((uint16_t)dataRaw[1] << 8 | dataRaw[0]); + + return SFE_BUS_OK; +} + +int8_t SfeAS7331Driver::readOutConv(void) +{ + uint8_t tconvRaw[4]; + + int8_t result = _theBus->readRegisterRegion(SFE_AS7331_REGISTER_MEAS_OUTCONV_L, tconvRaw, 4U); + + if (SFE_BUS_OK != result) + return result; + + _outputConversionTime = (uint32_t)(((uint32_t)tconvRaw[3] << 24) | + ((uint32_t)tconvRaw[2] << 16) | + ((uint32_t)tconvRaw[1] << 8) | + tconvRaw[0]); + + return SFE_BUS_OK; +} + +as7331_gain_t SfeAS7331Driver::getGain(void) +{ + return _sensorGain; +} + +int8_t SfeAS7331Driver::setGain(const as7331_gain_t &gain) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg1_t creg1; + + result = getCReg1(creg1); + if (SFE_BUS_OK != result) + return result; + + creg1.gain = gain; + + result = setCReg1(creg1); + if (SFE_BUS_OK != result) + return result; + + _sensorGain = gain; + + calculateConversionFactors(); + + return SFE_BUS_OK; +} + +as7331_conv_clk_freq_t SfeAS7331Driver::getCClk(void) +{ + return _cclk; +} + +int8_t SfeAS7331Driver::setCClk(const as7331_conv_clk_freq_t &cclk) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg3_t creg3; + + result = getCReg1(creg3); + if (SFE_BUS_OK != result) + return result; + + creg3.cclk = cclk; + + result = setCReg1(creg3); + if (SFE_BUS_OK != result) + return result; + + _cclk = cclk; + + calculateConversionFactors(); + + return SFE_BUS_OK; +} + +as7331_conv_time_t SfeAS7331Driver::getConversionTime(void) +{ + return _conversionTime; +} + +int8_t SfeAS7331Driver::setConversionTime(const as7331_conv_time_t &convTime) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg1_t creg1; + + result = getCReg1(creg1); + if (SFE_BUS_OK != result) + return result; + + creg1.time = convTime; + + result = setCReg1(creg1); + if (SFE_BUS_OK != result) + return result; + + _conversionTime = convTime; + + calculateConversionFactors(); + + return SFE_BUS_OK; +} + +as7331_ready_pin_mode_t SfeAS7331Driver::getReadyPinMode(void) +{ + return _readyPinMode; +} + +int8_t SfeAS7331Driver::setReadyPinMode(const bool &pinMode) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg3_t creg3; + + result = getCReg3(creg3); + if (SFE_BUS_OK != result) + return result; + + creg3.rdyod = pinMode; + + result = setCReg3(creg3); + if (SFE_BUS_OK != result) + return result; + + _readyPinMode = pinMode; + + return SFE_BUS_OK; +} + +bool SfeAS7331Driver::getDigitalDividerEnabled(void) +{ + return _dividerEnabled; +} + +// TODO: FIX +int8_t SfeAS7331Driver::setDigitalDividerEnabled(const bool &isEnabled) +{ + // TODO: REDO THIS + if (_dividerEnabled == isEnabled) + return SFE_BUS_OK; + + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg2_t creg2; + + result = getCReg2(creg2); + if (SFE_BUS_OK != result) + return result; + + creg2.en_div = isEnabled; + + result = setCReg2(creg2); + if (SFE_BUS_OK != result) + return result; + + _dividerEnabled = isEnabled; + + if (isEnabled == DIVIDER_ENABLED) + calculateConversionFactors(); + + return SFE_BUS_OK; +} + +as7331_divider_val_t SfeAS7331Driver::getDigitalDividerRange(void) +{ + return _dividerRange; +} + +// TODO: FIX +int8_t SfeAS7331Driver::setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &setEnableDiv = false) +{ + // TODO: REDO THIS + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg2_t creg2; + + result = getCReg2(&creg2); + if (SFE_BUS_OK != result) + return result; + + creg2.div = divider; + + result = setCReg2(&creg2); + if (SFE_BUS_OK != result) + return result; + + _config.dividerRange = divider; + + if (setEnableDiv) + result = setEnableDivider(DIVIDER_ENABLED); + else + calculateConversionFactors(); + + return SFE_BUS_OK; +} + +bool SfeAS7331Driver::getSyndTempConversionEnabled(void) +{ + return _tempConvEnabled; +} + +int8_t SfeAS7331Driver::setSyndTempConversionEnabled(const bool &isEnabled) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg2_t creg2; + + result = getCReg2(creg2); + if (SFE_BUS_OK != result) + return result; + + creg2.en_tm = isEnabled; + + result = setCReg2(creg2); + if (SFE_BUS_OK != result) + return result; + + _tempConvEnabled = isEnabled; + + return SFE_BUS_OK; +} + +bool SfeAS7331Driver::getIndexMode(void) +{ + return _indexMode; +} + +int8_t SfeAS7331Driver::setIndexMode(const bool &indexMode) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_optreg_t optreg; + + result = getOptIndex(optreg); + if (SFE_BUS_OK != result) + return result; + + optreg.init_idx = indexMode; + + result = setOptIndex(optreg); + if (SFE_BUS_OK != result) + return result; + + _indexMode = indexMode; + + return SFE_BUS_OK; +} + +uint8_t SfeAS7331Driver::getBreakTime(void) +{ + return _breakTime; +} + +int8_t SfeAS7331Driver::setBreakTime(const uint8_t &breakTime) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_break_t breakreg; + + result = getBreak(breakreg); + if (SFE_BUS_OK != result) + return result; + + breakreg = breakTime; + + result = setBreak(breakreg); + if (SFE_BUS_OK != result) + return result; + + _breakTime = breakTime; + + return SFE_BUS_OK; +} + +uint8_t SfeAS7331Driver::getNumEdges(void) +{ + return _numEdges; +} + +int8_t SfeAS7331Driver::setNumEdges(const uint8_t &numEdges) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_edges_t edgesreg; + + result = getEdges(edgesreg); + if (SFE_BUS_OK != result) + return result; + + edgesreg = numEdges; + + result = setEdges(edgesreg); + if (SFE_BUS_OK != result) + return result; + + _numEdges = numEdges; + + return SFE_BUS_OK; +} + +bool SfeAS7331Driver::getPowerDownState(void) +{ + return _powerDownEnableState; +} + +int8_t SfeAS7331Driver::setPowerDownState(const bool &pd) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_osr_t osr; + + result = getOSR(osr); + if (SFE_BUS_OK != result) + return result; + + osr.pd = pd; + + result = setOSR(osr); + if (SFE_BUS_OK != result) + return result; + + _powerDownEnableState = pd; + + return SFE_BUS_OK; +} + +as7331_device_op_state_t SfeAS7331Driver::getOperationMode(void) +{ + return _opMode; +} + +int8_t SfeAS7331Driver::setOperationMode(const as7331_device_op_state_t &opMode) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_osr_t osr; + + result = getOSR(osr); + if (SFE_BUS_OK != result) + return result; + + osr.dos = opMode; + + result = setOSR(osr); + if (SFE_BUS_OK != result) + return result; + + _opMode = opMode; + + return SFE_BUS_OK; +} + +as7331_meas_mode_t SfeAS7331Driver::getMeasurementMode(void) +{ + return _mmode; +} + +int8_t SfeAS7331Driver::setMeasurementMode(const as7331_meas_mode_t &measMode) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg3_t creg3; + + result = getCReg3(creg3); + if (SFE_BUS_OK != result) + return result; + + creg3.mmode = measMode; + + result = setCReg3(creg3); + if (SFE_BUS_OK != result) + return result; + + _mmode = measMode; + + return SFE_BUS_OK; +} + +bool SfeAS7331Driver::getStandbyState(void) +{ + return _standbyState; +} + +int8_t SfeAS7331Driver::setStandbyState(const bool &standby) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_creg3_t creg3; + + result = getCReg3(creg3); + if (SFE_BUS_OK != result) + return result; + + creg3.sb = standby; + + result = setCReg3(creg3); + if (SFE_BUS_OK != result) + return result; + + _standbyState = standby; + + return SFE_BUS_OK; +} + +bool SfeAS7331Driver::getStartState(void) +{ + return _startState; +} + +int8_t SfeAS7331Driver::setStartState(const bool &startState) +{ + int8_t result = SFE_BUS_OK; + + sfe_as7331_reg_cfg_osr_t osr; + + result = getOSR(osr); + if (SFE_BUS_OK != result) + return result; + + osr.ss = startState; + + result = setOSR(osr); + if (SFE_BUS_OK != result) + return result; + + _startState = startState; + + return SFE_BUS_OK; +} + +// TODO: add state check +int8_t SfeAS7331Driver::getStatus(sfe_as7331_reg_meas_osr_status_t &statusReg) +{ + int8_t result = SFE_BUS_OK; + uint8_t statusRaw[2]; + + result = readRegisterRegion(SFE_AS7331_REGISTER_MEAS_OSR_STATUS, statusRaw, 2U); + + if (SFE_BUS_OK != result) + return result; + + // Shift MSB to top of 16-bit register, then OR with LSB. + statusReg.word = (uint16_t)statusRaw[1] << 8 | statusRaw[0]; + + return SFE_BUS_OK; +} + +// TODO: add state check +int8_t SfeAS7331Driver::getOSR(sfe_as7331_reg_cfg_osr_t &osrReg) +{ + return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_OSR, osrReg.byte, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::setOSR(const sfe_as7331_reg_cfg_osr_t &osrReg) +{ + return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_OSR, osrReg.byte, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::getCReg1(sfe_as7331_reg_cfg_creg1_t &creg1) +{ + return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG1, creg1.byte, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::setCReg1(const sfe_as7331_reg_cfg_creg1_t &creg1) +{ + return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG1, creg1.byte, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::getCReg2(sfe_as7331_reg_cfg_creg2_t &creg2) +{ + return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG2, creg2.byte, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::setCReg2(const sfe_as7331_reg_cfg_creg2_t &creg2) +{ + return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG2, creg2.byte, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::getCReg3(sfe_as7331_reg_cfg_creg3_t &creg3) +{ + return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG3, creg3.byte, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::setCReg3(const sfe_as7331_reg_cfg_creg3_t &creg3) +{ + return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG3, creg3.byte, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::getBreak(uint8_t &breakReg) +{ + return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_BREAK, breakReg, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::setBreak(const uint8_t &breakReg) +{ + return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_BREAK, breakReg, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::getEdges(uint8_t &edgesReg) +{ + return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_EDGES, edgesReg, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::setEdges(const uint8_t &edgesReg) +{ + return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_EDGES, edgesReg, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::getOptIndex(sfe_as7331_reg_cfg_optreg_t &optReg) +{ + return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_OPTREG, optReg.byte, 1U); +} + +// TODO: add state check +int8_t SfeAS7331Driver::setOptIndex(const sfe_as7331_reg_cfg_optreg_t &optReg) +{ + return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_OPTREG, optReg.byte, 1U); +} + +int8_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) +{ + uint8_t uvRawVal[2]; + uint8_t address = 0; + float *fsr = nullptr; + float *retUV = nullptr; + + if (_mmode != MEAS_MODE_SYND) // There's a conversion factor already available. + float *conv = nullptr; + + switch (uv_type) + { + case AS7331_UVA: + default: // Since it's an enum, you can't miscall this function, so default to A. + address = SFE_AS7331_REGISTER_MEAS_MRES1; + fsr = &_fsrA; + retUV = &_uva; + if (_mmode != MEAS_MODE_SYND) + { + conv = &_conversionA; + } + break; + case AS7331_UVB: + address = SFE_AS7331_REGISTER_MEAS_MRES2; + fsr = &_fsrB; + retUV = &_uvb; + if (_mmode != MEAS_MODE_SYND) + { + conv = &_conversionB; + } + break; + case AS7331_UVC: + address = SFE_AS7331_REGISTER_MEAS_MRES3; + fsr = &_fsrC; + retUV = &_uvc; + if (_mmode != MEAS_MODE_SYND) + { + conv = &_conversionC; + } + break; + } + + int8_t result = _theBus->readRegisterRegion(address, uvRawVal, 2U); + + if (SFE_BUS_OK != result) + return result; + + if (_mmode == MEAS_MODE_SYND) + { + result = readOutConv(); + + if (SFE_BUS_OK != result) + return result; + + // See datasheet section 7.4 Equation 4. + // fsrX + // -------------- + // gain * outConv + float convFactor = (*fsr) / (((float)_outputConversionTime) * ((float)(1 << (11 - _sensorGain)))); + + *retUV = convertRawUVVal((uint16_t)uvRawVal[1] << 8 | uvRawVal[0], convFactor); + } + else + { + *retUV = convertRawUVVal((uint16_t)uvRawVal[1] << 8 | uvRawVal[0], conv); + } + + return SFE_BUS_OK; +} + +float SfeAS7331Driver::convertRawTempToTempC(const uint16_t &inputVal) +{ + // T_chip = TEMP*0.05 - 66.9 + // EX: TEMP=0x922 aka TEMP=0d2338, returns 50.0 + return ((float)(inputVal) * 0.05f) - 66.9f; +} + +float SfeAS7331Driver::convertRawUVVal(const uint16_t &rawVal, const float &convFactor) +{ + // If the divider is enabled, then we need to include the division factor. + float divFactor = _dividerEnabled ? (float)(1 << (1 + _dividerRange)) : 1.0f; + + return ((float)rawVal) * divFactor * convFactor; +} + +void SfeAS7331Driver::calculateConversionFactors(void) +{ + // See datasheet section 7.4 Equation 3. + // fsrX + // ------------------- + // gain * tconv * cclk + // Calculates a conversion factor based on the sensor gain, conversion time, and conversion clock frequency. + float convFactor = 1.0f / (((float)(1 << (11 - _sensorGain))) * ((float)(1 << _conversionTime)) * + ((float)(1024.0 * (1 << _cclk)))); + + _conversionA = _fsrA * convFactor; + _conversionB = _fsrB * convFactor; + _conversionC = _fsrC * convFactor; +} + +void SfeAS7331Driver::setDefaultSettings() +{ + _breakTime = 25; // 25 * 8us = 200us. + _numEdges = 1; // 1 edge. + _readyPinMode = false; // Push-pull. + _dividerEnabled = false; // Predivider disabled. + _tempConvEnabled = true; // Temp conversion in synd mode enabled. + _indexMode = true; // Repeat start enabled. + _standbyState = false; // Not in standby mode. + _startState = false; // Not started. + _powerDownEnableState = true; // Device is powered down. + _opMode = DEVICE_MODE_CFG; // Device is in configuration mode. + _sensorGain = GAIN_2; // Gain of 2x. + _cclk = CCLK_1_024_MHZ; // 1.024 MHz conversion clock + _mmode = MEAS_MODE_CMD; // Command/One Shot Mode. + _conversionTime = TIME_64MS; // 64 ms conversion time. + _dividerRange = DIV_2; // Predivider 2x. +} \ No newline at end of file diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h index 869cc82..da2670f 100644 --- a/src/SparkFun_AS7331.h +++ b/src/SparkFun_AS7331.h @@ -14,1312 +14,715 @@ Copyright (c) 2023 SparkFun Electronics Name: SparkFun_AS7331.h - + Description: - The SfeAS7331ArdI2C is a templated class that defines Arduino specific - behavior for initializing devices. It inherits from the base C++ class - SfeAS7331Base which defines the functions for interacting with the AS7331 - Spectral UV Sensor. + TODO: Redo this description. */ #pragma once -#include "sfe_as7331_registers.h" +#include #include -#include "sfe_i2c_arduino.h" - -/// @brief Measures structure to hold return values. -typedef struct { - float uva; - float uvb; - float uvc; - float temperature; - uint32_t outputConversionTime; -} sfeAS7331_measures_t; - -/// @brief Configuration struct to hold local sensor configuration. -typedef struct { - as7331_gain_t sensorGain; - as7331_conv_clk_freq_t cclk; - as7331_conv_time_t conversionTime; - as7331_ready_pin_mode_t readyPinMode; - as7331_divider_enable_t dividerEnabled; - as7331_divider_val_t dividerRange; - as7331_ext_syn_temp_meas_t enableTempConv; - as7331_simple_reg_read_mode_t indexMode; - uint8_t breakTime; - uint8_t numEdges; -} sfeAS7331_config_t; - -/// @brief State struct to hold local sensor state. -typedef struct { - as7331_power_state_t pd; - as7331_device_op_state_t opMode; - as7331_meas_mode_t mmode; - as7331_standby_mode_t sb; - as7331_startstate_t ss; -} sfeAS7331_state_t; - -/// @brief Default measures struct for initialization and reset. -const sfeAS7331_measures_t measuresDefault = { - .uva = (uint16_t)(-1), - .uvb = (uint16_t)(-1), - .uvc = (uint16_t)(-1), - .temperature = (uint16_t)(-1), - .outputConversionTime = (uint32_t)(-1) - }; - -/// @brief Default configuration struct for initialization and reset. -const sfeAS7331_config_t configDefault = { - .sensorGain = GAIN_2, - .cclk = CCLK_1_024_MHZ, - .conversionTime = TIME_64MS, - .readyPinMode = READYPIN_PUSHPULL, - .dividerEnabled = DIVIDER_DISABLED, - .dividerRange = DIV_2, - .enableTempConv = SYN_TEMP_ENABLED, - .indexMode = INDEX_REPEAT_START, - .breakTime = 25, // 25 * 8us = 200us - .numEdges = 1 - }; - -/// @brief Default state struct for initialization and reset. -const sfeAS7331_state_t stateDefault = { - .pd = POWER_DOWN_ENABLE, - .opMode = DEVICE_MODE_CFG, - .mmode = MEAS_MODE_CMD, - .sb = STANDBY_DISABLED, - .ss = START_STATE_DISABLED - }; - -/// @class SfeAS7331Base -/// @brief Template device class for the AS7331 Spectral UV Sensor. -template -class SfeAS7331Base { - public: - SfeAS7331Base(){ - _state = stateDefault; - - _config = configDefault; - - measures = measuresDefault; - }; - - /// @brief This method is called to initialize the AS7331 device through the specified bus. - /// @param theBus Pointer to the bus object. - /// @param devSettings Pointer to the device settings object. - /// @param deviceAddress I2C address for the device. - /// @return True if successful, false if it fails. - bool begin(sfeBusDevice *theBus, sfeBusDeviceSettings *devSettings, const uint8_t deviceAddress = AS7331_ADDR_DEFAULT) - { - setCommunicationBus(theBus, devSettings); - return begin(deviceAddress); - } - - /// @brief This method is called to initialize the AS7331 device through the specified bus. - /// @param theBus Pointer to the bus object. - /// @param deviceAddress I2C address for the device. - /// @return True if successful, false if it fails. - bool begin(sfeBusDevice *theBus, uint8_t deviceAddress = AS7331_ADDR_DEFAULT) - { - setCommunicationBus(theBus); - return begin(deviceAddress); - } - - /// @brief This method is called to initialize the AS7331 device at the specified bus address. - /// @param deviceAddress I2C address for the device. - /// @return True if successful, false if it fails. - bool begin(uint8_t deviceAddress = AS7331_ADDR_DEFAULT) - { - if(!_sfeBus) - setCommunicationBus(); - - setDeviceAddress(deviceAddress); - - _sfeBus->begin(); - - return init(); - } - - /// @brief Finds the device and initializes the default state. - /// @param runSetup Flag to run the default setup if set. - /// @return True if successful, false if it fails. - bool init(bool runSetup = true) - { - if(SFE_BUS_OK != _sfeBus->ping(_devSettings)) - return false; - - reset(); - - if(!isConnected()) - return false; - - if(runSetup) - return runDefaultSetup(); - - return true; - } - - /// @brief Writes to the data to the given register. - /// @param offset The register to write to. - /// @param data The data to write to the register. - /// @param length The number of writes - /// @return The SFE_BUS status code on the write to the given register. - int8_t writeRegister(const uint8_t offset, const uint8_t *data, const uint16_t length = 1) - { - return _sfeBus->writeRegisterBytes((SFEBusDevSettings*)_devSettings, offset, data, length); - } - - /// @brief Reads data from the specified register. - /// @param offset The register to read from. - /// @param data The pointer to the value to store the value. - /// @param length The number of reads - /// @return The SFE_BUS status code on the read to the given register. - int8_t readRegister(const uint8_t offset, uint8_t *data, const uint16_t length = 1) - { - return _sfeBus->readRegisterBytes((SFEBusDevSettings*)_devSettings, offset, data, length); - } - - /// @brief Checks to see if the AS7331 is connected. - /// @return True if successful, false otherwise. - bool isConnected(void) - { - return (AS7331_DEFAULT_DEV_ID == getDeviceID()); - } - - /// @brief Requests the device ID from the sensor. - /// @return The device ID of the sensor.` - uint8_t getDeviceID(void) - { - uint8_t devID; - - if(SFE_BUS_OK != readRegister(SFE_AS7331_REGISTER_CFG_AGEN, &devID)) - return 0; - - return devID; - } - - /// @brief Helper class that sets up the sensor and state in the POR configuration. - /// @param runSoftReset Flag that runs the soft reset function to reset the device. - /// @return True if successful, false otherwise. - bool runDefaultSetup(bool runSoftReset = false) - { - if(runSoftReset) - return reset(); - else - { - _state = stateDefault; - _config = configDefault; - measures = measuresDefault; - } - - sfe_as7331_reg_cfg_osr_t osr; - if(SFE_BUS_OK != getOSR(&osr)) - return false; - - uint8_t regs[6]; - if(SFE_BUS_OK != readRegister(SFE_AS7331_REGISTER_CFG_CREG1, regs, 6U)) - return false; - - sfe_as7331_reg_cfg_creg1_t creg1 = {.byte = regs[0]}; - sfe_as7331_reg_cfg_creg2_t creg2 = {.byte = regs[1]}; - sfe_as7331_reg_cfg_creg3_t creg3 = {.byte = regs[2]}; - sfe_as7331_reg_cfg_break_t breakreg = regs[3]; - sfe_as7331_reg_cfg_edges_t edgesreg = regs[4]; - sfe_as7331_reg_cfg_optreg_t optreg = {.byte = regs[5]}; - - osr.ss = _state.ss; - osr.pd = _state.pd; - osr.dos = _state.opMode; - - creg1.gain = _config.sensorGain; - creg1.time = _config.conversionTime; - - creg2.en_tm = _config.enableTempConv; - creg2.en_div = _config.dividerEnabled; - creg2.div = _config.dividerRange; - - creg3.mmode = _state.mmode; - creg3.sb = _state.sb; - creg3.rdyod = _config.readyPinMode; - creg3.cclk = _config.cclk; - - breakreg = _config.breakTime; - - edgesreg = _config.numEdges; - - optreg.init_idx = _config.indexMode; - - if(SFE_BUS_OK != setOSR(&osr)) - return false; - - regs[0] = creg1.byte; - regs[1] = creg2.byte; - regs[2] = creg3.byte; - regs[3] = breakreg; - regs[4] = edgesreg; - regs[5] = optreg.byte; - - if(SFE_BUS_OK != writeRegister(SFE_AS7331_REGISTER_CFG_CREG1, regs, 6U)) - return false; - - calculateConversionFactors(); - - return true; - } - - /// @brief Puts the sensor in the specified measurement mode. - /// @param measMode Measurement mode to enter. - /// @param startMeasure Flag to start measuring immediately if set. - /// @return True if successful, false otherwise. - bool startMeasurement(const as7331_meas_mode_t measMode = MEAS_MODE_CONT, bool startMeasure = false) - { - if(_state.pd == POWER_DOWN_ENABLE) - if(SFE_BUS_OK != setPowerState(POWER_DOWN_DISABLE)) return false; - - if(_state.mmode != measMode) - { - if(SFE_BUS_OK != setStandbyMode(STANDBY_DISABLED)) - return false; - if(SFE_BUS_OK != setMeasurementMode(measMode)) - return false; - } - - if(_state.opMode != DEVICE_MODE_MEAS) - if(SFE_BUS_OK != setOperationMode(DEVICE_MODE_MEAS)) return false; - - if(startMeasure) - if(SFE_BUS_OK != setStartStateMode(START_STATE_ENABLED)) return false; - - return true; - } - - /// @brief Sets the communication bus to the specified bus and device settings. - /// @param theBus Bus to set as the communication device. - /// @param deviceSettings Device settings to use when setting the bus for this sensor. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCommunicationBus(sfeBusDevice *theBus, sfeBusDeviceSettings *deviceSettings) - { - int8_t result = setCommunicationBus(theBus); - if(SFE_BUS_OK != result) - return result; - - return setCommunicationDevSettings(deviceSettings); - } - - /// @brief Sets teh communication bus to the specified bus. Creates new instance if none specified. - /// @param theBus Bus to set as the communication devie. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCommunicationBus(sfeBusDevice *theBus = nullptr) - { - if(theBus == nullptr) - theBus = new sfeBusDevice(); - - if(!theBus) - return SFE_BUS_E_NULL_PTR; - - _sfeBus = theBus; - - return SFE_BUS_OK; - } - - /// @brief Sets settings of this sensor's device. - /// @param deviceSettings Settings structure for this device. Creates new instance if none specified. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCommunicationDevSettings(sfeBusDeviceSettings *deviceSettings = nullptr) - { - if(deviceSettings == nullptr) - deviceSettings = new sfeBusDeviceSettings(); - - if(!deviceSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - _devSettings = deviceSettings; - - return SFE_BUS_OK; - } - - /// @brief Sets the address that the bus uses to communicate with the sensor. - /// @param deviceAddress Device address to use. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setDeviceAddress(const uint8_t deviceAddress) - { - int8_t result = SFE_BUS_OK; - - if(!_devSettings) - result = setCommunicationDevSettings(); - - if(SFE_BUS_OK != result) - return result; - - _devSettings->devAddr = deviceAddress; - - return SFE_BUS_OK; - } - - /// @brief Performs a soft reset of the sensor. - /// @return True if successful, false otherwise. - bool reset(void) - { - sfe_as7331_reg_cfg_osr_t osr; - - - if(SFE_BUS_OK != getOSR(&osr)) - return false; - - osr.sw_res = 1; - - if(SFE_BUS_OK != setOSR(&osr)) - return false; - - _state = stateDefault; - _config = configDefault; - measures = measuresDefault; - - return true; - } - - /// @brief Reads the sensor's temperature, converts it to a usable form, and saves it to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readTemp(void) - { - uint8_t tempRaw[2]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_TEMP, tempRaw, 2U); - - if(SFE_BUS_OK != result) - return result; - - measures.temperature = convertRawTempToTempC((uint16_t)(((uint16_t)tempRaw[1] << 8 | tempRaw[0]))); - - return SFE_BUS_OK; - } - - /// @brief Reads the sensor's UVA register, converts it to a usable form, and saves it to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readUVA(void) - { - uint8_t uvaRaw[2]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES1, uvaRaw, 2U); - - if(SFE_BUS_OK != result) - return result; - - if(_state.mmode == MEAS_MODE_SYND) { - result = readOutConv(); - - if(SFE_BUS_OK != result) - return result; - - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); - measures.uva = (float)((uint16_t)(((uint16_t)uvaRaw[1]) << 8 | uvaRaw[0])-1.0f)*fsrA*divFactor/convFactor; - } - else { - measures.uva = (float)((uint16_t)(((uint16_t)uvaRaw[1]) << 8 | uvaRaw[0])-1.0f)*_conversionA; - } - - return SFE_BUS_OK; - } - - /// @brief Reads the sensor's UVB register, converts it to a usable form, and saves it to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readUVB(void) - { - uint8_t uvbRaw[2]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES2, uvbRaw, 2U); - - if(SFE_BUS_OK != result) - return result; - - if(_state.mmode == MEAS_MODE_SYND) { - result = readOutConv(); - - if(SFE_BUS_OK != result) - return result; - - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); - measures.uvb = (float)((uint16_t)(((uint16_t)uvbRaw[1]) << 8 | uvbRaw[0])-1.0f)*fsrB*divFactor/convFactor; - } - else { - measures.uvb = (float)((uint16_t)(((uint16_t)uvbRaw[1]) << 8 | uvbRaw[0])-1.0f)*_conversionB; - } - - return SFE_BUS_OK; - } - - /// @brief Reads the sensor's UVC register, converts it to a usable form, and saves it to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readUVC(void) - { - uint8_t uvcRaw[2]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES3, uvcRaw, 2U); - - if(SFE_BUS_OK != result) - return result; - - if(_state.mmode == MEAS_MODE_SYND) { - result = readOutConv(); - - if(SFE_BUS_OK != result) - return result; - - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); - measures.uvc = (float)((uint16_t)(((uint16_t)uvcRaw[1]) << 8 | uvcRaw[0])-1.0f)*fsrC*divFactor/convFactor; - } - else { - measures.uvc = (float)((uint16_t)(((uint16_t)uvcRaw[1]) << 8 | uvcRaw[0])-1.0f)*_conversionC; - } - - return SFE_BUS_OK; - } - - /// @brief Read's all three UV registers, converts them to a usable form, then saves them to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readAllUV(void) - { - uint8_t dataRaw[6]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_MRES1, dataRaw, 6U); - - if(SFE_BUS_OK != result) - return result; - - if(_state.mmode == MEAS_MODE_SYND) { - result = readOutConv(); - - if(SFE_BUS_OK != result) - return result; - - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); - measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[1]) << 8 | dataRaw[0])-1.0f)*fsrA*divFactor/convFactor; - measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*fsrB*divFactor/convFactor; - measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*fsrC*divFactor/convFactor; - } - else { - measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[1]) << 8 | dataRaw[0])-1.0f)*_conversionA; - measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*_conversionB; - measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*_conversionC; - } - - return SFE_BUS_OK; - } - - /// @brief Read the sensor's temperature, UV, and external time conversion clock counts, converts them, and then saves them to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readAll(void) - { - uint8_t dataRaw[8]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_TEMP, dataRaw, 8U); - - if(SFE_BUS_OK != result) - return result; - - result = readOutConv(); - if(SFE_BUS_OK != result) - return result; - - if(_state.mmode == MEAS_MODE_SYND) { - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - float convFactor = ((float)measures.outputConversionTime)*((float)(1 << (11 - _config.sensorGain))); - measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*fsrA*divFactor/convFactor; - measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*fsrB*divFactor/convFactor; - measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[7]) << 8 | dataRaw[6])-1.0f)*fsrC*divFactor/convFactor; - } - else { - measures.uva = (float)((uint16_t)(((uint16_t)dataRaw[3]) << 8 | dataRaw[2])-1.0f)*_conversionA; - measures.uvb = (float)((uint16_t)(((uint16_t)dataRaw[5]) << 8 | dataRaw[4])-1.0f)*_conversionB; - measures.uvc = (float)((uint16_t)(((uint16_t)dataRaw[7]) << 8 | dataRaw[6])-1.0f)*_conversionC; - } - - measures.temperature = convertRawTempToTempC((uint16_t)(((uint16_t)dataRaw[1] << 8 | dataRaw[0]))); - - return SFE_BUS_OK; - } - - /// @brief Read the conversion clock counts register and saves it to the measures struct. - /// @return 0 if successful, negative if error, positive for warning. - int8_t readOutConv(void) - { - uint8_t tconvRaw[4]; - - int8_t result = readRegister(SFE_AS7331_REGISTER_MEAS_OUTCONV_L, tconvRaw, 4U); - - if(SFE_BUS_OK != result) - return result; - - measures.outputConversionTime = (uint32_t)(((uint32_t)tconvRaw[3] << 24) | ((uint32_t)tconvRaw[2] << 16) | ((uint32_t)tconvRaw[1] << 8) | tconvRaw[0]); - - return SFE_BUS_OK; - } - - /// @brief Getter function to get the current internal state. - /// @return internal state struct. - sfeAS7331_state_t getState(void) - { - return _state; - } - - /// @brief Getter function to get the current internal configuration. - /// @return internal configuration struct. - sfeAS7331_config_t getConfig(void) - { - return _config; - } - - /// @brief Sets the UV sensor's gain. - /// @param gain The gain to set the sensor to. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setGain(const as7331_gain_t gain) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg1_t creg1; - - result = getCReg1(&creg1); - if(SFE_BUS_OK != result) - return result; - - creg1.gain = gain; - - result = setCReg1(&creg1); - if(SFE_BUS_OK != result) - return result; - - _config.sensorGain = gain; - - calculateConversionFactors(); - - return SFE_BUS_OK; - } - - /// @brief Sets the conversion time that the sensor will run to. - /// @param convTime Conversion time to set the sensor to. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setConversionTime(const as7331_conv_time_t convTime) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg1_t creg1; - - result = getCReg1(&creg1); - if(SFE_BUS_OK != result) - return result; - - creg1.time = convTime; - - result = setCReg1(&creg1); - if(SFE_BUS_OK != result) - return result; - - _config.conversionTime = convTime; - - calculateConversionFactors(); - - return SFE_BUS_OK; - } - - /// @brief Set the sensor's internal clock speed. - /// @param cclk Clock speed to set on the sensor. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCClk(const as7331_conv_clk_freq_t cclk) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg3_t creg3; - - result = getCReg1(&creg3); - if(SFE_BUS_OK != result) - return result; - - creg3.cclk = cclk; - - result = setCReg1(&creg3); - if(SFE_BUS_OK != result) - return result; - - _config.cclk = cclk; - - calculateConversionFactors(); - - return SFE_BUS_OK; - } - - /// @brief Enables or disables the internal UV result divider. - /// @param isEnabled Enable or disable the divder. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setEnableDivider(const as7331_divider_enable_t isEnabled) - { - if(_config.dividerEnabled == isEnabled) - return SFE_BUS_OK; - - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg2_t creg2; - - result = getCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - creg2.en_div = isEnabled; - - result = setCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - _config.dividerEnabled = isEnabled; - - if(isEnabled == DIVIDER_ENABLED) - calculateConversionFactors(); - - return SFE_BUS_OK; - } - - /// @brief Sets the value of the internal UV result divider. - /// @param divider Divider value to set. - /// @param setEnableDiv Option to turn on the divider if desired. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setDigitalDivider(const as7331_divider_val_t divider, const bool setEnableDiv = false) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg2_t creg2; - - result = getCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - creg2.div = divider; - - result = setCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - _config.dividerRange = divider; - - if(setEnableDiv) - result = setEnableDivider(DIVIDER_ENABLED); - else - calculateConversionFactors(); - - return SFE_BUS_OK; - } - - /// @brief Sets the ready pin type to push-pull or open-drain. - /// @param pinMode Mode to set the ready pin to. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setReadyPinMode(const as7331_ready_pin_mode_t pinMode) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg3_t creg3; - - result = getCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - creg3.rdyod = pinMode; - - result = setCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - _config.readyPinMode = pinMode; - - return SFE_BUS_OK; - } - - /// @brief Enables or disables the temperature conversion when in the SYND mode. - /// @param isEnabled Enable or disable the feature. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setEnableTemperatureConversion(const as7331_ext_syn_temp_meas_t isEnabled) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg2_t creg2; - - result = getCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - creg2.en_tm = isEnabled; - - result = setCReg2(&creg2); - if(SFE_BUS_OK != result) - return result; - - _config.enableTempConv = isEnabled; - - return SFE_BUS_OK; - } - - /// @brief Set the index mode for compatibility with I2C controllers that don't support repeated start. - /// @param indexMode Simple or standard I2C addressing mode. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setIndexMode(const as7331_simple_reg_read_mode_t indexMode) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_optreg_t optreg; - - result = getOptIndex(&optreg); - if(SFE_BUS_OK != result) - return result; - - optreg.init_idx = indexMode; - - result = setOptIndex(&optreg); - if(SFE_BUS_OK != result) - return result; - - _config.indexMode = indexMode; - - return SFE_BUS_OK; - } - - /// @brief Set the minimum break time between measurements in CONT, SYNS, and SYND modes. - /// @param breakTime Time between measurements, 8us step time, max 2048us. A 0 value is a minimum of 3 fclk cycles. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setBreakTime(const uint8_t breakTime) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_break_t breakreg; - - result = getBreak(&breakreg); - if(SFE_BUS_OK != result) - return result; - - breakreg = breakTime; - - result = setBreak(&breakreg); - if(SFE_BUS_OK != result) - return result; - - _config.breakTime = breakTime; - - return SFE_BUS_OK; - } - - /// @brief Set the minimum number of falling edges required at the SYN input until the conversion is terminated. Only operational in SYND mode. - /// @param numEdges Number of edges prior to terminating conversion in SYND mode. 0 is not allowed, 1 is the minimum. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setNumEdges(const uint8_t numEdges) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_edges_t edgesreg; - - result = getEdges(&edgesreg); - if(SFE_BUS_OK != result) - return result; - - edgesreg = numEdges; - - result = setEdges(&edgesreg); - if(SFE_BUS_OK != result) - return result; - - _config.numEdges = numEdges; - - return SFE_BUS_OK; - } - - /// @brief Getter for the currently configured gain. - /// @return Sensor's gain expressed as (1 << (11 - gain)). - as7331_gain_t getConfigGain(void) - { - return _config.sensorGain; - } - - /// @brief Getter for the currently configured conversion clock. - /// @return Sensor's conversion clock expressed as 1024*(1 << cclk). - as7331_conv_clk_freq_t getConfigCClk(void) - { - return _config.cclk; - } - - /// @brief Getter for the currently configured conversion time. - /// @return Sensor's conversion time expressed as (1 << time). - as7331_conv_time_t getConfigConversionTime(void) - { - return _config.conversionTime; - } - - /// @brief Getter for the currently configured pin mode. - /// @return Sensor's ready pin configuration. - as7331_ready_pin_mode_t getConfigReadyPinMode(void) - { - return _config.readyPinMode; - } - - /// @brief Getter for the currently configured divider status. - /// @return Whether the sensor's divider is enabled or disabled. - as7331_divider_enable_t getConfigDividerEnabled(void) - { - return _config.dividerEnabled; - } - - /// @brief Getter for the currently configured divider range. - /// @return Sensor's internal UV predivider range. - as7331_divider_val_t getConfigDividerRange(void) - { - return _config.dividerRange; - } - - /// @brief Getter for the currently configured SYND temperature conversion status. - /// @return Whether the sensor will output temperature in SYND mode. - as7331_ext_syn_temp_meas_t getConfigExternalSyncTempConversion(void) - { - return _config.enableTempConv; - } - - /// @brief Getter for the currently configured I2C compatibility mode. - /// @return Sensor's configuration for interacting with simpler I2C controllers. - as7331_simple_reg_read_mode_t getConfigIndexMode(void) - { - return _config.indexMode; - } - - /// @brief Getter for the currently configured minimum break time in CONT, CMD, SYNS modes. - /// @return Sensor's breaktime in 8us steps. - uint8_t getConfigBreakTime(void) - { - return _config.breakTime; - } - - /// @brief Getter for the currently configured minimum number of edges to end conversion when in SYND mode. - /// @return Sensor's minimum number of edges, minimum 1 edge. - uint8_t getConfigNumEdges(void) - { - return _config.numEdges; - } - - /// @brief Gets the current power state from the sensor. - /// @param pd Pointer to an enum to store the current power state. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getPowerState(as7331_power_state_t *pd) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_osr_t osr; - - result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.pd = (as7331_power_state_t)osr.pd; - *pd = _state.pd; - - return SFE_BUS_OK; - } - - /// @brief Sets the power state of the sensor. - /// @param pd Power state to set. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setPowerState(const as7331_power_state_t pd = POWER_DOWN_DISABLE) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_osr_t osr; - - result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - osr.pd = pd; - - result = setOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.pd = pd; - - return SFE_BUS_OK; - } - - /// @brief Gets the current operation mode from the sensor. - /// @param opMode Pointer to an enum to store the current operating mode. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getOperationMode(as7331_device_op_state_t *opMode) - { - sfe_as7331_reg_cfg_osr_t osr; - int8_t result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.opMode = (as7331_device_op_state_t)osr.dos; - *opMode = _state.opMode; - - return SFE_BUS_OK; - } - - /// @brief Set the sensor's operating mode. - /// @param opMode Operating mode to set. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setOperationMode(const as7331_device_op_state_t opMode) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_osr_t osr; - - result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - osr.dos = opMode; - - result = setOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.opMode = opMode; - - return SFE_BUS_OK; - } - - /// @brief Gets the sensor's current measurement mode. - /// @param measMode Pointer to an enum to store the current measurment mode. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getMeasurementMode(as7331_meas_mode_t *measMode) - { - sfe_as7331_reg_cfg_creg3_t creg3; - int8_t result = getCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - _state.mmode = (as7331_meas_mode_t)creg3.mmode; - *measMode = _state.mmode; - - return SFE_BUS_OK; - } - - /// @brief Sets the sensor's measurement mode. - /// @param measMode Measurement mode to set. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setMeasurementMode(const as7331_meas_mode_t measMode) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg3_t creg3; - - result = getCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - creg3.mmode = measMode; - - result = setCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - _state.mmode = measMode; - - return SFE_BUS_OK; - } - - /// @brief Gets the sensor's current standby mode state. - /// @param standby Pointer to an enum to store the current standby state. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getStandbyMode(as7331_standby_mode_t *standby) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg3_t creg3; - - result = getCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - _state.sb = (as7331_standby_mode_t)creg3.sb; - *standby = _state.sb; - - return SFE_BUS_OK; - } - - /// @brief Sets the sensor's standby mode. - /// @param standby State to set. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setStandbyMode(const as7331_standby_mode_t standby) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_creg3_t creg3; - - result = getCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - creg3.sb = standby; - - result = setCReg3(&creg3); - if(SFE_BUS_OK != result) - return result; - - _state.sb = standby; - - return SFE_BUS_OK; - } - - /// @brief Gets the sensor's current start state. - /// @param startState Pointer to an enum to store the current start state. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getStartStateMode(as7331_startstate_t *startState) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_osr_t osr; - - result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.ss = (as7331_startstate_t)osr.ss; - *startState = _state.ss; - - return SFE_BUS_OK; - } - - /// @brief Sets the sensor's start state. This begins measurement. - /// @param startState Start state to set. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setStartStateMode(const as7331_startstate_t startState) - { - int8_t result = SFE_BUS_OK; - - sfe_as7331_reg_cfg_osr_t osr; - - result = getOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - osr.ss = startState; - - result = setOSR(&osr); - if(SFE_BUS_OK != result) - return result; - - _state.ss = startState; - - return SFE_BUS_OK; - } - - /// @brief Getter for the current power state. - /// @return Sensor's power state. - as7331_power_state_t getStatePower(void) - { - return _state.pd; - } - - /// @brief Getter for the current operational state. - /// @return Sensor's operational state. - as7331_device_op_state_t getStateOperational(void) - { - return _state.opMode; - } - - /// @brief Getter for the current measurement state. - /// @return Sensor's measurement state. - as7331_meas_mode_t getStateMeasurement(void) - { - return _state.mmode; - } - - /// @brief Getter for the current standby state. - /// @return Sensor's standby state. - as7331_standby_mode_t getStateStandby(void) - { - return _state.sb; - } - - /// @brief Getter for the current start state. - /// @return Sensor's start state. - as7331_startstate_t getStateStart(void) - { - return _state.ss; - } - - /// @brief Gets the sensor's status when in measurement operation mode. - /// @param statusReg Pointer to a register struct to store the sensor's current status. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getStatus(sfe_as7331_reg_meas_osr_status_t *statusReg) - { - int8_t result = SFE_BUS_OK; - uint8_t statusRaw[2]; - - result = readRegister(SFE_AS7331_REGISTER_MEAS_OSR_STATUS, statusRaw, 2U); - - if(SFE_BUS_OK != result) - return result; - - statusReg->word = ((uint16_t)statusRaw[1] << 8) | statusRaw[0]; - - return SFE_BUS_OK; - } - - /// @brief Gets the operational state register when in configuration operation mode. - /// @param osrReg Pointer to a register struct to store the sensor's current OSR register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getOSR(sfe_as7331_reg_cfg_osr_t *osrReg) - { - return readRegister(SFE_AS7331_REGISTER_CFG_OSR, &osrReg->byte); - } - - /// @brief Sets the operational state register when in configuration operation mode. - /// @param osrReg Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setOSR(const sfe_as7331_reg_cfg_osr_t *osrReg) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_OSR, &osrReg->byte); - } - - /// @brief Gets the configuration register #1 when in configuration operation mode. - /// @param creg1 Pointer to a register struct to store the sensor's current creg1 register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getCReg1(sfe_as7331_reg_cfg_creg1_t *creg1) - { - return readRegister(SFE_AS7331_REGISTER_CFG_CREG1, &creg1->byte); - } - - /// @brief Sets the configuration register #1 when in configuration operation mode. - /// @param creg1 Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCReg1(const sfe_as7331_reg_cfg_creg1_t *creg1) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_CREG1, &creg1->byte); - } - - /// @brief Gets the configuration register #2 when in configuration operation mode. - /// @param creg2 Pointer to a register struct to store the sensor's current creg2 register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getCReg2(sfe_as7331_reg_cfg_creg2_t *creg2) - { - return readRegister(SFE_AS7331_REGISTER_CFG_CREG2, &creg2->byte); - } - - /// @brief Sets the configuration register #2 when in configuration operation mode. - /// @param creg2 Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCReg2(const sfe_as7331_reg_cfg_creg2_t *creg2) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_CREG2, &creg2->byte); - } - - /// @brief Gets the configuration register #3 when in configuration operation mode. - /// @param creg3 Pointer to a register struct to store the sensor's current creg3 register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getCReg3(sfe_as7331_reg_cfg_creg3_t *creg3) - { - return readRegister(SFE_AS7331_REGISTER_CFG_CREG3, &creg3->byte); - } - - /// @brief Sets the configuration register #3 when in configuration operation mode. - /// @param creg3 Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCReg3(const sfe_as7331_reg_cfg_creg3_t *creg3) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_CREG3, &creg3->byte); - } - - /// @brief Gets the break register when in configuration operation mode. - /// @param breakReg Pointer to a register struct to store the sensor's current break register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getBreak(sfe_as7331_reg_cfg_break_t *breakReg) - { - return readRegister(SFE_AS7331_REGISTER_CFG_BREAK, breakReg); - } - - /// @brief Sets the break register when in configuration operation mode. - /// @param breakReg Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setBreak(const sfe_as7331_reg_cfg_break_t *breakReg) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_BREAK, breakReg); - } - - /// @brief Gets the edges register when in configuration operation mode. - /// @param edgesReg Pointer to a register struct to store the sensor's current edges register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getEdges(sfe_as7331_reg_cfg_edges_t *edgesReg) - { - return readRegister(SFE_AS7331_REGISTER_CFG_EDGES, edgesReg); - } - - - /// @brief Sets the edges register when in configuration operation mode. - /// @param edgesReg Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setEdges(const sfe_as7331_reg_cfg_edges_t *edgesReg) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_EDGES, edgesReg); - } - - /// @brief Gets the option register when in configuration operation mode. - /// @param optReg Pointer to a register struct to store the sensor's current option register. - /// @return 0 if successful, negative if error, positive for warning. - int8_t getOptIndex(sfe_as7331_reg_cfg_optreg_t *optReg) - { - return readRegister(SFE_AS7331_REGISTER_CFG_OPTREG, &optReg->byte); - } - - /// @brief Sets the option register when in configuration operation mode. - /// @param optReg Pointer to a register struct that has the new register configuration. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setOptIndex(const sfe_as7331_reg_cfg_optreg_t *optReg) - { - return writeRegister(SFE_AS7331_REGISTER_CFG_OPTREG, &optReg->byte); - } - - sfeAS7331_measures_t measures; - - private: - /// @brief Converts the raw temperature value to a human readable form. - /// @param inputVal Raw temperature value to convert. - /// @return The converted device temperature in degree C. - float convertRawTempToTempC(const uint16_t inputVal) - { - // T_chip = TEMP*0.05 - 66.9 - // EX: TEMP=0x922 aka TEMP=0d2338, returns 50.0 - return ((float)(inputVal) * 0.05f) - 66.9f; - } - - /// @brief Called when changing values that affect the conversion, calculates a new conversion factor to reduce the conversion overhead. - void calculateConversionFactors(void) - { - float divFactor = (bool)_config.dividerEnabled ? (float)(1 << (1+_config.dividerRange)) : 1.0f; - _conversionA = fsrA/((float)(1 << (11 - _config.sensorGain)))/((float)(1 << _config.conversionTime))/((float)(1024.0 * (1 << _config.cclk)))*divFactor; - _conversionB = fsrB/((float)(1 << (11 - _config.sensorGain)))/((float)(1 << _config.conversionTime))/((float)(1024.0 * (1 << _config.cclk)))*divFactor; - _conversionC = fsrC/((float)(1 << (11 - _config.sensorGain)))/((float)(1 << _config.conversionTime))/((float)(1024.0 * (1 << _config.cclk)))*divFactor; - } - - sfeBusDevice *_sfeBus; // Pointer to bus device. - sfeBusDeviceSettings *_devSettings; // Pointer to the device settings for this sensor. - - sfeAS7331_config_t _config; // Internal sensor configuration. - sfeAS7331_state_t _state; // Internal sensor state. - - float _conversionA; // Internal conversion factor for the UVA sensor. - float _conversionB; // Internal conversion factor for the UVB sensor. - float _conversionC; // Internal conversion factor for the UVC sensor. - - static constexpr float fsrA = 348160.0; // Full Scale Resolution for the UVA sensor. - static constexpr float fsrB = 387072.0; // Full Scale Resolution for the UVB sensor. - static constexpr float fsrC = 169984.0; // Full Scale Resolution for the UVC sensor. - +#include +// #include "C:\\Users\\alex.brudner\\Documents\\Arduino\\libraries\\SparkFun_Toolkit\\src\\SparkFun_Toolkit.h" + +/////////////////////////////////////////////////////////////////////////////// +// I2C Addressing +/////////////////////////////////////////////////////////////////////////////// +// 7-bit address defined as [1, 1, 1, 0, 1, A1, A0] where A1/A0 are the +// physical address pins tied high or low +#define AS7331_ADDR_DEFAULT 0x74 // A1 = 0, A0 = 0 +#define AS7331_ADDR_SEC 0x75 // A1 = 0, A1 = 1 +#define AS7331_ADDR_TER 0x76 // A1 = 1, A0 = 0 +#define AS7331_ADDR_QUA 0x77 // A1 = 1, A0 = 1 + +#define AS7331_DEFAULT_DEV_ID 0x21 // When polling the AGEN register, this should be returned on boot. +#define AS7331_DEV_ID_HIGH 0x2 // Top nibble of the AGEN byte, always the same. + +/////////////////////////////////////////////////////////////////////////////// +// Enum Definitions +/////////////////////////////////////////////////////////////////////////////// + +// Device Operating Mode +typedef enum +{ + // 0b00X invalid + DEVICE_MODE_CFG = 0x2, + DEVICE_MODE_MEAS = 0x3 + // 0b1XX invalid +} as7331_device_op_state_t; + +// Sensor gain, able to be read as (1 << (11 - gain)) aka 2^(11-gain). +typedef enum +{ + GAIN_2048 = 0x0000, + GAIN_1024, + GAIN_512, + GAIN_256, + GAIN_128, + GAIN_64, + GAIN_32, + GAIN_16, + GAIN_8, + GAIN_4, + GAIN_2, + GAIN_1 +} as7331_gain_t; + +// Conversion Clock Frequency available settings, read as (1024 * (1 << cclk)) +// aka 1024*2^cclk. +typedef enum +{ + CCLK_1_024_MHZ = 0x00, + CCLK_2_048_MHZ, + CCLK_4_096_MHZ, + CCLK_8_192_MHZ +} as7331_conv_clk_freq_t; + +// Measurement Mode types +typedef enum +{ + MEAS_MODE_CONT = 0x0, // Continuous mode + MEAS_MODE_CMD, // Command/OneShot mode + MEAS_MODE_SYNS, // SYNchronous Start mode + MEAS_MODE_SYND // SYNchronous start and enD mode +} as7331_meas_mode_t; + +// Time conversion values in CONT, CMD, SYNS modes. Read as (1 << tconv) aka +// 2^tconv. +typedef enum +{ + TIME_1MS = 0x0, + TIME_2MS, + TIME_4MS, + TIME_8MS, + TIME_16MS, + TIME_32MS, + TIME_64MS, + TIME_128MS, + TIME_256MS, + TIME_512MS, + TIME_1024MS, + TIME_2048MS, + TIME_4096MS, + TIME_8192MS, + TIME_16384MS +} as7331_conv_time_t; + +// Predivider values. Read as (1 << (1+div)) aka 2^(1+div). +typedef enum +{ + DIV_2 = 0x0, + DIV_4, + DIV_8, + DIV_16, + DIV_32, + DIV_64, + DIV_128, + DIV_256 +} as7331_divider_val_t; + +// Type of UV to pick. +typedef enum +{ + AS7331_UVA, + AS7331_UVB, + AS7331_UVC +} as7331_uv_type; + +/////////////////////////////////////////////////////////////////////////////// +// Configuration Register Descriptions +// +// Can only be accessed in the Configuration operation state. All 8 bits long. +// +// The following registers map to the underlying registers of the device. +/////////////////////////////////////////////////////////////////////////////// + +#define SFE_AS7331_REGISTER_CFG_OSR 0x00 // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t dos : 3; // Device Operating State - OSR[2:0] + uint8_t sw_res : 1; // Software Reset - OSR[3] + uint8_t reserved : 2; // Reserved, don't write. - OSR[4:5] + uint8_t pd : 1; // Power Down Enabled - OSR[6] + uint8_t ss : 1; // Start State - OSR[7] + }; + uint8_t byte; +} sfe_as7331_reg_cfg_osr_t; + +#define SFE_AS7331_REGISTER_CFG_AGEN 0x02 // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t mut : 4; // Increments when changes are made to the control + // registers. Defaults to 0b0001 aka 0x1. + uint8_t devid : 4; // Always equals 0b0010 aka 0x2. + }; + uint8_t byte; +} sfe_as7331_reg_cfg_agen_t; + +#define SFE_AS7331_REGISTER_CFG_CREG1 0x06 // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t time : 4; // Conversion time - CREG1[3:0] + uint8_t gain : 4; // Sensor gain - CREG1[7:4] + }; + uint8_t byte; +} sfe_as7331_reg_cfg_creg1_t; + +#define SFE_AS7331_REGISTER_CFG_CREG2 0x07 // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t div : 3; // Digital Divider value - CREG2[2:0] + uint8_t en_div : 1; // Whether the digital divider is enabled - CREG2[3] + uint8_t reserved : 2; // Reserved, don't write. - CREG2[5:4] + uint8_t en_tm : 1; // Whether temperature is calculated in SYND mode. - CREG2[6] + uint8_t reserved1 : 1; // Reserved, don't write. - CREG2[7] + }; + uint8_t byte; +} sfe_as7331_reg_cfg_creg2_t; + +#define SFE_AS7331_REGISTER_CFG_CREG3 0x08 // Register Address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t cclk : 2; // Conversion clock selection. - CREG3[1:0] + uint8_t reserved : 1; // Reserved, don't write. - CREG3[2] + uint8_t rdyod : 1; // Output mode of the ready pin. - CREG3[3] + uint8_t sb : 1; // Standby mode bit. - CREG3[4] + uint8_t reserved1 : 1; // Reserved, don't write. - CREG3[5] + uint8_t mmode : 2; // Measurement mode selection. - CREG3[7:6] + }; + uint8_t byte; +} sfe_as7331_reg_cfg_creg3_t; + +#define SFE_AS7331_REGISTER_CFG_BREAK 0x09 // Register address, register is a single uint8_t. + +#define SFE_AS7331_REGISTER_CFG_EDGES 0x0A // Register address, register is a single uint8_t. + +#define SFE_AS7331_REGISTER_CFG_OPTREG 0x0B // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t init_idx : 1; // I2C repeat start mode flag. - OPTREG[0] + uint8_t reserved : 7; // Reserved, don't write. - OPTREG[7:1] + }; + uint8_t byte; +} sfe_as7331_reg_cfg_optreg_t; + +/////////////////////////////////////////////////////////////////////////////// +// Measurement Register Descriptions +// +// Can only be accessed in the Measure operation state. All 16 bits long. They +// are all read-only except the bottom byte of the OSR/STATUS register. +// +// The following registers map to the underlying registers of the device. +/////////////////////////////////////////////////////////////////////////////// + +#define SFE_AS7331_REGISTER_MEAS_OSR_STATUS 0x00 // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + sfe_as7331_reg_cfg_osr_t osr; // See OSR configuration register above. - OSRSTAT[7:0] + uint8_t powerstate : 1; // Power down state. - OSRSTAT[8] + uint8_t standbystate : 1; // Standby mode state. - OSRSTAT[9] + uint8_t notready : 1; // Inverted value of the ready pin. - OSRSTAT[10] + uint8_t ndata : 1; // Indicates new data available. - OSRSTAT[11] + uint8_t ldata : 1; // Indicates data overwrite prior to retrieval. - OSRSTAT[12] + uint8_t adcof : 1; // OVF of at least one ADC channel. - OSRSTAT[13] + uint8_t mresof : 1; // OVF of at least one of the MRES registers. - OSRSTAT[14] + uint8_t outconvof : 1; // OVF of the internal 24-bit time reference. - OSRSTAT[15] + }; + uint16_t word; +} sfe_as7331_reg_meas_osr_status_t; + +// Output result registers. 16-bit values unless noted otherwise. +#define SFE_AS7331_REGISTER_MEAS_TEMP 0x01 // 12-bit temperature, MS 4-bits 0. +#define SFE_AS7331_REGISTER_MEAS_MRES1 0x02 +#define SFE_AS7331_REGISTER_MEAS_MRES2 0x03 +#define SFE_AS7331_REGISTER_MEAS_MRES3 0x04 +#define SFE_AS7331_REGISTER_MEAS_OUTCONV_L 0x05 // First 16-bits of 24-bit OUTCONV. +#define SFE_AS7331_REGISTER_MEAS_OUTCONV_H 0x06 // LSB is MSB of OUTCONV, MSB is 0. + +/////////////////////////////////////////////////////////////////////////////// + +class SfeAS7331Driver +{ + public: + SfeAS7331Driver(uint8_t address = AS7331_ADDR_DEFAULT) : _address{address} + { + } + + /// @brief This method is called to initialize the AS7331 device through the + /// specified bus. + /// @param theBus Pointer to the bus object. + /// @param devSettings Pointer to the device settings object. + /// @param deviceAddress I2C address for the device. + /// @return True if successful, false if it fails. + bool begin(sfeBusDevice *theBus, sfeBusDeviceSettings *devSettings, + const uint8_t deviceAddress = AS7331_ADDR_DEFAULT); + + /// @brief This method is called to initialize the AS7331 device through the + /// specified bus. + /// @param theBus Pointer to the bus object. + /// @param deviceAddress I2C address for the device. + /// @return True if successful, false if it fails. + bool begin(sfeBusDevice *theBus, uint8_t deviceAddress = AS7331_ADDR_DEFAULT); + + /// @brief This method is called to initialize the AS7331 device at the + /// specified bus address. + /// @param deviceAddress I2C address for the device. + /// @return True if successful, false if it fails. + bool begin(uint8_t deviceAddress = AS7331_ADDR_DEFAULT); + + /// @brief Finds the device and initializes the default state. + /// @param runSetup Flag to run the default setup if set. + /// @return True if successful, false if it fails. + bool init(bool runSetup = true); + + /// @brief Requests the device ID from the sensor. + /// @return The device ID of the sensor. + uint8_t getDeviceID(void); + + /// @brief Sets the communication bus to the specified bus and device + /// settings. + /// @param theBus Bus to set as the communication device. + /// @param deviceSettings Device settings to use when setting the bus for this + /// sensor. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCommunicationBus(sfeBusDevice *theBus, sfeBusDeviceSettings *deviceSettings); + + /// @brief Sets the communication bus to the specified bus. Creates new + /// instance if none specified. + /// @param theBus Bus to set as the communication devie. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCommunicationBus(sfeBusDevice *theBus = nullptr); + + /// @brief Sets settings of this sensor's device. + /// @param deviceSettings Settings structure for this device. Creates new + /// instance if none specified. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCommunicationDevSettings(sfeBusDeviceSettings *deviceSettings = nullptr); + + /// @brief Sets the address that the bus uses to communicate with the sensor. + /// @param deviceAddress Device address to use. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setDeviceAddress(const uint8_t deviceAddress); + + /// @brief Helper class that sets up the sensor and state in the POR + /// configuration. + /// @param runSoftReset Flag that runs the soft reset function to reset the + /// device. + /// @return True if successful, false otherwise. + bool runDefaultSetup(bool runSoftReset = false); + + /// @brief Puts the sensor in the specified measurement mode. + /// @param measMode Measurement mode to enter. + /// @param startMeasure Flag to start measuring immediately if set. + /// @return True if successful, false otherwise. + bool prepareMeasurement(const as7331_meas_mode_t measMode = MEAS_MODE_CONT, bool startMeasure = false); + + /// @brief Performs a soft reset of the sensor. + /// @return True if successful, false otherwise. + bool reset(void); + + /// @brief Reads the sensor's temperature, converts it to a usable form, and + /// saves it to the internal temperature variable. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readTemp(void); + + /// @brief Reads the sensor's UVA register, converts it to a usable form, and + /// saves it to the internal UVA variable. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readUVA(void); + + /// @brief Reads the sensor's UVB register, converts it to a usable form, and + /// saves it to the internal UVB variable. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readUVB(void); + + /// @brief Reads the sensor's UVC register, converts it to a usable form, and + /// saves it to the internal UVC variable. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readUVC(void); + + /// @brief Read's all three UV registers, converts them to a usable form, then + /// saves them to their respective internal variable. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readAllUV(void); + + /// @brief Read the sensor's temperature, UV, and external time conversion + /// clock counts, converts them, and then saves them to their respective + /// internal variable. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readAll(void); + + /// @brief Read the conversion clock counts register and saves it to the + /// internal output conversion time variable. + /// @return 0 if successful, negative if error, positive for warning. + int8_t readOutConv(void); + + /// @brief Getter for the currently configured gain. + /// @return Sensor's gain expressed as (1 << (11 - gain)). + as7331_gain_t getGain(void); + + /// @brief Sets the UV sensor's gain. + /// @param gain The gain to set the sensor to. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setGain(const as7331_gain_t &gain); + + /// @brief Getter for the currently configured conversion clock. + /// @return Sensor's conversion clock expressed as 1024*(1 << cclk). + as7331_conv_clk_freq_t getCClk(void); + + /// @brief Set the sensor's internal clock speed. + /// @param cclk Clock speed to set on the sensor. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCClk(const as7331_conv_clk_freq_t &cclk); + + /// @brief Getter for the currently configured conversion time. + /// @return Sensor's conversion time expressed as (1 << time). + as7331_conv_time_t getConversionTime(void); + + /// @brief Sets the conversion time that the sensor will run to. + /// @param convTime Conversion time to set the sensor to. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setConversionTime(const as7331_conv_time_t &convTime); + + /// @brief Getter for the currently configured pin mode. + /// @return False if push-pull, true if open-drain. + bool getReadyPinMode(void); + + /// @brief Sets the ready pin type to push-pull or open-drain. + /// @param pinMode Mode to set the ready pin to. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setReadyPinMode(const bool &pinMode); + + /// @brief Getter for the currently configured divider status. + /// @return True if Internal predivider is enabled, false otherwise. + bool getDigitalDividerEnabled(void); + + /// @brief Enables or disables the internal UV result divider. + /// @param isEnabled Enable or disable the divder. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setDigitalDividerEnabled(const bool &isEnabled); + + /// @brief Getter for the currently configured divider range. + /// @return Sensor's internal UV predivider range. + as7331_divider_val_t getDigitalDividerRange(void); + + /// @brief Sets the value of the internal UV result divider. + /// @param divider Divider value to set. + /// @param setEnableDiv Option to turn on the divider if desired. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &setEnableDiv = false); + + /// @brief Getter for the SYND temperature conversion status. + /// @return True if temperature conversion is enabled in SYND mode. + bool getSyndTempConversionEnabled(void); + + /// @brief Enables or disables temperature conversion when in SYND mode. + /// @param isEnabled Enable or disable the feature. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setSyndTempConversionEnabled(const bool &isEnabled); + + /// @brief Getter for the currently configured I2C compatibility mode. + /// @return True if the device will respond to repeat starts, false otherwise. + bool getIndexMode(void); + + /// @brief Set the index mode for compatibility with I2C controllers that + /// don't support repeated start. + /// @param indexMode Simple or standard I2C addressing mode. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setIndexMode(const bool &indexMode); + + /// @brief Getter for the currently configured minimum break time in CONT, + /// CMD, SYNS modes. + /// @return Sensor's breaktime in 8us steps. + uint8_t getBreakTime(void); + + /// @brief Set the minimum time between measurements in CONT, SYNS, SYND modes. + /// @param breakTime Time between measurements, 8us step time, max 2048us. A 0 + /// value is a minimum of 3 cclk cycles. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setBreakTime(const uint8_t &breakTime); + + /// @brief Getter for the currently configured minimum number of edges to end + /// conversion when in SYND mode. + /// @return Sensor's minimum number of edges, minimum 1 edge. + uint8_t getNumEdges(void); + + /// @brief Set the minimum number of falling edges required at the SYN input + /// until the conversion is terminated. Only operational in SYND mode. + /// @param numEdges Number of edges prior to terminating conversion in SYND + /// mode. 0 is not allowed, 1 is the minimum. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setNumEdges(const uint8_t &numEdges); + + /// @brief Getter for the current power state. + /// @return Sensor's power state. + as7331_power_state_t getPowerDownState(void); + + /// @brief Sets the power state of the sensor. + /// @param pd Power state to set. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setPowerDownState(const bool &pd); + + /// @brief Getter for the current operational state. + /// @return Sensor's operational mode. + as7331_device_op_state_t getOperationMode(void); + + /// @brief Set the sensor's operating mode. + /// @param opMode Operating mode to set. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setOperationMode(const as7331_device_op_state_t &opMode); + + /// @brief Getter for the current measurement state. + /// @return Sensor's measurement state. + as7331_meas_mode_t getMeasurementMode(void); + + /// @brief Sets the sensor's measurement mode. + /// @param measMode Measurement mode to set. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setMeasurementMode(const as7331_meas_mode_t &measMode); + + /// @brief Getter for the current standby state. + /// @return Sensor's standby state. + bool getStandbyState(void); + + /// @brief Sets the sensor's standby mode. + /// @param standby State to set. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setStandbyState(const bool &standby); + + /// @brief Getter for the current start state. + /// @return Sensor's start state. + bool getStartState(void); + + /// @brief Sets the sensor's start state. This begins measurement. + /// @param startState Start state to set. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setStartState(const bool &startState); + + /// @brief Gets the sensor's status when in measurement operation mode. + /// @param statusReg Pointer to a register struct to store the sensor's + /// current status. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getStatus(sfe_as7331_reg_meas_osr_status_t &statusReg); + + /// @brief Gets the operational state register when in configuration operation + /// mode. + /// @param osrReg Pointer to a register struct to store the sensor's current + /// OSR register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getOSR(sfe_as7331_reg_cfg_osr_t &osrReg); + + /// @brief Sets the operational state register when in configuration operation + /// mode. + /// @param osrReg Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setOSR(const sfe_as7331_reg_cfg_osr_t &osrReg); + + /// @brief Gets the configuration register #1 when in configuration operation + /// mode. + /// @param creg1 Pointer to a register struct to store the sensor's current + /// creg1 register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getCReg1(sfe_as7331_reg_cfg_creg1_t &creg1); + + /// @brief Sets the configuration register #1 when in configuration operation + /// mode. + /// @param creg1 Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCReg1(const sfe_as7331_reg_cfg_creg1_t &creg1); + + /// @brief Gets the configuration register #2 when in configuration operation + /// mode. + /// @param creg2 Pointer to a register struct to store the sensor's current + /// creg2 register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getCReg2(sfe_as7331_reg_cfg_creg2_t &creg2); + + /// @brief Sets the configuration register #2 when in configuration operation + /// mode. + /// @param creg2 Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCReg2(const sfe_as7331_reg_cfg_creg2_t &creg2); + + /// @brief Gets the configuration register #3 when in configuration operation + /// mode. + /// @param creg3 Pointer to a register struct to store the sensor's current + /// creg3 register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getCReg3(sfe_as7331_reg_cfg_creg3_t &creg3); + + /// @brief Sets the configuration register #3 when in configuration operation + /// mode. + /// @param creg3 Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setCReg3(const sfe_as7331_reg_cfg_creg3_t &creg3); + + /// @brief Gets the break register when in configuration operation mode. + /// @param breakReg Pointer to a register struct to store the sensor's current + /// break register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getBreak(uint8_t &breakReg); + + /// @brief Sets the break register when in configuration operation mode. + /// @param breakReg Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setBreak(const uint8_t &breakReg); + + /// @brief Gets the edges register when in configuration operation mode. + /// @param edgesReg Pointer to a register struct to store the sensor's current + /// edges register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getEdges(uint8_t &edgesReg); + + /// @brief Sets the edges register when in configuration operation mode. + /// @param edgesReg Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setEdges(const uint8_t &edgesReg); + + /// @brief Gets the option register when in configuration operation mode. + /// @param optReg Pointer to a register struct to store the sensor's current + /// option register. + /// @return 0 if successful, negative if error, positive for warning. + int8_t getOptIndex(sfe_as7331_reg_cfg_optreg_t &optReg); + + /// @brief Sets the option register when in configuration operation mode. + /// @param optReg Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + int8_t setOptIndex(const sfe_as7331_reg_cfg_optreg_t &optReg); + + private: + /// @brief Reads a UV result register and saves it to the respective internal + /// variable. + /// @param uv_type The type of UV you want to read. + /// @return 0 if successful, negative if error, positive if warning + int8_t readRawUV(const as7331_uv_type &uv_type); + + /// @brief Converts the raw temperature value to a human readable form. + /// @param inputVal Raw temperature value to convert. + /// @return The converted device temperature in degree C. + float convertRawTempToTempC(const uint16_t &inputVal); + + /// @brief Converts a raw result value and performs a conversion to a human + /// readable format. + /// @param rawVal The raw input value. + /// @param convFactor Conversion factor to multiply the raw input value. + /// @return The calculated conversion result + float convertRawUVVal(const uint16_t &rawVal, const float &convFactor); + + /// @brief Called when changing values that affect the conversion, calculates + /// a new conversion factor to reduce the conversion overhead. + void calculateConversionFactors(void); + + /// @brief Called to reset all local values back to initial conditions. + void setDefaultSettings(void); + + sfeTkIBus *_theBus; // Pointer to bus device. + uint8_t _address; // Device's I2C address. + + uint8_t _breakTime; // Local configuration value. Value is in us/8. EX: _breakTime = 20 means 20*8 = 160us. + uint8_t _numEdges; // Local configuration value. Edges seen on SYN pin before ending conversion in SYND mode. + + bool _readyPinMode; // Local configuration value. False is Push/Pull True is Open/Drain. + bool _dividerEnabled; // Local configuration value. False is disabled, True is enabled. + bool _tempConvEnabled; // Local configuration value. False is disabled, True is enabled. + bool _indexMode; // Local configuration value. False is for controllers where repeat start is not available when it is. + bool _standbyState; // Local state value. False means the device is not in standby mode. + bool _startState; // Local state value. False means the device is not allowed to measure. + bool _powerDownEnableState; // Local state value. False means the device is NOT POWERED DOWN. This is inverse. + + as7331_device_op_state_t _opMode; // Local state value. Configuration or Measure operating mode. + as7331_gain_t _sensorGain; // Local configuration value. Sensor gain stored as (1 << (11 - _sensorGain)). + as7331_conv_clk_freq_t _cclk; // Local configuration value. Sensor's conversion clock frequency stored + // as 1024*(1 << _cclk) kHz. + as7331_meas_mode_t _mmode; // Local state value. Details the device's measurement mode as CONT, CMD, SYNS, or SYND. + as7331_conv_time_t _conversionTime; // Local configuration value. Contains the sensor's conversion time in CONT, + // CMD, and SYNS mode stored as (1 << _conversionTime) ms. + as7331_divider_val_t _dividerRange; // Local configuration value. Contains the sensor's internal predivider value as + // (1 << _dividerRange). + + float _uva; // Last valid UVA result in uW/cm2. + float _uvb; // Last valid UVB result in uW/cm2. + float _uvc; // Last valid UVC result in uW/cm2. + float _temperature; // Last valid temperature result in degC. + + uint32_t _outputConversionTime; // Last valid output conversion time result in number of clock cycles. + + float _conversionA; // Internal conversion factor for the UVA sensor. + float _conversionB; // Internal conversion factor for the UVB sensor. + float _conversionC; // Internal conversion factor for the UVC sensor. + + static constexpr float _fsrA = 348160.0; // Full Scale Resolution for the UVA sensor. + static constexpr float _fsrB = 387072.0; // Full Scale Resolution for the UVB sensor. + static constexpr float _fsrC = 169984.0; // Full Scale Resolution for the UVC sensor. }; -/// @class SfeAS7331ArdI2C -/// @brief Arduino I2C implementation of the SfeAS7331Base class for the AS7331 Spectral UV Sensor. -class SfeAS7331ArdI2C : public SfeAS7331Base +class SfeAS7331ArdI2C : public SfeAS7331Driver { - /* Nothing to see here, see above. */ + public: + SfeAS7331ArdI2C() + { + } + + /// @brief Sets up Arduino specific I2C driver things then calls the super class begin. + /// @return True if successful, false otherwise. + bool begin(void) + { + if (!_theI2CBus.init(AS7331_ADDR_DEFAULT)) + return false; + + setCommunicationBus(&_theI2CBus); + + return SfeAS7331Driver::begin(); + } + + /// @brief Checks to see if the AS7331 is connected. + /// @return True if successful, false otherwise. + bool isConnected(void) + { + if (!_theI2CBus.ping()) + return false; + + return (AS7331_DEFAULT_DEV_ID == SfeAS7331Driver::getDeviceID()); + } + + private: + sfeTkArdI2C _theI2CBus; }; diff --git a/src/sfe_as7331_registers.h b/src/sfe_as7331_registers.h deleted file mode 100644 index 797c948..0000000 --- a/src/sfe_as7331_registers.h +++ /dev/null @@ -1,274 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Copyright (c) 2023 SparkFun Electronics - -*/ - -#pragma once -#include - -/// I2C addresses -// 7-bit address defined as [1, 1, 1, 0, 1, A1, A0] where A1/A0 are the address pins tied high or low -#define AS7331_ADDR_DEFAULT 0x74 -#define AS7331_ADDR_SEC 0x75 -#define AS7331_ADDR_TER 0x76 -#define AS7331_ADDR_QUA 0x77 - -#define AS7331_DEFAULT_DEV_ID 0x21 -#define AS7331_DEV_ID_HIGH 0x2 - - -/// Enum Settings - -typedef enum { - // 0b00X invalid - DEVICE_MODE_CFG = 0x2, - DEVICE_MODE_MEAS = 0x3 - // 0b1XX invalid -} as7331_device_op_state_t; - -typedef enum { - POWER_DOWN_DISABLE = 0x0, - POWER_DOWN_ENABLE -} as7331_power_state_t; - -typedef enum { - START_STATE_DISABLED = 0x0, - START_STATE_ENABLED -} as7331_startstate_t; - -typedef enum { - GAIN_2048 = 0x0000, - GAIN_1024, - GAIN_512, - GAIN_256, - GAIN_128, - GAIN_64, - GAIN_32, - GAIN_16, - GAIN_8, - GAIN_4, - GAIN_2, - GAIN_1 -} as7331_gain_t; - -typedef enum { - SYN_TEMP_DISABLED = 0x0, - SYN_TEMP_ENABLED -} as7331_ext_syn_temp_meas_t; - -typedef enum { - CCLK_1_024_MHZ = 0x00, - CCLK_2_048_MHZ, - CCLK_4_096_MHZ, - CCLK_8_192_MHZ -} as7331_conv_clk_freq_t; - -typedef enum { - READYPIN_PUSHPULL = 0x0, - READYPIN_OPENDRAIN -} as7331_ready_pin_mode_t; - -typedef enum { - STANDBY_DISABLED = 0x0, - STANDBY_ENABLED -} as7331_standby_mode_t; - -typedef enum { - MEAS_MODE_CONT = 0x0, - MEAS_MODE_CMD, - MEAS_MODE_SYNS, - MEAS_MODE_SYND -} as7331_meas_mode_t; - -typedef enum { - INDEX_NO_REPEAT_START = 0x0, - INDEX_REPEAT_START -} as7331_simple_reg_read_mode_t; - -typedef enum { - TIME_1MS = 0x0, - TIME_2MS, - TIME_4MS, - TIME_8MS, - TIME_16MS, - TIME_32MS, - TIME_64MS, - TIME_128MS, - TIME_256MS, - TIME_512MS, - TIME_1024MS, - TIME_2048MS, - TIME_4096MS, - TIME_8192MS, - TIME_16384MS -} as7331_conv_time_t; - -typedef enum { - DIVIDER_DISABLED = 0x0, - DIVIDER_ENABLED = 0x1 -} as7331_divider_enable_t; - -typedef enum { - DIV_2 = 0x0, - DIV_4, - DIV_8, - DIV_16, - DIV_32, - DIV_64, - DIV_128, - DIV_256 -} as7331_divider_val_t; - -/// Configuration Registers -// These registers can only be accessed in the configuration state, and they -// are all 8 bits long. - -#define SFE_AS7331_REGISTER_CFG_OSR 0x00 -typedef union { - struct { - uint8_t dos : 3; - uint8_t sw_res : 1; - uint8_t reserved : 2; - uint8_t pd : 1; - uint8_t ss : 1; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_osr_t; - -#define SFE_AS7331_REGISTER_CFG_AGEN 0x02 -typedef union { - struct { - uint8_t mut : 4; - uint8_t devid : 4; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_agen_t; - -#define SFE_AS7331_REGISTER_CFG_CREG1 0x06 -typedef union { - struct { - uint8_t time : 4; - uint8_t gain : 4; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_creg1_t; - -#define SFE_AS7331_REGISTER_CFG_CREG2 0x07 -typedef union { - struct { - uint8_t div : 3; - uint8_t en_div : 1; - uint8_t reserved : 2; - uint8_t en_tm : 1; - uint8_t reserved1 : 1; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_creg2_t; - -#define SFE_AS7331_REGISTER_CFG_CREG3 0x08 -typedef union { - struct { - uint8_t cclk : 2; - uint8_t reserved : 1; - uint8_t rdyod : 1; - uint8_t sb : 1; - uint8_t reserved1 : 1; - uint8_t mmode : 2; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_creg3_t; - -#define SFE_AS7331_REGISTER_CFG_BREAK 0x09 -typedef uint8_t sfe_as7331_reg_cfg_break_t; - -#define SFE_AS7331_REGISTER_CFG_EDGES 0x0A -typedef uint8_t sfe_as7331_reg_cfg_edges_t; - -#define SFE_AS7331_REGISTER_CFG_OPTREG 0x0B -typedef union { - struct { - uint8_t init_idx : 1; - uint8_t reserved : 7; - }; - uint8_t byte; -} sfe_as7331_reg_cfg_optreg_t; - - -/// Measurement Registers -// These registers can only be accessed in the measurement state. They are -// read-only and 16 bits long, except OUTCONV, which is 24 bits long. - -#define SFE_AS7331_REGISTER_MEAS_OSR_STATUS 0x00 -typedef union { - struct { - sfe_as7331_reg_cfg_osr_t osr; - uint8_t powerstate : 1; - uint8_t standbystate : 1; - uint8_t notready : 1; - uint8_t ndata : 1; - uint8_t ldata : 1; - uint8_t adcof : 1; - uint8_t mresof : 1; - uint8_t outconvof : 1; - }; - uint16_t word; -} sfe_as7331_reg_meas_osr_status_t; - -#define SFE_AS7331_REGISTER_MEAS_TEMP 0x01 -typedef union { - struct { - uint16_t temp : 12; - uint8_t zeros : 4; - }; - uint16_t word; -} sfe_as7331_reg_meas_temp_t; - -#define SFE_AS7331_REGISTER_MEAS_MRES1 0x02 -typedef union { - struct { - uint8_t res_l; - uint8_t res_h; - }; - uint16_t word; -} sfe_as7331_reg_meas_mres1_t; - -#define SFE_AS7331_REGISTER_MEAS_MRES2 0x03 -typedef union { - struct { - uint8_t res_l; - uint8_t res_h; - }; - uint16_t word; -} sfe_as7331_reg_meas_mres2_t; - - -#define SFE_AS7331_REGISTER_MEAS_MRES3 0x04 -typedef union { - struct { - uint8_t res_l; - uint8_t res_h; - }; - uint16_t word; -} sfe_as7331_reg_meas_mres3_t; - - -#define SFE_AS7331_REGISTER_MEAS_OUTCONV_L 0x05 -typedef union { - struct { - uint8_t outconv_l; - uint8_t outconv_m; - }; - uint16_t word; -} sfe_as7331_reg_meas_outconv_l_t; - - -#define SFE_AS7331_REGISTER_MEAS_OUTCONV_H 0x06 -typedef union { - struct { - uint8_t outconv_h; - uint8_t zeros; - }; - uint16_t word; -} sfe_as7331_reg_meas_outconv_h_t; diff --git a/src/sfe_bus.h b/src/sfe_bus.h deleted file mode 100644 index 0bff2a7..0000000 --- a/src/sfe_bus.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Copyright (c) 2023 SparkFun Electronics - - A pure virtual base class for implementing a common communication interface - in SparkFun products. -*/ - -#pragma once - -#include - -// Error and warning codes -#define SFE_BUS_OK 0 -#define SFE_BUS_E_UNKNOWN -1 -#define SFE_BUS_E_NULL_PTR -2 -#define SFE_BUS_E_TIMEOUT -3 -#define SFE_BUS_E_NO_RESPONSE -4 -#define SFE_BUS_E_DATA_TOO_LONG -5 -#define SFE_BUS_E_NULL_DEV_SETTINGS -6 -#define SFE_BUS_E_NULL_DATA_BUFFER -7 -#define SFE_BUS_W_UNKNOWN 1 -#define SFE_BUS_W_UNDER_READ 2 -#define SFE_BUS_W_NOT_ENABLED 3 - -/// @brief An abstract Bus address class for enabling multiple types of addresses. -class SFEBusDevSettings{}; // Nothing to see here... - -/// @brief An abstract interface for a communication bus -class SFEBus -{ - public: - /// @brief Begin bus. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t begin(void) = 0; - - /// @brief End bus. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t end(void) = 0; - - /// @brief Writes a number of bytes starting at the given register address. - /// @param devSettings Settings of the device. - /// @param regAddr The first register address to write to. - /// @param data Data buffer to write to registers. - /// @param numBytes Number of bytes to write. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, const uint8_t *data, const uint32_t numBytes) = 0; - - /// @brief Reads a number of bytes starting at the given register address. - /// @param devSettings Settings of the device. - /// @param regAddr The first register address to read from. - /// @param data Data buffer to read from registers. - /// @param numBytes Number of bytes to read. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, const uint32_t numBytes) = 0; - - /// @brief Writes a number of bytes to a device that doesn't use registers for communications. - /// @param devSettings Settings of the device. - /// @param data Data buffer to write from registers. - /// @param numBytes Number of bytes to write. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, const uint32_t numBytes) = 0; - - /// @brief Reads a number of bytes to a device that doesn't use registers for communications. - /// @param devSettings Settings of the device. - /// @param data Data buffer to read from registers. - /// @param numBytes Number of bytes to read. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, const uint32_t numBytes) = 0; -}; diff --git a/src/sfe_i2c.h b/src/sfe_i2c.h deleted file mode 100644 index d30b5b0..0000000 --- a/src/sfe_i2c.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Copyright (c) 2023 SparkFun Electronics -*/ - -#pragma once - -#include "sfe_bus.h" - -#define SFE_BUS_I2C_DEFAULT_ADDRESS 0x00 -#define SFE_BUS_I2C_DEFAULT_I2C_SPEED 100000 -#define SFE_BUS_I2C_BUFFER_SIZE 32 - -// @brief A simple bus address implementation for a generic I2C. -class SFEBusDevSettingsI2C : public SFEBusDevSettings -{ - public: - uint8_t devAddr = SFE_BUS_I2C_DEFAULT_ADDRESS; // Default I2C Address - uint32_t maxDataRate = SFE_BUS_I2C_DEFAULT_I2C_SPEED; // Default I2C Speed -}; - -/// @brief An abstract interface for an I2C communication bus -class SFEBusI2C : public SFEBus -{ - public: - /// @brief Pings I2C device and looks for an ACK response. - /// @param devAddr Address to ping. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t ping(const uint8_t devAddr) = 0; - - /// @brief Pings I2C device and looks for an ACK response. - /// @param devSettings Settings of device to ping. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t ping(const SFEBusDevSettings *devSettings) = 0; - - /// @brief Changes the I2C buffer size. - /// @param bufferSize New buffer size. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t setBufferSize(const uint32_t bufferSize) = 0; - - /// @brief Returns the I2C buffer size. - /// @param bufferSize Buffer to return the size of the transmit buffer. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t getBufferSize(uint32_t *bufferSize) = 0; - - /// @brief Changes the Bus transmit frequency. - /// @param frequency New bus frequency. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t setBusFrequency(const uint32_t frequency) = 0; - - /// @brief Returns the Bus transmit frequency. - /// @param frequency Buffer to return the frequency. - /// @return 0 for success, negative for failure, positive for warning. - virtual int8_t getBusFrequency(uint32_t *frequency) = 0; - - protected: - uint32_t _i2cBufferSize = SFE_BUS_I2C_BUFFER_SIZE; // Default Buffer Size - uint32_t _busFrequency = SFE_BUS_I2C_DEFAULT_I2C_SPEED; // Default Speed is 100kHz -}; diff --git a/src/sfe_i2c_arduino.cpp b/src/sfe_i2c_arduino.cpp deleted file mode 100644 index cada1ee..0000000 --- a/src/sfe_i2c_arduino.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Copyright (c) 2023 SparkFun Electronics -*/ - -#include "sfe_i2c_arduino.h" - -int8_t SFEBusArduinoI2C::begin(void) -{ - return begin(_i2cBus ? _i2cBus : &Wire ); -} - -int8_t SFEBusArduinoI2C::begin(TwoWire &wirePort) -{ - return begin(&wirePort); -} - -int8_t SFEBusArduinoI2C::begin(TwoWire *i2cBus) -{ - if (!i2cBus) - return SFE_BUS_E_NULL_PTR; - - _i2cBus = i2cBus; - - _i2cBus->begin(); - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::end(void) -{ - // Null pointer check. - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - _i2cBus->end(); - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::ping(const uint8_t devAddr) -{ - // Null pointer check. - if (_i2cBus == nullptr) - return SFE_BUS_E_NULL_PTR; - if (!devAddr) - return SFE_BUS_E_NULL_PTR; - // Begin and end transmission to check for ACK response - _i2cBus->beginTransmission(devAddr); - - return _mapError(_i2cBus->endTransmission()); -} - -int8_t SFEBusArduinoI2C::ping(const SFEBusDevSettings *devSettings) -{ - // Null pointer check. - if (!devSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; - - return ping(pAddr->devAddr); -} - -int8_t SFEBusArduinoI2C::writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, - const uint8_t *data, const uint32_t numBytes) -{ - // Null pointer check. - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - // Null pointer check. - if (!devSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; - - uint32_t writeOffset = 0; - uint32_t bytesToSend = numBytes; - int8_t result = 0; - - // Start transmission and send register address. - _i2cBus->beginTransmission(pAddr->devAddr); - _i2cBus->write(regAddr); - - while (bytesToSend > 0) - { - // Limit sendLength to the size of the I2C buffer to send in chunks. - uint8_t sendLength = (bytesToSend > _i2cBufferSize) ? _i2cBufferSize : bytesToSend; - - // Do the write thing. - for (uint8_t i = 0; i < sendLength; i++) - _i2cBus->write(data[writeOffset + i]); - - // If there's still more to send, send a repeat start. - if (bytesToSend > _i2cBufferSize) - { - result = _mapError(_i2cBus->endTransmission(false)); - if (SFE_BUS_OK != result) - return result; - } - - writeOffset += sendLength; - bytesToSend -= sendLength; - } - - return _mapError(_i2cBus->endTransmission()); -} - -int8_t SFEBusArduinoI2C::readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, - const uint32_t numBytes) -{ - // Null pointer check. - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - // Null pointer check. - if (!devSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - // Null pointer check. - if (!data) - return SFE_BUS_E_NULL_DATA_BUFFER; - - SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; - - // Start transmission and send register address. - _i2cBus->beginTransmission(pAddr->devAddr); - _i2cBus->write(regAddr); - - // Repeat start condition, return if there's an error. - int8_t result = _mapError(_i2cBus->endTransmission(false)); - if (SFE_BUS_OK != result) - return result; - - uint32_t bytesLeftToRead = numBytes; - uint32_t readOffset = 0; - - while (bytesLeftToRead > 0) - { - // Limit readLength to the size of the I2C buffer to read in chunks. - uint8_t readLength = (bytesLeftToRead > _i2cBufferSize) ? _i2cBufferSize : bytesLeftToRead; - - // Request bytes, then read them into the data buffer. - uint32_t numRead = _i2cBus->requestFrom(pAddr->devAddr, readLength); - - if (numRead < readLength) - return SFE_BUS_W_UNDER_READ; - - if (_i2cBus->available()) - { - for (uint8_t i = 0; i < readLength; i++) - data[readOffset + i] = _i2cBus->read(); - } - else - return SFE_BUS_E_NO_RESPONSE; - - readOffset += readLength; - bytesLeftToRead -= readLength; - } - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, uint32_t numBytes) -{ - // Null pointer check. - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - // Null pointer check. - if (!devSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; - - uint32_t writeOffset = 0; - uint32_t bytesToSend = numBytes; - int8_t result = 0; - - // Start transmission. - _i2cBus->beginTransmission(pAddr->devAddr); - - while (bytesToSend > 0) - { - // Limit sendLength to the size of the I2C buffer to send in chunks. - uint8_t sendLength = (bytesToSend > _i2cBufferSize) ? _i2cBufferSize : bytesToSend; - - // Do the write thing. - for (uint8_t i = 0; i < sendLength; i++) - _i2cBus->write(data[writeOffset + i]); - - // If there's still more to send, send a repeat start. - if (bytesToSend > _i2cBufferSize) - { - result = _mapError(_i2cBus->endTransmission(false)); - if (SFE_BUS_OK != result) - return result; - } - - writeOffset += sendLength; - bytesToSend -= sendLength; - } - - return _mapError(_i2cBus->endTransmission()); -} - -int8_t SFEBusArduinoI2C::readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, uint32_t numBytes) -{ - // Null pointer check. - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - // Null pointer check. - if (!devSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; - - // Start transmission. - _i2cBus->beginTransmission(pAddr->devAddr); - - // Repeat start condition, return if there's an error. - int8_t result = _mapError(_i2cBus->endTransmission(false)); - if (SFE_BUS_OK != result) - return result; - - uint32_t bytesLeftToRead = numBytes; - uint32_t readOffset = 0; - - while (bytesLeftToRead > 0) - { - // Limit readLength to the size of the I2C buffer to read in chunks. - uint8_t readLength = (bytesLeftToRead > _i2cBufferSize) ? _i2cBufferSize : bytesLeftToRead; - - // Request bytes, then read them into the data buffer. - uint32_t numRead = _i2cBus->requestFrom(pAddr->devAddr, readLength); - - if (numRead < readLength) - return SFE_BUS_W_UNDER_READ; - - if (_i2cBus->available()) - { - for (uint8_t i = 0; i < readLength; i++) - data[readOffset + i] = _i2cBus->read(); - } - else - return SFE_BUS_E_NO_RESPONSE; - - readOffset += readLength; - bytesLeftToRead -= readLength; - } - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::setBufferSize(const uint32_t bufferSize) -{ - _i2cBufferSize = bufferSize; - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::getBufferSize(uint32_t *bufferSize) -{ - *bufferSize = _i2cBufferSize; - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::setBusFrequency(const uint32_t frequency) -{ - if (!_i2cBus) - return SFE_BUS_E_NULL_PTR; - - _i2cBus->setClock(frequency); - _busFrequency = frequency; - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::getBusFrequency(uint32_t *frequency) -{ - *frequency = _busFrequency; - - return SFE_BUS_OK; -} - -int8_t SFEBusArduinoI2C::_mapError(const uint8_t error) -{ - if (!error) - return SFE_BUS_OK; - else if (error == 1) - return SFE_BUS_E_DATA_TOO_LONG; - else if ((error == 2) || (error == 3)) - return SFE_BUS_E_NO_RESPONSE; - else if (error == 5) - return SFE_BUS_E_TIMEOUT; - else - return SFE_BUS_E_UNKNOWN; -} diff --git a/src/sfe_i2c_arduino.h b/src/sfe_i2c_arduino.h deleted file mode 100644 index cad7824..0000000 --- a/src/sfe_i2c_arduino.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - SPDX-License-Identifier: MIT - - Copyright (c) 2023 SparkFun Electronics -*/ - -#pragma once - -#include -#include "sfe_i2c.h" -#include - -/// @brief An I2C communication bus implementation for Arduino -class SFEBusArduinoI2C : public SFEBusI2C -{ - public: - /// @brief Empty Constructor. - SFEBusArduinoI2C(void) : _i2cBus{nullptr}{}; - - /// @brief Passing in a TwoWire Object. - SFEBusArduinoI2C(TwoWire &wirePort) : _i2cBus{&wirePort}{}; - - /// @brief Begin the I2C object with the default Wire object. - /// @return 0 for success, negative for failure, positive for warning. - int8_t begin(void); - - /// @brief Begin the I2C object with the inputted Wire object. - /// @param wirePort I2C object to use for this bus. - /// @return 0 for success, negative for failure, positive for warning. - int8_t begin(TwoWire &wirePort); - - /// @brief Begin the I2C object with the inputted Wire object pointer. - /// @param i2cBus I2C bus pointer. - /// @return 0 for success, negative for failure, positive for warning. - int8_t begin(TwoWire *i2cBus); - - /// @brief End the I2C object. - /// @return 0 for success, negative for failure, positive for warning. - int8_t end(void); - - /// @brief Pings I2C device and looks for an ACK response. - /// @param devAddr Address to ping. - /// @return 0 for success, negative for failure, positive for warning. - int8_t ping(const uint8_t devAddr); - - /// @brief Pings I2C device and looks for an ACK response. - /// @param devSettings Settings object containing the address to ping. - /// @return 0 for success, negative for failure, positive for warning. - int8_t ping(const SFEBusDevSettings *devSettings); - - /// @brief Writes a number of bytes starting at the given register address. - /// @param devSettings I2C Settings object containing the address of the device. - /// @param regAddr The register address to write to. - /// @param data Data buffer to write to registers. - /// @param numBytes Number of bytes to write. - /// @return 0 for success, negative for failure, positive for warning. - int8_t writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, const uint8_t *data, const uint32_t numBytes); - - /// @brief Reads a number of bytes starting at the given register address. - /// @param devSettings I2C Settings object containing the address of the device. - /// @param regAddr The first register address to read from. - /// @param data Data buffer to read from registers. - /// @param numBytes Number of bytes to read. - /// @return 0 for success, negative for failure, positive for warning. - int8_t readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, const uint32_t numBytes); - - /// @brief Writes a number of bytes to a device that doesn't use registers for communications. - /// @param devSettings I2C Settings object containing the address of the device. - /// @param data Data buffer to write to registers. - /// @param numBytes Number of bytes to write. - /// @return 0 for success, negative for failure, positive for warning. - int8_t writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, const uint32_t numBytes); - - /// @brief Reads a number of bytes to a device that doesn't use registers for communications. - /// @param devSettings I2C Settings object containing the address of the device. - /// @param data Data buffer to read from registers. - /// @param numBytes Number of bytes to read. - /// @return 0 for success, negative for failure, positive for warning. - int8_t readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, const uint32_t numBytes); - - /// @brief Changes the I2C buffer size. - /// @param bufferSize New buffer size. - /// @return 0 for success, negative for failure, positive for warning. - int8_t setBufferSize(const uint32_t bufferSize); - - /// @brief Returns the I2C buffer size. - /// @param bufferSize Buffer to return the buffer... yep. - /// @return 0 for success, negative for failure, positive for warning. - int8_t getBufferSize(uint32_t *bufferSize); - - /// @brief Changes the Bus transmit frequency. - /// @param frequency New bus frequency. - /// @return 0 for success, negative for failure, positive for warning. - int8_t setBusFrequency(const uint32_t frequency); - - /// @brief Returns the Bus transmit frequency. - /// @param frequency Buffer to return the frequency. - /// @return 0 for success, negative for failure, positive for warning. - int8_t getBusFrequency(uint32_t *frequency); - - private: - TwoWire* _i2cBus; - - /// @brief Maps the TwoWire interface error scheme to the common bus error scheme. - /// @param error TwoWire error code. - /// @return 0 for success, negative for failure, positive for warning. - int8_t _mapError(const uint8_t error); -}; From 2c873d98ffb6c002bb661f0c511b9d7ea192a470 Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:04:02 -0700 Subject: [PATCH 04/39] Compiles but is untested. Still need to finish commenting. --- .../Example01_Basic_OneShot.ino | 84 +-- src/SparkFun_AS7331.cpp | 628 +++++++++--------- src/SparkFun_AS7331.h | 196 +++--- 3 files changed, 457 insertions(+), 451 deletions(-) diff --git a/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino b/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino index 4ea33a3..7eef265 100644 --- a/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino +++ b/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino @@ -24,55 +24,63 @@ #include "SparkFun_AS7331.h" -SfeAS7331ArdI2C myUVSensor; +void setup() { -int8_t result = SFE_BUS_OK; +} -void setup() { - Serial.begin(115200); - while(!Serial){delay(100);}; - Serial.println("UV LED Example."); +void loop() { + +} - // Initialize sensor and run default setup. - if(myUVSensor.begin() == false) { - Serial.println("Sensor failed to begin. Please check your wiring!"); - Serial.println("Spinning..."); - while(1); - } +// SfeAS7331ArdI2C myUVSensor; - Serial.println("Sensor began."); +// int8_t result = SFE_BUS_OK; - // Set measurement mode and change device operating mode to measure. - if(myUVSensor.startMeasurement(MEAS_MODE_CMD) == false) { - Serial.println("Sensor did not get set properly."); - Serial.println("Spinning..."); - while(1); - } +// void setup() { +// Serial.begin(115200); +// while(!Serial){delay(100);}; +// Serial.println("UV LED Example."); - Serial.println("Set mode to command."); +// // Initialize sensor and run default setup. +// if(myUVSensor.begin() == false) { +// Serial.println("Sensor failed to begin. Please check your wiring!"); +// Serial.println("Spinning..."); +// while(1); +// } -} +// Serial.println("Sensor began."); -void loop() { +// // Set measurement mode and change device operating mode to measure. +// if(myUVSensor.startMeasurement(MEAS_MODE_CMD) == false) { +// Serial.println("Sensor did not get set properly."); +// Serial.println("Spinning..."); +// while(1); +// } + +// Serial.println("Set mode to command."); + +// } + +// void loop() { - // Send a start measurement command. - if(SFE_BUS_OK != myUVSensor.setStartStateMode(START_STATE_ENABLED)) - Serial.println("Error starting reading!"); +// // Send a start measurement command. +// if(SFE_BUS_OK != myUVSensor.setStartStateMode(START_STATE_ENABLED)) +// Serial.println("Error starting reading!"); - // Wait for a bit longer than the conversion time. - delay(2+(1 << myUVSensor.getConfigConversionTime())); +// // Wait for a bit longer than the conversion time. +// delay(2+(1 << myUVSensor.getConfigConversionTime())); - // Read UV values. - if(SFE_BUS_OK != myUVSensor.readAllUV()) - Serial.println("Error reading UV."); +// // Read UV values. +// if(SFE_BUS_OK != myUVSensor.readAllUV()) +// Serial.println("Error reading UV."); - Serial.print("UVA:"); - Serial.print(myUVSensor.measures.uva); - Serial.print(" UVB:"); - Serial.print(myUVSensor.measures.uvb); - Serial.print(" UVC:"); - Serial.println(myUVSensor.measures.uvc); +// Serial.print("UVA:"); +// Serial.print(myUVSensor.measures.uva); +// Serial.print(" UVB:"); +// Serial.print(myUVSensor.measures.uvb); +// Serial.print(" UVC:"); +// Serial.println(myUVSensor.measures.uvc); - delay(2000); +// delay(2000); -}; +// }; diff --git a/src/SparkFun_AS7331.cpp b/src/SparkFun_AS7331.cpp index c794859..c2f39f8 100644 --- a/src/SparkFun_AS7331.cpp +++ b/src/SparkFun_AS7331.cpp @@ -1,116 +1,99 @@ #include "SparkFun_AS7331.h" -// TODO: FIX -bool SfeAS7331Driver::begin(sfeBusDevice *theBus, sfeBusDeviceSettings *devSettings, const uint8_t deviceAddress) -{ - setCommunicationBus(theBus, devSettings); - return begin(deviceAddress); -} - -// TODO: FIX -bool SfeAS7331Driver::begin(sfeBusDevice *theBus, uint8_t deviceAddress) +bool SfeAS7331Driver::begin(const uint8_t &deviceAddress, sfeTkIBus *theBus) { + // Set the internal bus pointer setCommunicationBus(theBus); - return begin(deviceAddress); -} - -// TODO: FIX -bool SfeAS7331Driver::begin(uint8_t deviceAddress) -{ - if (!_sfeBus) - setCommunicationBus(); - - setDeviceAddress(deviceAddress); - _sfeBus->begin(); - - return init(); -} - -// TODO: FIX -bool SfeAS7331Driver::init(bool runSetup) -{ - if (SFE_BUS_OK != _sfeBus->ping(_devSettings)) - return false; + // If the address passed in isn't the default, set the new address. + if(kDefaultAS7331Addr != deviceAddress) + setDeviceAddress(deviceAddress); + // Perform a soft reset so that we make sure the device is addressable. reset(); - if (!isConnected()) - return false; - - if (runSetup) - return runDefaultSetup(); - - return true; + // Get the device setup and ready. + return runDefaultSetup(); } -// bool SfeAS7331Driver::isConnected(void) -// { -// return (AS7331_DEFAULT_DEV_ID == getDeviceID()); -// } - uint8_t SfeAS7331Driver::getDeviceID(void) { uint8_t devID; - if (SFE_BUS_OK != _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_AGEN, &devID, 1U)) + // Read the device ID register, if it errors then return 0. + if (kSTkErrOk != _theBus->readRegisterRegion(kSfeAS7331RegCfgAgen, &devID, 1U)) return 0; return devID; } -// TODO: FIX -bool SfeAS7331Driver::runDefaultSetup(bool runSoftReset = false) +void SfeAS7331Driver::setCommunicationBus(sfeTkIBus *theBus) +{ + _theBus = theBus; +} + +void SfeAS7331Driver::setDeviceAddress(const uint8_t &deviceAddress) { + _address = deviceAddress; +} + +// TODO: Take a look. +bool SfeAS7331Driver::runDefaultSetup(bool runSoftReset) +{ + // Do we need to run a software reset? if (runSoftReset) - return reset(); + reset(); else - { - _state = stateDefault; - _config = configDefault; - measures = measuresDefault; - } + setDefaultSettings(); // This is performed in the reset() function, don't repeat. + // Read the OSR register by itself since the offset isn't contiguous with the rest. sfe_as7331_reg_cfg_osr_t osr; - if (SFE_BUS_OK != getOSR(&osr)) + if (kSTkErrOk != getOSR(osr)) return false; + // Read all the configuration registers in. uint8_t regs[6]; - if (SFE_BUS_OK != readRegister(SFE_AS7331_REGISTER_CFG_CREG1, regs, 6U)) + if (kSTkErrOk != _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg1, regs, 6U)) return false; + // Assign the read in bytes to each register's byte union. + // This allows us to address the individual bits and set them. sfe_as7331_reg_cfg_creg1_t creg1 = {.byte = regs[0]}; sfe_as7331_reg_cfg_creg2_t creg2 = {.byte = regs[1]}; sfe_as7331_reg_cfg_creg3_t creg3 = {.byte = regs[2]}; - sfe_as7331_reg_cfg_break_t breakreg = regs[3]; - sfe_as7331_reg_cfg_edges_t edgesreg = regs[4]; + uint8_t breakreg = regs[3]; + uint8_t edgesreg = regs[4]; sfe_as7331_reg_cfg_optreg_t optreg = {.byte = regs[5]}; - osr.ss = _state.ss; - osr.pd = _state.pd; - osr.dos = _state.opMode; + // Here we make sure the local settings match the sensor's settings + // by changing the sensor's settings. + osr.ss = _startState; + osr.pd = _powerDownEnableState; + osr.dos = _opMode; - creg1.gain = _config.sensorGain; - creg1.time = _config.conversionTime; + creg1.gain = _sensorGain; + creg1.time = _conversionTime; - creg2.en_tm = _config.enableTempConv; - creg2.en_div = _config.dividerEnabled; - creg2.div = _config.dividerRange; + creg2.en_tm = _tempConvEnabled; + creg2.en_div = _dividerEnabled; + creg2.div = _dividerRange; - creg3.mmode = _state.mmode; - creg3.sb = _state.sb; - creg3.rdyod = _config.readyPinMode; - creg3.cclk = _config.cclk; + creg3.mmode = _mmode; + creg3.sb = _standbyState; + creg3.rdyod = _readyPinMode; + creg3.cclk = _cclk; - breakreg = _config.breakTime; + breakreg = _breakTime; - edgesreg = _config.numEdges; + edgesreg = _numEdges; - optreg.init_idx = _config.indexMode; + optreg.init_idx = _indexMode; - if (SFE_BUS_OK != setOSR(&osr)) + // Write OSR first, since the offset is different from the rest. + if (kSTkErrOk != setOSR(osr)) return false; + // Assign the registers to a byte array for writing to the device. regs[0] = creg1.byte; regs[1] = creg2.byte; regs[2] = creg3.byte; @@ -118,104 +101,55 @@ bool SfeAS7331Driver::runDefaultSetup(bool runSoftReset = false) regs[4] = edgesreg; regs[5] = optreg.byte; - if (SFE_BUS_OK != writeRegister(SFE_AS7331_REGISTER_CFG_CREG1, regs, 6U)) + // Write the bytes to the sensor, ensuring the device matches local settings. + if (kSTkErrOk != _theBus->writeRegisterRegion(kSfeAS7331RegCfgCreg1, regs, 6U)) return false; + // Calculate new conversion factors to make sure they match the current settings. calculateConversionFactors(); return true; } // TODO: FIX -bool SfeAS7331Driver::prepareMeasurement(const as7331_meas_mode_t measMode = MEAS_MODE_CONT, bool startMeasure = false) +bool SfeAS7331Driver::prepareMeasurement(const as7331_meas_mode_t measMode, bool startMeasure) { - if (_state.pd == POWER_DOWN_ENABLE) - if (SFE_BUS_OK != setPowerState(POWER_DOWN_DISABLE)) + if(_opMode != DEVICE_MODE_CFG) + return false; + + if (_powerDownEnableState) + if (kSTkErrOk != setPowerDownState(false)) return false; - if (_state.mmode != measMode) + if (_mmode != measMode) { - if (SFE_BUS_OK != setStandbyMode(STANDBY_DISABLED)) + if (kSTkErrOk != setStandbyState(false)) return false; - if (SFE_BUS_OK != setMeasurementMode(measMode)) + if (kSTkErrOk != setMeasurementMode(measMode)) return false; } - if (_state.opMode != DEVICE_MODE_MEAS) - if (SFE_BUS_OK != setOperationMode(DEVICE_MODE_MEAS)) + if (_opMode != DEVICE_MODE_MEAS) + if (kSTkErrOk != setOperationMode(DEVICE_MODE_MEAS)) return false; if (startMeasure) - if (SFE_BUS_OK != setStartStateMode(START_STATE_ENABLED)) + if (kSTkErrOk != setStartState(true)) return false; return true; } -// TODO: FIX -int8_t SfeAS7331Driver::setCommunicationBus(sfeBusDevice *theBus, sfeBusDeviceSettings *deviceSettings) -{ - int8_t result = setCommunicationBus(theBus); - if (SFE_BUS_OK != result) - return result; - - return setCommunicationDevSettings(deviceSettings); -} - -// TODO: FIX -int8_t SfeAS7331Driver::setCommunicationBus(sfeBusDevice *theBus = nullptr) -{ - if (theBus == nullptr) - theBus = new sfeBusDevice(); - - if (!theBus) - return SFE_BUS_E_NULL_PTR; - - _sfeBus = theBus; - - return SFE_BUS_OK; -} - -// TODO: FIX -int8_t SfeAS7331Driver::setCommunicationDevSettings(sfeBusDeviceSettings *deviceSettings = nullptr) -{ - if (deviceSettings == nullptr) - deviceSettings = new sfeBusDeviceSettings(); - - if (!deviceSettings) - return SFE_BUS_E_NULL_DEV_SETTINGS; - - _devSettings = deviceSettings; - - return SFE_BUS_OK; -} - -// TODO: FIX -int8_t SfeAS7331Driver::setDeviceAddress(const uint8_t deviceAddress) -{ - int8_t result = SFE_BUS_OK; - - if (!_devSettings) - result = setCommunicationDevSettings(); - - if (SFE_BUS_OK != result) - return result; - - _devSettings->devAddr = deviceAddress; - - return SFE_BUS_OK; -} - bool SfeAS7331Driver::reset(void) { sfe_as7331_reg_cfg_osr_t osr; - if (SFE_BUS_OK != getOSR(osr)) + if (kSTkErrOk != getOSR(osr)) return false; osr.sw_res = 1; - if (SFE_BUS_OK != setOSR(osr)) + if (kSTkErrOk != setOSR(osr)) return false; setDefaultSettings(); @@ -223,49 +157,64 @@ bool SfeAS7331Driver::reset(void) return true; } -int8_t SfeAS7331Driver::readTemp(void) +sfeTkError_t SfeAS7331Driver::readTemp(void) { + if(_opMode != DEVICE_MODE_MEAS) + return kSTkErrFail; + uint8_t tempRaw[2]; - int8_t result = _theBus->readRegisterRegion(SFE_AS7331_REGISTER_MEAS_TEMP, tempRaw, 2U); + sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasTemp, tempRaw, 2U); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _temperature = convertRawTempToTempC((uint16_t)tempRaw[1] << 8 | tempRaw[0]); - return SFE_BUS_OK; + return kSTkErrOk; } -int8_t SfeAS7331Driver::readUVA(void) +sfeTkError_t SfeAS7331Driver::readUVA(void) { + if(_opMode != DEVICE_MODE_MEAS) + return kSTkErrFail; + return readRawUV(AS7331_UVA); } -int8_t SfeAS7331Driver::readUVB(void) +sfeTkError_t SfeAS7331Driver::readUVB(void) { + if(_opMode != DEVICE_MODE_MEAS) + return kSTkErrFail; + return readRawUV(AS7331_UVB); } -int8_t SfeAS7331Driver::readUVC(void) +sfeTkError_t SfeAS7331Driver::readUVC(void) { + if(_opMode != DEVICE_MODE_MEAS) + return kSTkErrFail; + return readRawUV(AS7331_UVC); } -int8_t SfeAS7331Driver::readAllUV(void) +sfeTkError_t SfeAS7331Driver::readAllUV(void) { + if(_opMode != DEVICE_MODE_MEAS) + return kSTkErrFail; + uint8_t dataRaw[6]; - int8_t result = _theBus->readRegisterRegion(SFE_AS7331_REGISTER_MEAS_MRES1, dataRaw, 6U); + sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasMres1, dataRaw, 6U); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; if (_mmode == MEAS_MODE_SYND) { result = readOutConv(); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; float convFactor = 1.0f / (((float)_outputConversionTime) * ((float)(1 << (11 - _sensorGain)))); @@ -281,21 +230,24 @@ int8_t SfeAS7331Driver::readAllUV(void) _uvc = convertRawUVVal((uint16_t)dataRaw[5] << 8 | dataRaw[4], _conversionC); } - return SFE_BUS_OK; + return kSTkErrOk; } -int8_t SfeAS7331Driver::readAll(void) +sfeTkError_t SfeAS7331Driver::readAll(void) { + if(_opMode != DEVICE_MODE_MEAS) + return kSTkErrFail; + uint8_t dataRaw[8]; - int8_t result = _theBus->readRegisterRegion(SFE_AS7331_REGISTER_MEAS_TEMP, dataRaw, 8U); + sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasTemp, dataRaw, 8U); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; result = readOutConv(); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; if (_mmode == MEAS_MODE_SYND) @@ -315,16 +267,19 @@ int8_t SfeAS7331Driver::readAll(void) _temperature = convertRawTempToTempC((uint16_t)dataRaw[1] << 8 | dataRaw[0]); - return SFE_BUS_OK; + return kSTkErrOk; } -int8_t SfeAS7331Driver::readOutConv(void) +sfeTkError_t SfeAS7331Driver::readOutConv(void) { + if(_opMode != DEVICE_MODE_MEAS) + return kSTkErrFail; + uint8_t tconvRaw[4]; - int8_t result = _theBus->readRegisterRegion(SFE_AS7331_REGISTER_MEAS_OUTCONV_L, tconvRaw, 4U); + sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasOutConvL, tconvRaw, 4U); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _outputConversionTime = (uint32_t)(((uint32_t)tconvRaw[3] << 24) | @@ -332,7 +287,7 @@ int8_t SfeAS7331Driver::readOutConv(void) ((uint32_t)tconvRaw[1] << 8) | tconvRaw[0]); - return SFE_BUS_OK; + return kSTkErrOk; } as7331_gain_t SfeAS7331Driver::getGain(void) @@ -340,27 +295,30 @@ as7331_gain_t SfeAS7331Driver::getGain(void) return _sensorGain; } -int8_t SfeAS7331Driver::setGain(const as7331_gain_t &gain) +sfeTkError_t SfeAS7331Driver::setGain(const as7331_gain_t &gain) { - int8_t result = SFE_BUS_OK; + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_creg1_t creg1; result = getCReg1(creg1); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; creg1.gain = gain; result = setCReg1(creg1); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _sensorGain = gain; calculateConversionFactors(); - return SFE_BUS_OK; + return kSTkErrOk; } as7331_conv_clk_freq_t SfeAS7331Driver::getCClk(void) @@ -368,27 +326,30 @@ as7331_conv_clk_freq_t SfeAS7331Driver::getCClk(void) return _cclk; } -int8_t SfeAS7331Driver::setCClk(const as7331_conv_clk_freq_t &cclk) +sfeTkError_t SfeAS7331Driver::setCClk(const as7331_conv_clk_freq_t &cclk) { - int8_t result = SFE_BUS_OK; + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_creg3_t creg3; - result = getCReg1(creg3); - if (SFE_BUS_OK != result) + result = getCReg3(creg3); + if (kSTkErrOk != result) return result; creg3.cclk = cclk; - result = setCReg1(creg3); - if (SFE_BUS_OK != result) + result = setCReg3(creg3); + if (kSTkErrOk != result) return result; _cclk = cclk; calculateConversionFactors(); - return SFE_BUS_OK; + return kSTkErrOk; } as7331_conv_time_t SfeAS7331Driver::getConversionTime(void) @@ -396,53 +357,59 @@ as7331_conv_time_t SfeAS7331Driver::getConversionTime(void) return _conversionTime; } -int8_t SfeAS7331Driver::setConversionTime(const as7331_conv_time_t &convTime) +sfeTkError_t SfeAS7331Driver::setConversionTime(const as7331_conv_time_t &convTime) { - int8_t result = SFE_BUS_OK; + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_creg1_t creg1; result = getCReg1(creg1); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; creg1.time = convTime; result = setCReg1(creg1); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _conversionTime = convTime; calculateConversionFactors(); - return SFE_BUS_OK; + return kSTkErrOk; } -as7331_ready_pin_mode_t SfeAS7331Driver::getReadyPinMode(void) +bool SfeAS7331Driver::getReadyPinMode(void) { return _readyPinMode; } -int8_t SfeAS7331Driver::setReadyPinMode(const bool &pinMode) +sfeTkError_t SfeAS7331Driver::setReadyPinMode(const bool &pinMode) { - int8_t result = SFE_BUS_OK; + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_creg3_t creg3; result = getCReg3(creg3); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; creg3.rdyod = pinMode; result = setCReg3(creg3); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _readyPinMode = pinMode; - return SFE_BUS_OK; + return kSTkErrOk; } bool SfeAS7331Driver::getDigitalDividerEnabled(void) @@ -451,32 +418,35 @@ bool SfeAS7331Driver::getDigitalDividerEnabled(void) } // TODO: FIX -int8_t SfeAS7331Driver::setDigitalDividerEnabled(const bool &isEnabled) +sfeTkError_t SfeAS7331Driver::setDigitalDividerEnabled(const bool &isEnabled) { + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + // TODO: REDO THIS if (_dividerEnabled == isEnabled) - return SFE_BUS_OK; + return kSTkErrOk; - int8_t result = SFE_BUS_OK; + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_creg2_t creg2; result = getCReg2(creg2); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; creg2.en_div = isEnabled; result = setCReg2(creg2); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _dividerEnabled = isEnabled; - if (isEnabled == DIVIDER_ENABLED) + if (isEnabled) calculateConversionFactors(); - return SFE_BUS_OK; + return kSTkErrOk; } as7331_divider_val_t SfeAS7331Driver::getDigitalDividerRange(void) @@ -485,31 +455,34 @@ as7331_divider_val_t SfeAS7331Driver::getDigitalDividerRange(void) } // TODO: FIX -int8_t SfeAS7331Driver::setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &setEnableDiv = false) +sfeTkError_t SfeAS7331Driver::setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &setEnableDiv) { + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + // TODO: REDO THIS - int8_t result = SFE_BUS_OK; + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_creg2_t creg2; - result = getCReg2(&creg2); - if (SFE_BUS_OK != result) + result = getCReg2(creg2); + if (kSTkErrOk != result) return result; creg2.div = divider; - result = setCReg2(&creg2); - if (SFE_BUS_OK != result) + result = setCReg2(creg2); + if (kSTkErrOk != result) return result; - _config.dividerRange = divider; + _dividerRange = divider; if (setEnableDiv) - result = setEnableDivider(DIVIDER_ENABLED); + result = setDigitalDividerEnabled(true); else calculateConversionFactors(); - return SFE_BUS_OK; + return kSTkErrOk; } bool SfeAS7331Driver::getSyndTempConversionEnabled(void) @@ -517,25 +490,28 @@ bool SfeAS7331Driver::getSyndTempConversionEnabled(void) return _tempConvEnabled; } -int8_t SfeAS7331Driver::setSyndTempConversionEnabled(const bool &isEnabled) +sfeTkError_t SfeAS7331Driver::setSyndTempConversionEnabled(const bool &isEnabled) { - int8_t result = SFE_BUS_OK; + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_creg2_t creg2; result = getCReg2(creg2); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; creg2.en_tm = isEnabled; result = setCReg2(creg2); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _tempConvEnabled = isEnabled; - return SFE_BUS_OK; + return kSTkErrOk; } bool SfeAS7331Driver::getIndexMode(void) @@ -543,25 +519,28 @@ bool SfeAS7331Driver::getIndexMode(void) return _indexMode; } -int8_t SfeAS7331Driver::setIndexMode(const bool &indexMode) +sfeTkError_t SfeAS7331Driver::setIndexMode(const bool &indexMode) { - int8_t result = SFE_BUS_OK; + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_optreg_t optreg; result = getOptIndex(optreg); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; optreg.init_idx = indexMode; result = setOptIndex(optreg); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _indexMode = indexMode; - return SFE_BUS_OK; + return kSTkErrOk; } uint8_t SfeAS7331Driver::getBreakTime(void) @@ -569,25 +548,28 @@ uint8_t SfeAS7331Driver::getBreakTime(void) return _breakTime; } -int8_t SfeAS7331Driver::setBreakTime(const uint8_t &breakTime) +sfeTkError_t SfeAS7331Driver::setBreakTime(const uint8_t &breakTime) { - int8_t result = SFE_BUS_OK; + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_break_t breakreg; + uint8_t breakreg; result = getBreak(breakreg); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; breakreg = breakTime; result = setBreak(breakreg); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _breakTime = breakTime; - return SFE_BUS_OK; + return kSTkErrOk; } uint8_t SfeAS7331Driver::getNumEdges(void) @@ -595,25 +577,28 @@ uint8_t SfeAS7331Driver::getNumEdges(void) return _numEdges; } -int8_t SfeAS7331Driver::setNumEdges(const uint8_t &numEdges) +sfeTkError_t SfeAS7331Driver::setNumEdges(const uint8_t &numEdges) { - int8_t result = SFE_BUS_OK; + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_edges_t edgesreg; + uint8_t edgesreg; result = getEdges(edgesreg); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; edgesreg = numEdges; result = setEdges(edgesreg); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _numEdges = numEdges; - return SFE_BUS_OK; + return kSTkErrOk; } bool SfeAS7331Driver::getPowerDownState(void) @@ -621,25 +606,25 @@ bool SfeAS7331Driver::getPowerDownState(void) return _powerDownEnableState; } -int8_t SfeAS7331Driver::setPowerDownState(const bool &pd) +sfeTkError_t SfeAS7331Driver::setPowerDownState(const bool &pd) { - int8_t result = SFE_BUS_OK; + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_osr_t osr; result = getOSR(osr); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; osr.pd = pd; result = setOSR(osr); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _powerDownEnableState = pd; - return SFE_BUS_OK; + return kSTkErrOk; } as7331_device_op_state_t SfeAS7331Driver::getOperationMode(void) @@ -647,25 +632,25 @@ as7331_device_op_state_t SfeAS7331Driver::getOperationMode(void) return _opMode; } -int8_t SfeAS7331Driver::setOperationMode(const as7331_device_op_state_t &opMode) +sfeTkError_t SfeAS7331Driver::setOperationMode(const as7331_device_op_state_t &opMode) { - int8_t result = SFE_BUS_OK; + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_osr_t osr; result = getOSR(osr); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; osr.dos = opMode; result = setOSR(osr); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _opMode = opMode; - return SFE_BUS_OK; + return kSTkErrOk; } as7331_meas_mode_t SfeAS7331Driver::getMeasurementMode(void) @@ -673,25 +658,28 @@ as7331_meas_mode_t SfeAS7331Driver::getMeasurementMode(void) return _mmode; } -int8_t SfeAS7331Driver::setMeasurementMode(const as7331_meas_mode_t &measMode) +sfeTkError_t SfeAS7331Driver::setMeasurementMode(const as7331_meas_mode_t &measMode) { - int8_t result = SFE_BUS_OK; + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_creg3_t creg3; result = getCReg3(creg3); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; creg3.mmode = measMode; result = setCReg3(creg3); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _mmode = measMode; - return SFE_BUS_OK; + return kSTkErrOk; } bool SfeAS7331Driver::getStandbyState(void) @@ -699,25 +687,28 @@ bool SfeAS7331Driver::getStandbyState(void) return _standbyState; } -int8_t SfeAS7331Driver::setStandbyState(const bool &standby) +sfeTkError_t SfeAS7331Driver::setStandbyState(const bool &standby) { - int8_t result = SFE_BUS_OK; + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_creg3_t creg3; result = getCReg3(creg3); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; creg3.sb = standby; result = setCReg3(creg3); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _standbyState = standby; - return SFE_BUS_OK; + return kSTkErrOk; } bool SfeAS7331Driver::getStartState(void) @@ -725,143 +716,171 @@ bool SfeAS7331Driver::getStartState(void) return _startState; } -int8_t SfeAS7331Driver::setStartState(const bool &startState) +sfeTkError_t SfeAS7331Driver::setStartState(const bool &startState) { - int8_t result = SFE_BUS_OK; + sfeTkError_t result = kSTkErrOk; sfe_as7331_reg_cfg_osr_t osr; result = getOSR(osr); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; osr.ss = startState; result = setOSR(osr); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; _startState = startState; - return SFE_BUS_OK; + return kSTkErrOk; } -// TODO: add state check -int8_t SfeAS7331Driver::getStatus(sfe_as7331_reg_meas_osr_status_t &statusReg) +sfeTkError_t SfeAS7331Driver::getStatus(sfe_as7331_reg_meas_osr_status_t &statusReg) { - int8_t result = SFE_BUS_OK; + if(_opMode != DEVICE_MODE_MEAS) + return kSTkErrFail; + + sfeTkError_t result = kSTkErrOk; uint8_t statusRaw[2]; - result = readRegisterRegion(SFE_AS7331_REGISTER_MEAS_OSR_STATUS, statusRaw, 2U); + result = _theBus->readRegisterRegion(kSfeAS7331RegMeasOsrStatus, statusRaw, 2U); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; // Shift MSB to top of 16-bit register, then OR with LSB. statusReg.word = (uint16_t)statusRaw[1] << 8 | statusRaw[0]; - return SFE_BUS_OK; + return kSTkErrOk; } -// TODO: add state check -int8_t SfeAS7331Driver::getOSR(sfe_as7331_reg_cfg_osr_t &osrReg) +sfeTkError_t SfeAS7331Driver::getOSR(sfe_as7331_reg_cfg_osr_t &osrReg) { - return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_OSR, osrReg.byte, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->readRegisterRegion(kSfeAS7331RegCfgOsr, &osrReg.byte, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::setOSR(const sfe_as7331_reg_cfg_osr_t &osrReg) +sfeTkError_t SfeAS7331Driver::setOSR(const sfe_as7331_reg_cfg_osr_t &osrReg) { - return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_OSR, osrReg.byte, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->writeRegisterRegion(kSfeAS7331RegCfgOsr, &osrReg.byte, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::getCReg1(sfe_as7331_reg_cfg_creg1_t &creg1) +sfeTkError_t SfeAS7331Driver::getCReg1(sfe_as7331_reg_cfg_creg1_t &creg1) { - return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG1, creg1.byte, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg1, &creg1.byte, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::setCReg1(const sfe_as7331_reg_cfg_creg1_t &creg1) +sfeTkError_t SfeAS7331Driver::setCReg1(const sfe_as7331_reg_cfg_creg1_t &creg1) { - return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG1, creg1.byte, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->writeRegisterRegion(kSfeAS7331RegCfgCreg1, &creg1.byte, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::getCReg2(sfe_as7331_reg_cfg_creg2_t &creg2) +sfeTkError_t SfeAS7331Driver::getCReg2(sfe_as7331_reg_cfg_creg2_t &creg2) { - return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG2, creg2.byte, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg2, &creg2.byte, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::setCReg2(const sfe_as7331_reg_cfg_creg2_t &creg2) +sfeTkError_t SfeAS7331Driver::setCReg2(const sfe_as7331_reg_cfg_creg2_t &creg2) { - return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG2, creg2.byte, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->writeRegisterRegion(kSfeAS7331RegCfgCreg2, &creg2.byte, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::getCReg3(sfe_as7331_reg_cfg_creg3_t &creg3) +sfeTkError_t SfeAS7331Driver::getCReg3(sfe_as7331_reg_cfg_creg3_t &creg3) { - return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG3, creg3.byte, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg3, &creg3.byte, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::setCReg3(const sfe_as7331_reg_cfg_creg3_t &creg3) +sfeTkError_t SfeAS7331Driver::setCReg3(const sfe_as7331_reg_cfg_creg3_t &creg3) { - return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_CREG3, creg3.byte, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->writeRegisterRegion(kSfeAS7331RegCfgCreg3, &creg3.byte, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::getBreak(uint8_t &breakReg) +sfeTkError_t SfeAS7331Driver::getBreak(uint8_t &breakReg) { - return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_BREAK, breakReg, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->readRegisterRegion(kSfeAS7331RegCfgBreak, &breakReg, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::setBreak(const uint8_t &breakReg) +sfeTkError_t SfeAS7331Driver::setBreak(const uint8_t &breakReg) { - return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_BREAK, breakReg, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->writeRegisterRegion(kSfeAS7331RegCfgBreak, &breakReg, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::getEdges(uint8_t &edgesReg) +sfeTkError_t SfeAS7331Driver::getEdges(uint8_t &edgesReg) { - return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_EDGES, edgesReg, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->readRegisterRegion(kSfeAS7331RegCfgEdges, &edgesReg, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::setEdges(const uint8_t &edgesReg) +sfeTkError_t SfeAS7331Driver::setEdges(const uint8_t &edgesReg) { - return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_EDGES, edgesReg, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->writeRegisterRegion(kSfeAS7331RegCfgEdges, &edgesReg, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::getOptIndex(sfe_as7331_reg_cfg_optreg_t &optReg) +sfeTkError_t SfeAS7331Driver::getOptIndex(sfe_as7331_reg_cfg_optreg_t &optReg) { - return _theBus->readRegisterRegion(SFE_AS7331_REGISTER_CFG_OPTREG, optReg.byte, 1U); + if(_opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->readRegisterRegion(kSfeAS7331RegCfgOptReg, &optReg.byte, 1U); } -// TODO: add state check -int8_t SfeAS7331Driver::setOptIndex(const sfe_as7331_reg_cfg_optreg_t &optReg) +sfeTkError_t SfeAS7331Driver::setOptIndex(const sfe_as7331_reg_cfg_optreg_t &optReg) { - return _theBus->writeRegisterRegion(SFE_AS7331_REGISTER_CFG_OPTREG, optReg.byte, 1U); + if(!_theBus || _opMode != DEVICE_MODE_CFG) + return kSTkErrFail; + + return _theBus->writeRegisterRegion(kSfeAS7331RegCfgOptReg, &optReg.byte, 1U); } -int8_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) +sfeTkError_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) { uint8_t uvRawVal[2]; uint8_t address = 0; - float *fsr = nullptr; float *retUV = nullptr; - - if (_mmode != MEAS_MODE_SYND) // There's a conversion factor already available. - float *conv = nullptr; + float *conv = nullptr; + const float *fsr = nullptr; switch (uv_type) { case AS7331_UVA: default: // Since it's an enum, you can't miscall this function, so default to A. - address = SFE_AS7331_REGISTER_MEAS_MRES1; + address = kSfeAS7331RegMeasMres1; fsr = &_fsrA; retUV = &_uva; if (_mmode != MEAS_MODE_SYND) @@ -870,7 +889,7 @@ int8_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) } break; case AS7331_UVB: - address = SFE_AS7331_REGISTER_MEAS_MRES2; + address = kSfeAS7331RegMeasMres2; fsr = &_fsrB; retUV = &_uvb; if (_mmode != MEAS_MODE_SYND) @@ -879,7 +898,7 @@ int8_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) } break; case AS7331_UVC: - address = SFE_AS7331_REGISTER_MEAS_MRES3; + address = kSfeAS7331RegMeasMres3; fsr = &_fsrC; retUV = &_uvc; if (_mmode != MEAS_MODE_SYND) @@ -889,16 +908,16 @@ int8_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) break; } - int8_t result = _theBus->readRegisterRegion(address, uvRawVal, 2U); + sfeTkError_t result = _theBus->readRegisterRegion(address, uvRawVal, 2U); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; if (_mmode == MEAS_MODE_SYND) { result = readOutConv(); - if (SFE_BUS_OK != result) + if (kSTkErrOk != result) return result; // See datasheet section 7.4 Equation 4. @@ -911,10 +930,10 @@ int8_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) } else { - *retUV = convertRawUVVal((uint16_t)uvRawVal[1] << 8 | uvRawVal[0], conv); + *retUV = convertRawUVVal((uint16_t)uvRawVal[1] << 8 | uvRawVal[0], *conv); } - return SFE_BUS_OK; + return kSTkErrOk; } float SfeAS7331Driver::convertRawTempToTempC(const uint16_t &inputVal) @@ -939,6 +958,7 @@ void SfeAS7331Driver::calculateConversionFactors(void) // ------------------- // gain * tconv * cclk // Calculates a conversion factor based on the sensor gain, conversion time, and conversion clock frequency. + // Only valid for CONT, CMD, SYNS modes. float convFactor = 1.0f / (((float)(1 << (11 - _sensorGain))) * ((float)(1 << _conversionTime)) * ((float)(1024.0 * (1 << _cclk)))); diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h index da2670f..3ff0cbf 100644 --- a/src/SparkFun_AS7331.h +++ b/src/SparkFun_AS7331.h @@ -33,13 +33,13 @@ /////////////////////////////////////////////////////////////////////////////// // 7-bit address defined as [1, 1, 1, 0, 1, A1, A0] where A1/A0 are the // physical address pins tied high or low -#define AS7331_ADDR_DEFAULT 0x74 // A1 = 0, A0 = 0 -#define AS7331_ADDR_SEC 0x75 // A1 = 0, A1 = 1 -#define AS7331_ADDR_TER 0x76 // A1 = 1, A0 = 0 -#define AS7331_ADDR_QUA 0x77 // A1 = 1, A0 = 1 +const uint8_t kDefaultAS7331Addr = 0x74; // A1 = 0, A0 = 0 +const uint8_t kSecondaryAS7331Addr = 0x75; // A1 = 0, A1 = 1 +const uint8_t kTertiaryAS7331Addr = 0x76; // A1 = 1, A0 = 0 +const uint8_t kQuaternaryAS7331Addr = 0x77; // A1 = 1, A0 = 1 -#define AS7331_DEFAULT_DEV_ID 0x21 // When polling the AGEN register, this should be returned on boot. -#define AS7331_DEV_ID_HIGH 0x2 // Top nibble of the AGEN byte, always the same. +const uint8_t kDefaultAS7331DeviceID = 0x21; // When polling the AGEN register, this should be returned on boot. +const uint8_t kAS7331DeviceIDTopNibble = 0x2; // Top nibble of the AGEN byte, always the same. /////////////////////////////////////////////////////////////////////////////// // Enum Definitions @@ -140,7 +140,7 @@ typedef enum // The following registers map to the underlying registers of the device. /////////////////////////////////////////////////////////////////////////////// -#define SFE_AS7331_REGISTER_CFG_OSR 0x00 // Register address +const uint8_t kSfeAS7331RegCfgOsr = 0x00; // Register address // A union is used here so that individual values from the register can be // accessed or the whole register can be accessed. @@ -156,7 +156,7 @@ typedef union { uint8_t byte; } sfe_as7331_reg_cfg_osr_t; -#define SFE_AS7331_REGISTER_CFG_AGEN 0x02 // Register address +const uint8_t kSfeAS7331RegCfgAgen = 0x02; // Register address // A union is used here so that individual values from the register can be // accessed or the whole register can be accessed. @@ -170,7 +170,7 @@ typedef union { uint8_t byte; } sfe_as7331_reg_cfg_agen_t; -#define SFE_AS7331_REGISTER_CFG_CREG1 0x06 // Register address +const uint8_t kSfeAS7331RegCfgCreg1 = 0x06; // Register address // A union is used here so that individual values from the register can be // accessed or the whole register can be accessed. @@ -183,7 +183,7 @@ typedef union { uint8_t byte; } sfe_as7331_reg_cfg_creg1_t; -#define SFE_AS7331_REGISTER_CFG_CREG2 0x07 // Register address +const uint8_t kSfeAS7331RegCfgCreg2 = 0x07; // Register address // A union is used here so that individual values from the register can be // accessed or the whole register can be accessed. @@ -199,7 +199,7 @@ typedef union { uint8_t byte; } sfe_as7331_reg_cfg_creg2_t; -#define SFE_AS7331_REGISTER_CFG_CREG3 0x08 // Register Address +const uint8_t kSfeAS7331RegCfgCreg3 = 0x08; // Register Address // A union is used here so that individual values from the register can be // accessed or the whole register can be accessed. @@ -216,11 +216,11 @@ typedef union { uint8_t byte; } sfe_as7331_reg_cfg_creg3_t; -#define SFE_AS7331_REGISTER_CFG_BREAK 0x09 // Register address, register is a single uint8_t. +const uint8_t kSfeAS7331RegCfgBreak = 0x09; // Register address, register is a single uint8_t. -#define SFE_AS7331_REGISTER_CFG_EDGES 0x0A // Register address, register is a single uint8_t. +const uint8_t kSfeAS7331RegCfgEdges = 0x0A; // Register address, register is a single uint8_t. -#define SFE_AS7331_REGISTER_CFG_OPTREG 0x0B // Register address +const uint8_t kSfeAS7331RegCfgOptReg = 0x0B; // Register address // A union is used here so that individual values from the register can be // accessed or the whole register can be accessed. @@ -242,7 +242,7 @@ typedef union { // The following registers map to the underlying registers of the device. /////////////////////////////////////////////////////////////////////////////// -#define SFE_AS7331_REGISTER_MEAS_OSR_STATUS 0x00 // Register address +const uint8_t kSfeAS7331RegMeasOsrStatus = 0x00; // Register address // A union is used here so that individual values from the register can be // accessed or the whole register can be accessed. @@ -263,77 +263,40 @@ typedef union { } sfe_as7331_reg_meas_osr_status_t; // Output result registers. 16-bit values unless noted otherwise. -#define SFE_AS7331_REGISTER_MEAS_TEMP 0x01 // 12-bit temperature, MS 4-bits 0. -#define SFE_AS7331_REGISTER_MEAS_MRES1 0x02 -#define SFE_AS7331_REGISTER_MEAS_MRES2 0x03 -#define SFE_AS7331_REGISTER_MEAS_MRES3 0x04 -#define SFE_AS7331_REGISTER_MEAS_OUTCONV_L 0x05 // First 16-bits of 24-bit OUTCONV. -#define SFE_AS7331_REGISTER_MEAS_OUTCONV_H 0x06 // LSB is MSB of OUTCONV, MSB is 0. +const uint8_t kSfeAS7331RegMeasTemp = 0x01; // 12-bit temperature, MS 4-bits 0. +const uint8_t kSfeAS7331RegMeasMres1 = 0x02; +const uint8_t kSfeAS7331RegMeasMres2 = 0x03; +const uint8_t kSfeAS7331RegMeasMres3 = 0x04; +const uint8_t kSfeAS7331RegMeasOutConvL = 0x05; // First 16-bits of 24-bit OUTCONV. +const uint8_t kSfeAS7331RegMeasOutConvH = 0x06; // LSB is MSB of OUTCONV, MSB is 0. /////////////////////////////////////////////////////////////////////////////// class SfeAS7331Driver { public: - SfeAS7331Driver(uint8_t address = AS7331_ADDR_DEFAULT) : _address{address} + SfeAS7331Driver(uint8_t address = kDefaultAS7331Addr) : _address{address} { } /// @brief This method is called to initialize the AS7331 device through the /// specified bus. /// @param theBus Pointer to the bus object. - /// @param devSettings Pointer to the device settings object. /// @param deviceAddress I2C address for the device. /// @return True if successful, false if it fails. - bool begin(sfeBusDevice *theBus, sfeBusDeviceSettings *devSettings, - const uint8_t deviceAddress = AS7331_ADDR_DEFAULT); - - /// @brief This method is called to initialize the AS7331 device through the - /// specified bus. - /// @param theBus Pointer to the bus object. - /// @param deviceAddress I2C address for the device. - /// @return True if successful, false if it fails. - bool begin(sfeBusDevice *theBus, uint8_t deviceAddress = AS7331_ADDR_DEFAULT); - - /// @brief This method is called to initialize the AS7331 device at the - /// specified bus address. - /// @param deviceAddress I2C address for the device. - /// @return True if successful, false if it fails. - bool begin(uint8_t deviceAddress = AS7331_ADDR_DEFAULT); - - /// @brief Finds the device and initializes the default state. - /// @param runSetup Flag to run the default setup if set. - /// @return True if successful, false if it fails. - bool init(bool runSetup = true); + bool begin(const uint8_t &deviceAddress = kDefaultAS7331Addr, sfeTkIBus *theBus = nullptr); /// @brief Requests the device ID from the sensor. /// @return The device ID of the sensor. uint8_t getDeviceID(void); - /// @brief Sets the communication bus to the specified bus and device - /// settings. - /// @param theBus Bus to set as the communication device. - /// @param deviceSettings Device settings to use when setting the bus for this - /// sensor. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCommunicationBus(sfeBusDevice *theBus, sfeBusDeviceSettings *deviceSettings); - - /// @brief Sets the communication bus to the specified bus. Creates new - /// instance if none specified. + /// @brief Sets the communication bus to the specified bus. /// @param theBus Bus to set as the communication devie. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCommunicationBus(sfeBusDevice *theBus = nullptr); - - /// @brief Sets settings of this sensor's device. - /// @param deviceSettings Settings structure for this device. Creates new - /// instance if none specified. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setCommunicationDevSettings(sfeBusDeviceSettings *deviceSettings = nullptr); + void setCommunicationBus(sfeTkIBus *theBus); /// @brief Sets the address that the bus uses to communicate with the sensor. /// @param deviceAddress Device address to use. - /// @return 0 if successful, negative if error, positive for warning. - int8_t setDeviceAddress(const uint8_t deviceAddress); + void setDeviceAddress(const uint8_t &deviceAddress); /// @brief Helper class that sets up the sensor and state in the POR /// configuration. @@ -355,38 +318,38 @@ class SfeAS7331Driver /// @brief Reads the sensor's temperature, converts it to a usable form, and /// saves it to the internal temperature variable. /// @return 0 if successful, negative if error, positive for warning. - int8_t readTemp(void); + sfeTkError_t readTemp(void); /// @brief Reads the sensor's UVA register, converts it to a usable form, and /// saves it to the internal UVA variable. /// @return 0 if successful, negative if error, positive for warning. - int8_t readUVA(void); + sfeTkError_t readUVA(void); /// @brief Reads the sensor's UVB register, converts it to a usable form, and /// saves it to the internal UVB variable. /// @return 0 if successful, negative if error, positive for warning. - int8_t readUVB(void); + sfeTkError_t readUVB(void); /// @brief Reads the sensor's UVC register, converts it to a usable form, and /// saves it to the internal UVC variable. /// @return 0 if successful, negative if error, positive for warning. - int8_t readUVC(void); + sfeTkError_t readUVC(void); /// @brief Read's all three UV registers, converts them to a usable form, then /// saves them to their respective internal variable. /// @return 0 if successful, negative if error, positive for warning. - int8_t readAllUV(void); + sfeTkError_t readAllUV(void); /// @brief Read the sensor's temperature, UV, and external time conversion /// clock counts, converts them, and then saves them to their respective /// internal variable. /// @return 0 if successful, negative if error, positive for warning. - int8_t readAll(void); + sfeTkError_t readAll(void); /// @brief Read the conversion clock counts register and saves it to the /// internal output conversion time variable. /// @return 0 if successful, negative if error, positive for warning. - int8_t readOutConv(void); + sfeTkError_t readOutConv(void); /// @brief Getter for the currently configured gain. /// @return Sensor's gain expressed as (1 << (11 - gain)). @@ -395,7 +358,7 @@ class SfeAS7331Driver /// @brief Sets the UV sensor's gain. /// @param gain The gain to set the sensor to. /// @return 0 if successful, negative if error, positive for warning. - int8_t setGain(const as7331_gain_t &gain); + sfeTkError_t setGain(const as7331_gain_t &gain); /// @brief Getter for the currently configured conversion clock. /// @return Sensor's conversion clock expressed as 1024*(1 << cclk). @@ -404,7 +367,7 @@ class SfeAS7331Driver /// @brief Set the sensor's internal clock speed. /// @param cclk Clock speed to set on the sensor. /// @return 0 if successful, negative if error, positive for warning. - int8_t setCClk(const as7331_conv_clk_freq_t &cclk); + sfeTkError_t setCClk(const as7331_conv_clk_freq_t &cclk); /// @brief Getter for the currently configured conversion time. /// @return Sensor's conversion time expressed as (1 << time). @@ -413,7 +376,7 @@ class SfeAS7331Driver /// @brief Sets the conversion time that the sensor will run to. /// @param convTime Conversion time to set the sensor to. /// @return 0 if successful, negative if error, positive for warning. - int8_t setConversionTime(const as7331_conv_time_t &convTime); + sfeTkError_t setConversionTime(const as7331_conv_time_t &convTime); /// @brief Getter for the currently configured pin mode. /// @return False if push-pull, true if open-drain. @@ -422,7 +385,7 @@ class SfeAS7331Driver /// @brief Sets the ready pin type to push-pull or open-drain. /// @param pinMode Mode to set the ready pin to. /// @return 0 if successful, negative if error, positive for warning. - int8_t setReadyPinMode(const bool &pinMode); + sfeTkError_t setReadyPinMode(const bool &pinMode); /// @brief Getter for the currently configured divider status. /// @return True if Internal predivider is enabled, false otherwise. @@ -431,7 +394,7 @@ class SfeAS7331Driver /// @brief Enables or disables the internal UV result divider. /// @param isEnabled Enable or disable the divder. /// @return 0 if successful, negative if error, positive for warning. - int8_t setDigitalDividerEnabled(const bool &isEnabled); + sfeTkError_t setDigitalDividerEnabled(const bool &isEnabled); /// @brief Getter for the currently configured divider range. /// @return Sensor's internal UV predivider range. @@ -441,7 +404,7 @@ class SfeAS7331Driver /// @param divider Divider value to set. /// @param setEnableDiv Option to turn on the divider if desired. /// @return 0 if successful, negative if error, positive for warning. - int8_t setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &setEnableDiv = false); + sfeTkError_t setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &setEnableDiv = false); /// @brief Getter for the SYND temperature conversion status. /// @return True if temperature conversion is enabled in SYND mode. @@ -450,7 +413,7 @@ class SfeAS7331Driver /// @brief Enables or disables temperature conversion when in SYND mode. /// @param isEnabled Enable or disable the feature. /// @return 0 if successful, negative if error, positive for warning. - int8_t setSyndTempConversionEnabled(const bool &isEnabled); + sfeTkError_t setSyndTempConversionEnabled(const bool &isEnabled); /// @brief Getter for the currently configured I2C compatibility mode. /// @return True if the device will respond to repeat starts, false otherwise. @@ -460,7 +423,7 @@ class SfeAS7331Driver /// don't support repeated start. /// @param indexMode Simple or standard I2C addressing mode. /// @return 0 if successful, negative if error, positive for warning. - int8_t setIndexMode(const bool &indexMode); + sfeTkError_t setIndexMode(const bool &indexMode); /// @brief Getter for the currently configured minimum break time in CONT, /// CMD, SYNS modes. @@ -471,7 +434,7 @@ class SfeAS7331Driver /// @param breakTime Time between measurements, 8us step time, max 2048us. A 0 /// value is a minimum of 3 cclk cycles. /// @return 0 if successful, negative if error, positive for warning. - int8_t setBreakTime(const uint8_t &breakTime); + sfeTkError_t setBreakTime(const uint8_t &breakTime); /// @brief Getter for the currently configured minimum number of edges to end /// conversion when in SYND mode. @@ -483,16 +446,16 @@ class SfeAS7331Driver /// @param numEdges Number of edges prior to terminating conversion in SYND /// mode. 0 is not allowed, 1 is the minimum. /// @return 0 if successful, negative if error, positive for warning. - int8_t setNumEdges(const uint8_t &numEdges); + sfeTkError_t setNumEdges(const uint8_t &numEdges); /// @brief Getter for the current power state. /// @return Sensor's power state. - as7331_power_state_t getPowerDownState(void); + bool getPowerDownState(void); /// @brief Sets the power state of the sensor. /// @param pd Power state to set. /// @return 0 if successful, negative if error, positive for warning. - int8_t setPowerDownState(const bool &pd); + sfeTkError_t setPowerDownState(const bool &pd); /// @brief Getter for the current operational state. /// @return Sensor's operational mode. @@ -501,7 +464,7 @@ class SfeAS7331Driver /// @brief Set the sensor's operating mode. /// @param opMode Operating mode to set. /// @return 0 if successful, negative if error, positive for warning. - int8_t setOperationMode(const as7331_device_op_state_t &opMode); + sfeTkError_t setOperationMode(const as7331_device_op_state_t &opMode); /// @brief Getter for the current measurement state. /// @return Sensor's measurement state. @@ -510,7 +473,7 @@ class SfeAS7331Driver /// @brief Sets the sensor's measurement mode. /// @param measMode Measurement mode to set. /// @return 0 if successful, negative if error, positive for warning. - int8_t setMeasurementMode(const as7331_meas_mode_t &measMode); + sfeTkError_t setMeasurementMode(const as7331_meas_mode_t &measMode); /// @brief Getter for the current standby state. /// @return Sensor's standby state. @@ -519,7 +482,7 @@ class SfeAS7331Driver /// @brief Sets the sensor's standby mode. /// @param standby State to set. /// @return 0 if successful, negative if error, positive for warning. - int8_t setStandbyState(const bool &standby); + sfeTkError_t setStandbyState(const bool &standby); /// @brief Getter for the current start state. /// @return Sensor's start state. @@ -528,112 +491,112 @@ class SfeAS7331Driver /// @brief Sets the sensor's start state. This begins measurement. /// @param startState Start state to set. /// @return 0 if successful, negative if error, positive for warning. - int8_t setStartState(const bool &startState); + sfeTkError_t setStartState(const bool &startState); /// @brief Gets the sensor's status when in measurement operation mode. /// @param statusReg Pointer to a register struct to store the sensor's /// current status. /// @return 0 if successful, negative if error, positive for warning. - int8_t getStatus(sfe_as7331_reg_meas_osr_status_t &statusReg); + sfeTkError_t getStatus(sfe_as7331_reg_meas_osr_status_t &statusReg); /// @brief Gets the operational state register when in configuration operation /// mode. /// @param osrReg Pointer to a register struct to store the sensor's current /// OSR register. /// @return 0 if successful, negative if error, positive for warning. - int8_t getOSR(sfe_as7331_reg_cfg_osr_t &osrReg); + sfeTkError_t getOSR(sfe_as7331_reg_cfg_osr_t &osrReg); /// @brief Sets the operational state register when in configuration operation /// mode. /// @param osrReg Pointer to a register struct that has the new register /// configuration. /// @return 0 if successful, negative if error, positive for warning. - int8_t setOSR(const sfe_as7331_reg_cfg_osr_t &osrReg); + sfeTkError_t setOSR(const sfe_as7331_reg_cfg_osr_t &osrReg); /// @brief Gets the configuration register #1 when in configuration operation /// mode. /// @param creg1 Pointer to a register struct to store the sensor's current /// creg1 register. /// @return 0 if successful, negative if error, positive for warning. - int8_t getCReg1(sfe_as7331_reg_cfg_creg1_t &creg1); + sfeTkError_t getCReg1(sfe_as7331_reg_cfg_creg1_t &creg1); /// @brief Sets the configuration register #1 when in configuration operation /// mode. /// @param creg1 Pointer to a register struct that has the new register /// configuration. /// @return 0 if successful, negative if error, positive for warning. - int8_t setCReg1(const sfe_as7331_reg_cfg_creg1_t &creg1); + sfeTkError_t setCReg1(const sfe_as7331_reg_cfg_creg1_t &creg1); /// @brief Gets the configuration register #2 when in configuration operation /// mode. /// @param creg2 Pointer to a register struct to store the sensor's current /// creg2 register. /// @return 0 if successful, negative if error, positive for warning. - int8_t getCReg2(sfe_as7331_reg_cfg_creg2_t &creg2); + sfeTkError_t getCReg2(sfe_as7331_reg_cfg_creg2_t &creg2); /// @brief Sets the configuration register #2 when in configuration operation /// mode. /// @param creg2 Pointer to a register struct that has the new register /// configuration. /// @return 0 if successful, negative if error, positive for warning. - int8_t setCReg2(const sfe_as7331_reg_cfg_creg2_t &creg2); + sfeTkError_t setCReg2(const sfe_as7331_reg_cfg_creg2_t &creg2); /// @brief Gets the configuration register #3 when in configuration operation /// mode. /// @param creg3 Pointer to a register struct to store the sensor's current /// creg3 register. /// @return 0 if successful, negative if error, positive for warning. - int8_t getCReg3(sfe_as7331_reg_cfg_creg3_t &creg3); + sfeTkError_t getCReg3(sfe_as7331_reg_cfg_creg3_t &creg3); /// @brief Sets the configuration register #3 when in configuration operation /// mode. /// @param creg3 Pointer to a register struct that has the new register /// configuration. /// @return 0 if successful, negative if error, positive for warning. - int8_t setCReg3(const sfe_as7331_reg_cfg_creg3_t &creg3); + sfeTkError_t setCReg3(const sfe_as7331_reg_cfg_creg3_t &creg3); /// @brief Gets the break register when in configuration operation mode. /// @param breakReg Pointer to a register struct to store the sensor's current /// break register. /// @return 0 if successful, negative if error, positive for warning. - int8_t getBreak(uint8_t &breakReg); + sfeTkError_t getBreak(uint8_t &breakReg); /// @brief Sets the break register when in configuration operation mode. /// @param breakReg Pointer to a register struct that has the new register /// configuration. /// @return 0 if successful, negative if error, positive for warning. - int8_t setBreak(const uint8_t &breakReg); + sfeTkError_t setBreak(const uint8_t &breakReg); /// @brief Gets the edges register when in configuration operation mode. /// @param edgesReg Pointer to a register struct to store the sensor's current /// edges register. /// @return 0 if successful, negative if error, positive for warning. - int8_t getEdges(uint8_t &edgesReg); + sfeTkError_t getEdges(uint8_t &edgesReg); /// @brief Sets the edges register when in configuration operation mode. /// @param edgesReg Pointer to a register struct that has the new register /// configuration. /// @return 0 if successful, negative if error, positive for warning. - int8_t setEdges(const uint8_t &edgesReg); + sfeTkError_t setEdges(const uint8_t &edgesReg); /// @brief Gets the option register when in configuration operation mode. /// @param optReg Pointer to a register struct to store the sensor's current /// option register. /// @return 0 if successful, negative if error, positive for warning. - int8_t getOptIndex(sfe_as7331_reg_cfg_optreg_t &optReg); + sfeTkError_t getOptIndex(sfe_as7331_reg_cfg_optreg_t &optReg); /// @brief Sets the option register when in configuration operation mode. /// @param optReg Pointer to a register struct that has the new register /// configuration. /// @return 0 if successful, negative if error, positive for warning. - int8_t setOptIndex(const sfe_as7331_reg_cfg_optreg_t &optReg); + sfeTkError_t setOptIndex(const sfe_as7331_reg_cfg_optreg_t &optReg); private: /// @brief Reads a UV result register and saves it to the respective internal /// variable. /// @param uv_type The type of UV you want to read. /// @return 0 if successful, negative if error, positive if warning - int8_t readRawUV(const as7331_uv_type &uv_type); + sfeTkError_t readRawUV(const as7331_uv_type &uv_type); /// @brief Converts the raw temperature value to a human readable form. /// @param inputVal Raw temperature value to convert. @@ -660,10 +623,10 @@ class SfeAS7331Driver uint8_t _breakTime; // Local configuration value. Value is in us/8. EX: _breakTime = 20 means 20*8 = 160us. uint8_t _numEdges; // Local configuration value. Edges seen on SYN pin before ending conversion in SYND mode. - bool _readyPinMode; // Local configuration value. False is Push/Pull True is Open/Drain. - bool _dividerEnabled; // Local configuration value. False is disabled, True is enabled. - bool _tempConvEnabled; // Local configuration value. False is disabled, True is enabled. - bool _indexMode; // Local configuration value. False is for controllers where repeat start is not available when it is. + bool _readyPinMode; // Local configuration value. False is Push/Pull True is Open/Drain. + bool _dividerEnabled; // Local configuration value. False is disabled, True is enabled. + bool _tempConvEnabled; // Local configuration value. False is disabled, True is enabled. + bool _indexMode; // Local configuration value. False is for controllers without repeat start. bool _standbyState; // Local state value. False means the device is not in standby mode. bool _startState; // Local state value. False means the device is not allowed to measure. bool _powerDownEnableState; // Local state value. False means the device is NOT POWERED DOWN. This is inverse. @@ -701,11 +664,11 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver { } - /// @brief Sets up Arduino specific I2C driver things then calls the super class begin. + /// @brief Sets up Arduino I2C driver using the default I2C address then calls the super class begin. /// @return True if successful, false otherwise. bool begin(void) { - if (!_theI2CBus.init(AS7331_ADDR_DEFAULT)) + if (!_theI2CBus.init(kDefaultAS7331Addr)) return false; setCommunicationBus(&_theI2CBus); @@ -713,6 +676,18 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver return SfeAS7331Driver::begin(); } + /// @brief Sets up Arduino I2C driver using the specified I2C address then calls the super class begin. + /// @return True if successful, false otherwise. + bool begin(const uint8_t &address) + { + if (!_theI2CBus.init(address)) + return false; + + setCommunicationBus(&_theI2CBus); + + return SfeAS7331Driver::begin(address); + } + /// @brief Checks to see if the AS7331 is connected. /// @return True if successful, false otherwise. bool isConnected(void) @@ -720,7 +695,10 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver if (!_theI2CBus.ping()) return false; - return (AS7331_DEFAULT_DEV_ID == SfeAS7331Driver::getDeviceID()); + // Perform soft reset just in case device got left in MEAS mode. + SfeAS7331Driver::reset(); + + return (kDefaultAS7331DeviceID == SfeAS7331Driver::getDeviceID()); } private: From 943bb450f03e31c5aaf9f67f13911f80fb8ba1ef Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:16:21 -0700 Subject: [PATCH 05/39] Added initialization defaults. --- src/SparkFun_AS7331.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h index 3ff0cbf..1dc0aa2 100644 --- a/src/SparkFun_AS7331.h +++ b/src/SparkFun_AS7331.h @@ -275,7 +275,14 @@ const uint8_t kSfeAS7331RegMeasOutConvH = 0x06; // LSB is MSB of OUTCONV, MSB is class SfeAS7331Driver { public: - SfeAS7331Driver(uint8_t address = kDefaultAS7331Addr) : _address{address} + // Default initialization values based on the datasheet. See SfeAS7331Driver::setDefaultSettings for + // an explanation of the values. + SfeAS7331Driver(uint8_t address = kDefaultAS7331Addr) + : _address{address}, _breakTime{25}, _numEdges{1}, _readyPinMode{false}, _dividerEnabled{false}, + _tempConvEnabled{true}, _indexMode{true}, _standbyState{false}, _startState{false}, + _powerDownEnableState{true}, _opMode{DEVICE_MODE_CFG}, _sensorGain{GAIN_2}, _cclk{CCLK_1_024_MHZ}, + _mmode{MEAS_MODE_CMD}, _conversionTime{TIME_64MS}, _dividerRange{DIV_2}, _uva{0.0f}, _uvb{0.0f}, _uvc{0.0f}, + _temperature{0.0f}, _outputConversionTime{0U}, _conversionA{0.0f}, _conversionB{0.0f}, _conversionC{0.0f} { } From 9c199ec77eac4a0a6f05164a3137c8e55f2d5395 Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:20:33 -0700 Subject: [PATCH 06/39] Get refactor working. Update constructor defaults. Add getter for device Address. Add getters for outputs. Edit comments for brevity. Make fsr variables const instead of static constexpr. Consolidate begin functions. Add nullptr checks. Incorporate new readRegisterRegion functions. Rework prepareMeasurement. Add comments. --- src/SparkFun_AS7331.cpp | 494 +++++++++++++++++++++++----------------- src/SparkFun_AS7331.h | 109 +++++---- 2 files changed, 340 insertions(+), 263 deletions(-) diff --git a/src/SparkFun_AS7331.cpp b/src/SparkFun_AS7331.cpp index c2f39f8..0099acf 100644 --- a/src/SparkFun_AS7331.cpp +++ b/src/SparkFun_AS7331.cpp @@ -2,26 +2,32 @@ bool SfeAS7331Driver::begin(const uint8_t &deviceAddress, sfeTkIBus *theBus) { - // Set the internal bus pointer - setCommunicationBus(theBus); + // Nullptr check. + if (!_theBus && !theBus) + return false; + + // Set the internal bus pointer, overriding current bus if it exists. + if (theBus != nullptr) + setCommunicationBus(theBus); // If the address passed in isn't the default, set the new address. - if(kDefaultAS7331Addr != deviceAddress) + if (kDefaultAS7331Addr != deviceAddress) setDeviceAddress(deviceAddress); - // Perform a soft reset so that we make sure the device is addressable. - reset(); - // Get the device setup and ready. return runDefaultSetup(); } uint8_t SfeAS7331Driver::getDeviceID(void) { + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) + return 0; + uint8_t devID; // Read the device ID register, if it errors then return 0. - if (kSTkErrOk != _theBus->readRegisterRegion(kSfeAS7331RegCfgAgen, &devID, 1U)) + if (kSTkErrOk != _theBus->readRegisterByte(kSfeAS7331RegCfgAgen, devID)) return 0; return devID; @@ -34,12 +40,20 @@ void SfeAS7331Driver::setCommunicationBus(sfeTkIBus *theBus) void SfeAS7331Driver::setDeviceAddress(const uint8_t &deviceAddress) { - _address = deviceAddress; + _devAddress = deviceAddress; +} + +uint8_t SfeAS7331Driver::getDeviceAddress(void) +{ + return _devAddress; } -// TODO: Take a look. bool SfeAS7331Driver::runDefaultSetup(bool runSoftReset) { + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) + return false; + // Do we need to run a software reset? if (runSoftReset) reset(); @@ -53,7 +67,10 @@ bool SfeAS7331Driver::runDefaultSetup(bool runSoftReset) // Read all the configuration registers in. uint8_t regs[6]; - if (kSTkErrOk != _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg1, regs, 6U)) + + uint32_t nRead = 0; + sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg1, regs, 6U, nRead); + if (nRead != 6 || result != kSTkErrOk) return false; // Assign the read in bytes to each register's byte union. @@ -65,7 +82,7 @@ bool SfeAS7331Driver::runDefaultSetup(bool runSoftReset) uint8_t edgesreg = regs[4]; sfe_as7331_reg_cfg_optreg_t optreg = {.byte = regs[5]}; - // Here we make sure the local settings match the sensor's settings + // Here we make sure the sensor's settings match the local settings // by changing the sensor's settings. osr.ss = _startState; osr.pd = _powerDownEnableState; @@ -111,47 +128,67 @@ bool SfeAS7331Driver::runDefaultSetup(bool runSoftReset) return true; } -// TODO: FIX bool SfeAS7331Driver::prepareMeasurement(const as7331_meas_mode_t measMode, bool startMeasure) { - if(_opMode != DEVICE_MODE_CFG) - return false; - + // If the device is currently in power down mode. if (_powerDownEnableState) - if (kSTkErrOk != setPowerDownState(false)) + { + if (kSTkErrOk != setPowerDownState(false)) // Set the device to be powered up. return false; + } + + // If the device is currently in standby mode. + if (_standbyState) + { + if (kSTkErrOk != setStandbyState(false)) // Set the device to not be in standby. + return false; + } + // If the device's currently configured measurement mode is different than the desired mode. + // This is just to reduce unnecessary calls. if (_mmode != measMode) { - if (kSTkErrOk != setStandbyState(false)) - return false; - if (kSTkErrOk != setMeasurementMode(measMode)) + if (kSTkErrOk != setMeasurementMode(measMode)) // Set the new mode. return false; } - if (_opMode != DEVICE_MODE_MEAS) - if (kSTkErrOk != setOperationMode(DEVICE_MODE_MEAS)) + // If the device is in start state and we don't want it to be. + // This prevents starting automatically in the next step. + if (_startState && !startMeasure) + { + if (kSTkErrOk != setStartState(false)) // Stop it from running automatically. return false; + } - if (startMeasure) - if (kSTkErrOk != setStartState(true)) + // Now that we've configured ourselves to measure properly, move the device op mode to measure. + if (kSTkErrOk != setOperationMode(DEVICE_MODE_MEAS)) + return false; + + // If the device is supposed to be started automatically and isn't configured that way. + if (!_startState && startMeasure) + { + if (kSTkErrOk != setStartState(true)) // Set the device to being measuring. return false; + } return true; } bool SfeAS7331Driver::reset(void) { + // Standard read-modify-write sequence. sfe_as7331_reg_cfg_osr_t osr; if (kSTkErrOk != getOSR(osr)) return false; + // Set software reset bit. osr.sw_res = 1; if (kSTkErrOk != setOSR(osr)) return false; + // Set internal variables back to defaults. setDefaultSettings(); return true; @@ -159,72 +196,78 @@ bool SfeAS7331Driver::reset(void) sfeTkError_t SfeAS7331Driver::readTemp(void) { - if(_opMode != DEVICE_MODE_MEAS) + // Temperature is only available in Measurement mode. + if (!_theBus || _opMode != DEVICE_MODE_MEAS) return kSTkErrFail; - uint8_t tempRaw[2]; + uint16_t tempRaw; - sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasTemp, tempRaw, 2U); + // Read in the raw value. + sfeTkError_t result = _theBus->readRegisterWord(kSfeAS7331RegMeasTemp, tempRaw); if (kSTkErrOk != result) return result; - _temperature = convertRawTempToTempC((uint16_t)tempRaw[1] << 8 | tempRaw[0]); + // Since temperature is more than 1 byte, need to order it correctly before conversion. + _temperature = convertRawTempToTempC(tempRaw); return kSTkErrOk; } sfeTkError_t SfeAS7331Driver::readUVA(void) { - if(_opMode != DEVICE_MODE_MEAS) - return kSTkErrFail; - return readRawUV(AS7331_UVA); } sfeTkError_t SfeAS7331Driver::readUVB(void) { - if(_opMode != DEVICE_MODE_MEAS) - return kSTkErrFail; - return readRawUV(AS7331_UVB); } sfeTkError_t SfeAS7331Driver::readUVC(void) { - if(_opMode != DEVICE_MODE_MEAS) - return kSTkErrFail; - return readRawUV(AS7331_UVC); } sfeTkError_t SfeAS7331Driver::readAllUV(void) { - if(_opMode != DEVICE_MODE_MEAS) + // UV results are only available in Measurement mode. + if (!_theBus || _opMode != DEVICE_MODE_MEAS) return kSTkErrFail; uint8_t dataRaw[6]; - sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasMres1, dataRaw, 6U); + // Read in the raw data from the results registers. + uint32_t nRead = 0; - if (kSTkErrOk != result) + sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasMres1, dataRaw, 6U, nRead); + + if (nRead != 6 || result != kSTkErrOk) return result; + // If we're in SYND mode, need to calculate conversion based on the conversion time. if (_mmode == MEAS_MODE_SYND) { result = readOutConv(); - if (kSTkErrOk != result) return result; + // See datasheet section 7.4 Equation 4. + // fsrX + // -------------- + // gain * outConv float convFactor = 1.0f / (((float)_outputConversionTime) * ((float)(1 << (11 - _sensorGain)))); + // Since result is more than 1 byte, need to order it correctly before conversion. + // FSR is dependent on the channel being measured, so do that here. _uva = convertRawUVVal(((uint16_t)dataRaw[1] << 8 | dataRaw[0]) * _fsrA, convFactor); _uvb = convertRawUVVal(((uint16_t)dataRaw[3] << 8 | dataRaw[2]) * _fsrB, convFactor); _uvc = convertRawUVVal(((uint16_t)dataRaw[5] << 8 | dataRaw[4]) * _fsrC, convFactor); } else { + // If we're in CONT, CMD, or SYNS mode, use the normally calculated conversion factor. + // Since result is more than 1 byte, need to order it correctly before conversion. _uva = convertRawUVVal((uint16_t)dataRaw[1] << 8 | dataRaw[0], _conversionA); _uvb = convertRawUVVal((uint16_t)dataRaw[3] << 8 | dataRaw[2], _conversionB); _uvc = convertRawUVVal((uint16_t)dataRaw[5] << 8 | dataRaw[4], _conversionC); @@ -235,36 +278,47 @@ sfeTkError_t SfeAS7331Driver::readAllUV(void) sfeTkError_t SfeAS7331Driver::readAll(void) { - if(_opMode != DEVICE_MODE_MEAS) + // Results are only available in Measurement mode. + if (!_theBus || _opMode != DEVICE_MODE_MEAS) return kSTkErrFail; - + uint8_t dataRaw[8]; - sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasTemp, dataRaw, 8U); + uint32_t nRead = 0; + sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasTemp, dataRaw, 8U, nRead); - if (kSTkErrOk != result) + if (nRead != 8 || result != kSTkErrOk) return result; result = readOutConv(); - if (kSTkErrOk != result) return result; + // If we're in SYND mode, need to calculate conversion based on the conversion time. if (_mmode == MEAS_MODE_SYND) { + // See datasheet section 7.4 Equation 4. + // fsrX + // -------------- + // gain * outConv float convFactor = 1.0f / (((float)_outputConversionTime) * ((float)(1 << (11 - _sensorGain)))); + // Since result is more than 1 byte, need to order it correctly before conversion. + // FSR is dependent on the channel being measured, so do that part here. _uva = convertRawUVVal(((uint16_t)dataRaw[3] << 8 | dataRaw[2]) * _fsrA, convFactor); _uvb = convertRawUVVal(((uint16_t)dataRaw[5] << 8 | dataRaw[4]) * _fsrB, convFactor); _uvc = convertRawUVVal(((uint16_t)dataRaw[7] << 8 | dataRaw[6]) * _fsrC, convFactor); } else { + // If we're in CONT, CMD, or SYNS mode, use the normally calculated conversion factor. + // Since result is more than 1 byte, need to order it correctly before conversion. _uva = convertRawUVVal((uint16_t)dataRaw[3] << 8 | dataRaw[2], _conversionA); _uvb = convertRawUVVal((uint16_t)dataRaw[5] << 8 | dataRaw[4], _conversionB); _uvc = convertRawUVVal((uint16_t)dataRaw[7] << 8 | dataRaw[6], _conversionC); } + // Since result is more than 1 byte, need to order it correctly before conversion. _temperature = convertRawTempToTempC((uint16_t)dataRaw[1] << 8 | dataRaw[0]); return kSTkErrOk; @@ -272,24 +326,51 @@ sfeTkError_t SfeAS7331Driver::readAll(void) sfeTkError_t SfeAS7331Driver::readOutConv(void) { - if(_opMode != DEVICE_MODE_MEAS) + // Results are only available in Measurement mode. + if (!_theBus || _opMode != DEVICE_MODE_MEAS) return kSTkErrFail; - + uint8_t tconvRaw[4]; - sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasOutConvL, tconvRaw, 4U); + uint32_t nRead = 0; - if (kSTkErrOk != result) + sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasOutConvL, tconvRaw, 4U, nRead); + + if (nRead != 4 || result != kSTkErrOk) return result; - _outputConversionTime = (uint32_t)(((uint32_t)tconvRaw[3] << 24) | - ((uint32_t)tconvRaw[2] << 16) | - ((uint32_t)tconvRaw[1] << 8) | - tconvRaw[0]); + // Since result is more than 1 byte, need to order it correctly. + _outputConversionTime = (uint32_t)(((uint32_t)tconvRaw[3] << 24) | ((uint32_t)tconvRaw[2] << 16) | + ((uint32_t)tconvRaw[1] << 8) | tconvRaw[0]); return kSTkErrOk; } +float SfeAS7331Driver::getUVA(void) +{ + return _uva; +} + +float SfeAS7331Driver::getUVB(void) +{ + return _uvb; +} + +float SfeAS7331Driver::getUVC(void) +{ + return _uvc; +} + +float SfeAS7331Driver::getTemp(void) +{ + return _temperature; +} + +uint32_t SfeAS7331Driver::getOutConv(void) +{ + return _outputConversionTime; +} + as7331_gain_t SfeAS7331Driver::getGain(void) { return _sensorGain; @@ -297,14 +378,10 @@ as7331_gain_t SfeAS7331Driver::getGain(void) sfeTkError_t SfeAS7331Driver::setGain(const as7331_gain_t &gain) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_creg1_t creg1; - result = getCReg1(creg1); + // Standard read-modify-write sequence. + sfeTkError_t result = getCReg1(creg1); if (kSTkErrOk != result) return result; @@ -314,8 +391,10 @@ sfeTkError_t SfeAS7331Driver::setGain(const as7331_gain_t &gain) if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _sensorGain = gain; + // Gain affects the conversion factors, so calculate new ones with the updated values. calculateConversionFactors(); return kSTkErrOk; @@ -328,14 +407,10 @@ as7331_conv_clk_freq_t SfeAS7331Driver::getCClk(void) sfeTkError_t SfeAS7331Driver::setCClk(const as7331_conv_clk_freq_t &cclk) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_creg3_t creg3; - result = getCReg3(creg3); + // Standard read-modify-write sequence. + sfeTkError_t result = getCReg3(creg3); if (kSTkErrOk != result) return result; @@ -345,8 +420,10 @@ sfeTkError_t SfeAS7331Driver::setCClk(const as7331_conv_clk_freq_t &cclk) if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _cclk = cclk; + // CClk affects the conversion factors, so calculate new ones with the updated values. calculateConversionFactors(); return kSTkErrOk; @@ -359,14 +436,10 @@ as7331_conv_time_t SfeAS7331Driver::getConversionTime(void) sfeTkError_t SfeAS7331Driver::setConversionTime(const as7331_conv_time_t &convTime) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_creg1_t creg1; - result = getCReg1(creg1); + // Standard read-modify-write sequence. + sfeTkError_t result = getCReg1(creg1); if (kSTkErrOk != result) return result; @@ -376,8 +449,10 @@ sfeTkError_t SfeAS7331Driver::setConversionTime(const as7331_conv_time_t &convTi if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _conversionTime = convTime; + // Conversion Time affects the conversion factors, so calculate new ones with the updated values. calculateConversionFactors(); return kSTkErrOk; @@ -390,14 +465,10 @@ bool SfeAS7331Driver::getReadyPinMode(void) sfeTkError_t SfeAS7331Driver::setReadyPinMode(const bool &pinMode) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_creg3_t creg3; - result = getCReg3(creg3); + // Standard read-modify-write sequence. + sfeTkError_t result = getCReg3(creg3); if (kSTkErrOk != result) return result; @@ -407,6 +478,7 @@ sfeTkError_t SfeAS7331Driver::setReadyPinMode(const bool &pinMode) if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _readyPinMode = pinMode; return kSTkErrOk; @@ -417,21 +489,15 @@ bool SfeAS7331Driver::getDigitalDividerEnabled(void) return _dividerEnabled; } -// TODO: FIX sfeTkError_t SfeAS7331Driver::setDigitalDividerEnabled(const bool &isEnabled) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - // TODO: REDO THIS if (_dividerEnabled == isEnabled) return kSTkErrOk; - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_creg2_t creg2; - result = getCReg2(creg2); + // Standard read-modify-write sequence. + sfeTkError_t result = getCReg2(creg2); if (kSTkErrOk != result) return result; @@ -441,10 +507,11 @@ sfeTkError_t SfeAS7331Driver::setDigitalDividerEnabled(const bool &isEnabled) if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _dividerEnabled = isEnabled; - if (isEnabled) - calculateConversionFactors(); + // Digital divider affects the conversion factors, so calculate new ones with the updated values. + calculateConversionFactors(); return kSTkErrOk; } @@ -454,33 +521,28 @@ as7331_divider_val_t SfeAS7331Driver::getDigitalDividerRange(void) return _dividerRange; } -// TODO: FIX -sfeTkError_t SfeAS7331Driver::setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &setEnableDiv) +sfeTkError_t SfeAS7331Driver::setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &enableDiv) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - // TODO: REDO THIS - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_creg2_t creg2; - result = getCReg2(creg2); + // Standard read-modify-write sequence. + sfeTkError_t result = getCReg2(creg2); if (kSTkErrOk != result) return result; creg2.div = divider; + creg2.en_div = enableDiv; result = setCReg2(creg2); if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _dividerRange = divider; + _dividerEnabled = enableDiv; - if (setEnableDiv) - result = setDigitalDividerEnabled(true); - else - calculateConversionFactors(); + // Digital divider affects the conversion factors, so calculate new ones with the updated values. + calculateConversionFactors(); return kSTkErrOk; } @@ -492,14 +554,10 @@ bool SfeAS7331Driver::getSyndTempConversionEnabled(void) sfeTkError_t SfeAS7331Driver::setSyndTempConversionEnabled(const bool &isEnabled) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_creg2_t creg2; - result = getCReg2(creg2); + // Standard read-modify-write sequence. + sfeTkError_t result = getCReg2(creg2); if (kSTkErrOk != result) return result; @@ -509,6 +567,7 @@ sfeTkError_t SfeAS7331Driver::setSyndTempConversionEnabled(const bool &isEnabled if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _tempConvEnabled = isEnabled; return kSTkErrOk; @@ -521,14 +580,10 @@ bool SfeAS7331Driver::getIndexMode(void) sfeTkError_t SfeAS7331Driver::setIndexMode(const bool &indexMode) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_optreg_t optreg; - result = getOptIndex(optreg); + // Standard read-modify-write sequence. + sfeTkError_t result = getOptIndex(optreg); if (kSTkErrOk != result) return result; @@ -538,6 +593,7 @@ sfeTkError_t SfeAS7331Driver::setIndexMode(const bool &indexMode) if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _indexMode = indexMode; return kSTkErrOk; @@ -550,14 +606,10 @@ uint8_t SfeAS7331Driver::getBreakTime(void) sfeTkError_t SfeAS7331Driver::setBreakTime(const uint8_t &breakTime) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - sfeTkError_t result = kSTkErrOk; - uint8_t breakreg; - result = getBreak(breakreg); + // Standard read-modify-write sequence. + sfeTkError_t result = getBreak(breakreg); if (kSTkErrOk != result) return result; @@ -567,6 +619,7 @@ sfeTkError_t SfeAS7331Driver::setBreakTime(const uint8_t &breakTime) if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _breakTime = breakTime; return kSTkErrOk; @@ -579,14 +632,10 @@ uint8_t SfeAS7331Driver::getNumEdges(void) sfeTkError_t SfeAS7331Driver::setNumEdges(const uint8_t &numEdges) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - sfeTkError_t result = kSTkErrOk; - uint8_t edgesreg; - result = getEdges(edgesreg); + // Standard read-modify-write sequence. + sfeTkError_t result = getEdges(edgesreg); if (kSTkErrOk != result) return result; @@ -596,6 +645,7 @@ sfeTkError_t SfeAS7331Driver::setNumEdges(const uint8_t &numEdges) if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _numEdges = numEdges; return kSTkErrOk; @@ -608,11 +658,10 @@ bool SfeAS7331Driver::getPowerDownState(void) sfeTkError_t SfeAS7331Driver::setPowerDownState(const bool &pd) { - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_osr_t osr; - result = getOSR(osr); + // Standard read-modify-write sequence. + sfeTkError_t result = getOSR(osr); if (kSTkErrOk != result) return result; @@ -622,23 +671,23 @@ sfeTkError_t SfeAS7331Driver::setPowerDownState(const bool &pd) if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _powerDownEnableState = pd; return kSTkErrOk; } -as7331_device_op_state_t SfeAS7331Driver::getOperationMode(void) +as7331_dev_op_state_t SfeAS7331Driver::getOperationMode(void) { return _opMode; } -sfeTkError_t SfeAS7331Driver::setOperationMode(const as7331_device_op_state_t &opMode) +sfeTkError_t SfeAS7331Driver::setOperationMode(const as7331_dev_op_state_t &opMode) { - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_osr_t osr; - result = getOSR(osr); + // Standard read-modify-write sequence. + sfeTkError_t result = getOSR(osr); if (kSTkErrOk != result) return result; @@ -648,6 +697,7 @@ sfeTkError_t SfeAS7331Driver::setOperationMode(const as7331_device_op_state_t &o if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _opMode = opMode; return kSTkErrOk; @@ -660,14 +710,10 @@ as7331_meas_mode_t SfeAS7331Driver::getMeasurementMode(void) sfeTkError_t SfeAS7331Driver::setMeasurementMode(const as7331_meas_mode_t &measMode) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_creg3_t creg3; - result = getCReg3(creg3); + // Standard read-modify-write sequence. + sfeTkError_t result = getCReg3(creg3); if (kSTkErrOk != result) return result; @@ -677,6 +723,7 @@ sfeTkError_t SfeAS7331Driver::setMeasurementMode(const as7331_meas_mode_t &measM if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _mmode = measMode; return kSTkErrOk; @@ -689,14 +736,10 @@ bool SfeAS7331Driver::getStandbyState(void) sfeTkError_t SfeAS7331Driver::setStandbyState(const bool &standby) { - if(_opMode != DEVICE_MODE_CFG) - return kSTkErrFail; - - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_creg3_t creg3; - result = getCReg3(creg3); + // Standard read-modify-write sequence. + sfeTkError_t result = getCReg3(creg3); if (kSTkErrOk != result) return result; @@ -706,6 +749,7 @@ sfeTkError_t SfeAS7331Driver::setStandbyState(const bool &standby) if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _standbyState = standby; return kSTkErrOk; @@ -718,11 +762,10 @@ bool SfeAS7331Driver::getStartState(void) sfeTkError_t SfeAS7331Driver::setStartState(const bool &startState) { - sfeTkError_t result = kSTkErrOk; - sfe_as7331_reg_cfg_osr_t osr; - result = getOSR(osr); + // Standard read-modify-write sequence. + sfeTkError_t result = getOSR(osr); if (kSTkErrOk != result) return result; @@ -732,6 +775,7 @@ sfeTkError_t SfeAS7331Driver::setStartState(const bool &startState) if (kSTkErrOk != result) return result; + // If write is successful, save to internal state. _startState = startState; return kSTkErrOk; @@ -739,148 +783,165 @@ sfeTkError_t SfeAS7331Driver::setStartState(const bool &startState) sfeTkError_t SfeAS7331Driver::getStatus(sfe_as7331_reg_meas_osr_status_t &statusReg) { - if(_opMode != DEVICE_MODE_MEAS) + // Status register is only available in Measurement mode. + if (!_theBus || _opMode != DEVICE_MODE_MEAS) return kSTkErrFail; - - sfeTkError_t result = kSTkErrOk; - uint8_t statusRaw[2]; - result = _theBus->readRegisterRegion(kSfeAS7331RegMeasOsrStatus, statusRaw, 2U); + uint16_t statusRaw; + + sfeTkError_t result = _theBus->readRegisterWord(kSfeAS7331RegMeasOsrStatus, statusRaw); if (kSTkErrOk != result) return result; - // Shift MSB to top of 16-bit register, then OR with LSB. - statusReg.word = (uint16_t)statusRaw[1] << 8 | statusRaw[0]; + statusReg.word = statusRaw; return kSTkErrOk; } sfeTkError_t SfeAS7331Driver::getOSR(sfe_as7331_reg_cfg_osr_t &osrReg) { - if(_opMode != DEVICE_MODE_CFG) + // OSR is available in both operation modes. + if (!_theBus) return kSTkErrFail; - - return _theBus->readRegisterRegion(kSfeAS7331RegCfgOsr, &osrReg.byte, 1U); + + return _theBus->readRegisterByte(kSfeAS7331RegCfgOsr, osrReg.byte); } sfeTkError_t SfeAS7331Driver::setOSR(const sfe_as7331_reg_cfg_osr_t &osrReg) { - if(_opMode != DEVICE_MODE_CFG) + // OSR is available in both operation modes. + if (!_theBus) return kSTkErrFail; - - return _theBus->writeRegisterRegion(kSfeAS7331RegCfgOsr, &osrReg.byte, 1U); + + return _theBus->writeRegisterByte(kSfeAS7331RegCfgOsr, osrReg.byte); } sfeTkError_t SfeAS7331Driver::getCReg1(sfe_as7331_reg_cfg_creg1_t &creg1) { - if(_opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - - return _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg1, &creg1.byte, 1U); + + return _theBus->readRegisterByte(kSfeAS7331RegCfgCreg1, creg1.byte); } sfeTkError_t SfeAS7331Driver::setCReg1(const sfe_as7331_reg_cfg_creg1_t &creg1) { - if(_opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - - return _theBus->writeRegisterRegion(kSfeAS7331RegCfgCreg1, &creg1.byte, 1U); + + return _theBus->writeRegisterByte(kSfeAS7331RegCfgCreg1, creg1.byte); } sfeTkError_t SfeAS7331Driver::getCReg2(sfe_as7331_reg_cfg_creg2_t &creg2) { - if(_opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - - return _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg2, &creg2.byte, 1U); + + return _theBus->readRegisterByte(kSfeAS7331RegCfgCreg2, creg2.byte); } sfeTkError_t SfeAS7331Driver::setCReg2(const sfe_as7331_reg_cfg_creg2_t &creg2) { - if(_opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - - return _theBus->writeRegisterRegion(kSfeAS7331RegCfgCreg2, &creg2.byte, 1U); + + return _theBus->writeRegisterByte(kSfeAS7331RegCfgCreg2, creg2.byte); } sfeTkError_t SfeAS7331Driver::getCReg3(sfe_as7331_reg_cfg_creg3_t &creg3) { - if(_opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - - return _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg3, &creg3.byte, 1U); + + return _theBus->readRegisterByte(kSfeAS7331RegCfgCreg3, creg3.byte); } sfeTkError_t SfeAS7331Driver::setCReg3(const sfe_as7331_reg_cfg_creg3_t &creg3) { - if(_opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - - return _theBus->writeRegisterRegion(kSfeAS7331RegCfgCreg3, &creg3.byte, 1U); + + return _theBus->writeRegisterByte(kSfeAS7331RegCfgCreg3, creg3.byte); } sfeTkError_t SfeAS7331Driver::getBreak(uint8_t &breakReg) { - if(_opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - - return _theBus->readRegisterRegion(kSfeAS7331RegCfgBreak, &breakReg, 1U); + + return _theBus->readRegisterByte(kSfeAS7331RegCfgBreak, breakReg); } sfeTkError_t SfeAS7331Driver::setBreak(const uint8_t &breakReg) { - if(_opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - - return _theBus->writeRegisterRegion(kSfeAS7331RegCfgBreak, &breakReg, 1U); + + return _theBus->writeRegisterByte(kSfeAS7331RegCfgBreak, breakReg); } sfeTkError_t SfeAS7331Driver::getEdges(uint8_t &edgesReg) { - if(_opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - return _theBus->readRegisterRegion(kSfeAS7331RegCfgEdges, &edgesReg, 1U); + return _theBus->readRegisterByte(kSfeAS7331RegCfgEdges, edgesReg); } sfeTkError_t SfeAS7331Driver::setEdges(const uint8_t &edgesReg) { - if(_opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - return _theBus->writeRegisterRegion(kSfeAS7331RegCfgEdges, &edgesReg, 1U); + return _theBus->writeRegisterByte(kSfeAS7331RegCfgEdges, edgesReg); } sfeTkError_t SfeAS7331Driver::getOptIndex(sfe_as7331_reg_cfg_optreg_t &optReg) { - if(_opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - return _theBus->readRegisterRegion(kSfeAS7331RegCfgOptReg, &optReg.byte, 1U); + return _theBus->readRegisterByte(kSfeAS7331RegCfgOptReg, optReg.byte); } sfeTkError_t SfeAS7331Driver::setOptIndex(const sfe_as7331_reg_cfg_optreg_t &optReg) { - if(!_theBus || _opMode != DEVICE_MODE_CFG) + // Config registers are only available in Configuration mode. + if (!_theBus || _opMode != DEVICE_MODE_CFG) return kSTkErrFail; - return _theBus->writeRegisterRegion(kSfeAS7331RegCfgOptReg, &optReg.byte, 1U); + return _theBus->writeRegisterByte(kSfeAS7331RegCfgOptReg, optReg.byte); } sfeTkError_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) { - uint8_t uvRawVal[2]; - uint8_t address = 0; + if (!_theBus || _opMode != DEVICE_MODE_MEAS) + return kSTkErrFail; + + uint16_t uvRawVal; + uint8_t regAddress = 0; float *retUV = nullptr; float *conv = nullptr; const float *fsr = nullptr; + // Set variables based on the UV channel requested. switch (uv_type) { - case AS7331_UVA: default: // Since it's an enum, you can't miscall this function, so default to A. - address = kSfeAS7331RegMeasMres1; + case AS7331_UVA: + regAddress = kSfeAS7331RegMeasMres1; fsr = &_fsrA; retUV = &_uva; if (_mmode != MEAS_MODE_SYND) @@ -889,7 +950,7 @@ sfeTkError_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) } break; case AS7331_UVB: - address = kSfeAS7331RegMeasMres2; + regAddress = kSfeAS7331RegMeasMres2; fsr = &_fsrB; retUV = &_uvb; if (_mmode != MEAS_MODE_SYND) @@ -898,7 +959,7 @@ sfeTkError_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) } break; case AS7331_UVC: - address = kSfeAS7331RegMeasMres3; + regAddress = kSfeAS7331RegMeasMres3; fsr = &_fsrC; retUV = &_uvc; if (_mmode != MEAS_MODE_SYND) @@ -908,11 +969,12 @@ sfeTkError_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) break; } - sfeTkError_t result = _theBus->readRegisterRegion(address, uvRawVal, 2U); + sfeTkError_t result = _theBus->readRegisterWord(regAddress, uvRawVal); if (kSTkErrOk != result) return result; + // If we're in SYND mode, need to calculate conversion based on the conversion time. if (_mmode == MEAS_MODE_SYND) { result = readOutConv(); @@ -926,11 +988,13 @@ sfeTkError_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) // gain * outConv float convFactor = (*fsr) / (((float)_outputConversionTime) * ((float)(1 << (11 - _sensorGain)))); - *retUV = convertRawUVVal((uint16_t)uvRawVal[1] << 8 | uvRawVal[0], convFactor); + // Since result is more than 1 byte, need to order it correctly before conversion. + *retUV = convertRawUVVal(uvRawVal, convFactor); } else { - *retUV = convertRawUVVal((uint16_t)uvRawVal[1] << 8 | uvRawVal[0], *conv); + // Since result is more than 1 byte, need to order it correctly before conversion. + *retUV = convertRawUVVal(uvRawVal, *conv); } return kSTkErrOk; @@ -969,19 +1033,19 @@ void SfeAS7331Driver::calculateConversionFactors(void) void SfeAS7331Driver::setDefaultSettings() { - _breakTime = 25; // 25 * 8us = 200us. - _numEdges = 1; // 1 edge. - _readyPinMode = false; // Push-pull. - _dividerEnabled = false; // Predivider disabled. - _tempConvEnabled = true; // Temp conversion in synd mode enabled. - _indexMode = true; // Repeat start enabled. - _standbyState = false; // Not in standby mode. - _startState = false; // Not started. + _breakTime = 25; // 25 * 8us = 200us. + _numEdges = 1; // 1 edge. + _readyPinMode = false; // Push-pull. + _dividerEnabled = false; // Predivider disabled. + _tempConvEnabled = true; // Temp conversion in synd mode enabled. + _indexMode = true; // Repeat start enabled. + _standbyState = false; // Not in standby mode. + _startState = false; // Not started. _powerDownEnableState = true; // Device is powered down. - _opMode = DEVICE_MODE_CFG; // Device is in configuration mode. - _sensorGain = GAIN_2; // Gain of 2x. - _cclk = CCLK_1_024_MHZ; // 1.024 MHz conversion clock - _mmode = MEAS_MODE_CMD; // Command/One Shot Mode. - _conversionTime = TIME_64MS; // 64 ms conversion time. - _dividerRange = DIV_2; // Predivider 2x. + _opMode = DEVICE_MODE_CFG; // Device is in configuration mode. + _sensorGain = GAIN_2; // Gain of 2x. + _cclk = CCLK_1_024_MHZ; // 1.024 MHz conversion clock + _mmode = MEAS_MODE_CMD; // Command/One Shot Mode. + _conversionTime = TIME_64MS; // 64 ms conversion time. + _dividerRange = DIV_2; // Predivider 2x. } \ No newline at end of file diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h index 1dc0aa2..c1b5e87 100644 --- a/src/SparkFun_AS7331.h +++ b/src/SparkFun_AS7331.h @@ -2,9 +2,9 @@ SparkFun Spectral UV Sensor - AS7331 Qwiic 1x1 - https://www.sparkfun.com/products/ + https://www.sparkfun.com/products/23517 Qwiic Mini - https://www.sparkfun.com/products/ + https://www.sparkfun.com/products/23518 Repository https://github.com/sparkfun/SparkFun_AS7331_Arduino_Library @@ -16,7 +16,9 @@ Name: SparkFun_AS7331.h Description: - TODO: Redo this description. + SfeAS7331Driver is a comms-agnostic driver for the AS7331 Spectral UV + sensor that uses the SparkFun Toolkit. The SfeAS7331ArdI2C class defines + the Arduino specific behavior for initializing and interadcting with devices. */ @@ -26,7 +28,6 @@ #include #include -// #include "C:\\Users\\alex.brudner\\Documents\\Arduino\\libraries\\SparkFun_Toolkit\\src\\SparkFun_Toolkit.h" /////////////////////////////////////////////////////////////////////////////// // I2C Addressing @@ -52,7 +53,7 @@ typedef enum DEVICE_MODE_CFG = 0x2, DEVICE_MODE_MEAS = 0x3 // 0b1XX invalid -} as7331_device_op_state_t; +} as7331_dev_op_state_t; // Sensor gain, able to be read as (1 << (11 - gain)) aka 2^(11-gain). typedef enum @@ -278,8 +279,8 @@ class SfeAS7331Driver // Default initialization values based on the datasheet. See SfeAS7331Driver::setDefaultSettings for // an explanation of the values. SfeAS7331Driver(uint8_t address = kDefaultAS7331Addr) - : _address{address}, _breakTime{25}, _numEdges{1}, _readyPinMode{false}, _dividerEnabled{false}, - _tempConvEnabled{true}, _indexMode{true}, _standbyState{false}, _startState{false}, + : _devAddress{address}, _theBus{nullptr}, _breakTime{25}, _numEdges{1}, _readyPinMode{false}, + _dividerEnabled{false}, _tempConvEnabled{true}, _indexMode{true}, _standbyState{false}, _startState{false}, _powerDownEnableState{true}, _opMode{DEVICE_MODE_CFG}, _sensorGain{GAIN_2}, _cclk{CCLK_1_024_MHZ}, _mmode{MEAS_MODE_CMD}, _conversionTime{TIME_64MS}, _dividerRange{DIV_2}, _uva{0.0f}, _uvb{0.0f}, _uvc{0.0f}, _temperature{0.0f}, _outputConversionTime{0U}, _conversionA{0.0f}, _conversionB{0.0f}, _conversionC{0.0f} @@ -288,8 +289,8 @@ class SfeAS7331Driver /// @brief This method is called to initialize the AS7331 device through the /// specified bus. - /// @param theBus Pointer to the bus object. /// @param deviceAddress I2C address for the device. + /// @param theBus Pointer to the bus object. /// @return True if successful, false if it fails. bool begin(const uint8_t &deviceAddress = kDefaultAS7331Addr, sfeTkIBus *theBus = nullptr); @@ -305,6 +306,10 @@ class SfeAS7331Driver /// @param deviceAddress Device address to use. void setDeviceAddress(const uint8_t &deviceAddress); + /// @brief Gets the currently configured device address. + /// @return device address. + uint8_t getDeviceAddress(void); + /// @brief Helper class that sets up the sensor and state in the POR /// configuration. /// @param runSoftReset Flag that runs the soft reset function to reset the @@ -358,6 +363,26 @@ class SfeAS7331Driver /// @return 0 if successful, negative if error, positive for warning. sfeTkError_t readOutConv(void); + /// @brief Returns the last valid UVA reading. + /// @return A float of the UVA reading. + float getUVA(void); + + /// @brief Returns the last valid UVB reading. + /// @return A float of the UVB reading. + float getUVB(void); + + /// @brief Returns the last valid UVC reading. + /// @return A float of the UVC reading. + float getUVC(void); + + /// @brief Returns the last valid Temperature reading. + /// @return A float of the Temperature reading. + float getTemp(void); + + /// @brief Returns the last valid output conversion. + /// @return The output conversion time in number of clock cycles. + uint32_t getOutConv(void); + /// @brief Getter for the currently configured gain. /// @return Sensor's gain expressed as (1 << (11 - gain)). as7331_gain_t getGain(void); @@ -411,7 +436,7 @@ class SfeAS7331Driver /// @param divider Divider value to set. /// @param setEnableDiv Option to turn on the divider if desired. /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &setEnableDiv = false); + sfeTkError_t setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &enableDiv = false); /// @brief Getter for the SYND temperature conversion status. /// @return True if temperature conversion is enabled in SYND mode. @@ -466,12 +491,12 @@ class SfeAS7331Driver /// @brief Getter for the current operational state. /// @return Sensor's operational mode. - as7331_device_op_state_t getOperationMode(void); + as7331_dev_op_state_t getOperationMode(void); /// @brief Set the sensor's operating mode. /// @param opMode Operating mode to set. /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setOperationMode(const as7331_device_op_state_t &opMode); + sfeTkError_t setOperationMode(const as7331_dev_op_state_t &opMode); /// @brief Getter for the current measurement state. /// @return Sensor's measurement state. @@ -624,44 +649,41 @@ class SfeAS7331Driver /// @brief Called to reset all local values back to initial conditions. void setDefaultSettings(void); - sfeTkIBus *_theBus; // Pointer to bus device. - uint8_t _address; // Device's I2C address. + sfeTkIBus *_theBus; // Pointer to bus device. + uint8_t _devAddress; // Device's I2C address. - uint8_t _breakTime; // Local configuration value. Value is in us/8. EX: _breakTime = 20 means 20*8 = 160us. - uint8_t _numEdges; // Local configuration value. Edges seen on SYN pin before ending conversion in SYND mode. + uint8_t _breakTime; // Local config value. Value is in us/8. EX: _breakTime = 20 means 20*8 = 160us. + uint8_t _numEdges; // Local config value. Edges seen on SYN pin before ending conversion in SYND mode. - bool _readyPinMode; // Local configuration value. False is Push/Pull True is Open/Drain. - bool _dividerEnabled; // Local configuration value. False is disabled, True is enabled. - bool _tempConvEnabled; // Local configuration value. False is disabled, True is enabled. - bool _indexMode; // Local configuration value. False is for controllers without repeat start. + bool _readyPinMode; // Local config value. False is Push/Pull True is Open/Drain. + bool _dividerEnabled; // Local config value. False is disabled, True is enabled. + bool _tempConvEnabled; // Local config value. False is disabled, True is enabled. + bool _indexMode; // Local config value. False is for controllers without repeat start. bool _standbyState; // Local state value. False means the device is not in standby mode. bool _startState; // Local state value. False means the device is not allowed to measure. bool _powerDownEnableState; // Local state value. False means the device is NOT POWERED DOWN. This is inverse. - as7331_device_op_state_t _opMode; // Local state value. Configuration or Measure operating mode. - as7331_gain_t _sensorGain; // Local configuration value. Sensor gain stored as (1 << (11 - _sensorGain)). - as7331_conv_clk_freq_t _cclk; // Local configuration value. Sensor's conversion clock frequency stored - // as 1024*(1 << _cclk) kHz. + as7331_dev_op_state_t _opMode; // Local state value. Configuration or Measure operating mode. + as7331_gain_t _sensorGain; // Local config value. Sensor gain stored as (1 << (11 - gain)). + as7331_conv_clk_freq_t _cclk; // Local config value. Sensor's conversion clock stored as 1024*(1 << cclk) kHz. as7331_meas_mode_t _mmode; // Local state value. Details the device's measurement mode as CONT, CMD, SYNS, or SYND. - as7331_conv_time_t _conversionTime; // Local configuration value. Contains the sensor's conversion time in CONT, - // CMD, and SYNS mode stored as (1 << _conversionTime) ms. - as7331_divider_val_t _dividerRange; // Local configuration value. Contains the sensor's internal predivider value as - // (1 << _dividerRange). + as7331_conv_time_t _conversionTime; // Local config value. Contains the conversion time stored as (1 << time) ms. + as7331_divider_val_t _dividerRange; // Local config value. Contains the predivider value stored as (1 << 1 + range). float _uva; // Last valid UVA result in uW/cm2. float _uvb; // Last valid UVB result in uW/cm2. float _uvc; // Last valid UVC result in uW/cm2. float _temperature; // Last valid temperature result in degC. - uint32_t _outputConversionTime; // Last valid output conversion time result in number of clock cycles. + uint32_t _outputConversionTime; // Last valid output conversion time result in of clock cycle count. float _conversionA; // Internal conversion factor for the UVA sensor. float _conversionB; // Internal conversion factor for the UVB sensor. float _conversionC; // Internal conversion factor for the UVC sensor. - static constexpr float _fsrA = 348160.0; // Full Scale Resolution for the UVA sensor. - static constexpr float _fsrB = 387072.0; // Full Scale Resolution for the UVB sensor. - static constexpr float _fsrC = 169984.0; // Full Scale Resolution for the UVC sensor. + const float _fsrA = 348160.0; // Full Scale Resolution for the UVA sensor. + const float _fsrB = 387072.0; // Full Scale Resolution for the UVB sensor. + const float _fsrC = 169984.0; // Full Scale Resolution for the UVC sensor. }; class SfeAS7331ArdI2C : public SfeAS7331Driver @@ -671,26 +693,20 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver { } - /// @brief Sets up Arduino I2C driver using the default I2C address then calls the super class begin. + /// @brief Sets up Arduino I2C driver using the specified I2C address then calls the super class begin. + /// @param address Address of the I2C device. /// @return True if successful, false otherwise. - bool begin(void) + bool begin(const uint8_t &address = kDefaultAS7331Addr) { - if (!_theI2CBus.init(kDefaultAS7331Addr)) + if (_theI2CBus.init(address) != kSTkErrOk) return false; setCommunicationBus(&_theI2CBus); - return SfeAS7331Driver::begin(); - } - - /// @brief Sets up Arduino I2C driver using the specified I2C address then calls the super class begin. - /// @return True if successful, false otherwise. - bool begin(const uint8_t &address) - { - if (!_theI2CBus.init(address)) + if (!reset()) return false; - setCommunicationBus(&_theI2CBus); + isConnected(); return SfeAS7331Driver::begin(address); } @@ -699,13 +715,10 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver /// @return True if successful, false otherwise. bool isConnected(void) { - if (!_theI2CBus.ping()) + if (_theI2CBus.ping() != kSTkErrOk) return false; - // Perform soft reset just in case device got left in MEAS mode. - SfeAS7331Driver::reset(); - - return (kDefaultAS7331DeviceID == SfeAS7331Driver::getDeviceID()); + return (kDefaultAS7331DeviceID == getDeviceID()); } private: From 9cf1ad8a842f5b06257b9b4074613a70cc7134f9 Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:20:50 -0700 Subject: [PATCH 07/39] Rework examples for updated library. --- .../Example01_Basic_OneShot.ino | 89 +++++++++---------- .../Example02_CONT_Mode.ino | 22 ++--- .../Example03_SYNS_Mode.ino | 20 +++-- .../Example04_SYND_Mode.ino | 20 +++-- 4 files changed, 75 insertions(+), 76 deletions(-) diff --git a/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino b/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino index 7eef265..fb97a41 100644 --- a/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino +++ b/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino @@ -22,65 +22,58 @@ https://www.sparkfun.com/products/23518 - Qwiic Mini */ -#include "SparkFun_AS7331.h" +#include +#include +#include -void setup() { - -} - -void loop() { - -} +SfeAS7331ArdI2C myUVSensor; -// SfeAS7331ArdI2C myUVSensor; - -// int8_t result = SFE_BUS_OK; - -// void setup() { -// Serial.begin(115200); -// while(!Serial){delay(100);}; -// Serial.println("UV LED Example."); +void setup() { + Serial.begin(115200); + while(!Serial){delay(100);}; + Serial.println("AS7331 UV A/B/C Command (One-shot) mode Example."); -// // Initialize sensor and run default setup. -// if(myUVSensor.begin() == false) { -// Serial.println("Sensor failed to begin. Please check your wiring!"); -// Serial.println("Spinning..."); -// while(1); -// } + Wire.begin(); -// Serial.println("Sensor began."); + // Initialize sensor and run default setup. + if(myUVSensor.begin() == false) { + Serial.println("Sensor failed to begin. Please check your wiring!"); + Serial.println("Spinning..."); + while(1); + } -// // Set measurement mode and change device operating mode to measure. -// if(myUVSensor.startMeasurement(MEAS_MODE_CMD) == false) { -// Serial.println("Sensor did not get set properly."); -// Serial.println("Spinning..."); -// while(1); -// } + Serial.println("Sensor began."); -// Serial.println("Set mode to command."); + // Set measurement mode and change device operating mode to measure. + if(myUVSensor.prepareMeasurement(MEAS_MODE_CMD) == false) { + Serial.println("Sensor did not get set properly."); + Serial.println("Spinning..."); + while(1); + } -// } + Serial.println("Set mode to command."); +} -// void loop() { +void loop() { -// // Send a start measurement command. -// if(SFE_BUS_OK != myUVSensor.setStartStateMode(START_STATE_ENABLED)) -// Serial.println("Error starting reading!"); + // Send a start measurement command. + if(kSTkErrOk != myUVSensor.setStartState(true)) + Serial.println("Error starting reading!"); -// // Wait for a bit longer than the conversion time. -// delay(2+(1 << myUVSensor.getConfigConversionTime())); + // Wait for a bit longer than the conversion time. + delay(2+(1 << myUVSensor.getConversionTime())); -// // Read UV values. -// if(SFE_BUS_OK != myUVSensor.readAllUV()) -// Serial.println("Error reading UV."); + // Read UV values. + if(kSTkErrOk != myUVSensor.readAllUV()) + Serial.println("Error reading UV."); -// Serial.print("UVA:"); -// Serial.print(myUVSensor.measures.uva); -// Serial.print(" UVB:"); -// Serial.print(myUVSensor.measures.uvb); -// Serial.print(" UVC:"); -// Serial.println(myUVSensor.measures.uvc); + Serial.print("UVA:"); + Serial.print(myUVSensor.getUVA()); + Serial.print(" UVB:"); + Serial.print(myUVSensor.getUVB()); + Serial.print(" UVC:"); + Serial.println(myUVSensor.getUVC()); -// delay(2000); + delay(2000); -// }; +} diff --git a/examples/Example02_CONT_Mode/Example02_CONT_Mode.ino b/examples/Example02_CONT_Mode/Example02_CONT_Mode.ino index a269c8b..d1a86b3 100644 --- a/examples/Example02_CONT_Mode/Example02_CONT_Mode.ino +++ b/examples/Example02_CONT_Mode/Example02_CONT_Mode.ino @@ -23,12 +23,12 @@ https://www.sparkfun.com/products/23518 - Qwiic Mini */ -#include "SparkFun_AS7331.h" +#include +#include +#include SfeAS7331ArdI2C myUVSensor; -int8_t result = SFE_BUS_OK; - const uint8_t interruptPin = 26; volatile bool newDataReady = false; @@ -37,6 +37,8 @@ void setup() { while(!Serial){delay(100);}; Serial.println("AS7331 UV A/B/C Continuous mode example."); + Wire.begin(); + // Configure Interrupt. pinMode(interruptPin, INPUT); attachInterrupt(digitalPinToInterrupt(interruptPin), dataReadyInterrupt, RISING); @@ -51,14 +53,14 @@ void setup() { Serial.println("Sensor began."); // Set break time to 900us (112 * 8us) to account for the time it takes to poll data. - if(SFE_BUS_OK != myUVSensor.setBreakTime(112)) { + if(kSTkErrOk != myUVSensor.setBreakTime(112)) { Serial.println("Sensor did not set break time properly."); Serial.println("Spinning..."); while(1); } // Set measurement mode and change device operating mode to measure. - if(myUVSensor.startMeasurement(MEAS_MODE_CONT) == false) { + if(myUVSensor.prepareMeasurement(MEAS_MODE_CONT) == false) { Serial.println("Sensor did not get set properly."); Serial.println("Spinning..."); while(1); @@ -67,7 +69,7 @@ void setup() { Serial.println("Set mode to continuous. Starting measurement..."); // Begin measurement. - if(SFE_BUS_OK != myUVSensor.setStartStateMode(START_STATE_ENABLED)) + if(kSTkErrOk != myUVSensor.setStartState(true)) Serial.println("Error starting reading!"); } @@ -78,15 +80,15 @@ void loop() { if(newDataReady) { newDataReady = false; - if(SFE_BUS_OK != myUVSensor.readAllUV()) + if(kSTkErrOk != myUVSensor.readAllUV()) Serial.println("Error reading UV."); Serial.print("UVA:"); - Serial.print(myUVSensor.measures.uva); + Serial.print(myUVSensor.getUVA()); Serial.print(" UVB:"); - Serial.print(myUVSensor.measures.uvb); + Serial.print(myUVSensor.getUVB()); Serial.print(" UVC:"); - Serial.println(myUVSensor.measures.uvc); + Serial.println(myUVSensor.getUVC()); } } diff --git a/examples/Example03_SYNS_Mode/Example03_SYNS_Mode.ino b/examples/Example03_SYNS_Mode/Example03_SYNS_Mode.ino index 78e4f02..a1b4914 100644 --- a/examples/Example03_SYNS_Mode/Example03_SYNS_Mode.ino +++ b/examples/Example03_SYNS_Mode/Example03_SYNS_Mode.ino @@ -24,12 +24,12 @@ https://www.sparkfun.com/products/23518 - Qwiic Mini */ -#include "SparkFun_AS7331.h" +#include +#include +#include SfeAS7331ArdI2C myUVSensor; -int8_t result = SFE_BUS_OK; - const uint8_t synPin = 27; const uint8_t interruptPin = 26; @@ -40,6 +40,8 @@ void setup() { while(!Serial){delay(100);}; Serial.println("AS7331 UV A/B/C Synchronous Start mode example."); + Wire.begin(); + // Configure SYN pin. pinMode(synPin, OUTPUT); digitalWrite(synPin, HIGH); // Active low, so start high. @@ -58,7 +60,7 @@ void setup() { Serial.println("Sensor began."); // Set measurement mode and change device operating mode to measure. - if(myUVSensor.startMeasurement(MEAS_MODE_SYNS) == false) { + if(myUVSensor.prepareMeasurement(MEAS_MODE_SYNS) == false) { Serial.println("Sensor did not get set properly."); Serial.println("Spinning..."); while(1); @@ -67,7 +69,7 @@ void setup() { Serial.println("Set mode to synchronous start (SYNS). Starting measurement..."); // Set device to be ready to measure. - if(SFE_BUS_OK != myUVSensor.setStartStateMode(START_STATE_ENABLED)) + if(kSTkErrOk != myUVSensor.setStartState(true)) Serial.println("Error starting reading!"); // Send start toggle. @@ -83,7 +85,7 @@ void loop() { if(newDataReady) { newDataReady = false; - if(SFE_BUS_OK != myUVSensor.readAllUV()) + if(kSTkErrOk != myUVSensor.readAllUV()) Serial.println("Error reading UV."); // Start next measurement @@ -92,11 +94,11 @@ void loop() { digitalWrite(synPin, HIGH); Serial.print("UVA:"); - Serial.print(myUVSensor.measures.uva); + Serial.print(myUVSensor.getUVA()); Serial.print(" UVB:"); - Serial.print(myUVSensor.measures.uvb); + Serial.print(myUVSensor.getUVB()); Serial.print(" UVC:"); - Serial.println(myUVSensor.measures.uvc); + Serial.println(myUVSensor.getUVC()); } } diff --git a/examples/Example04_SYND_Mode/Example04_SYND_Mode.ino b/examples/Example04_SYND_Mode/Example04_SYND_Mode.ino index 08196d4..d0d4858 100644 --- a/examples/Example04_SYND_Mode/Example04_SYND_Mode.ino +++ b/examples/Example04_SYND_Mode/Example04_SYND_Mode.ino @@ -24,12 +24,12 @@ https://www.sparkfun.com/products/23518 - Qwiic Mini */ -#include "SparkFun_AS7331.h" +#include +#include +#include SfeAS7331ArdI2C myUVSensor; -int8_t result = SFE_BUS_OK; - const uint8_t synPin = 27; void setup() { @@ -37,6 +37,8 @@ void setup() { while(!Serial){delay(100);}; Serial.println("AS7331 UV A/B/C Synchronous Start and End (SYND) mode example."); + Wire.begin(); + // Configure SYN pin. pinMode(synPin, OUTPUT); digitalWrite(synPin, HIGH); // Active low, so start high. @@ -51,7 +53,7 @@ void setup() { Serial.println("Sensor began."); // Set measurement mode and change device operating mode to measure. - if(myUVSensor.startMeasurement(MEAS_MODE_SYND) == false) { + if(myUVSensor.prepareMeasurement(MEAS_MODE_SYND) == false) { Serial.println("Sensor did not get set properly."); Serial.println("Spinning..."); while(1); @@ -60,7 +62,7 @@ void setup() { Serial.println("Set mode to synchronous start/end (SYND). Starting measurement..."); // Set device to be ready to measure. - if(SFE_BUS_OK != myUVSensor.setStartStateMode(START_STATE_ENABLED)) + if(kSTkErrOk != myUVSensor.setStartState(true)) Serial.println("Error starting reading!"); // Send start toggle. @@ -79,15 +81,15 @@ void loop() { delay(1); digitalWrite(synPin, HIGH); - if(SFE_BUS_OK != myUVSensor.readAllUV()) + if(kSTkErrOk != myUVSensor.readAllUV()) Serial.println("Error reading UV."); Serial.print("UVA:"); - Serial.print(myUVSensor.measures.uva); + Serial.print(myUVSensor.getUVA()); Serial.print(" UVB:"); - Serial.print(myUVSensor.measures.uvb); + Serial.print(myUVSensor.getUVB()); Serial.print(" UVC:"); - Serial.println(myUVSensor.measures.uvc); + Serial.println(myUVSensor.getUVC()); // Start next measurement. digitalWrite(synPin, LOW); From bca65619919dfc524c663696fdaab65fbdc8a0bb Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:26:14 -0700 Subject: [PATCH 08/39] Rev library to 2.0.0. Add dependency. --- library.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 152f5de..2b5d5bf 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun AS7331 Arduino Library -version=1.0.0 +version=2.0.0 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=An Arduino library to make use of the Qwiic and Qwiic Mini AS7331 Spectral UV Sensor @@ -7,3 +7,4 @@ paragraph= category=Other url=https://github.com/sparkfun/SparkFun_AS7331_Arduino_Library architectures=* +depends=SparkFun Toolkit From 3d0dba156fe41c4758498c33e6f9a5e4a2fd85e8 Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Thu, 14 Dec 2023 08:11:29 -0700 Subject: [PATCH 09/39] Add support for repeat starts in begin function --- src/SparkFun_AS7331.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h index c1b5e87..439d3b9 100644 --- a/src/SparkFun_AS7331.h +++ b/src/SparkFun_AS7331.h @@ -701,6 +701,9 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver if (_theI2CBus.init(address) != kSTkErrOk) return false; + // Device supports repeat starts, enable it. + _theI2CBus.setStop(false); + setCommunicationBus(&_theI2CBus); if (!reset()) From a2a943975382e17d551ad875c4eb4c2bd545fb45 Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Thu, 14 Dec 2023 09:12:24 -0700 Subject: [PATCH 10/39] Get v2.0.0 ready for arduino library. --- keywords.txt | 121 +++++++++++++++++++++++++++++++++++++++++++++ library.properties | 3 +- 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/keywords.txt b/keywords.txt index e69de29..e879f34 100644 --- a/keywords.txt +++ b/keywords.txt @@ -0,0 +1,121 @@ +# Syntax Coloring Map for SparkFun AS7331 Arduino Library + +# Datatypes (KEYWORD1) +as7331_dev_op_state_t KEYWORD1 +as7331_gain_t KEYWORD1 +as7331_conv_clk_freq_t KEYWORD1 +as7331_meas_mode_t KEYWORD1 +as7331_conv_time_t KEYWORD1 +as7331_divider_val_t KEYWORD1 +as7331_uv_type KEYWORD1 + +# Methods and Functions (KEYWORD2) +begin KEYWORD2 +getDeviceID KEYWORD2 +setCommunicationBus KEYWORD2 +setDeviceAddress KEYWORD2 +getDeviceAddress KEYWORD2 +runDefaultSetup KEYWORD2 +prepareMeasurement KEYWORD2 +reset KEYWORD2 +readTemp KEYWORD2 +readUVA KEYWORD2 +readUVB KEYWORD2 +readUVC KEYWORD2 +readAllUV KEYWORD2 +readAll KEYWORD2 +readOutConv KEYWORD2 +getUVA KEYWORD2 +getUVB KEYWORD2 +getUVC KEYWORD2 +getTemp KEYWORD2 +getOutConv KEYWORD2 +getGain KEYWORD2 +setGain KEYWORD2 +getCClk KEYWORD2 +setCClk KEYWORD2 +getConversionTime KEYWORD2 +setConversionTime KEYWORD2 +getReadyPinMode KEYWORD2 +setReadyPinMode KEYWORD2 +getDigitalDividerEnabled KEYWORD2 +setDigitalDividerEnabled KEYWORD2 +getDigitalDividerRange KEYWORD2 +setDigitalDividerRange KEYWORD2 +getSyndTempConversionEnabled KEYWORD2 +setSyndTempConversionEnabled KEYWORD2 +getIndexMode KEYWORD2 +setIndexMode KEYWORD2 +getBreakTime KEYWORD2 +setBreakTime KEYWORD2 +getNumEdges KEYWORD2 +setNumEdges KEYWORD2 +getPowerDownState KEYWORD2 +setPowerDownState KEYWORD2 +getOperationMode KEYWORD2 +setOperationMode KEYWORD2 +getMeasurementMode KEYWORD2 +setMeasurementMode KEYWORD2 +getStandbyState KEYWORD2 +setStandbyState KEYWORD2 +getStartState KEYWORD2 +setStartState KEYWORD2 +getStatus KEYWORD2 +getOSR KEYWORD2 +setOSR KEYWORD2 +getCReg1 KEYWORD2 +setCReg1 KEYWORD2 +getCReg2 KEYWORD2 +setCReg2 KEYWORD2 +getCReg3 KEYWORD2 +setCReg3 KEYWORD2 +getBreak KEYWORD2 +setBreak KEYWORD2 +getEdges KEYWORD2 +setEdges KEYWORD2 +getOptIndex KEYWORD2 +setOptIndex KEYWORD2 +readRawUV KEYWORD2 +convertRawTempToTempC KEYWORD2 +convertRawUVVal KEYWORD2 +calculateConversionFactors KEYWORD2 +setDefaultSettings KEYWORD2 +isConnected KEYWORD2 + +# Instances (KEYWORD2) + + +# Structures (KEYWORD3) +sfe_as7331_reg_cfg_osr_t KEYWORD3 +sfe_as7331_reg_cfg_agen_t KEYWORD3 +sfe_as7331_reg_cfg_creg1_t KEYWORD3 +sfe_as7331_reg_cfg_creg2_t KEYWORD3 +sfe_as7331_reg_cfg_creg3_t KEYWORD3 +sfe_as7331_reg_cfg_optreg_t KEYWORD3 +sfe_as7331_reg_meas_osr_status_t KEYWORD3 + +# Constants (LITERAL1) +kDefaultAS7331Addr LITERAL1 +kSecondaryAS7331Addr LITERAL1 +kTertiaryAS7331Addr LITERAL1 +kQuaternaryAS7331Addr LITERAL1 +kDefaultAS7331DeviceID LITERAL1 +kAS7331DeviceIDTopNibble LITERAL1 +kSfeAS7331RegCfgOsr LITERAL1 +kSfeAS7331RegCfgAgen LITERAL1 +kSfeAS7331RegCfgCreg1 LITERAL1 +kSfeAS7331RegCfgCreg2 LITERAL1 +kSfeAS7331RegCfgCreg3 LITERAL1 +kSfeAS7331RegCfgBreak LITERAL1 +kSfeAS7331RegCfgEdges LITERAL1 +kSfeAS7331RegCfgOptReg LITERAL1 +kSfeAS7331RegMeasOsrStatus LITERAL1 +kSfeAS7331RegMeasTemp LITERAL1 +kSfeAS7331RegMeasMres1 LITERAL1 +kSfeAS7331RegMeasMres2 LITERAL1 +kSfeAS7331RegMeasMres3 LITERAL1 +kSfeAS7331RegMeasOutConvL LITERAL1 +kSfeAS7331RegMeasOutConvH LITERAL1 +_fsrA LITERAL1 +_fsrB LITERAL1 +_fsrC LITERAL1 diff --git a/library.properties b/library.properties index 2b5d5bf..5ac192a 100644 --- a/library.properties +++ b/library.properties @@ -4,7 +4,8 @@ author=SparkFun Electronics maintainer=SparkFun Electronics sentence=An Arduino library to make use of the Qwiic and Qwiic Mini AS7331 Spectral UV Sensor paragraph= -category=Other +category=Sensors url=https://github.com/sparkfun/SparkFun_AS7331_Arduino_Library architectures=* depends=SparkFun Toolkit +includes=SparkFun_AS7331.h From b5f83b3ee71c9aa7fd79b6c0cf99275638fb8786 Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:06:07 -0700 Subject: [PATCH 11/39] Code Review update. Update Examples to say halting rather than spinning. Add comments about why break time is set. Move driver to it's own underlying file. Make getDeviceID change op mode if needed. Add checking to setDeviceAddress. Make runDefaultSetup change op mode if needed. Move bulk of runDefaultSetup into the else statement so we don't do unnecessary things. prepareMeasurement now uses references and consts. Add comment to reset about OSR register availability. Add human readable versions of getters for gain, cclk, convTime. Rename original versions of those to Raw. Remove unnecessary if's in readRawUV. setDigitalDividerRange now defaults enabling the divider to true. Modify Arduino begin to take in a wire port. Remove unnecessary reset call in begin due to logic changes above. --- .../Example01_Basic_OneShot.ino | 6 +- .../Example02_CONT_Mode.ino | 6 +- .../Example03_SYNS_Mode.ino | 4 +- .../Example04_SYND_Mode.ino | 4 +- src/SparkFun_AS7331.h | 672 +---------------- src/{SparkFun_AS7331.cpp => sfeAS7331.cpp} | 202 +++-- src/sfeAS7331.h | 697 ++++++++++++++++++ 7 files changed, 842 insertions(+), 749 deletions(-) rename src/{SparkFun_AS7331.cpp => sfeAS7331.cpp} (86%) create mode 100644 src/sfeAS7331.h diff --git a/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino b/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino index fb97a41..8485b4f 100644 --- a/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino +++ b/examples/Example01_Basic_OneShot/Example01_Basic_OneShot.ino @@ -38,7 +38,7 @@ void setup() { // Initialize sensor and run default setup. if(myUVSensor.begin() == false) { Serial.println("Sensor failed to begin. Please check your wiring!"); - Serial.println("Spinning..."); + Serial.println("Halting..."); while(1); } @@ -47,7 +47,7 @@ void setup() { // Set measurement mode and change device operating mode to measure. if(myUVSensor.prepareMeasurement(MEAS_MODE_CMD) == false) { Serial.println("Sensor did not get set properly."); - Serial.println("Spinning..."); + Serial.println("Halting..."); while(1); } @@ -61,7 +61,7 @@ void loop() { Serial.println("Error starting reading!"); // Wait for a bit longer than the conversion time. - delay(2+(1 << myUVSensor.getConversionTime())); + delay(2+myUVSensor.getConversionTimeMillis()); // Read UV values. if(kSTkErrOk != myUVSensor.readAllUV()) diff --git a/examples/Example02_CONT_Mode/Example02_CONT_Mode.ino b/examples/Example02_CONT_Mode/Example02_CONT_Mode.ino index d1a86b3..44586b3 100644 --- a/examples/Example02_CONT_Mode/Example02_CONT_Mode.ino +++ b/examples/Example02_CONT_Mode/Example02_CONT_Mode.ino @@ -46,16 +46,18 @@ void setup() { // Initialize sensor and run default setup. if(myUVSensor.begin() == false) { Serial.println("Sensor failed to begin. Please check your wiring!"); - Serial.println("Spinning..."); + Serial.println("Halting..."); while(1); } Serial.println("Sensor began."); + // Set the delay between measurements so that the processor can read out the + // results without interfering with the ADC. // Set break time to 900us (112 * 8us) to account for the time it takes to poll data. if(kSTkErrOk != myUVSensor.setBreakTime(112)) { Serial.println("Sensor did not set break time properly."); - Serial.println("Spinning..."); + Serial.println("Halting..."); while(1); } diff --git a/examples/Example03_SYNS_Mode/Example03_SYNS_Mode.ino b/examples/Example03_SYNS_Mode/Example03_SYNS_Mode.ino index a1b4914..f5b690e 100644 --- a/examples/Example03_SYNS_Mode/Example03_SYNS_Mode.ino +++ b/examples/Example03_SYNS_Mode/Example03_SYNS_Mode.ino @@ -53,7 +53,7 @@ void setup() { // Initialize sensor and run default setup. if(myUVSensor.begin() == false) { Serial.println("Sensor failed to begin. Please check your wiring!"); - Serial.println("Spinning..."); + Serial.println("Halting..."); while(1); } @@ -62,7 +62,7 @@ void setup() { // Set measurement mode and change device operating mode to measure. if(myUVSensor.prepareMeasurement(MEAS_MODE_SYNS) == false) { Serial.println("Sensor did not get set properly."); - Serial.println("Spinning..."); + Serial.println("Halting..."); while(1); } diff --git a/examples/Example04_SYND_Mode/Example04_SYND_Mode.ino b/examples/Example04_SYND_Mode/Example04_SYND_Mode.ino index d0d4858..b59b24c 100644 --- a/examples/Example04_SYND_Mode/Example04_SYND_Mode.ino +++ b/examples/Example04_SYND_Mode/Example04_SYND_Mode.ino @@ -46,7 +46,7 @@ void setup() { // Initialize sensor and run default setup. if(myUVSensor.begin() == false) { Serial.println("Sensor failed to begin. Please check your wiring!"); - Serial.println("Spinning..."); + Serial.println("Halting..."); while(1); } @@ -55,7 +55,7 @@ void setup() { // Set measurement mode and change device operating mode to measure. if(myUVSensor.prepareMeasurement(MEAS_MODE_SYND) == false) { Serial.println("Sensor did not get set properly."); - Serial.println("Spinning..."); + Serial.println("Halting..."); while(1); } diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h index 439d3b9..580bbe7 100644 --- a/src/SparkFun_AS7331.h +++ b/src/SparkFun_AS7331.h @@ -16,676 +16,17 @@ Name: SparkFun_AS7331.h Description: - SfeAS7331Driver is a comms-agnostic driver for the AS7331 Spectral UV - sensor that uses the SparkFun Toolkit. The SfeAS7331ArdI2C class defines - the Arduino specific behavior for initializing and interadcting with devices. + The SfeAS7331ArdI2C class defines the Arduino specific behavior for + initializing and interadcting with devices. */ #pragma once -#include - +#include "sfeAS7331.h" #include #include -/////////////////////////////////////////////////////////////////////////////// -// I2C Addressing -/////////////////////////////////////////////////////////////////////////////// -// 7-bit address defined as [1, 1, 1, 0, 1, A1, A0] where A1/A0 are the -// physical address pins tied high or low -const uint8_t kDefaultAS7331Addr = 0x74; // A1 = 0, A0 = 0 -const uint8_t kSecondaryAS7331Addr = 0x75; // A1 = 0, A1 = 1 -const uint8_t kTertiaryAS7331Addr = 0x76; // A1 = 1, A0 = 0 -const uint8_t kQuaternaryAS7331Addr = 0x77; // A1 = 1, A0 = 1 - -const uint8_t kDefaultAS7331DeviceID = 0x21; // When polling the AGEN register, this should be returned on boot. -const uint8_t kAS7331DeviceIDTopNibble = 0x2; // Top nibble of the AGEN byte, always the same. - -/////////////////////////////////////////////////////////////////////////////// -// Enum Definitions -/////////////////////////////////////////////////////////////////////////////// - -// Device Operating Mode -typedef enum -{ - // 0b00X invalid - DEVICE_MODE_CFG = 0x2, - DEVICE_MODE_MEAS = 0x3 - // 0b1XX invalid -} as7331_dev_op_state_t; - -// Sensor gain, able to be read as (1 << (11 - gain)) aka 2^(11-gain). -typedef enum -{ - GAIN_2048 = 0x0000, - GAIN_1024, - GAIN_512, - GAIN_256, - GAIN_128, - GAIN_64, - GAIN_32, - GAIN_16, - GAIN_8, - GAIN_4, - GAIN_2, - GAIN_1 -} as7331_gain_t; - -// Conversion Clock Frequency available settings, read as (1024 * (1 << cclk)) -// aka 1024*2^cclk. -typedef enum -{ - CCLK_1_024_MHZ = 0x00, - CCLK_2_048_MHZ, - CCLK_4_096_MHZ, - CCLK_8_192_MHZ -} as7331_conv_clk_freq_t; - -// Measurement Mode types -typedef enum -{ - MEAS_MODE_CONT = 0x0, // Continuous mode - MEAS_MODE_CMD, // Command/OneShot mode - MEAS_MODE_SYNS, // SYNchronous Start mode - MEAS_MODE_SYND // SYNchronous start and enD mode -} as7331_meas_mode_t; - -// Time conversion values in CONT, CMD, SYNS modes. Read as (1 << tconv) aka -// 2^tconv. -typedef enum -{ - TIME_1MS = 0x0, - TIME_2MS, - TIME_4MS, - TIME_8MS, - TIME_16MS, - TIME_32MS, - TIME_64MS, - TIME_128MS, - TIME_256MS, - TIME_512MS, - TIME_1024MS, - TIME_2048MS, - TIME_4096MS, - TIME_8192MS, - TIME_16384MS -} as7331_conv_time_t; - -// Predivider values. Read as (1 << (1+div)) aka 2^(1+div). -typedef enum -{ - DIV_2 = 0x0, - DIV_4, - DIV_8, - DIV_16, - DIV_32, - DIV_64, - DIV_128, - DIV_256 -} as7331_divider_val_t; - -// Type of UV to pick. -typedef enum -{ - AS7331_UVA, - AS7331_UVB, - AS7331_UVC -} as7331_uv_type; - -/////////////////////////////////////////////////////////////////////////////// -// Configuration Register Descriptions -// -// Can only be accessed in the Configuration operation state. All 8 bits long. -// -// The following registers map to the underlying registers of the device. -/////////////////////////////////////////////////////////////////////////////// - -const uint8_t kSfeAS7331RegCfgOsr = 0x00; // Register address - -// A union is used here so that individual values from the register can be -// accessed or the whole register can be accessed. -typedef union { - struct - { - uint8_t dos : 3; // Device Operating State - OSR[2:0] - uint8_t sw_res : 1; // Software Reset - OSR[3] - uint8_t reserved : 2; // Reserved, don't write. - OSR[4:5] - uint8_t pd : 1; // Power Down Enabled - OSR[6] - uint8_t ss : 1; // Start State - OSR[7] - }; - uint8_t byte; -} sfe_as7331_reg_cfg_osr_t; - -const uint8_t kSfeAS7331RegCfgAgen = 0x02; // Register address - -// A union is used here so that individual values from the register can be -// accessed or the whole register can be accessed. -typedef union { - struct - { - uint8_t mut : 4; // Increments when changes are made to the control - // registers. Defaults to 0b0001 aka 0x1. - uint8_t devid : 4; // Always equals 0b0010 aka 0x2. - }; - uint8_t byte; -} sfe_as7331_reg_cfg_agen_t; - -const uint8_t kSfeAS7331RegCfgCreg1 = 0x06; // Register address - -// A union is used here so that individual values from the register can be -// accessed or the whole register can be accessed. -typedef union { - struct - { - uint8_t time : 4; // Conversion time - CREG1[3:0] - uint8_t gain : 4; // Sensor gain - CREG1[7:4] - }; - uint8_t byte; -} sfe_as7331_reg_cfg_creg1_t; - -const uint8_t kSfeAS7331RegCfgCreg2 = 0x07; // Register address - -// A union is used here so that individual values from the register can be -// accessed or the whole register can be accessed. -typedef union { - struct - { - uint8_t div : 3; // Digital Divider value - CREG2[2:0] - uint8_t en_div : 1; // Whether the digital divider is enabled - CREG2[3] - uint8_t reserved : 2; // Reserved, don't write. - CREG2[5:4] - uint8_t en_tm : 1; // Whether temperature is calculated in SYND mode. - CREG2[6] - uint8_t reserved1 : 1; // Reserved, don't write. - CREG2[7] - }; - uint8_t byte; -} sfe_as7331_reg_cfg_creg2_t; - -const uint8_t kSfeAS7331RegCfgCreg3 = 0x08; // Register Address - -// A union is used here so that individual values from the register can be -// accessed or the whole register can be accessed. -typedef union { - struct - { - uint8_t cclk : 2; // Conversion clock selection. - CREG3[1:0] - uint8_t reserved : 1; // Reserved, don't write. - CREG3[2] - uint8_t rdyod : 1; // Output mode of the ready pin. - CREG3[3] - uint8_t sb : 1; // Standby mode bit. - CREG3[4] - uint8_t reserved1 : 1; // Reserved, don't write. - CREG3[5] - uint8_t mmode : 2; // Measurement mode selection. - CREG3[7:6] - }; - uint8_t byte; -} sfe_as7331_reg_cfg_creg3_t; - -const uint8_t kSfeAS7331RegCfgBreak = 0x09; // Register address, register is a single uint8_t. - -const uint8_t kSfeAS7331RegCfgEdges = 0x0A; // Register address, register is a single uint8_t. - -const uint8_t kSfeAS7331RegCfgOptReg = 0x0B; // Register address - -// A union is used here so that individual values from the register can be -// accessed or the whole register can be accessed. -typedef union { - struct - { - uint8_t init_idx : 1; // I2C repeat start mode flag. - OPTREG[0] - uint8_t reserved : 7; // Reserved, don't write. - OPTREG[7:1] - }; - uint8_t byte; -} sfe_as7331_reg_cfg_optreg_t; - -/////////////////////////////////////////////////////////////////////////////// -// Measurement Register Descriptions -// -// Can only be accessed in the Measure operation state. All 16 bits long. They -// are all read-only except the bottom byte of the OSR/STATUS register. -// -// The following registers map to the underlying registers of the device. -/////////////////////////////////////////////////////////////////////////////// - -const uint8_t kSfeAS7331RegMeasOsrStatus = 0x00; // Register address - -// A union is used here so that individual values from the register can be -// accessed or the whole register can be accessed. -typedef union { - struct - { - sfe_as7331_reg_cfg_osr_t osr; // See OSR configuration register above. - OSRSTAT[7:0] - uint8_t powerstate : 1; // Power down state. - OSRSTAT[8] - uint8_t standbystate : 1; // Standby mode state. - OSRSTAT[9] - uint8_t notready : 1; // Inverted value of the ready pin. - OSRSTAT[10] - uint8_t ndata : 1; // Indicates new data available. - OSRSTAT[11] - uint8_t ldata : 1; // Indicates data overwrite prior to retrieval. - OSRSTAT[12] - uint8_t adcof : 1; // OVF of at least one ADC channel. - OSRSTAT[13] - uint8_t mresof : 1; // OVF of at least one of the MRES registers. - OSRSTAT[14] - uint8_t outconvof : 1; // OVF of the internal 24-bit time reference. - OSRSTAT[15] - }; - uint16_t word; -} sfe_as7331_reg_meas_osr_status_t; - -// Output result registers. 16-bit values unless noted otherwise. -const uint8_t kSfeAS7331RegMeasTemp = 0x01; // 12-bit temperature, MS 4-bits 0. -const uint8_t kSfeAS7331RegMeasMres1 = 0x02; -const uint8_t kSfeAS7331RegMeasMres2 = 0x03; -const uint8_t kSfeAS7331RegMeasMres3 = 0x04; -const uint8_t kSfeAS7331RegMeasOutConvL = 0x05; // First 16-bits of 24-bit OUTCONV. -const uint8_t kSfeAS7331RegMeasOutConvH = 0x06; // LSB is MSB of OUTCONV, MSB is 0. - -/////////////////////////////////////////////////////////////////////////////// - -class SfeAS7331Driver -{ - public: - // Default initialization values based on the datasheet. See SfeAS7331Driver::setDefaultSettings for - // an explanation of the values. - SfeAS7331Driver(uint8_t address = kDefaultAS7331Addr) - : _devAddress{address}, _theBus{nullptr}, _breakTime{25}, _numEdges{1}, _readyPinMode{false}, - _dividerEnabled{false}, _tempConvEnabled{true}, _indexMode{true}, _standbyState{false}, _startState{false}, - _powerDownEnableState{true}, _opMode{DEVICE_MODE_CFG}, _sensorGain{GAIN_2}, _cclk{CCLK_1_024_MHZ}, - _mmode{MEAS_MODE_CMD}, _conversionTime{TIME_64MS}, _dividerRange{DIV_2}, _uva{0.0f}, _uvb{0.0f}, _uvc{0.0f}, - _temperature{0.0f}, _outputConversionTime{0U}, _conversionA{0.0f}, _conversionB{0.0f}, _conversionC{0.0f} - { - } - - /// @brief This method is called to initialize the AS7331 device through the - /// specified bus. - /// @param deviceAddress I2C address for the device. - /// @param theBus Pointer to the bus object. - /// @return True if successful, false if it fails. - bool begin(const uint8_t &deviceAddress = kDefaultAS7331Addr, sfeTkIBus *theBus = nullptr); - - /// @brief Requests the device ID from the sensor. - /// @return The device ID of the sensor. - uint8_t getDeviceID(void); - - /// @brief Sets the communication bus to the specified bus. - /// @param theBus Bus to set as the communication devie. - void setCommunicationBus(sfeTkIBus *theBus); - - /// @brief Sets the address that the bus uses to communicate with the sensor. - /// @param deviceAddress Device address to use. - void setDeviceAddress(const uint8_t &deviceAddress); - - /// @brief Gets the currently configured device address. - /// @return device address. - uint8_t getDeviceAddress(void); - - /// @brief Helper class that sets up the sensor and state in the POR - /// configuration. - /// @param runSoftReset Flag that runs the soft reset function to reset the - /// device. - /// @return True if successful, false otherwise. - bool runDefaultSetup(bool runSoftReset = false); - - /// @brief Puts the sensor in the specified measurement mode. - /// @param measMode Measurement mode to enter. - /// @param startMeasure Flag to start measuring immediately if set. - /// @return True if successful, false otherwise. - bool prepareMeasurement(const as7331_meas_mode_t measMode = MEAS_MODE_CONT, bool startMeasure = false); - - /// @brief Performs a soft reset of the sensor. - /// @return True if successful, false otherwise. - bool reset(void); - - /// @brief Reads the sensor's temperature, converts it to a usable form, and - /// saves it to the internal temperature variable. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t readTemp(void); - - /// @brief Reads the sensor's UVA register, converts it to a usable form, and - /// saves it to the internal UVA variable. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t readUVA(void); - - /// @brief Reads the sensor's UVB register, converts it to a usable form, and - /// saves it to the internal UVB variable. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t readUVB(void); - - /// @brief Reads the sensor's UVC register, converts it to a usable form, and - /// saves it to the internal UVC variable. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t readUVC(void); - - /// @brief Read's all three UV registers, converts them to a usable form, then - /// saves them to their respective internal variable. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t readAllUV(void); - - /// @brief Read the sensor's temperature, UV, and external time conversion - /// clock counts, converts them, and then saves them to their respective - /// internal variable. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t readAll(void); - - /// @brief Read the conversion clock counts register and saves it to the - /// internal output conversion time variable. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t readOutConv(void); - - /// @brief Returns the last valid UVA reading. - /// @return A float of the UVA reading. - float getUVA(void); - - /// @brief Returns the last valid UVB reading. - /// @return A float of the UVB reading. - float getUVB(void); - - /// @brief Returns the last valid UVC reading. - /// @return A float of the UVC reading. - float getUVC(void); - - /// @brief Returns the last valid Temperature reading. - /// @return A float of the Temperature reading. - float getTemp(void); - - /// @brief Returns the last valid output conversion. - /// @return The output conversion time in number of clock cycles. - uint32_t getOutConv(void); - - /// @brief Getter for the currently configured gain. - /// @return Sensor's gain expressed as (1 << (11 - gain)). - as7331_gain_t getGain(void); - - /// @brief Sets the UV sensor's gain. - /// @param gain The gain to set the sensor to. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setGain(const as7331_gain_t &gain); - - /// @brief Getter for the currently configured conversion clock. - /// @return Sensor's conversion clock expressed as 1024*(1 << cclk). - as7331_conv_clk_freq_t getCClk(void); - - /// @brief Set the sensor's internal clock speed. - /// @param cclk Clock speed to set on the sensor. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setCClk(const as7331_conv_clk_freq_t &cclk); - - /// @brief Getter for the currently configured conversion time. - /// @return Sensor's conversion time expressed as (1 << time). - as7331_conv_time_t getConversionTime(void); - - /// @brief Sets the conversion time that the sensor will run to. - /// @param convTime Conversion time to set the sensor to. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setConversionTime(const as7331_conv_time_t &convTime); - - /// @brief Getter for the currently configured pin mode. - /// @return False if push-pull, true if open-drain. - bool getReadyPinMode(void); - - /// @brief Sets the ready pin type to push-pull or open-drain. - /// @param pinMode Mode to set the ready pin to. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setReadyPinMode(const bool &pinMode); - - /// @brief Getter for the currently configured divider status. - /// @return True if Internal predivider is enabled, false otherwise. - bool getDigitalDividerEnabled(void); - - /// @brief Enables or disables the internal UV result divider. - /// @param isEnabled Enable or disable the divder. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setDigitalDividerEnabled(const bool &isEnabled); - - /// @brief Getter for the currently configured divider range. - /// @return Sensor's internal UV predivider range. - as7331_divider_val_t getDigitalDividerRange(void); - - /// @brief Sets the value of the internal UV result divider. - /// @param divider Divider value to set. - /// @param setEnableDiv Option to turn on the divider if desired. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &enableDiv = false); - - /// @brief Getter for the SYND temperature conversion status. - /// @return True if temperature conversion is enabled in SYND mode. - bool getSyndTempConversionEnabled(void); - - /// @brief Enables or disables temperature conversion when in SYND mode. - /// @param isEnabled Enable or disable the feature. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setSyndTempConversionEnabled(const bool &isEnabled); - - /// @brief Getter for the currently configured I2C compatibility mode. - /// @return True if the device will respond to repeat starts, false otherwise. - bool getIndexMode(void); - - /// @brief Set the index mode for compatibility with I2C controllers that - /// don't support repeated start. - /// @param indexMode Simple or standard I2C addressing mode. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setIndexMode(const bool &indexMode); - - /// @brief Getter for the currently configured minimum break time in CONT, - /// CMD, SYNS modes. - /// @return Sensor's breaktime in 8us steps. - uint8_t getBreakTime(void); - - /// @brief Set the minimum time between measurements in CONT, SYNS, SYND modes. - /// @param breakTime Time between measurements, 8us step time, max 2048us. A 0 - /// value is a minimum of 3 cclk cycles. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setBreakTime(const uint8_t &breakTime); - - /// @brief Getter for the currently configured minimum number of edges to end - /// conversion when in SYND mode. - /// @return Sensor's minimum number of edges, minimum 1 edge. - uint8_t getNumEdges(void); - - /// @brief Set the minimum number of falling edges required at the SYN input - /// until the conversion is terminated. Only operational in SYND mode. - /// @param numEdges Number of edges prior to terminating conversion in SYND - /// mode. 0 is not allowed, 1 is the minimum. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setNumEdges(const uint8_t &numEdges); - - /// @brief Getter for the current power state. - /// @return Sensor's power state. - bool getPowerDownState(void); - - /// @brief Sets the power state of the sensor. - /// @param pd Power state to set. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setPowerDownState(const bool &pd); - - /// @brief Getter for the current operational state. - /// @return Sensor's operational mode. - as7331_dev_op_state_t getOperationMode(void); - - /// @brief Set the sensor's operating mode. - /// @param opMode Operating mode to set. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setOperationMode(const as7331_dev_op_state_t &opMode); - - /// @brief Getter for the current measurement state. - /// @return Sensor's measurement state. - as7331_meas_mode_t getMeasurementMode(void); - - /// @brief Sets the sensor's measurement mode. - /// @param measMode Measurement mode to set. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setMeasurementMode(const as7331_meas_mode_t &measMode); - - /// @brief Getter for the current standby state. - /// @return Sensor's standby state. - bool getStandbyState(void); - - /// @brief Sets the sensor's standby mode. - /// @param standby State to set. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setStandbyState(const bool &standby); - - /// @brief Getter for the current start state. - /// @return Sensor's start state. - bool getStartState(void); - - /// @brief Sets the sensor's start state. This begins measurement. - /// @param startState Start state to set. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setStartState(const bool &startState); - - /// @brief Gets the sensor's status when in measurement operation mode. - /// @param statusReg Pointer to a register struct to store the sensor's - /// current status. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t getStatus(sfe_as7331_reg_meas_osr_status_t &statusReg); - - /// @brief Gets the operational state register when in configuration operation - /// mode. - /// @param osrReg Pointer to a register struct to store the sensor's current - /// OSR register. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t getOSR(sfe_as7331_reg_cfg_osr_t &osrReg); - - /// @brief Sets the operational state register when in configuration operation - /// mode. - /// @param osrReg Pointer to a register struct that has the new register - /// configuration. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setOSR(const sfe_as7331_reg_cfg_osr_t &osrReg); - - /// @brief Gets the configuration register #1 when in configuration operation - /// mode. - /// @param creg1 Pointer to a register struct to store the sensor's current - /// creg1 register. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t getCReg1(sfe_as7331_reg_cfg_creg1_t &creg1); - - /// @brief Sets the configuration register #1 when in configuration operation - /// mode. - /// @param creg1 Pointer to a register struct that has the new register - /// configuration. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setCReg1(const sfe_as7331_reg_cfg_creg1_t &creg1); - - /// @brief Gets the configuration register #2 when in configuration operation - /// mode. - /// @param creg2 Pointer to a register struct to store the sensor's current - /// creg2 register. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t getCReg2(sfe_as7331_reg_cfg_creg2_t &creg2); - - /// @brief Sets the configuration register #2 when in configuration operation - /// mode. - /// @param creg2 Pointer to a register struct that has the new register - /// configuration. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setCReg2(const sfe_as7331_reg_cfg_creg2_t &creg2); - - /// @brief Gets the configuration register #3 when in configuration operation - /// mode. - /// @param creg3 Pointer to a register struct to store the sensor's current - /// creg3 register. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t getCReg3(sfe_as7331_reg_cfg_creg3_t &creg3); - - /// @brief Sets the configuration register #3 when in configuration operation - /// mode. - /// @param creg3 Pointer to a register struct that has the new register - /// configuration. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setCReg3(const sfe_as7331_reg_cfg_creg3_t &creg3); - - /// @brief Gets the break register when in configuration operation mode. - /// @param breakReg Pointer to a register struct to store the sensor's current - /// break register. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t getBreak(uint8_t &breakReg); - - /// @brief Sets the break register when in configuration operation mode. - /// @param breakReg Pointer to a register struct that has the new register - /// configuration. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setBreak(const uint8_t &breakReg); - - /// @brief Gets the edges register when in configuration operation mode. - /// @param edgesReg Pointer to a register struct to store the sensor's current - /// edges register. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t getEdges(uint8_t &edgesReg); - - /// @brief Sets the edges register when in configuration operation mode. - /// @param edgesReg Pointer to a register struct that has the new register - /// configuration. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setEdges(const uint8_t &edgesReg); - - /// @brief Gets the option register when in configuration operation mode. - /// @param optReg Pointer to a register struct to store the sensor's current - /// option register. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t getOptIndex(sfe_as7331_reg_cfg_optreg_t &optReg); - - /// @brief Sets the option register when in configuration operation mode. - /// @param optReg Pointer to a register struct that has the new register - /// configuration. - /// @return 0 if successful, negative if error, positive for warning. - sfeTkError_t setOptIndex(const sfe_as7331_reg_cfg_optreg_t &optReg); - - private: - /// @brief Reads a UV result register and saves it to the respective internal - /// variable. - /// @param uv_type The type of UV you want to read. - /// @return 0 if successful, negative if error, positive if warning - sfeTkError_t readRawUV(const as7331_uv_type &uv_type); - - /// @brief Converts the raw temperature value to a human readable form. - /// @param inputVal Raw temperature value to convert. - /// @return The converted device temperature in degree C. - float convertRawTempToTempC(const uint16_t &inputVal); - - /// @brief Converts a raw result value and performs a conversion to a human - /// readable format. - /// @param rawVal The raw input value. - /// @param convFactor Conversion factor to multiply the raw input value. - /// @return The calculated conversion result - float convertRawUVVal(const uint16_t &rawVal, const float &convFactor); - - /// @brief Called when changing values that affect the conversion, calculates - /// a new conversion factor to reduce the conversion overhead. - void calculateConversionFactors(void); - - /// @brief Called to reset all local values back to initial conditions. - void setDefaultSettings(void); - - sfeTkIBus *_theBus; // Pointer to bus device. - uint8_t _devAddress; // Device's I2C address. - - uint8_t _breakTime; // Local config value. Value is in us/8. EX: _breakTime = 20 means 20*8 = 160us. - uint8_t _numEdges; // Local config value. Edges seen on SYN pin before ending conversion in SYND mode. - - bool _readyPinMode; // Local config value. False is Push/Pull True is Open/Drain. - bool _dividerEnabled; // Local config value. False is disabled, True is enabled. - bool _tempConvEnabled; // Local config value. False is disabled, True is enabled. - bool _indexMode; // Local config value. False is for controllers without repeat start. - bool _standbyState; // Local state value. False means the device is not in standby mode. - bool _startState; // Local state value. False means the device is not allowed to measure. - bool _powerDownEnableState; // Local state value. False means the device is NOT POWERED DOWN. This is inverse. - - as7331_dev_op_state_t _opMode; // Local state value. Configuration or Measure operating mode. - as7331_gain_t _sensorGain; // Local config value. Sensor gain stored as (1 << (11 - gain)). - as7331_conv_clk_freq_t _cclk; // Local config value. Sensor's conversion clock stored as 1024*(1 << cclk) kHz. - as7331_meas_mode_t _mmode; // Local state value. Details the device's measurement mode as CONT, CMD, SYNS, or SYND. - as7331_conv_time_t _conversionTime; // Local config value. Contains the conversion time stored as (1 << time) ms. - as7331_divider_val_t _dividerRange; // Local config value. Contains the predivider value stored as (1 << 1 + range). - - float _uva; // Last valid UVA result in uW/cm2. - float _uvb; // Last valid UVB result in uW/cm2. - float _uvc; // Last valid UVC result in uW/cm2. - float _temperature; // Last valid temperature result in degC. - - uint32_t _outputConversionTime; // Last valid output conversion time result in of clock cycle count. - - float _conversionA; // Internal conversion factor for the UVA sensor. - float _conversionB; // Internal conversion factor for the UVB sensor. - float _conversionC; // Internal conversion factor for the UVC sensor. - - const float _fsrA = 348160.0; // Full Scale Resolution for the UVA sensor. - const float _fsrB = 387072.0; // Full Scale Resolution for the UVB sensor. - const float _fsrC = 169984.0; // Full Scale Resolution for the UVC sensor. -}; - class SfeAS7331ArdI2C : public SfeAS7331Driver { public: @@ -696,9 +37,9 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver /// @brief Sets up Arduino I2C driver using the specified I2C address then calls the super class begin. /// @param address Address of the I2C device. /// @return True if successful, false otherwise. - bool begin(const uint8_t &address = kDefaultAS7331Addr) + bool begin(const uint8_t &address = kDefaultAS7331Addr, TwoWire &wirePort = Wire) { - if (_theI2CBus.init(address) != kSTkErrOk) + if (_theI2CBus.init(wirePort, address) != kSTkErrOk) return false; // Device supports repeat starts, enable it. @@ -706,9 +47,6 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver setCommunicationBus(&_theI2CBus); - if (!reset()) - return false; - isConnected(); return SfeAS7331Driver::begin(address); diff --git a/src/SparkFun_AS7331.cpp b/src/sfeAS7331.cpp similarity index 86% rename from src/SparkFun_AS7331.cpp rename to src/sfeAS7331.cpp index 0099acf..bc25370 100644 --- a/src/SparkFun_AS7331.cpp +++ b/src/sfeAS7331.cpp @@ -1,4 +1,4 @@ -#include "SparkFun_AS7331.h" +#include "sfeAS7331.h" bool SfeAS7331Driver::begin(const uint8_t &deviceAddress, sfeTkIBus *theBus) { @@ -20,9 +20,27 @@ bool SfeAS7331Driver::begin(const uint8_t &deviceAddress, sfeTkIBus *theBus) uint8_t SfeAS7331Driver::getDeviceID(void) { - // Config registers are only available in Configuration mode. - if (!_theBus || _opMode != DEVICE_MODE_CFG) + // Nullptr check. + if (!_theBus) + return 0; + + // Check sensor's operating mode. + sfe_as7331_reg_cfg_osr_t osr; + if (kSTkErrOk != getOSR(osr)) return 0; + + bool needsToBeChangedBack = false; + + // Change if necessary. + if (osr.dos != DEVICE_MODE_CFG) + { + needsToBeChangedBack = true; + + osr.dos = DEVICE_MODE_CFG; + + if(kSTkErrOk != setOSR(osr)) + return 0; + } uint8_t devID; @@ -30,6 +48,13 @@ uint8_t SfeAS7331Driver::getDeviceID(void) if (kSTkErrOk != _theBus->readRegisterByte(kSfeAS7331RegCfgAgen, devID)) return 0; + // If we changed it at first, change it back. + if(needsToBeChangedBack) + { + if(kSTkErrOk != setOperationMode(DEVICE_MODE_MEAS)) + return 0; + } + return devID; } @@ -40,7 +65,19 @@ void SfeAS7331Driver::setCommunicationBus(sfeTkIBus *theBus) void SfeAS7331Driver::setDeviceAddress(const uint8_t &deviceAddress) { - _devAddress = deviceAddress; + switch(deviceAddress) + { + // If it's any of the allowed addresses, set it. + case kDefaultAS7331Addr: + case kSecondaryAS7331Addr: + case kTertiaryAS7331Addr: + case kQuaternaryAS7331Addr: + _devAddress = deviceAddress; + break; + default: // Otherwise set it to the default. No disallowed addresses. + _devAddress = kDefaultAS7331Addr; + break; + } } uint8_t SfeAS7331Driver::getDeviceAddress(void) @@ -48,79 +85,90 @@ uint8_t SfeAS7331Driver::getDeviceAddress(void) return _devAddress; } -bool SfeAS7331Driver::runDefaultSetup(bool runSoftReset) +bool SfeAS7331Driver::runDefaultSetup(const bool &runSoftReset) { - // Config registers are only available in Configuration mode. - if (!_theBus || _opMode != DEVICE_MODE_CFG) + // Nullptr check. + if (!_theBus) return false; // Do we need to run a software reset? if (runSoftReset) reset(); else - setDefaultSettings(); // This is performed in the reset() function, don't repeat. + { + setDefaultSettings(); - // Read the OSR register by itself since the offset isn't contiguous with the rest. - sfe_as7331_reg_cfg_osr_t osr; - if (kSTkErrOk != getOSR(osr)) - return false; + // Read the OSR register by itself since the offset isn't contiguous with the rest. + sfe_as7331_reg_cfg_osr_t osr; + if (kSTkErrOk != getOSR(osr)) + return false; - // Read all the configuration registers in. - uint8_t regs[6]; + // Change operation mode if necessary. + if (osr.dos != DEVICE_MODE_CFG) + { + osr.dos = DEVICE_MODE_CFG; - uint32_t nRead = 0; - sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg1, regs, 6U, nRead); - if (nRead != 6 || result != kSTkErrOk) - return false; + if(kSTkErrOk != setOSR(osr)) + return false; + } - // Assign the read in bytes to each register's byte union. - // This allows us to address the individual bits and set them. - sfe_as7331_reg_cfg_creg1_t creg1 = {.byte = regs[0]}; - sfe_as7331_reg_cfg_creg2_t creg2 = {.byte = regs[1]}; - sfe_as7331_reg_cfg_creg3_t creg3 = {.byte = regs[2]}; - uint8_t breakreg = regs[3]; - uint8_t edgesreg = regs[4]; - sfe_as7331_reg_cfg_optreg_t optreg = {.byte = regs[5]}; + // Read all the configuration registers in. + uint8_t regs[6]; - // Here we make sure the sensor's settings match the local settings - // by changing the sensor's settings. - osr.ss = _startState; - osr.pd = _powerDownEnableState; - osr.dos = _opMode; + uint32_t nRead = 0; + sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg1, regs, 6U, nRead); + if (nRead != 6 || result != kSTkErrOk) + return false; - creg1.gain = _sensorGain; - creg1.time = _conversionTime; + // Assign the read in bytes to each register's byte union. + // This allows us to address the individual bits and set them. + sfe_as7331_reg_cfg_creg1_t creg1 = {.byte = regs[0]}; + sfe_as7331_reg_cfg_creg2_t creg2 = {.byte = regs[1]}; + sfe_as7331_reg_cfg_creg3_t creg3 = {.byte = regs[2]}; + uint8_t breakreg = regs[3]; + uint8_t edgesreg = regs[4]; + sfe_as7331_reg_cfg_optreg_t optreg = {.byte = regs[5]}; - creg2.en_tm = _tempConvEnabled; - creg2.en_div = _dividerEnabled; - creg2.div = _dividerRange; + // Here we make sure the sensor's settings match the local settings + // by changing the sensor's settings. + osr.ss = _startState; + osr.pd = _powerDownEnableState; + osr.dos = _opMode; - creg3.mmode = _mmode; - creg3.sb = _standbyState; - creg3.rdyod = _readyPinMode; - creg3.cclk = _cclk; + creg1.gain = _sensorGain; + creg1.time = _conversionTime; - breakreg = _breakTime; + creg2.en_tm = _tempConvEnabled; + creg2.en_div = _dividerEnabled; + creg2.div = _dividerRange; - edgesreg = _numEdges; + creg3.mmode = _mmode; + creg3.sb = _standbyState; + creg3.rdyod = _readyPinMode; + creg3.cclk = _cclk; - optreg.init_idx = _indexMode; + breakreg = _breakTime; - // Write OSR first, since the offset is different from the rest. - if (kSTkErrOk != setOSR(osr)) - return false; + edgesreg = _numEdges; - // Assign the registers to a byte array for writing to the device. - regs[0] = creg1.byte; - regs[1] = creg2.byte; - regs[2] = creg3.byte; - regs[3] = breakreg; - regs[4] = edgesreg; - regs[5] = optreg.byte; + optreg.init_idx = _indexMode; - // Write the bytes to the sensor, ensuring the device matches local settings. - if (kSTkErrOk != _theBus->writeRegisterRegion(kSfeAS7331RegCfgCreg1, regs, 6U)) - return false; + // Write OSR first, since the offset is different from the rest. + if (kSTkErrOk != setOSR(osr)) + return false; + + // Assign the registers to a byte array for writing to the device. + regs[0] = creg1.byte; + regs[1] = creg2.byte; + regs[2] = creg3.byte; + regs[3] = breakreg; + regs[4] = edgesreg; + regs[5] = optreg.byte; + + // Write the bytes to the sensor, ensuring the device matches local settings. + if (kSTkErrOk != _theBus->writeRegisterRegion(kSfeAS7331RegCfgCreg1, regs, 6U)) + return false; + } // Calculate new conversion factors to make sure they match the current settings. calculateConversionFactors(); @@ -128,7 +176,7 @@ bool SfeAS7331Driver::runDefaultSetup(bool runSoftReset) return true; } -bool SfeAS7331Driver::prepareMeasurement(const as7331_meas_mode_t measMode, bool startMeasure) +bool SfeAS7331Driver::prepareMeasurement(const as7331_meas_mode_t &measMode, const bool &startMeasure) { // If the device is currently in power down mode. if (_powerDownEnableState) @@ -176,6 +224,8 @@ bool SfeAS7331Driver::prepareMeasurement(const as7331_meas_mode_t measMode, bool bool SfeAS7331Driver::reset(void) { + // OSR is available in all modes, no need to change modes. + // Standard read-modify-write sequence. sfe_as7331_reg_cfg_osr_t osr; @@ -371,11 +421,16 @@ uint32_t SfeAS7331Driver::getOutConv(void) return _outputConversionTime; } -as7331_gain_t SfeAS7331Driver::getGain(void) +as7331_gain_t SfeAS7331Driver::getGainRaw(void) { return _sensorGain; } +uint16_t SfeAS7331Driver::getGainValue(void) +{ + return (1 << (11 - _sensorGain)); +} + sfeTkError_t SfeAS7331Driver::setGain(const as7331_gain_t &gain) { sfe_as7331_reg_cfg_creg1_t creg1; @@ -400,11 +455,16 @@ sfeTkError_t SfeAS7331Driver::setGain(const as7331_gain_t &gain) return kSTkErrOk; } -as7331_conv_clk_freq_t SfeAS7331Driver::getCClk(void) +as7331_conv_clk_freq_t SfeAS7331Driver::getCClkRaw(void) { return _cclk; } +uint16_t SfeAS7331Driver::getCClkKHz(void) +{ + return 1024*(1 << _cclk); +} + sfeTkError_t SfeAS7331Driver::setCClk(const as7331_conv_clk_freq_t &cclk) { sfe_as7331_reg_cfg_creg3_t creg3; @@ -429,11 +489,16 @@ sfeTkError_t SfeAS7331Driver::setCClk(const as7331_conv_clk_freq_t &cclk) return kSTkErrOk; } -as7331_conv_time_t SfeAS7331Driver::getConversionTime(void) +as7331_conv_time_t SfeAS7331Driver::getConversionTimeRaw(void) { return _conversionTime; } +uint16_t SfeAS7331Driver::getConversionTimeMillis(void) +{ + return (1 << _conversionTime); +} + sfeTkError_t SfeAS7331Driver::setConversionTime(const as7331_conv_time_t &convTime) { sfe_as7331_reg_cfg_creg1_t creg1; @@ -944,28 +1009,19 @@ sfeTkError_t SfeAS7331Driver::readRawUV(const as7331_uv_type &uv_type) regAddress = kSfeAS7331RegMeasMres1; fsr = &_fsrA; retUV = &_uva; - if (_mmode != MEAS_MODE_SYND) - { - conv = &_conversionA; - } + conv = &_conversionA; break; case AS7331_UVB: regAddress = kSfeAS7331RegMeasMres2; fsr = &_fsrB; retUV = &_uvb; - if (_mmode != MEAS_MODE_SYND) - { - conv = &_conversionB; - } + conv = &_conversionB; break; case AS7331_UVC: regAddress = kSfeAS7331RegMeasMres3; fsr = &_fsrC; retUV = &_uvc; - if (_mmode != MEAS_MODE_SYND) - { - conv = &_conversionC; - } + conv = &_conversionC; break; } @@ -1048,4 +1104,4 @@ void SfeAS7331Driver::setDefaultSettings() _mmode = MEAS_MODE_CMD; // Command/One Shot Mode. _conversionTime = TIME_64MS; // 64 ms conversion time. _dividerRange = DIV_2; // Predivider 2x. -} \ No newline at end of file +} diff --git a/src/sfeAS7331.h b/src/sfeAS7331.h new file mode 100644 index 0000000..a441ccf --- /dev/null +++ b/src/sfeAS7331.h @@ -0,0 +1,697 @@ +/* + SparkFun Spectral UV Sensor - AS7331 + + Qwiic 1x1 + https://www.sparkfun.com/products/23517 + Qwiic Mini + https://www.sparkfun.com/products/23518 + + Repository + https://github.com/sparkfun/SparkFun_AS7331_Arduino_Library + + SPDX-License-Identifier: MIT + + Copyright (c) 2023 SparkFun Electronics + + Name: sfeAS7331.h + + Description: + SfeAS7331Driver is a comms-agnostic driver for the AS7331 Spectral UV + sensor that uses the SparkFun Toolkit. The SfeAS7331ArdI2C class defines + the Arduino specific behavior for initializing and interadcting with devices. + +*/ + +#pragma once + +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +// I2C Addressing +/////////////////////////////////////////////////////////////////////////////// +// 7-bit address defined as [1, 1, 1, 0, 1, A1, A0] where A1/A0 are the +// physical address pins tied high or low +const uint8_t kDefaultAS7331Addr = 0x74; // A1 = 0, A0 = 0 +const uint8_t kSecondaryAS7331Addr = 0x75; // A1 = 0, A1 = 1 +const uint8_t kTertiaryAS7331Addr = 0x76; // A1 = 1, A0 = 0 +const uint8_t kQuaternaryAS7331Addr = 0x77; // A1 = 1, A0 = 1 + +const uint8_t kDefaultAS7331DeviceID = 0x21; // When polling the AGEN register, this should be returned on boot. +const uint8_t kAS7331DeviceIDTopNibble = 0x2; // Top nibble of the AGEN byte, always the same. + +/////////////////////////////////////////////////////////////////////////////// +// Enum Definitions +/////////////////////////////////////////////////////////////////////////////// + +// Device Operating Mode +typedef enum +{ + // 0b00X invalid + DEVICE_MODE_CFG = 0x2, + DEVICE_MODE_MEAS = 0x3 + // 0b1XX invalid +} as7331_dev_op_state_t; + +// Sensor gain, able to be read as (1 << (11 - gain)) aka 2^(11-gain). +typedef enum +{ + GAIN_2048 = 0x0000, + GAIN_1024, + GAIN_512, + GAIN_256, + GAIN_128, + GAIN_64, + GAIN_32, + GAIN_16, + GAIN_8, + GAIN_4, + GAIN_2, + GAIN_1 +} as7331_gain_t; + +// Conversion Clock Frequency available settings, read as (1024 * (1 << cclk)) +// aka 1024*2^cclk. +typedef enum +{ + CCLK_1_024_MHZ = 0x00, + CCLK_2_048_MHZ, + CCLK_4_096_MHZ, + CCLK_8_192_MHZ +} as7331_conv_clk_freq_t; + +// Measurement Mode types +typedef enum +{ + MEAS_MODE_CONT = 0x0, // Continuous mode + MEAS_MODE_CMD, // Command/OneShot mode + MEAS_MODE_SYNS, // SYNchronous Start mode + MEAS_MODE_SYND // SYNchronous start and enD mode +} as7331_meas_mode_t; + +// Time conversion values in CONT, CMD, SYNS modes. Read as (1 << tconv) aka +// 2^tconv. +typedef enum +{ + TIME_1MS = 0x0, + TIME_2MS, + TIME_4MS, + TIME_8MS, + TIME_16MS, + TIME_32MS, + TIME_64MS, + TIME_128MS, + TIME_256MS, + TIME_512MS, + TIME_1024MS, + TIME_2048MS, + TIME_4096MS, + TIME_8192MS, + TIME_16384MS +} as7331_conv_time_t; + +// Predivider values. Read as (1 << (1+div)) aka 2^(1+div). +typedef enum +{ + DIV_2 = 0x0, + DIV_4, + DIV_8, + DIV_16, + DIV_32, + DIV_64, + DIV_128, + DIV_256 +} as7331_divider_val_t; + +// Type of UV to pick. +typedef enum +{ + AS7331_UVA, + AS7331_UVB, + AS7331_UVC +} as7331_uv_type; + +/////////////////////////////////////////////////////////////////////////////// +// Configuration Register Descriptions +// +// Can only be accessed in the Configuration operation state. All 8 bits long. +// +// The following registers map to the underlying registers of the device. +/////////////////////////////////////////////////////////////////////////////// + +const uint8_t kSfeAS7331RegCfgOsr = 0x00; // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t dos : 3; // Device Operating State - OSR[2:0] + uint8_t sw_res : 1; // Software Reset - OSR[3] + uint8_t reserved : 2; // Reserved, don't write. - OSR[4:5] + uint8_t pd : 1; // Power Down Enabled - OSR[6] + uint8_t ss : 1; // Start State - OSR[7] + }; + uint8_t byte; +} sfe_as7331_reg_cfg_osr_t; + +const uint8_t kSfeAS7331RegCfgAgen = 0x02; // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t mut : 4; // Increments when changes are made to the control + // registers. Defaults to 0b0001 aka 0x1. + uint8_t devid : 4; // Always equals 0b0010 aka 0x2. + }; + uint8_t byte; +} sfe_as7331_reg_cfg_agen_t; + +const uint8_t kSfeAS7331RegCfgCreg1 = 0x06; // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t time : 4; // Conversion time - CREG1[3:0] + uint8_t gain : 4; // Sensor gain - CREG1[7:4] + }; + uint8_t byte; +} sfe_as7331_reg_cfg_creg1_t; + +const uint8_t kSfeAS7331RegCfgCreg2 = 0x07; // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t div : 3; // Digital Divider value - CREG2[2:0] + uint8_t en_div : 1; // Whether the digital divider is enabled - CREG2[3] + uint8_t reserved : 2; // Reserved, don't write. - CREG2[5:4] + uint8_t en_tm : 1; // Whether temperature is calculated in SYND mode. - CREG2[6] + uint8_t reserved1 : 1; // Reserved, don't write. - CREG2[7] + }; + uint8_t byte; +} sfe_as7331_reg_cfg_creg2_t; + +const uint8_t kSfeAS7331RegCfgCreg3 = 0x08; // Register Address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t cclk : 2; // Conversion clock selection. - CREG3[1:0] + uint8_t reserved : 1; // Reserved, don't write. - CREG3[2] + uint8_t rdyod : 1; // Output mode of the ready pin. - CREG3[3] + uint8_t sb : 1; // Standby mode bit. - CREG3[4] + uint8_t reserved1 : 1; // Reserved, don't write. - CREG3[5] + uint8_t mmode : 2; // Measurement mode selection. - CREG3[7:6] + }; + uint8_t byte; +} sfe_as7331_reg_cfg_creg3_t; + +const uint8_t kSfeAS7331RegCfgBreak = 0x09; // Register address, register is a single uint8_t. + +const uint8_t kSfeAS7331RegCfgEdges = 0x0A; // Register address, register is a single uint8_t. + +const uint8_t kSfeAS7331RegCfgOptReg = 0x0B; // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + uint8_t init_idx : 1; // I2C repeat start mode flag. - OPTREG[0] + uint8_t reserved : 7; // Reserved, don't write. - OPTREG[7:1] + }; + uint8_t byte; +} sfe_as7331_reg_cfg_optreg_t; + +/////////////////////////////////////////////////////////////////////////////// +// Measurement Register Descriptions +// +// Can only be accessed in the Measure operation state. All 16 bits long. They +// are all read-only except the bottom byte of the OSR/STATUS register. +// +// The following registers map to the underlying registers of the device. +/////////////////////////////////////////////////////////////////////////////// + +const uint8_t kSfeAS7331RegMeasOsrStatus = 0x00; // Register address + +// A union is used here so that individual values from the register can be +// accessed or the whole register can be accessed. +typedef union { + struct + { + sfe_as7331_reg_cfg_osr_t osr; // See OSR configuration register above. - OSRSTAT[7:0] + uint8_t powerstate : 1; // Power down state. - OSRSTAT[8] + uint8_t standbystate : 1; // Standby mode state. - OSRSTAT[9] + uint8_t notready : 1; // Inverted value of the ready pin. - OSRSTAT[10] + uint8_t ndata : 1; // Indicates new data available. - OSRSTAT[11] + uint8_t ldata : 1; // Indicates data overwrite prior to retrieval. - OSRSTAT[12] + uint8_t adcof : 1; // OVF of at least one ADC channel. - OSRSTAT[13] + uint8_t mresof : 1; // OVF of at least one of the MRES registers. - OSRSTAT[14] + uint8_t outconvof : 1; // OVF of the internal 24-bit time reference. - OSRSTAT[15] + }; + uint16_t word; +} sfe_as7331_reg_meas_osr_status_t; + +// Output result registers. 16-bit values unless noted otherwise. +const uint8_t kSfeAS7331RegMeasTemp = 0x01; // 12-bit temperature, MS 4-bits 0. +const uint8_t kSfeAS7331RegMeasMres1 = 0x02; +const uint8_t kSfeAS7331RegMeasMres2 = 0x03; +const uint8_t kSfeAS7331RegMeasMres3 = 0x04; +const uint8_t kSfeAS7331RegMeasOutConvL = 0x05; // First 16-bits of 24-bit OUTCONV. +const uint8_t kSfeAS7331RegMeasOutConvH = 0x06; // LSB is MSB of OUTCONV, MSB is 0. + +/////////////////////////////////////////////////////////////////////////////// + +class SfeAS7331Driver +{ + public: + // Default initialization values based on the datasheet. See SfeAS7331Driver::setDefaultSettings for + // an explanation of the values. + SfeAS7331Driver(uint8_t address = kDefaultAS7331Addr) + : _devAddress{address}, _theBus{nullptr}, _breakTime{25}, _numEdges{1}, _readyPinMode{false}, + _dividerEnabled{false}, _tempConvEnabled{true}, _indexMode{true}, _standbyState{false}, _startState{false}, + _powerDownEnableState{true}, _opMode{DEVICE_MODE_CFG}, _sensorGain{GAIN_2}, _cclk{CCLK_1_024_MHZ}, + _mmode{MEAS_MODE_CMD}, _conversionTime{TIME_64MS}, _dividerRange{DIV_2}, _uva{0.0f}, _uvb{0.0f}, _uvc{0.0f}, + _temperature{0.0f}, _outputConversionTime{0U}, _conversionA{0.0f}, _conversionB{0.0f}, _conversionC{0.0f} + { + } + + /// @brief This method is called to initialize the AS7331 device through the + /// specified bus. + /// @param deviceAddress I2C address for the device. + /// @param theBus Pointer to the bus object. + /// @return True if successful, false if it fails. + bool begin(const uint8_t &deviceAddress = kDefaultAS7331Addr, sfeTkIBus *theBus = nullptr); + + /// @brief Requests the device ID from the sensor. + /// @return The device ID of the sensor. + uint8_t getDeviceID(void); + + /// @brief Sets the communication bus to the specified bus. + /// @param theBus Bus to set as the communication devie. + void setCommunicationBus(sfeTkIBus *theBus); + + /// @brief Sets the address that the bus uses to communicate with the sensor. + /// @param deviceAddress Device address to use. + void setDeviceAddress(const uint8_t &deviceAddress); + + /// @brief Gets the currently configured device address. + /// @return device address. + uint8_t getDeviceAddress(void); + + /// @brief Helper class that sets up the sensor and state in the POR + /// configuration. + /// @param runSoftReset Flag that runs the soft reset function to reset the + /// device. + /// @return True if successful, false otherwise. + bool runDefaultSetup(const bool &runSoftReset = false); + + /// @brief Puts the sensor in the specified measurement mode. + /// @param measMode Measurement mode to enter. + /// @param startMeasure Flag to start measuring immediately if set. + /// @return True if successful, false otherwise. + bool prepareMeasurement(const as7331_meas_mode_t &measMode = MEAS_MODE_CONT, const bool &startMeasure = false); + + /// @brief Performs a soft reset of the sensor. + /// @return True if successful, false otherwise. + bool reset(void); + + /// @brief Reads the sensor's temperature, converts it to a usable form, and + /// saves it to the internal temperature variable. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t readTemp(void); + + /// @brief Reads the sensor's UVA register, converts it to a usable form, and + /// saves it to the internal UVA variable. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t readUVA(void); + + /// @brief Reads the sensor's UVB register, converts it to a usable form, and + /// saves it to the internal UVB variable. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t readUVB(void); + + /// @brief Reads the sensor's UVC register, converts it to a usable form, and + /// saves it to the internal UVC variable. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t readUVC(void); + + /// @brief Read's all three UV registers, converts them to a usable form, then + /// saves them to their respective internal variable. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t readAllUV(void); + + /// @brief Read the sensor's temperature, UV, and external time conversion + /// clock counts, converts them, and then saves them to their respective + /// internal variable. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t readAll(void); + + /// @brief Read the conversion clock counts register and saves it to the + /// internal output conversion time variable. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t readOutConv(void); + + /// @brief Returns the last valid UVA reading. + /// @return A float of the UVA reading. + float getUVA(void); + + /// @brief Returns the last valid UVB reading. + /// @return A float of the UVB reading. + float getUVB(void); + + /// @brief Returns the last valid UVC reading. + /// @return A float of the UVC reading. + float getUVC(void); + + /// @brief Returns the last valid Temperature reading. + /// @return A float of the Temperature reading. + float getTemp(void); + + /// @brief Returns the last valid output conversion. + /// @return The output conversion time in number of clock cycles. + uint32_t getOutConv(void); + + /// @brief Getter for the currently configured gain. + /// @return Sensor's gain expressed as (1 << (11 - gain)). + as7331_gain_t getGainRaw(void); + + /// @brief Getter for the currently configured gain. + /// @return Sensor's gain expressed as a unitless scalar. + uint16_t getGainValue(void); + + /// @brief Sets the UV sensor's gain. + /// @param gain The gain to set the sensor to. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setGain(const as7331_gain_t &gain); + + /// @brief Getter for the currently configured conversion clock. + /// @return Sensor's conversion clock expressed as 1024*(1 << cclk). + as7331_conv_clk_freq_t getCClkRaw(void); + + /// @brief Getter for the currently configured conversion clock. + /// @return Sensor's conversion clock expressed in kHz. + uint16_t getCClkKHz(void); + + /// @brief Set the sensor's internal clock speed. + /// @param cclk Clock speed to set on the sensor. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setCClk(const as7331_conv_clk_freq_t &cclk); + + /// @brief Getter for the currently configured conversion time. + /// @return Sensor's conversion time expressed as (1 << time). + as7331_conv_time_t getConversionTimeRaw(void); + + /// @brief Getter for the currently configured conversion time. + /// @return Sensor's conversion time in milliseconds. + uint16_t getConversionTimeMillis(void); + + /// @brief Sets the conversion time that the sensor will run to. + /// @param convTime Conversion time to set the sensor to. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setConversionTime(const as7331_conv_time_t &convTime); + + /// @brief Getter for the currently configured pin mode. + /// @return False if push-pull, true if open-drain. + bool getReadyPinMode(void); + + /// @brief Sets the ready pin type to push-pull or open-drain. + /// @param pinMode Mode to set the ready pin to. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setReadyPinMode(const bool &pinMode); + + /// @brief Getter for the currently configured divider status. + /// @return True if Internal predivider is enabled, false otherwise. + bool getDigitalDividerEnabled(void); + + /// @brief Enables or disables the internal UV result divider. + /// @param isEnabled Enable or disable the divder. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setDigitalDividerEnabled(const bool &isEnabled); + + /// @brief Getter for the currently configured divider range. + /// @return Sensor's internal UV predivider range. + as7331_divider_val_t getDigitalDividerRange(void); + + /// @brief Sets the value of the internal UV result divider. + /// @param divider Divider value to set. + /// @param setEnableDiv Option to turn on the divider if desired. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setDigitalDividerRange(const as7331_divider_val_t ÷r, const bool &enableDiv = true); + + /// @brief Getter for the SYND temperature conversion status. + /// @return True if temperature conversion is enabled in SYND mode. + bool getSyndTempConversionEnabled(void); + + /// @brief Enables or disables temperature conversion when in SYND mode. + /// @param isEnabled Enable or disable the feature. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setSyndTempConversionEnabled(const bool &isEnabled); + + /// @brief Getter for the currently configured I2C compatibility mode. + /// @return True if the device will respond to repeat starts, false otherwise. + bool getIndexMode(void); + + /// @brief Set the index mode for compatibility with I2C controllers that + /// don't support repeated start. + /// @param indexMode Simple or standard I2C addressing mode. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setIndexMode(const bool &indexMode); + + /// @brief Getter for the currently configured minimum break time in CONT, + /// CMD, SYNS modes. + /// @return Sensor's breaktime in 8us steps. + uint8_t getBreakTime(void); + + /// @brief Set the minimum time between measurements in CONT, SYNS, SYND modes. + /// @param breakTime Time between measurements, 8us step time, max 2048us. A 0 + /// value is a minimum of 3 cclk cycles. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setBreakTime(const uint8_t &breakTime); + + /// @brief Getter for the currently configured minimum number of edges to end + /// conversion when in SYND mode. + /// @return Sensor's minimum number of edges, minimum 1 edge. + uint8_t getNumEdges(void); + + /// @brief Set the minimum number of falling edges required at the SYN input + /// until the conversion is terminated. Only operational in SYND mode. + /// @param numEdges Number of edges prior to terminating conversion in SYND + /// mode. 0 is not allowed, 1 is the minimum. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setNumEdges(const uint8_t &numEdges); + + /// @brief Getter for the current power state. + /// @return Sensor's power state. + bool getPowerDownState(void); + + /// @brief Sets the power state of the sensor. + /// @param pd Power state to set. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setPowerDownState(const bool &pd); + + /// @brief Getter for the current operational state. + /// @return Sensor's operational mode. + as7331_dev_op_state_t getOperationMode(void); + + /// @brief Set the sensor's operating mode. + /// @param opMode Operating mode to set. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setOperationMode(const as7331_dev_op_state_t &opMode); + + /// @brief Getter for the current measurement state. + /// @return Sensor's measurement state. + as7331_meas_mode_t getMeasurementMode(void); + + /// @brief Sets the sensor's measurement mode. + /// @param measMode Measurement mode to set. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setMeasurementMode(const as7331_meas_mode_t &measMode); + + /// @brief Getter for the current standby state. + /// @return Sensor's standby state. + bool getStandbyState(void); + + /// @brief Sets the sensor's standby mode. + /// @param standby State to set. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setStandbyState(const bool &standby); + + /// @brief Getter for the current start state. + /// @return Sensor's start state. + bool getStartState(void); + + /// @brief Sets the sensor's start state. This begins measurement. + /// @param startState Start state to set. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setStartState(const bool &startState); + + /// @brief Gets the sensor's status when in measurement operation mode. + /// @param statusReg Pointer to a register struct to store the sensor's + /// current status. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t getStatus(sfe_as7331_reg_meas_osr_status_t &statusReg); + + /// @brief Gets the operational state register when in configuration operation + /// mode. + /// @param osrReg Pointer to a register struct to store the sensor's current + /// OSR register. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t getOSR(sfe_as7331_reg_cfg_osr_t &osrReg); + + /// @brief Sets the operational state register when in configuration operation + /// mode. + /// @param osrReg Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setOSR(const sfe_as7331_reg_cfg_osr_t &osrReg); + + /// @brief Gets the configuration register #1 when in configuration operation + /// mode. + /// @param creg1 Pointer to a register struct to store the sensor's current + /// creg1 register. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t getCReg1(sfe_as7331_reg_cfg_creg1_t &creg1); + + /// @brief Sets the configuration register #1 when in configuration operation + /// mode. + /// @param creg1 Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setCReg1(const sfe_as7331_reg_cfg_creg1_t &creg1); + + /// @brief Gets the configuration register #2 when in configuration operation + /// mode. + /// @param creg2 Pointer to a register struct to store the sensor's current + /// creg2 register. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t getCReg2(sfe_as7331_reg_cfg_creg2_t &creg2); + + /// @brief Sets the configuration register #2 when in configuration operation + /// mode. + /// @param creg2 Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setCReg2(const sfe_as7331_reg_cfg_creg2_t &creg2); + + /// @brief Gets the configuration register #3 when in configuration operation + /// mode. + /// @param creg3 Pointer to a register struct to store the sensor's current + /// creg3 register. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t getCReg3(sfe_as7331_reg_cfg_creg3_t &creg3); + + /// @brief Sets the configuration register #3 when in configuration operation + /// mode. + /// @param creg3 Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setCReg3(const sfe_as7331_reg_cfg_creg3_t &creg3); + + /// @brief Gets the break register when in configuration operation mode. + /// @param breakReg Pointer to a register struct to store the sensor's current + /// break register. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t getBreak(uint8_t &breakReg); + + /// @brief Sets the break register when in configuration operation mode. + /// @param breakReg Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setBreak(const uint8_t &breakReg); + + /// @brief Gets the edges register when in configuration operation mode. + /// @param edgesReg Pointer to a register struct to store the sensor's current + /// edges register. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t getEdges(uint8_t &edgesReg); + + /// @brief Sets the edges register when in configuration operation mode. + /// @param edgesReg Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setEdges(const uint8_t &edgesReg); + + /// @brief Gets the option register when in configuration operation mode. + /// @param optReg Pointer to a register struct to store the sensor's current + /// option register. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t getOptIndex(sfe_as7331_reg_cfg_optreg_t &optReg); + + /// @brief Sets the option register when in configuration operation mode. + /// @param optReg Pointer to a register struct that has the new register + /// configuration. + /// @return 0 if successful, negative if error, positive for warning. + sfeTkError_t setOptIndex(const sfe_as7331_reg_cfg_optreg_t &optReg); + + private: + /// @brief Reads a UV result register and saves it to the respective internal + /// variable. + /// @param uv_type The type of UV you want to read. + /// @return 0 if successful, negative if error, positive if warning + sfeTkError_t readRawUV(const as7331_uv_type &uv_type); + + /// @brief Converts the raw temperature value to a human readable form. + /// @param inputVal Raw temperature value to convert. + /// @return The converted device temperature in degree C. + float convertRawTempToTempC(const uint16_t &inputVal); + + /// @brief Converts a raw result value and performs a conversion to a human + /// readable format. + /// @param rawVal The raw input value. + /// @param convFactor Conversion factor to multiply the raw input value. + /// @return The calculated conversion result + float convertRawUVVal(const uint16_t &rawVal, const float &convFactor); + + /// @brief Called when changing values that affect the conversion, calculates + /// a new conversion factor to reduce the conversion overhead. + void calculateConversionFactors(void); + + /// @brief Called to reset all local values back to initial conditions. + void setDefaultSettings(void); + + sfeTkIBus *_theBus; // Pointer to bus device. + uint8_t _devAddress; // Device's I2C address. + + uint8_t _breakTime; // Local config value. Value is in us/8. EX: _breakTime = 20 means 20*8 = 160us. + uint8_t _numEdges; // Local config value. Edges seen on SYN pin before ending conversion in SYND mode. + + bool _readyPinMode; // Local config value. False is Push/Pull True is Open/Drain. + bool _dividerEnabled; // Local config value. False is disabled, True is enabled. + bool _tempConvEnabled; // Local config value. False is disabled, True is enabled. + bool _indexMode; // Local config value. False is for controllers without repeat start. + bool _standbyState; // Local state value. False means the device is not in standby mode. + bool _startState; // Local state value. False means the device is not allowed to measure. + bool _powerDownEnableState; // Local state value. False means the device is NOT POWERED DOWN. This is inverse. + + as7331_dev_op_state_t _opMode; // Local state value. Configuration or Measure operating mode. + as7331_gain_t _sensorGain; // Local config value. Sensor gain stored as (1 << (11 - gain)). + as7331_conv_clk_freq_t _cclk; // Local config value. Sensor's conversion clock stored as 1024*(1 << cclk) kHz. + as7331_meas_mode_t _mmode; // Local state value. Details the device's measurement mode as CONT, CMD, SYNS, or SYND. + as7331_conv_time_t _conversionTime; // Local config value. Contains the conversion time stored as (1 << time) ms. + as7331_divider_val_t _dividerRange; // Local config value. Contains the predivider value stored as (1 << 1 + range). + + float _uva; // Last valid UVA result in uW/cm2. + float _uvb; // Last valid UVB result in uW/cm2. + float _uvc; // Last valid UVC result in uW/cm2. + float _temperature; // Last valid temperature result in degC. + + uint32_t _outputConversionTime; // Last valid output conversion time result in of clock cycle count. + + float _conversionA; // Internal conversion factor for the UVA sensor. + float _conversionB; // Internal conversion factor for the UVB sensor. + float _conversionC; // Internal conversion factor for the UVC sensor. + + const float _fsrA = 348160.0; // Full Scale Resolution for the UVA sensor. + const float _fsrB = 387072.0; // Full Scale Resolution for the UVB sensor. + const float _fsrC = 169984.0; // Full Scale Resolution for the UVC sensor. +}; From 3be9d9bdeaa543db13fdf68573615661c4754fa7 Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Fri, 15 Dec 2023 16:50:17 -0700 Subject: [PATCH 12/39] Update changes. Make default case for setDeviceAddress do nothing. Manually setOSR in getDeviceID. --- src/sfeAS7331.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sfeAS7331.cpp b/src/sfeAS7331.cpp index bc25370..7e168a0 100644 --- a/src/sfeAS7331.cpp +++ b/src/sfeAS7331.cpp @@ -51,7 +51,9 @@ uint8_t SfeAS7331Driver::getDeviceID(void) // If we changed it at first, change it back. if(needsToBeChangedBack) { - if(kSTkErrOk != setOperationMode(DEVICE_MODE_MEAS)) + osr.dos = DEVICE_MODE_MEAS; + + if(kSTkErrOk != setOSR(osr)) return 0; } @@ -74,8 +76,7 @@ void SfeAS7331Driver::setDeviceAddress(const uint8_t &deviceAddress) case kQuaternaryAS7331Addr: _devAddress = deviceAddress; break; - default: // Otherwise set it to the default. No disallowed addresses. - _devAddress = kDefaultAS7331Addr; + default: // Default to doing nothing. break; } } From f2c7c4aab7b90ee29d423c6d3758a3e8357363ab Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Fri, 15 Dec 2023 16:53:49 -0700 Subject: [PATCH 13/39] update keywords.txt for new functions. --- keywords.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/keywords.txt b/keywords.txt index e879f34..e6d485c 100644 --- a/keywords.txt +++ b/keywords.txt @@ -30,11 +30,14 @@ getUVB KEYWORD2 getUVC KEYWORD2 getTemp KEYWORD2 getOutConv KEYWORD2 -getGain KEYWORD2 +getGainRaw KEYWORD2 +getGainValue KEYWORD2 setGain KEYWORD2 -getCClk KEYWORD2 +getCClkRaw KEYWORD2 +getCClkKHz KEYWORD2 setCClk KEYWORD2 -getConversionTime KEYWORD2 +getConversionTimeRaw KEYWORD2 +getConversionTimeMillis KEYWORD2 setConversionTime KEYWORD2 getReadyPinMode KEYWORD2 setReadyPinMode KEYWORD2 From 46b34a0cd2598c49625f8806439d3b5afeac4c11 Mon Sep 17 00:00:00 2001 From: SFEMark Date: Fri, 15 Dec 2023 17:00:22 -0700 Subject: [PATCH 14/39] Create add_issue_to_project.yml Adding .yml file for issue tracking in GH project. --- .github/workflows/add_issue_to_project.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/add_issue_to_project.yml diff --git a/.github/workflows/add_issue_to_project.yml b/.github/workflows/add_issue_to_project.yml new file mode 100644 index 0000000..6b60cc3 --- /dev/null +++ b/.github/workflows/add_issue_to_project.yml @@ -0,0 +1,18 @@ +name: Add new issue to our main project + +on: + issues: + types: + - opened + +jobs: + add-to-project: + name: Add issue to project + runs-on: ubuntu-latest + steps: + - uses: actions/add-to-project@main + with: + # You can target a project in a different organization + # to the issue + project-url: https://github.com/orgs/sparkfun/projects/19 + github-token: ${{ secrets.DEFECT_ADD_TO_PROJECT }} From 15601fdc69fcff51818f457cf3c102b893b80f3a Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Fri, 15 Dec 2023 17:25:39 -0700 Subject: [PATCH 15/39] return from isConnected if not connected. --- src/SparkFun_AS7331.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h index 580bbe7..76d3a22 100644 --- a/src/SparkFun_AS7331.h +++ b/src/SparkFun_AS7331.h @@ -47,7 +47,8 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver setCommunicationBus(&_theI2CBus); - isConnected(); + if(!isConnected()) + return false; return SfeAS7331Driver::begin(address); } From fa3eb7ac16bd51fa8d0014346cb9faca2f2519d2 Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Fri, 15 Dec 2023 17:26:48 -0700 Subject: [PATCH 16/39] Return from isConnected if not connected. --- src/SparkFun_AS7331.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h index 580bbe7..76d3a22 100644 --- a/src/SparkFun_AS7331.h +++ b/src/SparkFun_AS7331.h @@ -47,7 +47,8 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver setCommunicationBus(&_theI2CBus); - isConnected(); + if(!isConnected()) + return false; return SfeAS7331Driver::begin(address); } From 3b54790bf334ae4da1ad6a4400de82b2bc00cdf7 Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Tue, 19 Dec 2023 15:46:11 -0700 Subject: [PATCH 17/39] Fix setDeviceAddress Rev to v2.1.0 since logic changed. Moved set and get address functions to interface-specific class. Removed address-related things from the driver class. --- library.properties | 2 +- src/SparkFun_AS7331.h | 37 ++++++++++++++++++++++++++++++++++++- src/sfeAS7331.cpp | 27 +-------------------------- src/sfeAS7331.h | 16 +++------------- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/library.properties b/library.properties index 5ac192a..01816d9 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun AS7331 Arduino Library -version=2.0.0 +version=2.1.0 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=An Arduino library to make use of the Qwiic and Qwiic Mini AS7331 Spectral UV Sensor diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h index 76d3a22..689ab20 100644 --- a/src/SparkFun_AS7331.h +++ b/src/SparkFun_AS7331.h @@ -47,10 +47,17 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver setCommunicationBus(&_theI2CBus); + // If the address passed in isn't the default, set the new address. + if (kDefaultAS7331Addr != address) + { + if(!setDeviceAddress(address)) + return false; + } + if(!isConnected()) return false; - return SfeAS7331Driver::begin(address); + return SfeAS7331Driver::begin(); } /// @brief Checks to see if the AS7331 is connected. @@ -63,6 +70,34 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver return (kDefaultAS7331DeviceID == getDeviceID()); } + /// @brief Sets the address that the bus uses to communicate with the sensor. + /// @param deviceAddress Device address to use. + /// @return True if a valid address is set. + bool setDeviceAddress(const uint8_t &deviceAddress) + { + switch(deviceAddress) + { + // If it's any of the allowed addresses, set it. + case kDefaultAS7331Addr: + case kSecondaryAS7331Addr: + case kTertiaryAS7331Addr: + case kQuaternaryAS7331Addr: + _theI2CBus.setAddress(deviceAddress); + break; + default: // If it's invalid, return false. + return false; + break; + } + return true; + } + + /// @brief Gets the currently configured device address. + /// @return device address. + uint8_t getDeviceAddress(void) + { + return _theI2CBus.address(); + } + private: sfeTkArdI2C _theI2CBus; }; diff --git a/src/sfeAS7331.cpp b/src/sfeAS7331.cpp index 7e168a0..97158df 100644 --- a/src/sfeAS7331.cpp +++ b/src/sfeAS7331.cpp @@ -1,6 +1,6 @@ #include "sfeAS7331.h" -bool SfeAS7331Driver::begin(const uint8_t &deviceAddress, sfeTkIBus *theBus) +bool SfeAS7331Driver::begin(sfeTkIBus *theBus) { // Nullptr check. if (!_theBus && !theBus) @@ -10,10 +10,6 @@ bool SfeAS7331Driver::begin(const uint8_t &deviceAddress, sfeTkIBus *theBus) if (theBus != nullptr) setCommunicationBus(theBus); - // If the address passed in isn't the default, set the new address. - if (kDefaultAS7331Addr != deviceAddress) - setDeviceAddress(deviceAddress); - // Get the device setup and ready. return runDefaultSetup(); } @@ -65,27 +61,6 @@ void SfeAS7331Driver::setCommunicationBus(sfeTkIBus *theBus) _theBus = theBus; } -void SfeAS7331Driver::setDeviceAddress(const uint8_t &deviceAddress) -{ - switch(deviceAddress) - { - // If it's any of the allowed addresses, set it. - case kDefaultAS7331Addr: - case kSecondaryAS7331Addr: - case kTertiaryAS7331Addr: - case kQuaternaryAS7331Addr: - _devAddress = deviceAddress; - break; - default: // Default to doing nothing. - break; - } -} - -uint8_t SfeAS7331Driver::getDeviceAddress(void) -{ - return _devAddress; -} - bool SfeAS7331Driver::runDefaultSetup(const bool &runSoftReset) { // Nullptr check. diff --git a/src/sfeAS7331.h b/src/sfeAS7331.h index a441ccf..e3a1447 100644 --- a/src/sfeAS7331.h +++ b/src/sfeAS7331.h @@ -276,8 +276,8 @@ class SfeAS7331Driver public: // Default initialization values based on the datasheet. See SfeAS7331Driver::setDefaultSettings for // an explanation of the values. - SfeAS7331Driver(uint8_t address = kDefaultAS7331Addr) - : _devAddress{address}, _theBus{nullptr}, _breakTime{25}, _numEdges{1}, _readyPinMode{false}, + SfeAS7331Driver() + : _theBus{nullptr}, _breakTime{25}, _numEdges{1}, _readyPinMode{false}, _dividerEnabled{false}, _tempConvEnabled{true}, _indexMode{true}, _standbyState{false}, _startState{false}, _powerDownEnableState{true}, _opMode{DEVICE_MODE_CFG}, _sensorGain{GAIN_2}, _cclk{CCLK_1_024_MHZ}, _mmode{MEAS_MODE_CMD}, _conversionTime{TIME_64MS}, _dividerRange{DIV_2}, _uva{0.0f}, _uvb{0.0f}, _uvc{0.0f}, @@ -287,10 +287,9 @@ class SfeAS7331Driver /// @brief This method is called to initialize the AS7331 device through the /// specified bus. - /// @param deviceAddress I2C address for the device. /// @param theBus Pointer to the bus object. /// @return True if successful, false if it fails. - bool begin(const uint8_t &deviceAddress = kDefaultAS7331Addr, sfeTkIBus *theBus = nullptr); + bool begin(sfeTkIBus *theBus = nullptr); /// @brief Requests the device ID from the sensor. /// @return The device ID of the sensor. @@ -300,14 +299,6 @@ class SfeAS7331Driver /// @param theBus Bus to set as the communication devie. void setCommunicationBus(sfeTkIBus *theBus); - /// @brief Sets the address that the bus uses to communicate with the sensor. - /// @param deviceAddress Device address to use. - void setDeviceAddress(const uint8_t &deviceAddress); - - /// @brief Gets the currently configured device address. - /// @return device address. - uint8_t getDeviceAddress(void); - /// @brief Helper class that sets up the sensor and state in the POR /// configuration. /// @param runSoftReset Flag that runs the soft reset function to reset the @@ -660,7 +651,6 @@ class SfeAS7331Driver void setDefaultSettings(void); sfeTkIBus *_theBus; // Pointer to bus device. - uint8_t _devAddress; // Device's I2C address. uint8_t _breakTime; // Local config value. Value is in us/8. EX: _breakTime = 20 means 20*8 = 160us. uint8_t _numEdges; // Local config value. Edges seen on SYN pin before ending conversion in SYND mode. From dc395ced3028464aaa1fc38325a479773ada1f04 Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Wed, 20 Dec 2023 12:07:47 -0700 Subject: [PATCH 18/39] Remove redundant address set from begin. --- src/SparkFun_AS7331.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/SparkFun_AS7331.h b/src/SparkFun_AS7331.h index 689ab20..1555e03 100644 --- a/src/SparkFun_AS7331.h +++ b/src/SparkFun_AS7331.h @@ -47,13 +47,6 @@ class SfeAS7331ArdI2C : public SfeAS7331Driver setCommunicationBus(&_theI2CBus); - // If the address passed in isn't the default, set the new address. - if (kDefaultAS7331Addr != address) - { - if(!setDeviceAddress(address)) - return false; - } - if(!isConnected()) return false; From 89c4c5a8584893ed8c0edefde9925734b69e8ecc Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Tue, 2 Jan 2024 14:25:36 -0700 Subject: [PATCH 19/39] Update sfeAS7331.cpp Retype a few variables to match the dependent i2c library. --- src/sfeAS7331.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sfeAS7331.cpp b/src/sfeAS7331.cpp index 97158df..c3109b1 100644 --- a/src/sfeAS7331.cpp +++ b/src/sfeAS7331.cpp @@ -91,7 +91,7 @@ bool SfeAS7331Driver::runDefaultSetup(const bool &runSoftReset) // Read all the configuration registers in. uint8_t regs[6]; - uint32_t nRead = 0; + size_t nRead = 0; sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegCfgCreg1, regs, 6U, nRead); if (nRead != 6 || result != kSTkErrOk) return false; @@ -264,7 +264,7 @@ sfeTkError_t SfeAS7331Driver::readAllUV(void) uint8_t dataRaw[6]; // Read in the raw data from the results registers. - uint32_t nRead = 0; + size_t nRead = 0; sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasMres1, dataRaw, 6U, nRead); @@ -310,7 +310,7 @@ sfeTkError_t SfeAS7331Driver::readAll(void) uint8_t dataRaw[8]; - uint32_t nRead = 0; + size_t nRead = 0; sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasTemp, dataRaw, 8U, nRead); if (nRead != 8 || result != kSTkErrOk) @@ -358,7 +358,7 @@ sfeTkError_t SfeAS7331Driver::readOutConv(void) uint8_t tconvRaw[4]; - uint32_t nRead = 0; + size_t nRead; sfeTkError_t result = _theBus->readRegisterRegion(kSfeAS7331RegMeasOutConvL, tconvRaw, 4U, nRead); From 85fc1a0096a7af36f77decdb325b442916467b2b Mon Sep 17 00:00:00 2001 From: Alex Brudner <101155592+SFE-Brudnerd@users.noreply.github.com> Date: Tue, 2 Jan 2024 14:52:35 -0700 Subject: [PATCH 20/39] Rev to v2.1.1. --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 01816d9..82f057b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun AS7331 Arduino Library -version=2.1.0 +version=2.1.1 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=An Arduino library to make use of the Qwiic and Qwiic Mini AS7331 Spectral UV Sensor From 3a43ef51b82073e3246058e9b804b95d75d9cbb0 Mon Sep 17 00:00:00 2001 From: Kirk Benell Date: Sun, 16 Feb 2025 13:32:22 -0700 Subject: [PATCH 21/39] add version qualifier on toolkit dependancy; bumped version --- library.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library.properties b/library.properties index 82f057b..89cdc77 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun AS7331 Arduino Library -version=2.1.1 +version=2.1.2 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=An Arduino library to make use of the Qwiic and Qwiic Mini AS7331 Spectral UV Sensor @@ -7,5 +7,5 @@ paragraph= category=Sensors url=https://github.com/sparkfun/SparkFun_AS7331_Arduino_Library architectures=* -depends=SparkFun Toolkit +depends=SparkFun Toolkit (<1.0.0) includes=SparkFun_AS7331.h From 2db54a4354829f375c1b7aa68306de17a9f9b67f Mon Sep 17 00:00:00 2001 From: Kirk Benell Date: Sun, 16 Feb 2025 13:43:17 -0700 Subject: [PATCH 22/39] enable /setup initial doxygen for github pages files --- .gitmodules | 3 + docs/doxygen/doxygen-awesome-css | 1 + docs/doxygen/doxygen-config | 2818 +++++++++++++++++++++++ docs/doxygen/doxygen-custom/custom.css | 21 + docs/doxygen/doxygen-custom/header.html | 126 + docs/images/sfe_flame.png | Bin 0 -> 3832 bytes 6 files changed, 2969 insertions(+) create mode 100644 .gitmodules create mode 160000 docs/doxygen/doxygen-awesome-css create mode 100644 docs/doxygen/doxygen-config create mode 100644 docs/doxygen/doxygen-custom/custom.css create mode 100644 docs/doxygen/doxygen-custom/header.html create mode 100644 docs/images/sfe_flame.png diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a8d0052 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "docs/doxygen/doxygen-awesome-css"] + path = docs/doxygen/doxygen-awesome-css + url = https://github.com/jothepro/doxygen-awesome-css.git diff --git a/docs/doxygen/doxygen-awesome-css b/docs/doxygen/doxygen-awesome-css new file mode 160000 index 0000000..5b27b3a --- /dev/null +++ b/docs/doxygen/doxygen-awesome-css @@ -0,0 +1 @@ +Subproject commit 5b27b3a747ca1e559fa54149762cca0bad6036fb diff --git a/docs/doxygen/doxygen-config b/docs/doxygen/doxygen-config new file mode 100644 index 0000000..d421d7f --- /dev/null +++ b/docs/doxygen/doxygen-config @@ -0,0 +1,2818 @@ +# Doxyfile 1.10.0 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "SparkFun Spectral UV Sensor - AS7331" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +# PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Library for the SparkFun Spectral UV Sensor - AS7331" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = ./docs/images/sfe_flame.png + +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./docs + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = YES + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = src \ + README.md \ + docs + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.h \ + *.hpp \ + *.markdown \ + *.md \ + *.dox + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = docs/doxygen \ + docs/html + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# ANamespace::AClass, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = docs/images + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = README.md + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# multi-line macros, enums or list initialized variables directly into the +# documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = ./docs/doxygen/doxygen-custom/header.html + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = docs/doxygen/doxygen-awesome-css/doxygen-awesome.css \ + docs/doxygen/doxygen-awesome-css/doxygen-awesome-sidebar-only.css \ + docs/doxygen/doxygen-custom/custom.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 209 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 255 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 113 + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 12 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 350 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /