Using two i2c ports simultaneously

Hello,

I am doing a project that involves using multiple devices through i2c ports on Arduino Due.

I did manage to fix the common issue of Wire.h library not being recognised by SDA1 and SCL1 by replacing everything with Wire1.(xxxx) and using pull up resistors. So I can control a device with one i2c or the other.

However, my question is now: how can I use both SDA1, SCL1 and SDA 20, SCL 21 at the same time? Is there a wire library that can do that? Or Due was not intended for such use?

Cheers!

That’s an interesting question. :slight_smile:

I guess you know you can connect several I2C slaves on the same bus (select TWI0 (SDA1/SCL1) or TWI1 (SDA/SCL) and use the library).

The 2 I2C buses can work together, but the Wire.h library is not intended for that purpose. And if your DUE board has to be an I2C master for a bunch of I2C slaves, and at the same time an I2C slave for an I2C master device, you will meet the same issue.

To make the 2 I2C buses work at the same time, I see two possibilities:

Make your own library , from Wire.h, by duplicating all functions(but will not solve the DUE as I2C slave), or Program directly the TWI registers.

here is an example sketch to test the second solution with a single board (note that the temperature sensor is not precise, but this is not the point for this sketch):

/******************************************************************************/
/************    Master TWI0, Slave TWI1, temperature reading     *************/
/******************************************************************************/
#define read  1
#define write 0
#define TemperatureAddress      (0b1001000)

/*********************     Init TWIn      ************************/

void I2c_Init(Twi* pTWI, boolean Master) {

  if (pTWI == TWI0) {
    PMC->PMC_PCER0 |= PMC_PCER0_PID22;      // TWI0 power ON
    PIOA->PIO_PDR |= PIO_PDR_P17            // Enable peripheral control
                     | PIO_PDR_P18;
    PIOA->PIO_ABSR &= ~(PIO_PA17A_TWD0      // TWD0 & TWCK0 Peripherals type A
                        | PIO_PA18A_TWCK0);
  }
  else {
    PMC->PMC_PCER0 |= PMC_PCER0_PID23;      // TWI1 power ON
    PIOB->PIO_PDR |= PIO_PDR_P13            // Enable peripheral control
                     | PIO_PDR_P12;
    PIOB->PIO_ABSR &= ~(PIO_PB12A_TWD1      // TWD1 & TWCK1 Peripherals type A
                        | PIO_PB13A_TWCK1);
  }
  // I2C lines are Open drain by hardware, no need to program PIO_ODER

  pTWI->TWI_CR = TWI_CR_SWRST;  // TWIn software reset
  pTWI->TWI_RHR;                // Flush reception buffer

  pTWI->TWI_CR = TWI_CR_SVDIS | TWI_CR_MSDIS; // Disable Master and Slave modes

  //Enable master mode
  if (Master == 1) {
    //enter slave address
    pTWI->TWI_MMR |= TWI_MMR_DADR(TemperatureAddress);

    pTWI->TWI_CR = TWI_CR_MSEN;  // Master mode enable
    //clockwave from 100khz to 400khz
    SetClock(pTWI, 400000); // from 100000 to 400000
  }
  else { // Enable Slave mode
    pTWI->TWI_SMR = TWI_SMR_SADR(TemperatureAddress);
    pTWI->TWI_CR = TWI_CR_SVEN;     // Slave mode enable
  }
}

/****************************     Start    ****************************/

void I2c_Start(Twi* pTWI, uint8_t slave_address, uint8_t mread) { //read=1, write=0
  //set slave address
  pTWI->TWI_MMR = (pTWI->TWI_MMR & ~TWI_MMR_DADR_Msk)
                  | TWI_MMR_DADR(slave_address);
  //set read/write direction
  if (mread == write) { //write
    pTWI->TWI_MMR &= ~TWI_MMR_MREAD;
  }
  else if (mread == read) { //read
    pTWI->TWI_MMR |= TWI_MMR_MREAD;
  }
  //send start
  pTWI->TWI_CR |= TWI_CR_START;

  //wait for ack
  while (!(pTWI->TWI_SR & TWI_SR_TXRDY));
}

/***************************   Stop    ****************************/

void I2c_Stop(Twi* pTWI) {
  pTWI->TWI_CR |= TWI_CR_STOP;
}

/**********************     Read 1 byte   **************************/

uint8_t I2c_ReadByte(Twi* pTWI) {
  uint8_t receivedByte;
  //If the stop bit in the control register is not set,
  //Sam3x will automatically ACK after reading TWIn_RHR register
  //RXRDY will be set when data arrives in TWIn_RHR register

  while (!(pTWI->TWI_SR & TWI_SR_RXRDY));
  //reading data will clear RXRDY bit in the status register
  receivedByte = pTWI->TWI_RHR;
  return receivedByte;
}

/*****************     Read the last byte   ***********************/

uint8_t I2c_ReadLastByte(Twi* pTWI) {
  uint8_t receivedByte;
  //Sam3x requires stop bit to be set before data is set on the TWIn_RHR
  //when stop bit is set, Sam3x will send a NACK instead of an ACK automatically
  I2c_Stop(pTWI);
  //When data arrives in the TWIn_RHR register RXRDY is set in the Status Register
  while (!(pTWI->TWI_SR & TWI_SR_RXRDY));
  //reading data will clear RXRDY bit in the status register
  receivedByte = pTWI->TWI_RHR;
  while (!(pTWI->TWI_SR & TWI_SR_TXCOMP));
  return receivedByte;
}

/************************     Write 1 byte     *******************/

void I2c_WriteByte(Twi* pTWI, uint8_t data) {
  //write data or slave register to THR
  pTWI->TWI_THR |= data;
  //wait for ack
  while (!(pTWI->TWI_SR & TWI_SR_TXRDY));
}

/***********************    Write last byte    *******************/

void I2c_WriteLastByte(Twi* pTWI, uint8_t data) {
  //write data or slave register to THR
  pTWI->TWI_THR |= data;
  I2c_Stop(pTWI);
  //wait for ack
  while (!(pTWI->TWI_SR & TWI_SR_TXRDY));
  while (!(pTWI->TWI_SR & TWI_SR_TXCOMP));
}

/*****************   Set Interrupt Configuration   *********************/

void I2c_Interrupt(Twi* pTWI, uint32_t InterrConfig) {

  pTWI->TWI_IER = InterrConfig;
  if (pTWI == TWI0) {
    NVIC_EnableIRQ(TWI0_IRQn);
  }
  else {
    NVIC_EnableIRQ(TWI1_IRQn);
  }
}

/*******************        TWI1 Handler    ***********************/

void TWI1_Handler(void) {

  uint32_t status = TWI1->TWI_SR;

  if (status & TWI_SR_SVREAD) {
    TWI1->TWI_RHR;
    TWI1->TWI_THR = temperature();
  }
}

/**********************    Configure clock    *********************/

void SetClock(Twi* pTWI , uint32_t frequency) {
  uint32_t CLDIV = 0;
  uint32_t CKDIV = 0;
  uint8_t readyByte = 0;

  while (!readyByte) {
    CLDIV = ((VARIANT_MCK / (2 * frequency)) - 4) / (1 << CKDIV) ;
    if ( CLDIV <= 255 ) {
      readyByte = 1 ;
    }
    else {
      CKDIV++ ;
    }
  }
  pTWI->TWI_CWGR = (CKDIV << 16) | (CLDIV << 8) | CLDIV;
}

void setup() {

  // Power OFF all peripherals
  PMC->PMC_PCDR0 = 0xFFFFFFFF;
  PMC->PMC_PCDR1 = 0xFFFFFFFF;
  Serial.begin(250000);
  PMC->PMC_PCER1 |= PMC_PCER1_PID37;  // ADC power ON
  ADC->ADC_ACR |= ADC_ACR_TSON;       // Temperature sensor ON
  
  I2c_Init(TWI0, 1);  // TWI0 Master
  I2c_Init(TWI1, 0);  // TWI1 Slave
  uint32_t InterruptConfig = TWI_IER_SVACC;  // | TWI_IER_SVREAD
  I2c_Interrupt(TWI1, InterruptConfig);

}

void loop() {

  uint8_t ReceivedByte;
  static uint32_t Oldmillis;

  if ((millis() - Oldmillis) > 1000) {
    Oldmillis = millis();
    I2c_Start(TWI0, TemperatureAddress, 1);
    ReceivedByte = I2c_ReadLastByte(TWI0);
    printf("%d\xB0" "C\n", ReceivedByte );
  }
}

/*****************   Temperature function    *******************/

uint8_t temperature() {
  float trans = 3.3 / 4096;
  float offset = 0.76;
  float factor = 0.00265;
  float fixtemp = 15;
  uint32_t ulValue;
  float treal;
  uint8_t __treal;

  ADC->ADC_CHER |= 1 << ADC_TEMPERATURE_SENSOR;

  ADC->ADC_CR = ADC_CR_START;

  while (!(ADC->ADC_ISR & ADC_ISR_DRDY));

  ulValue = ADC->ADC_LCDR;

  ADC->ADC_CHDR |= 1 << ADC_TEMPERATURE_SENSOR;

  treal = fixtemp + (( trans * ulValue ) - offset ) / factor;
  __treal = (uint8_t) treal;
  return __treal;
}

the Wire.h library is not intended for that purpose

what?
just a moment - do you want to express that

#include <Wire.h>
Wire.begin();     // SCL, SDA: providig internal pullups
Wire1.begin();    // SCL1, SDA1: add external pullups when i2c master (not if slave)

will NOT work on the Due for both i2c busses in the same sketch (IDE 1.8.5) ? :o