diff --git a/drivers/include/opt3001.h b/drivers/include/opt3001.h index d2248eeb5863..3429045791a5 100644 --- a/drivers/include/opt3001.h +++ b/drivers/include/opt3001.h @@ -14,6 +14,7 @@ * @file opt3001.h * @brief driver for OPT3001 sensor * @author Oleg Artamonov [info@unwds.com] + * @author Dmitriy Faychuk [checordog@gmail.com] */ #ifndef OPT3001_H_ #define OPT3001_H_ @@ -40,7 +41,9 @@ #define OPT3001_CFG_FC_4 0x0200 #define OPT3001_CFG_FC_8 0x0300 #define OPT3001_CFG_MASK 0x0400 +#define OPT3001_CFG_POLNEG 0x0000 #define OPT3001_CFG_POLPOS 0x0800 +#define OPT3001_CFG_HYSTER 0x0000 #define OPT3001_CFG_LATCH 0x1000 #define OPT3001_CFG_FLAGL 0x2000 #define OPT3001_CFG_FLAGH 0x4000 @@ -54,6 +57,7 @@ #define OPT3001_CFG_RNAUTO 0x00C0 #define OPT3001_CFG (OPT3001_CFG_FC_1 | OPT3001_CFG_SHOT | OPT3001_CFG_100MS | OPT3001_CFG_RNAUTO ) +#define OPT3001_CFG_INT (OPT3001_CFG_CONT | OPT3001_CFG_800MS | OPT3001_CFG_RNAUTO) #define OPT3001_CFG_DEFAULT 0x10C8 /** @@ -61,10 +65,49 @@ */ #define OPT3001_REG_RESULT 0x00 #define OPT3001_REG_CONFIG 0x01 +#define OPT3001_REG_LOW 0x02 +#define OPT3001_REG_HIGH 0x03 #define OPT3001_REG_ID 0x7E #define OPT3001_CHIP_ID 0x4954 #define OPT3001_REG_CONFIG_MASK 0xFE1F + +/** + * @brief Enum that defines the OPT3001 fault counter states + */ +typedef enum { + OPT3001_FC_1 = (OPT3001_CFG_FC_1), /**< Threshold of faults is 1 */ + OPT3001_FC_2 = (OPT3001_CFG_FC_2), /**< Threshold of faults is 2 */ + OPT3001_FC_4 = (OPT3001_CFG_FC_4), /**< Threshold of faults is 4 */ + OPT3001_FC_8 = (OPT3001_CFG_FC_8) /**< Threshold of faults is 8 */ +} opt3001_fault_counter_t; + +/** + * @brief Enum that defines the comparison modes of the OPT3001 Interrupt Reporting Mechanism + */ +typedef enum { + OPT3001_LATCHED = (OPT3001_CFG_LATCH), /**< Latched Window-Style Comparison Mode, requires manual clearing of the INT pin */ + OPT3001_TRANSPARENT = (OPT3001_CFG_HYSTER) /**< Transparent Hysteresis-Style Comparison Mode */ +} opt3001_cmp_mode_t; + + +/** + * @brief Enum that defines the OPT3001 end of conversion mode, which allow INT pin going active on every measurement completion + */ +typedef enum { + OPT3001_NO_EOC, /**< Disabled */ + OPT3001_EOC /**< Enabled */ +} opt3001_eoc_mode_t; + +/** + * @brief Enum that defines the OPT3001 polarity or active state of the INT pin + */ +typedef enum { + OPT3001_IRQ_ON_LOW = (OPT3001_CFG_POLNEG), /**< Active state will be LOW */ + OPT3001_IRQ_ON_HIGH = (OPT3001_CFG_POLPOS) /**< Active state will be HIGH */ +} opt3001_pol_t; + + /** * @brief Structure that holds the OPT3001 driver internal state and parameters */ @@ -82,8 +125,8 @@ typedef struct { * @brief OPT3001 driver initialization routine * @note Corresponding I2C peripheral MUST be initialized before * - * @param[out] dev device structure pointer - * @param[in] param OPT3001 driver parameters, data will be copied into device parameters + * @param[out] dev device structure pointer + * @param[in] param OPT3001 driver parameters, data will be copied into device parameters * * @return 0 if initialization succeeded * @return <0 in case of error @@ -93,8 +136,8 @@ int opt3001_init(opt3001_t *dev); /** * @brief Get OPT3001 measure * - * @param[in] dev pointer to the initialized OPT3001 device - * @param[in] measure pointer to the allocated memory + * @param[in] dev pointer to the initialized OPT3001 device + * @param[in] measure pointer to the allocated memory * * @retval 0 for success * @@ -108,4 +151,34 @@ int opt3001_init(opt3001_t *dev); */ uint32_t opt3001_measure(opt3001_t *dev, opt3001_measure_t *measure); +/** + * @brief Init opt3001 Interrupt Reporting Mechanism + * + * @param[in] dev pointer to the initialized OPT3001 device + * @param[in] cmp comparison modes of the IRM + * @param[in] eoc end of conversion mode of the IRM + * @param[in] pol polarity of the INT pin + * @param[in] flt fault counter threshold + * @param[in] high HIGH limit value [0.01 - 83865.6], obviously should be higher than LOW + * @param[in] low LOW limit value [0.01 - 83865.6], obviously should be lower than HIGH + * + * @return 0 if succeeded + * + */ +int opt3001_init_int(opt3001_t *dev, opt3001_cmp_mode_t cmp, opt3001_eoc_mode_t eoc, + opt3001_pol_t pol, opt3001_fault_counter_t flt, + uint32_t high, uint32_t low); + +/** + * @brief OPT3001 INTpin/FlagH/FlagL clearing routine + * @note Only works with Latched Window-style comparison mode + * + * @param[in] dev pointer to the initialized OPT3001 device + * + * @return 0 if succeeded + * + */ +int opt3001_clear_int(opt3001_t *dev); + + #endif /* OPT3001_H_ */ diff --git a/drivers/opt3001/opt3001.c b/drivers/opt3001/opt3001.c index ee16a6a5afb4..a506e073942d 100644 --- a/drivers/opt3001/opt3001.c +++ b/drivers/opt3001/opt3001.c @@ -12,7 +12,8 @@ * @{ * @file opt3001.c * @brief basic driver for OPT3001 sensor - * @authoh Oleg Artamonov [info@unwds.com] + * @author Oleg Artamonov [info@unwds.com] + * @author Dmitriy Faychuk [checordog@gmail.com] */ @@ -145,6 +146,89 @@ uint32_t opt3001_measure(opt3001_t *dev, opt3001_measure_t *measure) } +uint8_t get_exp(uint32_t lum) +{ + uint8_t exp = 0; + while(lum > 41) { + lum /= 2.0; + exp++; + } + return exp; +} + +uint16_t convert(uint32_t lum) +{ + uint8_t exp = get_exp(lum); + uint16_t lsb_size_x100 = (1 << exp); + uint16_t binary_mantissa = (lum * 100) / lsb_size_x100; + uint16_t binary_exp = exp << 12; + uint16_t binary_limit = binary_exp | binary_mantissa; + return binary_limit; +} + + +/** + * @brief Init opt3001 Interrupt Reporting Mechanism + * + * @param[in] dev pointer to the initialized OPT3001 device + * @param[in] cmp comparison mode of the IRM + * @param[in] eoc end of conversion mode of the IRM + * @param[in] pol polarity of the INT pin + * @param[in] flt fault counter threshold + * @param[in] high HIGH limit value [0.01 - 83865.6], obviously should be higher than LOW + * @param[in] low LOW limit value [0.01 - 83865.6], obviously should be lower than HIGH + * + * @return 0 if succeeded + * + */ +int opt3001_init_int(opt3001_t *dev, opt3001_cmp_mode_t cmp, opt3001_eoc_mode_t eoc, + opt3001_pol_t pol, opt3001_fault_counter_t flt, + uint32_t high, uint32_t low) +{ + assert(dev != NULL); + i2c_acquire(dev->i2c); + + uint16_t cfg = OPT3001_CFG_INT | cmp | pol | flt; + i2c_write_regs(dev->i2c, OPT3001_ADDRESS, OPT3001_REG_CONFIG, (char *)&cfg, 2); + + uint16_t high_limit = convert(high); + /* swap bytes, as OPT3001 expects MSB first and I2C driver sends LSB first */ + high_limit = ((high_limit >> 8) | (high_limit << 8)); + i2c_write_regs(dev->i2c, OPT3001_ADDRESS, OPT3001_REG_HIGH, (char *)&high_limit, 2); + + uint16_t low_limit = convert(low); + if(eoc == OPT3001_EOC) + low_limit = (low_limit & 0x3FFF) | 0xC000; // Clear two most significant bits and set them to 11b. + /* swap bytes, as OPT3001 expects MSB first and I2C driver sends LSB first */ + low_limit = ((low_limit >> 8) | (low_limit << 8)); + i2c_write_regs(dev->i2c, OPT3001_ADDRESS, OPT3001_REG_LOW, (char *)&low_limit, 2); + + i2c_release(dev->i2c); + return 0; +} + + +/** + * @brief OPT3001 INTpin/FlagH/FlagL clearing routine + * @note Only works with Latched Window-style comparison mode + * + * @param[in] dev pointer to the initialized OPT3001 device + * + * @return 0 if succeeded + * + */ +int opt3001_clear_int(opt3001_t *dev) +{ + assert(dev != NULL); + + i2c_acquire(dev->i2c); + uint16_t cfg = i2c_read_regs(dev->i2c, OPT3001_ADDRESS, OPT3001_REG_CONFIG, (char *)&cfg, 2); + i2c_release(dev->i2c); + + return 0; +} + + #ifdef __cplusplus } #endif diff --git a/examples/irq_limit_opt/Makefile b/examples/irq_limit_opt/Makefile new file mode 100644 index 000000000000..25eb8fb7e114 --- /dev/null +++ b/examples/irq_limit_opt/Makefile @@ -0,0 +1,25 @@ +# name of your application +APPLICATION = irq_limit_opt + +# If no BOARD is found in the environment, use this default: +BOARD ?= unwd-range-l1-r3 + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +USEMODULE += opt3001 +USEMODULE += xtimer + +FEATURES_REQUIRED += periph_i2c +FEATURES_REQUIRED += periph_rtc +FEATURES_REQUIRED += periph_gpio + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/irq_limit_opt/main.c b/examples/irq_limit_opt/main.c new file mode 100644 index 000000000000..1ff0203c2cac --- /dev/null +++ b/examples/irq_limit_opt/main.c @@ -0,0 +1,63 @@ +#include +#include "periph/gpio.h" +#include "periph/pm.h" +#include "thread.h" +#include "opt3001.h" +#include "xtimer.h" + +#define ENABLE_DEBUG (1) +#include "debug.h" + +#define LED_CONTROL_STACK_SIZE (1024) + +static kernel_pid_t led_control_pid; +static msg_t led_control_msg; + +static void* led_control(void * arg) { + (void)arg; + msg_t message; + while(1) { + msg_receive(&message); + gpio_toggle(GPIO_PIN(PORT_B, 0)); + DEBUG("%s\n", "Irq spotted!" ); + } + return NULL; +} + +static opt3001_t opt3001; + +static void interrupt_handler(void* arg) { + (void)arg; + msg_send(&led_control_msg, led_control_pid); + opt3001_clear_int(&opt3001); +} + + +int main(void) +{ + xtimer_init(); + + pm_init(); + pm_prevent_sleep = 1; + + puts("Led toggling program. Control the led by irq's sended from opt3001"); + + printf("You are running RIOT on a(n) %s board.\n", RIOT_BOARD); + printf("This board features a(n) %s MCU.\n", RIOT_MCU); + + gpio_init(GPIO_PIN(PORT_B, 0), GPIO_OUT); + gpio_set(GPIO_PIN(PORT_B, 0)); + + opt3001.i2c = 1; + opt3001_init(&opt3001); + opt3001_init_int(&opt3001, OPT3001_LATCHED, OPT3001_NO_EOC, OPT3001_IRQ_ON_LOW, OPT3001_FC_1, 10000, 100); + gpio_init_int(GPIO_PIN(PORT_A,7), GPIO_IN_PU, GPIO_FALLING, interrupt_handler, NULL); + + char stack[LED_CONTROL_STACK_SIZE]; + led_control_pid = thread_create(stack, LED_CONTROL_STACK_SIZE, + THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST, + led_control, NULL, "Toggling the led"); + + return 0; +} +// \ No newline at end of file