ATSAMD21G18A: I2C interface issue on PB02 and PB03

Hi,

I created own board based on MKR WAN 1300 and now I think I messed up with HW design when I added the second I2C interface.

MKR WAN 1300 has I2C interface on pin PA08 and PA09. Sensors on this I2C works fine.

I added sensors to pins PB02 (SCL) and PB03 (SDA). I thought I can do this because in ATSAMD21 data sheet those pins are listed as SERCOM-ALT pins (page 33/1116).

Now I realized that on page 34/1116 those pins are not listed as "SERCOM I2C Pins".

So my question is: can I use PB02 and PB03 for second I2C interface?
With SERCOM functionality or somehow else?

With ESP32 adding second I2C was as easy as Wire.begin(I2C_SDA, I2C_SCL) on any pin. Can I do that?

Thanks,
Tipo

@tipo1000 Interestingly, Adafruit also uses same pins PB02 and PB03 on SERCOM5 for their SAMD51 based Metro M4 board's I2C interface, even though the datasheet doesn't specify them as I2C compatible pins. I've used the I2C interface on this board without any issues.

I know that the Metro M4 uses a SAMD51 microcontroller rather than the SAMD21 found on the MKR WAN 1300, but their SERCOM modules are identical from a register perspective, so you might be OK. Unfortunately, the datasheet doesn't state how the I2C pins are different, perhaps it's something to do with their drive capability?

To add a new, additional I2C port, follow Adafruit's tutorial: https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports/overview.

A software I2C bus can be on any digital pins. If you are only interfacing a few normal sensors, then it should be no problem.

1 Like

The interpretation of "Sercom but not I2C" pins is that there is probably some obscure electrically specification in the I2C spec that those pins fail to meet. It's probably only relevant for "edge" cases and SOME peripheral chips - I don't think that anybody has reported problems trying to use those pins in "typical" Arduino-like applications.

Sounds promising.

Can somebody detail with what Arduido code I could try to use PB02 and PB03?
Should I do with SERCOM or somehow else?

/tipo

@tipo1000 Looking at the MKR WAN 1300 Arduino core variant.h and variant.cpp files, there's an additional complication, in that SERCOM5 is already assigned to the Serial1 port:

// Serial1
Uart Serial1(&sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX);

void SERCOM5_Handler()
{
  Serial1.IrqHandler();
}

Serial1 is on port pins PB22 and PB23 that like PB02 and PB03, only have access to SERCOM5. Therefore if you use SERCOM5 for I2C on PB02/PB03, you'll lose Serial1 functionality and vice versa.

Adafruit's SAMD21 tutorial on creating a new I2C port can be found here: https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports/creating-a-new-wire.

Luckily my custom board is not using PB22 and PB23 for anything. :slightly_smiling_face:

I didn't touch variant.cpp or variant.h. With these modification to the sketch I got it working:

In declarations:

#include <Wire.h>
#include "wiring_private.h" // pinPeripheral() function

TwoWire myWire(&sercom5, 16, 17);

in setup():

myWire.begin();
// Assign pins 16 & 17 to SERCOM functionality
pinPeripheral(16, PIO_SERCOM_ALT);
pinPeripheral(17, PIO_SERCOM_ALT);

and in loop():

myWire.beginTransmission(address);
error = myWire.endTransmission();  //testing with I2C scanner

So thanks MartinL :+1:

/tipo

2 Likes

Maybe the last question on this subject...

How do switch reading two I2C interfaces?

I have this in declarations:

#include <MKRWAN.h>
#include <Arduino.h>
#include <Wire.h>
#include "wiring_private.h" // pinPeripheral() function
#include "Adafruit_SHT31.h"

TwoWire myWire(&sercom5, 16, 17);

Adafruit_SHT31 sht31_kotelo = Adafruit_SHT31(); // on default MKR1310 I2C
Adafruit_SHT31 sht31_tappi_1 = Adafruit_SHT31(); // on default MKR1310 I2C
Adafruit_SHT31 sht31_tappi_2 = Adafruit_SHT31(); // on second I2C on SERCOM5
Adafruit_SHT31 sht31_tappi_3 = Adafruit_SHT31(); // on second I2C on SERCOM5

So now when ever I read a sensor it reads only sensors in second I2C on SERCOM5.

This below doesn't work because sensor is in default MKR1310 I2C.

float temp = sht31_tappi_1.readTemperature();
float hum = sht31_tappi_1.readHumidity();

This works.

float temp = sht31_tappi_2.readTemperature();
float hum = sht31_tappi_2.readHumidity();

I can read sensors on default I2C if I comment out this:

TwoWire myWire(&sercom5, 16, 17);

Thanks,
Tipo

@tipo1000 It looks like the Adafruit SHT31 library should be able to run multiple instances of the I2C other than the default Wire library.

I think you just need to specify your myWire object as an argument to the SHT31 constructors:

Adafruit_SHT31 sht31_tappi_2 = Adafruit_SHT31(&myWire); // on second I2C on SERCOM5
Adafruit_SHT31 sht31_tappi_3 = Adafruit_SHT31(&myWire); // on second I2C on SERCOM5

Unfortunately that didn't work...

With myWire I can't read even sensors in second I2C:

Adafruit_SHT31 sht31_tappi_2 = Adafruit_SHT31(&myWire); // on second I2C on SERCOM5
Adafruit_SHT31 sht31_tappi_3 = Adafruit_SHT31(&myWire); // on second I2C on SERCOM5

Maybe with this library it would work: GitHub - RobTillaart/SHT31: Arduino library for the SHT31 temperature and humidity sensor ?

It has this option:

  • *begin(address, TwoWire wire) for platforms with multiple I2C busses.

But how exactly should I use it in this case...?

Thanks,
Tipo

Most probably like this:

#include <SHT31.h>

SHT31 sht32_tappi_2;

void setup() {
sht32_tappi_2.begin(SHT31_I2C_ADDR, &myWire);
// ...
// ...

Still trying get two I2C's to work...

  1. To make sure SERCOM I2C is working I connected SHT31 with address 0x44 to default I2C and SHT31 with address 0x45 to SERCOM I2C. I2C scanner output below seems to confirm both I2Cā€™s are working.

I2C_scanner_SAMD21.ino (2.0 KB)

I2C Scanner

Scanning myWire (sercom5 I2C)...
I2C device found at address 0x45  !
done

Scanning Wire (default I2C)...
I2C device found at address 0x44  !
done
  1. Having confirmed the above I added other SHT31 sensors' so that both I2C's have 0x44 and 0x45, and then I tried to run a sketch with Adafruit_SHT31.h library.

ATSAMD21_2xI2C_Adafruit_lib.ino (1.5 KB)

But the result is unfortunately this:

START, delay 3s...
Checking SHT31 sensors...

sht31_0 OK.
sht31_1 OK.

Checking with scope at the same time I didn't see any traffic in SERCOM I2C.

Any idea where to look to find the issue, solution?

@tipo1000 Are you also initialising the SHT31 objects with the begin(I2C_ADDR) function for the I2C addresses 0x44 and 0x45?

If possible, it might be worth posting some of your initialisation code.

Sensors are still connected like this: SHT31 with address 0x44 to default I2C and SHT31 with address 0x45 to SERCOM I2C.

If I run this:

#include <MKRWAN.h>
#include <Arduino.h>
#include <Wire.h>
#include "wiring_private.h" // pinPeripheral() function
#include "Adafruit_SHT31.h"

TwoWire myWire(&sercom5, 16, 17);

//bool enableHeater = false;
Adafruit_SHT31 sht31_0 = Adafruit_SHT31(&Wire); // on default MKR1310 I2C
Adafruit_SHT31 sht31_1 = Adafruit_SHT31(&Wire); // on default MKR1310 I2C
Adafruit_SHT31 sht31_2 = Adafruit_SHT31(&myWire); // on second I2C on SERCOM5
Adafruit_SHT31 sht31_3 = Adafruit_SHT31(&myWire); // on second I2C on SERCOM5

void setup() {

// Assign pins 16 & 17 to SERCOM functionality
pinPeripheral(16, PIO_SERCOM_ALT);
pinPeripheral(17, PIO_SERCOM_ALT);

Wire.begin();
myWire.begin();

Serial.begin(115200);

while (!Serial)
delay(10);
Serial.println("START, delay 3s...");
Serial.println("Checking SHT31 sensors...");
Serial.println();
delay(3000);

if (! sht31_0.begin(0x44)) {
Serial.println("sht31_0 not found1");
} else {
Serial.println("sht31_0 OK.");
}

if (! sht31_1.begin(0x45)) {
Serial.println("sht31_1 not found!");
} else {
Serial.println("sht31_1 OK.");
}

if (! sht31_2.begin(0x44)) {
Serial.println("sht31_2 not found!");
} else {
Serial.println("SHT31_2 OK.");
}

if (! sht31_3.begin(0x45)) {
Serial.println("sht31_3 not found!");
} else {
Serial.println("sht31_3 OK.");
}
}

void loop() {

}

I get this:

START, delay 3s...
Checking SHT31 sensors...

sht31_0 OK.
sht31_1 not found!

And if I change the order in the initialization a bit (pinPeripheral) and run this:

#include <MKRWAN.h>
#include <Arduino.h>
#include <Wire.h>
#include "wiring_private.h" // pinPeripheral() function
#include "Adafruit_SHT31.h"

TwoWire myWire(&sercom5, 16, 17);

//bool enableHeater = false;
Adafruit_SHT31 sht31_0 = Adafruit_SHT31(&Wire); // on default MKR1310 I2C
Adafruit_SHT31 sht31_1 = Adafruit_SHT31(&Wire); // on default MKR1310 I2C
Adafruit_SHT31 sht31_2 = Adafruit_SHT31(&myWire); // on second I2C on SERCOM5
Adafruit_SHT31 sht31_3 = Adafruit_SHT31(&myWire); // on second I2C on SERCOM5

void setup() {
Wire.begin();
myWire.begin();

// Assign pins 16 & 17 to SERCOM functionality
pinPeripheral(16, PIO_SERCOM_ALT);
pinPeripheral(17, PIO_SERCOM_ALT);

Serial.begin(115200);

while (!Serial)
delay(10);
Serial.println("START, delay 3s...");
Serial.println("Checking SHT31 sensors...");
Serial.println();
delay(3000);

if (! sht31_0.begin(0x44)) {
Serial.println("sht31_0 not found1");
} else {
Serial.println("sht31_0 OK.");
}

if (! sht31_1.begin(0x45)) {
Serial.println("sht31_1 not found!");
} else {
Serial.println("sht31_1 OK.");
}

if (! sht31_2.begin(0x44)) {
Serial.println("sht31_2 not found!");
} else {
Serial.println("SHT31_2 OK.");
}

if (! sht31_3.begin(0x45)) {
Serial.println("sht31_3 not found!");
} else {
Serial.println("sht31_3 OK.");
}
}

void loop() {

}

I get this:

START, delay 3s...
Checking SHT31 sensors...

sht31_0 OK.
sht31_1 not found!
sht31_2 not found!

Not sure what to think about that but it's weird that "sht31_3" never gets reported... Would be great to find something wrong in the initialization part.

Hi @tipo1000

Have you tried indiviually testing your additional myWire I2C port with only one SHT31 device attached?

This would at least confirm that the port is working as expected.

Are the SDA and SCL lines declared the right way around?:

TwoWire myWire(&sercom5, 16, 17);

It also may be an issue that sercom5 is already configured as serial port in the MKRWAN 1300's variant.cpp file?:

// Serial1
Uart Serial1(&sercom5, PIN_SERIAL1_RX, PIN_SERIAL1_TX, PAD_SERIAL1_RX, PAD_SERIAL1_TX);

void SERCOM5_Handler()
{
  Serial1.IrqHandler();
}

It might be worth trying to comment out these lines.