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

TWI nRF52 Reads 1 Byte Ok, But Multiple Sequential Byte Reads Fails

Hi, 

I am using a nRF52832 to communicate with a ICM-20948. I've been trying to write a 16 byte packet of data and read back and verify that same data. According to the ICM-20948 datasheet, the write and read sequences for multiple bytes needs to be as follows. This seems to work just fine when I use an oscilloscope to monitor the data written. All of the ACK conditions and the stop condition are returned by the ICM-20948 for each of the 16 bytes of data.

For a single byte read (i.e. WhoAmI register) the write then read sequence completes just fine with all ACKs and NACKs (and I read back the expected register data 0xEA)

For multiple byte reads however, when using the following setup and implementation of TWI shown in the code below, I am able to complete the first start condition through the last ACK just fine. The point where I get an error is when I send the Slave I2C address and read condition (AD+R) I should get an ACK back from the device. However, this first ACK in the read sequence isn't returned. Each subsequent ACK after each byte of data is received just fine and the data is NACK'ed  at the end. Is there something in my TWI setup that would disallow the device to have enough time or be able to send that first ACK correctly?

TWI Initialization:

extern "C"{
#include "nrf_drv_twi.h"
#include "app_util_platform.h"
#include <string.h> // For memcpy
}
#include "I2Cdev.h"

static nrf_drv_twi_t m_twi=NRF_DRV_TWI_INSTANCE(0);
/** Default constructor.
 */
I2Cdev::I2Cdev() {
}

#define SCL_PIN 27  //change to 27
#define SDA_PIN 26  //change to 26

uint16_t I2Cdev::readTimeout=I2CDEV_DEFAULT_READ_TIMEOUT;
/** Initialize I2C0
 */

void I2Cdev::initialize() {
	 ret_code_t err_code;

	    const nrf_drv_twi_config_t config = {
	       .scl                = SCL_PIN,
	       .sda                = SDA_PIN,
	       .frequency          = NRF_DRV_TWI_FREQ_400K,
	       .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
               .clear_bus_init     = true  //changed to true
	    };


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

}

/** Enable or disable I2C
 * @param isEnabled true = enable, false = disable
 */
void I2Cdev::enable(bool isEnabled) {
  
	if (isEnabled)
		nrf_drv_twi_enable(&m_twi);
	else
		nrf_drv_twi_disable(&m_twi);

}

readByte and readBytes method:

/** Read single byte from an 8-bit device register.
 * @param devAddr I2C slave device address
 * @param regAddr Register regAddr to read from
 * @param data Container for byte value read from device
 * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
 * @return Status of read operation (true = success)
 */
int8_t I2Cdev::readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout) {
    return readBytes(devAddr, regAddr, 1, data, timeout);
}

/** Read multiple bytes from an 8-bit device register.
 * @param devAddr I2C slave device address
 * @param regAddr First register regAddr to read from
 * @param length Number of bytes to read
 * @param data Buffer to store read data in
 * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout)
 * @return I2C_TransferReturn_TypeDef http://downloads.energymicro.com/documentation/doxygen/group__I2C.html
 */
int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint32_t length, uint8_t *data, uint16_t timeout) {

	//bool transfer_succeeded;
	    nrf_drv_twi_tx(&m_twi,devAddr,&regAddr,1,true);

	//transfer_succeeded  = twi_master_transfer(m_device_address, &register_address, 1, TWI_DONT_ISSUE_STOP);

	    ret_code_t r= nrf_drv_twi_rx(&m_twi,devAddr,data,length);
}

	//transfer_succeeded &= twi_master_transfer(m_device_address|TWI_READ_BIT, destination, number_of_bytes, TWI_ISSUE_STOP);
	    return r==NRF_SUCCESS;
  
}

Method to call readBytes, etc:

int my_serif_open_read_reg(void * context, uint8_t reg, uint8_t * rbuffer, uint32_t rlen)
  {
        (void)context, (void)reg, (void)rbuffer, (void)rlen;
        //MyTarget_SPI_do_read_reg(&reg, 1, rbuffer, rlen);
        I2C.readBytes(ICM20948_ADDR, reg, rlen, rbuffer);
        return 0; // shall return a negative value on error

  }
int my_serif_open_write_reg(void * context, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen) 
  {
        (void)context, (void)reg, (void)wbuffer, (void)wlen;
        
        I2C.writeBytes(ICM20948_ADDR, reg, wlen, wbuffer);
        //I2C.writeByte(ICM20948_ADDR, reg, wbuffer[0]);
        // MyTarget_SPI_do_write_reg(&reg, 1, wbuffer, wlen);
        return 0; // shall return a negative value on error

  }

Related