/**
Set the width of the positive pulses on one of the PWM channels.
\param[in] i2cAdr The I2C address of the PWM chip.
\param[in] channel The channel on the PWM chip for which to set the pulse width.
\param[in] dutyCycle The desired duty-cycle of the positive pulses (0-0xFFFFFFFF for 0-100%).
\return HRESULT success or error code.
*/
HRESULT PCA9685Device::SetPwmDutyCycle(ULONG i2cAdr, ULONG channel, ULONG dutyCycle)
{
    HRESULT hr = S_OK;
    I2cTransactionClass transaction;
    ULONGLONG tmpPulsetime = 0;
    UCHAR bitRegsAdr = 0;                       // Address of start of registers for bit in question
    UCHAR pulseData[REGS_PER_LED] = { 0x00, 0x00, 0x00, 0x00 };    // Registers data to set pulse time


    if (channel >= LED_COUNT)
    {
        hr = DMAP_E_INVALID_PORT_BIT_FOR_DEVICE;
    }

    if (SUCCEEDED(hr))
    {
        // Make sure the PWM chip is initialized.
        hr = _InitializeChip(i2cAdr);
    }

    if (SUCCEEDED(hr))
    {
        // Set the I2C address of the PWM chip.
        hr = transaction.setAddress(i2cAdr);
    }

    if (SUCCEEDED(hr))
    {
        // Indicate this chip supports high speed I2C transfers.
        transaction.useHighSpeed();

        // Calculate the address of the first register for the port in question.
        bitRegsAdr = (UCHAR)(LEDS_BASE_ADR + (channel * REGS_PER_LED));

        // Queue sending the base address of the port registers to the chip.
        hr = transaction.queueWrite(&bitRegsAdr, 1);
    }

    if (SUCCEEDED(hr))
    {
        // Get the pulse high time in PWM chip terms.
        tmpPulsetime = ((((ULONGLONG)dutyCycle) * (1LL << PWM_BITS)) + 0x80000000LL) / 0x100000000LL;
        tmpPulsetime = tmpPulsetime & ((1LL << PWM_BITS) - 1LL);
        pulseData[2] = (UCHAR)(tmpPulsetime & 0xFF);
        pulseData[3] = (UCHAR)((tmpPulsetime >> 8) & 0xFF);

        // Queue sending the registers contents for the desired pulse width.
        hr = transaction.queueWrite(pulseData, REGS_PER_LED);
    }

    if (SUCCEEDED(hr))
    {
        // Actually perform the I2C transfers specified above.
        hr = transaction.execute(g_i2c.getController());
    }
    
    return hr;
}
/**
This method takes the actions needed to set a port bit of the PWM chip to the desired state.
\param[in] i2cAdr The I2C address of the PWM chip.
\param[in] portBit The number of the port bit to modify.
\param[in] state The state to set the port bit to: HIGH or LOW.
\return HRESULT success or error code.
*/
HRESULT PCA9685Device::SetBitState(ULONG i2cAdr, ULONG portBit, ULONG state)
{
    HRESULT hr = S_OK;
    I2cTransactionClass transaction;
    UCHAR bitRegsAdr = 0;                       // Address of start of registers for bit in question
    UCHAR lowBitData[REGS_PER_LED] = { 0x00, 0x00, 0x00, 0x10 };    // Registers data to set bit low
    UCHAR highBitData[REGS_PER_LED] = { 0x00, 0x10, 0x00, 0x00 };   // Registers data to set bit high

    if (portBit >= LED_COUNT)
    {
        hr = DMAP_E_INVALID_PORT_BIT_FOR_DEVICE;
    }

    if (SUCCEEDED(hr) && (state != HIGH) && (state != LOW))
    {
        hr = DMAP_E_INVALID_PIN_STATE_SPECIFIED;
    }

    if (SUCCEEDED(hr))
    {
        // Make sure the PWM chip is initialized.
        hr = _InitializeChip(i2cAdr);
    }

    if (SUCCEEDED(hr))
    {
        // Set the I2C address of the PWM chip.
        hr = transaction.setAddress(i2cAdr);
    }

    if (SUCCEEDED(hr))
    {
        // Indicate this chip supports high speed I2C transfers.
        transaction.useHighSpeed();

        // Calculate the address of the first register for the port in question.
        bitRegsAdr = (UCHAR)(LEDS_BASE_ADR + (portBit * REGS_PER_LED));

        // Queue sending the base address of the port registers to the chip.
        hr = transaction.queueWrite(&bitRegsAdr, 1);
    }

    if (SUCCEEDED(hr))
    {
        // Queue sending the registers contents to set the specified bit state.
        if (state == LOW)
        {
            hr = transaction.queueWrite(lowBitData, REGS_PER_LED);
        }
        else
        {
            hr = transaction.queueWrite(highBitData, REGS_PER_LED);
        }
        
    }

    if (SUCCEEDED(hr))
    {
        // Actually perform the I2C transfers specified above.
        hr = transaction.execute(g_i2c.getController());
    }
    
    return hr;
}
/**
This expects the port bit to be configured to be constantly on or off.
\param[in] i2cAdr The I2C address of the PWM chip.
\param[in] portBit The number of the port bit to read.
\param[out] state The state of the port bit: HIGH or LOW.
\return HRESULT success or error code.
*/
HRESULT PCA9685Device::GetBitState(ULONG i2cAdr, ULONG portBit, ULONG & state)
{
    HRESULT hr = S_OK;
    I2cTransactionClass transaction;
    UCHAR bitRegsAdr = 0;                       // Address of start of registers for bit in question
    UCHAR bitData[REGS_PER_LED] = { 0 };        // Buffer for bit register contents


    if (portBit >= LED_COUNT)
    {
        hr = DMAP_E_INVALID_PORT_BIT_FOR_DEVICE;
    }

    if (SUCCEEDED(hr))
    {
        // Make sure the PWM chip is initialized.
        hr = _InitializeChip(i2cAdr);
    }

    if (SUCCEEDED(hr))
    {
        // Set the I2C address of the PWM chip.
        hr = transaction.setAddress(i2cAdr);
    }

    if (SUCCEEDED(hr))
    {
        // Indicate this chip supports high speed I2C transfers.
        transaction.useHighSpeed();

        // Calculate the address of the first register for the port in question.
        bitRegsAdr = (UCHAR)(LEDS_BASE_ADR + (portBit * REGS_PER_LED));

        // Queue sending the base address of the port registers to the chip.
        hr = transaction.queueWrite(&bitRegsAdr, 1);
    }

    if (SUCCEEDED(hr))
    {
        // Queue reading the registers for the bit in question.
        hr = transaction.queueRead(bitData, REGS_PER_LED);
    }

    if (SUCCEEDED(hr))
    {
        // Actually perform the I2C transfers specified above.
        hr = transaction.execute(g_i2c.getController());
    }

    if (SUCCEEDED(hr))
    {
        // If constant OFF bit is 1, the port bit is LOW, regardless of constant ON bit.
        if ((bitData[3] & 0x10) != 0)
        {
            state = LOW;
        }
        // If constant OFF bit is 0, and constant ON bit is 1, the port bit is HIGH.
        else if ((bitData[1] & 0x10) != 0)
        {
            state = HIGH;
        }
        // If both constant state bits are zero, the port bit is not in a constant state.
        {
            hr = DMAP_E_GPIO_PIN_IS_SET_TO_PWM;
        }
    }
    
    return hr;
}
/**
Set the pulse repetition rate for the PWM channels on the specified chip.
\param[in] i2cAdr The I2C address of the PWM chip.
\param[in] frequency The desired PWM pulse repetition rate in pulses per second.
\return HRESULT success or error code.
*/
HRESULT PCA9685Device::SetPwmFrequency(ULONG i2cAdr, ULONG frequency)
{
    HRESULT hr = S_OK;
    I2cTransactionClass transaction;
    ULONG preScale;
    UCHAR readBuf[1] = { 0 };                       // Buffer for reading data from chip
    UCHAR mode1RegAdr[1] = { MODE1_ADR };           // Buffer for MODE1 register address
    UCHAR preScaleAdr[1] = { PRE_SCALE_ADR };       // Buffer for PRE_SCALE register address
    MODE1 mode1Sleep = { 0, 0, 0, 0, 1, 1, 0, 0 };  // Sleep, auto-increment, internal clock
    MODE1 mode1Run = { 0, 0, 0, 0, 0, 1, 0, 0 };    // No sleep, auto-increment, internal clock


                                                    // Make sure the PWM chip is initialized.
    hr = _InitializeChip(i2cAdr);

    if (SUCCEEDED(hr))
    {
        // Set the I2C address of the PWM chip.
        hr = transaction.setAddress(i2cAdr);
    }

    if (SUCCEEDED(hr))
    {
        // Indicate this chip supports high speed I2C transfers.
        transaction.useHighSpeed();

        // Calculate the nearest prescale value for the requested pulse rate.
        if (frequency < 24)
        {
            preScale = 0xFF;
        }
        else
        {
            // From PCA9685 datasheet: prescale = round(25,000,000 / (4096 * pulse_rate)) - 1
            preScale = (((25000000 + ((4096 * frequency) / 2))) / (4096 * frequency)) - 1;
        }
        preScale = preScale & 0xFF;
    }

    // If we need to set a new prescale value.
    if (SUCCEEDED(hr) && (m_freqPreScale != preScale))
    {
        // Queue a write to set the Sleep bit (so we can change the PWM frequency).
        hr = transaction.queueWrite(mode1RegAdr, sizeof(mode1RegAdr));
        if (SUCCEEDED(hr))
        {
            hr = transaction.queueWrite((PUCHAR)&mode1Sleep, 1);
        }

        // Queue a write to set the frequency prescale value.
        if (SUCCEEDED(hr))
        {
            hr = transaction.queueWrite(preScaleAdr, sizeof(preScaleAdr), TRUE);
        }
        if (SUCCEEDED(hr))
        {
            hr = transaction.queueWrite((PUCHAR)&preScale, 1);
        }

        // Queue a write to clear the Sleep bit.
        if (SUCCEEDED(hr))
        {
            hr = transaction.queueWrite(mode1RegAdr, sizeof(mode1RegAdr), TRUE);
        }
        if (SUCCEEDED(hr))
        {
            hr = transaction.queueWrite((PUCHAR)&mode1Run, 1);
        }

        // Delay for 500 microseconds for clock to start.
        for (int i = 0; SUCCEEDED(hr) && (i < 5); i++)
        {
            hr = transaction.queueWrite(mode1RegAdr, sizeof(mode1RegAdr), TRUE);
            if (SUCCEEDED(hr))
            {
                hr = transaction.queueRead(readBuf, sizeof(readBuf));
            }
        }

        if (SUCCEEDED(hr))
        {
            // Actually perform the I2C transfers specified above.
            hr = transaction.execute(g_i2c.getController());
        }

        // Record the prescale value just set.
        if (SUCCEEDED(hr))
        {
            m_freqPreScale = (UCHAR)preScale;
        }
    }

    return hr;
}