Disable I2C functionality on SDA/SCL pins

For a special 4-channel DAC (MCP4728) in a system I am prototyping, I need to be able to bit-bang the I2C pins in order to be able to change its I2C address (it's complicated). I can successfully bit-bang this via other GPIO pins on the Zero but the SDA/SCL pins just won't move with digitalWrite(), also not if the two pins are just connected to an oscilloscope. I tried different methods like pinMode() and pinPeripheral() but it doesn't work...

I have dug a bit deeper and I think it's because these two pins are by default handeled by SERCOM5 and are therefor not usable as GPIO pins. There is a way to change that, specifically I think one needs to change a value in the PINCFG register of the Zero. That value is the "Bit 0 – PMUXEN Peripheral Multiplexer Enable" which in the case of SDA/SCL pins enabled to be handled by SERCOM5. I think if I would be able to disable this "peripheral" I could use the pin as GPIOs for my little address changing routine.

Problem is, this is way above my head. The corresponding section in the datasheet[1] is on page 387 at the very bottom. I would be super grateful if someone could help me out here and not suggest I use a different microcontroller, DAC or otherwise change the specifications of my project. Thank you!

[1]http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D21_DA1_Family_Data%20Sheet_DS40001882E.pdf

I ran this sketch on my M0 Pro. It worked fine.

void setup()
{
    pinMode(SDA, OUTPUT);
    pinMode(13, OUTPUT);
}

void loop()
{
    digitalWrite(SDA, HIGH);
    digitalWrite(13, HIGH);
    delay(1000);
    digitalWrite(SDA, LOW);
    digitalWrite(13, LOW);
    delay(1000);
}

The real mystery is WHY ?

The purpose of a bus is to transport multiple passengers.
If for some reason you have two Slaves with identical Slave addresses, you might run two hardware buses to accommodate.

But there is no reason to bit-bang I2C in software when you can have up to six hardware I2C buses if you really want.

David.

Hi osterchrisi,

There is however an issue with the SAMD21’s Wire library, as the Wire.end() function simply disables the SERCOM and doesn’t switch the SCL and SDA pins back to GPIO.

To return the pins back to GPIO just use the following lines of code for SCL and SDA respectively:

// Return the SCL pin back to GPIO
PORT->Group[g_APinDescription[SCL].ulPort].PINCFG[g_APinDescription[SCL].ulPin].bit.PMUXEN = 0;

// Return the SDA pin back to GPIO
PORT->Group[g_APinDescription[SDA].ulPort].PINCFG[g_APinDescription[SDA].ulPin].bit.PMUXEN = 0;

Thereafter it’s possible to use the GPIO functions pinMode() and digitalWrite() oncemore.

From memory, pinMode() should do the appropriate PMUXEN regardless.

Untested. (I should not type blindly)

Hey, thank you two very much for your help. I should have expected an answer sooner and check back here. In the meanwhile I also brought it to run. I think I made it too complicated in my head and tried too many things at the same time, when I couldn't get it to work.

Both of you are correct in that sense. A simple pinMode(SDA, OUTPUT); sets the SDA pin low for example. It's critical though to use the "SDA" and "SCL" pin names, otherwise it doesn't work. Confusing.

I discovered also the problem with the Wire library not "giving back" the pins and I simply solved it by bit-banging the address before I start the I2C communication. That works and is consistent with my program flow anyway. But it brought also some confusion.

But there is no reason to bit-bang I2C in software when you can have up to six hardware I2C buses if you really want.

Oh, there are many reasons :slight_smile: I have four of these DACs on my I2C bus, so in the worst case I would need 12 GPIOs ((4 * SDA/SCL) + (4 * latch) plus the "normal" I2C pins to do what I want. But I only need 2 I2C pins and a multiplexed LDAC pin to do it the way I am planning it.

I understand that this kind of undermines the advantage of I2C only needing two wires but I also didn't design this DAC and if it wouldn't be IDEAL for my application I'd immediately chose another one.

For reference, here is what I need to do during setup:

Shirley, you configure each chip to have a specific I2C address as a one-off. It is stored in EEPROM forever.

Then you can have 8 chips on the same bus. Job done.

5.6.8 WRITE COMMAND: WRITE I2C
ADDRESS BITS (C2=0, C1=1, C0=1)
This command writes new I2C address bits (A2, A1,
A0) to the DAC input registers and EEPROM. When
the device receives this command, it overwrites the
current address bits with the new address bits.
This command is valid only when the LDAC pin makes
a transition from “High” to “Low” at the low time of the
last bit (8th clock) of the second byte, and stays “Low”
until the end of the third byte. The update occurs after
“Stop” bit, if the conditions are met. The LDAC pin is
used to select a device of interest to write. The highest
clock rate of this command is 400 kHz. Figure 5-11
shows the details of the address write command.

David.

david_prentice:
Shirley, you configure each chip to have a specific I2C address as a one-off. It is stored in EEPROM forever.

Then you can have 8 chips on the same bus. Job done.
David.

Yes, that's the idea. With 4 chips anyway. I appreciate your effort to simplify this for me.

I didn't give you enough context to understand better: This will be a commercial project in the future and everything comes SMD assembled by the factory. I cannot configure each chip to have a specific address as a one-off. I mean, I can, but I won't :smiley: I don't think I can bother anyone to change I2C addresses for single SMD chips before they go on the board... So I'll run this routine once for every board during production and then all is good.