This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Conversion from NRFX_TWI to NRFX_TWIM

HI,

I am working on the interface between the nrf52832 and the MPU9250 IMU.

I have communication working fine with using nrfx_twi, but I have the following error when I try to use  nrfx_twim :

<error> app: ASSERTION FAILED at /Users/slareau/nrf52_development/nRF5_SDK_16/modules/nrfx/drivers/src/nrfx_twim.c:561

I can get WHOIAM working indicating that the communication between the NRF52 and the IMU is working.

The error occur when I call the IMU self-test.

Looking nrfx_twim.c line 561 is :  p_xfer_desc->secondary_length))    from the following code:

nrfx_err_t nrfx_twim_xfer(nrfx_twim_t           const * p_instance,
                          nrfx_twim_xfer_desc_t const * p_xfer_desc,
                          uint32_t                      flags)
{
    NRFX_ASSERT(TWIM_LENGTH_VALIDATE(p_instance->drv_inst_idx,
                                     p_xfer_desc->primary_length,
                                     p_xfer_desc->secondary_length));

    nrfx_err_t err_code = NRFX_SUCCESS;

 I have include the init / read / write driver below.  Those came from an old exemple (SDK12)  that I have modernize as I could - I don't have a lot of experience and nrfx TWI and TWIM exemple are rare...  As I mention, the nrfx_twi version work fine (except for a compass calibration issue - might be related to communication issue or not).

To create the TWIM version, i have juste replace TWI by TWIM in appropriate place, and change the SDK_config setting.

How can I fix this ? Your help is always greatly appreciate.

Regards,

 /*
  * The library is not extensively tested and only
  * meant as a simple explanation and for inspiration.
  * NO WARRANTY of ANY KIND is provided.
  */


#if defined(MPU_USES_TWI) // Use TWI drivers

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "nrfx_twim.h"
#include "nrf_drv_mpu.h"
#include "app_util_platform.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"

#include "nrf_log.h"



/* Pins to connect MPU. Pinout is different for nRF51 DK and nRF52 DK
 * and therefore I have added a conditional statement defining different pins
 * for each board. This is only for my own convenience. 
 */
/*
 #if defined BOARD_PCA10040
   #define MPU_TWI_SDA_PIN 26
  #define MPU_TWI_SCL_PIN 27

 # elif  BOARD_HOL18008
 #define MPU_TWI_SDA_PIN 11 // HOL18008
 #define MPU_TWI_SCL_PIN 12 
 #endif
*/
 //#define MPU_TWI_SDA_PIN 26
 //#define MPU_TWI_SCL_PIN 27  // HOL18008 + MPU9255 ext
  
  #define MPU_TWI_SDA_PIN 22
  #define MPU_TWI_SCL_PIN 24  // Ebyte73 + MPU9255 ext
  


#define MPU_TWI_BUFFER_SIZE     	14 // 14 byte buffers will suffice to read acceleromter, gyroscope and temperature data in one transmission.
#define MPU_TWI_TIMEOUT 		15000 //10000 SL
#define MPU_ADDRESS     		0x68 
#define MPU_AK89XX_MAGN_ADDRESS         0x0C


static const nrfx_twim_t m_twi_instance = NRFX_TWIM_INSTANCE(0);
volatile static bool twi_tx_done = false;
volatile static bool twi_rx_done = false;

uint8_t twi_tx_buffer[MPU_TWI_BUFFER_SIZE];

static void twi_event_handler(nrfx_twim_evt_t const * p_event, void * p_context)
{
    switch(p_event->type)
    {
        case NRFX_TWIM_EVT_DONE:
            switch(p_event->xfer_desc.type)
            {
                case NRFX_TWIM_XFER_TX:
                    twi_tx_done = true;
                    break;
                case NRFX_TWIM_XFER_TXTX:
                    twi_tx_done = true;
                    break;
                case NRFX_TWIM_XFER_RX:
                    twi_rx_done = true;
                    break;
                case NRFX_TWIM_XFER_TXRX:
                    twi_rx_done = true;
                    break;
                default:
                    break;
            }
            break;
        case NRFX_TWIM_EVT_ADDRESS_NACK:
            break;
        case NRFX_TWIM_EVT_DATA_NACK:
            break;
        default:
            break;
    }
}



/**
 * @brief TWI initialization.
 * Just the usual way. Nothing special here
 */
uint32_t nrf_drv_mpu_init(void)
{
    uint32_t err_code;
    
    const nrfx_twim_config_t twi_mpu_config = {
       .scl                = MPU_TWI_SCL_PIN,
       .sda                = MPU_TWI_SDA_PIN,
       .frequency          = NRF_TWIM_FREQ_400K
     //  .interrupt_priority = APP_IRQ_PRIORITY_HIGHEST,
       //.clear_bus_init     = false
    };
    
    err_code = nrfx_twim_init(&m_twi_instance, &twi_mpu_config, twi_event_handler, NULL);
    if(err_code != NRF_SUCCESS)
	{
		return err_code;
	}
    
    nrfx_twim_enable(&m_twi_instance);
	
	return NRF_SUCCESS;
}




// The TWI driver is not able to do two transmits without repeating the ADDRESS + Write bit byte
// Hence we need to merge the MPU register address with the buffer and then transmit all as one transmission
static void merge_register_and_data(uint8_t * new_buffer, uint8_t reg, uint8_t * p_data, uint32_t length)
{
    new_buffer[0] = reg;
    memcpy((new_buffer + 1), p_data, length);
}


uint32_t  nrf_mpu_write(uint8_t slave_addr, uint8_t reg_addr, uint8_t length, uint8_t  * p_data)
//uint32_t  Sensors_I2C_WriteRegister(uint8_t slave_addr, uint8_t reg_addr, uint8_t length, uint8_t  * p_data)
{
    // This burst write function is not optimal and needs improvement.
    // The new SDK 11 TWI driver is not able to do two transmits without repeating the ADDRESS + Write bit byte
    uint32_t err_code;
    uint32_t timeout = MPU_TWI_TIMEOUT;

    uint8_t twi_tx_buffer[length+1];
    twi_tx_buffer[0] = reg_addr;
    memcpy(&twi_tx_buffer[1], p_data, length);

    // Merging MPU register address and p_data into one buffer.
    //merge_register_and_data(twi_tx_buffer, reg_addr, p_data, length);
     
    // Setting up transfer
    nrfx_twim_xfer_desc_t xfer_desc;
    xfer_desc.address = MPU_ADDRESS;
    xfer_desc.type = NRFX_TWIM_XFER_TX;
    xfer_desc.primary_length = length + 1;
    xfer_desc.p_primary_buf = twi_tx_buffer;

    // Transferring
    err_code = nrfx_twim_xfer(&m_twi_instance, &xfer_desc, 0);

    while((!twi_tx_done) && --timeout);
    if(!timeout) return NRF_ERROR_TIMEOUT;
    twi_tx_done = false;

    return err_code;
}




uint32_t  nrf_mpu_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t length, uint8_t  * p_data)
//uint32_t  Sensors_I2C_ReadRegister(uint8_t slave_addr, uint8_t reg_addr, uint8_t length, uint8_t  * p_data)
{
    uint32_t err_code;
    uint32_t timeout = MPU_TWI_TIMEOUT;

    //NRF_LOG_INFO("Read twi");

    err_code = nrfx_twim_tx(&m_twi_instance, MPU_ADDRESS, &reg_addr, 1, false);
    if(err_code != NRF_SUCCESS) return err_code;

    //nrf_delay_ms(1); does not help

    //NRF_LOG_INFO("Read twi");

    while((!twi_tx_done) && --timeout);
    if(!timeout) return NRF_ERROR_TIMEOUT;
    twi_tx_done = false;

    err_code = nrfx_twim_rx(&m_twi_instance, MPU_ADDRESS, p_data, length);
    if(err_code != NRF_SUCCESS) return err_code;

    timeout = MPU_TWI_TIMEOUT;
    while((!twi_rx_done) && --timeout);
    if(!timeout) return NRF_ERROR_TIMEOUT;
    twi_rx_done = false;

    return err_code;

}


#endif // Use TWI drivers

/**
  @}
*/

<error> app: ASSERTION FAILED at /Users/slareau/nrf52_development/nRF5_SDK_16/modules/nrfx/drivers/src/nrfx_twim.c:561

  • Hi,

    What is the maximum length you are sending in a single TWI transfer? Note that the TWIM peripheral on nRF52832 only supports up to 255 bytes (8-bit length/MAXCNT register). 

    If you need longer transfers, you have to split into multiple transfers or use the legacy TWI peripheral (or switch to nRF52840, which supports 16-bit transfer length).

    Best regards,
    Jørgen

  • Thanks for the info, but I don't think its the issue.   In the following test program base on Teiscanner exemple.  I had put a 1 ms delay in the init sequence to work.  Without it, it won't init.  nrfx_TWI work fine, its inly the TWIM version that need this fix.  Once init, the scanning part work.   Then, if I want to read the 1 byte register WHOAMI,  TWI work, but not TWIM.  I dont have an error message, but the program reboot in a loop.

    Here is the code.

    //USE TWIM
    
    //work OK with all TWIM  TWIM0 set to 1.  and TWI to 0
    //SHould use TWIM because modern vsTWI old
    
    
    //<warning> TWIM: Function: nrfx_twim_init, error code: NRF_ERROR_INVALID_STATE.
    //<error> app: Fatal error
    
    
    
    
    /**
     * Copyright (c) 2016 - 2019, Nordic Semiconductor ASA
     *
     * 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, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, 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 Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
     *
     */
    /** @file
     * @defgroup tw_scanner main.c
     * @{
     * @ingroup nrf_twi_example
     * @brief TWI Sensor Example main file.
     *
     * This file contains the source code for a sample application using TWI.
     *
     */
    
    #include <stdio.h>
    #include "boards.h"
    #include "app_util_platform.h"
    #include "app_error.h"
    //#include "nrfx_twi.h"
    #include "nrfx_twim.h"
    #include "nrf_delay.h"
    
    
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
      #define MPU_TWI_SDA_PIN 26
      #define MPU_TWI_SCL_PIN 27  // PCA10040
    
    
      //#define MPU_TWI_SDA_PIN 22
     // #define MPU_TWI_SCL_PIN 24  // Ebyte73 + MPU9255 ext
    
    
    /* TWI instance ID. */
    #if TWI0_ENABLED
    #define TWI_INSTANCE_ID     0
    #elif TWI1_ENABLED
    #define TWI_INSTANCE_ID     1
    #endif
    
     /* Number of possible TWI addresses. */
     #define TWI_ADDRESSES      127
    
      #define MPU_ADRESS       0x68  // SL
        #define MPU_WHOIAM       0x00  // SL 
    
    /* TWI instance. */
    //static const nrfx_twi_t m_twi = NRFX_TWI_INSTANCE(TWI_INSTANCE_ID);
    static const nrfx_twim_t m_twi = NRFX_TWIM_INSTANCE(0);
    
    
    
    /**
     * @brief TWI initialization.
     */
    void twi_init (void)
    {
        ret_code_t err_code;
    
        const nrfx_twim_config_t twi_config = {
           .scl                = MPU_TWI_SCL_PIN, //27,  //ARDUINO_SCL_PIN,
           .sda                = MPU_TWI_SDA_PIN, //26, // ARDUINO_SDA_PIN,
           .frequency          = NRF_TWIM_FREQ_400K
           //.interrupt_priority = APP_IRQ_PRIORITY_HIGH  => set sdk_config
          // .clear_bus_init     = false
        };
    
        err_code = nrfx_twim_init(&m_twi, &twi_config, NULL, NULL);
        nrf_delay_ms(1);  //important
        APP_ERROR_CHECK(err_code);
        
        nrfx_twim_enable(&m_twi);
     
    }
    
    uint32_t mpu_read_whoAmI()
    {   ret_code_t err_code;
        uint8_t result;
         
    
        NRF_LOG_INFO("WHO I AM 2: " );
        NRF_LOG_FLUSH();
    
        err_code = nrfx_twim_tx(&m_twi, MPU_ADRESS, MPU_WHOIAM, 1, false);
        
        APP_ERROR_CHECK(err_code);
    
        nrf_delay_ms(1);
        
        err_code = nrfx_twim_rx(&m_twi, MPU_ADRESS, &result, sizeof(result));
        APP_ERROR_CHECK(err_code);
        
       NRF_LOG_INFO("WHO I AM (ICM-20948 should be 0xEA) : 0x%02x", result );
       NRF_LOG_FLUSH();
       
    }
    
    
    
    int main(void)
    {
        ret_code_t err_code;
        uint8_t address;
        uint8_t sample_data;
        bool detected_device = false;
    
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        NRF_LOG_INFO("TWI scanner started.");
        NRF_LOG_FLUSH();
        twi_init();
    
        for (address = 1; address <= TWI_ADDRESSES; address++)
        {
            err_code = nrfx_twim_rx(&m_twi, address, &sample_data, sizeof(sample_data));
            nrf_delay_ms(1);
            if (err_code == NRF_SUCCESS)
            {
                detected_device = true;
                NRF_LOG_INFO("TWI device detected at address 0x%x.", address);
            }
            NRF_LOG_FLUSH();
        }
    
        if (!detected_device)
        {
            NRF_LOG_INFO("No device was found.");
            NRF_LOG_FLUSH();
        }
    
        mpu_read_whoAmI();
    
    
        while (true)
        {
            /* Empty loop. */
        }
    }
    
    /** @} */
    

  • You should set the DEBUG flag in order to get the error code output on the log. If you are using SES, you can build using the "Debug" build settings. For other compilers, add the flag "DEBUG" to the preprocessor defines. It is also possible to read out the error code, as described in this post if you do not have the NRF_LOG module enabled in your application.

  • Manage to get the proper debug setting working and this is the error:

     TWIM: Function: nrfx_twim_init, error code: NRF_SUCCESS.
     TWIM: Instance enabled: 0.
     TWIM: Transfer type: XFER_RX.
     TWIM: Transfer buffers length: primary: 1, secondary: 0.
     TWIM: Function: nrfx_twim_xfer, error code: NRF_ERROR_DRV_TWI_ERR_ANACK.
  • OK I did not give you the complete error code.  

    This is the error while reading WHOIAM  

    nfo> app: WHO I AM 2: 
     TWIM: Transfer type: XFER_TX.
     TWIM: Transfer buffers length: primary: 1, secondary: 0.
     TWIM: Function: twim_xfer, error code: NRF_ERROR_INVALID_ADDR.
     TWIM: Function: nrfx_twim_xfer, error code: NRF_ERROR_INVALID_ADDR.
     app: ERROR 16 [NRF_ERROR_INVALID_ADDR] at /Users/slareau/nrf52_development/nRF5_SDK_16/examples/peripheral/twi_scanner SL/pca10040/blank/ses/main_TWIM.c:139

    For some reason, the address is not recognized.  The same address and routine work fine with nrfx_twi

    but not with nrfx_twim.     M was add at the proper place to create the TWIM routine, from TWI. 

Related