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

Parents
  • 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. */
        }
    }
    
    /** @} */
    

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

  • line 139 is :

     err_code = nrfx_twim_tx(&m_twi, MPU_ADRESS, MPU_WHOIAM, 1, false);
        APP_ERROR_CHECK(err_code);
  • The reason for this error code is described in the function API documentation, nrfx_twim_xfer:

    NRFX_ERROR_INVALID_ADDR The provided buffers are not placed in the Data RAM region.

    TWIM uses EasyDMA to read/write directly from/to the RAM buffer. You have used #define to declare the symbol MPU_WHOIAM, which will make it a constant located in flash. In order for the TWIM peripheral to be able to access this symbol, it needs to be placed in RAM. If you declare it as a variable, your application should work fine. 

  • I have done more test:

    In SDK14, The read / write TWI driver was working without issue.

    I went back to the old nrf_drv_twi in the above SDK16 test program.

    I wrote the problematic  command this way: 

      err_code = nrf_drv_twi_tx(&m_twi, 0x68, 0x00, 1, false);

    This line work fine with nrf_drv_ twi and   #define TWI0_USE_EASY_DMA 0

    When I set #define TWI0_USE_EASY_DMA 1  - same

    ERROR 16 [NRF_ERROR_INVALID_ADDR] as nrfx_twim come back
Reply
  • I have done more test:

    In SDK14, The read / write TWI driver was working without issue.

    I went back to the old nrf_drv_twi in the above SDK16 test program.

    I wrote the problematic  command this way: 

      err_code = nrf_drv_twi_tx(&m_twi, 0x68, 0x00, 1, false);

    This line work fine with nrf_drv_ twi and   #define TWI0_USE_EASY_DMA 0

    When I set #define TWI0_USE_EASY_DMA 1  - same

    ERROR 16 [NRF_ERROR_INVALID_ADDR] as nrfx_twim come back
Children
Related