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?

Parents
  •  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.

    I do not see the point as to why you stopped using nrfx_twi_driver and started to make your own driver? What have to tried with nrfx_twi_driver and what failed? It does not seem productive to me to debug your custom driver when a functional driver which has been tested to work on so many products is already handy.

  • So, If I'm trying to use the nrfx_twi_driver it seems as following:

    First prepare the instance, status and config structures to use:

    static  nrfx_twi_t instance;
    nrfx_err_t volatile status;
    static nrfx_twi_config_t config;
    
    static void vI2CCallback(nrfx_twi_evt_t const * p_event,
                       void *                 p_context)
    
    {
      (void)p_event;
      (void)p_context;
    };


    Initialize the TWI

        int main()
        {
        .....
        // set address
        instance.p_twi = (NRF_TWI_Type*)(0x40004000);
        // set driver index 
        instance.drv_inst_idx = 1;
        // set confuguration (pins)
        config.scl = HOST_I2C_CLK_PIN_ID;
        config.sda = HOST_I2C_DATA_PIN_ID;
        // set confuguration (frequency)
        config.frequency = TWI_FREQUENCY_FREQUENCY_K400;
        // set interrupt priority
        
        config.interrupt_priority = 7;
        
        // be sure the instance is free
        
        nrfx_twi_uninit(&instance);
        
        // init instance 
        status = nrfx_twi_init(&instance, &config, vI2CCallback, NULL);
        if(status != NRFX_SUCCESS)
        {
          while(1)
          {
            __asm("NOP");
          }
        }
        
        }



    and trying to send some data:


        uint8_t i2CAddress = 0x68;
        uint8_t i2CTxBuffer[32];
    
        i2CTxBuffer[0] = 0x35;
        i2CTxBuffer[1] = 0x70;  
    
        status = nrfx_twi_tx(&instance, i2CAddress, i2CTxBuffer, 2, false);



    The result: Nothing on wire....

    Than I have looked to the TWI1 registers. The configuration was not the same I have tried to load (Pins are wrong, CLK shall be pin 30 and SDA the pin 31).



    Configuration structures content was also changed by the driver to something not usable.


    I could not understand why is it totally wrong and written own driver according to datasheet.

    UPDATE:

    In case I'm using a TWIM the configuration is set correctly. But the Driver does not start TX operation. After some delay the NRF_TWIM1->MAXCNT has still two bytes. 

  •     instance.p_twi = (NRF_TWI_Type*)(0x40004000;

    Why are you manually filling each and every field when there are so many helper function to aid in this. Which SDK are you using? nRF5SDK or nRF Connect SDK?

    Please try to use a template example/sample where this driver is being used and take your application ahead from there.

    It is very unproductive for you to write a new driver and me to debug a new driver. It is like re-inventing the wheel all over again.

  • It is only one field which is filled mannualy. All other fields actually shall be filled by the nrfx_ driver.
    I'm using nRF5_SDK_17.1.0. As IDE is IAR used.

    I'm setting here the link to a minimal IAR project. In case you would like to see the whole setup

    Minimal project

    In case it is not possible to download that, I post the main.x and driver files. In main.h the driver to use can be selected via define.

    #include <stdint.h>
    #include <stdbool.h>
    #include "nrf_peripherals.h"
    #include "nrf_erratas.h"
    #include "nrf.h"
    #include "system_nrf52840.h"
    #include "main.h"
    #include <nrfx_twim.h>
    #include <nrfx_twi.h>
    
    #include <nRF52_I2C.h>
    
    #ifdef CHECK_PHERIPHERIE
    #define DELAY (6400000)
    /** 
     uses to test the periphery
    */
    #define i2CAddress (0x68)
    
    uint32_t delay = DELAY;
    
    uint8_t i2CRxBuffer[32];
    uint8_t i2CTxBuffer[32];
    uint8_t dataLength;
    
    void testPeriphery();
    
    #ifdef NRF_Driver
    nrfx_err_t volatile status;
    
    #ifdef USE_TWIM
    static  nrfx_twim_t instance;
    static nrfx_twim_config_t  config;
    static void vI2CCallback(nrfx_twim_evt_t const * p_event,
                       void *                 p_context)
    {
      (void)p_event;
      (void)p_context;
    };
    #else
    #ifdef USE_TWI
    static  nrfx_twi_t instance;
    static nrfx_twi_config_t config;
    static void vI2CCallback(nrfx_twi_evt_t const * p_event,
                       void *                 p_context)
    
    {
      (void)p_event;
      (void)p_context;
    };
    #endif
    #endif
    
    #else 
    
    static void vI2CCallback(uint8_t errorCode)
    {
      (void)errorCode;
    }
    
    #endif
    #endif
    
    int main()
    {
        SystemInit();
    // for test only. 
    #ifdef CHECK_PHERIPHERIE
    // use nordic drivers
    #ifdef NRF_Driver
    #ifdef USE_TWIM   
        instance.drv_inst_idx = 0;
        instance.p_twim = (NRF_TWIM_Type*)(0x40004000);
        config.frequency = 0x06200000UL;//NRF_TWIM_FREQ_400K;
        config.scl = HOST_I2C_CLK_PIN_ID;
        config.sda = HOST_I2C_DATA_PIN_ID;
        config.interrupt_priority = 7;
        status = nrfx_twim_init(&instance, &config, vI2CCallback, NULL);
        if(status != NRFX_SUCCESS)
        {
          while(1)
          {
            __asm("NOP");
          }
        }
        
    #else
    #ifdef USE_TWI
        instance.p_twi = (NRF_TWI_Type*)(0x40004000);
        instance.drv_inst_idx = 1;
        
        config.scl = HOST_I2C_CLK_PIN_ID;
        config.sda = HOST_I2C_DATA_PIN_ID;
        config.frequency = TWI_FREQUENCY_FREQUENCY_K400;
        config.interrupt_priority = 7;
        // be sure the instance is free
        nrfx_twi_uninit(&instance);
        // init instance 
        status = nrfx_twi_init(&instance, &config, vI2CCallback, NULL);
        if(status != NRFX_SUCCESS)
        {
          while(1)
          {
            __asm("NOP");
          }
        }
    #endif
    #endif
    #else
        nRF52_I2CInit(HOST_I2C_CLK_PIN_ID, HOST_I2C_DATA_PIN_ID, K400, vI2CCompleteHandler);
    #endif
        testPeriphery();
    #else
          while(1)
          {
            __asm("NOP");
          }
    #endif
    
    }
    
    #ifdef CHECK_PHERIPHERIE
    /** 
     uses to test the periphery
    */
    void testPeriphery()
    {
      
        i2CTxBuffer[0] = 0x35; 
        i2CTxBuffer[1] = 0x70;  
    // use nordic drivers
    #ifdef NRF_Driver
    #ifdef USE_TWI
        status = nrfx_twi_tx(&instance, i2CAddress, i2CTxBuffer, 2, false);
    #else
    #ifdef USE_TWIM
        status = nrfx_twim_tx(&instance,
                            i2CAddress,
                            i2CTxBuffer,
                            2,
                            false);
    #endif
    #endif
    // use own driver
    #else
        nRF52_I2C_TX(i2CAddress, i2CTxBuffer[0], &i2CTxBuffer[1], 1);
    #endif
        // wait some time...
        while(delay > 0)
        {
           delay--;
           __asm("NOP");
        }
       // NVIC_SetPendingIRQ(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn);
        i2CTxBuffer[0] = 0x01;
        i2CTxBuffer[1] = 0xA0;
        
    // use nordic drivers
    #ifdef NRF_Driver
    #ifdef USE_TWI
        status = nrfx_twi_tx(&instance, i2CAddress, i2CTxBuffer, 2, false);
    #else
    #ifdef USE_TWIM
        status = nrfx_twim_tx(&instance,
                            i2CAddress,
                            i2CTxBuffer,
                            2,
                            false);
    #endif
    #endif
    // use own driver
    #else
        nRF52_I2C_TX(i2CAddress, i2CTxBuffer[0], &i2CTxBuffer[1], 1);
    #endif
        // wait some time...
        while(delay > 0)
        {
           delay--;
           __asm("NOP");
        }
        i2CTxBuffer[0] = 0x02;
        i2CTxBuffer[1] = 0x00;
        
    // use nordic drivers
    #ifdef NRF_Driver
    #ifdef USE_TWI
        status = nrfx_twi_tx(&instance, i2CAddress, i2CTxBuffer, 2, false);
    #else
    #ifdef USE_TWIM
        status = nrfx_twim_tx(&instance,
                            i2CAddress,
                            i2CTxBuffer,
                            2,
                            false);
    #endif
    #endif
        
    // use own driver
    #else
        nRF52_I2C_TX(i2CAddress, i2CTxBuffer[0], &i2CTxBuffer[1], 1);
    #endif
        
        while(1)
        {
    
            
        }
    }
    #endif
    

    0878.main.h

    nRF52_I2C.h

    /*
     *  Copyright (c) 2020, Kunbus GmbH
     *  All rights reserved.
     *
     *  Redistribution and use in source and binary forms, with or without
     *  modification, are permitted provided that the following conditions are met:
     *
     *  1. Redistributions of source code must retain the above copyright notice, this
     *     list of conditions and the following disclaimer.
     *
     *  2. Redistributions in binary form must reproduce the above copyright notice,
     *     this list of conditions and the following disclaimer in the documentation
     *     and/or other materials provided with the distribution.
     *
     *  3. Neither the name of the copyright holder nor the names of its
     *     contributors may be used to endorse or promote products derived from
     *     this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
     *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    #include "nRF52_I2C.h"
    #include <nrf52840_peripherals.h>
    
    
    // select the IRQ for the needed I2C. There is only 2 I2C instances
    #if (I2C_INSTANCE == 1)
    IRQn_Type I2C_IRQNr = SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQn;
    #elif (I2C_INSTANCE == 0)
    IRQn_Type I2C_IRQNr = SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn;
    #else 
    #error "The interface instance is not available"
    #endif
    
    void commonEventHandler();
    
    // get the interface pointer
    #ifdef USE_TWIM
    NRF_TWIM_Type *I2C = COMBINE_I2C_NAME(NRF_TWIM, I2C_INSTANCE); 
    uint8_t outputBuffer[32];
    #else 
    #ifdef USE_TWI
    NRF_TWI_Type *I2C = COMBINE_I2C_NAME(NRF_TWI, I2C_INSTANCE); 
    #endif
    #endif
    i2cTransaction_t i2cTransaction = 
    {
      .busy = true,
      .callback = 0,
      .lastUsedAddress = 0,
      .dataRx = 0,
      .countRx = 0,
      .dataTx = 0,
      .countTx = 0,
      .lengthRX = 0,
      .lengthTX = 0,
    };
    
    void clearPendingEvents()
    {
    #ifdef USE_TWIM
      I2C->EVENTS_LASTRX = false;
      I2C->EVENTS_LASTTX = false;
      I2C->EVENTS_RXSTARTED = false;
      I2C->EVENTS_TXSTARTED = false;
    #else 
    #ifdef USE_TWI
      I2C->EVENTS_TXDSENT = false;
      I2C->EVENTS_RXDREADY = false;
    #endif
    #endif
      I2C->ERRORSRC = 0;
      I2C->EVENTS_STOPPED = false;
      I2C->EVENTS_SUSPENDED = false;
      I2C->EVENTS_ERROR = false;
    }
    
    inline bool checkStopEvent()
    {
      if(I2C->EVENTS_STOPPED)
      {
        I2C->EVENTS_STOPPED = false; 
        if(I2C->EVENTS_ERROR == 0)
        {
          i2cTransaction.busy = false;
          if(i2cTransaction.callback != 0)
          {     
            i2cTransaction.callback(i2cTransaction.errorCode);
          }
          I2C->ENABLE = false;
        }
        return true;
      }
      return false;
    }
    
    inline bool checkErrorEvent()
    {
      if(I2C->EVENTS_ERROR)
      {
        clearPendingEvents();
        i2cTransaction.countTx = 0;
        i2cTransaction.lengthTX = 0;
        i2cTransaction.countRx = 0;
        i2cTransaction.lengthRX = 0;
        i2cTransaction.busy = false;
        if(i2cTransaction.callback != 0)
        {
          i2cTransaction.callback(i2cTransaction.errorCode);
        }
        I2C->ENABLE = false;
        return true;
      }
      return false;
    }
    
    #ifdef USE_TWIM
    inline void checkTXStartEvent()
    {
      if(I2C->EVENTS_TXSTARTED)
      {
        I2C->EVENTS_TXSTARTED = false;
      }
    }
    
    inline void checkRXStartEvent()
    {
      if(I2C->EVENTS_RXSTARTED)
      {
        I2C->EVENTS_RXSTARTED = false;
      }
    }
    
    inline void checkTXEndEvent()
    {
      if(I2C->EVENTS_LASTTX)
      {
        I2C->EVENTS_LASTTX = false;
      }
    }
    
    inline void checkRXEndEvent()
    {
      if(I2C->EVENTS_LASTRX)
      {
        I2C->EVENTS_LASTRX = false;
      }
    }
    
    #else 
    #ifdef USE_TWI
    inline bool checkTXSendEvent()
    {
      if(I2C->EVENTS_TXDSENT)
      {
        I2C->EVENTS_TXDSENT = false;
        // check the tranfer shall contunue
        if(i2cTransaction.nextOp == TX)
        {
          if(i2cTransaction.countTx < i2cTransaction.lengthTX)
          {
            I2C->TASKS_STARTTX = true;
            I2C->TXD = i2cTransaction.dataTx[i2cTransaction.countTx++];
          }
          else 
          {
            I2C->TASKS_STOP = true;
          }
        }
        else 
        {
          I2C->TASKS_STARTRX = true;
        }
        return true;
      } 
      return false;
    }
    
    inline bool checkRXSendEvent()
    {
      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;
        }
        return true;
      }
      return false;
    }
    #endif
    #endif
    
    inline void commonEventHandler()
    {
      // i2c has finisched data transfer
      i2cTransaction.errorCode = (errorCode_t)I2C->ERRORSRC;
      checkStopEvent();
    
      // check error occures -> drop function
      if(checkErrorEvent())
      {
        return;
      }
    #ifdef USE_TWIM
      checkTXStartEvent();
      checkRXStartEvent();
      checkTXEndEvent();
      checkRXEndEvent();
    #else 
    #ifdef USE_TWI
      // check data byte was send
      if(!checkTXSendEvent())
      {
        // check data byte received 
         checkRXSendEvent();
      }
    #endif
    #endif
    }
    
    
    #if (I2C_INSTANCE == 1)
    void SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler(void)
    {
      commonEventHandler();
    }
    
    #elif(I2C_INSTANCE == 0)
    void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void)
    {
      commonEventHandler();
    }
    #endif
    
    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
      nRF52_I2CSetPins(clkPin, dataPin);
      // set up clock rate
      I2C->FREQUENCY = clockRate;
      // disable shorts
      I2C->SHORTS = (0);
      // set interrupts 
    #ifdef USE_TWIM
      I2C->INTENSET = (TWIM_INTENSET_ERROR_Msk | 
                       TWIM_INTENSET_LASTTX_Msk | 
                       TWIM_INTENSET_LASTRX_Msk |
                       TWIM_INTENSET_TXSTARTED_Msk |
                       TWIM_INTENSET_RXSTARTED_Msk |
                       TWIM_INTENSET_STOPPED_Msk);
      
      I2C->INTEN =    (TWIM_INTEN_ERROR_Msk | 
                       TWIM_INTEN_LASTTX_Msk | 
                       TWIM_INTEN_LASTRX_Msk |
                       TWIM_INTEN_TXSTARTED_Msk |
                       TWIM_INTEN_RXSTARTED_Msk |
                       TWIM_INTEN_STOPPED_Msk);
    #else
    #ifdef USE_TWI
      I2C->INTENSET = (TWI_INTENSET_ERROR_Msk | 
                       TWI_INTENSET_TXDSENT_Msk | 
                       TWI_INTENSET_RXDREADY_Msk | 
                       TWI_INTENSET_STOPPED_Msk);
    #endif 
    #endif
    
      I2C->INTENCLR = 0;
      // clear all events
      clearPendingEvents();
    
      i2cTransaction.busy = false;
      
      NVIC_SetPriority(I2C_IRQNr, 7);
      NVIC_ClearPendingIRQ(I2C_IRQNr);
      NVIC_EnableIRQ(I2C_IRQNr); 
        
    }
    
    void nRF52_I2CSetAddress(uint8_t address)
    {
      if(address != i2cTransaction.lastUsedAddress)
      {
        i2cTransaction.lastUsedAddress = address;
        I2C->ADDRESS = address;
      }
    }
    
    void nRF52_I2CSetPins(uint32_t clkPin, uint32_t dataPin)
    {
      uint32_t pinConfig;
    
      pinConfig = (uint32_t)((uint32_t)(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |  
                             (uint32_t)(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
                             (uint32_t)(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
                             (uint32_t)(GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |
                             (uint32_t)(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos));
      
      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;
      }
    
      I2C->PSEL.SCL = 0x00000000  | clkPin;
      I2C->PSEL.SDA = 0x00000000  | dataPin;
    
    }
    
    bool nRF52_I2CAddressExists(uint8_t address)
    {
      i2cCallbackHandler_t callback_l = i2cTransaction.callback;
      i2cTransaction.callback = 0;
    
      if(nRF52_I2C_RX(address, 0x00, 0, 0) == I2C_SUCCESS)
      {
        i2cTransaction.callback = callback_l;
        return true;
      }
      else 
      {
        i2cTransaction.callback = callback_l;
        return false;
      }
    }
    
    #ifdef USE_TWIM
    
    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)
      {
        // copy data to internal buffer
        outputBuffer[0] = reg;
        memcpy(&outputBuffer[1], data, length);
        // prepare I2C
        I2C->ENABLE = true;
        I2C->ADDRESS = address;
        // prepare output buffer
        I2C->TXD.PTR = (uint32_t)(outputBuffer);
        I2C->TXD.MAXCNT = length + 1;
        I2C->TXD.LIST = false;
        I2C->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
        clearPendingEvents();
        NVIC_ClearPendingIRQ(I2C_IRQNr);
        // start I2C transfer
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
        
        i2cTransaction.errorCode = I2C_SUCCESS;
        i2cTransaction.busy = true;
        
        if(i2cTransaction.callback == 0)
        {
          while (i2cTransaction.busy)
          {
            __asm("NOP");
          }
          currentStatus = i2cTransaction.errorCode;
        }
      }
      return currentStatus;
    };
      
    errorCode_t nRF52_I2C_RX(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;
        // copy data to internal buffer
        outputBuffer[0] = reg;
        
        I2C->ENABLE = true;
        I2C->ADDRESS = address;
        // prepare output buffer
        I2C->TXD.PTR = (uint32_t)outputBuffer;
        I2C->TXD.MAXCNT = 1;
        I2C->TXD.LIST = false;
        
        I2C->RXD.PTR = (uint32_t)data;
        I2C->RXD.MAXCNT = length;
        I2C->RXD.LIST = false;
        // setup shorts
        I2C->SHORTS = TWIM_SHORTS_LASTTX_STARTRX_Msk;
        
        clearPendingEvents();
        NVIC_ClearPendingIRQ(I2C_IRQNr);
        // start I2C transfer
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
    
        
        if(i2cTransaction.callback == 0)
        {
          while (i2cTransaction.busy)
          {
            __asm("NOP");
          }
          currentStatus = i2cTransaction.errorCode;
        }
      }
      return currentStatus;
    };
    
    errorCode_t nRF52_I2C_FREE_TX(uint8_t address, uint8_t *data, uint8_t length)
    {
      errorCode_t currentStatus = (errorCode_t)i2cTransaction.busy;
      if(currentStatus == 0)
      {
        // start I2C transfer
        // prepare I2C
        I2C->ENABLE = true;
        I2C->ADDRESS = address;
        // prepare output buffer
        I2C->TXD.PTR = (uint32_t)data;
        I2C->TXD.MAXCNT = length;
        I2C->TXD.LIST = false;
        I2C->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
        clearPendingEvents();
        NVIC_ClearPendingIRQ(I2C_IRQNr);
        // start I2C transfer
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
        
        
        i2cTransaction.errorCode = I2C_SUCCESS;
        i2cTransaction.busy = true;
        if(i2cTransaction.callback == 0)
        {
          while (i2cTransaction.busy)
          {
            __asm("NOP");
          }
          currentStatus = i2cTransaction.errorCode;
        }
      }
      return currentStatus;
    };
    
    errorCode_t nRF52_I2C_FREE_RX(uint8_t address, uint8_t *data, uint8_t length)
    {
      errorCode_t currentStatus = (errorCode_t)i2cTransaction.busy;
      if(currentStatus == 0)
      {
        // start I2C transfer
        // prepare I2C
        I2C->ENABLE = true;
        I2C->ADDRESS = address;
        // prepare output buffer
        I2C->RXD.PTR = (uint32_t)data;
        I2C->RXD.MAXCNT = length;
        I2C->RXD.LIST = false;
        I2C->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
        // start I2C transfer
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTRX = true;
        
        clearPendingEvents();
        NVIC_ClearPendingIRQ(I2C_IRQNr);
        
        i2cTransaction.errorCode = I2C_SUCCESS;
        i2cTransaction.busy = true;
        
        if(i2cTransaction.callback == 0)
        {
          while (i2cTransaction.busy)
          {
            __asm("NOP");
          }
          currentStatus = i2cTransaction.errorCode;
        }
      }
      return currentStatus;
    };
    
    #else
    #ifdef USE_TWI
    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;
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
        clearPendingEvents();
        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;
    }
    
    errorCode_t nRF52_I2C_RX(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;
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
        clearPendingEvents();
        I2C->TXD = (uint32_t)reg;
        // preapare internal struct 
        i2cTransaction.dataRx = data;
        i2cTransaction.countRx = 0;
        i2cTransaction.lengthRX = length;
        i2cTransaction.nextOp = RX;
        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;
    }
    
    errorCode_t nRF52_I2C_FREE_TX(uint8_t address, 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;
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
        clearPendingEvents();
        I2C->TXD = (uint32_t)data[0];
        // preapare internal struct 
        i2cTransaction.dataRx = data;
        i2cTransaction.countRx = 0;
        i2cTransaction.lengthRX = length-1;
        i2cTransaction.nextOp = RX;
        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;
    }
    
    errorCode_t nRF52_I2C_FREE_RX(uint8_t address, 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;
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTRX = true;
        clearPendingEvents();
        // preapare internal struct 
        i2cTransaction.dataRx = data;
        i2cTransaction.countRx = 0;
        i2cTransaction.lengthRX = length;
        i2cTransaction.nextOp = RX;
        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;
    }
    #endif
    #endif
    


    P.S.
    Own written Radio (proprietary mode for IO-Link Wireless Protocol), and SPI drivers are working fine. So I could not understood why the I2C is not starting.
    Actually with Nordic realy grate documentation it takes less to write an own driver as to understand and find all defines I shall set to make existing driver functional :). (It is a personal think.)

Reply
  • It is only one field which is filled mannualy. All other fields actually shall be filled by the nrfx_ driver.
    I'm using nRF5_SDK_17.1.0. As IDE is IAR used.

    I'm setting here the link to a minimal IAR project. In case you would like to see the whole setup

    Minimal project

    In case it is not possible to download that, I post the main.x and driver files. In main.h the driver to use can be selected via define.

    #include <stdint.h>
    #include <stdbool.h>
    #include "nrf_peripherals.h"
    #include "nrf_erratas.h"
    #include "nrf.h"
    #include "system_nrf52840.h"
    #include "main.h"
    #include <nrfx_twim.h>
    #include <nrfx_twi.h>
    
    #include <nRF52_I2C.h>
    
    #ifdef CHECK_PHERIPHERIE
    #define DELAY (6400000)
    /** 
     uses to test the periphery
    */
    #define i2CAddress (0x68)
    
    uint32_t delay = DELAY;
    
    uint8_t i2CRxBuffer[32];
    uint8_t i2CTxBuffer[32];
    uint8_t dataLength;
    
    void testPeriphery();
    
    #ifdef NRF_Driver
    nrfx_err_t volatile status;
    
    #ifdef USE_TWIM
    static  nrfx_twim_t instance;
    static nrfx_twim_config_t  config;
    static void vI2CCallback(nrfx_twim_evt_t const * p_event,
                       void *                 p_context)
    {
      (void)p_event;
      (void)p_context;
    };
    #else
    #ifdef USE_TWI
    static  nrfx_twi_t instance;
    static nrfx_twi_config_t config;
    static void vI2CCallback(nrfx_twi_evt_t const * p_event,
                       void *                 p_context)
    
    {
      (void)p_event;
      (void)p_context;
    };
    #endif
    #endif
    
    #else 
    
    static void vI2CCallback(uint8_t errorCode)
    {
      (void)errorCode;
    }
    
    #endif
    #endif
    
    int main()
    {
        SystemInit();
    // for test only. 
    #ifdef CHECK_PHERIPHERIE
    // use nordic drivers
    #ifdef NRF_Driver
    #ifdef USE_TWIM   
        instance.drv_inst_idx = 0;
        instance.p_twim = (NRF_TWIM_Type*)(0x40004000);
        config.frequency = 0x06200000UL;//NRF_TWIM_FREQ_400K;
        config.scl = HOST_I2C_CLK_PIN_ID;
        config.sda = HOST_I2C_DATA_PIN_ID;
        config.interrupt_priority = 7;
        status = nrfx_twim_init(&instance, &config, vI2CCallback, NULL);
        if(status != NRFX_SUCCESS)
        {
          while(1)
          {
            __asm("NOP");
          }
        }
        
    #else
    #ifdef USE_TWI
        instance.p_twi = (NRF_TWI_Type*)(0x40004000);
        instance.drv_inst_idx = 1;
        
        config.scl = HOST_I2C_CLK_PIN_ID;
        config.sda = HOST_I2C_DATA_PIN_ID;
        config.frequency = TWI_FREQUENCY_FREQUENCY_K400;
        config.interrupt_priority = 7;
        // be sure the instance is free
        nrfx_twi_uninit(&instance);
        // init instance 
        status = nrfx_twi_init(&instance, &config, vI2CCallback, NULL);
        if(status != NRFX_SUCCESS)
        {
          while(1)
          {
            __asm("NOP");
          }
        }
    #endif
    #endif
    #else
        nRF52_I2CInit(HOST_I2C_CLK_PIN_ID, HOST_I2C_DATA_PIN_ID, K400, vI2CCompleteHandler);
    #endif
        testPeriphery();
    #else
          while(1)
          {
            __asm("NOP");
          }
    #endif
    
    }
    
    #ifdef CHECK_PHERIPHERIE
    /** 
     uses to test the periphery
    */
    void testPeriphery()
    {
      
        i2CTxBuffer[0] = 0x35; 
        i2CTxBuffer[1] = 0x70;  
    // use nordic drivers
    #ifdef NRF_Driver
    #ifdef USE_TWI
        status = nrfx_twi_tx(&instance, i2CAddress, i2CTxBuffer, 2, false);
    #else
    #ifdef USE_TWIM
        status = nrfx_twim_tx(&instance,
                            i2CAddress,
                            i2CTxBuffer,
                            2,
                            false);
    #endif
    #endif
    // use own driver
    #else
        nRF52_I2C_TX(i2CAddress, i2CTxBuffer[0], &i2CTxBuffer[1], 1);
    #endif
        // wait some time...
        while(delay > 0)
        {
           delay--;
           __asm("NOP");
        }
       // NVIC_SetPendingIRQ(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn);
        i2CTxBuffer[0] = 0x01;
        i2CTxBuffer[1] = 0xA0;
        
    // use nordic drivers
    #ifdef NRF_Driver
    #ifdef USE_TWI
        status = nrfx_twi_tx(&instance, i2CAddress, i2CTxBuffer, 2, false);
    #else
    #ifdef USE_TWIM
        status = nrfx_twim_tx(&instance,
                            i2CAddress,
                            i2CTxBuffer,
                            2,
                            false);
    #endif
    #endif
    // use own driver
    #else
        nRF52_I2C_TX(i2CAddress, i2CTxBuffer[0], &i2CTxBuffer[1], 1);
    #endif
        // wait some time...
        while(delay > 0)
        {
           delay--;
           __asm("NOP");
        }
        i2CTxBuffer[0] = 0x02;
        i2CTxBuffer[1] = 0x00;
        
    // use nordic drivers
    #ifdef NRF_Driver
    #ifdef USE_TWI
        status = nrfx_twi_tx(&instance, i2CAddress, i2CTxBuffer, 2, false);
    #else
    #ifdef USE_TWIM
        status = nrfx_twim_tx(&instance,
                            i2CAddress,
                            i2CTxBuffer,
                            2,
                            false);
    #endif
    #endif
        
    // use own driver
    #else
        nRF52_I2C_TX(i2CAddress, i2CTxBuffer[0], &i2CTxBuffer[1], 1);
    #endif
        
        while(1)
        {
    
            
        }
    }
    #endif
    

    0878.main.h

    nRF52_I2C.h

    /*
     *  Copyright (c) 2020, Kunbus GmbH
     *  All rights reserved.
     *
     *  Redistribution and use in source and binary forms, with or without
     *  modification, are permitted provided that the following conditions are met:
     *
     *  1. Redistributions of source code must retain the above copyright notice, this
     *     list of conditions and the following disclaimer.
     *
     *  2. Redistributions in binary form must reproduce the above copyright notice,
     *     this list of conditions and the following disclaimer in the documentation
     *     and/or other materials provided with the distribution.
     *
     *  3. Neither the name of the copyright holder nor the names of its
     *     contributors may be used to endorse or promote products derived from
     *     this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
     *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    #include "nRF52_I2C.h"
    #include <nrf52840_peripherals.h>
    
    
    // select the IRQ for the needed I2C. There is only 2 I2C instances
    #if (I2C_INSTANCE == 1)
    IRQn_Type I2C_IRQNr = SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQn;
    #elif (I2C_INSTANCE == 0)
    IRQn_Type I2C_IRQNr = SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn;
    #else 
    #error "The interface instance is not available"
    #endif
    
    void commonEventHandler();
    
    // get the interface pointer
    #ifdef USE_TWIM
    NRF_TWIM_Type *I2C = COMBINE_I2C_NAME(NRF_TWIM, I2C_INSTANCE); 
    uint8_t outputBuffer[32];
    #else 
    #ifdef USE_TWI
    NRF_TWI_Type *I2C = COMBINE_I2C_NAME(NRF_TWI, I2C_INSTANCE); 
    #endif
    #endif
    i2cTransaction_t i2cTransaction = 
    {
      .busy = true,
      .callback = 0,
      .lastUsedAddress = 0,
      .dataRx = 0,
      .countRx = 0,
      .dataTx = 0,
      .countTx = 0,
      .lengthRX = 0,
      .lengthTX = 0,
    };
    
    void clearPendingEvents()
    {
    #ifdef USE_TWIM
      I2C->EVENTS_LASTRX = false;
      I2C->EVENTS_LASTTX = false;
      I2C->EVENTS_RXSTARTED = false;
      I2C->EVENTS_TXSTARTED = false;
    #else 
    #ifdef USE_TWI
      I2C->EVENTS_TXDSENT = false;
      I2C->EVENTS_RXDREADY = false;
    #endif
    #endif
      I2C->ERRORSRC = 0;
      I2C->EVENTS_STOPPED = false;
      I2C->EVENTS_SUSPENDED = false;
      I2C->EVENTS_ERROR = false;
    }
    
    inline bool checkStopEvent()
    {
      if(I2C->EVENTS_STOPPED)
      {
        I2C->EVENTS_STOPPED = false; 
        if(I2C->EVENTS_ERROR == 0)
        {
          i2cTransaction.busy = false;
          if(i2cTransaction.callback != 0)
          {     
            i2cTransaction.callback(i2cTransaction.errorCode);
          }
          I2C->ENABLE = false;
        }
        return true;
      }
      return false;
    }
    
    inline bool checkErrorEvent()
    {
      if(I2C->EVENTS_ERROR)
      {
        clearPendingEvents();
        i2cTransaction.countTx = 0;
        i2cTransaction.lengthTX = 0;
        i2cTransaction.countRx = 0;
        i2cTransaction.lengthRX = 0;
        i2cTransaction.busy = false;
        if(i2cTransaction.callback != 0)
        {
          i2cTransaction.callback(i2cTransaction.errorCode);
        }
        I2C->ENABLE = false;
        return true;
      }
      return false;
    }
    
    #ifdef USE_TWIM
    inline void checkTXStartEvent()
    {
      if(I2C->EVENTS_TXSTARTED)
      {
        I2C->EVENTS_TXSTARTED = false;
      }
    }
    
    inline void checkRXStartEvent()
    {
      if(I2C->EVENTS_RXSTARTED)
      {
        I2C->EVENTS_RXSTARTED = false;
      }
    }
    
    inline void checkTXEndEvent()
    {
      if(I2C->EVENTS_LASTTX)
      {
        I2C->EVENTS_LASTTX = false;
      }
    }
    
    inline void checkRXEndEvent()
    {
      if(I2C->EVENTS_LASTRX)
      {
        I2C->EVENTS_LASTRX = false;
      }
    }
    
    #else 
    #ifdef USE_TWI
    inline bool checkTXSendEvent()
    {
      if(I2C->EVENTS_TXDSENT)
      {
        I2C->EVENTS_TXDSENT = false;
        // check the tranfer shall contunue
        if(i2cTransaction.nextOp == TX)
        {
          if(i2cTransaction.countTx < i2cTransaction.lengthTX)
          {
            I2C->TASKS_STARTTX = true;
            I2C->TXD = i2cTransaction.dataTx[i2cTransaction.countTx++];
          }
          else 
          {
            I2C->TASKS_STOP = true;
          }
        }
        else 
        {
          I2C->TASKS_STARTRX = true;
        }
        return true;
      } 
      return false;
    }
    
    inline bool checkRXSendEvent()
    {
      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;
        }
        return true;
      }
      return false;
    }
    #endif
    #endif
    
    inline void commonEventHandler()
    {
      // i2c has finisched data transfer
      i2cTransaction.errorCode = (errorCode_t)I2C->ERRORSRC;
      checkStopEvent();
    
      // check error occures -> drop function
      if(checkErrorEvent())
      {
        return;
      }
    #ifdef USE_TWIM
      checkTXStartEvent();
      checkRXStartEvent();
      checkTXEndEvent();
      checkRXEndEvent();
    #else 
    #ifdef USE_TWI
      // check data byte was send
      if(!checkTXSendEvent())
      {
        // check data byte received 
         checkRXSendEvent();
      }
    #endif
    #endif
    }
    
    
    #if (I2C_INSTANCE == 1)
    void SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler(void)
    {
      commonEventHandler();
    }
    
    #elif(I2C_INSTANCE == 0)
    void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void)
    {
      commonEventHandler();
    }
    #endif
    
    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
      nRF52_I2CSetPins(clkPin, dataPin);
      // set up clock rate
      I2C->FREQUENCY = clockRate;
      // disable shorts
      I2C->SHORTS = (0);
      // set interrupts 
    #ifdef USE_TWIM
      I2C->INTENSET = (TWIM_INTENSET_ERROR_Msk | 
                       TWIM_INTENSET_LASTTX_Msk | 
                       TWIM_INTENSET_LASTRX_Msk |
                       TWIM_INTENSET_TXSTARTED_Msk |
                       TWIM_INTENSET_RXSTARTED_Msk |
                       TWIM_INTENSET_STOPPED_Msk);
      
      I2C->INTEN =    (TWIM_INTEN_ERROR_Msk | 
                       TWIM_INTEN_LASTTX_Msk | 
                       TWIM_INTEN_LASTRX_Msk |
                       TWIM_INTEN_TXSTARTED_Msk |
                       TWIM_INTEN_RXSTARTED_Msk |
                       TWIM_INTEN_STOPPED_Msk);
    #else
    #ifdef USE_TWI
      I2C->INTENSET = (TWI_INTENSET_ERROR_Msk | 
                       TWI_INTENSET_TXDSENT_Msk | 
                       TWI_INTENSET_RXDREADY_Msk | 
                       TWI_INTENSET_STOPPED_Msk);
    #endif 
    #endif
    
      I2C->INTENCLR = 0;
      // clear all events
      clearPendingEvents();
    
      i2cTransaction.busy = false;
      
      NVIC_SetPriority(I2C_IRQNr, 7);
      NVIC_ClearPendingIRQ(I2C_IRQNr);
      NVIC_EnableIRQ(I2C_IRQNr); 
        
    }
    
    void nRF52_I2CSetAddress(uint8_t address)
    {
      if(address != i2cTransaction.lastUsedAddress)
      {
        i2cTransaction.lastUsedAddress = address;
        I2C->ADDRESS = address;
      }
    }
    
    void nRF52_I2CSetPins(uint32_t clkPin, uint32_t dataPin)
    {
      uint32_t pinConfig;
    
      pinConfig = (uint32_t)((uint32_t)(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |  
                             (uint32_t)(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
                             (uint32_t)(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
                             (uint32_t)(GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |
                             (uint32_t)(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos));
      
      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;
      }
    
      I2C->PSEL.SCL = 0x00000000  | clkPin;
      I2C->PSEL.SDA = 0x00000000  | dataPin;
    
    }
    
    bool nRF52_I2CAddressExists(uint8_t address)
    {
      i2cCallbackHandler_t callback_l = i2cTransaction.callback;
      i2cTransaction.callback = 0;
    
      if(nRF52_I2C_RX(address, 0x00, 0, 0) == I2C_SUCCESS)
      {
        i2cTransaction.callback = callback_l;
        return true;
      }
      else 
      {
        i2cTransaction.callback = callback_l;
        return false;
      }
    }
    
    #ifdef USE_TWIM
    
    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)
      {
        // copy data to internal buffer
        outputBuffer[0] = reg;
        memcpy(&outputBuffer[1], data, length);
        // prepare I2C
        I2C->ENABLE = true;
        I2C->ADDRESS = address;
        // prepare output buffer
        I2C->TXD.PTR = (uint32_t)(outputBuffer);
        I2C->TXD.MAXCNT = length + 1;
        I2C->TXD.LIST = false;
        I2C->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
        clearPendingEvents();
        NVIC_ClearPendingIRQ(I2C_IRQNr);
        // start I2C transfer
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
        
        i2cTransaction.errorCode = I2C_SUCCESS;
        i2cTransaction.busy = true;
        
        if(i2cTransaction.callback == 0)
        {
          while (i2cTransaction.busy)
          {
            __asm("NOP");
          }
          currentStatus = i2cTransaction.errorCode;
        }
      }
      return currentStatus;
    };
      
    errorCode_t nRF52_I2C_RX(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;
        // copy data to internal buffer
        outputBuffer[0] = reg;
        
        I2C->ENABLE = true;
        I2C->ADDRESS = address;
        // prepare output buffer
        I2C->TXD.PTR = (uint32_t)outputBuffer;
        I2C->TXD.MAXCNT = 1;
        I2C->TXD.LIST = false;
        
        I2C->RXD.PTR = (uint32_t)data;
        I2C->RXD.MAXCNT = length;
        I2C->RXD.LIST = false;
        // setup shorts
        I2C->SHORTS = TWIM_SHORTS_LASTTX_STARTRX_Msk;
        
        clearPendingEvents();
        NVIC_ClearPendingIRQ(I2C_IRQNr);
        // start I2C transfer
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
    
        
        if(i2cTransaction.callback == 0)
        {
          while (i2cTransaction.busy)
          {
            __asm("NOP");
          }
          currentStatus = i2cTransaction.errorCode;
        }
      }
      return currentStatus;
    };
    
    errorCode_t nRF52_I2C_FREE_TX(uint8_t address, uint8_t *data, uint8_t length)
    {
      errorCode_t currentStatus = (errorCode_t)i2cTransaction.busy;
      if(currentStatus == 0)
      {
        // start I2C transfer
        // prepare I2C
        I2C->ENABLE = true;
        I2C->ADDRESS = address;
        // prepare output buffer
        I2C->TXD.PTR = (uint32_t)data;
        I2C->TXD.MAXCNT = length;
        I2C->TXD.LIST = false;
        I2C->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
        clearPendingEvents();
        NVIC_ClearPendingIRQ(I2C_IRQNr);
        // start I2C transfer
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
        
        
        i2cTransaction.errorCode = I2C_SUCCESS;
        i2cTransaction.busy = true;
        if(i2cTransaction.callback == 0)
        {
          while (i2cTransaction.busy)
          {
            __asm("NOP");
          }
          currentStatus = i2cTransaction.errorCode;
        }
      }
      return currentStatus;
    };
    
    errorCode_t nRF52_I2C_FREE_RX(uint8_t address, uint8_t *data, uint8_t length)
    {
      errorCode_t currentStatus = (errorCode_t)i2cTransaction.busy;
      if(currentStatus == 0)
      {
        // start I2C transfer
        // prepare I2C
        I2C->ENABLE = true;
        I2C->ADDRESS = address;
        // prepare output buffer
        I2C->RXD.PTR = (uint32_t)data;
        I2C->RXD.MAXCNT = length;
        I2C->RXD.LIST = false;
        I2C->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
        // start I2C transfer
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTRX = true;
        
        clearPendingEvents();
        NVIC_ClearPendingIRQ(I2C_IRQNr);
        
        i2cTransaction.errorCode = I2C_SUCCESS;
        i2cTransaction.busy = true;
        
        if(i2cTransaction.callback == 0)
        {
          while (i2cTransaction.busy)
          {
            __asm("NOP");
          }
          currentStatus = i2cTransaction.errorCode;
        }
      }
      return currentStatus;
    };
    
    #else
    #ifdef USE_TWI
    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;
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
        clearPendingEvents();
        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;
    }
    
    errorCode_t nRF52_I2C_RX(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;
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
        clearPendingEvents();
        I2C->TXD = (uint32_t)reg;
        // preapare internal struct 
        i2cTransaction.dataRx = data;
        i2cTransaction.countRx = 0;
        i2cTransaction.lengthRX = length;
        i2cTransaction.nextOp = RX;
        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;
    }
    
    errorCode_t nRF52_I2C_FREE_TX(uint8_t address, 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;
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTTX = true;
        clearPendingEvents();
        I2C->TXD = (uint32_t)data[0];
        // preapare internal struct 
        i2cTransaction.dataRx = data;
        i2cTransaction.countRx = 0;
        i2cTransaction.lengthRX = length-1;
        i2cTransaction.nextOp = RX;
        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;
    }
    
    errorCode_t nRF52_I2C_FREE_RX(uint8_t address, 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;
        I2C->TASKS_RESUME = true;
        I2C->TASKS_STARTRX = true;
        clearPendingEvents();
        // preapare internal struct 
        i2cTransaction.dataRx = data;
        i2cTransaction.countRx = 0;
        i2cTransaction.lengthRX = length;
        i2cTransaction.nextOp = RX;
        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;
    }
    #endif
    #endif
    


    P.S.
    Own written Radio (proprietary mode for IO-Link Wireless Protocol), and SPI drivers are working fine. So I could not understood why the I2C is not starting.
    Actually with Nordic realy grate documentation it takes less to write an own driver as to understand and find all defines I shall set to make existing driver functional :). (It is a personal think.)

Children
No Data
Related