Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

A problem with I2C on NRF52840 using Instance NRF_TWI1

Hi experts,

 I habe a following issue. First I have tried to use nrfx_twi driver. There was any data send over I2C...
So I have written own driver using the Datasheet and also received a problem. After sending the data
the RX interrupt is rising. Which causes a logical error. How th driver was realized:

First of all the initial

#define I2C_INSTANCE 1

NRF_TWI_Type *I2C = COMBINE_I2C_NAME(NRF_TWI, I2C_INSTANCE); 


void nRF52_I2CInit(uint32_t clkPin, uint32_t dataPin, enum i2CClockRate clockRate, i2cCallbackHandler_t callback)
{
  i2cTransaction.callback = callback;
  i2cTransaction.busy = true;
  // stop I2C
  I2C->ENABLE = false;
  
  // configure pins
  uint32_t pinConfig;
  // set pin configuration
  pinConfig = (uint32_t)((uint32_t)INPUT |  
                         (uint32_t)CONNECTED |
                         (uint32_t)PULLUP |
                         (uint32_t)S0D1 |
                         (uint32_t)SENSE_DIS);
  // load configuration
  if(clkPin < P0_PIN_NUM)
  {
    NRF_P0->PIN_CNF[clkPin] = pinConfig; 
  }
  else
  {
    NRF_P1->PIN_CNF[(clkPin & 0x1F)] = pinConfig;
  }
  if(dataPin < P0_PIN_NUM)
  {
    NRF_P0->PIN_CNF[dataPin] = pinConfig; 
  }
  else
  {
    NRF_P1->PIN_CNF[(dataPin & 0x1F)] = pinConfig;
  }
  // connect pins to TWI
  I2C->PSEL.SCL = clkPin;
  I2C->PSEL.SDA = dataPin;
  
  // set up clock rate
  I2C->FREQUENCY = clockRate;
  // disable shorts
  I2C->SHORTS = (TWI_SHORTS_BB_STOP_Disabled | TWI_SHORTS_BB_SUSPEND_Disabled);
  // set interrupts
  I2C->INTENSET = (TWI_INTENSET_ERROR_Msk |
                   TWI_INTENSET_TXDSENT_Msk |
                   TWI_INTENSET_RXDREADY_Msk |
                   TWI_INTENSET_STOPPED_Msk);
   // clear all events
  I2C->EVENTS_RXDREADY = 0;
  I2C->EVENTS_STOPPED = 0;
  I2C->EVENTS_SUSPENDED = 0;
  I2C->EVENTS_TXDSENT = 0;
  I2C->EVENTS_ERROR = 0;

  i2cTransaction.busy = false;
 
  NVIC_SetPriority(I2C_IRQNr, 7);
  NVIC_ClearPendingIRQ(I2C_IRQNr);
  NVIC_EnableIRQ(I2C_IRQNr);
    
}

After the I2C is configured, a data transfer try was performed:

errorCode_t nRF52_I2C_TX(uint8_t address, uint8_t reg, uint8_t *data, uint8_t length)
{
  errorCode_t currentStatus = (errorCode_t)i2cTransaction.busy;
  if(currentStatus == 0)
  {
    // start I2C transfer
    I2C->ENABLE = true;
    I2C->ADDRESS = address;
    // clear all events
    I2C->TASKS_STARTTX = 0;
    I2C->EVENTS_RXDREADY = 0;
    I2C->EVENTS_STOPPED = 0;
    I2C->EVENTS_SUSPENDED = 0;
    I2C->EVENTS_TXDSENT = 0;
    I2C->EVENTS_ERROR = 0;
    // clear interrupt
    NVIC_ClearPendingIRQ(I2C_IRQNr);
    
    I2C->TXD = reg;
    // preapare internal struct 
    i2cTransaction.dataTx = data;
    i2cTransaction.countTx = 0;
    i2cTransaction.lengthTX = length;
    i2cTransaction.nextOp = TX;
    i2cTransaction.errorCode = I2C_SUCCESS;
    i2cTransaction.busy = true;
    // there is no callback registred -> use blocking
    if(i2cTransaction.callback == 0)
    {
      while (i2cTransaction.busy)
      {
        __asm("NOP");
      }
      currentStatus = i2cTransaction.errorCode;
    }
  }
  return currentStatus;
}

The event Manager was realized as following:

void SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler(void)
{
  // i2c has an event
  // get current error code:
  i2cTransaction.errorCode = (errorCode_t)I2C->ERRORSRC;
  // check is the transaction ends?
  if(I2C->EVENTS_STOPPED)
  {
    I2C->EVENTS_STOPPED = false; 
    i2cTransaction.busy = false;
    if(i2cTransaction.callback != 0)
    {     
      i2cTransaction.callback(i2cTransaction.errorCode);
    }
    I2C->ENABLE = false;
  }
  // is an error occures?
  if(I2C->EVENTS_ERROR)
  {
    // drop all other events
    I2C->EVENTS_RXDREADY = 0;
    I2C->EVENTS_STOPPED = 0;
    I2C->EVENTS_SUSPENDED = 0;
    I2C->EVENTS_TXDSENT = 0;
    I2C->EVENTS_ERROR = 0;
    // clear transaction struct
    i2cTransaction.countRx = 0;
    i2cTransaction.lengthRX = 0;
    i2cTransaction.countRx = 0;
    i2cTransaction.lengthRX = 0;
    i2cTransaction.busy = false;
    
    if(i2cTransaction.callback != 0)
    {
      i2cTransaction.callback(i2cTransaction.errorCode);
    }
    I2C->ENABLE = false;
  }
  else 
  {
    // i2c has send a byte
    if(I2C->EVENTS_TXDSENT)
    {
      I2C->EVENTS_TXDSENT = false;
      // check the operation is transfer only 
      if(i2cTransaction.nextOp == TX)
      {
        // check the tranfer shall contunue
        if(i2cTransaction.countTx < i2cTransaction.lengthTX)
        {
          I2C->TASKS_STARTTX = true;
          I2C->TXD = i2cTransaction.dataTx[i2cTransaction.countTx++];
        }
        else 
        {
          I2C->TASKS_STOP = true;
        }
      }
      else 
      {
        // operation is "read register", the read is following 
        I2C->TASKS_STARTRX = true;
      }
    } 
    else if(I2C->EVENTS_RXDREADY) // i2c has received a byte
    {
      I2C->EVENTS_RXDREADY = false;
      if(i2cTransaction.countRx < i2cTransaction.lengthRX)
      {
        i2cTransaction.dataRx[i2cTransaction.countRx++] = I2C->RXD; 
        I2C->TASKS_STARTRX = true;
      }
      else
      {
        I2C->TASKS_STOP = true;
        i2cTransaction.dataRx[i2cTransaction.countRx++] = I2C->RXD; 
        i2cTransaction.busy = false;
      }
    }
  }
}


After initialization the following data was send:

uint8_t i2CTxBuffer[2];

i2CTxBuffer[0] = 0x35;
i2CTxBuffer[1] = 0x70; 

// send to addess 0x3C, to register 0x35, value 0x70
nRF52_I2C_TX(0x3C, i2CTxBuffer[0], &i2CTxBuffer[1], 1);


As can be seen of  the figure below, the TWI do not generate any start/stop conditions. The first and the last byte is send if 0x35. After the firts byte were proessed
the TWI genareted EVENTS_RXDREADY event. Which does not pass, and causes an internal logical error...

Scope picture

Could you explain, what I'm doing wrong?

  • Semiory said:
    Nevertheless, I dont know how to get original Nordic nrfx_twi_xxx or nrfx_twim_xxx part working. Disappointed And it will be grate if you say what I'm doing wrong there.

    You seems to have managed to write a driver on yourself, That shows that you have good understanding of the hardware. When using nrfx_twi_xxx api, after configuration you could connect the debugger just before sending data to see if all the twi registers are configured correctly. Then you could step through the nrfx_twi_ data transfer function to see why there is not data being sent on the serial lines.

Related