Handling of I2C bus at 400kHz and TWBR register of Wire.h

The IDE (version 1.6.5) rejected the call for TWBR of the Wire.h to set the I2C frequency to 400kHz:

iMyFilter:37: error: 'TWBR' was not declared in this scope
'TWBR' was not declared in this scope

The call was:

TWBR = ((CPUFREQ / 400000L) - 16) / 2; // Set I2C frequency to 400kHz

What is the best method to assign the I2C ("Wire.h") to "fast" I2C 400kHz?

you may need to hack the library, Wire.h has

static const uint32_t TWI_CLOCK = 100000

on linux, libary is located at
.arduino15/packages/arduino/hardware/samd/1.6.1/libraries/Wire/

I tried editing Wires.h, along with a few other tricks, before making my forum post. The change you suggested was one of the first thing I tried. I'm honored other hackers have the same thinking. However, it's still not reading the sensor. The Arduino Uno reads it just fine but the Zero seems to have i2c issues.

Or, if you prefer not to hack the Wire.h library, you can just add the following code after Wire.begin().

Wire.begin();                                  // Start Wire (I2C)
sercom3.disableWIRE();                         // Disable the I2C bus
SERCOM3->I2CM.BAUD.bit.BAUD = SystemCoreClock / ( 2 * 400000) - 1 ;   // // Set the I2C SCL frequency to 400kHz
sercom3.enableWIRE();                          // Restart the I2C bus

The SAMD21 seems to be more particular than the AVRs when it comes to changing the SCL clock frequency. The AVRs only require the line: TWBR=12; to be added after Wire.begin(). The SAMD21 however requires the I2C bus to be disabled. It's therefore necessary to kick-start it again after the frequency has been changed.

It's a mystery why Arduino has never provided a 400kHz I2C option, as this is such a common requirement.

Thank you for the replies. I edited /samd/1.6.1/libraries/Wire/Wire.h with 400000.

My problem was the A4/A5 pins of my shield were not bridged with the SDL/SDA pins 9,10 of J101. It's a shame that the Zero isn't pin-compatible with the legacy Arduino Uno platform. Little incompatibilities like these are what typically consume my transition time between platforms, and make backwards compatibility between platforms difficult to manage (i.e. ability to switch between Zero and Uno during the project development timeline).

smoore:
I tried editing Wires.h, along with a few other tricks, before making my forum post. The change you suggested was one of the first thing I tried. I'm honored other hackers have the same thinking. However, it's still not reading the sensor. The Arduino Uno reads it just fine but the Zero seems to have i2c issues.

Sounds like problem is not I2C clock speed. You'll need to provide more info: I2C device, schematic or pin-to-pin summary, pullups, your software, photo ....

The UNO has internal I2C pullup resistors, it is my experience that the ZERO does not have internal I2C pullups.

Couldn't a function like this:

void TwoWire::setClock(uint32_t frequency) {
  sercom->disableWIRE();                         // Disable the I2C bus
  sercom->setClock(frequency);                // Set the I2C SCL frequency
  sercom->enableWIRE();                          // Restart the I2C bus
}

and in the SERCOM class...

void SERCOM::setClock(uint32_t frequency) {
   sercom->I2CM.BAUD.bit.BAUD = SystemCoreClock / ( 2 * frequency) - 1 ;   // Set the I2C SCL frequency
}

...be added to the Zero's Wire library to replace the dummy function that's currently there?

I also noticed that TwoWire::setClock() function has been implemented for the AVR Arduinos.

1 Like

arduino with mcp4725
I made an analog translator
but I want to run fast
400kHz
it usually says that

TWBR = 12; // 400 khz
Could you help
(smoore) can you share friend code
I need your help

thank

Hi nzmxp,

You dug up a topic that's almost 5 years old. Nowadays it's possible to set the I2C to 400kHz by using the Wire.setClock() function. Just call it after Wire.begin():

Wire.begin();           // Start I2C comms on SCL and SDA
Wire.setClock(400000);  // Set the SCL clock speed to 400kHz
1 Like
#include <Wire.h> // specify use of Wire.h library
#define MCP4725 0x62 // MCP4725 base address

unsigned int val;
byte buffer[3];

void setup()   {

  Wire.begin(); // begin I2C

}  // end setup

void loop() {
   
       
(((((Wire.setClock(400000);  // Set the SCL clock speed to 400kHz   )))     here is the code  

  buffer[0] = 0b01000000; // control byte
  val = analogRead(0) * 4; // read pot
  buffer[1] = val >> 4; // MSB 11-4 shift right 4 places
  buffer[2] = val << 4; // LSB 3-0 shift left 4 places

  Wire.beginTransmission(MCP4725); // address device
  Wire.write(buffer[0]);  // pointer
  Wire.write(buffer[1]);  // 8 MSB
  Wire.write(buffer[2]);  // 4 LSB
  Wire.endTransmission();

  // just an indicator
  digitalWrite(13, HIGH);
  delay(100);
  digitalWrite(13, LOW);
  delay(100);

} // end loop

can you check sample code

void loop
is it enough to add
Will it be 400khz without adding anything extra?

i will try it immediately

thank you for your help

Hi nzmxp,

The Wire.setClock() function only needs to be called one time. Looking at your sample code, the function should be moved from the loop() function and instead be placed in setup(), located just after Wire.begin():

#include <Wire.h> // specify use of Wire.h library
#define MCP4725 0x62 // MCP4725 base address

unsigned int val;
byte buffer[3];

void setup()   {

  Wire.begin(); // begin I2C
  Wire.setClock(400000);  // Set the SCL clock speed to 400kHz

}  // end setup

void loop() {

  buffer[0] = 0b01000000; // control byte
  val = analogRead(0) * 4; // read pot
  buffer[1] = val >> 4; // MSB 11-4 shift right 4 places
  buffer[2] = val << 4; // LSB 3-0 shift left 4 places

  Wire.beginTransmission(MCP4725); // address device
  Wire.write(buffer[0]);  // pointer
  Wire.write(buffer[1]);  // 8 MSB
  Wire.write(buffer[2]);  // 4 LSB
  Wire.endTransmission();

  // just an indicator
  digitalWrite(13, HIGH);
  delay(100);
  digitalWrite(13, LOW);
  delay(100);

} // end loop

(martinL)
Thanks you for the information

if everything goes right
would be great

you specified my error with the code you wrote

MartinL:
Hi nzmxp,

The Wire.setClock() function only needs to be called one time. Looking at your sample code, the function should be moved from the loop() function and instead be placed in setup(), located just after Wire.begin():

#include <Wire.h> // specify use of Wire.h library

#define MCP4725 0x62 // MCP4725 base address

unsigned int val;
byte buffer[3];

void setup()  {

Wire.begin(); // begin I2C
  Wire.setClock(400000);  // Set the SCL clock speed to 400kHz

}  // end setup

void loop() {

buffer[0] = 0b01000000; // control byte
????
????
[/quote]

once for the code
you said to be called
Wouldn't it be constantly working in the program?

will we delete the program after calling the program

you wrote last
can i just use the program
continuous

or enablewıre then do we disablewıre?
is it necessary

Hi nzmxp,

once for the code
you said to be called
Wouldn't it be constantly working in the program?

The Arduino libraries such as Wire are setting the microcontroller's registers behind the scenes. The registers are analagous to a giant switchboard that are detailed in the SAMD21 datasheet. They determine the microcontroller's behaviour. For example the Wire.setClock() function changes the value of the I2C's BAUD register, this action causes the SCL clock rate to change from 100kHz to 400kHz. Also, the action only has to be performed one time in order to take effect.

or enablewıre then do we disablewıre?
is it necessary

When the Arduino Zero was realeased back in 2015, the Arduino core code hadn't reached full maturity and functions like Wire.setClock() hadn't been implemented. This meant that to get 400kHz on SCL you had to manually disable the I2C communications, set the BAUD register then reenable I2C comms oncemore. Nowadays the Wire.setClock() function does that for you.

OK
so every 5.0 volts
in the appl
this code always works

we write the code once and it will always be there
will work constantly in the program
i hope i understand

thanks (martinL)