nRF5 SDK is not maintained anymore
More Info: Consider nRF Connect SDK for new designs
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

TWI Errata 89 workaround

I am using LSM6DSOX IMU sensor INT detection with NRF52-DK and have extra 400µA (PPK2 used) on my setup.

After a few days of nightmares I finally found that this is related to Errata 89. The documentation says that there is a workaround, but I can't figure out how to use it. Could you point out where I should put this code to get rid of the 400µA ? should I apply the workaround on every read/write operation?

...
#include "nrf_drv_twi.h"

#include "nrf_drv_gpiote.h"

#include "lsm6dsox_reg.h"

/* TWI instance, ID. and address for LSM6DSOX */
#define TWI_INSTANCE_ID 0
#define TWI_SDA_PIN 26
#define TWI_SCL_PIN 27

#define LSM6DSOX_INT_PIN 12
#define LSM6DSOX_INT_LED LED_1

#define LSM6DSOX_ADRRESS 0x6B
static stmdev_ctx_t dev_ctx;

static
const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);

void i2c_init(void);
int32_t i2c_LSM6DSOX_write(void * handle, uint8_t reg, uint8_t * bufp, uint16_t len);
int32_t i2c_LSM6DSOX_read(void * handle, uint8_t reg, uint8_t * bufp, uint16_t len);

/**@brief Function for initializing power management.
 */
static void power_management_init(void) {
  ret_code_t err_code;
  err_code = nrf_pwr_mgmt_init();
  APP_ERROR_CHECK(err_code);
}

/**@brief Function for handling the idle state (main loop).
 *
 * @details If there is no pending log operation, then sleep until next the next event occurs.
 */
static void idle_state_handle(void) {
  nrf_pwr_mgmt_run();
}

void LSM6DSOX_int_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
  nrf_drv_gpiote_out_toggle(LSM6DSOX_INT_LED);

  uint8_t MLC[8] = {
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00
  };
  //lsm6dsox_mlc_out_get(&dev_ctx, MLC);
}

/**
 * @brief Function for configuring: LSM6DSOX_INT_PIN pin for input, LSM6DSOX_INT_LED pin for output,
 * and configures GPIOTE to give an interrupt on pin change.
 */
static void gpio_init(void) {
  ret_code_t err_code;

  err_code = nrf_drv_gpiote_init();
  APP_ERROR_CHECK(err_code);

  nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(false);
  err_code = nrf_drv_gpiote_out_init(LSM6DSOX_INT_LED, & out_config);
  APP_ERROR_CHECK(err_code);

  nrf_drv_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
  err_code = nrf_drv_gpiote_in_init(LSM6DSOX_INT_PIN, & in_config, LSM6DSOX_int_pin_handler);
  APP_ERROR_CHECK(err_code);
  nrf_drv_gpiote_in_event_enable(LSM6DSOX_INT_PIN, true);
}

int main(void) {
  power_management_init();

  /*
  ble_stack_init();
  advertising_init();
  advertising_start();
  */

  i2c_init();
  gpio_init();

  /*
   *	Initialize mems driver interface
   */
  dev_ctx.write_reg = i2c_LSM6DSOX_write;
  dev_ctx.read_reg = i2c_LSM6DSOX_read;
  dev_ctx.handle = NULL;

  /* Check device ID */
  uint8_t whoamI = 0;
  lsm6dsox_device_id_get( & dev_ctx, & whoamI);

  if (whoamI == LSM6DSOX_ID) {
    ...
  }

  for (;;) {
    idle_state_handle();
  }
}

/**
 * @brief	Function for i2c bus init.
 *
 * @param	none
 *
 */
void i2c_init() {
  ret_code_t err_code;

  const nrf_drv_twi_config_t twi_config = {
    .scl = TWI_SCL_PIN,
    .sda = TWI_SDA_PIN,
    .frequency = (nrf_drv_twi_frequency_t) NRF_TWI_FREQ_400K,
    .interrupt_priority = APP_IRQ_PRIORITY_LOW,
    .clear_bus_init = true
  };
  err_code = nrf_drv_twi_init( & m_twi, & twi_config, NULL, NULL);
  APP_ERROR_CHECK(err_code);

  nrf_drv_twi_enable( & m_twi);

  nrf_drv_twi_disable( & m_twi);
  nrf_drv_twi_uninit( & m_twi);

  // Nordic work around to prevent burning excess power due to chip errata
  // Use 0x400043FFC for TWI0. TODO: here right?
  *(volatile uint32_t * ) 0x400043FFC = 0;
  *(volatile uint32_t * ) 0x400043FFC;
  *(volatile uint32_t * ) 0x400043FFC = 1;

  err_code = nrf_drv_twi_init( & m_twi, & twi_config, NULL, NULL);
  APP_ERROR_CHECK(err_code);

  nrf_drv_twi_enable( & m_twi);
}

/**
 * @brief	Write generic device register (LSM6DSOX requirement)
 *
 * @param	handle	customizable argument. In this case is used in
 *					order to select the correct sensor bus handler.
 * @param	reg		register to write
 * @param	bufp	pointer to data to write in register reg
 * @param	len		number of consecutive register to write
 *
 * @retval			status: 0 means no Error
 */
int32_t i2c_LSM6DSOX_write(void * handle, uint8_t reg, uint8_t * bufp, uint16_t len) {
  while (nrf_drv_twi_is_busy( & m_twi)) {}

  uint8_t reg_tmp[100] = {
    0
  };
  reg_tmp[0] = reg;
  memcpy( & (reg_tmp[1]), bufp, len);

  uint32_t status = nrf_drv_twi_tx( & m_twi, LSM6DSOX_ADRRESS, reg_tmp, len + 1, false);
  if (status != NRF_SUCCESS) {
    return status;
  }

  status = nrf_drv_twi_tx( & m_twi, LSM6DSOX_ADRRESS, & (reg_tmp[0]), 1, true);
  if (status != NRF_SUCCESS) {
    return status;
  }
  // TODO: Nordic Errata 89 workaround here?
  return status;
}

/**
 * @brief	Read generic device register (LSM6DSOX requirement)
 *
 * @param	handle	customizable argument. In this case is used in
 *					order to select the correct sensor bus handler.
 * @param	reg		register to read
 * @param	bufp	pointer to buffer that store the data read
 * @param	len		number of consecutive register to read
 *
 * @retval			status: 0 means no Error
 */
int32_t i2c_LSM6DSOX_read(void * handle, uint8_t reg, uint8_t * bufp, uint16_t len) {
  uint8_t preg = reg;
  uint32_t status = nrf_drv_twi_tx( & m_twi, LSM6DSOX_ADRRESS, & preg, 1, true);
  if (status != NRF_SUCCESS) {
    return status;
  }

  status = nrf_drv_twi_rx( & m_twi, LSM6DSOX_ADRRESS, bufp, len);
  if (status != NRF_SUCCESS) {
    return status;
  }
  // TODO: Nordic Errata 89 workaround here?
  return status;
}

Parents Reply Children
  • void MLC_int_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
    {
    	i2c_init(); //Because the system in low power
    
    	nrf_drv_gpiote_out_toggle(MLC_INT_LED);
    	
    	uint8_t MLC[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
    	lsm6dsox_mlc_out_get(&dev_ctx, MLC);
    	
    	/* When putting the system in low power and the peripheral is not needed, 
    	lowest possible power consumption is achieved by stopping, and then disabling the peripheral. */
    	nrf_drv_twi_disable( &m_twi );
    	nrf_drv_twi_uninit( &m_twi );
    	
    	/* Nordic work around to prevent burning excess power due to chip errata
    	   Use 0x40003FFC for TWI0 */
    	*(volatile uint32_t *)0x40003FFC = 0;
    	*(volatile uint32_t *)0x40003FFC;
    	*(volatile uint32_t *)0x40003FFC = 1;
    }
    
    ...
    
    /**
     * @brief	Function for i2c bus init.
     *
     * @param	none
     *
     */
    void i2c_init()
    {
      ret_code_t err_code;
    
      const nrf_drv_twi_config_t twi_config = {.scl                 = TWI_SCL_PIN,
                                                .sda                = TWI_SDA_PIN,
                                                .frequency          = (nrf_drv_twi_frequency_t)NRF_TWI_FREQ_400K,
                                                .interrupt_priority = APP_IRQ_PRIORITY_LOW,
                                                .clear_bus_init     = true};
      err_code                               = nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);
      APP_ERROR_CHECK(err_code);
    																	
    	nrf_drv_twi_enable(&m_twi);
    }
    
    ...
    
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        ...
    	
    	gpio_init();
    	i2c_init();
    
    	/*
    	*	Initialize mems driver interface
    	*/
    	dev_ctx.write_reg = i2c_LSM6DSOX_write;
    	dev_ctx.read_reg 	= i2c_LSM6DSOX_read;
        dev_ctx.handle = NULL;
    
    	/* Check device ID */
    	static uint8_t whoamI=0, rst=0;
        lsm6dsox_device_id_get(&dev_ctx, &whoamI);
      		    		  	
    	if (whoamI == LSM6DSOX_ID) 
    	{
    		 //Restore default configuration
    		int ttl = 8;
    		lsm6dsox_reset_set(&dev_ctx, PROPERTY_ENABLE);
    		do {
    			lsm6dsox_reset_get(&dev_ctx, &rst);
    			if ((ttl-- < 0) && rst) {
    				return 1;
    			}
    		} while (rst);
      		  		
    		...
    		
    		/* Nordic work around to prevent burning excess power due to chip errata
    			 Use 0x40003FFC for TWI0 */
    		*(volatile uint32_t *)0x40003FFC = 0;
    		*(volatile uint32_t *)0x40003FFC;
    		*(volatile uint32_t *)0x40003FFC = 1;
    	}
    	else
    	{
    		return 1;
    	}
    	
    	for (;;)
    	{
    		idle_state_handle();
    	}
    }
    

    Thanks! Finally done!

    I will post the final code here (parts that I changed regarding my post), maybe it will come in handy for someone!

Related