diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 3d2e780b56..e3132fa491 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -568,6 +568,10 @@ ifneq (,$(filter opt3001,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter pca9633,$(USEMODULE))) + FEATURES_REQUIRED += periph_i2c +endif + ifneq (,$(filter pca9685,$(USEMODULE))) FEATURES_REQUIRED += periph_gpio FEATURES_REQUIRED += periph_i2c diff --git a/drivers/Makefile.include b/drivers/Makefile.include index e9d78958ae..7b5a43b897 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -272,6 +272,10 @@ ifneq (,$(filter opt3001,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/opt3001/include endif +ifneq (,$(filter pca9633,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/pca9633/include +endif + ifneq (,$(filter pca9685,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/pca9685/include endif diff --git a/drivers/include/pca9633.h b/drivers/include/pca9633.h new file mode 100644 index 0000000000..59c5cd09ee --- /dev/null +++ b/drivers/include/pca9633.h @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2020 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_pca9633 PCA9633 I2C PWM controller + * @ingroup drivers_actuators + * @brief Device driver for the NXP PCA9633 + * + * @{ + * + * @author Hendrik van Essen + * @file + */ + +#ifndef PCA9633_H +#define PCA9633_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "stdbool.h" +#include "periph/i2c.h" + +#include "pca9633_regs.h" + +/** + * @brief Blinking period with a maximum duration of ~10.73 s + */ +#define PCA9633_BLINKING_PERIOD_MAX_MS (10625) + +/** + * @brief Ration between on/ off in blinking mode is balanced. + * + * 128 = 255 / 2 + */ +#define PCA9633_BLINKING_RATIO_BALANCED 128 + +/** + * @brief PCA9633 device initialization parameters + */ +typedef struct { + i2c_t i2c_dev; /**< I2C device */ + uint16_t i2c_addr; /**< I2C address of device */ + + uint8_t reg_pwm_red; /**< Register for red color */ + uint8_t reg_pwm_green; /**< Register for green color */ + uint8_t reg_pwm_blue; /**< Register for blue color */ + uint8_t reg_pwm_amber; /**< Register for amber color */ + + bool has_amber_channel; /**< Whether PCA9633 has fourth channel */ +} pca9633_params_t; + +/** + * @brief PCA9633 PWM device data structure type + */ +typedef struct { + pca9633_params_t params; /**< Device initialization parameters */ + uint8_t stored_reg_ledout; /**< Stored register content of LEDOUT */ +} pca9633_t; + +/** + * @brief PCA9633 driver error codes + */ +typedef enum { + PCA9633_OK = 0, /**< Success */ + PCA9633_ERROR_I2C = 1, /**< I2C communication error */ +} pca9685_error_t; + +/** + * @brief PCA9633 PWM channel definitions + */ +typedef enum { + PCA9633_PWM_CHANNEL_0 = PCA9633_REG_PWM0, /**< PWM channel 0 */ + PCA9633_PWM_CHANNEL_1 = PCA9633_REG_PWM1, /**< PWM channel 1 */ + PCA9633_PWM_CHANNEL_2 = PCA9633_REG_PWM2, /**< PWM channel 2 */ + PCA9633_PWM_CHANNEL_3 = PCA9633_REG_PWM3, /**< PWM channel 3 */ +} pca9633_pwm_channel_t; + +/** + * @brief LED driver output state, LEDOUT (page 14, below table 13) + */ +typedef enum { + /** + * @brief LED driver x is off + */ + PCA9633_LDR_STATE_OFF, + + /** + * @brief LED driver x is fully on (individual brightness and group + * dimming/ blinking not controlled) + */ + PCA9633_LDR_STATE_ON, + + /** + * @brief LED driver x individual brightness can be controlled through its + * PWMx register + */ + PCA9633_LDR_STATE_IND, + + /** + * @brief LED driver x individual brightness and group dimming/ blinking can + * be controlled through its PWMx register and the GRPPWM registers. + * If using PCA9633_LDR_STATE_IND_GRP the controller takes the + * minimum value of PWM* and GRPPWM register + */ + PCA9633_LDR_STATE_IND_GRP, +} pca9633_ldr_state_t; + +/** + * @brief Auto-Increment options (page 10, table 6) + */ +typedef enum { + /** + * @brief No Auto-Increment + */ + PCA9633_AI_DISABLED, + + /** + * @brief Auto-Increment for all registers. D3, D2, D1, D0 roll over to + * ‘0000’ after the last register (1100) is accessed. + */ + PCA9633_AI_ALL, + + /** + * @brief Auto-Increment for individual brightness registers only. + * D3, D2, D1, D0 roll over to ‘0010’ after the last register (0101) + * is accessed. + */ + PCA9633_AI_IND, + + /** + * @brief Auto-Increment for global control registers only. D3, D2, D1, D0 + * roll over to ‘0110’ after the last register (0111) is accessed. + */ + PCA9633_AI_GBL, + + /** + * @brief Auto-Increment for individual and global control registers only. + * D3, D2, D1, D0 roll over to ‘0010’ after the last register (0111) + * is accessed. + */ + PCA9633_AI_IND_GBL, +} pca9633_auto_inc_option_t; + +/** + * @brief PCA9633 group control modes + */ +typedef enum { + /** + * @brief Control mode for blinking + */ + PCA9633_GROUP_CONTROL_MODE_BLINKING, + + /** + * @brief Control mode for dimming + */ + PCA9633_GROUP_CONTROL_MODE_DIMMING, +} pca9633_group_control_mode_t; + +/** + * @brief Initialization. + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] params Parameters for device initialization + * + * @return PCA9633_OK on success + * @return -PCA9633_ERROR_I2C if acquiring of I2C bus fails + * @return -EIO When slave device doesn't ACK the byte + * @return -ENXIO When no devices respond on the address sent on the bus + * @return -ETIMEDOUT When timeout occurs before device's response + * @return -EINVAL When an invalid argument is given + * @return -EOPNOTSUPP When MCU driver doesn't support the flag operation + * @return -EAGAIN When a lost bus arbitration occurs + */ +int pca9633_init(pca9633_t *dev, const pca9633_params_t *params); + +/** + * @brief Turn on all LEDs. Restores settings saved at pca9633_turn_off(). + * + * WARNING: If you call pca9633_turn_off() twice, without calling + * pca9633_turn_on() in between, then the restored state will be + * PCA9633_LDR_STATE_OFF! + * + * @param[in] dev Device descriptor of the PCA9633 + */ +void pca9633_turn_on(pca9633_t* dev); + +/** + * @brief Turn off all LEDs. Saves current settings for pca9633_turn_on(). + * For power saving, see pca9633_sleep(). + * + * WARNING: If you call pca9633_turn_off() twice, without calling + * pca9633_turn_on() in between, then the restored state will be + * PCA9633_LDR_STATE_OFF! + * + * @param[in] dev Device descriptor of the PCA9633 + */ +void pca9633_turn_off(pca9633_t* dev); + +/** + * @brief Switch to normal mode. + * + * @param[in] dev Device descriptor of the PCA9633 + */ +void pca9633_wakeup(pca9633_t* dev); + +/** + * @brief Switch to low power mode. + * + * @param[in] dev Device descriptor of the PCA9633 + */ +void pca9633_sleep(pca9633_t* dev); + +/** + * @brief Set individual PWM signal for a given channel. + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] pwm_channel PWM channel + * @param[in] pwm PWM value + */ +void pca9633_set_pwm(pca9633_t* dev, + pca9633_pwm_channel_t pwm_channel, uint8_t pwm); + +/** + * @brief Set global PWM signal. + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] pwm PWM value + */ +void pca9633_set_grp_pwm(pca9633_t* dev, uint8_t pwm); + +/** + * @brief Set up values for blinking mode. Blinking mode needs to be activated + * manually by calling + * pca9633_set_group_control_mode(GROUP_CONTROL_MODE_BLINKING). + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] blink_period_ms Period in ms for one blink (turning off and on). + * Maximum period possible is + * PCA9633_BLINKING_PERIOD_MAX_MS ≈ 10.73 s. All + * values above this maximum will we capped to it. + * @param[in] on_off_ratio Value between 0 and 255, where e.g. a value of + * 64 (255/4) means 1/4 of the time the LEDs are on + * and 3/4 of the time the LEDs are off. + */ +void pca9633_set_blinking(pca9633_t* dev, uint16_t blink_period_ms, + uint8_t on_off_ratio); + +/** + * @brief Set PWM values for RGB. + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] r Value for red color channel + * @param[in] g Value for green color channel + * @param[in] b Value for blue color channel + */ +void pca9633_set_rgb(pca9633_t* dev, uint8_t r, uint8_t g, uint8_t b); + +/** + * @brief Set PWM values for RGBA. + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] r Value for red color channel + * @param[in] g Value for green color channel + * @param[in] b Value for blue color channel + * @param[in] w Value for amber color channel + */ +void pca9633_set_rgba(pca9633_t* dev, uint8_t r, uint8_t g, uint8_t b, uint8_t w); + +/** + * @brief Set the LED driver output state for a given channel. + * There are four states: + * - PCA9633_LDR_STATE_OFF + * - PCA9633_LDR_STATE_ON + * - PCA9633_LDR_STATE_IND + * - PCA9633_LDR_STATE_IND_GRP + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] state One of the four possible states + * @param[in] pwm_channel PWM channel belonging to LDR + */ +void pca9633_set_ldr_state(pca9633_t* dev, + pca9633_ldr_state_t state, + pca9633_pwm_channel_t pwm_channel); + +/** + * @brief Set the LED driver output state for all channels. + * There are four states: + * - PCA9633_LDR_STATE_OFF + * - PCA9633_LDR_STATE_ON + * - PCA9633_LDR_STATE_IND + * - PCA9633_LDR_STATE_IND_GRP + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] state One of the four possible states + */ +void pca9633_set_ldr_state_all(pca9633_t* dev, pca9633_ldr_state_t state); + +/** + * @brief Set an option for auto increment. + * There are five options: + * - PCA9633_AI_DISABLED + * - PCA9633_AI_ALL + * - PCA9633_AI_IND + * - PCA9633_AI_GBL + * - PCA9633_AI_IND_GBL + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] option One of the possible five options + */ +void pca9633_set_auto_increment(pca9633_t* dev, pca9633_auto_inc_option_t option); + +/** + * @brief Set the group control mode. + * There are two modes: + * - PCA9633_GROUP_CONTROL_MODE_BLINKING + * - PCA9633_GROUP_CONTROL_MODE_DIMMING + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] mode One of the two possible modes + */ +void pca9633_set_group_control_mode(pca9633_t* dev, + pca9633_group_control_mode_t mode); + +#ifdef __cplusplus +} +#endif + +#endif /* PCA9633_H */ +/** @} */ diff --git a/drivers/pca9633/Makefile b/drivers/pca9633/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/pca9633/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/pca9633/include/pca9633_params.h b/drivers/pca9633/include/pca9633_params.h new file mode 100644 index 0000000000..233d544a58 --- /dev/null +++ b/drivers/pca9633/include/pca9633_params.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2020 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_actuators + * @brief Default configuration for the PCA9633 I2C PWM controller + * + * @{ + * + * @author Hendrik van Essen + * @file + */ +#ifndef PCA9633_PARAMS_H +#define PCA9633_PARAMS_H + +#include + +#include "periph/i2c.h" + +#include "pca9633_regs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** +* @name Set default configuration parameters +* @{ +*/ +#ifndef PCA9633_PARAM_I2C_DEV +/** I2C device is I2C_DEV(0) */ +#define PCA9633_PARAM_I2C_DEV I2C_DEV(0) +#endif + +#ifndef PCA9633_PARAM_I2C_ADDR +/** I2C address of device is (0xc0 >> 1) */ +#define PCA9633_PARAM_I2C_ADDR (0xc0 >> 1) +#endif + +#ifndef PCA9633_PARAM_REG_PWM_RED +/** Register for red color is PCA9633_REG_PWM2 */ +#define PCA9633_PARAM_REG_PWM_RED PCA9633_REG_PWM2 +#endif + +#ifndef PCA9633_PARAM_REG_PWM_GREEN +/** Register for green color is PCA9633_REG_PWM1 */ +#define PCA9633_PARAM_REG_PWM_GREEN PCA9633_REG_PWM1 +#endif + +#ifndef PCA9633_PARAM_REG_PWM_BLUE +/** Register for blue color is PCA9633_REG_PWM0 */ +#define PCA9633_PARAM_REG_PWM_BLUE PCA9633_REG_PWM0 +#endif + +#ifndef PCA9633_PARAM_REG_PWM_AMBER +/** Register for amber color is not given (0) */ +#define PCA9633_PARAM_REG_PWM_AMBER 0 +#endif + +#ifndef PCA9633_PARAM_HAS_AMBER_CHANNEL +/** PCA9633 has no connected channel for amber (false) */ +#define PCA9633_PARAM_HAS_AMBER_CHANNEL false +#endif + +#ifndef PCA9633_PARAMS +#define PCA9633_PARAMS \ + { \ + .i2c_dev = PCA9633_PARAM_I2C_DEV, \ + .i2c_addr = PCA9633_PARAM_I2C_ADDR, \ + .reg_pwm_red = PCA9633_PARAM_REG_PWM_RED, \ + .reg_pwm_green = PCA9633_PARAM_REG_PWM_GREEN, \ + .reg_pwm_blue = PCA9633_PARAM_REG_PWM_BLUE, \ + .reg_pwm_amber = PCA9633_PARAM_REG_PWM_AMBER, \ + .has_amber_channel = PCA9633_PARAM_HAS_AMBER_CHANNEL \ + } +#endif /* PCA9633_PARAMS */ +/**@}*/ + +/** + * @brief Allocate some memory to store the actual configuration + */ +static const pca9633_params_t pca9633_params[] = +{ + PCA9633_PARAMS +}; + +#ifdef __cplusplus +} +#endif + +#endif /* PCA9633_PARAMS_H */ +/** @} */ diff --git a/drivers/pca9633/include/pca9633_regs.h b/drivers/pca9633/include/pca9633_regs.h new file mode 100644 index 0000000000..ed46002d41 --- /dev/null +++ b/drivers/pca9633/include/pca9633_regs.h @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2020 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_pca9633 + * @brief Register definitions for the PCA9633 I2C PWM controller + * @author Hendrik van Essen + * @file + * @{ + */ + +#ifndef PCA9633_REGS_H +#define PCA9633_REGS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Register definitions (page 11, table 7) */ + +/** + * @brief Mode register 1 + */ +#define PCA9633_REG_MODE1 0x00 + +/** + * @brief Mode register 2 + */ +#define PCA9633_REG_MODE2 0x01 + +/** + * @brief Brightness control LED0 + */ +#define PCA9633_REG_PWM0 0x02 + +/** + * @brief Brightness control LED1 + */ +#define PCA9633_REG_PWM1 0x03 + +/** + * @brief Brightness control LED2 + */ +#define PCA9633_REG_PWM2 0x04 + +/** + * @brief Brightness control LED3 + */ +#define PCA9633_REG_PWM3 0x05 + +/** + * @brief Group duty cycle control + */ +#define PCA9633_REG_GRPPWM 0x06 + +/** + * @brief Group frequency + */ +#define PCA9633_REG_GRPFREQ 0x07 + +/** + * @brief LED output state + */ +#define PCA9633_REG_LEDOUT 0x08 + +/** + * @brief I2C-bus subaddress 1 + */ +#define PCA9633_REG_SUBADR1 0x09 + +/** + * @brief I2C-bus subaddress 2 + */ +#define PCA9633_REG_SUBADR2 0x0A + +/** + * @brief I2C-bus subaddress 3 + */ +#define PCA9633_REG_SUBADR3 0x0B + +/** + * @brief LED All Call I2C-bus address + */ +#define PCA9633_REG_ALLCALLADR 0x0C + + + +/* Bits in REG_MODE1 (page 12, table 8) */ + +/** + * @brief Bit for register Auto-Increment + * 0 = disabled + * 1 = enabled + */ +#define PCA9633_BIT_AI2 7 + +/** + * @brief Bit for Auto-Increment bit1 + */ +#define PCA9633_BIT_AI1 6 + +/** + * @brief Bit for Auto-Increment bit0 + */ +#define PCA9633_BIT_AI0 5 + +/** + * @brief 0 = Normal mode + * 1 = Low power mode. Oscillator off + */ +#define PCA9633_BIT_SLEEP 4 + +/** + * @brief 0 = PCA9633 does not respond to I2C-bus subaddress 1 + * 1 = PCA9633 responds to I2C-bus subaddress 1 + */ +#define PCA9633_BIT_SUB1 3 + +/** + * @brief 0 = PCA9633 does not respond to I2C-bus subaddress 2 + * 1 = PCA9633 responds to I2C-bus subaddress 2 + */ +#define PCA9633_BIT_SUB2 2 + +/** + * @brief 0 = PCA9633 does not respond to I2C-bus subaddress 3 + * 1 = PCA9633 responds to I2C-bus subaddress 3 + */ +#define PCA9633_BIT_SUB3 1 + +/** + * @brief 0 = PCA9633 does not respond to LED All Call I2C-bus address + * 1 = PCA9633 responds to LED All Call I2C-bus address + */ +#define PCA9633_BIT_ALLCALL 0 + + + +/* Bits in REG_MODE2 (page 12-13, table 9) */ + +/** + * @brief Bit for group control; 0=dimming, 1=blinking + */ +#define PCA9633_BIT_DMBLNK 5 + +/** + * @brief 0 = Output logic state not inverted. Value to use when no external driver used + * 1 = Output logic state inverted. Value to use when external driver used + */ +#define PCA9633_BIT_INVRT 4 + +/** + * @brief 0 = Outputs change on STOP command + * 1 = Outputs change on ACK + */ +#define PCA9633_BIT_OCH 3 + +/** + * @brief 0 = The 4 LED outputs are configured with an open-drain structure + * 1 = The 4 LED outputs are configured with a totem pole structure + */ +#define PCA9633_BIT_OUTDRV 2 + +/** + * @brief See PCA9633_BIT_OUTNE0 + */ +#define PCA9633_BIT_OUTNE1 1 + +/** + * @brief 00 = When OE = 1 (output drivers not enabled), LEDn = 0. + * 01* = When OE = 1 (output drivers not enabled): + * LEDn = 1 when OUTDRV = 1 + * LEDn = high-impedance when OUTDRV = 0 (same as OUTNE[1:0] = 10) + * 10 When OE = 1 (output drivers not enabled), LEDn = high-impedance. + * 11 reserved + */ +#define PCA9633_BIT_OUTNE0 0 + + + +/* Bits in REG_LEDOUT (page 14, table 13) */ + +/** + * @brief Lower of two bits for LDR3 + */ +#define PCA9633_BIT_LDR3 6 + +/** + * @brief Lower of two bits for LDR2 + */ +#define PCA9633_BIT_LDR2 4 + +/** + * @brief Lower of two bits for LDR1 + */ +#define PCA9633_BIT_LDR1 2 + +/** + * @brief Lower of two bits for LDR0 + */ +#define PCA9633_BIT_LDR0 0 + +#ifdef __cplusplus +} +#endif + +#endif /* PCA9633_REGS_H */ +/** @} */ diff --git a/drivers/pca9633/pca9633.c b/drivers/pca9633/pca9633.c new file mode 100644 index 0000000000..68e2b21e91 --- /dev/null +++ b/drivers/pca9633/pca9633.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2020 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_pca9633 + * @brief Device driver for the PCA9633 I2C PWM controller + * @author Hendrik van Essen + * @file + * @{ + */ + +#include + +#include "pca9633.h" +#include "pca9633_regs.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Write data to a register. + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] reg Register address to write to + * @param[in] data Data to write + * + * @return 0 on success + * @return -PCA9633_ERROR_I2C if acquiring of I2C bus fails + * @return -EIO When slave device doesn't ACK the byte + * @return -ENXIO When no devices respond on the address sent on the bus + * @return -ETIMEDOUT When timeout occurs before device's response + * @return -EINVAL When an invalid argument is given + * @return -EOPNOTSUPP When MCU driver doesn't support the flag operation + * @return -EAGAIN When a lost bus arbitration occurs + */ +static int _write_reg(pca9633_t* dev, uint8_t reg, uint8_t data); + +/** + * @brief Read data from a register. + * + * @param[in] dev Device descriptor of the PCA9633 + * @param[in] reg Register address to read from + * @param[out] data Byte read from given registerAddress + * + * @return 0 on success + * @return -PCA9633_ERROR_I2C if acquiring of I2C bus fails + * @return -EIO When slave device doesn't ACK the byte + * @return -ENXIO When no devices respond on the address sent on the bus + * @return -ETIMEDOUT When timeout occurs before device's response + * @return -EINVAL When an invalid argument is given + * @return -EOPNOTSUPP When MCU driver doesn't support the flag operation + * @return -EAGAIN When a lost bus arbitration occurs + */ +static int _read_reg(pca9633_t* dev, uint8_t reg, uint8_t* data); + +int pca9633_init(pca9633_t *dev, const pca9633_params_t *params) +{ + assert(dev); + assert(params); + + dev->params = *params; + + i2c_init(dev->params.i2c_dev); + + int rc = _write_reg(dev, PCA9633_REG_MODE1, 0x0); + _write_reg(dev, PCA9633_REG_MODE2, 0x0); + + if (rc != PCA9633_OK) { + return rc; + } + + return PCA9633_OK; +} + +void pca9633_wakeup(pca9633_t* dev) +{ + uint8_t reg; + _read_reg(dev, PCA9633_REG_MODE1, ®); + reg = reg & ~(1 << PCA9633_BIT_SLEEP); + + _write_reg(dev, PCA9633_REG_MODE1, reg); +} + +void pca9633_sleep(pca9633_t* dev) +{ + uint8_t reg; + _read_reg(dev, PCA9633_REG_MODE1, ®); + reg = reg | (1 << PCA9633_BIT_SLEEP); + + _write_reg(dev, PCA9633_REG_MODE1, reg); +} + +void pca9633_turn_on(pca9633_t* dev) +{ + _write_reg(dev, PCA9633_REG_LEDOUT, dev->stored_reg_ledout); +} + +void pca9633_turn_off(pca9633_t* dev) +{ + _read_reg(dev, PCA9633_REG_LEDOUT, &dev->stored_reg_ledout); + _write_reg(dev, PCA9633_REG_LEDOUT, PCA9633_LDR_STATE_OFF); +} + +void pca9633_set_pwm(pca9633_t* dev, + pca9633_pwm_channel_t pwm_channel, uint8_t pwm) +{ + _write_reg(dev, pwm_channel, pwm); +} + +void pca9633_set_grp_pwm(pca9633_t* dev, uint8_t pwm) +{ + _write_reg(dev, PCA9633_REG_GRPPWM, pwm); +} + +void pca9633_set_blinking(pca9633_t* dev, uint16_t blink_period_ms, + uint8_t on_off_ratio) +{ + /* frequency of 24 Hz is used: */ + uint16_t blink_period = (24 * blink_period_ms) / 1000; + if (blink_period > 255) { + blink_period = 255; + } + + _write_reg(dev, PCA9633_REG_GRPFREQ, (uint8_t) blink_period); + _write_reg(dev, PCA9633_REG_GRPPWM, on_off_ratio); +} + +void pca9633_set_rgb(pca9633_t* dev, uint8_t r, uint8_t g, uint8_t b) +{ + pca9633_set_pwm(dev, dev->params.reg_pwm_red, r); + pca9633_set_pwm(dev, dev->params.reg_pwm_green, g); + pca9633_set_pwm(dev, dev->params.reg_pwm_blue, b); +} + +void pca9633_set_rgba(pca9633_t* dev, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + pca9633_set_rgb(dev, r, g, b); + + if (dev->params.has_amber_channel) { + pca9633_set_pwm(dev, dev->params.reg_pwm_amber, a); + } +} + +void pca9633_set_ldr_state(pca9633_t* dev, + pca9633_ldr_state_t state, pca9633_pwm_channel_t pwm_channel) +{ + uint8_t ldr_bit; + switch (pwm_channel) { + case PCA9633_PWM_CHANNEL_0: + ldr_bit = PCA9633_BIT_LDR0; + break; + case PCA9633_PWM_CHANNEL_1: + ldr_bit = PCA9633_BIT_LDR1; + break; + case PCA9633_PWM_CHANNEL_2: + ldr_bit = PCA9633_BIT_LDR2; + break; + case PCA9633_PWM_CHANNEL_3: + ldr_bit = PCA9633_BIT_LDR3; + break; + default: + return; + } + + uint8_t reg; + _read_reg(dev, PCA9633_REG_LEDOUT, ®); + + /* first clear both bits of ldr */ + reg = reg & ~(0b11 << ldr_bit); + + /* second set new state to specified ldr */ + reg |= (state << ldr_bit); + + _write_reg(dev, PCA9633_REG_LEDOUT, reg); +} + +void pca9633_set_ldr_state_all(pca9633_t* dev, pca9633_ldr_state_t state) +{ + uint8_t reg = (state << PCA9633_BIT_LDR3) + | (state << PCA9633_BIT_LDR2) + | (state << PCA9633_BIT_LDR1) + | (state << PCA9633_BIT_LDR0); + + _write_reg(dev, PCA9633_REG_LEDOUT, reg); +} + +void pca9633_set_auto_increment(pca9633_t* dev, pca9633_auto_inc_option_t option) +{ + uint8_t new_reg; + + switch (option) { + + case PCA9633_AI_ALL: + new_reg = (1 << PCA9633_BIT_AI2) + | (0 << PCA9633_BIT_AI1) + | (0 << PCA9633_BIT_AI0); + break; + + case PCA9633_AI_IND: + new_reg = (1 << PCA9633_BIT_AI2) + | (1 << PCA9633_BIT_AI1) + | (0 << PCA9633_BIT_AI0); + break; + + case PCA9633_AI_GBL: + new_reg = (1 << PCA9633_BIT_AI2) + | (0 << PCA9633_BIT_AI1) + | (1 << PCA9633_BIT_AI0); + break; + + case PCA9633_AI_IND_GBL: + new_reg = (1 << PCA9633_BIT_AI2) + | (1 << PCA9633_BIT_AI1) + | (1 << PCA9633_BIT_AI0); + break; + + case PCA9633_AI_DISABLED: + /* fall-thru */ + default: + new_reg = (0 << PCA9633_BIT_AI2) + | (0 << PCA9633_BIT_AI1) + | (0 << PCA9633_BIT_AI0); + break; + } + + _write_reg(dev, PCA9633_REG_MODE1, new_reg); +} + +void pca9633_set_group_control_mode(pca9633_t* dev, + pca9633_group_control_mode_t mode) +{ + uint8_t prev_reg; + _read_reg(dev, PCA9633_REG_MODE2, &prev_reg); + + switch (mode) { + + case PCA9633_GROUP_CONTROL_MODE_BLINKING: + _write_reg(dev, PCA9633_REG_MODE2, prev_reg | (1 << PCA9633_BIT_DMBLNK)); + break; + + case PCA9633_GROUP_CONTROL_MODE_DIMMING: + default: + _write_reg(dev, PCA9633_REG_MODE2, prev_reg & ~(1 << PCA9633_BIT_DMBLNK)); + break; + } +} + +int _write_reg(pca9633_t* dev, uint8_t reg, uint8_t data) +{ + i2c_t i2c_dev = dev->params.i2c_dev; + + if (i2c_acquire(i2c_dev) != 0) { + return -PCA9633_ERROR_I2C; + } + int rc = i2c_write_reg(i2c_dev, dev->params.i2c_addr, reg, data, 0); + i2c_release(i2c_dev); + + return rc; +} + +int _read_reg(pca9633_t* dev, uint8_t reg, uint8_t* data) +{ + i2c_t i2c_dev = dev->params.i2c_dev; + + if (i2c_acquire(i2c_dev) != 0) { + return -PCA9633_ERROR_I2C; + } + int rc = i2c_read_reg(i2c_dev, dev->params.i2c_addr, reg, data, 0); + i2c_release(i2c_dev); + + return rc; +}