0

Have I understood the ATmega328P datasheet correctly regarding the TWI clock frequency?

From §21.5.2 Bit Rate Generator Unit, the TWI clock frequency is calculated as follows:

f_{scl}=\frac{f_{cpu}}{16+2\timesTWBR\timesPrescaler}

Then solving for TWBR:

TWBR=\frac{\frac{f_{cpu}}{f_{scl}}-16}{2\timesPrescaler}

However, §8.12.2 CLKPR – Clock Prescale Register mentions that the CPU frequency is prescaled by CLKPR. So should the equation be:

f_{scl}=\frac{f_{cpu}}{2^{CLKPR}\times(16+2\timesTWBR\timesPrescaler)}

And solving for TWBR:

TWBR=\frac{\frac{f_{cpu}}{2^{CLKPR}\times f_{scl}}-16}{2\timesPrescaler}

Is the following code a correct implementation of the equations?

void SetFrequency(uint32_t twi_freq)
{
    if (smbus)
    {
        // SMBus clock frequency is 10 kHz to 100 kHz.
        if (twi_freq > 100000u)
        {
            twi_freq = 100000u;
        }
        else if twi_freq < 10000u)
        {
            twi_freq = 10000u;
        }
    }
    else if (twi_freq > 400000u)
    {
        // Up to 400 kHz data transfer speed.
        // §21.1 Features
        twi_freq = 400000u;
    }

    // TWI prescaler of 1.
    // Table 21-8. TWI Bit Rate Prescaler
    // TWPS1 = 0, TWPS0 = 0
    TWSR &= B11111100;

    // Achievable maximum TWI frequency with current CPU frequency, CLK prescaler and minimum TWBR with TWI prescaler of 1.
    // Rounded down to nearest integer.
    const uint32_t TWI_FREQ_MAX = F_CPU / ( ( 1 << CLKPR ) * ( 16 + 2 * 0x00 * 1 ) );

    // Achievable minimum TWI frequency with current CPU frequency, CLK prescaler and maximum TWBR with TWI prescaler of 1.
    // Rounded up to nearest integer.
    const uint32_t DIVISOR = ( ( 1 << CLKPR  ) * ( 16 + 2 * 0xFF * 1 ) );
    const uint32_t TWI_FREQ_MIN = (F_CPU + (DIVISOR - 1)) / DIVISOR;

    if (twi_freq > TWI_FREQ_MAX)
    {
        twi_freq = TWI_FREQ_MAX;
    }
    else if (twi_freq < TWI_FREQ_MIN)
    {
        twi_freq = TWI_FREQ_MIN;
    }

    // TWI bit rate.
    // §21.5.2 Bit Rate Generator Unit
    // §8.12.2 CLKPR – Clock Prescale Register
    byte bit_rate = (F_CPU / (1 << CLKPR) / twi_freq - 16) / 2

    TWBR = bit_rate;

    // Check.
    const uint32_t twi_freq = F_CPU / ( ( 1 << CLKPR ) * ( 16 + 2 * TWBR * 1 ) );
}

Note that smbus is a flag set elsewhere (global or class member) to indicate SMBus mode.

1 Answer 1

0

Just to confirm, in case anyone else is wondering, the second pair of equations in my question are the correct ones which take the CPU clock prescaler (CLKPR) into account.

TWI clock frequency:

f_{scl}=\frac{f_{cpu}}{2^{CLKPR}\times(16+2\timesTWBR\timesPrescaler)}

And solving for TWBR:

TWBR=\frac{\frac{f_{cpu}}{2^{CLKPR}\times f_{scl}}-16}{2\timesPrescaler}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.