16-channel, 12-bit PWM Fm I2C-bus LED controller
Diff: PCA9685.cpp
- Revision:
- 2:fa75aff130cc
- Parent:
- 0:1527da6e7c05
--- a/PCA9685.cpp Tue Nov 07 14:28:19 2017 +0000 +++ b/PCA9685.cpp Tue Nov 07 14:52:15 2017 +0000 @@ -0,0 +1,908 @@ +/** + * @brief PCA9685.h + * @details 16-channel, 12-bit PWM Fm+ I2C-bus LED controller. + * Functions file. + * + * + * @return NA + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre NaN. + * @warning NaN + * @pre This code belongs to AqueronteBlog ( http://unbarquero.blogspot.com ). + */ + +#include "PCA9685.h" + + +PCA9685::PCA9685 ( PinName sda, PinName scl, uint32_t addr, uint32_t freq ) + : i2c ( sda, scl ) + , PCA9685_Addr ( addr ) +{ + i2c.frequency( freq ); +} + + +PCA9685::~PCA9685() +{ +} + + + +/** + * @brief PCA9685_SoftReset ( void ) + * + * @details It performs a software reset. + * + * @param[in] NaN. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SoftReset. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre The device will be ready to be addressed again within + * the specified bus free time ( t_BUF ). + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SoftReset ( void ) +{ + char cmd = SWRST; + uint32_t aux; + + + + aux = i2c.write ( GENERAL_CALL_ADDRESS, &cmd, 1, false ); + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetMode ( PCA9685_mode1_sleep_t ) + * + * @details It configures the device in Low power mode or in Normal operation + * mode. + * + * @param[in] myMode: Sleep or Normal mode. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetMode. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre NaN. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetMode ( PCA9685_mode1_sleep_t myMode ) +{ + char cmd[] = { MODE1, 0 }; + uint32_t aux; + + + aux = i2c.write ( PCA9685_Addr, &cmd[0], 1, true ); + aux = i2c.read ( PCA9685_Addr, &cmd[1], 1 ); + cmd[1] &= ~MODE1_SLEEP_MASK; + cmd[1] |= myMode; + + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetPWM_Freq ( float ) + * + * @details It sets a new PWM frequency. + * + * @param[in] myNewFrequency: New PWM frequency. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetPWM_Freq. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre This library can ONLY work with the internal clock, otherwise + * PCA9685_INTERNAL_CLOCK must be changed in the header file. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetPWM_Freq ( float myNewFrequency ) +{ + char cmd[] = { MODE1, 0 }; + char prev_mode1 = 0; + uint32_t aux; + + + // The maximum PWM frequency is 1526 Hz and the minimum PWM frequency is 24 Hz. + if ( ( myNewFrequency < 24 ) || ( myNewFrequency > 1526 ) ) + return PCA9685_FAILURE; + + + // The device MUST be in SLEEP mode + aux = i2c.write ( PCA9685_Addr, &cmd[0], 1, true ); + aux = i2c.read ( PCA9685_Addr, &prev_mode1, 1 ); + cmd[1] = ( prev_mode1 | MODE1_SLEEP_ENABLED ); + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + // Calculate the new PWM frequency + if ( myNewFrequency == 24 ) + cmd[1] = 255; + else + cmd[1] = _MYROUND ( ( PCA9685_INTERNAL_CLOCK / ( PCA9685_ADC_STEPS * myNewFrequency ) ) - 1 ); + + + cmd[0] = PRE_SCALE; + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + // Restore the device's mode + cmd[0] = MODE1; + cmd[1] = prev_mode1; + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetPWM_DutyCycle ( PCA9685_led_channel_t , uint8_t , uint8_t ) + * + * @details It sets a new PWM duty cycle on the given LED ( channel ). + * + * @param[in] myLEDchannel: Chosen LED ( channel ). + * @param[in] myDelay: PWM delay. + * @param[in] myPWM_DutyCycle: PWM duty cycle. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetPWM_DutyCycle. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre Datasheet p.17 ( Example 1 and Example 2). + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetPWM_DutyCycle ( PCA9685_led_channel_t myLEDchannel, uint8_t myDelay, uint8_t myPWM_DutyCycle ) +{ + char cmd[] = { 0, 0 }; + uint32_t myAux = 0; + uint32_t aux; + + + // The range is from 0% up to 100%. + if ( ( myDelay > 100 ) || ( myPWM_DutyCycle > 100 ) ) + return PCA9685_FAILURE; + + + // Delay time cannot be 0% + if ( myDelay == 0 ) + myDelay = 1; + + + // DELAY TIME: LEDn_ON_L + LEDn_ON_H + myAux = _MYROUND ( ( myDelay / 100.0 ) * PCA9685_ADC_STEPS ) - 1; + + // LEDn_ON_L + cmd[0] = LED0_ON_L + ( myLEDchannel << 2 ); + cmd[1] = ( myAux & 0xFF ); + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDn_ON_H + cmd[0] = LED0_ON_H + ( myLEDchannel << 2 ); + + if ( myPWM_DutyCycle == 100 ) + cmd[1] = 0x10; // LEDn full ON + else + cmd[1] = ( ( myAux >> 8 ) & 0xFF ); + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + // LED OFF TIME: LEDn_OFF_L + LEDn_OFF_H + myAux += _MYROUND ( ( myPWM_DutyCycle / 100.0 ) * PCA9685_ADC_STEPS ); + + if ( ( myDelay + myPWM_DutyCycle ) <= 100 ) + myAux--; + else + myAux = ( myAux - 4096 ); + + + // LEDn_OFF_L + cmd[0] = LED0_OFF_L + ( myLEDchannel << 2 ); + cmd[1] = ( myAux & 0xFF ); + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDn_OFF_H + cmd[0] = LED0_OFF_H + ( myLEDchannel << 2 ); + + if ( myPWM_DutyCycle == 0 ) + cmd[1] = 0x10; // LEDn full OFF + else + cmd[1] = ( ( myAux >> 8 ) & 0xFF ); + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetPWM_DutyCycle_AllLEDs ( uint8_t , uint8_t ) + * + * @details It sets a new PWM duty cycle on all LEDs ( all channels ). + * + * @param[in] myDelay: PWM delay. + * @param[in] myPWM_DutyCycle: PWM duty cycle. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetPWM_DutyCycle_AllLEDs. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre Datasheet p.17 ( Example 1 and Example 2). + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetPWM_DutyCycle_AllLEDs ( uint8_t myDelay, uint8_t myPWM_DutyCycle ) +{ + char cmd[] = { 0, 0 }; + uint32_t myAux = 0; + uint32_t aux; + + + // The range is from 0% up to 100%. + if ( ( myDelay > 100 ) || ( myPWM_DutyCycle > 100 ) ) + return PCA9685_FAILURE; + + + // Delay time cannot be 0% + if ( myDelay == 0 ) + myDelay = 1; + + + // DELAY TIME: LEDs_ON_L + LEDs_ON_H + myAux = _MYROUND ( ( myDelay / 100.0 ) * PCA9685_ADC_STEPS ) - 1; + + // LEDs_ON_L + cmd[0] = ALL_LED_ON_L; + cmd[1] = ( myAux & 0xFF ); + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDs_ON_H + cmd[0] = ALL_LED_ON_H; + + if ( myPWM_DutyCycle == 100 ) + cmd[1] = 0x10; // All LEDs full ON + else + cmd[1] = ( ( myAux >> 8 ) & 0xFF ); + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + // LED OFF TIME: LEDs_OFF_L + LEDs_OFF_H + myAux += _MYROUND ( ( myPWM_DutyCycle / 100.0 ) * PCA9685_ADC_STEPS ); + + if ( ( myDelay + myPWM_DutyCycle ) <= 100 ) + myAux--; + else + myAux = ( myAux - 4096 ); + + + // LEDs_OFF_L + cmd[0] = ALL_LED_OFF_L; + cmd[1] = ( myAux & 0xFF ); + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDs_OFF_H + cmd[0] = ALL_LED_OFF_H; + + if ( myPWM_DutyCycle == 0 ) + cmd[1] = 0x10; // All LEDs full OFF + else + cmd[1] = ( ( myAux >> 8 ) & 0xFF ); + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + +/** + * @brief PCA9685_SetLED_ON ( PCA9685_led_channel_t ) + * + * @details It sets LEDn ON. + * + * @param[in] myLEDchannel: Chosen LED ( channel ). + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetLED_ON. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre Update on ACK requires all 4 PWM channel registers to be loaded before outputs will + * change on the last ACK. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetLED_ON ( PCA9685_led_channel_t myLEDchannel ) +{ + char cmd[] = { 0, 0 }; + uint32_t aux; + + + + // LEDn_ON_L + cmd[0] = LED0_ON_L + ( myLEDchannel << 2 ); + cmd[1] = 0x00; // Dummy value + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDn_ON_H + cmd[0] = LED0_ON_H + ( myLEDchannel << 2 ); + cmd[1] = 0x10; // LEDn full ON + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + // LEDn_OFF_L + cmd[0] = LED0_OFF_L + ( myLEDchannel << 2 ); + cmd[1] = 0x00; // Dummy value + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDn_OFF_H + cmd[0] = LED0_OFF_H + ( myLEDchannel << 2 ); + cmd[1] = 0x00; // LEDn full OFF + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetLED_OFF ( PCA9685_led_channel_t ) + * + * @details It sets LEDn OFF. + * + * @param[in] myLEDchannel: Chosen LED ( channel ). + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetLED_OFF. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre Update on ACK requires all 4 PWM channel registers to be loaded before outputs will + * change on the last ACK. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetLED_OFF ( PCA9685_led_channel_t myLEDchannel ) +{ + char cmd[] = { 0, 0 }; + uint32_t aux; + + + + // LEDn_ON_L + cmd[0] = LED0_ON_L + ( myLEDchannel << 2 ); + cmd[1] = 0x00; // Dummy value + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDn_ON_H + cmd[0] = LED0_ON_H + ( myLEDchannel << 2 ); + cmd[1] = 0x00; // Dummy value + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + // LEDn_OFF_L + cmd[0] = LED0_OFF_L + ( myLEDchannel << 2 ); + cmd[1] = 0x00; // Dummy value + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDn_OFF_H + cmd[0] = LED0_OFF_H + ( myLEDchannel << 2 ); + cmd[1] = 0x10; // LEDn full OFF + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetAllLED_ON ( void ) + * + * @details It sets All LEDs ON. + * + * @param[in] NaN. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetAllLED_ON. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre Update on ACK requires all 4 PWM channel registers to be loaded before outputs will + * change on the last ACK. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetAllLED_ON ( void ) +{ + char cmd[] = { 0, 0 }; + uint32_t aux; + + + + // LEDs_ON_L + cmd[0] = ALL_LED_ON_L; + cmd[1] = 0x00; // Dummy value + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDs_ON_H + cmd[0] = ALL_LED_ON_H; + cmd[1] = 0x10; // All LEDs full ON + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + // LEDs_OFF_L + cmd[0] = ALL_LED_OFF_L; + cmd[1] = 0x00; // Dummy value + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDs_OFF_H + cmd[0] = ALL_LED_OFF_H; + cmd[1] = 0x00; // Dummy value + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetAllLED_OFF ( void ) + * + * @details It sets All LEDs OFF. + * + * @param[in] NaN. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetAllLED_OFF. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre Update on ACK requires all 4 PWM channel registers to be loaded before outputs will + * change on the last ACK. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetAllLED_OFF ( void ) +{ + char cmd[] = { 0, 0 }; + uint32_t aux; + + + + // LEDs_ON_L + cmd[0] = ALL_LED_ON_L; + cmd[1] = 0x00; // Dummy value + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDs_ON_H + cmd[0] = ALL_LED_ON_H; + cmd[1] = 0x00; // Dummy value + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + // LEDs_OFF_L + cmd[0] = ALL_LED_OFF_L; + cmd[1] = 0x00; // Dummy value + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + // LEDs_OFF_H + cmd[0] = ALL_LED_OFF_H; + cmd[1] = 0x10; // All LEDs full OFF + + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetSUB1 ( PCA9685_mode1_sub1_t ) + * + * @details The device responds ( Enabled ) or not ( Disabled ) to I2C-bus subaddress 1. + * + * @param[in] mySUB1_mode: SUB1 Enabled/Disabled. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetSUB1. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre NaN. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetSUB1 ( PCA9685_mode1_sub1_t mySUB1_mode ) +{ + char cmd[] = { MODE1, 0 }; + uint32_t aux; + + + // Mask SUB1 and update its value + aux = i2c.write ( PCA9685_Addr, &cmd[0], 1, true ); + aux = i2c.read ( PCA9685_Addr, &cmd[1], 1 ); + cmd[1] &= ~MODE1_SUB1_MASK; + cmd[1] |= mySUB1_mode; + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetSUB2 ( PCA9685_mode1_sub2_t ) + * + * @details The device responds ( Enabled ) or not ( Disabled ) to I2C-bus subaddress 2. + * + * @param[in] mySUB2_mode: SUB2 Enabled/Disabled. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetSUB2. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre NaN. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetSUB2 ( PCA9685_mode1_sub2_t mySUB2_mode ) +{ + char cmd[] = { MODE1, 0 }; + uint32_t aux; + + + // Mask SUB2 and update its value + aux = i2c.write ( PCA9685_Addr, &cmd[0], 1, true ); + aux = i2c.read ( PCA9685_Addr, &cmd[1], 1 ); + cmd[1] &= ~MODE1_SUB2_MASK; + cmd[1] |= mySUB2_mode; + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetSUB3 ( PCA9685_mode1_sub3_t ) + * + * @details The device responds ( Enabled ) or not ( Disabled ) to I2C-bus subaddress 3. + * + * @param[in] mySUB3_mode: SUB3 Enabled/Disabled. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetSUB3. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre NaN. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetSUB3 ( PCA9685_mode1_sub3_t mySUB3_mode ) +{ + char cmd[] = { MODE1, 0 }; + uint32_t aux; + + + // Mask SUB3 and update its value + aux = i2c.write ( PCA9685_Addr, &cmd[0], 1, true ); + aux = i2c.read ( PCA9685_Addr, &cmd[1], 1 ); + cmd[1] &= ~MODE1_SUB3_MASK; + cmd[1] |= mySUB3_mode; + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetALLCALL ( PCA9685_mode1_allcall_t ) + * + * @details The device responds ( Enabled ) or not ( Disabled ) to LED All Call I2C-bus address. + * + * @param[in] myALLCALL_mode: ALLCALL Enabled/Disabled. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetALLCALL. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre NaN. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetALLCALL ( PCA9685_mode1_allcall_t myALLCALL_mode ) +{ + char cmd[] = { MODE1, 0 }; + uint32_t aux; + + + // Mask SUB3 and update its value + aux = i2c.write ( PCA9685_Addr, &cmd[0], 1, true ); + aux = i2c.read ( PCA9685_Addr, &cmd[1], 1 ); + cmd[1] &= ~MODE1_ALLCALL_MASK; + cmd[1] |= myALLCALL_mode; + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetINVERT ( PCA9685_mode2_invrt_t ) + * + * @details Output logic state inverted ( Enabled ) or not ( Disabled ). Value to use + * when external driver used. Applicable when #OE = 0 + * + * @param[in] myINVERT_mode: INVERT Enabled/Disabled. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetINVERT. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre NaN. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetINVERT ( PCA9685_mode2_invrt_t myINVERT_mode ) +{ + char cmd[] = { MODE2, 0 }; + uint32_t aux; + + + // Mask SUB3 and update its value + aux = i2c.write ( PCA9685_Addr, &cmd[0], 1, true ); + aux = i2c.read ( PCA9685_Addr, &cmd[1], 1 ); + cmd[1] &= ~MODE2_INVRT_MASK; + cmd[1] |= myINVERT_mode; + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +} + + + + +/** + * @brief PCA9685_SetOUTDRV ( PCA9685_mode2_outdrv_t ) + * + * @details It sets the 16 LEDn as open-drain or totem pole structure. + * + * @param[in] myOUTDRV_mode: OUTDRV mode. + * + * @param[out] NaN. + * + * + * @return Status of PCA9685_SetOUTDRV. + * + * + * @author Manuel Caballero + * @date 7/November/2017 + * @version 7/November/2017 The ORIGIN + * @pre NaN. + * @warning NaN. + */ +PCA9685::PCA9685_status_t PCA9685::PCA9685_SetOUTDRV ( PCA9685_mode2_outdrv_t myOUTDRV_mode ) +{ + char cmd[] = { MODE2, 0 }; + uint32_t aux; + + + // Mask SUB3 and update its value + aux = i2c.write ( PCA9685_Addr, &cmd[0], 1, true ); + aux = i2c.read ( PCA9685_Addr, &cmd[1], 1 ); + cmd[1] &= ~MODE2_OUTDRV_MASK; + cmd[1] |= myOUTDRV_mode; + aux = i2c.write ( PCA9685_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false ); + + + + + + if ( aux == I2C_SUCCESS ) + return PCA9685_SUCCESS; + else + return PCA9685_FAILURE; +}