ATMEGA328P I2C Clock Issue

I have a custom designed CCA that uses an ATMEGA328P micro controller, a MCP4725 DAC (I2C interface) and another sensor (SPI interface). In my initial design all of these devices ran off of 5 V and the ATmega utilized a 16MHz resonator to provide the clock.  Everything worked great, I had my I2C clock at 400khz and my SPI clock at 5Mhz.

In an effort to reduce the power consumption, I was playing with the clock scaler to get the ATmega to run at a lower speed and use less power with success.

To reduce power even further I made the following changes:

1. I replaced the 16MHz resonator with a 2MHz resonator

2. I put a 5V to 3.3V regulator on my board to run the ATMEGA at 3.3V

3. I used a PCA9306 voltage level translator to work between the ATMEGA and MCP4725 (I2C lines)

4. I updated the boards.txt file to change f.cpu to 2Mhz (this was already done when i was using the clock scalers)

The design functions but for some reason my I2C clock is running at 1.9-3.8khz depending on how I play with my code.

1. When I have the following enabled in my code i get the 1.9khz, when I disable it (i.e. \\) i get 3.8khz

CLKPR = 0x80;
CLKPR = 0x01;

1. I played around with the TWBR, which I had always run the following:

TWBR = ((2000000 /400000l) - 16) / 2; // Change the i2c clock to 400KHz

but it didn't seem to make a difference.

When I have both #1 and #2 above enabled this is what I get:

• The ATMEGA328P clock was probed at the external resonator

• The SPI clock was probed at the ATMEGA328P pin

• The I2C clock was probed at the ATMEGA328P pin (although I probed it on the MCP4725 side and got the same #'s)

ATMEGA328P Clock ...2Mhz as expected

SPI Clock (was expecting 500khz but plenty fast for what i need)

I2C Clock... 1.9Khz.... Much slower than what i expect which is 400khz

If i disable the clock scaler (#1 above), the ATMEGA328P clock stays the same.

SPI clock... 500khz as expected

I2C clock... 3.8khz, so it doubles but it is way low still.

No idea why my I2C clock is so slow now, any ideas?

Basic code below:

#include <Wire.h>
#include <Adafruit_MCP4725.h>
#include <SPI.h>
#define kNOERROR 0
#define kPRIMARYREADERROR 1
#define kEXTENDEDREADTIMEOUTERROR 2
#define kPRIMARYWRITEERROR 3
#define kEXTENDEDWRITETIMEOUTERROR 4
#define kCRCERROR 5
#define kUNABLETOCHANGEPROCESSORSTATE 6
#include <avr/sleep.h>
#include <avr/power.h>

//190907const uint16_t LEDPin = 13;
const uint32_t WRITE = 0x40;
const uint32_t READ = 0x00;

const uint32_t COMMAND_MASK = 0xC0;
const uint32_t ADDRESS_MASK = 0x3F;
unsigned long nextTime;
bool ledOn = false;
bool includeCRC = true;

Adafruit_MCP4725 dac;

void setup(void) {

//disable ADC
ADCSRA = 0;

power_adc_disable(); // ADC converter
power_timer0_disable();// Timer 0
power_timer1_disable();// Timer 1
power_timer2_disable();// Timer 2

CLKPR = 0x80;
CLKPR = 0x01;

uint16_t unused;
uint32_t flags;
uint32_t flagsAndZeroOffset;
// Initialize SPI
SPI.begin();
pinMode(SS, OUTPUT);
//Sets Unused Pins to Internal Pullup
pinMode(2,INPUT_PULLUP);
pinMode(3,INPUT_PULLUP);
pinMode(4,INPUT_PULLUP);
pinMode(5,INPUT_PULLUP);
pinMode(6,INPUT_PULLUP);
pinMode(7,INPUT_PULLUP);
pinMode(8,INPUT_PULLUP);
pinMode(9,INPUT_PULLUP);
pinMode(15,INPUT_PULLUP);
pinMode(16,INPUT_PULLUP);
pinMode(17,INPUT_PULLUP);

//190907 pinMode(LEDPin, OUTPUT);

nextTime = millis();
//190907digitalWrite(LEDPin, LOW);
digitalWrite(SS, HIGH);
// Make sure all of the SPI pins are
// ready by doing a read
PrimaryRead(SS, 0x0, unused);
// Unlock the device
ExtendedWrite(SS, 0xFFFE, 0x27811F77);
// Make sure the device is unlocked
ExtendedRead(SS, 0x22, flags);
if (!((flags & 0x0022) == 0x0020))
{
Serial.println("Device is not Unlocked");
}

// For Adafruit MCP4725A1 the address is 0x62 (default) or 0x63 (ADDR pin tied to VCC)
dac.begin(0x62);
TWBR = ((2000000 /400000l) - 16) / 2; // Change the i2c clock to 400KHz

}

void loop(void) {

temp = PrimaryRead(SS, 0x20, angle);
SPIvariable = (float)(angle & 0x0FFF) * (360.0 / 4096.0);

//    Serial.println(SPIvariable);

dac.setVoltage(3*SPIvariable,false);

}

• please format your code – jsotola Jan 10 at 7:23

1 Answer

The TWBR calculation is wrong. If you take 2 MHz and divide it by 400 kHz, result is 5. If you subtract 16, you go negative to -11 so it wraps to 245. Further, dividing -11 by two gives -5 so it is 251. 2 MHz divided by 16+(2*251) is exactly the 3.861 kHz clock speed you see when clock prescaler is not dividing the clock and half of it when clock prescaler is dividing the clock by 2. Also note that with 2 MHz clock, you can only reach 125 kHz I2C clock speed, not more.

• Thanks man So if TWBR = 0.5 * (F_CPU / 100kHz - 16) Then I calculated I need to set TWBR = 2. Don't need to keep the formula in the code and can just state "TWBR = 2;" I wonder if I have a prescaler set somewhere. Is there any harm in putting this in the code too? TWSR = 0x00; // Select Prescaler of 1 – Peter Ruggiero Jan 10 at 14:42
• Not sure what i'm doing wrong but I set TWBR = 0; and the prescaler CLKPR = 0x00; which should give me 1.... same results, i wonder if i mucked up something in my boards.txt file – Peter Ruggiero Jan 10 at 17:16
• Some AVRs need to have certain minimum value for TWBR, but at least the datasheet for your chip does not say if it has this limitation. But in which way "it does not work", what does it do then? – Justme Jan 10 at 17:18
• It doesn't work in that the computer which is fed by the output of my arduino is wanting a higher sampling rate that what my current I2C clock can provide. – Peter Ruggiero Jan 10 at 17:42