예제 #1
0
/**
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;
}
예제 #2
0
/**
This method takes any actions needed to initialize the PWM chip for use by other methods.
If the chip has already been initialized it does nothing, otherwise it sets the pulse rate
prescale value, turns on the chip and sets the mode registers for how other methods access 
the chip.
\param[in] i2cAdr The I2C address of the PWM chip.
\return HRESULT success or error code.
*/
HRESULT PCA9685Device::_InitializeChip(ULONG i2cAdr)
{
    HRESULT hr = S_OK;

    // If we don't know that the chip is already initialized:
    if (!m_chipIsInitialized)
    {
        //
        // Queue a read of the chip MODE1 register to get the state of the SLEEP bit.
        //

        I2cTransactionClass transaction;
        UCHAR readBuf[1] = { 0 };                   // Buffer for reading data from chip
        UCHAR mode1RegAdr[1] = { MODE1_ADR };       // Buffer for MODE1 register address
        UCHAR mode2RegAdr[1] = { MODE2_ADR };       // Buffer for MODE2 register address
        UCHAR preScaleAdr[1] = { PRE_SCALE_ADR };   // Buffer for PRE_SCALE register address
        MODE1 mode1Reg = { 0, 0, 0, 0, 0, 1, 0, 0 }; // No sleep, auto-increment, internal clock
        MODE2 mode2Reg = { 0, 1, 1, 0, 0 };         // Drive outputs both high & low, change on ACK, non-inverted

        // 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();

            // Queue sending the address of the MODE1 regeister to the PWM chip.
            hr = transaction.queueWrite(mode1RegAdr, sizeof(mode1Reg));
        }

        if (SUCCEEDED(hr))
        {
            // Queue reading the contents of the MODE1 register.
            hr = transaction.queueRead(readBuf, sizeof(readBuf));
        }

        //
        // If the SLEEP bit is clear, the chip has been initialize: abort the rest of the transaction.
        //

        if (SUCCEEDED(hr))
        {
            // Register a callback to test if the chip is initialized.
            hr = transaction.queueCallback([&readBuf, &transaction]()
            {
                PMODE1 mode1RegPtr = (PMODE1)readBuf;
                if (mode1RegPtr->SLEEP == 0)   // If chip is not in sleep mode,
                {
                    transaction.abort();
                }
                return S_OK;
            }
            );
        }

        //
        // Queue writes to initialize the PWM chip.
        //

        if (SUCCEEDED(hr))
        {
            // Queue sending the address of the PRE_SCALE register to the PWM chip.
            hr = transaction.queueWrite(preScaleAdr, sizeof(preScaleAdr), TRUE);
        }

        if (SUCCEEDED(hr))
        {
            // Queue sendng the prescale value.
            hr = transaction.queueWrite(&m_freqPreScale, 1);
        }

        if (SUCCEEDED(hr))
        {
            // Queue sending the address of the MODE1 register to the PWM chip.
            hr = transaction.queueWrite(mode1RegAdr, sizeof(mode1RegAdr), TRUE);
        }

        if (SUCCEEDED(hr))
        {
            // Queue sending the contents of MODE1 register.
            hr = transaction.queueWrite((PUCHAR)&mode1Reg, 1);
        }

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

        if (SUCCEEDED(hr))
        {
            // Queue RESTART and sending the address of the MODE2 register to the PWM chip.
            hr = transaction.queueWrite(mode2RegAdr, sizeof(mode2RegAdr), TRUE);
        }

        if (SUCCEEDED(hr))
        {
            // Queue sending the contents of MODE2 register.
            hr = transaction.queueWrite((PUCHAR)&mode2Reg, 1);
        }

        //
        // Perform the read, decision, and writes to initialize the chip (if needed).
        //

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

        //
        // Whether we initialized the chip or found it initialized, we now know it is initialized.
        //

        if (SUCCEEDED(hr))
        {
            // Indicate chip is initialized.
            m_chipIsInitialized = TRUE;
        }
    }
    
    return hr;
}
예제 #3
0
/**
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;
}