Undocumented i2c clock speeds for MCP4728

It's been a while since anyone posted about the MCP4728 Quad DAC but I wanted to try it for a project and needed to dig a bit to see how fast I could make it run. I'm too lazy to build filters to smooth PWM output, so it was DAC time, and I need at least 3 channels.

First of all, I am using the GitHub - hideakitai/MCP4728: Arduino library for MCP4728 quad channel, 12-bit voltage output Digital-to-Analog Convertor with non-volatile memory and I2C compatible Serial Interface library and a Nano. The library worked like a charm and I was making waves within a couple of minutes of hooking everything up.

When I started, I had no idea what the limiting factors would be. I needed to generate 60Hz sine waves and by default, this isn't satisfactorily possible. Iterating through a 256-point array with the default 100kHz clock yields a 4.2Hz waveform. By experimentation, I learned that this is limited by the clock rate of the i2c bus.

I'm sure that this is obvious by inspection to even the most casual observer, but not me.

With an ATmega328P (Nano clone), it turns out that the highest clock rate supported by the i2c library (without assembly I assume) and the MCP4728 is 888kHz. The MCP itself supports a High-Speed mode of 3.4MHz but it is not obvious to me how to initialize the chip & processor & library to run that fast.
Wire.begin(100000L); sets 100kHz clock (default), 400000L sets 400kHz and although undocumented anywhere that I could find, 800000L sets the clock to 800kHz and it works.
But we can squeeze a bit more out of it. twi.c (on a Mac, /Users/me/Library/Arduino15/packages/arduino/hardware/avr/1.6.23/libraries/Wire/src/utility/twi.c) contains the following code:

  // initialize twi prescaler and bit rate
  cbi(TWSR, TWPS0);
  cbi(TWSR, TWPS1);
  TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;

  /* twi bit rate formula from atmega128 manual pg 204
  SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
  note: TWBR should be 10 or higher for master mode
  It is 72 for a 16mhz Wiring board with 100kHz TWI */

We know that this calculation can be overridden by specifying TWBR explicitly after
Wire.begin(). A little pencil and paper work tells us that 100kHz clock is TWBR = 72, 400kHz clock is TWBR=12 and TWBR = 2 yields 800kHz. Since TWBR can't be negative, setting it to 1 gives us a clock of 888kHz (calculated). With this I can generate three passable albeit ugly, 60Hz sine waves, 120deg offset (power system simulation).

I kind of think I'm pushing the Nano pretty hard though. Adding any extra code slows the rate at which data is written to the DAC. Time to try a Trinket M0.

Note: The [GitHub - hideakitai/MCP4728: Arduino library for MCP4728 quad channel, 12-bit voltage output Digital-to-Analog Convertor with non-volatile memory and I2C compatible Serial Interface](GitHub - hideakitai/MCP4728: Arduino library for MCP4728 quad channel, 12-bit voltage output Digital-to-Analog Convertor with non-volatile memory and I2C compatible Serial Interface library) library works with the Adafruit Trinket M0.
Clock speeds of up to 1.4MHz are possible with the MCP4728:

Wire.setClock(1400000L);

Wire.h handles setting i2c clock speeds only with the command above. TWBR is not a parameter for the Trinket M0 and trying to use it prevents successful compilation.