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
  • Hello,

    You can find that there is a Low Power chapter when using the TWI in the product specification:
    https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.nrf52832.ps.v1.1/twim.html#concept_twi_low_power 

    So after executing a twi transfer, you should always disable the twi until next transaction. Before each twi transfer you can enable the twi and start twi transfer. The only difference due to the Errata 89, is that you need to execute the lines specified in Errata 89 after disabling the twi transfer.

    I assume in your case you will need to add twi init and twi start transfer in LSM6DSOX_int_pin_handler(). And in the twi callback handler you need to disable and call Errata 89 if no additional twi is needed until next LSM6DSOX_int_pin_handler().

    Kenneth

Reply
  • Hello,

    You can find that there is a Low Power chapter when using the TWI in the product specification:
    https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.nrf52832.ps.v1.1/twim.html#concept_twi_low_power 

    So after executing a twi transfer, you should always disable the twi until next transaction. Before each twi transfer you can enable the twi and start twi transfer. The only difference due to the Errata 89, is that you need to execute the lines specified in Errata 89 after disabling the twi transfer.

    I assume in your case you will need to add twi init and twi start transfer in LSM6DSOX_int_pin_handler(). And in the twi callback handler you need to disable and call Errata 89 if no additional twi is needed until next LSM6DSOX_int_pin_handler().

    Kenneth

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